这几天不得不说,真的好热.表示我这个不喜欢吹空调的人都老老实实蹲进空调房了.
这下讲的是栈和队列.
这两者都是重要的数据结构,都是线性结构.它们在日后的软件开发中有着重大作用.后面会有实例讲解.
两者区别和联系,其实总结起来就一句.栈,后进先出;队列,先进先出.
可以将栈与队列的存储空间比作一个只够一个身位的水管.
栈的水管,有一头被堵住了.所以当人们进去后,想出来就只能让最靠近出口的那位先出去,依次推出.(后进先出).
队列的水管,类似单向车道.所以当人们进去后,想出来就只能一直向前,走出来,不可以从入口出来.(先进先出).
所以,道理还是很简单的.接下来就是学习一些专属函数就 ok 了.
实例 090 应用栈实现进制转换
问题:应用栈实现进制转换,可以将十进制数转换为其他进制数.
逻辑:首先我们来想一下进制转换的算法,就如之前提到的,不断地取余,求商.依次循环进行,再按顺序输出,获取最终结果.
代码:
1#include 2 typedef struct 3 {
4 DataType * base;
5 //有人问DataType是什么东东.
6 //DataType是一种数据类型,是数据结构专属.采取的是类C的语言.所以需要将其映射为C数据类型.
7 DataType * top;
8 int stacksize;
9 //提醒一句,stack是栈的英文.
10
}
SeqStack;
11 12 13 //************接下来就是重点
14 void Initial(SeqStack * s) 15 //栈的初始化函数,构造一个空栈S.
16 {
17 s - >base = (DataType * ) malloc(STACK_SIZE * sizeof(DataType));
18 //类似于链表的获取存储空间,连使用的函数都一样.
19 //注意,我这里使用的是base.所以,这个栈是"向上生长"的栈.
20
if (!s - >base) 21 exit( - 1);
22 //exit()是一个退出函数,其参数是退出状态代码.(0是正常退出状态码)
23 s - >top = s - >base;
24 //初始化时,栈内为空.从而可以这样初始化其中的top变量.
25 s - >stacksize = STACK_SIZE;
26 //获取存储空间大小.
27
}
28 int IsEmpty(SeqStack * S) 29 //判断栈是否为空的函数
30 {
31
return S - >top == S - >base;
32 //返回值为判断结果.
33
}
34 int IsFull(SeqStack * S) 35 //判断栈是否满了.
36 {
37
return S - >top - S - >base == STACK_SIZE - 1;
38 //注意看,中间那个是减号.
39
}
40 void Push(SeqStack * S, DataType x) 41 //栈的入栈函数
42 {
43
if (IsFull(S)) 44 {
45 printf("栈上溢出");
46 exit(1);
47
}
48 * S - >top++=x;
49 //入栈时,切记 栈指针top,"先压后加".
50
}
51 DataType Pop(SeqStack * S) 52 //栈的出栈函数
53 {
54
if (IsEmpty(S)) 55 {
56 printf("栈为空");
57 exit(1);
58
}
59
return * --S - >top;
60 //注意上面是*(--s),别问我*--是什么.出栈时,切记 栈指针to[,"先减后弹".
61
}
62 DataType Top(SeqStack * S) 63 //栈顶变化函数.
64 //因为这个栈是从栈底构建的,所以栈底不变.变化的是栈顶.
65 {
66
if (IsEmpty(S)) 67 {
68 printf("栈为空");
69 exit(1);
70
}
71
return * (S - >top - 1);
72 //改变栈顶地址.
73
}
74 //************以上是重点
75 76 77 78 void conversion(int N, int B) 79 //解决问题的进制转换函数
80 {
81 int i;
82 SeqStack * S;
83 Initial(S);
84 //初始化栈S
85
while (N) 86 {
87 Push(S, N % B);
88 N = N / B;
89
}
90 //这个循环不用我讲了吧.就知平时进制转化的循环.
91
while (!IsEmpty(S)) 92 {
93 i = Pop(S);
94 printf("%d", i);
95 //依次输出栈中元素,其实就是我们的结果了.
96
}
97
}
98 void main() 99 //主函数不用细讲.就是完成输入,输出,调用变换函数.
100 {
101 int n,
d;
102 printf("Input the integer you want to transform:");
103 scanf("%d", &n);
104 printf("Input the integer of the system:");
105 scanf("%d", &d);
106 printf("result:");
107 conversion(n, d);
108 getch();
109
}
反思:栈的应用主要就是初始化,判断空,判断满,入栈,出栈,栈顶六个函数.懂了这六个函数,应用就没什么了.
其中的技巧,代码备注也提到了,比如:"先压后加" 等.不过这是针对这个栈的,一个 "向上生长" 的栈.
其实还有用栈设置密码,实现编辑程序等.但其中关于栈的部分基本大同小异,所以不再赘述.
如果,还有不懂,可以看看视屏.比如,张洪瑞与严蔚敏老师的视屏讲解.(我不会告诉你,计算机考研的专业课可是要求严蔚敏老师的书.不过建议张洪瑞老师的视屏.主要严蔚敏老师讲的.总感觉和我不是一个时代,事实上,也确实不是一个时代的.)
实例 095 链队列
问题:采取链式存储法编程实现元素入队,出队以及将队列中的元素显示出来,要求整个过程以菜单选择的形式实现.
逻辑:逻辑与栈没有太多区别.将队列的主要操作:创建,入队,出队,展示等功能分为多个函数进行.最后主函数实现题目要求的界面功能,及输入,调用等功能.
代码:
1#include 2#include 3 typedef int ElemType;
4 //怕你们不懂之前说的DataType到底怎么弄.这个就是一个例子.如果没有这句话,你们会发现在VC里,这个程序报错报炸了.
5 typedef struct node 6 //这个结构体是用来存储节点的.
7 {
8 ElemType data;
9 //节点数据
10 struct node * next;
11 //节点指向下一个节点地址
12
}
quenode;
13 //这个节点结构体名称.
14 struct quefr 15 //这个结构体用来存放队列的队首,队尾地址指针.
16 {
17 quenode * front,
*rear;
18 //quenode是什么?额,就是我们之前定义的节点结构体.
19
};
20 void creat(struct quefr * q) 21 //定义函数creat,用来创建,初始化队列.
22 {
23 quenode * h;
24 h = (quenode * ) malloc(sizeof(quenode));
25 //获取存储空间,空间大小与quenode同步.
26 h - >next = NULL;
27 //初始化h->next,为空.
28 q - >front = h;
29 q - >rear = h;
30 //初始化相关的quefr中数据,均为h.(队列刚刚建立,那么队首,队尾当然是同一个元素(结构体quenode的h)).
31
}
32 void enqueue(struct quefr * q, int x) 33 //定义入队函数enqueue,用来实现元素x进入队列q.
34 {
35 quenode * s;
36 //建立一个新的节点s,用来存储新的入队元素x.
37 s = (quenode * ) malloc(sizeof(quenode));
38 //依旧是获取存储空间
39 s - >data = x;
40 //向quenode结构体s导入新的入队元素x.
41 s - >next = NULL;
42 //s的指针域设置为空,作为初始化.
43 //也许,有人就问了.这里设置为空,那么队列还怎么连接起来啊.
44 //嗯.其实,有关连接方面的数据处理统一交给了quefr处理了.看下面就知道了.
45 q - >rear - >next = s;
46 //这里实现了队列元素的连接.切记,其中rear指针的数据类型可是quenode,所以可以实现调用next,可以实现连接.
47 q - >rear = s;
48 //这时队尾指针地址改变,因为增加了新的元素了.(这个程序设定是队首地址不变的,向队尾添加元素.)
49 //我这个程序设定的是队尾入队,队首出队.
50
}
51 ElemType dequeue(struct quefr * q) 52 //定义出队函数dequeue,用来实现元素离开队列q.(由于,之前提到的队列性质,所以离开队列的函数必然是队首元素.)
53 {
54 quenode * p;
55 ElemType x;
56 //初始化,详细参考上面的.
57 p = (quenode * ) malloc(sizeof(quenode));
58 //有人说,初始化,入队要获取存储空间就算了.为什么出队还要获取存储空间呢?
59 //其实,不是没有道理.但是,1,我们对数据处理部分采用的一直都是结构体quenode,出队是便于数据处理,当然还是得建立结构体,自然就要获取存储空间;2.很简单一句,便于模块化操作.
60
if (q - >front == q - >rear) 61 //判断原队列是否为空
62 {
63 printf("queue is NULL\n");
64 x = 0;
65
}
66
else 67 {
68 p = q - >front - >next;
69 //赋值指针p,为q指针的下一个地址.
70 q - >front - >next = p - >next;
71 //重新定位q->front->next,从而重新定位了q.
72 //这两句也许有些人会看得有点晕,可以自己画画图,或者参考一些之前的链表.主要就是把原来q的地址遗失了.重新定位q.
73
if (p - >next == NULL) 74 //判断原队列是否就一个元素
75 q - >rear = q - >front;
76 //重新定位.很好奇这步有没有必要.
77 x = p - >data;
78 //获取出队列元素
79 free(p);
80 //释放空白空间.
81
}
82
return (x);
83 //返回出队元素
84
}
85 void display(struct quefr dq) 86 //定义函数display,用于展示队列内的所有元素
87 {
88 quenode * p;
89 p = (quenode * ) malloc(sizeof(quenode));
90 //为p分配存储空间
91 p = dq.front - >next;
92 //赋值p
93
while (p != NULL) 94 //等同于判断dq.front->next是否不为空
95 {
96 printf("data=%d\n", p - >data);
97 //展示元素
98 p = p - >next;
99 //将计数器指向下一个元素地址.
100
}
101 printf("end\n");
102 //最终输出end,表示元素展示完毕.
103
}
104 main() 105 //建立主函数作为程序入口,实现题目要求的界面,以及输入,调用,输出.
106 {
107 struct quefr * que;
108 //定义*que的数据类型为struct quefr.
109 int n,
i,
x,
sel;
110 void display();
111 void creat();
112 void enqueue();
113 ElemType dequeue();
114 do 115 {
116 printf("\n");
117 printf(" 1 creat queue \n");
118 printf(" 2 into the queue \n");
119 printf(" 3 delete from queue \n");
120 printf(" 4 display \n");
121 printf(" 5 exit \n");
122 printf("--------------------------------------------\n");
123 printf("please choose(1,2,3,4,5)\n");
124 scanf("%d", &sel);
125
switch (sel) 126 //采用switch判断语句.
127 {
128
case 1:
129 que = (struct quefr * ) malloc(sizeof(struct quefr));
130 creat(que);
131 printf(132 "please input the number of element which do you want to creat:");
133 scanf("%d", &n);
134
for (i = 1; i <= n; i++) 135 {
136 scanf("%d", &x);
137 enqueue(que, x);
138
}
139
break;
140
case 2:
141 printf("please input the element:");
142 scanf("%d", &x);
143 enqueue(que, x);
144
break;
145
case 3:
146 printf("x=%d\n", dequeue(que));
147
break;
148
case 4:
149 display( * que);
150
break;
151
case 5:
152 exit(0);
153
}
154
}
155
while (sel <= 4);
156 //使用了do while循环.当sel为5时,sel>4,跳出循环.
157
}
反思:之所以将队列的问题分为多个函数分别解决,是为了更好地模块化操作.越发地感受到编程中模块化操作的重要性,及好处.
实例 096 循环缓冲区问题
ps:这个问题原本想将代码打出来的,但我觉得之前队列程序已经将主要架构显现出来了.所以这个问题只会简单一提,如果有人有需要,可以找我要.
逻辑:队列的这个实例主要采用了循环队列.可以很好地解决顺序队列 "假溢出" 的现象.这里的循环队列和之前循环链表一样,就是将顺序队列构建成一个首尾相连的循环表.这样可以更好地解决我之前在循环链表提到的循环存储问题.
总结:到此为止,栈与队列队列的问题就差不多了.你会发现,这两个就是差不多的东西.事实上,也就两种特殊的顺序表.操作思想是一致的.更重要的是体会到其中越发明显的模块化操作的思想.
接下来是是串与广义表,编程中的数据处理,无非数值处理与非数值处理,而非数值处理基本上是字符串数据.而现在计算机的硬件及其架构更多地是为了数值计算,所以相对而言,处理字符串数据就更为复杂了.其中广义表是线性表的推广,目前多用于人工智能等领域的表处理语言中.所以,有这方面兴趣的可以多多关注.
实例 098 简单的文本编辑器
问题:要求实现三个功能:第一,要求对指定行输入字符串;第二,删除指定行的字符串;第三,显示输入的字符串的内容.
逻辑:串其实就是由零个,或多个字符串组成的有限序列.
串的存储方式由两种:静态存储与动态存储. 其中动态存储结构又有两种:链式存储结构与堆结构存储.这个问题的解决采用了链式存储结构.
串的链式存储结构是包含数据域和指针域的节点结构(是不是感觉很眼熟,和链表好像啊).
由于每个节点只存放一个字符,太过浪费空间,为节省空间,故令每个节点存放若干个字符(题目中采用了 50 个字符为一块),这种结构叫块链结构.(题目中采用了这种结构).
代码:
1#include 2#include 3#define MAX 100 4 void Init();
5 void input();
6 void Delline();
7 void List();
8 int Menu();
9 //仅仅是函数声明,由于主函数位于最后面,所以这五个语句去掉也可以.
10 11 typedef struct node 12 //定义存放字符串的节点.
13 {
14 char data[50];
15 struct node * next;
16
}
strnode;
17 18 typedef struct head 19 //定义每行的头结点.
20 {
21 int number;
22 int length;
23 strnode * next;
24
}
headnode;
25 //如之前的一样,两个结构体,前者作为数据节点,存储数据,后者则负责存储的位置等数据以外的事情.
26 27 headnode Head[MAX];
28 //定义有100行.
29 30 void Init() 31 //定义函数Init,用来初始化串.
32 {
33 int i;
34
for (i = 0; i) 35 {
36 Head[i].length = 0;
37 //设定长度均为0
38
}
39
}
40 41 int Menu() 42 //定义函数Menu,用来控制函数的人机交互界面.
43 {
44 int i;
45 i = 0;
46 printf("1.Input\n");
47 printf("2.Delete\n");
48 printf("3.List\n");
49 printf("4.Exit\n");
50
while (i <= 0 || i > 4) 51 {
52 printf("please choose\n");
53 scanf("%d", &i);
54
}
55
return i;
56 //返还用户的输入函数
57
}
58 59 void input() 60 //定义函数input,作为串的输入函数.
61 {
62 strnode * p,
*find();
63 int i,
j,
LineNum;
64 char ch;
65
while (1) 66 {
67 j = -1;
68 //这里j=-1,其实,由于采用的是while(1)循环.计数器的增加在开头 j++.所以,其实还是从j=0开始的.
69 printf("input the number of line(0-100),101-exit:\n");
70 scanf("%d", &LineNum);
71 //输入要输入字符串的所在行数.
72
if (LineNum < 0 || LineNum >= MAX) 73
return;
74 //超出可能的行数,即直接返回.
75 printf("please input,#-end\n");
76 i = LineNum;
77 Head[i].number = LineNum;
78 Head[i].next = (strnode * ) malloc(sizeof(strnode));
79 //申请存储空间.
80 p = Head[i].next;
81 //p就是我们将要存储字符串的头结点(字符串的第一个字符的地址)
82 ch = getchar();
83
while (ch != '#') 84 {
85 j++;
86 //验证前面所说的j问题
87
if (j >= 50) 88 //这里采用了50为一行字符串的上限.
89 {
90 p - >next = (strnode * ) malloc(sizeof(strnode));
91 p = p - >next;
92 //大于50了,就会重新申请存储空间,继续存储字符串.所以一个head节点只存储50字符串长度的数据.
93 //但是,由于我们并没有修改number,所以在其表现来看,我们输入的超过50字符的字符串还是同一个字符串.
94 //这里可以将这个方法,应用到更多的地方.
95
}
96 p - >data[j % 50] = ch;
97 //理解j%50即可.
98 ch = getchar();
99
}
100 Head[i].length = j + 1;
101 //因为我们j是从0开始的.
102
}
103
}
104 105 void Delline() 106 //定义函数Delline(),用来删除确定的字符串.
107 {
108 strnode * p,
*q;
109 int i,
LineNum;
110
while (1) 111 {
112 printf("input the number of line which do you want to delete(0-100),101-exit:\n");
113 scanf("%d", &LineNum);
114 //输入具体要删除的字符串所在的行数.
115
if (LineNum < 0 || LineNum >= MAX) 116
return;
117 i = LineNum;
118 p = Head[i].next;
119 //中间这四行代码同input函数一样
120
if (Head[i].length > 0) 121 //判断所要删除的函数并非空
122
while (p != NULL) 123 //判断所要删除字符串的下一个字符串非空时,采取的处理.
124 {
125 q = p - >next;
126 free(p);
127 p = q;
128 //和之前链表,栈等的删除相同.
129
}
130 Head[i].length = 0;
131 Head[i].number = 0;
132 //确定该字符串的头文件中length,number均为0,等同于消除存在.
133 //正如之前Head[i].length>0这句代码,有时可通过对length,number的判断来显示,该Head是否为空.
134
}
135
}
136 137 void List() 138 //自定义函数List(),用来展示所有的字符串.
139 {
140 strnode * p;
141 int i,
j,
m,
n;
142
for (i = 0; i) 143 //老样子,这个串,说白了数据深度就两层.所以两个循环就OK了.第二个循环在后面.要看成整体,中间只是多个判断和顺序.
144 //其中我是用printf一个字符一个字符地输出的.所以一个字符串就是一层循环.
145 //但是,如果你要换成一个字符串一个字符串的输出,类似putstr.自己改啊.
146 {
147
if (Head[i].length > 0) 148 //这里就验证了之前提到的用length判断是否为空.空时就不会输出该字符串了.
149 {
150 printf("line%d: ", Head[i].number);
151 //输出所要输出字符串所在的行数.
152 n = Head[i].length;
153 m = 1;
154 p = Head[i].next;
155
for (j = 0; j) 156 //第二个循环.用来将字符串中字符一个一个输出.
157
if (j >= 50 * m) 158 //还记得我之前在输入函数中提到的50长度的问题吗?这里就有了解决之道.
159 {
160 p = p - >next;
161 m++;
162
}
163
else 164 printf("%c", p - >data[j % 50]);
165 //输出一个个字符.
166 printf("\n");
167
}
168
}
169 printf("\n");
170
}
171 172 main() 173 //创建主函数mian(),作为程序的入口.控制程序的选择界面,函数调用等.
174 {
175 int sel;
176 Init();
177
while (1) 178 {
179 sel = Menu();
180
switch (sel) 181 {
182
case 1:
183 input();
184
break;
185
case 2:
186 Delline();
187
break;
188
case 3:
189 List();
190
break;
191
case 4:
192 exit(0);
193
}
194
}
195 //主函数不做解释了.这和之前的一模一样啊.
196
}
反思:其中关于块链结构的应用那两段代码,应当好好理解.是个很好用的思路.
接下来是广义表
广义表是一种非线性的列表.是线性表和树的推广.广泛应用与人工智能领域.有兴趣的小伙伴要多多注意了.
实例 099 广义表的存储实例 100 广义表的存储
问题:我将两个问题和在了一起.编程实现用动态链接结构存储广义表与广义表的复制,要求输出该广义表的长度和深度,以及表头和表尾.
逻辑:在一个广义表中,数据元素可以是一个单一元素,也可以是子表,相应地在对应的存储结构中,存储结构节点也有单元素节点和子表节点之分.单元素节点包含值域和指向其后继结点的指针域.对于子表节点,应包含指向子表中第一个节点的表头指针和指向其后继结点的指针域.为了区分广义表中单元素节点和子表元素,我们设置 tag 作为标志,当 tag=0 时表示本节点为单元素,当 tag=1 时表示本节点为子表.
当 tag=1 时,说明其为子表节点,则第二域是 sublist 域,存放的是指向其子表的指针,next 域存放指向与该节点同层的直接后继结点的指针,当该节点是所在层的最后一个节点时,此时 next 为空(和之前链表等,一样).
广义表的复制可以采用递归实现.
其实,广义表这个东西如果有图,看一下图,就立马懂了.我过会儿找找,有的话,就贴上.(我才不会说,我还不知道怎么添加图片呢.)
代码:
1#include 2#include 3 typedef char ElemType;
4 //替换的实现
5 typedef struct lnode 6 //
7 {
8 int tag;
9 union 10 //由于,节点元素要么是单一元素,要么是子表元素.所以采用了union来统一管理,同时实现存储空间的节约.
11 {
12 ElemType data;
13 //用来存放单一元素.
14 struct lnode * sublist;
15 //用来存放子表元素--子表头结点指针地址.
16
}
val;
17 //值域
18 struct lnode * next;
19 //指向后继节点的指针域.
20
}
GLNode;
21 //广义表结构体名.
22 23 void creatGList(struct lnode * *gl) 24 //创建函数creatGList(),用来实现广义表的创建.
25 {
26 char ch;
27 ch = getchar();
28
if (ch == '#') 29 {
30 * gl = NULL;
31
}
32
else if (ch == '(') 33 //输入的字符若是左括号,则建立子表.
34 {
35 * gl = malloc(sizeof(struct lnode));
36 //创建存储空间.
37( * gl) - >tag = 1;
38 //表明是子表
39 creatGList( & (( * gl) - >val.sublist));
40 //调用函数creatGList(),创建子表.
41
}
42
else 43 {
44 * gl = malloc(sizeof(struct lnode));
45 //创建存储空间
46( * gl) - >tag = 0;
47 //表明是字符.
48( * gl) - >val.data = ch;
49 //存储输入的字符.
50
}
51 ch = getchar();
52
if ( * gl == NULL) 53 {
54;
55
}
56
else if (ch == ',') 57 //输入的是",",表示递归构造后继表.
58 {
59 creatGList( & (( * gl) - >next));
60
}
61
else 62( * gl) - >next = NULL;
63
return;
64
}
65 66 void printGList(struct lnode * gl) 67 //创建函数printGList(),用于输出广义表.
68 {
69
if (gl - >tag == 1) 70 //如果类型是子表,则输出括号
71 {
72 printf("(");
73 //先输出左括号
74
if (gl - >val.sublist == NULL) 75 {
76 printf("#");
77
}
78
else 79 {
80 printGList(gl - >val.sublist);
81 //递归输出子表
82
}
83 printf(")");
84 //输出右括号
85
}
86
else 87 //否则,即输出的是字符.
88 printf("%c", gl - >val.data);
89 //直接输出字符.
90
if (gl - >next != NULL) 91 {
92 printf(",");
93 printGList(gl - >next);
94 //递归输出后继表.
95
}
96
return;
97
}
98 99 int GLLength(GLNode * gl) 100 //创建函数GLLength(),用来计算广义表的长度.
101 {
102 int n = 0;
103 gl = gl - >val.sublist;
104
while (gl != NULL) 105 {
106 n++;
107 //长度计数.
108 gl = gl - >next;
109 //指向下一个节点.
110
}
111
return n;
112
}
113 114 int GLDepth(GLNode * gl) 115 //创建函数GLDepth(),用来计算广义表的深度.
116 {
117 int max = 0,
dep;
118
if (gl - >tag == 0) 119
return 0;
120 gl = gl - >val.sublist;
121
if (gl = NULL) 122
return 1;
123
while (gl != NULL) 124 {
125
if (gl - >tag == 1) 126 //类型为子表
127 {
128 dep = GLDepth(gl);
129 //递归计算广义表的深度.
130
if (dep > max) 131 max = dep;
132 //记录下最大深度.
133
}
134 gl = gl - >next;
135 //指向下一个节点.
136
}
137
return (max + 1);
138
}
139 140 GLNode * GLCopy(GLNode * gl) 141 //创建函数GLCopy(),用来实现广义表的复制.
142 {
143 GLNode * q;
144
if (gl == NULL) 145
return NULL;
146 q = (GLNode * ) malloc(sizeof(GLNode));
147 //申请存储空间
148 q - >tag = gl - >tag;
149 //复制元素类型.
150
if (gl - >tag == 1) 151 q - >val.sublist = GLCopy(gl - >val.sublist);
152 //递归复制子表
153
else 154 q - >val.data = gl - >val.sublist;
155 //递归复制数据元素
156 q - >next = GLCopy(gl - >next);
157 //复制next信息,指向下一个节点,递归复制.
158
return q;
159
}
160 GLNode * head(GLNode * gl) 161 //创建函数head(),用来实现计算广义表的表头.
162 {
163 GLNode * p = gl - >val.sublist;
164 //初始化,赋值p.
165 GLNode * q,
*t;
166 //初始化q,t.
167
if (gl == NULL) 168 {
169 printf("NULL\n");
170
return NULL;
171
}
172
if (gl - >tag == 0) 173 //如果广义表为空.
174 {
175 printf("atom is not head! \n");
176 //输出广义表没有表头.
177
return NULL;
178
}
179
if (p - >tag == 0) 180 //如果元素的类型为字符
181 {
182 q = (GLNode * ) malloc(sizeof(GLNode));
183 //为q申请存储空间.
184 q - >tag = 0;
185 //记录数据类型
186 q - >val.data = p - >val.data;
187 //复制数据.
188 q - >next = NULL;
189 //单个元素的下一个节点为空.
190
}
191
else 192 {
193 t = (GLNode * ) malloc(sizeof(GLNode));
194 //申请存储空间.
195 t - >tag = 1;
196 //记录元素的类型为子表
197 t - >val.sublist = p - >val.sublist;
198 //赋值子表
199 t - >next = NULL;
200 //单个元素的下一个节点为空.
201 q = GLCopy(t);
202 //复制子表.
203 free(t);
204 //释放t的存储空间.
205
}
206
return q;
207 //返回q.
208
}
209 210 GLNode * tail(GLNode * gl) 211 //创建函数tail(),用来计算广义表的表尾.
212 //概念同上,故不做注释.参考上一个函数.
213 {
214 GLNode * p = gl - >val.sublist;
215 GLNode * q,
*t;
216
if (gl == NULL) 217 {
218 printf("NULL\n");
219
return NULL;
220
}
221
if (gl - >tag == 0) 222 {
223 printf("atom is not tail!\n");
224
return NULL;
225
}
226 p = p - >next;
227 t = (GLNode * ) malloc(sizeof(GLNode));
228 t - >tag = 1;
229
来源: http://www.cnblogs.com/Tiancheng-Duan/p/5712665.html