一、什么是链栈?

链栈:是指利用链式存储结构实现的栈。

想想看栈只是栈顶来做插入和删除操作,栈顶放在链栈的头部还是尾部呢?由于单链表有头指针,而栈顶指针也是必须的,那干吗不让它俩合二为一呢,所以比较好的办法是把栈顶放在链栈的头部(如下图所示)。另外,都已经有了栈顶在头部了,单链表中比较常用的头结点也就失去了意义,通常对于链栈来说,是不需要头结点的。

对于空栈来说,链表原定义是头指针指向空,那么链栈的空其实就是 top=NULL 的时候。

链栈的结构代码如下:

typedef int ElemType; /* ElemType类型根据实际情况而定,这里假设为int */

/* 链栈结点结构 */
typedef struct StackNode
{
ElemType data;
struct StackNode *next;
}StackNode; /* 链栈结构 */
typedef struct
{
StackNode *top;
int count;
}LinkStack;

链栈的操作绝大部分都和单链表类似,只是在插入和删除上,特殊一些。

顺序栈与链栈的区别

两者在时间复杂度上是一样的,均为 O(1)。对于空间性能,顺序栈需要事先确定一个固定的长度,可能会存在内存空间浪费的问题,但它的优势是存取时定位很方便,而链栈则要求每个元素都有指针域,这同时也增加了一些内存开销,但对于栈的长度无限制。所以它们的区别和线性表中讨论的一样,如果栈的使用过程中元素变化不可预料,有时很小,有时非常大,那么最好是用链栈,反之,如果它的变化在可控范围内,建议使用顺序栈会更好一些。

二、基本操作

2.1 初始化栈操作

实现代码如下:

// 初始化栈操作
Status initStack(LinkStack **stack)
{
// 注意要给链栈分配内存
*stack = (LinkStack *)malloc(sizeof(LinkStack)); (*stack)->top = NULL; // 链栈的空其实就是 top=NULL 的时候
(*stack)->count = 0; return TRUE;
}

2.2 进栈操作

对于链栈的进栈 push 操作,假设元素值为 e 的新结点是 s,top 为栈顶指针,示意图如下图所示:

实现代码如下:

// 进栈操作
Status push(LinkStack *stack, ElemType e)
{
StackNode *s = (StackNode *)malloc(sizeof(StackNode));
s->data = e;
s->next = stack->top; // 把当前的栈顶元素赋值给新结点的直接后继,见图中①
stack->top = s; // 将新的结点s赋值给栈顶指针,见图中②
stack->count++; return TRUE;
}

2.3 出栈操作

至于链栈的出栈 pop 操作,也是很简单的三句操作。假设变量 p 用来存储要删除的栈顶结点,将栈顶指针下移一位,最后释放 p 即可,如下图所示:

// 出栈操作
Status pop(LinkStack *stack, ElemType *e)
{
StackNode *p;
if (isEmpty(stack))
return FALSE;
*e = stack->top->data;
p = stack->top; // p用来存储要删除的栈顶结点,见图中③
stack->top = stack->top->next; // 使得栈顶指针下移一位,指向后一结点,见图中④
free(p); // 释放结点p
stack->count--; return TRUE;
}

链栈的进栈 push 和出栈 pop 操作都很简单,没有任何循环操作,时间复杂度均为 O(1)。

2.4 遍历栈操作

实现代码如下:

// 遍历栈操作
Status traverseStack(LinkStack *stack)
{
StackNode *p;
p = stack->top;
while (p)
{
printf("%d ", p->data);
p = p->next;
}
printf("\n"); return TRUE;
}

三、完整程序

#include <stdio.h>
#include <stdlib.h> #define TRUE 1
#define FALSE 0
#define MAXSIZE 20 /* 存储空间初始分配量 */ typedef int Status;
typedef int ElemType; /* ElemType类型根据实际情况而定,这里假设为int */ /* 链栈结点结构 */
typedef struct StackNode
{
ElemType data;
struct StackNode *next;
}StackNode; /* 链栈结构 */
typedef struct
{
StackNode *top;
int count;
}LinkStack; Status initStack(LinkStack **stack); // 初始化栈操作
Status push(LinkStack *stack, const ElemType e); // 进栈操作
Status pop(LinkStack *stack, ElemType *e); // 出栈操作
Status traverseStack(LinkStack *stack); // 遍历栈操作
Status clearStack(LinkStack *stack); // 清空栈操作
Status isEmpty(LinkStack *stack); // 判断是否为空
Status getTop(LinkStack *stack, ElemType *e); // 获得栈顶元素
int getLength(LinkStack *stack); // 获取栈的长度 // 初始化栈操作
Status initStack(LinkStack **stack)
{
// 注意要给链栈分配内存
*stack = (LinkStack *)malloc(sizeof(LinkStack)); (*stack)->top = NULL; // 链栈的空其实就是 top=NULL 的时候
(*stack)->count = 0; return TRUE;
} // 进栈操作
Status push(LinkStack *stack, ElemType e)
{
StackNode *s = (StackNode *)malloc(sizeof(StackNode));
s->data = e;
s->next = stack->top; // 把当前的栈顶元素赋值给新结点的直接后继,见图中①
stack->top = s; // 将新的结点s赋值给栈顶指针,见图中②
stack->count++; return TRUE;
} // 出栈操作
Status pop(LinkStack *stack, ElemType *e)
{
StackNode *p;
if (isEmpty(stack))
return FALSE;
*e = stack->top->data;
p = stack->top; // p用来存储要删除的栈顶结点,见图中③
stack->top = stack->top->next; // 使得栈顶指针下移一位,指向后一结点,见图中④
free(p); // 释放结点p
stack->count--; return TRUE;
} // 遍历栈操作
Status traverseStack(LinkStack *stack)
{
StackNode *p;
p = stack->top;
while (p)
{
printf("%d ", p->data);
p = p->next;
}
printf("\n"); return TRUE;
} // 清除栈操作
Status clearStack(LinkStack *stack)
{
StackNode *p;
StackNode *q;
p = stack->top;
while (p)
{
q = p;
p = p->next;
free(q);
}
stack->count = 0; return TRUE;
} // 判断是否为空栈
Status isEmpty(LinkStack *stack)
{
return stack->count == 0 ? TRUE : FALSE;
} // 获得栈顶元素
Status getTop(LinkStack *stack, ElemType *e)
{
if (stack->top == NULL)
return FALSE;
else
*e = stack->top->data; return TRUE;
} // 获得栈的长度
int getLength(LinkStack *stack)
{
return stack->count;
} int main()
{
// 初始化栈
LinkStack *stack;
if (initStack(&stack) == TRUE)
printf("初始化链栈成功!\n\n"); // 入栈操作
for (int j = 1; j <= 10; j++)
push(stack, j);
printf("入栈操作(0-10)!\n\n"); // 出栈操作
int e;
pop(stack, &e);
printf("弹出的栈顶元素e=%d\n\n", e); // 遍历栈
printf("遍历栈,栈中元素依次为:");
traverseStack(stack);
printf("\n"); // 获得栈顶元素
getTop(stack, &e);
printf("栈顶元素 e=%d 栈的长度为%d\n\n", e, getLength(stack)); // 判断是否为空栈
printf("栈空否:%d(1:空 0:否)\n\n", isEmpty(stack)); // 清空栈
clearStack(stack);
printf("清空栈后,栈空否:%d(1:空 0:否)\n\n", isEmpty(stack)); return 0;
}

输出结果如下图所示:

参考:

《大话数据结构 - 第4章》 栈与队列

数据结构 - 链栈的实行(C语言)的更多相关文章

  1. C语言数据结构链栈(创建、入栈、出栈、取栈顶元素、遍历链栈中的元素)

    /**创建链栈*创建一个top指针代表head指针*采用链式存储结构*采用头插法创建链表*操作 创建 出栈 入栈 取栈顶元素*创建数据域的结构体*创建数据域的名称指针*使用随机函数对数据域的编号进行赋 ...

  2. 链栈的基本操作(C语言)

    栈的链式储存结构称为链栈.链栈的节点类型与链式线性表的节点类型 定义相同,不同的是它是仅在表头进行操作的单链表.链栈通常用不带头节 点的单链表来实现,栈顶指针就是链表的头指针 ,如图所示: 代码如下: ...

  3. 数据结构——链栈(link stack)

    /* linkStack.c */ /* 链栈 */ #include <stdio.h> #include <stdlib.h> #include <stdbool.h ...

  4. 数据结构 - 链栈的实现 C++

    链栈封装 C++ 使用C++对链栈进行了简单的封装,实现了栈的基本操作 封装方法: pop(),top(),size(),empty(),push() 代码已经过测试 #pragma once #in ...

  5. C#数据结构-链栈

    上一篇我们通过数组结构实现了栈结构(准确的说是栈的顺序存储结构),现在我们通过链(单链)存储栈,也就是链栈. 通常对于正向单链表来说,是从头节点开始,在链的尾部附加节点,前一个节点的指针指向附加节点: ...

  6. 数据结构 - 链队列的实行(C语言)

    数据结构-链队列的实现 1 链队列的定义 队列的链式存储结构,其实就是线性表的单链表,只不过它只能尾进头出而已, 我们把它简称为链队列.为了操作上的方便,我们将队头指针指向链队列的头结点,而队尾指针指 ...

  7. 数据结构 - 顺序栈的实行(C语言)

    数据结构-顺序栈的实现 1 顺序栈的定义 既然栈是线性表的特例,那么栈的顺序存储其实也是线性表顺序存储的简化,我们简称为顺序栈.线性表是用数组来实现的,对于栈这种只能一头插入删除的线性表来说,用数组哪 ...

  8. 算法与数据结构(二) 栈与队列的线性和链式表示(Swift版)

    数据结构中的栈与队列还是经常使用的,栈与队列其实就是线性表的一种应用.因为线性队列分为顺序存储和链式存储,所以栈可以分为链栈和顺序栈,队列也可分为顺序队列和链队列.本篇博客其实就是<数据结构之线 ...

  9. C语言实现链栈以及基本操作

    链栈,即用链表实现栈存储结构.链栈的实现思路同顺序栈类似,顺序栈是将数顺序表(数组)的一端作为栈底,另一端为栈顶:链栈也如此,通常我们将链表的头部作为栈顶,尾部作为栈底,如下下图所示: 将链表头部作为 ...

随机推荐

  1. Ubuntu 16.04安装Guake Terminal终端(使用一键唤醒功能)

    安装: sudo apt-get install guake-indicator sudo apt-get install guake 使用: 先启动guake-indicator,再启动guake. ...

  2. 个人网站开发***云服务器+Linux+域名***

    作为一个改变世界的程序猿,我们不应该只会埋头写程序修bug还得会点别的, 当然如果要是自己搞个网站玩玩,既可以锻炼技术,没事也可以和圈外的朋友吹吹 牛.因为水平有限,就弄一些最基础的看看喽,不喜勿喷. ...

  3. Javascript setTimeout(0),闭包

    setTimeout常常被用于延迟运行某个函数,使用方法为 setTimeout(function(){ - }, timeout); 有时为了进行异步处理,而使用setTimeout(functio ...

  4. hdu 5087 Revenge of LIS II ( LIS ,第二长子序列)

    链接:hdu 5087 题意:求第二大的最长升序子序列 分析:这里的第二大指的是,全部的递增子序列的长度(包含相等的), 从大到小排序后.排在第二的长度 cid=546" style=&qu ...

  5. fixedBox固定div漂浮代码 支持ie6以上大部分浏览器

    fixedBox固定div漂浮代码 支持ie6以上大部分浏览器 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//E ...

  6. 在线生成32位和16位大小写MD5密文

    MD5是一种不可逆的加密算法,全称是Message-Digest Algorithm 5(信息-摘要算法).是当前计算机领域用于确保信息传输完整一致而广泛使用的散列算法之一. MD5的典型应用是对一段 ...

  7. Oracle - 数据更新 - 事务

    /* 事务 事务是为了控制数据异步访问所使用的一种技术 就类似于java中的锁机制 synchronized,只不过功能更加强大 事务不能进行嵌套,当我们开启一个事务的之后作的每一次dml语句都属于这 ...

  8. leetcode 400 Add to List 400. Nth Digit

    Find the nth digit of the infinite integer sequence 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, ... Note:n is ...

  9. 一步一步学Silverlight 2系列(3):界面布局

    述 Silverlight 2 Beta 1版本发布了,无论从Runtime还是Tools都给我们带来了很多的惊喜,如支持框架语言Visual Basic, Visual C#, IronRuby, ...

  10. silverlight DataGrid 显示篇

    silverlight DataGrid 显示篇 分类: Silverlight2012-05-12 21:55 693人阅读 评论(0) 收藏 举报 datagridsilverlightbindi ...