本篇文章的代码是在上一篇代码的基础上,
实现了链表的合并;
实现了约瑟夫环;
并且判断两条链表是否有交点等功能;
检查链表是否为环,若是环又如何去计算环的长度以及查找环的入口;
查找环的入口思路如图示:
要实现的接口如下:
#ifndef _SLIST_H__
#define _SLIST_H__
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
typedef int DataType;
typedef struct Node
{
DataType data;
struct Node *next;
}Node,*pNode,*pList;
pNode BuyNode(DataType d);
void InitList(pList* pplist);
void PrintfList(pList plist);
void PushBack(pList* pplist,DataType d);
void PopBack(pList* pplist);
pNode FindMidNode(pList plist);
void DelKNode(pList *plist, int k);
int GetLength(pList plist);
pNode Find(pList plist, DataType d);
void ReversePrint(pList plist);
pList MergeList(pList plist1, pList plist2);
pNode CheckCycle(pList cplist);
int GetCycleLength(pNode meet);
pNode GetCycleEnter(pList plist, pNode meet);
int CheckCross(pList plist1, pList plist2);
pNode JosephCycle(pList *pplist, int num);
#endif
基本函数接口的实现:
#include "SList.h"
void InitList(pList* pplist)
{
assert(pplist);
*pplist = NULL;
}
pNode BuyNode(DataType d)
{
pNode newnode = (pNode)malloc(sizeof(Node));
if (newnode == NULL)
{
perror("malloc");
return NULL;
}
newnode->data = d;
newnode->next = NULL;
return newnode;
}
void PrintfList(pList plist)
{
pNode cur = plist;
if (plist == NULL)
{
printf("列表为空\n");
return;
}
while (cur)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
void PushBack(pList* pplist, DataType d)
{
assert(pplist);
pNode pNewNode = BuyNode(d);
if (*pplist == NULL)
{
*pplist = pNewNode;
}
else
{
pNode cur = *pplist;
while (cur->next)
{
cur = cur->next;
}
cur->next = pNewNode;
}
}
void PopBack(pList* pplist)
{
assert(pplist);
pNode cur = *pplist;
if (cur == NULL)
{
printf("列表为空\n");
return;
}
if (cur->next == NULL)
{
*pplist = NULL;
printf("删除成功\n");
return;
}
while (cur->next->next)
{
cur = cur->next;
}
pNode del = cur->next;
cur -> next = NULL;
free(del);
printf("删除成功\n");
}
pNode FindMidNode(pList plist) //找中间结点
{
pNode fast = plist;
pNode slow = plist;
if (plist == NULL)
{
return NULL;
}
while (fast&&fast->next)
{
fast = fast->next->next;
slow = slow->next;
}
return slow;
}
void DelKNode(pList *pplist, int k) //删除倒数第K个结点(K>1&&K<链表的总长度)
{
assert(pplist);
if (k<=1 || k>=GetLength(*pplist))
{
printf("输入不合法\n");
return;
}
pNode fast = *pplist;
pNode slow = *pplist;
while (fast->next)
{
fast = fast->next;
k--;
if (k < 0)
{
slow = slow->next;
}
}
pNode del = slow->next;
slow->next = slow->next->next;
free(del);
printf("删除成功\n");
}
int GetLength(pList plist)
{
pNode cur = plist;
int count = 0;
if (plist == NULL)
{
return -1;
}
while (cur)
{
cur = cur->next;
count++;
}
return count;
}
pNode Find(pList plist, DataType d)
{
pNode cur = plist;
while (cur)
{
if (cur->data == d)
return cur;
else
cur = cur->next;
}
return NULL;
}
逆序打印采用的是递归调用:
void ReversePrint(pList plist)
{
pNode cur = plist;
if (plist == NULL)
{
return;
}
if (cur->next)
{
ReversePrint(cur->next);
}
printf("%d->", cur->data);
}
合并两条链表的关键是确定新链表的头结点,再将两条链表的结点按大小顺序插入到头结点后面。
pList MergeList(pList plist1, pList plist2)
{
pList newlist = NULL;
pNode tail = NULL;
if (plist1 == plist2) //两条链表相同
return plist1;
if (plist1 == NULL) //一条链表为空
return plist2;
if (plist2 == NULL)
return plist1;
if (plist2->data <= plist2->data) //确定头结点
{
newlist = plist1;
plist1 = plist1->next;
}
else
{
newlist = plist2;
plist2 = plist2->next;
}
tail = newlist;
while (plist1 != NULL&&plist2 != NULL)
{
if (plist1->data <= plist2->data)
{
tail->next = plist1;
tail = plist1;
plist1 = plist1->next;
}
else
{
tail->next = plist2;
tail = plist2;
plist2 = plist2->next;
}
}
if (plist1 == NULL)
{
tail->next = plist2;
return newlist;
}
else
{
tail->next = plist1;
return newlist;
}
}
定义一个快指针,一个慢指针,同时出发,如果快指针和慢指针在某一点相遇,则该链表带环,否则不带。
pNode CheckCycle(pList cplist) //检查是否为环,若是,则返回相遇结点
{
pNode fast = cplist;
pNode slow = cplist;
while (fast&&fast->next)
{
fast = fast->next->next;
slow = slow->next;
if (fast == slow)
{
return slow;
}
}
return NULL;
}
int GetCycleLength(pNode meet) //计算环的长度
{
int count = 0;
pNode cur = meet;
do
{
count++;
cur = cur->next;
} while (cur != meet);
return count;
}
思路如上图所示:
pNode GetCycleEnter(pList plist, pNode meet) //寻找环的入口
{
pNode pline = plist;
pNode pcycle = meet;
while (pline != pcycle)
{
pline = pline->next;
pcycle = pcycle->next;
}
return pline;
}
如果两条链表的尾结点相同,则有交点,否则没有:
int CheckCross(pList plist1, pList plist2) //判断两条链表是否相交
{
pNode cur1 = plist1;
pNode cur2 = plist2;
if ((cur1 == NULL) || (cur2 == NULL))
{
return -1;
}
while (cur1->next)
{
cur1 = cur1->next;
}
while (cur2->next)
{
cur2 = cur2->next;
}
if (cur1 == cur2)
{
return 1;
}
else
{
return -1;
}
}
约瑟夫环问题:
pNode JosephCycle(pList *pplist, int num) //约瑟夫环
{
assert(pplist);
pNode cur = *pplist;
while (1)
{
if (cur->next == cur)
{
printf("%d\n", cur->data);
break;
}
int i = 0;
for (i = 0; i < num - 1; i++)
{
cur = cur->next;
}
printf("%d->", cur->data);
pNode del = cur->next;
cur->data = cur->next->data;
cur->next = cur->next->next;
free(del);
}
return cur;
}
测试文件:
#define _CRT_SECURE_NO_DEPRECATE
#include "SList.h"
void menu()
{
printf("\n");
printf("*************** 单链表 **************\n");
printf(" 0.Exit 1.PrintfList \n");
printf(" 2.PushBack 3.PopBack \n");
printf(" 4.FindMidNode 5.DelKNode \n");
printf(" 6.GetLength 7.Find \n");
printf(" 8.ReversePrint 9.MergeList \n");
printf(" 10.CheckCycle 11.GetCycleLength\n");
printf(" 12.GetCycleEnter 13.CheckCross \n");
printf(" 14.JosephCycle \n");
}
void text1()
{
pList plist1 = NULL;
pList plist2 = NULL;
PushBack(&plist1, 1);
PushBack(&plist1, 3);
PushBack(&plist1, 7);
PushBack(&plist1, 8);
PushBack(&plist2, 2);
PushBack(&plist2, 5);
PushBack(&plist2, 9);
PushBack(&plist2, 10);
PrintfList(plist1);
PrintfList(plist2);
pList pret = MergeList(plist1, plist2);
PrintfList(pret);
}
void text2()
{
pList plist1 = NULL;
pList plist2 = NULL;
PushBack(&plist1, 1);
PushBack(&plist1, 3);
PushBack(&plist1, 7);
PushBack(&plist1, 8);
PushBack(&plist2, 2);
PushBack(&plist2, 5);
PushBack(&plist2, 9);
PushBack(&plist2, 10);
Find(plist1,8)->next = Find(plist2, 9);
int ret = CheckCross(plist1, plist2);
if (ret == -1)
{
printf("无交点\n");
}
else
{
printf("有交点\n");
}
}
void text()
{
int input = 0;
pList plist = NULL;
InitList(&plist);
pList cplist = NULL;
InitList(&cplist);
PushBack(&cplist, 1);
PushBack(&cplist, 2);
PushBack(&cplist, 3);
PushBack(&cplist, 4);
PushBack(&cplist, 5);
PushBack(&cplist, 6);
PushBack(&cplist, 7);
PushBack(&cplist, 8);
PushBack(&cplist, 9);
Find(cplist, 9)->next = Find(cplist, 4);
do
{
DataType data = 0;
pNode pret = NULL;
pNode enter = NULL;
int k = 0;
int ret = 0;
int clength = 0;
menu();
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 0:
break;
case 1:
PrintfList(plist);
break;
case 2:
printf("请输入要插入的数:");
scanf("%d", &data);
PushBack(&plist,data);
printf("插入成功\n");
break;
case 3:
PopBack(&plist);
break;
case 4:
pret = FindMidNode(plist);
if (pret==NULL)
printf("列表为空\n");
else
printf("中间结点为 %d\n", pret->data);
break;
case 5:
if (plist == NULL)
printf("当前列表为空\n");
else
{
printf("请输入要删除的倒数K结点:");
scanf("%d", &k);
DelKNode(&plist, k);
}
break;
case 6:
ret = GetLength(plist);
if (ret == -1)
printf("当前列表为空\n");
else
printf("当前列表长度为%d", ret);
case 7:
printf("请输入要查找的数:");
scanf("%d", &data);
pret = Find(plist, data);
if (pret == NULL)
printf("当前列表中无该元素\n");
else
printf("%d", pret->data);
break;
case 8:
ReversePrint(plist);
printf("NULL");
break;
case 9:
text1();
break;
case 10:
pret = CheckCycle(cplist);
if (pret == NULL)
printf("没有环\n");
else
printf("有环,并且相遇结点是%d", pret->data);
break;
case 11:
pret = CheckCycle(cplist);
clength = GetCycleLength(pret);
printf("%d", clength);
break;
case 12:
pret = CheckCycle(cplist);
enter = GetCycleEnter(cplist, pret);
printf("%d", enter->data);
break;
case 13:
text2();
break;
case 14:
Find(cplist, 9)->next = cplist;
pNode ret = JosephCycle(&cplist, 3);
printf("%d\n", ret->data);
break;
default:
printf("输入错误,请重新输入\n");
break;
}
} while (input);
}
int main()
{
text();
return 0;
}