定义链栈,并实现基本操作(要求双链表实现,栈顶在链尾)
定义链栈,并实现基本操作(要求双链表实现,栈顶在链尾)
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
// 双向链表节点定义
typedef struct DNode {
int data; // 数据域
struct DNode *prev; // 指向前驱节点(栈底方向)
struct DNode *next; // 指向后继节点(栈顶方向)
} DNode;
// 链栈结构定义(栈顶在链尾,用栈顶指针和栈底指针标识)
typedef struct {
DNode *bottom; // 栈底指针(固定在链表头部,方便定位)
DNode *top; // 栈顶指针(指向链表尾部,操作端)
int size; // 栈中元素个数(可选,优化操作效率)
} DStack;
// 初始化后栈底和栈顶均为 NULL,size 为 0
bool InitStack(DStack *stack) {
if (stack == NULL) return false; // 传入的栈指针无效
stack->bottom = NULL;
stack->top = NULL;
stack->size = 0;
return true;
}
// 将元素 val 压入栈顶(链尾)
bool Push(DStack *stack, int val) {
if (stack == NULL) return false; // 栈未初始化
// 创建新节点
DNode *newNode = (DNode*)malloc(sizeof(DNode));
if (newNode == NULL) return false; // 内存分配失败
newNode->data = val;
newNode->next = NULL; // 新节点为链尾,next 指向 NULL
// 若栈为空,新节点既是栈底也是栈顶
if (stack->size == 0) {
newNode->prev = NULL;
stack->bottom = newNode;
stack->top = newNode;
} else {
// 栈非空,新节点前驱指向原栈顶,原栈顶 next 指向新节点
newNode->prev = stack->top;
stack->top->next = newNode;
stack->top = newNode; // 栈顶更新为新节点
}
stack->size++;
return true;
}
// 弹出栈顶元素(链尾),通过 val 传出数据
bool Pop(DStack *stack, int *val) {
if (stack == NULL || val == NULL || stack->size == 0) {
return false; // 栈未初始化 / 空栈 / 传出指针无效
}
DNode *topNode = stack->top; // 记录当前栈顶节点
*val = topNode->data; // 传出栈顶数据
// 若栈中只有一个元素,弹出后栈为空
if (stack->size == 1) {
stack->bottom = NULL;
stack->top = NULL;
} else {
// 栈非空,栈顶前移到原栈顶的前驱节点,新栈顶 next 置空
stack->top = topNode->prev;
stack->top->next = NULL;
}
// 释放原栈顶节点内存
free(topNode);
stack->size--;
return true;
}
// 栈为空返回 true,否则返回 false
bool IsEmpty(DStack *stack) {
if (stack == NULL) return true; // 未初始化视为空栈
return stack->size == 0;
}
// 获取栈顶元素数据,通过 val 传出
bool GetTop(DStack *stack, int *val) {
if (stack == NULL || val == NULL || stack->size == 0) {
return false; // 栈未初始化 / 空栈 / 传出指针无效
}
*val = stack->top->data;
return true;
}
如果没有看懂怎么插入的
没关系!我们用 图文拆解 的方式,一步步讲清双向链表链栈(栈顶在链尾)的入栈操作,就像搭积木一样直观~
先明确核心设定
• 栈结构:DStack 里有 bottom(栈底指针)、top(栈顶指针)、size(元素个数)。
• 栈顶在链尾:新元素永远加在链表的最后(链尾),这个位置就是栈顶。
• 双向链表节点:每个节点有 prev(指向前一个节点)、next(指向后一个节点)、data(数据)。
入栈操作分两种情况拆解
情况1:栈为空时入栈(第一次加元素)
假设初始栈是空的:bottom = NULL,top = NULL,size = 0。
现在要入栈元素 10:
1. 创建新节点:新节点 p 的 data = 10,prev = NULL(暂时没前节点),next = NULL(暂时没后节点)。
2. 关联栈结构:
◦ 因为栈空,新节点既是栈底也是栈顶,所以 stack->bottom = p,stack->top = p。
◦ size 从 0 变成 1。
效果:链表只有一个节点,栈底和栈顶都指向它。
栈底 bottom → [10] ← 栈顶 top
prev: NULL,next: NULL
情况2:栈非空时入栈(已有元素,再加新元素)
假设栈里已有元素 10(栈顶在 10 这个节点),现在要入栈 20:
1. 创建新节点:新节点 p 的 data = 20,next = NULL(它将是新链尾,后面没节点)。
2. 关联前节点:
◦ 新节点的 prev 要指向当前栈顶(也就是 10 这个节点),即 p->prev = stack->top。
◦ 原来的栈顶节点(10)的 next 要指向新节点,即 stack->top->next = p(让链表连起来)。
3. 更新栈顶:栈顶移到新节点,stack->top = p。
4. 更新大小:size 从 1 变成 2。
效果:链表变成 10 → 20,栈顶现在指向 20(链尾)。
栈底 bottom → [10] → [20] ← 栈顶 top
prev: NULL prev: [10]
next: [20] next: NULL
再入栈一个元素 30(强化理解)
按上面的逻辑:
• 新节点 p 的 data=30,prev 指向当前栈顶 20,20 的 next 指向 30。
• 栈顶更新为 30,size=3。
最终链表:
栈底 → [10] → [20] → [30] ← 栈顶
prev:NULL prev:[10] prev:[20]
next:[20] next:[30] next:NULL
入栈操作的核心逻辑总结
就像在队伍末尾加人:
1. 新来的人(新节点)左手(prev)拉住当前队尾的人(原栈顶)。
2. 当前队尾的人(原栈顶)右手(next)拉住新来的人。
3. 队尾标志(栈顶指针 top)移到新来的人身上。
4. 队伍人数(size)加 1。
如果队伍原本没人(栈空),新来的人自己就是队头(栈底)和队尾(栈顶)。

浙公网安备 33010602011771号