定义链栈,并实现基本操作(要求双链表实现,栈顶在链尾)

定义链栈,并实现基本操作(要求双链表实现,栈顶在链尾)

#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。

如果队伍原本没人(栈空),新来的人自己就是队头(栈底)和队尾(栈顶)。

posted @ 2025-08-21 16:53  清初  阅读(49)  评论(0)    收藏  举报