【第十周】线性表的链结表表示法+火车浏览器
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 }
学习思考以上老师代码
自主学习火车浏览器的使用方法
浙公网安备 33010602011771号