基于“链表”的算法训练
单链表的基本功训练
1.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
| typedef struct LNode{ int data; struct LNode* link; }Lnode,*Linklist;
void print(Linklist L) { Linklist p = L->link; while(p!=NULL) { printf("%d ",p->data); p = p->link; } printf("\n"); }
void add(Linklist L,int x) { Linklist node = (Linklist)malloc(sizeof(LNode)); node->link = L->link; L->link = node; node->data = x; } void del(Linklist L, int x) { Linklist p = L; while(p->link != NULL) { if(p->link->data == x) { Linklist tmp = p->link; p->link = tmp->link; free(tmp); } else { p = p->link; } } }
int main() { Linklist head = (Linklist)malloc(sizeof(LNode)); head->link = NULL; add(head, 3); add(head, 1); add(head, 2); add(head, 3); add(head, 4); add(head, 3); print(head); del(head,3); print(head); }
|
2.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| typedef struct LNode{ int data; struct LNode* link; }LNode, *Linklist;
void print(Linklist L) { Linklist p = L->link; while(p!=NULL) { printf("%d ",p->data); p = p->link; } printf("\n"); }
void add(Linklist L,int x, Linklist& p) { Linklist node = (Linklist)malloc(sizeof(LNode)); p->link = node; node->data = x; node->link = NULL; p = node; }
void delmin(Linklist L) { Linklist minNodepre = L; Linklist p = L; while(p->link != NULL) { if(p->link->data < minNodepre->link->data) { minNodepre = p; } p = p->link; } Linklist tmp = minNodepre->link; minNodepre->link = tmp->link; free(tmp); }
int main() { Linklist head = (Linklist)malloc(sizeof(LNode)); Linklist p = head; head->link = NULL; add(head, 3,p); add(head, 1,p); add(head, 2,p); add(head, 3,p); add(head, 4,p); add(head, 3,p); print(head); delmin(head); print(head); }
|

3.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| typedef struct LNode{ int data; struct LNode* link; }Lnode,*Linklist;
void print(Linklist L) { Linklist p = L->link; while(p!=NULL) { printf("%d ",p->data); p = p->link; } printf("\n"); }
void add(Linklist L,int x) { Linklist node = (Linklist)malloc(sizeof(LNode)); node->link = L->link; L->link = node; node->data = x; } void delbet(Linklist L, int l, int r) { Linklist p = L; while(p->link != NULL) { if(p->link->data >= l && p->link->data <= r) { Linklist tmp = p->link; p->link = tmp->link; free(tmp); } else { p = p->link; } } }
int main() { Linklist head = (Linklist)malloc(sizeof(LNode)); head->link = NULL; add(head, 3); add(head, 1); add(head, 2); add(head, 5); add(head, 4); add(head, 3); print(head); delbet(head,2,3); print(head); }
|
4.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58
| typedef struct LNode{ int data; struct LNode* link; }Lnode,*Linklist;
void print(Linklist L) { Linklist p = L->link; while(p!=NULL) { printf("%d ",p->data); p = p->link; } printf("\n"); }
void add(Linklist L,int x) { Linklist node = (Linklist)malloc(sizeof(LNode)); node->link = L->link; L->link = node; node->data = x; } void delcommon(Linklist L) { Linklist p = L->link; if(p == NULL) return ; while(p->link != NULL) { if(p->link->data == p->data) { Linklist tmp = p->link; p->link = tmp->link; free(tmp); } else p = p->link; } }
int main() { Linklist head = (Linklist)malloc(sizeof(LNode)); head->link = NULL; add(head, 70); add(head, 51); add(head, 42); add(head, 42); add(head, 42); add(head, 30); add(head, 21); add(head, 10); add(head, 10); add(head, 7); print(head); delcommon(head); print(head); }
|
5.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| typedef struct LNode{ int data; struct LNode* link; }Lnode,*Linklist;
void print(Linklist L) { Linklist p = L->link; while(p!=NULL && p != L) { printf("%4d ",p->data); p = p->link; } printf("\n");
}
void add(Linklist L,int x) { Linklist node = (Linklist)malloc(sizeof(LNode)); node->link = L->link; L->link = node; node->data = x; }
void merge(Linklist L1, Linklist L2) { if(L2->link==L2) { free(L2); return; }
Linklist p = L1; while(p->link != L1) { p=p->link; } p->link = L2->link; while(p->link!=L2) { p=p->link; } p->link = L1; free(L2); }
int main() { Linklist h1 = (Linklist)malloc(sizeof(LNode)); h1->link = h1; add(h1, 70); add(h1, 51); add(h1, 42);
Linklist h2 = (Linklist)malloc(sizeof(LNode)); h2->link = h2; add(h2, 42); add(h2, 42); add(h2, 30); print(h1); print(h2);
merge(h1,h2); print(h1); }
|
真题
2009

1.设计思想
用指针p和q遍历链表
当指针p遍历到第k-1个位置,指针q出发。
指针p遍历到链表尾部时,指针p指向倒数第1个结点,指针q指向倒数第k个节点。
若指针q没有出发,说明链表长度小于k,返回0。否则能找到该值,输出该节点data域的值,返回1。
2.代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| typedef struct LNode{ int data; struct LNode* link; }LNode, *LinkList;
void add(LinkList L, int x) { LinkList node = (LinkList)malloc(sizeof(LNode)); node->link = L->link; node->data = x; L->link = node; }
int GetData(LinkList L, int npos) { LinkList p = L; LinkList q = L; while(p->link != NULL && --npos>0) p=p->link; while(p->link != NULL) { p=p->link; q=q->link; } if(q==L) return 0; else { printf("%d\n", q->data); return 1; } }
int main() { LinkList L = (LinkList)malloc(sizeof(LNode)); L->link = NULL; add(L,1); add(L,2); add(L,3); add(L,4); add(L,5); GetData(L,1); GetData(L,2); GetData(L,3); GetData(L,4); GetData(L,5); GetData(L,6); }
|
3.复杂度
由于只需要遍历一次链表,时间复杂度为O(n),只需要两个额外指针,空间复杂度为O(1)。
4.改进
2012

1.设计思想
- 采用快慢指针,首先对str1和str2链表进行遍历求出两个链表的长度len1和len2。
- 创建两个指针变量p,q分别指向str1和str2,当A的长度大于B时,指针p先出发,当p遍历到第tmp个节点时,q出发,此时p和q到链尾的距离链尾的距离相同。同理,若A的长度小于B时,q先出发。
- 然后p和q继续遍历,若p和q的地址相同,则是共同后缀的起始位置。
1 2 3 4 5 6 7
| 注意读懂题意,题目中的ing已经是共同后缀,指针相同则是位置p
1. 创建指针变量pos,指向str1可能出现的共同后缀起始位置。p和q都出发后。记录当前节点的位置为pos。匹配后面的结点: 1. 若遍历到队尾节点元素对应相等,则pos就是共同后缀的起始位置 2. 若中间有不同节点,则更新当前pos变量 2. 若pos不是队尾,则pos就是共同后缀的起始位置。若pos是队尾且链表队尾元素相同,则最后一个节点元素是共同元素,否则无共同元素;
|
2.代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
| typedef struct LNode { char data; struct LNode* next; }LNode, *LinkList;
void print(LinkList L) { LinkList p = L->next; while(p!=NULL) { printf("%16c",p->data); p = p->next; } printf("\n"); p = L->next; while(p!=NULL) { printf("%16p",p); p = p->next; } printf("\n"); }
int lenstr(char * str) { int len = 0; while(str[len]!='\0') len++; return len; }
void ins(LinkList L1,LinkList L2,char* str1, char* str2, char* str3) { for(int i = lenstr(str3)-1;i>=0; i--) { LinkList tmp = (LinkList)(malloc(sizeof(LNode))); tmp->next = L1->next; tmp->data = str3[i]; L1->next = tmp; tmp->next = L2->next; tmp->data = str3[i]; L2->next = tmp; }
for(int i = lenstr(str1)-1;i>=0; i--) { LinkList tmp = (LinkList)(malloc(sizeof(LNode))); tmp->next = L1->next; tmp->data = str1[i]; L1->next = tmp; } for(int i = lenstr(str2)-1;i>=0; i--) { LinkList tmp = (LinkList)(malloc(sizeof(LNode))); tmp->next = L2->next; tmp->data = str2[i]; L2->next = tmp; } }
int GetLen(LinkList L) { int len = 0; LinkList p = L; while(L->next != NULL) { L=L->next; len++; } return len; }
LinkList findPos(LinkList L1, LinkList L2) { int len1 = GetLen(L1); int len2 = GetLen(L2); LinkList p = L1; LinkList q = L2; if(len1 > len2) { while(len1--!=len2) { p= p->next; } } else { while(len2--!=len1) { q = q->next; } } while(q->next!=NULL && p->next!=NULL) { p = p->next; q = q->next; if(q==p) return q; } return NULL;
}
int main() { LinkList L1 = (LinkList)malloc(sizeof(LNode)); LinkList L2 = (LinkList)malloc(sizeof(LNode)); char str1[] = "load"; char str2[] = "be"; char str3[] = "ing"; ins(L1,L2,str1,str2,str3); print(L1); print(L2); printf("%p",findPos(L1, L2)); }
|
3.复杂度
两次求链表长度,和两次遍历链表,str1长度为m,str2长度为n。时间复杂度为O(m+n),无需额外辅助空间,空间复杂度O(1)。
4.改进
2015

1.设计思想
定义一个辅助布尔数组arr[n]记录该绝对值是否出现过
从链表开始遍历节点,若节点data的绝对值已经出现过,则删除该节点
否则记录该绝对值已经出现过
2.代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66
| typedef struct Node{ int data; struct Node* link; }Node,*LinkList;
void print(LinkList L) { LinkList p = L; while(p->link!=NULL) { p=p->link; printf("%d ", p->data); } printf("\n"); }
void add(LinkList L,int x) { LinkList tmp = (LinkList)malloc(sizeof(Node)); tmp->link = L->link; tmp->data = x; L->link = tmp; }
int abs_(int x) { if(x>0) return x; else return -x; }
void modify(LinkList L, int n) { int* arr = (int*)malloc(sizeof(bool)*(n+1)); for(int i = 0; i<=n;i++) { arr[i] = false; } LinkList p = L; while(p->link!=NULL) { if(arr[abs_(p->link->data)] == true) { LinkList tmp = p->link; p->link = tmp->link; free(tmp); } else { arr[abs_(p->link->data)] = true; p = p->link; } } }
int main() { LinkList L1 = (LinkList)malloc(sizeof(Node)); L1->link = NULL; add(L1,15); add(L1,-7); add(L1,-15); add(L1,-15); add(L1,21); print(L1); modify(L1, 21); print(L1); }
|
3.复杂度
需要遍历一次链表,时间复杂度O(m),需要辅助数组arr[n],空间复杂度O(n)
4.改进
2019

1.设计思想
- 求链表长度,分别取出链表的前半部分,后半部分,前半部分=后半部分,或前半部分+1=后半部分。
- 将链表前半部分进行逆置。
- 将后半部分插入前半部分
2.代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
| typedef struct node { int data; struct node* next; } NODE;
void print(NODE* L) { NODE* p = L; while(p->next!=NULL) { p=p->next; printf("%d ", p->data); } printf("\n"); }
void add(NODE* L, int x) { NODE* node = (NODE*)malloc(sizeof(NODE)); node->data = x; node->next = L->next; L->next = node; }
void rev(NODE* L) { NODE* pre = NULL; NODE* now = L->next; NODE* next = NULL; while(now!=NULL) { next = now->next; now->next = pre; pre = now; now = next; } L->next = pre; }
int getLen(NODE* L) { NODE* p = L->next; int cnt = 0; while(p!=NULL) { p = p->next; cnt++; } return cnt; }
void transfer(NODE* L) { NODE* p = L; int len = getLen(L); int pos = (len+1)/2; while(pos--!=0) { p=p->next; } NODE* L1 = (NODE*)malloc(sizeof(NODE)); L1->next = p->next; p->next = NULL; rev(L1); print(L1); print(L); NODE* pp = L->next; NODE* qq = L1->next;
while (pp && qq) { NODE* tmp1 = pp->next; NODE* tmp2 = qq->next; pp->next = qq; qq->next = tmp1; pp = tmp1; qq = tmp2; } }
int main() { NODE* L = (NODE*)malloc(sizeof(NODE)); add(L,1); add(L,2); add(L,3); add(L,4); print(L); rev(L); print(L); printf("%d\n",getLen(L)); transfer(L); print(L); }
|
3.复杂度
时间复杂度O(n),空间复杂度O(1)
4.改进
单链表的“原地逆置”技巧
1.

- 三指针法
使用 三个指针 pre、now、next遍历链表,逐个反转指针方向。
- 头插法
将原链表节点逐个取出,用 头插法 插入新链表,实现逆置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| typedef struct node { int data; struct node* next; } NODE;
void print(NODE* L) { NODE* p = L; while(p->next!=NULL) { p=p->next; printf("%d ", p->data); } printf("\n"); }
void add(NODE* L, int x) { NODE* node = (NODE*)malloc(sizeof(NODE)); node->data = x; node->next = L->next; L->next = node; }
void rev1(NODE* L) { NODE* now = L->next; NODE* pre = NULL; NODE* next = NULL; while(now!=NULL) { next = now->next; now->next = pre; pre = now; now = next; } L->next = pre; }
void rev2(NODE* L) { NODE* p = L->next; L->next = NULL; while(p!=NULL) { NODE* q = p->next; p->next = L->next; L->next = p; p = q; } }
int main() { NODE* L = (NODE*)malloc(sizeof(NODE)); add(L,1); add(L,2); add(L,3); print(L); rev1(L); print(L); rev2(L); print(L); }
|