(翻译)数据结构与算法系列 —— 栈

401 阅读5分钟

(翻译)数据结构与算法系列 —— 栈

栈的介绍

在这篇教程中,你将学到栈数据结构以及它的在 Python, Java and C/C++ 中的实现。

栈在编程中是一种有用的数据结构,就像一堆盘子摞在一起。

考虑一下你能对这样的一堆盘子能够做的事:

  • 放一个新的盘子在最上面
  • 拿掉最上面的盘子

如果你想要最下面的那个盘子,你必须先移开上面所有的盘子。这种安排称为后进先出——最后进去的要第一个出来。

栈后进先出的原则

在程序属于中,把一个元素放在栈的顶部的称为push,而移除最顶部的元素称为pop

在上面的图片中,虽然第二项是最后添加的,但它是最后移除的——所以它遵循后进先出的原则。

我们可以使用任意的编程语言实现栈,像C,C++, Java, Python 或者 C#,但规范几乎是一样的。

栈的基本操作

栈像一个对象(一种抽象的数据类型 - ADT),它允许下面的操作:

  • Push:在栈的顶部添加一个元素
  • Pop:从栈的顶部移除一个元素
  • IsEmpty:检查栈是否为空
  • IsFull:检查栈是否为满
  • Peek:获取最顶部的元素并且不移除它

栈的工作原理

栈的操作如下:

  • 1.一个称作TOP的指针用跟踪栈的顶部元素。
  • 2.当初始化栈时,我们设置TOP的值为 -1 ,所以我们可以通过比较TOP == -1来判断栈是否为空。
  • 3.当添加进一个元素时,我们增加TOP的值,并将新的元素放在TOP指向的位置。
  • 4.弹出一个元素时,我们返回TOP指向的元素,并且减少TOP的值。
  • 5.在添加之前,我们先要检查栈是否已满。
  • 6.在弹出之前,我们先要检查栈是否为空。

栈在Python中的实现

# 栈在python中的实现


# 创建一个栈
def create_stack():
    stack = []
    return stack


# 创建一个空栈
def check_empty(stack):
    return len(stack) == 0


# 向栈添加元素
def push(stack, item):
    stack.append(item)
    print("pushed item: " + item)


# 从栈中移除元素
def pop(stack):
    if (check_empty(stack)):
        return "stack is empty"

    return stack.pop()


stack = create_stack()
push(stack, str(1))
push(stack, str(2))
push(stack, str(3))
push(stack, str(4))
print("popped item: " + pop(stack))
print("stack after popping an element: " + str(stack))

栈在Java中的实现

// 栈在Java中的实现

class Stack {
  private int arr[];
  private int top;
  private int capacity;

  // 创建一个栈
  Stack(int size) {
    arr = new int[size];
    capacity = size;
    top = -1;
  }

  // 向栈添加元素
  public void push(int x) {
    if (isFull()) {
      System.out.println("OverFlow\nProgram Terminated\n");
      System.exit(1);
    }

    System.out.println("Inserting " + x);
    arr[++top] = x;
  }

  // 从栈中移除元素
  public int pop() {
    if (isEmpty()) {
      System.out.println("STACK EMPTY");
      System.exit(1);
    }
    return arr[top--];
  }

  // 返回堆栈大小的实用函数
  public int size() {
    return top + 1;
  }

  // 检查栈是否为空
  public Boolean isEmpty() {
    return top == -1;
  }

  // 检查栈是否已满
  public Boolean isFull() {
    return top == capacity - 1;
  }

  public void printStack() {
    for (int i = 0; i <= top; i++) {
      System.out.println(arr[i]);
    }
  }

  public static void main(String[] args) {
    Stack stack = new Stack(5);

    stack.push(1);
    stack.push(2);
    stack.push(3);
    stack.push(4);

    stack.pop();
    System.out.println("\nAfter popping out");

    stack.printStack();

  }
}

栈在C中的实现

// 栈在C中的实现

#include <stdio.h>
#include <stdlib.h>

#define MAX 10

int count = 0;

// 创建一个栈
struct stack {
  int items[MAX];
  int top;
};
typedef struct stack st;

void createEmptyStack(st *s) {
  s->top = -1;
}

// 检查栈是否已满
int isfull(st *s) {
  if (s->top == MAX - 1)
    return 1;
  else
    return 0;
}

// 检查栈是否为空
int isempty(st *s) {
  if (s->top == -1)
    return 1;
  else
    return 0;
}

// 向栈添加元素
void push(st *s, int newitem) {
  if (isfull(s)) {
    printf("STACK FULL");
  } else {
    s->top++;
    s->items[s->top] = newitem;
  }
  count++;
}

// 从栈中移除元素
void pop(st *s) {
  if (isempty(s)) {
    printf("\n STACK EMPTY \n");
  } else {
    printf("Item popped= %d", s->items[s->top]);
    s->top--;
  }
  count--;
  printf("\n");
}

// 打印出栈的元素
void printStack(st *s) {
  printf("Stack: ");
  for (int i = 0; i < count; i++) {
    printf("%d ", s->items[i]);
  }
  printf("\n");
}

// 驱动代码
int main() {
  int ch;
  st *s = (st *)malloc(sizeof(st));

  createEmptyStack(s);

  push(s, 1);
  push(s, 2);
  push(s, 3);
  push(s, 4);

  printStack(s);

  pop(s);

  printf("\nAfter popping out\n");
  printStack(s);
}

栈在C++中的实现

// 栈在C++中的实现

#include <stdlib.h>
#include <iostream>

using namespace std;

#define MAX 10
int size = 0;

// 创建一个栈
struct stack {
  int items[MAX];
  int top;
};
typedef struct stack st;

void createEmptyStack(st *s) {
  s->top = -1;
}

// 检查栈是否已满
int isfull(st *s) {
  if (s->top == MAX - 1)
    return 1;
  else
    return 0;
}

// 检查栈是否为空
int isempty(st *s) {
  if (s->top == -1)
    return 1;
  else
    return 0;
}

// 向栈添加元素
void push(st *s, int newitem) {
  if (isfull(s)) {
    printf("STACK FULL");
  } else {
    s->top++;
    s->items[s->top] = newitem;
  }
  size++;
}

// 从栈中移除元素
void pop(st *s) {
  if (isempty(s)) {
    printf("\n STACK EMPTY \n");
  } else {
    printf("Item popped= %d", s->items[s->top]);
    s->top--;
  }
  size--;
  cout << endl;
}

// 打印出栈的元素
void printStack(st *s) {
  printf("Stack: ");
  for (int i = 0; i < size; i++) {
    cout << s->items[i] << " ";
  }
  cout << endl;
}

// 驱动代码
int main() {
  int ch;
  st *s = (st *)malloc(sizeof(st));

  createEmptyStack(s);

  push(s, 1);
  push(s, 2);
  push(s, 3);
  push(s, 4);

  printStack(s);

  pop(s);

  cout << "\nAfter popping out\n";
  printStack(s);
}

栈的时间复杂度

对于基于数组实现的栈来说,添加和弹出操作需要固定的时间,即O(1)。

栈数据结构的应用

尽管栈是一种简单的数据结构实现,但它却非常强大。栈最常见的用法是:

  • 反转单词 —— 把字母放入一个栈中,并且弹出它们。因为栈是后进先出的顺序,所以你可以得到反转之后的字母顺序。
  • 编译器 —— 编译器使用栈来计算表达式的值,像2 + 4 / 5 * (7 - 9),方法是将表达式转换为前缀或后缀形式。
  • 浏览器 —— 浏览器的返回按钮将所有你之前访问过的URL地址保存在一个栈中。每次你浏览一个新页面时,它会在添加到栈的顶部。当你按下返回按钮时,当前的URL会从栈中移除,而之前的URL会被取出。