(一)合并两个循环链表
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
", seq);
else
printf("no Circle
");
if (HasCircle02(L))
printf("has Circle
");
else
printf("no Circle
");
if (HasCircle03(L))
printf("has Circle
");
else
printf("no Circle
");
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("
");
}
测试结果
(三)魔术师发牌问题
一共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
", 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
", q->data);
}