③.链表
一:定义
链表是一系列不存在内存中相连的结构组成。每一个结构均含有表元素和指向包含该元素后继元素的结构的指针:Next指针。最后一个单元的Next指向NULL

为了方便,我们使用表头(header)或哑节点(dummy node)来指向第一个节点

二:操作
具体链表例程见代码
1 #include <stdio.h> 2 #include <stdlib.h> 3 4 typedef struct Node *Position; 5 typedef struct Node *List; 6 7 struct Node{ //链表节点定义 8 int Element; 9 Position Next; 10 }; 11 12 void CreateList(List L){ 13 int a; 14 Position pre,cur; 15 pre = cur = L; 16 scanf_s("%d", &a); 17 while (a != -1){ 18 cur = (Position)malloc(sizeof(struct Node)); 19 cur->Element = a; 20 pre->Next = cur; 21 pre = cur; 22 scanf_s("%d", &a); 23 } 24 cur->Next = NULL; //不忘 25 } 26 27 void PrintList(List L){ 28 Position P; 29 P = L->Next; 30 while (P != NULL){ 31 printf("%d ", P->Element); 32 P = P->Next; 33 } 34 } 35 int isEmpty(List L){ 36 return L->Next==NULL; 37 } 38 39 int isLast(Position P, List L){ 40 return P->Next == NULL; 41 } 42 43 Position Find(int X, List L){ 44 L = L->Next; 45 while (L != NULL&&L->Element != X){ 46 L = L->Next; 47 } 48 return L; 49 } 50 51 Position FindPrevious(int X, List L){ 52 while (L->Next != NULL&&L->Next->Element != X){ 53 L = L->Next; 54 } 55 return L; 56 } 57 58 void Delete(int X, List L){ 59 Position Temp; 60 Position P = FindPrevious(X, L); 61 if (!isLast(P, L)){ 62 Temp = P->Next; 63 P ->Next = P->Next->Next; 64 free(Temp); 65 } 66 } 67 68 void Insert(int X, Position P){ 69 Position Temp = (Position)malloc(sizeof(struct Node)); 70 Temp->Next = P->Next; 71 P->Next = Temp; 72 Temp->Element = X; 73 } 74 75 void DeleteList(List L){ 76 Position Temp, P; 77 P = L->Next; 78 while (P != NULL){ 79 Temp = P->Next; 80 free(P); 81 P = Temp; 82 83 } 84 L->Next = NULL; //不忘 85 86 87 88 }
唯一要注意的就是链表末节点->Next=NULL这句不要忘记
三:应用
①计数排序:
如果我们有N个整数,范围从1到M,则我们可以有M个空位置,也就是留置一个数组,称为Count,大小为M,初始化为0.当Ai读入时,Count[Ai]加1,等全部输入读进后,扫描一遍数组就可以打印输出排好序的表。 时间复杂度:O(M+N)

A是原始数据, C是每个数据重复了几次,相当于Count数组,C’是Count[i]+Count[i-1], 然后逆序把数字一个个重新填放入B数组,就是排好序的啦,具体代码实现见基数排序方法二。
②基数排序:
举例来说明:假设我们有10个数,范围在0-999之间,这样我们就不能用计数排序,因为M太大了。所以我们就想用多趟计数排序。并且,此时是用最低位优先的方式进行计数排序。
eg:我们现在有0,1,512,343,64,125,216,27,8,729 十个数:
第一次:
(按个位排序)
然后再按照顺序收起来:0,1,512,343,64,125,216,27,8,729
第二次:
(按十位排序)
然后再按照顺序收起来:0,1,8,512,216,125,27,729,343,64
第三次:
(按百位排序)
这次收起来就排好序了:0,1,8,27,64,125,216,343,512,729
时间复杂度:O(P(N+B)) P是排序趟数,N是排序元素的个数,B是Count
代码一:
这个使用一个二维数组当做一个个桶,每个桶的第一个数据代表桶内有几个数字,初始置为0。
1 int GetDigit(int x, int d){ //取得x的第d位数,个位为第1位数依次递增 2 int a[10] = { 0,1, 10, 100, 1000 }; 3 4 return (x / a[d])% 10; 5 } 6 7 8 void Radix_Sort(int *a, int length){ 9 int Bucket[10][100]; //假设一个桶最多能装100个数据 10 int i, num,j, k = 0; 11 for (num = 1; num < 4; num++){ 12 k = 0; 13 for (i = 0; i < 10; i++){ 14 Bucket[i][0] = 0; //这位记录第i个桶有多少个数据 15 } 16 for (i = 0; i < length; i++){ 17 Bucket[GetDigit(a[i], num)][++Bucket[GetDigit(a[i], num)][0]] = a[i]; //把数据放到相应的桶中 18 } 19 20 for (i = 0; i < 10; i++){ //从桶中回收数据 21 for (j = 1;j <= Bucket[i][0]; j++){ 22 a[k++] = Bucket[i][j]; 23 } 24 } 25 } 26 }
代码二:
这个就完全用到的是计数排序的实现方法,用了一个Count数组。 就是计数排序重复来个几次。
1 int GetDigit(int x, int d){ //取得x的第d位数,个位为第1位数依次递增 2 int a[10] = { 0,1, 10, 100, 1000 }; 3 4 return (x / a[d])% 10; 5 } 6 7 8 void Radix_Sort(int *a, int length){ 9 int Count[10]; //Count数组是每个桶里有多少个数字 10 int Bucket[10]; 11 int i, j; 12 for (j = 1; j < 4; j++){ 13 for (i = 0; i < 10; i++){ //初始置零 14 Count[i] = 0; 15 } 16 for (i = 0; i < length; i++){ 17 Count[GetDigit(a[i], j)]++; 18 } 19 for (i = 1; i < 10; i++){ //这一步就很像计数排序的方法了 20 Count[i] += Count[i - 1]; 21 } 22 for (i = length - 1; i >= 0; i--){ 23 Bucket[--Count[GetDigit(a[i], j)]] = a[i]; 24 } 25 for (i = 0; i < 10; i++){ 26 a[i] = Bucket[i]; 27 } 28 } 29 }
四.链表的游标实现
如果有些语言没有指针,那链表不就没有next指针指来指去了吗!所以这里我们就有游标(cursor)实现法:
我们用数组下标代表地址,所以需要有一个全局的结构体数组。
比如这张图:
对于Next,0代表的指针是null.然后这张表显示了两个链表,分别是3-7-8-2:cdf 以及:5-10-1-9:abe
然后0-6-4-0代表的是没分配出去的地址:therefore
引出在游标实现中的Malloc和free:
malloc:要新给出一个地址,所以将表头后面的第一个元素删除,按照上表就是0-4-0,如果没有空间可以用了,那返回就是0
free:回收一个,放到最前面:(比如回收8)就是0-8-6-4-0
一些习题:
3.5:
L和P已经排序,求L∪P
1 void Union(List L, List P){ 2 List Final = (List)malloc(sizeof(struct Node));//最终并集的结果 3 Final->Next = NULL; 4 L = L->Next; P = P->Next; 5 while (L != NULL&&P != NULL){ 6 if (L->Element > P->Element){ 7 Insert(L->Element, Final); 8 L = L->Next; 9 } 10 else if (L->Element < P->Element){ 11 Insert(P->Element, Final); 12 P = P->Next; 13 } 14 else{ 15 Insert(P->Element, Final); 16 L = L->Next; P = P->Next; 17 } 18 19 } 20 while (L != NULL){ 21 Insert(L->Element, Final); 22 L = L->Next; 23 } 24 while (P != NULL){ 25 Insert(P->Element, Final); 26 P = P->Next; 27 } 28 }
3.12:
反转单链表 用O(N)的时间。
如果用栈的话就要用O(N)的空间,不用栈的话只要常数附加空间
1 法1: 2 List pre, temp, temp1; 3 List cur = (List)malloc(sizeof(struct Node)); 4 CreateList(cur); 5 PrintList(cur); printf("\n"); 6 pre = NULL; temp1 = cur; cur = cur->Next; 7 while (cur != NULL){ 8 temp = cur->Next; 9 cur->Next = pre; 10 pre = cur; 11 cur = temp; 12 } 13 temp1->Next = pre; 14 15 16 17 18 19 法2: 20 //用栈 卧槽 21 int top = -1; List temp1, temp; 22 List cur = (List)malloc(sizeof(struct Node)); 23 temp = temp1 = cur; 24 CreateList(cur); 25 PrintList(cur); printf("\n"); 26 Position stack[200]; 27 cur = cur->Next; 28 while (cur != NULL){ 29 stack[++top] = cur; 30 cur = cur->Next; 31 } 32 while (top != -1){ 33 temp->Next = stack[top--]; 34 temp = temp->Next; 35 36 } 37 temp->Next = NULL; 38

浙公网安备 33010602011771号