当前位置:   article > 正文

《数据结构-用C语言描述第三版》课后答案 第三章

《数据结构-用C语言描述第三版》课后答案 第三章

撰写的内容可能有错误的地方,如果有错误或者疑问,欢迎评论区或者私信指正,我会及时改正

1.简述下列术语或概念:

(1)递归进层需要做的三件事。 

  • 保留本层参数与返回地址
  • 为被调用函数的局部变量分配存储区,给下层参数赋值
  • 将程序转移到被调用函数的入口

(2)递归退层需要做的三件事。

  • 保存被调用函数的计算结果
  • 释放被调用函数的数据区,恢复上层参数
  • 依照被调用函数保存的返回地址,将控制转移回调用函数

(3)顺序队列的"假溢出"现象。

        随着进队和入队,队头指针和队尾指针加1,最后出现队头指针和队尾指针指向超出顺序表索引,但是此时队列仍有空间的现象

(4)循环队列的判空、判满条件(少用一个空间区分队空队满)。

  1. 判空条件

        rear == front

  1. 判满条件

        (rear + 1) mod MAXSIZE == font


2.选择题

(1)输入序列为123,若进栈、出栈操作可以交替进行,则不能得到的出栈序列是(B

A .321

B .312

C .123 

D .132

(2)以下会用到栈的应用是(D)。

A .递归

B .子程序调用

C .括号匹配

D .以上选项均是

(3)栈和队列的共同点是(C)。

A .都是先进先出

B .都是先进后出

C .只允许在端点处插入和删除元素

D .它们没有共同点

(4)循环队列存储在数组 A [0.. m -1]中,则入队时 rear 应变化为(C)

A . rear ++ 

B . rear =( rear +1) mod ( m -1)

C . rear = (rear+1) mod m

D . rear = (rear + 1) mod (m+1)

(5)设有一个顺序共享栈 S [0.. n -1],其中第一个栈项指针 topl 的初值为﹣1,第二个栈顶指针top2的初值为 n ,则判断共享栈满的条件是(C)

A . topl ==top2 

B .top1+top2== n 

C . topl +1==top2

D . topl -1==top2

3.按照四则运算加、减、乘、除和幂运算优先关系的惯例,画出对下列算术表达式求值时,算数栈和运算符栈的变化过程:

A-B*C/D+E^F

不带界限符的表达式求职
不带界限符的表达式求值

4.已知表达式为a*b+(c-d/e)*f :

(1)编写算法,将原表达式转换为后缀表达式 ab*cde/-fx+

(2)编写算法,对转换后的后缀表达式进行求值。

答:(1)

此处编写了一个完整的可运行代码,但是程序可能不太稳定,qaq

  1. #include<stdio.h>
  2. #define MaxSize 100
  3. char ch[MaxSize] = "a*b+(c-d/e)*f";
  4. //定义栈和队列结构
  5. typedef struct Stack{
  6. char data[MaxSize];
  7. int top;
  8. }Stack;
  9. //定义栈的函数
  10. //初始化
  11. void initStack(Stack * s){
  12. s->top = -1;
  13. }
  14. //判空
  15. int isEmptyStack(Stack *s){
  16. return s->top==-1;
  17. }
  18. //判满
  19. int isFullStack(Stack *s){
  20. return s->top==MaxSize;
  21. }
  22. //出栈
  23. char popStack(Stack * s){
  24. if(isEmptyStack(s))
  25. return '\0';
  26. return s->data[s->top--];
  27. }
  28. //入栈
  29. int pushStack(Stack * s,char elem){
  30. if(isFullStack(s))
  31. return 0;
  32. s->data[++s->top] = elem;
  33. return 1;
  34. }
  35. //获取栈顶元素
  36. char getTopStack(Stack *s){
  37. if(isEmptyStack(s))
  38. return '\0';
  39. return s->data[s->top];
  40. }
  41. //定义队列
  42. typedef struct Queue{
  43. char data[MaxSize];
  44. int rear;
  45. int front;
  46. }Queue;
  47. //初始化
  48. void initQueue(Queue *q){
  49. q->rear = 0;
  50. q->front= 0;
  51. }
  52. //判空
  53. int isEmptyQueue(Queue *q){
  54. return q->rear == q->front;
  55. }
  56. //判满
  57. int isFullQueue(Queue *q){
  58. return (q->rear+1)%MaxSize == q->front;
  59. }
  60. // 入队
  61. void enQueue(Queue *q, char elem) {
  62. if (isFullQueue(q))
  63. printf("queue is full!!!");
  64. q->data[q->rear++] = elem;
  65. return ;
  66. }
  67. // 出队
  68. char deQueue(Queue *q) {
  69. if (isEmptyQueue(q)){
  70. printf("queue is empty!!!");
  71. return '\0';
  72. }
  73. return q->data[q->front++];
  74. }
  75. //比较优先级
  76. int compare(char ch1,char ch2){
  77. if(ch1 == '+' || ch1 == '-'){
  78. if(ch2 == '+' || ch2 == '-')
  79. return 0;
  80. else{
  81. return -1;
  82. }
  83. }
  84. if(ch1 == '*' || ch1 == '/'){
  85. if(ch2 == '+' || ch2=='-'){
  86. return 1;
  87. }else{
  88. return 0;
  89. }
  90. }
  91. }
  92. void transform(){
  93. int i = 13;
  94. printf("please input your function\n");
  95. //获取表达式
  96. //while(scanf("%c",&ch[i])&&ch[i]!='\n'&&i<MaxSize)
  97. // i++;
  98. //将中缀表达式转换为后缀表达式
  99. /* 思路:
  100. * 初始化一个操作符栈和一个输出队列,一个运算符栈S,一个输出队列Q
  101. * 1.遇到操作数: 直接添加到输出队列
  102. * 2.遇到运算符: a:如果S1为空,或者栈顶元素为'(',直接入栈
  103. * b:否则比较优先级
  104. * 当前运算符比较级小于等于栈顶元素运算法与优先级,栈元素出栈并进入到输出队列
  105. * 否则 入栈
  106. * c:如果为')',S出栈并进入输出队列,直到遇到'(',左右括号出栈后不添加到输出队列
  107. * */
  108. //理论结束 实战开始
  109. //初始化栈和队列
  110. for(int j = 0;j<i;j++){
  111. printf("%c",ch[j]);
  112. }
  113. printf("\n");
  114. Stack s;
  115. Queue q;
  116. initStack(&s);
  117. initQueue(&q);
  118. printf("------------\n");
  119. for(int j=0;j<i;j++){
  120. //printf("%c",ch[j]);
  121. switch(ch[j]){
  122. case '+':
  123. case '-':
  124. case '*':
  125. case '/':
  126. if(isEmptyStack(&s) || getTopStack(&s) == '('){
  127. pushStack(&s,ch[j]);
  128. }else{
  129. int compareResult = compare(ch[j],getTopStack(&s));
  130. if(compareResult !=1){
  131. //循环比较并出栈进入到输出队列
  132. do{
  133. char dataTop = popStack(&s);
  134. enQueue(&q,dataTop);
  135. }while(compare(ch[j],getTopStack(&s))!=1 && !isEmptyStack(&s));
  136. pushStack(&s,ch[j]);
  137. }else{
  138. pushStack(&s,ch[j]);
  139. }
  140. }
  141. break;
  142. case '(':
  143. pushStack(&s,ch[j]);
  144. break;
  145. case ')':
  146. while(getTopStack(&s)!='(')
  147. enQueue(&q,popStack(&s));
  148. popStack(&s);
  149. break;
  150. default:
  151. enQueue(&q,ch[j]);
  152. }
  153. }
  154. while(!isEmptyStack(&s)){
  155. enQueue(&q,popStack(&s));
  156. }
  157. //队列元素循环出队
  158. while(!isEmptyQueue(&q)){
  159. printf("%c",deQueue(&q));
  160. }
  161. }
  162. void test(){
  163. Stack s;
  164. Queue q;
  165. initStack(&s);
  166. initQueue(&q);
  167. char ch4[] = {'a','b','c','d'};
  168. printf("------------");
  169. for(int i=0;i<4;i++){
  170. pushStack(&s,ch4[i]);
  171. enQueue(&q,ch4[i]);
  172. }
  173. printf("------------");
  174. while(!isEmptyStack(&s)){
  175. printf("%c",popStack(&s));
  176. }
  177. printf("\n");
  178. //队列元素循环出队
  179. while(!isEmptyQueue(&q)){
  180. printf("%c",deQueue(&q));
  181. }
  182. }
  183. int main(){
  184. //test();
  185. transform();
  186. return 0;
  187. }

(2)

求出后缀表达式后。后缀表达式的求值只需要将后缀表达式数字依次入栈,遇到运算符出栈两个元素并计算,然后继续执行上述步骤


5.假设以带头结点的循环链表表示队列,并且只设一个指针指向队尾元素结点(注意不设头指针),试编写相应的队列初始化、人队列和出队列算法。

  1. $ cat program5.c
  2. /*
  3. * 带头节点的循环链表表示队列,只设置一个尾指针,不设置
  4. * 头指针,编写相应的队列初始化、入队、出队算法
  5. * */
  6. typedef struct QueueNode{
  7. int data;
  8. struct QueueNnode *next;
  9. }Queue;
  10. void initStruct(Queue *q){
  11. q = NULL;
  12. }
  13. int enQueue(Queue *q,int elem){
  14. if(isFullQueue(q))
  15. return 0;
  16. struct QueueNode *node = (struct QueueNode *)malloc(sizeof(struct QueueNode));
  17. node->data = elem;
  18. if(q==NULL){
  19. //队列为空
  20. q = node;
  21. q->next = next;
  22. }else{
  23. node->next = q->next;
  24. q->next = node;
  25. q = node;
  26. }
  27. return 1;
  28. }
  29. int deQueue(Queue *q,int elem){
  30. if(isEmptyQueue(q))
  31. return 0;
  32. struct QueueNode *node = q->next;//q是尾指针,则q->next对应头结点
  33. struct QueueNode *s = node->next;//要删除的结点
  34. if(s == node){
  35. //只有一个结点
  36. free(s);
  37. q = NULL:
  38. }else{
  39. node->next = s->next;
  40. free(s);
  41. }
  42. return 1;
  43. }

6.要求循环队列不损失一个空间全部都能得到利用,设置一个标志域 tag ,以 tag 为0或1来区分头尾指针相同时的队列状态的空与满,试编写与此结构相应的人队与出队算法。

  1. /*
  2. 要求循环队列不损失一个空间全部都能得到利用,设置一个标志域 tag ,
  3. 以 tag 为0或1来区分头尾指针相同时的队列状态的空与满,试编写与此结
  4. 构相应的人队与出队算法。
  5. */
  6. #define MaxSize 100
  7. typedef struct Queue{
  8. int tag; //标志域 tag == 1 为满 tag == 0 为空
  9. int data[MaxSize]; //数据域
  10. int front,rear; //首尾指针
  11. }Queue;
  12. void InitQueue(Queue *q){
  13. q->front = 0;
  14. q->rear = 0;
  15. q->tag = 0;
  16. }
  17. void enQueue(Queue *q,int elem){
  18. if(tag){
  19. printf("队满!!!")
  20. return;
  21. }
  22. q->data[q->rear] = elem;
  23. q->rear++;
  24. q->rear %= MaxSize;
  25. if(q->rear == q->front){
  26. q->tag = 1;
  27. }
  28. }
  29. int deQueue(Queue *q){
  30. if(!tag){
  31. printf("队空!!!");
  32. return -1;
  33. }
  34. int temp = q->data[q->front];
  35. q->front++;
  36. q->front%=MaxSize;
  37. if(q->front == q->rear)
  38. tag = 0;
  39. return temp;
  40. }

7.设有4个元素1、2、3、4依次进栈,而出栈操作可随时进行(进出栈可任意交错进行,但要保证进栈次序不破坏1、2、3、4的相对次序),试写出所有不可能的出栈次序和所有可能的出栈次序。

四个元素1、2、3、4的排序种类有4*3*2*1=24种

依次为

1 2 3 4 , 1 2 4 3 , 1 3 2 4 ,1 3 4 2 ,  1 4 2 3 , 1 4 3 2

2 1 3 4 , 2 1 4 3 , 2 3 1 4 , 2 3 4 1 , 2 4 1 3 , 2 4 3 1 

3 1 2 4 , 3 1 4 2 3 2 1 4 , 3 2 4 1 3 4 1 2 3 4 2 1 

4 1 2 3 , 4 1 3 2 , 4 2 1 3 , 4 2 3 1 , 4 3 1 2 4 3 2 1   

红色为不可能的出栈顺序,黑色为可能的出栈顺序

n个元素进栈,可能的出栈顺序有\frac{1}{n+1} C^n_{2n}

则四个元素进栈可能的出栈顺序有14种


8.已知递归函数如下:

g(m,n) = \left\{\begin{matrix} 0, & m=0,n>=0 \\ g(m-1,2n)+n,&m>0,n>=0 \end{matrix}\right.

(1)编写递归算法

  1. int funcG(int m,int n){
  2. if(m == 0)
  3. return 0;
  4. return funcG(m-1,2*n)+n;
  5. }

(2)给出g(3,5)的递归执行过程图示

(3)将递归算法改写为非递归算法

  1. int funcG(int m,int n){
  2. int sum = 0;
  3. while(m!=0){
  4. sum+=n;
  5. n*=2;
  6. m--;
  7. }
  8. return sum;
  9. }

9.有如下函数定义:

  1. void bin(int b[],int n){
  2. if(n==1){
  3. b[1] = 2;
  4. b[2] = 2;
  5. }else{
  6. bin(b,n-1);
  7. b[n+1] = 2;
  8. for(int i=n;i>=2;i--){
  9. b[i] = b[i]+b[i-1];
  10. }
  11. }
  12. }

若调用 bin ( A ,5),给出 A 数组中第1个到第6个数组元素的值。

答:

数组中的值为

2       10      20      20      10      2 

每次递归将后面一个元素赋值为2,然后前面的元素分别加等其前面的元素


10.分析汉诺塔问题的时间复杂度。

假设柱子编号为 A B C

执行步骤:

(1)将上面的n-1个盘子从A经过C移动到B;

(2)将编号为n的盘子从A移到C;

(3)将B上的n-1个盘子经过A移动到C

        此时需要先将B上的n-2个盘子经过C移动到A,然后将第n-1个盘子从B移动到C

设ai 为有i个盘子时需要移动的次数

a1 = 1

a2 = 3

a3 = 2*a_{2}+1  = 7    

a4 = 2*a_{3}+1  = 15

.......

an = 2*a_{n-1}+1 = 2^{n}-1

为什么?

根据移动的三个步骤:

n个盘子时

将n-1个盘子从一个柱子经另一个柱子移动到剩余的柱子需要a_{n-1}

将编号为n的盘子从A移动到C 需要1步

将n-1个盘子从一个柱子经另一个柱子移动到剩余的柱子需要a_{n-1}

则 an = 2*a_{n-1}+1 


11.求解汉诺塔问题时,若初始是5个盘子,计算移动盘子的次数。

a_{5} = 2^{5} - 1 = 31

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

闽ICP备14008679号