前言:
本专栏属于数据结构相关内容,附带一些代码加深对一些内容的理解,为方便读者观看,本专栏内的所有文章会同时附带C语言和Python对应的代码,(可自行通过目录跳转到对应的部分)辅助不同主修语言的读者去更好的理解对应的内容,若是代码0基础的读者,可先去博主其他专栏学习一下基础的语法及知识点:
魔法天才的跳转链接:
Python语言:python1_Gu_shiwww的博客-CSDN博客
其他数据结构内容可见:数据结构_Gu_shiwww的博客-CSDN博客
链式栈的特点
逻辑结构:线性结构
物理结构:链式结构
数据特点:后进先出
1 基本概念
栈具有后进先出的特点,我们使用链表来实现栈,即链式栈。那么栈顶是入栈和出栈的地方,单向链表有头有尾,那我们将链表的头作为栈顶还是链表的尾作为栈顶呢?如果每次在链表的尾部进行插入或删除,就需要遍历整个链表来找到尾结点即终端结点。而在头部即起始结点进行插入或删除时,仅需头引用找到链表的起始结点,而无需遍历整个链表。
所以链式栈的入栈、出栈都是通过对链表进行头插、头删来实现

1.* 顺序栈和链式栈的区别
存储结构不同,那么实现的方式也不同,顺序栈用数组也就是顺序表实现,内存连续,长度固定; 链式栈用链表实现,内存不连续,长度不固定。
2 编程实现链式栈
C语言编程实现链式栈
C.* 函数接口
cpp
#include <stdio.h>
#include <stdlib.h>
typedef int datatype;
typedef struct node_t
{
datatype data;
struct node_t *next;
} link_node_t, *link_node_p;
//创建一个空栈
void createEmptyLinkStack(link_node_p *p); //p=&top
//入栈, data是入栈的数据
int pushLinkStack(link_node_p *p, datatype data);
//判断栈是否为空
int isEmptyLinkStack(link_node_p top);
//出栈
datatype popLinkStack(link_node_p *p); //p=&top =>*p=top
//求栈的长度
int lengthLinkStack(link_node_p top);
C.1 创建一个空栈
cpp
//创建一个空栈
void createEmptyLinkStack(link_node_p *p) //p=&top
{
*p = NULL; //*p = top =NULL
}
将定义好的某结构体内的数据赋为NULL,即初始化空
C.2 入栈
python
//入栈, data是入栈的数据
int pushLinkStack(link_node_p *p, datatype data) //参数上之所以采用二级指针,因为我们要//随着入栈添加新的节点作为头,top需要永远指向当前链表的头//那么修改main函数中的top,我们能采用地址传递。
{
//1. 新建节点
link_node_p pnew = (link_node_p)malloc(sizeof(link_node_t));
if (NULL == pnew)
{
perror("pnew malloc err");
return -1;
}
//2. 初始化新节点
pnew->data = data;
//3. 连接新节点到链表
pnew->next = *p; //p=&top ==> *p=top
//4. 移动弄栈针到新节点
*p = pnew; //也就是top指向了pnew,因为*p就是top
return 0;
}
入栈之前要首先用malloc开辟一个节点,为提高代码健壮性判断节点是否开辟成功。将入栈传入的形参data值传入新开辟的pnew的data域,然后将传入的p解引用连接到pnew的next域,这样就实现了链表栈的连接操作,此时指针p的功能可以视作顺序表中的top指针,从该位置开始遍历整个栈且完成入栈出栈操作。故p指针也要向前移动到pnew出。因为传入的p是一个指针变量的地址,解引用之后表示指针变量的存储空间,可以存储一个指针,也就可以将pnew传给p
C.3 判空
cpp
//判断栈是否为空
int isEmptyLinkStack(link_node_p top)
{
return top == NULL;
}
top是一个结构体指针变量,外部传参时传递的是链式栈的top指针。当这个指针的指向为空的时候,证明栈内无任何数据。可将此判断结果作为函数的返回值。(注意,链式栈理论上来讲可无限将节点链接起来,不需要判满)
C.4 出栈
cpp
//出栈
datatype popLinkStack(link_node_p *p) //p=&top =>*p=top
{
//1. 判空
if (isEmptyLinkStack(*p))
{
printf("is empy\n");
return -1;
}
//2. 设指针pdel指向要释放节点
link_node_p pdel = *p;
//3. 设变量temp保存要出栈数据
datatype temp = (*p)->data; //或者temp = pdel->data
//4.将栈针向后移动一个单位
*p = (*p)->next; //*p = pdel->next;
//5. 释放要删除节点
free(pdel);
//6.返回要出栈数据
return temp;
}
链式栈与顺序栈不同,出栈意味着元素被舍弃,不能进行逻辑删除,同时要确定节点在内部的物理地址中也被删除。出栈操作前要先对栈进行判空操作,空栈无数据可出。
在函数内部定义一个结构体指针pdel指向栈顶,用一个变量temp存储栈顶元素(*p)->data,然后将指针p向后移动。此时原来的栈顶结点与链式栈断开,但是依旧可以通过pdel访问到该节点。最后free(pdel)完成出栈操作(若无pdel,被断开的节点就无法被找到,可能会造成内存泄露)
C.5 求栈长度
cpp
//求栈的长度
int lengthLinkStack(link_node_p top)
{
int len = 0;
while (top != NULL)
{
len++;
top = top->next;
}
return len;
}
传入栈顶指针(传入的虽然是地址,但是该变量在外部主函数中也是一个指针变量的形式,改变函数内部的这个指针不会对主函数中的指针发生改变,若想改变主函数中指针的指向,需要获取到该指针变量的地址,定义一个二级指针,且在外部调用的时候,需要用&获取指针变量的地址。),在函数内部定义一个变量len作为栈长度的存储变量。当top的内容不为空的时候,需要将top指向后移,直到top不再指向具体内容的时候循环停止。最终函数返回len,即整个链式栈的长度
C.6 完整代码(可运行)
cpp
#include <stdio.h>
#include <stdlib.h>
typedef int datatype;
typedef struct node_t
{
datatype data;
struct node_t *next;
} link_node_t, *link_node_p;
//创建一个空栈
void createEmptyLinkStack(link_node_p *p) //p=&top
{
*p = NULL; //*p = top =NULL
}
//入栈, data是入栈的数据
int pushLinkStack(link_node_p *p, datatype data) //参数上之所以采用二级指针,因为我们要//随着入栈添加新的节点作为头,top需要永远指向当前链表的头//那么修改main函数中的top,我们能采用地址传递。
{
//1. 新建节点
link_node_p pnew = (link_node_p)malloc(sizeof(link_node_t));
if (NULL == pnew)
{
perror("pnew malloc err");
return -1;
}
//2. 初始化新节点
pnew->data = data;
//3. 连接新节点到链表
pnew->next = *p; //p=&top ==> *p=top
//4. 移动弄栈针到新节点
*p = pnew; //也就是top指向了pnew,因为*p就是top
return 0;
}
//判断栈是否为空
int isEmptyLinkStack(link_node_p top)
{
return top == NULL;
}
//出栈
datatype popLinkStack(link_node_p *p) //p=&top =>*p=top
{
//1. 判空
if (isEmptyLinkStack(*p))
{
printf("is empy\n");
return -1;
}
//2. 设指针pdel指向要释放节点
link_node_p pdel = *p;
//3. 设变量temp保存要出栈数据
datatype temp = (*p)->data; //或者temp = pdel->data
//4.将栈针向后移动一个单位
*p = (*p)->next; //*p = pdel->next;
//5. 释放要删除节点
free(pdel);
//6.返回要出栈数据
return temp;
}
//求栈的长度
int lengthLinkStack(link_node_p top)
{
int len = 0;
while (top != NULL)
{
len++;
top = top->next;
}
return len;
}
int main(int argc, char const *argv[])
{
link_node_p top;
createEmptyLinkStack(&top); //调用函数结束后 top=NULL
pushLinkStack(&top, 1);
pushLinkStack(&top, 2);
pushLinkStack(&top, 3);
printf(" len = %d\n", lengthLinkStack(top));
while (!isEmptyLinkStack(top))
printf("%d\n", popLinkStack(&top)); //3 2 1
return 0;
}
Python语言编写链式栈
P.* 函数接口
python
class Node:
"""链式栈节点类"""
def __init__(self, data):
self.data = data
self.next = None
class LinkStack:
def __init__(self):
self.top = None
# 1.入栈,data是入栈的数据
def push(self, data):
pass
# 2.判断栈是否为空
def is_empty(self):
pass
# 3.出栈
def pop(self):
pass
# 4.清空栈
def clear(self):
pass
# 5.求栈的长度
def length(self):
pass
# 6.获取栈顶数据,不是出栈,不需要移动top
def get_top(self):
pass
if __name__ == '__main__':
pass
P.1 入栈
每次都将新节点连接到无头单向链表的头
栈顶top永远指向无头单向链表的头,栈空时除外

python
# 1.入栈,data是入栈的数据
def push(self, data):
new_node = Node(data)
new_node.next = self.top
self.top = new_node
建立一个新节点new_node存储传入的形参数据data,用指针连接到top后面
P.2 判空
python
# 3.判断栈是否为空
def is_empty(self):
return self.top is None
即判断top是否为空,可将判断式的结果作为函数返回值
P.3 出栈

python
# 4.出栈
def pop(self):
if self.is_empty():
print("pop error")
return
data = self.top.data
self.top = self.top.next
return data
首先容错判断,若栈为空则无数据可出栈。定义一个data变量存储出栈节点的数据,将top指针移到top的下一个位置,data作为返回值可以看看本次出栈的数据是什么(Python中不需要单独释放被断开的节点,会用垃圾回收机制自动回收)
P.4 清空栈
python
# 5.清空栈
def clear(self):
self.top = None
清空,只需要将top的数据赋为空,就可以将top与后续所有的节点断开,后续节点会由Python的垃圾回收机制自动回收
P.5 求栈长
python
# 6.求栈的长度
def length(self):
current = self.top
length = 0
while current:
length += 1
current = current.next
return length
首先current指向top,从top开始依次遍历,定义一个length变量,当current内不为空的时候,while循环执行,current后移,length+1,最后把length的值作为返回值
P.6 获取栈顶数据
python
# 7.获取栈顶数据,不是出栈,不需要移动top
def get_top(self):
if self.is_empty():
print("get_top error")
return
return self.top.data
首先容错判断,若栈为空则无栈顶无数据。若栈不空直接返回top对应的data域中的数据
P.7 完整代码(可执行)
python
class Node:
"""链式栈节点类"""
def __init__(self, data):
self.data = data
self.next = None
class LinkStack:
def __init__(self):
self.top = None
# 1.入栈,data是入栈的数据
def push(self, data):
new_node = Node(data)
new_node.next = self.top
self.top = new_node
# 2.判断栈是否为空
def is_empty(self):
return self.top is None
# 3.出栈
def pop(self):
if self.is_empty():
print("pop error")
return
data = self.top.data
self.top = self.top.next
return data
# 4.清空栈
def clear(self):
self.top = None
# 5.求栈的长度
def length(self):
current = self.top
length = 0
while current:
length += 1
current = current.next
return length
# 6.获取栈顶数据,不是出栈,不需要移动top
def get_top(self):
if self.is_empty():
print("get_top error")
return
return self.top.data
if __name__ == '__main__':
link_stack = LinkStack()
for i in range(6):
link_stack.push(i)
print("top_data",link_stack.get_top())
print("stack_length",link_stack.length())
for _ in range(6):
print("data", link_stack.pop())