当前位置:   article > 正文

数据结构——栈的应用_数据结构-栈的应用

数据结构-栈的应用

栈结构固有的“后进先出”特性,使得栈成为程序设计中的有用工具,下文将讨论几个栈应用的典型例子,本文各经典案例的代码实现均采用顺序栈的结构,如有需要,在理解算法思想的基础上,不难将其推导为链栈结构实现,本文不过多赘述。

一、数制转换

十进制数N与其他d进制数的转换是计算机实现计算的基本问题,一个数制转换的简单算法基于以下原理:

N = ( N / d ) *  d + N % d

采用栈结构实现上述数制转换的逻辑表示:

 假设现在要编辑一个程序满足以下要求:对于输入的任意一个非负十进制整数,打印输出其对应的八进制数。由于采用除留余数法的计算过程是从低位到高位的顺序依次产生八进制的各个数位;而打印通常从高位向低位依次打印。因此可想,将计算过程中的得到的各个八进制数位依次进栈,则按照出栈序列依次打印输出的即为与输入对应的八进制数。

算法函数的代码实现:(可直接运行)

  1. //此算法实现采用顺序栈
  2. #include<stdio.h>
  3. #include<stdlib.h>
  4. #include<assert.h>
  5. #pragma warning(disable: 4996)
  6. #define Stack_Init_Size 10
  7. #define Stack_Add_Size 5
  8. typedef int ElemType;
  9. typedef struct Stack
  10. {
  11. ElemType* data;
  12. int size;
  13. int capacity;
  14. }Stack;
  15. //栈的初始化
  16. void StackInit(Stack* S)
  17. {
  18. S->data = (ElemType*)malloc(sizeof(ElemType) * Stack_Init_Size);
  19. if (NULL == S->data)
  20. {
  21. printf("空间申请失败\n");
  22. exit(0);
  23. }
  24. S->size = 0;
  25. S->capacity = Stack_Init_Size;
  26. }
  27. //检查当前栈是否已满,若满则进行扩容
  28. void CapacityCheck(Stack* S)
  29. {
  30. assert(S);
  31. if (S->size >= S->capacity) //当前栈满,需进行扩容
  32. {
  33. S->data = (ElemType*)realloc(S->data, sizeof(ElemType) * (S->capacity + Stack_Add_Size));
  34. if (NULL == S->data)
  35. {
  36. printf("空间扩容失败\n");
  37. exit(0);
  38. }
  39. S->capacity += Stack_Add_Size;
  40. }
  41. }
  42. //入栈
  43. void StackPush(Stack* S, ElemType e)
  44. {
  45. CapacityCheck(S);
  46. S->data[S->size++] = e;
  47. }
  48. //判断栈是否为空
  49. int StackEmpty(Stack* S)
  50. {
  51. return S->size == 0;
  52. }
  53. //出栈操作
  54. void StackPop(Stack* S, ElemType* e)
  55. {
  56. assert(S);
  57. if (StackEmpty(S))
  58. {
  59. printf("当前栈为空\n");
  60. return;
  61. }
  62. *e = S->data[--S->size];
  63. }
  64. //数制转换
  65. void conversion(Stack* S)
  66. {
  67. StackInit(S);
  68. int num = 0;
  69. printf("请输入需要进行转换的十进制数:> ");
  70. scanf("%d", &num);
  71. while (num)
  72. {
  73. StackPush(S, num % 8);
  74. num /= 8;
  75. }
  76. printf("转换为八进制数为:> ");
  77. while (!StackEmpty(S))
  78. {
  79. ElemType e;
  80. StackPop(S,&e);
  81. printf("%d",e);
  82. }
  83. printf("\n");
  84. }
  85. int main()
  86. {
  87. Stack S;
  88. conversion(&S);
  89. return 0;
  90. }

二、括号匹配

假设表达式中允许包含两种括号:圆括号()和方括号[],其嵌套的顺序随意,采用栈结构的出栈与入栈的操作实现括号的匹配。

括号匹配算法:在算法中设置一个栈,每读入一个括号,若为右括号,则或者使置于栈最顶端的最迫切期待消除的数据元素得以消解,或者是不合法的情况;若都为左括号,则作为一个最迫切待消除的数据元素压入栈顶,使栈中原有数据元素的待消解的紧迫性都下降一级。最终通过判断栈最终的空栈情况来确定括号是否匹配。

具体代码实现如下:

  1. //括号匹配问题
  2. #include<stdio.h>
  3. #include<stdlib.h>
  4. #include<assert.h>
  5. #pragma warning(disable: 4996)
  6. #define Stack_Init_Size 10
  7. #define Stack_Add_Size 5
  8. //定义栈的结构体
  9. typedef char ElemType;
  10. typedef struct Stack
  11. {
  12. ElemType* data;
  13. int size;
  14. int capacity;
  15. }Stack;
  16. //栈的初始化
  17. void StackInit(Stack* S)
  18. {
  19. S->data = (ElemType*)malloc(sizeof(ElemType) * Stack_Init_Size);
  20. if (NULL == S->data)
  21. {
  22. printf("空间申请失败\n");
  23. exit(0);
  24. }
  25. S->capacity = Stack_Init_Size;
  26. S->size = 0;
  27. }
  28. //判断栈中是否为空 1-表示为空 0-表示非空
  29. int IsEmpty(Stack* S)
  30. {
  31. return S->size == 0;
  32. }
  33. ElemType GetTop(Stack* S)
  34. {
  35. assert(S);
  36. if (IsEmpty(S))
  37. {
  38. return;
  39. }
  40. return S->data[S->size - 1];
  41. }
  42. //判断栈是否满,若满则扩容
  43. void CapacityCheck(Stack* S)
  44. {
  45. if (S->size >= S->capacity)
  46. {
  47. S->data = (ElemType*)realloc(S->data, sizeof(ElemType) * (S->capacity + Stack_Add_Size));
  48. if (NULL == S->data)
  49. {
  50. printf("空间扩容失败\n");
  51. exit(0);
  52. }
  53. S->capacity += Stack_Add_Size;
  54. }
  55. }
  56. //入栈
  57. void StackPush(Stack* S, ElemType* e)
  58. {
  59. assert(S);
  60. CapacityCheck(S);
  61. S->data[S->size++] = e;
  62. }
  63. //出栈
  64. void StackPop(Stack* S)
  65. {
  66. assert(S);
  67. if (!IsEmpty(S))
  68. {
  69. S->size--;
  70. return;
  71. }
  72. }
  73. //栈销毁
  74. void StackDestory(Stack* S)
  75. {
  76. assert(S);
  77. if (S->data != NULL)
  78. {
  79. free(S->data);
  80. S->data = NULL;
  81. }
  82. S->capacity = 0;
  83. S->size = 0;
  84. }
  85. void isValid(Stack* S)
  86. {
  87. StackInit(S);
  88. ElemType ch = { 0 };
  89. printf("请输入括号:> ");
  90. while ((ch = getchar()) != EOF)
  91. {
  92. if(ch != '\n')
  93. {
  94. ElemType temp_01 = GetTop(S);
  95. StackPush(S, ch);
  96. ElemType temp_02 = GetTop(S);
  97. if ((temp_01 == '(' && temp_02 == ')') || (temp_01 == '[' && temp_02 == ']'))
  98. {
  99. StackPop(S);
  100. StackPop(S);
  101. }
  102. }
  103. else
  104. {
  105. break;
  106. }
  107. }
  108. if (IsEmpty(S))
  109. {
  110. StackDestory(S);
  111. printf("括号匹配成功\n");
  112. }
  113. else
  114. {
  115. StackDestory(S);
  116. printf("括号匹配失败\n");
  117. }
  118. }
  119. int main()
  120. {
  121. Stack S;
  122. isValid(&S);
  123. }

三、行编辑程序

一个简单的行编辑程序的功能:接收用户从终端输入的程序或数据,并存入用户的数据区。

由于用户在终端进行输入时,不能保证不出差错,因此在行编辑程序中,通常采用,设置一个输入缓冲区,用以接收用户输入的一行字符,然后逐行存入用户数据区。允许用户输入出现差错,并在有错误时及时进行更正。

例如:当用户发现刚刚键入的一个字符是错误的时,可以补入一个退格符’#‘,以表示前一个字符无效;如果本行之间键入的差错较多或难以补救,则可以补入一个退行符’@‘,以表示当前行的字符无效。 

注意:Windows环境下,通过键盘输入ctrl+z表示EOF结束终止符!

  1. //行编辑程序
  2. #include<stdio.h>
  3. #include<stdlib.h>
  4. #include<assert.h>
  5. #define Stack_Init_Size 10
  6. #define Stack_Add_Size 5
  7. //定义栈结构
  8. typedef char ElemType;
  9. typedef struct Stack
  10. {
  11. ElemType* data;
  12. int size;
  13. int capacity;
  14. }Stack;
  15. //栈的初始化
  16. void StackInit(Stack* S)
  17. {
  18. S->data = (ElemType*)malloc(sizeof(ElemType) * Stack_Init_Size);
  19. if (NULL == S->data)
  20. {
  21. printf("空间申请失败\n");
  22. exit(0);
  23. }
  24. S->size = 0;
  25. S->capacity = Stack_Init_Size;
  26. }
  27. //栈的判空操作
  28. int IsEmpty(Stack* S)
  29. {
  30. return S->size == 0;
  31. }
  32. //出栈
  33. void StackPop(Stack* S)
  34. {
  35. assert(S);
  36. if (IsEmpty(S))
  37. {
  38. return;
  39. }
  40. S->size--;
  41. }
  42. //清空栈栈中内容
  43. void StackClear(Stack* S)
  44. {
  45. assert(S);
  46. if (IsEmpty(S))
  47. {
  48. return;
  49. }
  50. S->size = 0;
  51. }
  52. //栈的扩容判断
  53. void CapacityCheck(Stack* S)
  54. {
  55. if (S->size >= S->capacity)
  56. {
  57. S->data = (ElemType*)realloc(S->data, sizeof(ElemType) * (S->capacity + Stack_Add_Size));
  58. if (NULL == S->data)
  59. {
  60. printf("栈空间扩容失败\n");
  61. exit(0);
  62. }
  63. S->capacity += Stack_Add_Size;
  64. }
  65. }
  66. //入栈
  67. void StackPush(Stack* S, ElemType data)
  68. {
  69. assert(S);
  70. CapacityCheck(S);
  71. S->data[S->size++] = data;
  72. }
  73. //销毁栈
  74. void StackDestory(Stack* S)
  75. {
  76. assert(S);
  77. if (NULL != S->data)
  78. {
  79. free(S->data);
  80. S->data = NULL;
  81. }
  82. S->capacity = 0;
  83. S->size = 0;
  84. }
  85. //打印当前栈中的文本文件
  86. void StackPrint(Stack* S)
  87. {
  88. assert(S);
  89. if (IsEmpty(S))
  90. {
  91. return;
  92. }
  93. printf("当前行文本为:> %s\n", S->data);
  94. }
  95. void LineEdit(Stack* S)
  96. {
  97. StackInit(S);
  98. ElemType ch = { 0 };
  99. printf("请输入文本内容:> ");
  100. ch = getchar();
  101. while (ch != EOF)
  102. {
  103. while (ch != EOF && ch != '\n')
  104. {
  105. switch (ch)
  106. {
  107. case '#' :
  108. StackPop(S);
  109. break;
  110. case '@':
  111. StackClear(S);
  112. break;
  113. default:
  114. StackPush(S, ch);
  115. }
  116. ch = getchar();
  117. }
  118. StackPush(S, '\0');
  119. StackPrint(S);
  120. StackClear(S);
  121. if (ch != EOF)
  122. {
  123. printf("请输入文本内容:> ");
  124. ch = getchar();
  125. }
  126. }
  127. StackDestory(S);
  128. }
  129. int main()
  130. {
  131. Stack S;
  132. LineEdit(&S);
  133. }

四、表达式求值

表达式求值是程序设计语言编译中的一个最基本问题。它的实现是栈应用的又一个的经典例子。本文介绍一种简单直观、广为使用的算法——“算符优先算法”。

要把一个表达式翻译成正确求值的一个机器指令序列,或者直接对表达式求值,首先应该能正确解释表达式。

例如要对算术表达式求值,首先要了解四则运算的规则。即:

  1. 先乘除,后加减
  2. 从左到右算
  3. 先括号内,后括号外

算符优先算法就是根据这个运算优先关系的规定来实现对表达式的编译或解释执行的。

任何一个表达式都是由操作数、运算符、和界限符组成的,称之为单词。一般的,操作数既可以是常数也可以是被说明为变量或常量的标识符;运算符可以分为算术运算符、关系运算符和逻辑运算符三类;基本界限符有左右括号和表达式结束符‘#’等。本文仅就只包含加减乘除四则运算的简单算术表达式的求值问题展开讨论。

为了实现算符优先算法,可以使用两个工作栈。一个叫做OPTR,用以寄存运算符;另一个称之为OPND,用以寄存操作数或运算结果。算法的基本思想如下:

  1. 首先置操作数栈为空栈,表达式起始符‘#’为运算符栈的栈底元素;
  2. 依次读入表达式中的各个字符,若为操作数即进入OPND栈,若为运算符则和OPTR栈的栈顶运算符比较优先权后做相应的操作,直至整个表达式求值完毕,即OPTR栈的栈顶和当前读入的字符均为‘#’。

表达式求值算法的代码实现如下:

学生党受限于时间原因,本章内容后续几天逐步更新完毕,望见谅!!!

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

闽ICP备14008679号