链式栈
链式栈和顺序栈的逻辑相同都是先进后出,但是使用的是不同的存储结构.顺序结构是连续固定的,而链式结构是不连续动态分配的.
C语言
#include<stdio.h>
#include<stdlib.h>
typedef struct Node {
int data;//链式栈的内存不连续,一个结点只存一个数
struct Node* next;//下一个节点的地址,形成链式结构
}SNode,*LinkStack;//:LinkStack 等同于 SNode* SNode是普通节点 LinkStack是头节点也是栈顶
LinkStack InitStack() {
SNode* head = (SNode*)malloc(sizeof(SNode));
if (head == NULL) {
printf_s("内存申请失败\n");
return head;
}
head->next = NULL;
return head;
}
//入栈也就是头插法
LinkStack Push(LinkStack s, int k) {
SNode* p = (SNode*)malloc(sizeof(SNode));
if (s == NULL) {
printf_s("内存申请失败\n");
return s;
}
p->next = s->next;
s->next = p;
p->data = k;
return s;
}
//判空:top->next==NULL
int IsEmpety(LinkStack s) {
if (s->next == NULL) {
return 1;
}
return 0;
}
//出栈:先判空,删除首元结点
LinkStack Pop(LinkStack s) {
if (IsEmpety(s)) {
printf_s("栈空,无法删除\n");
return s;
}
SNode* p = s->next;
s->next = p->next;
free(p);
p = NULL;
return s;
}
int Get(LinkStack s) {
if (IsEmpety(s)) {
printf_s("栈空无法获取\n");
return -1;
}
return s->next->data;
}
int main()
{
LinkStack s = InitStack();
s = Push(s, 1);
s = Push(s, 2);
s = Push(s, 3);
s = Pop(s);
s = Pop(s);
s = Pop(s);
printf("%d\n", Get(s));
return 0;
}
链式栈的操作和顺序栈一样初始化 入栈 判空 出栈 获取栈顶元素,但是因为存储结构不同操作的方式并不相同.
用结构体包装栈中的节点,由数据域和指针域组成,指针域记录了下一个节点的地址,从而形成了链式结构.我们把头节点视为栈顶,为了区别头节点和普通节点使用typedef关键字取别名.这里的LinkStack 等同于 SNode*,LinkStack是头节点也是栈顶.
1.初始化就是构建一个含头节点的空链栈,用malloc申请内存然后判空,内存申请成功后将头指针置空这样一个空栈就初始化了,头节点是没有数据域的不用对head->data进行操作.
2.入栈的操作就是链表中的头插法,先malloc一个普通节点p,判空后将头节点的后继接到p后再将p接到头节点后就完成了元素的入栈.再更新p节点的数据域即可.
3.判空操作非常的简单直接判断头指针的后继是否为空即可,为空返回1,反之返回0.
4.出栈操作就是删除首元节点,先对链式栈判空,对他进行逻辑上的删除,先用一个临时节点p记录这个节点的地址,然后把p的后继接到头结点后就删除成功了.然后把p节点的内存free掉再把p指针置空.
5.获取栈顶元素就是获取首元节点的数据域,判空后直接返回即可.
链栈就是用链式结构来进行栈的操作,所以实现链栈前得熟悉链表中的操作.
C++
#include<iostream>
using namespace std;
class LinkStack {
private:
struct Node{
int data;
struct Node* next;
};
Node* top;
public:
LinkStack() {
top = new Node;
top->next = nullptr;
}
void Push(int k) {
Node* p = new Node;
p->next = top->next;
top->next = p;
p->data = k;
}
bool isEmpty() const {
return top->next == nullptr;
}
void Pop() {
if (this->isEmpty()) {
cout << "栈空无法删除" << endl;
return;
}
Node* p = top->next;
top->next = p->next;
delete p;
p = nullptr;
return;
}
int getTop() {
if (this->isEmpty()) {
cout << "栈空无法获取" << endl;
return -1;
}
return top->next->data;
}
~LinkStack() {
while (!isEmpty()) {
Pop();
}
delete top;
top = nullptr;
}
};
// 测试主函数
int main() {
LinkStack s;
s.Push(1);
s.Push(2);
s.Push(3);
s.Pop();
s.Pop();
s.Pop();
cout << s.getTop() << endl;
return 0;
}
C++中直接把节点封装进类中保证安全,用构造函数初始化,析构函数来释放内存.这次用CPP印象比较深的是判空函数,我们不需要对这个栈进行修改只读不改就可以用const关键字来修饰函数,来确保安全.
👉 只读函数加 const,修改函数不加 const