定义链栈,并实现基本操作(要求双链表实现,栈顶在链尾)
定义链栈,并实现基本操作(要求双链表实现,栈顶在链尾)
#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。
如果队伍原本没人(栈空),新来的人自己就是队头(栈底)和队尾(栈顶)。