【C语言】c语言实现简单的链表(续)

本篇文章的代码是在上一篇代码的基础上,

实现了链表的合并;

实现了约瑟夫环;

并且判断两条链表是否有交点等功能;

检查链表是否为环,若是环又如何去计算环的长度以及查找环的入口;

查找环的入口思路如图示:



要实现的接口如下:

#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;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值