三、棧的實現

臣有一事不知當不當講發表於2018-04-08

棧的定義:棧是一種特殊的線性表(容器)。

棧僅能線上性表的一端進行操作:

棧頂(top):允許操作的一端;

棧底(bottom):不允許操作的一端。

棧的特性:後進先出(Last in First Out)

棧的操作(函式):建立(stack())、銷燬(~stack())、清空(clear())、進棧(push())、出棧(pop())、獲取棧頂元素(top())、獲取棧的大小(size())。

template < typename T>
class Stack : public Object
{
public:
    virtual void push( const T& e) = 0;
    virtual void pop() = 0;
    virtual T top() const = 0;
    virtual void clear() = 0;
    virtual int size() const = 0;
};


一、StaticStack順序棧設計要點:

類别範本:使用原生陣列作為棧的儲存空間

             使用模板引數(<T, N>)決定棧的最大容量


程式碼實現如下:

template <typename T, int N>
class StaticStack : public Stack<T>
{
protected:
    T m_space[N];  //棧儲存空間,N為模板引數
    //此處已經涉及到泛指型別進行具體物件的構造了,所以效率不高。
    int m_top;  //棧頂標誌
    int m_size; //當前棧的大小
    
public:
    StaticStack()
    {
        m_top = -1;
        m_size = 0;
    }
    
    int capacity() const
    {
        return N;
    }
    
    void push(const T& e)
    {
        if( m_size < N)
        {
            m_space[m_top + 1] = e;  //為了異常安全,在複製操作符出現異常的情況下,棧內部的資料並不會出現改變
            m_top++;     //e可能是一個類型別,如果真的這個類型別未進行復制操作符過載,丟擲異常前不對m_top做任何改變
            m_size++;
        }
        else
        {
           //丟擲異常
        }
    }
    
    void pop()
    {
        if( m_size > 0)
        {
          m_top--;
          m_size--;
        }
        else
        {
          //丟擲異常
      }   
 }    
T top() const { if( m_size > 0) { return m_space[m_top]; //直接返回棧頂的值 } else { //丟擲異常 } } void clear() { m_top = -1; m_size = 0; } int size() const { return m_size; } };

在主函式中測試如下程式碼:

int main()
{
    StaticStack<int, 5> ss;

    for(int i=0; i<5; i++)
    {
        ss.push(i);
    }

    while (ss.size() > 0)
    {
        cout << ss.top();

        ss.pop();
    }

    return 0;
}

輸出效果為:


小結:棧只允許線上性表的一端進行操作。

注意:StaticStack使用原生陣列作為內部儲存空間

          StaticStack的最大容量由模板引數決定。


二、(LinkStack)鏈式棧的實現

問題提出:當儲存的元素為類型別時,StaticStack的物件在建立時,會多次呼叫元素型別的建構函式,影響效率。原因在於使用原生陣列作為儲存空間,在建立棧物件的時候,會呼叫泛指型別的建構函式。

鏈式棧的設計要點(本質就是連結串列):

1、類别範本,抽象父類Stack的直接子類;

2、在內部組合使用LinkList類,實現棧的鏈式儲存;

3、只在單連結串列成員物件的頭部進行操作


實現程式碼如下:

template <typename T>
class LinkStack : public Stack <T>
{
protected:
    LinkList<T> m_list;
public:
    void push(const T& e)  //O(1)  注:此處為頭插法
    {
        m_list.insert(0,e);
    }

    void pop()   //O(1  )
    {
        if( m_list.length() > 0)
        {
            m_list.remove(0);
        }
        else
        {
            THROW_EXCEPTION(InvalidOperationException,"No element in current stack...");
        }
    }

    T top() const
    {
        if( m_list.length() > 0)
        {
            return m_list.get(0);
        }
        else
        {
            THROW_EXCEPTION(InvalidOperationException,"No element in current stack...");
        }
    }

    void clear()  //O(n)
    {
        m_list.clear();
    }

    int size() const  //O(1)
    {
        return m_list.length();
    }
};

在主函式中編寫如下程式碼:

class Test : public Object
{
  public:
    Test()
    {
        cout << "Test()" << endl;
    }

    ~Test()
    {
        cout << "~Test()" << endl;
    }
};

int main()
{
    LinkStack<Test> ls;

    cout << ls.size() << endl;

    return 0;
}
執行後發現,建構函式和解構函式均未執行,效率提升。


棧的應用之一:符號匹配問題:



bool scan(const char* code)
{
    LinkStack<char> stack;
    int i=0;
    bool ret = true;

    code = (code == NULL)? "" : code;

    while( ret && (code[i] != '\0'))
    {
        if( is_left(code[i]))
        {
           stack.push(code[i]);
        }
        else if(is_right(code[i]))
        {
            if( (stack.size()) && is_match(stack.top(), code[i]))
            {
                stack.pop();
            }
            else
            {
                ret = false;
            }
        }
        else if( is_quot(code[i]))
        {
            if( stack.size() == 0 || !is_match(stack.top(), code[i]) )  //不匹配的時候
            {
               stack.push(code[i]);
            }
            else if(is_match(stack.top(), code[i]))
            {
               stack.pop();
            }
        }

        i++;
    }

    return ret && (stack.size() == 0);
}

小結:鏈式棧的實現組合使用了單連結串列物件(又是不同的資料結構)。

在單連結串列的頭部進行操作能夠高效的入棧和出棧操作。

棧“後進後出”的特性適用於檢測成對出現的符號。

棧非常適合於需要“就近匹配”的場合。



相關文章