1-1 栈:数组实现

使用数组栈

栈是一种遵循后进先出(LIFO)原则的线性数据结构。可以通过将数组的末尾视为栈顶来使用数组实现栈。

使用数组实现栈时,我们需要维护:

  • 一个整型数组用于存储元素。
  • 一个变量 capacity 用于表示栈的最大容量。
  • 一个变量 top 用于跟踪顶部元素的索引。初始时,top = -1 表示空栈。
class Stack
 { 
    // array to store elements
    int *arr;       
    
    // maximum size of stack
    int capacity;   
    
    // index of top element
    int top;          

public:
    
    // constructor
    Stack(int cap)
    {
        capacity = cap;
        arr = new int[capacity];
        top = -1;
    }
};

int main()
{
    Stack mystack{ 5 };
    return 0;
}

image


栈操作

基本的操作共有五种:一个是压栈(push), 弹栈(pop), 获得栈顶元素(peek), 栈的判空(isEmpty)和判满(isFull)

操作 函数 说明 例子
压栈 push(x) 将元素 x 放入栈顶。若栈满,输出 "Stack Overflow" st.push(1); st.push(2); → 栈内: [1, 2]
弹栈 pop() 移除并返回栈顶元素。若栈空,输出 "Stack Underflow",返回 -1 st.pop(); → 返回 2,栈内: [1]
取顶 peek() ortop() 返回栈顶元素但不移除。若栈空,输出 "Stack is Empty",返回 -1 st.peek(); → 返回 1
判空 isEmpty() 检查栈是否为空,返回布尔值 st.isEmpty(); → false
判满 isFull() 检查栈是否已满,返回布尔值 st.isFull(); → false

压栈操作

向栈中添加一个元素。如果栈已满,则称为溢出状态。

  • 在将元素压入栈之前,我们会检查栈是否已满。
  • 如果栈已满(top == capacity - 1),则发生栈溢出,我们无法将该元素插入栈中。
  • 否则,我们将 top 的值加 1(top = top + 1),并将新值插入到栈顶位置。
  • 元素可以被压入栈中,直到达到栈的容量上限。
    // push operation
    void push(int x) 
    {
        if (top == capacity - 1)  
        {
            cout << "Stack Overflow\n";
            return;
        }
   
        arr[++top] = x;
    }

我们尝试将1,2依次压入栈中,完整代码如下:

# include <iostream>

class Stack 
{  
    // array to store elements
    int *arr;       
    
    // maximum size of stack
    int capacity;   
    
    // index of top element
    int top;          

public:
    
    // constructor
    Stack(int cap) 
    {
        capacity = cap;
        arr = new int[capacity];
        top = -1;
    }
    
    void push(int x)
    {
      if (top == capacity - 1)
      {
        std::cout << "Stack Overflow\n";
        return;
      }
      arr[++top] = x;
    }
};

int main()
{
  Stack pushStack{2};
  
  pushStack.push(1);
  pushStack.push(2);

  return 0;
}

image

我们通过Stack pushStack{2};使用列表初始化栈的对象pushStack最大容量初始化为2, 而依次将1、2依次压入栈中之后,这样栈就满了。 这时候我们再尝试压栈,使用pushStack.push(3);将第3个元素压入栈中,就会触发“Stack Overflow”并且返回空。

# include <iostream>

class Stack 
{  
    // array to store elements
    int *arr;       
    
    // maximum size of stack
    int capacity;   
    
    // index of top element
    int top;          

public:
    
    // constructor
    Stack(int cap) 
    {
        capacity = cap;
        arr = new int[capacity];
        top = -1;
    }
    
    void push(int x)
    {
      if (top == capacity - 1)
      {
        std::cout << "Stack Overflow\n";
        return;
      }
      arr[++top] = x;
    }
};

int main()
{
  Stack pushStack{2};
  
  pushStack.push(1);
  pushStack.push(2);
  pushStack.push(3);

  return 0;
}

运行上面代码将输出

Stack Overflow

弹栈操作

从栈中移除一个元素。元素按与压入栈时的相反顺序弹出。如果栈为空,则称为栈下溢。

  • 在从栈中弹出元素之前,我们会检查栈是否为空。
  • 如果栈为空(top == -1),则发生栈下溢Underflow,此时无法从栈中移除任何元素。
  • 否则,我们会将 top 的值保存下来,将 top 的值减 1(top = top – 1),并返回保存的 top 值。
    // pop operation
    int pop() 
    {

        if (top == -1) 
        {
            cout << "Stack Underflow\n";
            return -1;
        }

    return arr[top--];
}

为了使得例子更加方便,我们列表初始化Stack对象的最大容量为1, 然后对它进行弹栈操作, 就会触发Underflow并且返回-1。

#include <iostream>

class Stack
 { 
    // array to store elements
    int *arr;       
    
    // maximum size of stack
    int capacity;   
    
    // index of top element
    int top;          

public:
    
    // constructor
    Stack(int cap)
    {
        capacity = cap;
        arr = new int[capacity];
        top = -1;
    }
    
    int pop() 
    {
        if (top == -1) 
        {
            std::cout << "Stack Underflow\n";
            return -1;
        }

        return arr[top--];
    }
};

int main()
{
    Stack popStack{ 1 };
    popStack.pop();
    
    return 0;
}

运行该程序将输出

Stack Underflow

栈的取顶(Top)或查看(Peek)操作:

返回栈的顶部元素。

  • 在从栈中返回顶部元素之前,我们会先检查栈是否为空。
  • 如果栈为空(top == -1),则直接输出“栈为空”。
  • 否则,返回存储在索引为 top 处的元素。
    // peek (or top) operation
    int peek() 
    {
        if (top == -1) 
        {
            cout << "Stack is Empty\n";
            return -1;
        }
        return arr[top];
    }

同样的,我们创建一个栈对象peekStack, 将它最大容量实例化为1, 然后检查它是否为空。

# include <iostream>

class Stack
 { 
    // array to store elements
    int *arr;       
    
    // maximum size of stack
    int capacity;   
    
    // index of top element
    int top;          

public:
    
    // constructor
    Stack(int cap)
    {
        capacity = cap;
        arr = new int[capacity];
        top = -1;
    }

    // peek (or top) operation
    int peek() 
    {
        if (top == -1) 
        {
            std::cout << "Stack is Empty\n";
            return -1;
        }
        return arr[top];
    }
};

int main()
{
    Stack peekStack{ 1 };
    peekStack.peek();

    return 0;
}

运行上面例子,因为栈顶索引我们一开始构造初始化为top = -1, 当此时对栈对象peekStack取顶peekStack.peek();时,会触发Stack is Empty并且返回-1。

Stack is Empty

image


栈中的 isEmpty 操作:

如果栈为空,则返回 true;否则返回 false。

  • 检查栈顶元素的值。
  • 如果 (top == -1),则栈为空,因此返回 true。
  • 否则,栈不为空,因此返回 false。
    // check if stack is empty
    bool isEmpty() 
    {
        return top == -1;
    }

同理,当此时对栈对象emptyStack执行判空emptyStack.isEmpty(),因为栈顶索引我们一开始构造初始化为top = -1, top 与 -1 比较,求值结果为相等,返回true, std::cout 默认对布尔值进行数值输出,true 会被隐式转换为 1。

# include <iostream>

class Stack
 { 
    // array to store elements
    int *arr;       
    
    // maximum size of stack
    int capacity;   
    
    // index of top element
    int top;          

public:
    
    // constructor
    Stack(int cap)
    {
        capacity = cap;
        arr = new int[capacity];
        top = -1;
    }

    // check if stack is empty
    bool isEmpty() 
    {
        return top == -1;
    }
};

int main()
{
    Stack emptyStack{ 1 };
    std::cout << emptyStack.isEmpty() << '\n';

    return 0;
}

运行该例子将输出

1

相关内容
我们将在第4.9课——布尔值讨论布尔值以整数类型存储。


栈中的 isFull 操作:

如果栈已满,则返回 true;否则返回 false。

  • 检查栈顶元素的值。
  • 如果 (top == capacity-1),则栈已满,因此返回 true。
  • 否则,栈未满,因此返回 false。
    // check if stack is full
    bool isFull() 
    {
        return top == capacity - 1;
    }

完整代码如下:

class Stack
 { 
    // array to store elements
    int *arr;       
    
    // maximum size of stack
    int capacity;   
    
    // index of top element
    int top;          

public:
    
    // constructor
    Stack(int cap)
    {
        capacity = cap;
        arr = new int[capacity];
        top = -1;
    }

    // check if stack is full
    bool isFull() 
    {
        return top == capacity - 1;
    }
};

int main()
{
    Stack fullStack{ 2 };
    std::cout << fullStack.isFull() << '\n';
    return 0;
}

同理,当此时对栈对象emptyStack执行判满`fullStack.isFull()`,因为栈顶索引我们一开始构造初始化为`top = -1`, top 与 apacity - 1 比较,求值结果为不相等,返回false, std::cout 默认对布尔值进行数值输出,false 会被隐式转换为 0。这里还没有测试栈满的情况,我们会在下面完整的栈:数组实现中测试。

---

# 使用数组实现栈的完整实现

```cpp
#include <iostream>

class Stack 
{
    
    // array to store elements
    int *arr;       
    
    // maximum size of stack
    int capacity;   
    
    // index of top element
    int top;        

public:

    // constructor
    Stack(int cap) 
    {
        capacity = cap;
        arr = new int[capacity];
        top = -1;
    }

    // push operation
    void push(int x) 
    {
        if (top == capacity - 1) 
       {
            std::cout << "Stack Overflow\n";
            return;
        }
        arr[++top] = x;
    }

    // pop operation
    int pop() {
        if (top == -1) 
        {
            std::cout << "Stack Underflow\n";
            return -1;
        }
        return arr[top--];
    }

    // peek (or top) operation
    int peek() 
    {
        if (top == -1)   
        {
            std::cout << "Stack is Empty\n";
            return -1;
        }
        return arr[top];
    }

    // check if stack is empty
    bool isEmpty() 
    {
        return top == -1;
    }

    // check if stack is full
    bool isFull() 
    {
        return top == capacity - 1;
    }
};

int main() 
{
    Stack stack{4};

    // pushing elements
    stack.push(1);
    stack.push(2);

    // checking if stack is full
    std::cout << "Is stack full: " << (stack.isFull() ? "Yes" : "No") << "\n";

    // checking if stack is empty
    std::cout << "Is stack empty: " << (stack.isEmpty() ? "Yes" : "No") << "\n";

    // popping one element
    std::cout << "Popped: " << stack.pop() << "\n";
    // popping two element
    std::cout << "Popped: " << stack.pop() << "\n";

    // checking top element
    std::cout << "Top element: " << stack.peek() << "\n";

    // checking if stack is full
    std::cout << "Is stack full: " << (stack.isFull() ? "Yes" : "No") << "\n";

    // checking if stack is empty
    std::cout << "Is stack empty: " << (stack.isEmpty() ? "Yes" : "No") << "\n";

    return 0;
}

这里new[] 操作符的数组大小形参期望的是 std::size_t(通常是 unsigned long 或 unsigned long long),而我们传入的实参capacity是 int(有符号)。这里capacity 从有符号的转化为无符号的会触发符号转化警告, 如果我们当初将类型安全警告被提升为错误(-Werror)会报错,需要降低类型安全警告等级,或者定义capacity为std::size_t类型。

image

相关内容
我们将在第16.3节—— std::vector 与无符号长度及下标问题 中讨论设计者当初将下标选择为无符号。

程序将输出结果为

image


使用动态数组实现栈

当使用固定大小的数组时,栈的最大容量无法超过其初始大小。为克服这一限制,我们可以使用动态数组。动态数组会在元素被添加或移除时自动调整大小,这使得栈更加灵活。
在 C++ 中,我们可以使用 vector, 查看当前大小size函数将替代判满操作。

  #include <iostream>                                                                                                                                                                                            
  #include <vector>
  
  class Stack 
  {
  private:
      std::vector<int> arr;
  public:
      // push operation
      void push(int x) 
      {
          arr.push_back(x);
      }
  
      // pop operation
      int pop() {
          if (arr.empty()) 
          {
              std::cout << "Stack Underflow\n";
              return -1;
          }
          int val = arr.back();
          arr.pop_back();
          return val;
      }
  
      // peek (or top) operation
      int peek() 
      {
          if (arr.empty())   
          {
              std::cout << "Stack is Empty\n";
              return -1;
          }
          return arr.back();
      }
  
      // check if stack is empty
      bool isEmpty() 
      {
          return arr.empty();
      }
  
      // current size
      int size()
      {
          return arr.size();
      }
  };
  
  int main() 
  {
      Stack stack;
  
      // pushing elements
      stack.push(x: 1);
      stack.push(x: 2);
  
      // checking current size
      std::cout << "Current size: " << stack.size() << '\n';
  
      // checking if stack is empty
      std::cout << "Is stack empty: " << (stack.isEmpty() ? "Yes" : "No") << "\n";
  
      // popping one element
      std::cout << "Popped: " << stack.pop() << "\n";
      // popping two element
      std::cout << "Popped: " << stack.pop() << "\n";
  
      // checking top element
      std::cout << "Top element: " << stack.peek() << "\n";
  
      // checking current size
      std::cout << "Current size: " << stack.size() << '\n';
  
      // checking if stack is empty
      std::cout << "Is stack empty: " << (stack.isEmpty() ? "Yes" : "No") << "\n";
  
      return 0;
  }  

运行程序将输出

Current size: 2
Is stack empty: No
Popped: 2
Popped: 1
Top element: Stack is Empty
-1
Current size: 0
Is stack empty: Yes

image

posted @ 2026-03-18 01:11  游翔  阅读(2)  评论(0)    收藏  举报