【第十周】线性表的链结表表示法+火车浏览器

  1 .h
  2 // 定义循环链结表元素类型为整数。
  3 typedef int ElemType; 
  4 
  5 // 定义循环链结表节点
  6 typedef struct node {  
  7   ElemType elem; // 节点数据,整数。
  8   struct node* prev; // 节点的前链,递归定义。
  9   struct node* next; // 节点的后链,递归定义。 
 10 } Node; // 节点型态 
 11 
 12 // 节点的链,是节点的指针。
 13 typedef Node* Link; 
 14 
 15 // 循环双向链结表使用双节点指针,指向头节点和尾节点。
 16 typedef struct {
 17   Link head; // 头节点指针 
 18   Link tail; // 尾节点指针
 19 } DList; 
 20 
 21 // 初始化循环双向链结表。
 22 void initial(DList *);
 23 // 取得循环双向链结表的元素个数。
 24 int getSize(DList); 
 25 
 26 // 取出循环双向链结表的第 i 个元素, 返回该元素的值。
 27 // 若 inx<0, 向左移动;若 inx>0, 向右移动。
 28 // 若 |inx| 大于双向链结表的元素个数,则循环重复计数
 29 ElemType getElem(DList, int);
 30 
 31 // 搜寻循环双向链结表的元素。若成功,返回元素位置;否则,返回 -1。
 32 int search(DList, ElemType);  
 33 
 34 // 将一个元素插入到循环双向链结表,返回该元素的位置。
 35 int insert(DList *, ElemType); 
 36 
 37 // 从循环双向链结表删除一个元素。若成功,返回该元素原来的位置;
 38 // 否则,返回 -1。
 39 int delete(DList *, ElemType); 
 40 
 41 // 将循环双向链结表清空。
 42 void clear(DList *);
 43 
 44 // 檢查循环双向链结表是否為空表。若是空,返回 1;否则,返回 0。
 45 int is_empty(DList); 
 46 
 47 // 打印循环双向链结表元素。
 48 void printlst(DList); 
 49 
 50 
 51 .c
 52 // 循环单链有序线性表基本操作介面
 53 #include <stdio.h>
 54 #include <stdlib.h>
 55 #include "sorted_list_circular_double_list.h"
 56 
 57 // 初始化双向链结表 L。
 58 void initial(DList *L) {
 59   L->head = NULL; // 将双向链结表的头节点指针设为空值。
 60   L->tail = NULL; // 将双向链结表的尾节点指针设为空值。 
 61 }
 62 
 63 // 取得双向链结表 L 的元素个数。 
 64 int getSize(DList L) {
 65   Link current = L.head; // 节点指针。 
 66   int size = 0; // 线性表个数初始值设为 0.
 67 
 68   if (current==NULL) return size; // 如果线性表为空,则元素个数为 0.
 69          
 70   do { // 加一个节点计数。 
 71     size++; // 个数加 1。 
 72     current = current->next; // 下一个节点。
 73   } while (current!=L.head); // 如果不是尾节点,继续搜寻。
 74   return size; // 线性表 L 的元素个数。     
 75 }
 76 
 77 // 取出循环双向链结表的第 i 个元素, 返回该元素的值。
 78 // 若 inx<0, 向左移动;若 inx>0, 向右移动。
 79 // 若 |inx| 大于双向链结表的元素个数,则循环重复计数。 
 80 ElemType getElem(DList L, int inx) {
 81   Link current = L.head; // 头节点指针。
 82   int i; // 循环变量。 
 83     
 84   if (current==NULL) return -1; // 如果线性表为空,则无此元素。
 85     
 86   // 循环线性表,如果 inx>size, 会重复 L 的头节点。
 87   for (i=0; i<abs(inx); i++)
 88     if (inx>0) current = current->next; // 往右移到下一个元素。 
 89     else current = current->prev; // 往左移到前一个元素。
 90   return current->elem; // 返回节点的元素值。 
 91 } 
 92 
 93 // 搜寻循环双向链结表的元素。若成功,返回元素位置;否则,返回 -1。
 94 int search(DList L, ElemType e) {
 95   Link current = L.head; // 头节点指针。  int position=0; // 元素位置,设定初始值为 0。
 96     
 97   if (current==NULL) return -1; // 如果循环双向链结表为空,则无此元素。
 98     
 99   do { // 循环双向链结表不是空的,至少做一次。 
100     if (current->elem==e) return position; // 搜寻成功,返回元素 e 的位置。 
101     else if (current->elem<e) { // 还有其它节点,继续搜寻。
102       position++; // 位置加 1。  
103       current = current->next; // 移到下一个节点。 
104     }
105     else return -1; // 节点的值已超过,搜寻失败。 
106   } while (current!=L.head); // 若回到头节点,则停止循环。 
107   return -1; // 已经超过循环双向链结表的最后一个节点,搜寻失败。 
108 } 
109 
110 // 将一个元素插入到循环双向链结表,返回该元素的位置。
111 int insert(DList *L, ElemType e) {
112   Link current = L->head; // 指向头节点。
113   Link previous = L->tail; // 指向尾节点。 
114   Link newNode; // 新节点的指针。
115   int size = getSize(*L); // 循环双向链结表的元素个数。 
116   int position=0; // 目前节点位置。
117   
118   if (current==NULL) { // Case 1:当循环双向链结表为空时。 
119     newNode = (Link) malloc(sizeof(Node)); // 要求一个新节点的内存。
120     newNode->elem = e; // 复制节点的数据。
121     newNode->prev = newNode; // 循环双向链结表只有一个节点,prev 指向自己。
122     newNode->next = newNode; // 循环双向链结表只有一个节点,next 指向自己。
123     L->head = newNode; // 设定循环双向链结表只头节点。 
124     L->tail = newNode; // 设定循环双向链结表只尾节点。 
125     return position; // 头节点的位置为 0。
126   }
127   
128   for (position=0; position<size; position++) { // 寻找插入位置。
129     if (current->elem>=e) break; // 找到第一个元素大于或等于 e 的节点。
130     previous = current; // 下一个 previous。 
131     current = current->next; // 下一个 current。      
132   } 
133   // Cases 2, 3 & 4: 插入一个节点的 current 之前。
134   newNode = (Link) malloc(sizeof(Node)); // 要求一个新节点的内存。
135   newNode->elem = e; // 复制目前节点的数据。 
136   newNode->prev = previous; // 设定新节点的的 prev 指针。
137   previous->next = newNode; // 将前一节点的 next 指针指向新节点。 
138   newNode->next = current; // 设定新节点的 next 指针。
139    current->prev = newNode; // 将现节点的 prev 指针指向新节点。
140   if (position==0) L->head = newNode; // Case 2:更新头节点。
141   else if (position==size) L->tail = newNode; // Case 4: 更新尾节点。
142   return position; // 返回位置。
143 }
144 
145 // 从循环双向链结表删除一个元素。若成功,返回该元素原来的位置;
146 // 否则,返回 -1。
147 int delete(DList *L, ElemType e) {
148   Link current = L->head; // 指向头节点。
149   Link previous = L->tail; // 指向尾节点。 
150   int position = 0; // 目前节点位置。
151     
152   if (current==NULL) return -1; // 若循环双向链结表为空,删除失败。
153     
154   do { // 当循环双向链结表还有节点。
155     if (current->elem==e) { // 找到要删除的节点。
156       if (position==0) { // 删除的节点是头节点。
157         if (current->next==current) { // 循环双向链结表只有一个节点。 
158           L->head = NULL; // 将头节点清空。 
159           L->tail = NULL; // 将尾节点清空。
160         }
161         else {
162           previous->next = current->next; // 修改前一个节点的 next 指针。
163           current->next->prev = previous; // 修改下一个节点的 prev 指针。 
164           L->head = current->next; // 更新头节点。 
165         } 
166         free(current); // 释放删除的节点。
167         return position; // 返回删除节点的原来位置。 
168       }
169       else {
170         if (current->next==L->head) // 删除的是尾节点。
171           L->tail = previous; // 更新尾节点。 
172         previous->next = current->next; // 修改前一个节点的 next 指针。 
173         current->next->prev = previous; // 修改下一个节点的 prev 指针。
174         free(current); // 释放删除的节点。
175         return position; // 返回删除节点的原来位置。 
176       } 
177     }
178     else if (current->elem<e) { // 检查下一个节点。 
179       previous = current; // 将目前的节点设为前一个节点。 
180       current = current->next; // 将下一个节点设为下一步骤的目前节点。 
181       position++; // 位置加 1。
182     }
183     else return -1; // 目前节点数据已超过删除的值;删除失败。
184   } while (current!=L->head); // 若还没回到头节点,继续检查。
185 
186   return -1; // current 回到头节点,删除失败。
187 }
188 
189 // 将循环双向链结表清空。
190 void clear(DList *L) {
191   Link current = L->head; // 指向目前的节点。
192   Link previous; // 前一个节点。 
193     
194   while (current->next!=L->head) { // 不是尾节点。
195     previous = current; // 目前的节点。 
196     current = current->next; // 下一个节点。
197     free(previous); // 释放目前的节点。 
198   }
199   free(current); // 释放尾节点。
200   L->head = NULL; // 将循环双向链结表的头节点设为空。 
201   L->tail = NULL; // 将循环双向链结表设尾节点设为空。 
202 }
203 
204 // 檢查循环双向链结表是否為空表。若是空,返回 1;否则,返回 0。
205 int is_empty(DList L) {
206   return L.head==NULL; // 若 L 的头节点为空,返回 1,否则,返回 0。 
207 } 
208 
209 // 打印循环双向链结表元素。
210 void printlst(DList L) {
211   Link current = L.head; // 指向目前的节点。
212   int position = 0; // 目前节点的位置。 
213     
214   printf("线性表元素个数:%3d 元素\n", getSize(L));
215 
216   if (current!=NULL) { // 循环双向链结表不是空的,开始打印。 
217     while (current->next!=L.head) { // 还有节点要打印。
218       printf("%3d ", current->elem); // 打印节点数据。
219       if ((position+1)%20==0) printf("\n"); // 满 20 个元素,打印换行。
220       current = current->next; // 移到下一个节点。 
221       position++; // 下一个位置。 
222     }  
223     printf("%3d \n", current->elem); // 打印尾节点数据和换行。 
224   }
225   printf("\n"); // 打印一个换行。 
226 } 
227 
228 
229 main.c
230 #include <stdio.h>
231 #include <stdlib.h>
232 #include <time.h>
233 #include "sorted_list_circular_double_list.h"
234 
235 // 合并 (merge) 两个循环线性表 L1 和 L2。
236 // 返回合并后的循环线性表。 
237 DList merge_list(DList L1, DList L2) {
238   DList L; // 合并后的循环双向链结表。   
239   int size1=getSize(L1), size2=getSize(L2); // 循环双向链结表 L1,和 L2 的大小。  
240   ElemType e1, e2; // 循环双向链结表 L1 和 L2 的元素。 
241   int i1=0, i2=0; // 循环双向链结表 L1 和 L2 的索引。
242     
243   initial(&L); // 初始化 L。 
244     
245   // i1<L1.size: l1 还有元素待合并。
246   // i2<L2.size: l2 还有元素待合并。 
247   while (i1<size1 && i2<size2) {
248     e1 = getElem(L1, i1); // 取得 L1 的元素。
249     e2 = getElem(L2, i2); // 取得 L2 的元素。
250     // 将 L1 和 L2 较小的元素放到 L 中,并移到下一个元素的位置。 
251     if (e1<=e2) {insert(&L, e1); i1++;}    else {insert(&L, e2); i2++;}
252   }
253     
254   // L1 还有元素,继续复制 L1 的元素。
255   while (i1<size1) insert(&L, getElem(L1, i1++));
256     
257   // L2 还有元素,继续复制 L2 的元素。 
258   while (i2<size2) insert(&L, getElem(L2, i2++));
259     
260   return L; // 返回合并后的循环双向链结表。  
261 } 
262 
263 // 合自 L1 中移除 (remove) 所有 L2 的元素。
264 // 返回移除后的循环双向链结表。 
265 DList remove_list(DList L1, DList L2) {
266   DList L; // 移除后的循环双向链结表。   
267   int size1=getSize(L1), size2=getSize(L2); // 循环双向链结表 L1,和 L2 的大小。  
268   ElemType e1, e2; // 循环双向链结表 L1 和 L2 的元素。 
269   int i1=0, i2=0; // 循环双向链结表 L1 和 L2 的索引。
270     
271   initial(&L); // 初始化 L。
272       // i1<size1: l1 还有元素待移除。
273   // i2<size2: l2 还有元素待移除。 
274   while (i1<size1 && i2<size2) {
275     e1 = getElem(L1, i1); // 取得 L1 的元素。
276     e2 = getElem(L2, i2); // 取得 L2 的元素。 
277     // 若 L1 和 L2 的元素相同,移除 L1 的元素,不储存。 
278     if (e1==e2) i1++;
279     // 若 L1 的元素小于 L2 的元素,将 L1 的元素放到 L 中。  
280     else if (e1<e2) {insert(&L, e1); i1++;}
281     // 若 L1 的元素大于 L2 的元素,检查下一个 L2 的元素。 
282     else i2++;
283   } 
284     
285   // L2 已经没有元素;但是,L1 还有元素,继续将 L1 的元素放到 L 中。  
286   while (i1<size1) insert(&L, getElem(L1, i1++));
287     
288   return L; // 返回移除后的循环双向链结表。   
289 } 
290 
291 int main(void) {
292   DList L, L1, L2; // 声明循环双向链结表。 
293   int leng1, leng2; // 两个循环双向链结表的长度。   int i; // 循环变量。 
294     
295   initial(&L1); // 初始化 L1。
296   initial(&L2); // 初始化 L2。
297     
298   srand(time(NULL)); // 随机数生成器的种子。
299     
300   // 输入循环双向链结表 L1 的元素个数。 
301   do {
302     printf("输入循环双向链结表 L1 的元素个数 (1 与 100 (含)之间):");
303     scanf("%d", &leng1);
304   } while (leng1<0 || leng1>100);
305   
306   // 输入循环双向链结表 L2 的元素个数。 
307   do {
308     printf("输入循环双向链结表 L2 的元素个数 (1 与 100 (含)之间):");
309     scanf("%d", &leng2);
310   } while (leng2<0 || leng2>100); 
311   printf("-------------------------------------------\n");
312 
313   // 随机产生 L1 的元素。 
314   for (i=0; i<leng1; i++) insert(&L1, rand() % 100); // 插入 L1 的元素。
315   printf("循环双向链结表 L1:\n");
316   printlst(L1); // 打印循环双向链结表 L1。 
317   
318   // 随机产生 L2 的元素。 
319   for (i=0; i<leng2; i++) insert(&L2, rand() % 100); // 插入 L2 的元素。 
320   printf("循环双向链结表 L2:\n"); 
321   printlst(L2); // 打印循环双向链结表 L2。
322 
323   L = merge_list(L1, L2); // 合并 L1 和 L2。 
324   printf("合并 L1 和 L2 的循环双向链结表:\n"); 
325   printlst(L); // 打印合并的循环双向链结表。
326     
327   L = remove_list(L1, L2); // 从 L1 移除 L2。 
328   printf("从 L1 移除 L2 的循环双向链结表:\n"); 
329   printlst(L); // 打印移除的循环双向链结表。
330 
331   return 0; 
332 } 

学习思考以上老师代码

自主学习火车浏览器的使用方法

posted @ 2020-05-14 21:11  R洛溪宸  阅读(163)  评论(0)    收藏  举报