数据结构与算法之栈和队列基础——顺序栈与链栈(C与C++双人打)

后进先出(Last In First Out, LIFO)的线性序列,称为“栈”。栈也是一种线性表,只不过它是操作受限的线性表,只能在一端进出操作。进出的一端称为栈顶(top),另一端称为栈底(base)。

栈可以用顺序存储,也可以用链式存储,分别称为顺序栈和链栈。

顺序栈

顺序栈的存储方式

在这里插入图片描述
顺序栈需要两个指针,base指向栈底,top指向栈顶。

顺序栈的结构体定义(动态分配的形式)

在这里插入图片描述
栈定义好了之后,还要先定义一个最大的分配空间,顺序结构都是如此,需要预先分配空间,因此可以采用宏定义。
在这里插入图片描述
上面的结构体定义采用了动态分配的形式,也可以采用静态分配的形式

顺序栈的结构体定义(静态分配的形式)

使用一个定长数组存储数据元素,一个整型下标记录栈顶元素的位置。静态分配的顺序栈结构体定义如图所示
在这里插入图片描述
注意:栈只能在一端操作,后进先出,是人为规定的,也就是说不允许在中间查找、取值、插入、删除等操作。顺序栈本身是顺序存储的,有人就想:我偏要从中间取一个元素,不行吗?那肯定可以,但是这样做,就不是栈了。

下面讲解顺序栈的初始化、入栈,出栈,取栈顶元素等基本操作。顺序栈采用动态存储形式,元素以int类型为例

顺序栈的初始化

初始化一个空栈,动态分配Maxsize大小的空间,用S.top和S.base指向该空间的基地址
在这里插入图片描述

bool InitStack(SqStack &S)
{
    
    
	S.base = new int[Maxsize];
	if(!S.base)
	{
    
    
		return false;
	}
	S.top = S.base;
	return true;
}

入栈

入栈前要判断是否栈满,如果栈已满,则入栈失败;否则将元素放入栈顶,栈顶指针向上移动一个位置(top++)

算法图解

在这里插入图片描述

bool Push(SqStack &S, int e)
{
    
    
	if(S.top - S.base == Maxsize)
	{
    
    
		return false;
	}
	*S.top++ = e;		//等价于*S.top = e;S.top++; 
	return true;
}

出栈

出栈前要判断是否栈空,如果栈是空的,则出栈失败;否则将栈顶元素暂存给一个变量,栈顶指针向下移动一个位置(top–)。

算法图解

在这里插入图片描述
在这里插入图片描述

bool Pop(SqStack &S, int &e)
{
    
    
	if(S.base == S.top)
	{
    
    
		return false;
	}
	e = *--S.top;
	return true; 
} 

取栈顶元素

取栈顶元素和出栈不同。取栈顶元素只是把栈顶元素复制一份,栈顶指针未移动,栈内元素个数未变。而出栈是指栈顶指针向下移动一个位置,栈内不再包含这个元素。

算法图解

例如,如图所示,取栈顶元素*(S.top-1),即元素4,取值后S.top指针没有改变,栈内元素的个数也没有改变。
在这里插入图片描述

int GetTop(SqStack S)
{
    
    
	if(S.top != S.base)
	{
    
    
		return *(S.top - 1);
	}
	else
	{
    
    
		return -1;
	}
} 

顺序栈我也要玩一玩

#include<bits/stdc++.h>
using namespace std;

#define Maxsize 10

typedef int ElemType;
typedef struct SqStack{
    
    
	ElemType *base;
	ElemType *top;
//	int stacksize;
}SqStack;

bool InitStack(SqStack &S)
{
    
    
	S.base = new int[Maxsize];
	if(!S.base)
	{
    
    
		return false;
	}
	S.top = S.base;
//	S.stacksize = Maxsize;
	return true;
}

bool Push(SqStack &S, int e)
{
    
    
	if(S.top - S.base == Maxsize)
	{
    
    
		return false;
	}
	*S.top++ = e;		//等价于*S.top = e;S.top++; 
	return true;
}

bool Pop(SqStack &S, int &e)
{
    
    
	if(S.base == S.top)
	{
    
    
		return false;
	}
	e = *--S.top;
	return true; 
} 
 
int GetTop(SqStack S)
{
    
    
	if(S.top != S.base)
	{
    
    
		return *(S.top - 1);
	}
	else
	{
    
    
		return -1;
	}
} 
 
int main()
{
    
    
	SqStack sq;
	InitStack(sq);
	for(int i = 0; i < 6; i++)
	{
    
    
		Push(sq, i);
	}
	int tmp;
	Pop(sq, tmp);
	cout << tmp << endl;
	cout << GetTop(sq) << endl; 
	return 0;
} 

在这里插入图片描述

链栈

在这里插入图片描述
顺序栈是分配一段连续的空间,需要两个指针:base指向栈底,top指向栈顶。而链栈每个节点的地址是不连续的,只需要一个栈顶指针即可。

链栈的每个节点都包含两个域:数据域和指针域。是不是和单链表一模一样?可以把链栈看作一个不带头节点的单链表,但只能在头部进行插入、删除、取值等操作,不可以在中间和尾部操作。

链栈的结构体定义

在这里插入图片描述
链栈的节点定义和单链表一样,只不过它只能在栈顶那一端操作而已。下面讲解链栈的初始化、入栈、出栈、取栈顶元素等基本操作(元素以int类型为例)。

链栈的初始化

初始化一个空的链栈是不需要头节点的,因此只需要让栈顶指针为空即可

bool InitStack(LinkStack &S)
{
    
    
	S = NULL;
	return true;
}

入栈

入栈是将新元素节点压入栈顶。因为链栈中第一个节点为栈顶,因此将新元素节点插到第一个节点的前面,然后修改栈顶指针指向新节点即可。有点像摞盘子,将新节点摞到栈顶之上,新节点成为新的栈顶。

算法图解

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

bool Push(LinkStack &S, int e)
{
    
    
	LinkStack p;
	p = new Snode;
	p->data = e;
	p->next = S;
	S = p;
	return true;
} 

出栈

出栈就是把栈顶元素删除,让栈顶指针指向下一个节点,然后释放该节点空间,如图所示。
在这里插入图片描述

赋值解释

① p=S:将S的地址赋值给p,即p指向栈顶元素节点。

② S=S->next:将S的后继节点的地址赋值给S,即S指向它的后继节点。

③ delete p:最后释放p指向的节点空间,即delete p。

bool Pop(LinkStack &S, int &e)
{
    
    
	LinkStack p;
	if(S == NULL)
	{
    
    
		return false;
	}
	e = S->data;
	p = S;
	S = S->next;
	delete p;
	return true;
}

取栈顶元素

取栈顶元素和出栈不同,取栈顶元素只是把栈顶元素复制一份,栈顶指针并没有改变。而出栈是指删除栈顶元素,栈顶指针指向了下一个元素。

int GetTop(LinkStack S)
{
    
    
	if(S != NULL)
	{
    
    
		return S->data;
	}
	else
	{
    
    
		return -1;
	}
}

顺序栈和链栈的所有基本操作都只需要常数时间,所以在时间效率上难分伯仲。

在空间效率方面,顺序栈需要预先分配固定长度的空间,有可能造成空间浪费或溢出;链栈每次只分配一个节点,除非没有内存,否则不会出现溢出,但是每个节点需要一个指针域,结构性开销增加。

因此,如果元素个数变化较大,可以采用链栈;反之,可以采用顺序栈。在实际应用中,顺序栈比链栈应用更广泛。

代码敲起来,链栈随便玩儿

#include<bits/stdc++.h>
using namespace std;

typedef int ElemType;
typedef struct Snode{
    
    
	ElemType data;
	struct Snode *next;
}Snode, *LinkStack;

bool InitStack(LinkStack &S)
{
    
    
	S = NULL;
	return true;
}

bool Push(LinkStack &S, int e)
{
    
    
	LinkStack p;
	p = new Snode;
	p->data = e;
	p->next = S;
	S = p;
	return true;
} 

bool Pop(LinkStack &S, int &e)
{
    
    
	LinkStack p;
	if(S == NULL)
	{
    
    
		return false;
	}
	e = S->data;
	p = S;
	S = S->next;
	delete p;
	return true;
}

int GetTop(LinkStack S)
{
    
    
	if(S != NULL)
	{
    
    
		return S->data;
	}
	else
	{
    
    
		return -1;
	}
}

int main()
{
    
    
	LinkStack S;
	InitStack(S);
	for(int i = 0; i < 6; i++)
	{
    
    
		Push(S, i);
	}
	int tmp;
	Pop(S, tmp);
	cout << tmp << endl;
	cout << GetTop(S) << endl;

	return 0;
} 

在这里插入图片描述

之后我会持续更新,如果喜欢我的文章,请记得一键三连哦,点赞关注收藏,你的每一个赞每一份关注每一次收藏都将是我前进路上的无限动力 !!!↖(▔▽▔)↗感谢支持!

猜你喜欢

转载自blog.csdn.net/qq_44631615/article/details/117447380