数据结构(一)线性表循环链表相关补充
(一)合并两个循环链表
p = rearA->next; //A的头结点,一会还要使用 rearA->next = rearB->next->next; //是A的尾结点指向B的第一个结点 q = rearB->next; //存放B的头结点,需要释放 rearB->next = p; //使B的尾结点指向A的头结点 free(q); //释放B的头结点
(二)判断单链表中是否有环
方法一:使用两个指针,循环嵌套,A指针在外层循环,一步一步向下走,B指针在内层循环,循环到A的位置,当两者的位置相同时判断走的步数是否一致,不一致则代表有环。且能够得到准确的环路节点。其中A是要将链表从头走到尾,B是一直在内层进行循环,时间复杂度为O(n^2)
//两层循环进行判断 Status HasCircle01(List L,int *seq) { List la, lb; int stepa, stepb; la = lb = L; //la在外层循环,lb在内层循环 stepa = stepb = 1; while (la) { while (lb!=la) { lb = lb->next; stepb++; } if (stepa != stepb) break; stepa++; la = la->next; lb = L; stepb = 1; } if (la!=NULL) { *seq = stepb; return TRUE; } return FALSE; }
方法二:使用快慢指针若是有环那么快指针会一直在环中循环,当慢指针进入环中节点后,一定会出现快指针在慢指针后面(或者相等)的情况,就可以判断是否有环,不过这种方法不容易获取到环路节点位置,时间复杂度按照慢指针来算,为O(n)
//快慢指针进行判断 Status HasCircle02(List L) { List high, low; high = low = L; while (low&&high&&high->next) { if (high->next) high = high->next->next; low = low++; if (high == low) return OK; } return FALSE; }
方法三:判断地址的大小
1.栈的地址是由高向低增长的. 2.堆得地址增长方向是由低到高向上增长的
我们创建链表时,一般是使用堆区进行,所以一般机器都是地址向上增长,若是有环,则地址会减小,我们可以使用一个指针,或者一个快指针,将每次的结点地址比较,这样时间复杂度为O(n/2),若是环足够大,我们设置的指针增长步长够大,也会优化更多。
不过有限制,就是我们创建的链表需要地址增长是单向的,就是只能使用尾插法或者头插法,不能使用中间插入或者联合使用
//地址字节进行判断,为了这种方法实现,上面无论是创建直链表还是循环链表都是使用的尾插法 Status HasCircle03(List L) { List high=L; int MaxAddr = 0; while (high&&high->next) { if (high->next) { high = high->next->next; if (MaxAddr < high) MaxAddr = high; else break; } } if (high&&high->next) return TRUE; return FALSE; }
//判断堆增长方向 int StackGrow() { int *a,*b; int flag; a = (int *)malloc(sizeof(int)); b = (int *)malloc(sizeof(int)); if (a > b) flag = 0; else flag = 1; free(a); free(b); return flag; }
其他方法还需要再继续回顾知识后才有思路.....
全部实现代码
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <time.h> #define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 typedef int ElemType; typedef int Status; typedef struct Node { ElemType data; struct Node* next; }Node; typedef struct Node* List; //创建一个列表,有无环,若是有环,将单链表和循环链表合并即可 Status InitList(List* L, int flag, int* sep); //创建一个单链表 Status CreateList(List* L, int n); //创建一个循环链表 Status CreateCList(List* L, int n); //开始进行判断是否有环 //两层循环进行判断 Status HasCircle01(List L,int *seq); //快慢指针进行判断 Status HasCircle02(List L); //地址字节进行判断,为了这种方法实现,上面无论是创建直链表还是循环链表都是使用的尾插法 Status HasCircle03(List L); //用来打印链表 void PrintList(List L,int flag, int seq); int main() { List L = NULL; int seq = 0; //分割点 int flag = 1; printf("create cList?(1/0):"); scanf("%d", &flag); if (!InitList(&L, flag, &seq)) //现在L指向第一个结点 return 0; PrintList(L,flag,seq); if (HasCircle01(L, &seq)) printf("has Circle:%d\n", seq); else printf("no Circle\n"); if (HasCircle02(L)) printf("has Circle\n"); else printf("no Circle\n"); if (HasCircle03(L)) printf("has Circle\n"); else printf("no Circle\n"); system("pause"); return 0; } //创建一个列表,有无环,若是有环,将单链表和循环链表合并即可,这里单链表和循环链表都没有头结点 Status InitList(List* L, int flag,int* sep) { int n; List SL,CL; List q; srand(time(0)); printf("please enter the length of list:"); scanf("%d", &n); *sep = n; if (!CreateList(&SL, n)) //链表创建失败,直接退出 return ERROR; if (flag) //创建一个有环链表 { printf("please enter the length of Clist:"); scanf("%d", &n); if (!CreateCList(&CL, n)) //CL是循环链表头指针 return ERROR; q = SL; for (n = 1; n < *sep; n++) q = q->next; //直接指向单链表的末尾,下面开始合并 q->next = CL; } *L = SL; return OK; } //创建一个单链表 Status CreateList(List* L, int n) { int i; List q,p; if (n < 1) return ERROR; *L = (List)malloc(sizeof(Node)); (*L)->data = rand() % 100; q = *L; for (i = 1; i < n; i++) { p = (List)malloc(sizeof(Node)); p->data = rand() % 100; q->next = p; q = p; } q->next = NULL; return OK; } //创建一个循环链表 Status CreateCList(List* L, int n) { List q,p; ElemType item; if (n < 1) return ERROR; *L = (List)malloc(sizeof(Node)); if (*L == NULL) return ERROR; (*L)->data = rand() % 100; p = *L; p->next = p; //形成回环 for (int i = 1; i < n;i++) { //生成新的节点,根据尾指针添加节点,并实时更新尾指针。注意这里数据插入是尾插法 q = (List)malloc(sizeof(Node)); q->data = rand()%100; q->next = p->next; p->next = q; p = q; } return OK; } //两层循环进行判断 Status HasCircle01(List L,int *seq) { List la, lb; int stepa, stepb; la = lb = L; //la在外层循环,lb在内层循环 stepa = stepb = 1; while (la) { while (lb!=la) { lb = lb->next; stepb++; } if (stepa != stepb) break; stepa++; la = la->next; lb = L; stepb = 1; } if (la!=NULL) { *seq = stepb; return TRUE; } return FALSE; } //快慢指针进行判断 Status HasCircle02(List L) { List high, low; high = low = L; while (low&&high&&high->next) { if (high->next) high = high->next->next; low = low++; if (high == low) return OK; } return FALSE; } //地址字节进行判断,为了这种方法实现,上面无论是创建直链表还是循环链表都是使用的尾插法 Status HasCircle03(List L) { List high=L; int MaxAddr = 0; while (high&&high->next) { if (high->next) { high = high->next->next; if (MaxAddr < high) MaxAddr = high; else break; } } if (high&&high->next) return TRUE; return FALSE; } //用来打印链表 void PrintList(List L, int flag, int seq) { List CHead; List q = L; //获取头指针 int i; if (!flag) { while (q) { printf("%d ", q->data); q = q->next; } } else { for (i = 1; i <= seq; i++) { printf("%d ", q->data); q = q->next; } //for循环退出就进入了循环链表范围内 printf("-|- "); CHead = q; while (q->next != CHead) { printf("%d ", q->data); q = q->next; } printf("%d", q->data); } printf("\n"); }
测试结果
(三)魔术师发牌问题
一共13张黑牌1-13,预先排好顺序,牌面朝下,开始数数, 数1翻开第一张牌为1取出, 数1,2,将喊到1的牌放在末尾,喊到2的牌翻开为2取出 喊1,2,3,将喊到1和2的牌放到末尾,将喊到3的牌翻开取出 ...... 喊到13,直到将所有牌取出。 正好翻开顺序为1-2-....-13 我们需要找到他排列的预先顺序
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <time.h> #define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 typedef int ElemType; typedef int Status; typedef struct Node { ElemType data; struct Node* next; }Node; typedef struct Node* CLinkList; //初始化链表 Status InitList(CLinkList* L, int CardCount); //获取牌的顺序 Status Magician(CLinkList* L, int CardCount); //用来打印链表 void PrintList(CLinkList rear); int main() { CLinkList L = NULL; CLinkList p; ElemType e; int CardCount = 13; InitList(&L, CardCount); //初始化13个节点,无头结点 Magician(&L, CardCount); //获取牌的顺序 PrintList(L); //开始打印 system("pause"); return 0; } //初始化带有头结点的链表,初始化牌数的结点,数据域都设为0 Status InitList(CLinkList* L, int CardCount) { CLinkList rear, q; //rear是尾结点 rear = q = NULL; for (int i = 0; i < CardCount; i++) { if (*L == NULL) { *L = (CLinkList)malloc(sizeof(Node)); if (!(*L)) return ERROR; (*L)->data = 0; (*L)->next = *L; //自己指向自己 rear = *L; //设置尾指针位置 } else { //生成新的节点,根据尾指针添加节点,并实时更新尾指针。注意这里数据插入是尾插法 q = (CLinkList)malloc(sizeof(Node)); q->data = 0; q->next = rear->next; rear->next = q; rear = q; } } return OK; }
//获取牌的顺序 Status Magician(CLinkList* L, int CardCount) { CLinkList start = *L; int CardNumber = 1; int i; if (CardCount < 1 || (*L) == NULL) return ERROR; while (1) { //无论是第一次还是后面,我们现在指针指向的当前牌都是数值为0的牌,我们只需要再向下找CardNumber-1张就可以找到下一张赋值的牌。 for (i = 1; i <= CardNumber-1; i++) { //一直找到下一张空闲的位置,就是完成一次for循环 start = start->next; while (start->data != 0) start = start->next; } start->data = CardNumber; CardNumber++; if (CardNumber > CardCount) //注意这个判断要在while前面,不然会造成死循环 break; //当上面一张牌被赋值后,我们就将指针指向下一张空闲的牌,即为第一张, start = start->next; while (start->data!=0) start = start->next; } return OK; } //用来打印链表 void PrintList(CLinkList L) { CLinkList q = L; //获取头指针 while (q->next != L) { printf("黑桃%d ", q->data); q = q->next; } printf("%d\n", q->data); }
(四)拉丁方阵
nXn方阵,方阵中恰有n中不同的元素,例如1-n,现在希望每行每列中元素不重复。打印出来
思路:一个循环链表1-n,从开始结点读一圈后,将结点移动下一个再读取一圈,直到将结点移动到最后就结束打印
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> #include <time.h> #define OK 1 #define ERROR 0 #define TRUE 1 #define FALSE 0 typedef int ElemType; typedef int Status; typedef struct Node { ElemType data; struct Node* next; }Node; typedef struct Node* CLinkList; //初始化链表 Status InitList(CLinkList* L, int number); //用来打印链表 void PrintList(CLinkList rear); int main() { CLinkList L = NULL; CLinkList p; int number = 5; InitList(&L, number); //现在L指向第一个结点 p = L; //开始打印每一行数据 for (int i = 0; i < number;i++) { PrintList(p); p = p->next; } system("pause"); return 0; } //初始化带有头结点的链表,初始化牌数的结点,数据域都设为0 Status InitList(CLinkList* L, int number) { CLinkList rear, q; //rear是尾结点 rear = q = NULL; for (int i = 1; i <= number; i++) { if (*L == NULL) { *L = (CLinkList)malloc(sizeof(Node)); if (!(*L)) return ERROR; (*L)->data = i; (*L)->next = *L; //自己指向自己 rear = *L; //设置尾指针位置 } else { //生成新的节点,根据尾指针添加节点,并实时更新尾指针。注意这里数据插入是尾插法 q = (CLinkList)malloc(sizeof(Node)); q->data = i; q->next = rear->next; rear->next = q; rear = q; } } return OK; } //用来打印链表 void PrintList(CLinkList L) { CLinkList q = L; //获取头指针 while (q->next != L) { printf("%d ", q->data); q = q->next; } printf("%d\n", q->data); }