当前位置:   article > 正文

[数据结构 10] 栈之链栈

链栈

一、什么是链栈?

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

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

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

链栈的结构代码如下:

  1. typedef int ElemType; /* ElemType类型根据实际情况而定,这里假设为int */
  2. /* 链栈结点结构 */
  3. typedef struct StackNode
  4. {
  5. ElemType data;
  6. struct StackNode *next;
  7. }StackNode;
  8. /* 链栈结构 */
  9. typedef struct
  10. {
  11. StackNode *top;
  12. int count;
  13. }LinkStack;

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

顺序栈与链栈的区别

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

二、基本操作

2.1 初始化栈操作

实现代码如下:

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


2.2 进栈操作

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

实现代码如下:

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


2.3 出栈操作

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

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

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


2.4 遍历栈操作

实现代码如下:

  1. // 遍历栈操作
  2. Status traverseStack(LinkStack *stack)
  3. {
  4. StackNode *p;
  5. p = stack->top;
  6. while (p)
  7. {
  8. printf("%d ", p->data);
  9. p = p->next;
  10. }
  11. printf("\n");
  12. return TRUE;
  13. }

三、完整程序

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #define TRUE 1
  4. #define FALSE 0
  5. #define MAXSIZE 20 /* 存储空间初始分配量 */
  6. typedef int Status;
  7. typedef int ElemType; /* ElemType类型根据实际情况而定,这里假设为int */
  8. /* 链栈结点结构 */
  9. typedef struct StackNode
  10. {
  11. ElemType data;
  12. struct StackNode *next;
  13. }StackNode;
  14. /* 链栈结构 */
  15. typedef struct
  16. {
  17. StackNode *top;
  18. int count;
  19. }LinkStack;
  20. Status initStack(LinkStack **stack); // 初始化栈操作
  21. Status push(LinkStack *stack, const ElemType e); // 进栈操作
  22. Status pop(LinkStack *stack, ElemType *e); // 出栈操作
  23. Status traverseStack(LinkStack *stack); // 遍历栈操作
  24. Status clearStack(LinkStack *stack); // 清空栈操作
  25. Status isEmpty(LinkStack *stack); // 判断是否为空
  26. Status getTop(LinkStack *stack, ElemType *e); // 获得栈顶元素
  27. int getLength(LinkStack *stack); // 获取栈的长度
  28. // 初始化栈操作
  29. Status initStack(LinkStack **stack)
  30. {
  31. // 注意要给链栈分配内存
  32. *stack = (LinkStack *)malloc(sizeof(LinkStack));
  33. (*stack)->top = NULL; // 链栈的空其实就是 top=NULL 的时候
  34. (*stack)->count = 0;
  35. return TRUE;
  36. }
  37. // 进栈操作
  38. Status push(LinkStack *stack, ElemType e)
  39. {
  40. StackNode *s = (StackNode *)malloc(sizeof(StackNode));
  41. s->data = e;
  42. s->next = stack->top; // 把当前的栈顶元素赋值给新结点的直接后继,见图中①
  43. stack->top = s; // 将新的结点s赋值给栈顶指针,见图中②
  44. stack->count++;
  45. return TRUE;
  46. }
  47. // 出栈操作
  48. Status pop(LinkStack *stack, ElemType *e)
  49. {
  50. StackNode *p;
  51. if (isEmpty(stack))
  52. return FALSE;
  53. *e = stack->top->data;
  54. p = stack->top; // p用来存储要删除的栈顶结点,见图中③
  55. stack->top = stack->top->next; // 使得栈顶指针下移一位,指向后一结点,见图中④
  56. free(p); // 释放结点p
  57. stack->count--;
  58. return TRUE;
  59. }
  60. // 遍历栈操作
  61. Status traverseStack(LinkStack *stack)
  62. {
  63. StackNode *p;
  64. p = stack->top;
  65. while (p)
  66. {
  67. printf("%d ", p->data);
  68. p = p->next;
  69. }
  70. printf("\n");
  71. return TRUE;
  72. }
  73. // 清除栈操作
  74. Status clearStack(LinkStack *stack)
  75. {
  76. StackNode *p;
  77. StackNode *q;
  78. p = stack->top;
  79. while (p)
  80. {
  81. q = p;
  82. p = p->next;
  83. free(q);
  84. }
  85. stack->count = 0;
  86. return TRUE;
  87. }
  88. // 判断是否为空栈
  89. Status isEmpty(LinkStack *stack)
  90. {
  91. return stack->count == 0 ? TRUE : FALSE;
  92. }
  93. // 获得栈顶元素
  94. Status getTop(LinkStack *stack, ElemType *e)
  95. {
  96. if (stack->top == NULL)
  97. return FALSE;
  98. else
  99. *e = stack->top->data;
  100. return TRUE;
  101. }
  102. // 获得栈的长度
  103. int getLength(LinkStack *stack)
  104. {
  105. return stack->count;
  106. }
  107. int main()
  108. {
  109. // 初始化栈
  110. LinkStack *stack;
  111. if (initStack(&stack) == TRUE)
  112. printf("初始化链栈成功!\n\n");
  113. // 入栈操作
  114. for (int j = 1; j <= 10; j++)
  115. push(stack, j);
  116. printf("入栈操作(0-10)!\n\n");
  117. // 出栈操作
  118. int e;
  119. pop(stack, &e);
  120. printf("弹出的栈顶元素e=%d\n\n", e);
  121. // 遍历栈
  122. printf("遍历栈,栈中元素依次为:");
  123. traverseStack(stack);
  124. printf("\n");
  125. // 获得栈顶元素
  126. getTop(stack, &e);
  127. printf("栈顶元素 e=%d 栈的长度为%d\n\n", e, getLength(stack));
  128. // 判断是否为空栈
  129. printf("栈空否:%d(1:空 0:否)\n\n", isEmpty(stack));
  130. // 清空栈
  131. clearStack(stack);
  132. printf("清空栈后,栈空否:%d(1:空 0:否)\n\n", isEmpty(stack));
  133. return 0;
  134. }

输出结果如下图所示:

参考:

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/天景科技苑/article/detail/905931
推荐阅读
相关标签
  

闽ICP备14008679号