
什么是堆栈?
堆栈是一种线性数据结构,其中新元素的插入和现有元素的删除发生在表示为堆栈顶部的同一端。
为了实现栈,需要维护一个
指向栈顶的指针
,栈顶是最后插入的元素,因为
我们只能访问栈顶的元素。
LIFO(后进先出):
该策略规定最后插入的元素将首先出现。
您可以将一堆相互叠放的盘子作为现实生活中的示例。
我们最后放置的盘子位于顶部,并且由于我们移除了顶部的盘子,所以我们可以说最后放置的盘子最先出现。
堆栈的基本操作
为了在堆栈中进行操作,向我们提供了某些操作。
-
push()
将一个元素插入栈中
-
pop()
从堆栈中删除一个元素
-
top()
返回栈顶元素。
-
如果堆栈为空,
isEmpty()返回 true,否则返回 false。
-
size()
返回堆栈的大小。
堆
推:
将一个项目添加到堆栈中。
如果堆栈已满,则称为
溢出情况。
推送算法:
开始
如果堆栈已满
返回
万一
别的
增量顶部
stack[top] 赋值
结束其他
结束程序
流行音乐:
从堆栈中删除一个项目。
项目的弹出顺序与推送顺序相反。
如果堆栈为空,则称为
下溢
条件。
流行算法:
开始
如果堆栈为空
返回
万一
别的
存储堆栈的值[top]
减少顶部
返回值
结束其他
结束程序
顶部:
返回栈顶元素。
顶部算法:
开始
返回堆栈[顶部]
结束程序
是空的:
如果堆栈为空则返回 true,否则返回 false。
isEmpty 的算法
:
开始
如果顶部 < 1
返回真
别的
返回错误
结束程序
实际理解堆栈:
现实生活中有很多堆栈的例子。
考虑一个简单的例子,在食堂里,盘子一个一个地叠在一起。
位于顶部的盘子是第一个被移除的盘子,即放置在最底部位置的盘子在堆叠中保留最长的时间。
因此,可以简单地看出遵循 LIFO/FILO 顺序。
复杂度分析:
|
运营
|
复杂
|
|
推()
|
复杂度(1)
|
|
流行音乐()
|
复杂度(1)
|
|
是空的()
|
复杂度(1)
|
|
尺寸()
|
复杂度(1)
|
堆栈类型:
-
固定大小堆栈
:顾名思义,固定大小堆栈具有固定的大小,不能动态增长或收缩。
如果堆栈已满并尝试向其中添加元素,则会发生溢出错误。
如果堆栈为空并且尝试从中删除元素,则会发生下溢错误。
-
动态大小堆栈
:动态大小堆栈可以动态增长或收缩。
当堆栈已满时,它会自动增加其大小以容纳新元素,而当堆栈为空时,它会减少其大小。
这种类型的堆栈是使用链表实现的,因为它允许轻松调整堆栈的大小。
除了这两种主要类型之外,堆栈还有其他几种变体,包括:
-
中缀到后缀堆栈
:这种类型的堆栈用于将中缀表达式转换为后缀表达式。
-
表达式计算堆栈
:这种类型的堆栈用于计算后缀表达式。
-
递归堆栈
:这种类型的堆栈用于跟踪计算机程序中的函数调用,并在函数返回时将控制权返回到正确的函数。
-
内存管理堆栈
:这种类型的堆栈用于存储计算机程序中程序计数器的值和寄存器的值,允许程序在函数返回时返回到先前的状态。
-
平衡括号堆栈
:这种类型的堆栈用于检查表达式中括号的平衡。
-
撤消重做堆栈
:这种类型的堆栈用于计算机程序中,允许用户撤消和重做操作。
栈的应用:
-
中缀到后缀
/前缀的转换
-
许多地方都有重做/撤消功能,例如编辑器、Photoshop。
-
网络浏览器中的前进和后退功能
-
用于许多算法,如
汉诺塔、
树遍历
、
股票跨度问题
和
直方图问题
。
-
回溯是算法设计技术之一。
回溯的一些例子包括骑士之旅问题、N-皇后问题、在迷宫中寻找出路以及所有这些问题中的类似国际象棋或西洋跳棋的问题,如果这种方式效率不高,我们会回到之前的问题状态并进入另一条道路。
为了从当前状态返回,我们需要存储以前的状态,为此我们需要一个堆栈。
-
在拓扑排序
和
强连通分量
等图算法中
-
在内存管理中,任何现代计算机都使用堆栈作为运行目的的主要管理。
计算机系统中运行的每个程序都有自己的内存分配
-
字符串反转也是栈的另一个应用。
这里每个字符都被一一插入到堆栈中。
因此,字符串的第一个字符位于堆栈底部,字符串的最后一个元素位于堆栈顶部。
在堆栈上执行弹出操作后,我们得到一个相反顺序的字符串。
-
堆栈还有助于在计算机中实现函数调用。
最后调用的函数总是最先完成。
-
堆栈还用于在文本编辑器中实现撤消/重做操作。
堆栈的实现:
堆栈可以使用数组或链表来实现。
在基于数组的实现中,推送操作是通过递增顶部元素的索引并将新元素存储在该索引处来实现的。
弹出操作是通过递减顶部元素的索引并返回存储在该索引处的值来实现的。
在基于链表的实现中,推送操作是通过使用新元素创建新节点并将当前顶节点的下一个指针设置为新节点来实现的。
出栈操作是通过将当前顶节点的next指针设置为下一个节点并返回当前顶节点的值来实现的。
堆栈在计算机科学中通常用于各种应用,包括表达式求值、函数调用和内存管理。
在表达式的计算中,堆栈可用于在处理操作数和运算符时存储它们。
在函数调用中,堆栈可用于跟踪函数调用的顺序,并在函数返回时将控制权返回到正确的函数。
在内存管理中,堆栈可用于存储计算机程序中的程序计数器的值和寄存器的值,从而允许程序在函数返回时返回到先前的状态。
总之,堆栈是一种按照 LIFO 原理运行的线性数据结构,可以使用数组或链表来实现。
可以在堆栈上执行的基本操作包括入栈、出栈和查看,并且堆栈在计算机科学中常用于各种应用,包括表达式求值、函数调用和内存管理。有两种方法实现一个堆栈 –
使用数组实现堆栈:
-
C++
-
C
-
Java
-
Python3
-
C#
-
JavaScript
C++
#include <bits/stdc++.h>
using namespace std;
#define MAX 1000
class Stack {
int
top;
public:
int
a[MAX];
Stack() { top = -1; }
bool push(int x);
int
pop();
int
peek();
bool isEmpty();
};
bool Stack::push(int x)
{
if
(top >= (MAX - 1)) {
cout << "Stack Overflow";
return false;
}
else {
a[++top] = x;
cout << x << " pushed into stack\n";
return true;
}
}
int Stack::pop()
{
if
(top < 0) {
cout << "Stack Underflow";
return 0;
}
else {
int x = a[top--];
return x;
}
}
int Stack::peek()
{
if
(top < 0) {
cout << "Stack is Empty";
return 0;
}
else {
int x = a[top];
return x;
}
}
bool Stack::isEmpty()
{
return (top < 0);
}
int main()
{
class Stack s;
s.push(10);
s.push(20);
s.push(30);
cout << s.pop() << " Popped from stack\n";
cout << "Top element is : " << s.peek() << endl;
cout <<"Elements present in stack : ";
while(!s.isEmpty())
{
cout << s.peek() <<" ";
s.pop();
}
return 0;
}
|
C
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
struct Stack {
int
top;
unsigned capacity;
int* array;
};
struct Stack* createStack(unsigned capacity)
{
struct Stack* stack = (struct Stack*)malloc(sizeof(struct Stack));
stack->capacity = capacity;
stack->top = -1;
stack->array = (int*)malloc(stack->capacity * sizeof(int));
return stack;
}
int isFull(struct Stack* stack)
{
return stack->top == stack->capacity - 1;
}
int isEmpty(struct Stack* stack)
{
return stack->top == -1;
}
void push(struct Stack* stack, int item)
{
if
(isFull(stack))
return;
stack->array[++stack->top] = item;
printf("%d pushed to stack\n", item);
}
int pop(struct Stack* stack)
{
if
(isEmpty(stack))
return INT_MIN;
return stack->array[stack->top--];
}
int peek(struct Stack* stack)
{
if
(isEmpty(stack))
return INT_MIN;
return stack->array[stack->top];
}
int main()
{
struct Stack* stack = createStack(100);
push(stack, 10);
push(stack, 20);
push(stack, 30);
printf("%d popped from stack\n", pop(stack));
return 0;
}
|
Java
class Stack {
static
final int MAX = 1000;
int
top;
int
a[] = new int[MAX];
boolean
isEmpty()
{
return (top < 0);
}
Stack()
{
top = -1;
}
boolean
push(int x)
{
if (top >= (MAX - 1)) {
System.out.println("Stack Overflow");
return false;
}
else {
a[++top] = x;
System.out.println(x + " pushed into stack");
return true;
}
}
int
pop()
{
if (top < 0) {
System.out.println("Stack Underflow");
return 0;
}
else {
int x = a[top--];
return x;
}
}
int
peek()
{
if (top < 0) {
System.out.println("Stack Underflow");
return 0;
}
else {
int x = a[top];
return x;
}
}
void
print(){
for(int i = top;i>-1;i--){
System.out.print(" "+ a[i]);
}
}
}
class Main {
public
static void main(String args[])
{
Stack s = new Stack();
s.push(10);
s.push(20);
s.push(30);
System.out.println(s.pop() + " Popped from stack");
System.out.println("Top element is :" + s.peek());
System.out.print("Elements present in stack :");
s.print();
}
}
|
Python3
from sys import maxsize
def createStack():
stack = []
return
stack
def isEmpty(stack):
return
len(stack) == 0
def push(stack, item):
stack.append(item)
print(item + " pushed to stack ")
def pop(stack):
if
(isEmpty(stack)):
return str(-maxsize -1)
return
stack.pop()
def peek(stack):
if
(isEmpty(stack)):
return str(-maxsize -1)
return
stack[len(stack) - 1]
stack = createStack()
push(stack, str(10))
push(stack, str(20))
push(stack, str(30))
print(pop(stack) + " popped from stack")
|
C#
using System;
namespace ImplementStack {
class Stack {
private
int[] ele;
private
int top;
private
int max;
public
Stack(int size)
{
ele = new int[size];
top = -1;
max = size;
}
public
void push(int item)
{
if (top == max - 1) {
Console.WriteLine("Stack Overflow");
return;
}
else {
ele[++top] = item;
}
}
public
int pop()
{
if (top == -1) {
Console.WriteLine("Stack is Empty");
return -1;
}
else {
Console.WriteLine("{0} popped from stack ", ele[top]);
return ele[top--];
}
}
public
int peek()
{
if (top == -1) {
Console.WriteLine("Stack is Empty");
return -1;
}
else {
Console.WriteLine("{0} popped from stack ", ele[top]);
return ele[top];
}
}
public
void printStack()
{
if (top == -1) {
Console.WriteLine("Stack is Empty");
return;
}
else {
for (int
i = 0; i <= top; i++) {
Console.WriteLine("{0} pushed into stack", ele[i]);
}
}
}
}
class Program {
static
void Main()
{
Stack p = new Stack(5);
p.push(10);
p.push(20);
p.push(30);
p.printStack();
p.pop();
}
}
}
|
Javascript
<script>
var t = -1;
var MAX = 1000;
var
a = Array(MAX).fill(0);
function isEmpty() {
return (t < 0);
}
function push(x) {
if (t >= (MAX - 1)) {
document.write("Stack Overflow");
return false;
} else {
t+=1;
a[t] = x;
document.write(x + " pushed into stack<br/>");
return true;
}
}
function pop() {
if (t < 0) {
document.write("Stack Underflow");
return 0;
} else {
var x = a[t];
t-=1;
return x;
}
}
function peek() {
if (t < 0) {
document.write("Stack Underflow");
return 0;
} else {
var x = a[t];
return x;
}
}
function print() {
for (i = t; i > -1; i--) {
document.write(" " + a[i]);
}
}
push(10);
push(20);
push(30);
document.write(pop() + " Popped from stack");
document.write("<br/>Top element is :" + peek());
document.write("<br/>Elements present in stack : ");
print();
</script>
|
输出
10 入栈
20 入栈
30 压入堆栈
30 从堆栈中弹出
顶部元素是:20
堆栈中存在的元素:20 10
数组实现的优点:
数组实现的缺点:
-
它不是动态的,即它不会根据运行时的需要而增长和收缩。
[但是对于动态大小的数组,例如 C++ 中的向量、Python 中的列表、Java 中的
ArrayList,堆栈也可以随着数组实现而增长和收缩]。
-
堆栈的总大小必须事先定义。
使用链表实现堆栈:
-
C++
-
C
-
Java
-
Python3
-
C#
-
JavaScript
C++
#include <bits/stdc++.h>
using namespace std;
class StackNode {
public:
int
data;
StackNode* next;
};
StackNode* newNode(int data)
{
StackNode* stackNode = new StackNode();
stackNode->data = data;
stackNode->next = NULL;
return stackNode;
}
int isEmpty(StackNode* root)
{
return !root;
}
void push(StackNode** root, int data)
{
StackNode* stackNode = newNode(data);
stackNode->next = *root;
*root = stackNode;
cout << data << " pushed to stack\n";
}
int pop(StackNode** root)
{
if
(isEmpty(*root))
return INT_MIN;
StackNode* temp = *root;
*root = (*root)->next;
int
popped = temp->data;
free(temp);
return popped;
}
int peek(StackNode* root)
{
if
(isEmpty(root))
return INT_MIN;
return root->data;
}
int main()
{
StackNode* root = NULL;
push(&root, 10);
push(&root, 20);
push(&root, 30);
cout << pop(&root) << " popped from stack\n";
cout << "Top element is " << peek(root) << endl;
cout <<"Elements present in stack : ";
while(!isEmpty(root))
{
cout << peek(root) <<" ";
pop(&root);
}
return 0;
}
|
C
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
struct StackNode {
int
data;
struct StackNode* next;
};
struct StackNode* newNode(int data)
{
struct StackNode* stackNode =
(struct StackNode*)
malloc(sizeof(struct StackNode));
stackNode->data = data;
stackNode->next = NULL;
return stackNode;
}
int isEmpty(struct StackNode* root)
{
return !root;
}
void push(struct StackNode** root, int data)
{
struct StackNode* stackNode = newNode(data);
stackNode->next = *root;
*root = stackNode;
printf("%d pushed to stack\n", data);
}
int pop(struct StackNode** root)
{
if
(isEmpty(*root))
return INT_MIN;
struct StackNode* temp = *root;
*root = (*root)->next;
int
popped = temp->data;
free(temp);
return popped;
}
int peek(struct StackNode* root)
{
if
(isEmpty(root))
return INT_MIN;
return root->data;
}
int main()
{
struct StackNode* root = NULL;
push(&root, 10);
push(&root, 20);
push(&root, 30);
printf("%d popped from stack\n", pop(&root));
printf("Top element is %d\n", peek(root));
return 0;
}
|
Java
public class StackAsLinkedList {
StackNode root;
static
class StackNode {
int data;
StackNode next;
StackNode(int data) { this.data = data; }
}
public
boolean isEmpty()
{
if (root == null) {
return true;
}
else
return false;
}
public
void push(int data)
{
StackNode newNode = new StackNode(data);
if (root == null) {
root = newNode;
}
else {
StackNode temp = root;
root = newNode;
newNode.next = temp;
}
System.out.println(data + " pushed to stack");
}
public
int pop()
{
int popped = Integer.MIN_VALUE;
if (root == null) {
System.out.println("Stack is Empty");
}
else {
popped = root.data;
root = root.next;
}
return popped;
}
public
int peek()
{
if (root == null) {
System.out.println("Stack is empty");
return Integer.MIN_VALUE;
}
else {
return root.data;
}
}
public
static void main(String[] args)
{
StackAsLinkedList sll = new StackAsLinkedList();
sll.push(10);
sll.push(20);
sll.push(30);
System.out.println(sll.pop()
+ " popped from stack");
System.out.println("Top element is " + sll.peek());
}
}
|
Python3
class StackNode:
def
__init__(self, data):
self.data =
data
self.next
= None
class Stack:
def
__init__(self):
self.root =
None
def
isEmpty(self):
return True if self.root is None
else False
def
push(self, data):
newNode = StackNode(data)
newNode.next = self.root
self.root =
newNode
print ("% d pushed to stack" % (data))
def
pop(self):
if (self.isEmpty()):
return float("-inf")
temp = self.root
self.root =
self.root.next
popped = temp.data
return popped
def
peek(self):
if self.isEmpty():
return float("-inf")
return self.root.data
stack = Stack()
stack.push(10)
stack.push(20)
stack.push(30)
print ("% d popped from stack" % (stack.pop()))
print ("Top element is % d " % (stack.peek()))
|
C#
using System;
public class StackAsLinkedList {
StackNode root;
public
class StackNode {
public int data;
public StackNode next;
public StackNode(int data) { this.data = data; }
}
public
bool isEmpty()
{
if (root == null) {
return true;
}
else
return false;
}
public
void push(int data)
{
StackNode newNode = new StackNode(data);
if (root == null) {
root = newNode;
}
else {
StackNode temp = root;
root = newNode;
newNode.next = temp;
}
Console.WriteLine(data + " pushed to stack");
}
public
int pop()
{
int popped = int.MinValue;
if (root == null) {
Console.WriteLine("Stack is Empty");
}
else {
popped = root.data;
root = root.next;
}
return popped;
}
public
int peek()
{
if (root == null) {
Console.WriteLine("Stack is empty");
return int.MinValue;
}
else {
return root.data;
}
}
public
static void Main(String[] args)
{
StackAsLinkedList sll = new StackAsLinkedList();
sll.push(10);
sll.push(20);
sll.push(30);
Console.WriteLine(sll.pop() + " popped from stack");
Console.WriteLine("Top element is " + sll.peek());
}
}
|
Javascript
<script>
var root;
class StackNode {
constructor(data) {
this.data = data;
this.next = null;
}
}
function isEmpty() {
if (root == null) {
return true;
} else
return false;
}
function push(data) {
var newNode = new StackNode(data);
if (root == null) {
root = newNode;
} else {
var temp = root;
root = newNode;
newNode.next = temp;
}
document.write(data + " pushed to stack<br/>");
}
function pop() {
var popped = Number.MIN_VALUE;
if (root == null) {
document.write("Stack is Empty");
} else {
popped = root.data;
root = root.next;
}
return popped;
}
function peek() {
if (root == null) {
document.write("Stack is empty");
return Number.MIN_VALUE;
} else {
return root.data;
}
}
push(10);
push(20);
push(30);
document.write(pop() + " popped from stack<br/>");
document.write("Top element is " + peek());
</script>
|
输出
10 压入堆栈
20 压入堆栈
30 压入堆栈
30 从堆栈中弹出
顶部元素为 20
堆栈中存在的元素:20 10
链表实现的优点:
-
堆栈的链表实现可以根据运行时的需要进行增长和收缩。
-
它被用在许多虚拟机中,比如 JVM。
链表实现的缺点:
-
由于涉及指针,需要额外的内存。
-
堆栈中不可能进行随机访问。
相关文章: