链表的基本操作及链表面试题

本文详细介绍了链表的基本操作,包括打印、删除、插入等,并提供了多种链表面试题的解答,如约瑟夫环、逆置链表、合并有序链表、判断链表环、寻找链表中间节点、倒数K个节点等。此外,还讨论了链表相交问题及其在链表带环情况下的处理策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

LinkList.h

#ifndef _LINKLIST_H_
#define _LINKLIST_H_

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

typedef int DataType;

typedef struct Node
{
	DataType data;
	struct  Node *next;
}Node, *pNode,List,*pList;

typedef struct NewNode
{
	DataType data;
	struct NewNode *next;
	struct NewNode *random;
}pNewList;

void lnitLinkList(pList *pplist);
pNode BuyNode(DataType d);
void DestroyLinkList(pList *pplist);
void PushBack(pList* pplist, DataType d);
void PopBack(pList *pplist);
void PushFront(pList* pplist, DataType d);
void PopFront(pList *pplist);
pNode Find(pList plist, DataType d);
//
////在指定位置之前插入一个值
void Insert(pList *pplist,pNode pos, DataType d);
////指定位置删除
void Erase(pList *pplist, pNode pos);
void Remove(pList *pplist, DataType d);
void RemoveAll(pList *pplist, DataType d);
void EraseNotTailNode(pNode pos);
void PrintLinklist(pList plist);
void GetListLength(pList plist);
//链表面试题


//1.逆序打印单项链表
void PrintTailToHead(pList plist);
void Row(pList plist);
//2.删除一个无头单链表的非尾节点(不能遍历这个链表)
void DelNode(pList pos);
//3.从无头单链表的一个节点前插入一个节点(不能便利单链表)
void InsertNode(pList pos, DataType d);
//4.单链表实现约瑟夫环;
pList JosephCycle(pList *pplist);
//5.逆置/反转单链表
void ReverseList(pList *pplist);
//6.单链表排序
void LinkBubbleSort(pList *pplist);
void LinkQuickSort(pList *pplist);
//7.合并两个有序的单链表
pList MergeList(pList *pplist, pList *cur);
//8.遍历一遍链表,找到中间的那个节点
pList FindNode(pList *pplist);
//9.遍历一遍链表,找出倒数第K个节点
pList FindKNode(pList *pplist,DataType k);
//10.删除倒数第K个节点
pList EraseKNode(pList *pplist, DataType k);
//11.判断单链表是否带环,若带环,求环的长度,求环的入口点,并计算每个算法的时间复杂度&空间复杂度
pList IsCycle(pList plist);
DataType  Looplength(pList ret);
pList LinkInNode(pList plist, pList ret);
//12.判断两个链表是否相交,若相交,求交点(假设链表不带环)
pList LinkIntersect1(pList plist,pList cur);
pList LinkIntersect2(pList plist,pList cur);
//13.判断来两个链表是否相交,若相交,求交点
pList LinkInterLoop(pList plist, pList cur);
//14.求两个以排序单链表中相同的数据
void UnionSet(Node *l1, Node *l2);
//
pNewList* CopLinklist(pNewList **pplist);
#endif //_LINKLIST_H_

Link.c

链表的基本操作:

#include"Linklist.h"
//建立一个节点
pNode BuyNode(DataType d)
{
	pNode cur = NULL;
	cur = (pList)malloc(sizeof(Node));
	if(NULL == cur)
	{
		printf("申请失败");
		exit(1);
	}
	cur->data = d;
	cur->next = NULL;
	return cur;
}

//初始化
void lnitLinkList(pList *pplist)
{
	assert(pplist);
	*pplist = NULL;
}
//销毁链表
void DestroyLinkList(pList *pplist)
{
    pList top = *pplist;
	assert(pplist);
	while(top != NULL)
	{
		pList cur = top;
		top = top->next;
		free(cur);
        cur = NULL;
	}
	*pplist = NULL;
}


//尾插法
void PushBack(pList* pplist, DataType d)
{
	pList ret = NULL;
	pNode cur = BuyNode(d);
	assert(pplist);
	ret = *pplist;
	//如果链表为空
	if(*pplist == NULL)
		*pplist = cur;
	//如果链表非空,则找到最后一个节点
	else
	{
		while(ret->next != NULL)
		{
			ret = ret->next;
		}
		ret->next = cur;
	} 
}
//头插
void PushFront(pList *pplist, DataType d)
{
	pNode NewNode = BuyNode(d);
	assert(pplist);
	NewNode->next = *pplist;
	*pplist = NewNode;
}
//尾删
void PopBack(pList* pplist)
{
	pList cur = NULL;
	assert(pplist);
	cur = *pplist;
	//如果链表为空
	if(*pplist == NULL)
		return ;
	//如果只有一个节点
	else if((*pplist)->next == NULL)
	{
		free(*pplist);
		*pplist = NULL;
	}
	//有两个以上的节点
	else 
	{
		while(cur->next->next != NULL)
			cur = cur->next;
		free(cur->next);
		cur->next = NULL;
	}
}
//头删
void PopFront(pList *pplist)
{
	pList cur = NULL;
	assert(pplist);
	cur = *pplist;
	//空链表
	if(NULL == cur)
		return ;
	//不为空链表
	else
	{
		*pplist = cur->next;
		free(cur);
		cur = NULL;
	}
}
//找指定数据的位置
pNode Find(pList plist, DataType d)
{
	//链表为空时
	if(NULL == plist)
		printf("链表为空\n");
	else
	{
		while(plist->data != d && plist != NULL)
			plist = plist->next;
	}
	return plist;
}
//打印
void PrintLinklist(pList plist)
{
	if(plist == NULL)
		printf("链表为空\n");
	else
	{
		while(NULL != plist)
		{
			printf("%d->", plist->data);
			plist = plist->next;
		}
	}
	printf("NULL\n");
}
//在指定位置之前插入
//指定位置一定要在链子内
void Insert(pList *pplist,pNode pos, DataType d)
{
	pList NewNode = BuyNode(d);
	pList cur = NULL;
	assert(pplist);
	//空链表时(并且指定位置为空时)
	if(*pplist == NULL && pos == NULL)
	{
		*pplist = BuyNode(d);
		return;
	}
	//有一个节点时
	cur = *pplist;
	if(cur == pos)
	{
		NewNode->next = *pplist;
		*pplist = NewNode;
	}
	//有两个以上的结点时
	else
	{
		while(cur->next != pos)
		{
			cur = cur->next;
		}
		cur->next = BuyNode(d);
		cur->next->next = pos;
	}
}
//指定位置删除
void Erase(pList *pplist, pNode pos)
{
	pList cur = NULL;
	assert(pplist);
	cur = *pplist;
	//当链表为空时
	if(*pplist == NULL)
	{
		return;
	}
	//当要删除的节点时第一个节点时
	if(*pplist == pos)
	{
		*pplist = (*pplist)->next;
		free(cur);
		cur = NULL;
	}
	//当要删除的节点不是第一个时
	else
	{
		pNode ret = NULL;
		while(cur->next != pos)
			cur = cur->next;
		ret = cur->next;
		cur->next = cur->next->next;
		free(ret);
		ret = NULL;
	}
}
//删除第一个
void Remove(pList *pplist, DataType d)
{
	pList cur = NULL;
	assert(pplist);
	cur = Find(*pplist, d);
	if(cur != NULL)
		Erase(pplist, cur);
}
//删除链表中所有指定值的节点
void RemoveAll(pList *pplist, DataType d)
{
	pNode ret = NULL;
	pList cur = NULL;
	assert(pplist);
	cur = *pplist;
	//链表为空时
	if(*pplist == NULL)
		return ;
	//链表不为空时
	if(cur->next != NULL)//链表有两个或两个以上的节点
	{
		while(cur->next != NULL)
		{
			if(cur->next->data == d)
			{
				ret = cur->next;
				cur->next = ret->next;
				free(ret);
				ret = NULL;
			}
			else
				cur = cur->next;
		}
	}
		//判断第一个节点
		if((*pplist)->data == d)
		{
			PopFront(pplist);
		}
}

链表的面试题: 

1.从头到尾打印单链表

(1)递归打印,直接递归到最后一个节点,遍历打印该节点,然后返回到上一层,再次遍历打印倒数第二个节点。直到调用函数函数结束

(2)循环打印单链表,首先用一个指针pEnd指向NULL,另一个指针pNode指向链表的首地址,当pNode->next域是pEnd时,代表找到最后一个元素,先遍历打印该节点,然后将pEnd指向pNode此时的地址,将pNode指向链表的首地址。如果此时的    pEnd      == pNode时直接退出。否则,将pNode再次从头开始遍历,当pNode->next域是pEnd时,找到上次遍历节点的前一个节点。然后将pEnd再次指向pNode,如此反复,直到pEnd == pNode时停止。

(3)创建一个栈,遍历单链表,每遍历一个节点就将该节点的数据放入栈中,全部放入之后,将栈中的元素不断出栈,这样就形成了链表的逆序打印。

//逆序打印单链表
//递归
void Row(pList plist)
{
	if(plist == NULL)
		return ;
	else
	{
		Row(plist->next);
		printf("<-%d", plist->data);
	}
}
//非递归写法
void PrintTailToHead(pList plist)
{
	pList pEnd = NULL;
	pList pFirst = plist;
	while(pEnd != plist)
	{
		pFirst = plist;
		while(pFirst->next != pEnd)
		{
			pFirst = pFirst->next;
		}
		printf("<-%d", pFirst->data);
		pEnd = pFirst;
	}
	printf("\n");
}

2..删除一个无头单链表的非尾节点(不能遍历这个链表)

这题的意思就是只给你一个节点的位置,要你删除该节点。

思路:首先用pos表示要删除的节点,用NextNode表示要删除节点的下一个节点。因为要删除的节点一定不是尾节点,所以它的下一个节点一定存在。我们将NextNode中的数据替换掉pos中的数据。然后将pos节点中的next域指向NextNode中next域中存放的地址,这样就达到删除的效果,最后将NextNode节点释放。(该方法叫替换删除法)

//删除一个无头单链表的非尾节点
void DelNode(pList pos)
{
	pList cur = NULL;
	assert(pos && pos->next);
	cur = pos->next;
	pos->data = cur->data;
	pos->next = cur->next;
	free(cur);
	cur = NULL;
}

 

3.从无头单链表的一个节点前插入一个节点(不能遍历单链表)

这题的意思和上题差不多,只给你一个节点的地址,然后让你在该节点的前面插入一个新节点

思路:和上题差不多,因为我们不知道该节点前面一个节点的地址,所以无法直接插入。用一个指针pos指向该节点,然后用Node来记录新节点的位置,既然无法直接插入到pos的前面,那么就将Node节点插入到pos节点的后面,然后交换Node节点和pos节点中的数据,这样新节点就插入到前面了。

 

//3.从无头单链表的一个节点前插入一个节点(不能便利单链表)
//创建一个数据为d的新节点连接在此节点和下一个节点之间,然后交换新节点和此节点的数据;
void InsertNode(pList pos, DataType d)
{
	pList node = pos;
	DataType ret = 0;
	assert(pos);
	node = BuyNode(d);
	node->next = pos->next;
	pos->next = node;
	ret = pos->data ;
	pos->data = node->data;
	node->data = ret;
}

4.单链表实现约瑟夫环

约瑟夫环(约瑟夫问题)是一个数学的应用问题:已知n个人(以编号1,2,3...n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依规律重复下去,直到圆桌周围的人全部出列

思路:首先将链表的最后一个节点的next域指向首节点,这样就构成了一个环,然后从首节点开始,将首节点的编号为1,依此遍历,每遍历一个节点数字加1,假设m = 3,所以每次遇到编号为三的节点就出列(出列就和上面的删除一个非尾节点的操作一样),下一个节点从1开始,继续遍历直到链表中只有一个元素时退出。

 

//4.单链表实现约瑟夫环(此时已构成环)
pList JosephCycle(pList *pplist)
{
	pList cur = NULL;
	assert(pplist);
	cur = *pplist;
	while(cur->next != cur)
	{
		int i = 0;
		pList ret = cur;
		for(i = 1; i < 3; i++)
		{
			cur = cur->next; 
		}
	    ret = cur->next;
		cur->data = ret->data;
		cur->next = ret->next;
		free(ret);
		ret = NULL;
	}
	*pplist = cur;
	return cur;
}

5.逆置/反转单链表

思路:

如果链表为空或者链表内只有一个节点时,不需要逆置。

如果链表有两个节点的时候,将第二个节点的next域指向第一个节点,然后将第一个节点的next域指向NULL,最后将链表指针指向第二个节点

如果链表有两个以上的结点时。使用三个指针分别指向:Fir指向第一个节点,Sec指向第二个节点,Tre指向空

       1.将Tre指向Sec后面的节点。

       2.将Sec指向节点的next域指向Fir。

       3.将Fir指向Sec的位置,然后将Sec指向Tre,将Tre指向Sec中next域中存放的地址。

直到Sec为空时结束。然后将原链表中第一个节点的next域指向空,将指向链表的指针指向Fir的位置

//5.逆置/反转单链表
void ReverseList(pList *pplist)
{
	pList Fir = NULL;
	pList Sec = NULL;
	pList Tre = NULL;
	assert(pplist);
	//链表只有一个节点时
	if((*pplist)->next == NULL)
		return;
	//链表有两个节点时
	if((*pplist)->next->next == NULL)
	{
		(*pplist)->next->next = *pplist;
		*pplist = NULL;
		return;
	}
	//链表有两个以上的节点时
	Fir = *pplist;
	Sec = Fir->next;
	while(Sec != NULL)
	{	
		Tre = Sec->next;	
		Sec->next = Fir;
		Fir = Sec;
		Sec = Tre;
	}
	(*pplist)->next = NULL;
	*pplist = Fir;
}

 

6.合并两个有序的链表,使合并之后的链表依然有序。

思路:使用两个指针,一个指针Fir指向第一条链表,另一个指针Sec指向第二条链表,然后再使用一个指针NewList指向合并之后链表的首地址,使用一个指针End指向合并后链表的尾(刚开始时将End = NewList)

如果Fir为空时,直接让NewList指向第二条链表,反之,如果Sec为空时,将NewList指向第一条链表。否则执行下面的循环

      比较Fir和Sec节点中元素的大小,如果Fir小的话,让End指向Fir,然后将Fir指向它后面的那个节点(Fir  = Fir->next),反之如果Sec小的话,让End指向Sec,然后将Sec指向它后面的节点(Sec = Sec->next)。

如果Fir或者Sec指向空时退出。

然后查看哪一条链表不为空,将该链表直接接到新链表的后面。

//合并两个有序的单链表
pList MergeList(pList *pplist, pList *cur)
{
	pList Fir = NULL;
	pList Sec = NULL;
	pList Newlist = NULL;
	assert(pplist && cur);
	Fir = *pplist;
	Sec = *cur;
	while(Fir != NULL && Sec != NULL)
	{
		if(Fir->data < Sec->data)
		{
			PushBack(&Newlist, Fir->data);
			Fir = Fir->next;
		}
		else
		{
			PushBack(&Newlist, Sec->data);
			Sec = Sec->next;
		}
	}
	if(Fir != NULL)
	{
		while(Fir != NULL)
		{
			PushBack(&Newlist, Fir->data);
			Fir = Fir->next;
		}
	}
	if(Sec != NULL)
	{
		while(Sec != NULL)
		{
			PushBack(&Newlist, Sec->data);
			Sec = Sec->next;
		}
	}
	return Newlist;
}

 

7.查找链表的中间节点,要求只遍历一遍链表

思路:

如果链表为空的话,直接返回

1.如果链表中只有一个节点或两个节点,直接返回第一个节点的地址。

2.如果存在两个节点以上的节点,那么此时设置两个指针,一个指针Fir每次走一个节点,另一个指针Sec每次走两个节点。这样一直向后遍历,直到Sec走到空或者Sec->next为空时停止,此时Fir所指向的节点就是中间节点。

 

//遍历一遍链表,找出中间节点
pList FindNode(pList *pplist)
{
	pList Fir = NULL;
	pList Sec = NULL;
	assert(pplist);
	//空链表时
	if(*pplist == NULL)
		return NULL;
	//链表只有一个节点时
	if((*pplist)->next == NULL)
		return *pplist;
	//链表有多个节点
	Fir = *pplist;
	Sec = (*pplist)->next;
    while(Sec->next && Sec->next->next)
	{
		Fir = Fir->next;
		Sec = Sec->next->next;
	}
	return Fir;
}

8.遍历一遍链表,找出倒数第K个节点

思路:如果链表为空的话,直接返回。

1.如果链表没有K个节点的话,也直接返回。

2.如果链表中的节点个数大于等于K个时,设置两个指针,一个指针Sec首先走K步(也就是指向第K个节点),一个指针Fir指向链表的第一个节点。然后Fir和Sec每次遍历一个节点,直到Sec为空时退出,此时Fir指向的节点就是第K个节点。

 

//遍历一遍找出倒数第K个节点
pList FindKNode(pList *pplist,DataType k)
{
	pList Fir = NULL;
	pList Sec = NULL;	
	int i = 0;
	assert(pplist);
	Sec = *pplist;
	//链表的节点数少于k个
	while(Sec != NULL)
	{
		i++;
		Sec = Sec->next;
		if(i == k)
			break;
	}
	if(i < k)
		return NULL;
	//链表的节点数大于等于k个
	else
	{
		Fir = *pplist;
		while(Sec != NULL)
		{
			Fir = Fir->next;
			Sec = Sec->next;
		}
	}
	return Fir;
}

9.删除倒数第K个节点

首先找到第K个节点,这部操作和上面一样。如过链表中没有K个节点,那么就不用删除。如果链表中有K个历节点,假设用EraseNode表示倒数的第K个节点,用Next表示EraseNode节点的下一个节点。首先将Next中的数据替代EraseNode节点中的数据,然后将EraseNode节点的next域指向Next节点的下一个节点。

 

//删除链表的倒数第k个节点
pList EraseKNode(pList *pplist, DataType k)
{
	pList Tre = NULL;
	pList Fir = NULL;
	pList Sec = NULL;	
	int i = 0;
	assert(pplist);
	Sec = *pplist;
	//链表的节点数少于k个
	while(Sec != NULL)
	{
		i++;
		Sec = Sec->next;
		if(i == k)
			break;
	}
	if(i < k)
	{
		printf("链表没有%d个节点\n", k);
		return NULL;
	}
	//链表的节点数大于等于k个
	else
	{
		Fir = *pplist;
		while(Sec != NULL)
		{
			Fir = Fir->next;
			Sec = Sec->next;
		}
	}
	//删除该节点
	if(Fir->next != NULL)
	{
		Tre = Fir->next;
		Fir->data = Tre->data;
		Fir->next = Tre->next;
		free(Tre);
		Tre = NULL;
	}
	return *pplist;
}

10.判断单链表是否带环,若带环,求环的长度,求环的入口点

   1.判断单链表是否带环。

思路:如果链表为空的话,链表不带环。

如果链表不为空的话,设置两个指针Fir和Sec,然后开始向前移动,Fir一次移动一步,Sec一次移动两步。如果Sec或者Sec->next为空时,代表该链表没有环。如果Sec == Fir时,代表链表有环。

//判断单链表是否有环
pList IsCycle(pList plist)
{
	pList slow = plist;
	pList fast = plist;
	//判断是否有环(用两个指针,一个每次走一步,一个每次走两步,如果有环的话,快慢指针一定会在环内的某一点相遇)
	while(fast && fast->next)
	{
		slow = slow->next;
		fast = fast->next->next;
		//当慢的与快的相遇时,代表有环
		if(slow == fast)
		{
			return slow;
		}
	}
	return NULL;
}

  2.若有环,求环的长度。

思路:首先进行进行上面的操作,如果带环的话,用lengh长度,lenght刚开始等于1。用一个指针pos标记Sec == Fir的节点的位置,t表示换的,然后让Fir从此位置开始,依此向后遍历,每遍历一个节点的话,lenght++;直到pos == Sec时退出遍历,这时lenght的大小就是环的长度。

//求环的长度
DataType  Looplength(pList ret)
{
	int count = 1;
	pList cur = NULL;
	cur = ret->next;
	//计算环的长度(绕环走一圈)
	while(cur != ret)
	{
		cur = cur->next;
		count++;
	}
	return count; 
}

  3.如果有环的话,求入口点。

思路:设置两个指针ret和cur,cur指向链表的第一个节点,ret指向交点(判断链表带不带环中Fir == Sec的点)。两个指针同时向前移动,每次移动一步。当它们相遇时(ret == cur)此处就是环的入口点。

 

//求入口点
pList LinkInNode(pList plist, pList ret)
{
	//计算入口点
	//一个节点从头开始走,另一个从交点(上面判断单链表是否带环的交点)开始走,每次走一步,最后它们会在入口点相遇
	pList cur = NULL;
	cur = plist;
	while(cur != ret)
	{
		cur = cur->next;
		ret = ret->next;
	}
	return cur;//入口点
}

10.判断两个链表是否相交,若相交,求交点(假设链表不带环)

两种方法:

1.首先将其中一条链表的首尾相连成环,如果两个链表相交的话,就和上面的操作(判断链表是否带环)一样,那么换的入口点就是两条链表的交点。

 

2.首先遍历两条链表,记录两条链表的长度,然后用count记录下两条链表之差。设置两个指针Fir和Sec只想两个链表,首先找到比较长的那条链表,假设Fir指向的链表比较长,那么Fir先向后遍历count个节点。此时两个链表的长度相等,这时让两个指针依此向后遍历,每遍历一个节点判断一次。判断Fir 是否等于 Sec,如果在两个指针等于空之前相等的话,第一次相等的节点就是入口点

 

//12.判断两个链表是否相交,若相交,求交点(假设链表不带环)
//用构成环的方法
pList LinkIntersect1(pList plist, pList cur)
{
	//判断两个链表是否相交(判断两个链表的尾节点是否相同)
	pList Fir = plist;
	pList Sec = cur; 
	while(Fir->next != NULL)
	{
		Fir = Fir->next;
	}
	while(Sec->next != NULL)
	{
		Sec = Sec->next; 
	}
	if(Fir == Sec)
	{
		printf("两个链表相交\n");
	}
	else
	{
		printf("两个链表不相交\n");
		return NULL;
	}
	//如果带环,求交点
	//1.构成环
	Fir->next = plist;
	//2.求两个链表的交点(也就是带环链表的入口点)
	return LinkInNode(cur, IsCycle(cur));	
}
//不用带环的计算
pList LinkIntersect2(pList plist,pList cur)
{
	int i = 0;
	int j = 0;
	int count = 0;
	pList Fir = plist;
	pList Sec = cur; 
	//计算出两个链表的长度
	while(Fir != NULL)
	{
		i++;
		Fir = Fir->next;
	}
	while(Sec != NULL)
	{
		j++;
		Sec = Sec->next; 
	}
//计算出长的链表比短的链表多出几个节点,那么长的链表就先走几步,这样两个链表就是相同的长度
	Fir = plist;
	Sec = cur; 
	if(i > j)
	{
		count = i - j;
		while(count--)
		{
			Fir = Fir->next;
		}
	}
	else
	{
		count = j - i;
		while(count--)
		{
			Sec = Sec->next;
		}
	}
	//然后两个链表同时前进,每走一步比较一次
	while(Fir != NULL && Sec != NULL)
	{
		if(Fir == Sec)
			return Fir;
		Fir = Fir->next;
		Sec = Sec->next;
	}
	return NULL;
}

 

11.判断两个链表是否相交,若相交,求交点(假设链表可能带环)

1.如果两条链表都不带环,只有情况1和情况4,再判断两条链表是否相交

       用上面第10个标题中判断两个不带环的链表是否相交的方法做。

2.如果一条链表带环,一条链表不带环。则是情况2.

        这种情况一定不相交

3.如果两个链表都有环的话,则是情况3、5、6;

        两条链表带环不相交

        两条链表带环相交(交点一种情况是在环外,另一种是交点在环内且交点有两个)

                 首先判断入口点是否相同,如果入口点相同的话,是情况5的相交

               如果入口点不相同,那么取链表1的入口点,然后遍历一遍链表2的环,如果遍历的过程中有节点与链表1的入口点相同,代表两条链表相交,为情况6.如果没有节点与链表1的入口点相同的话,代表两条链表不相交,是情况3.

pList LinkInterLoop(pList plist, pList cur)
{
	pList Fir = IsCycle(plist);
	pList Sec = IsCycle(cur);
	pList node = Fir->next;
	
	int i = 0;   //(记录两个单链表是否在同一个环内,在的话为1,不在为0)
	//先判断两个链表是否带环
	if(Fir && Sec)
	{
		printf("这两个单链表带环");
		//1.判断这两个单链表是否带相同的环(如果两个有环单链表的相遇在同一个环内)
		while(node != Fir )
		{
			if(node == Sec || node->next == Sec)//在环内找到另一个单链表的相遇点
			{
				i = 1;
				break;
			}
			node = node->next;
		}
		//如果有入口点相同且两个链表带有相同的环
		if(i == 1 && LinkInNode(plist, Fir) == LinkInNode(cur, Sec))
		{
			pList endnode = LinkInNode(plist, Fir);
			endnode->next  = NULL;
			return LinkIntersect1(plist, cur);
		}
		//如果两个链表有相同的环但入口点不同时,两个链表的交点就是他们的入口点
		if(i == 1 && LinkInNode(plist, Fir) != LinkInNode(cur, Sec))
		{
			return LinkInNode(plist,Fir);
		}
	}
	//当其中一个带环,另一个不带环时.没有交点
	if(Fir == NULL || Sec == NULL)
	{
		return NULL;
	}
	//两个都不带环
	if(Fir == NULL && Sec == NULL)
	{
		return LinkIntersect1(plist, cur);
	}
	//其他
	return NULL;
}

12.复制一个复杂链表

 

 

//复制一个复杂链表
pNewList* CopLinklist(pNewList *pplist)
{
	pNewList *cur = pplist;
	pNewList *NewList = NULL;
	pNewList *ret = NULL;
	//1.将原链表的每个节点后面都接一个相同的节点
	while(cur)
	{
		NewList = (pNewList *)malloc(sizeof(struct NewNode));
		if(NewList == NULL)
		{
			printf("申请内存失败\n");
			return NULL;
		}
		NewList->data = cur->data;
		NewList->next = cur->next;
		cur->next = NewList;
		cur = NewList->next;
	}
	//2.将随机指针域复制下来
	cur = pplist;
	while(cur)
	{
		ret = cur->next;
		if(NULL == cur->random)
			ret->random = NULL;
		else
			ret->random = cur->random->next;
		cur = ret->next;
	}
	//3.将原节点后面的节点拆下来
	NewList = (pplist)->next;//拆之前,记住新链表的地址
	cur = pplist;
	ret = cur->next;
	while(ret)
	{
		cur->next = ret->next;
		cur = ret;
		ret = cur->next;
		
	}
	cur->next = NULL;
	return NewList;
}

13.求两个已排序单链表中的相同数据

因为这两个单链表已经有序,所以我们可以依此遍历比较。

思路:用两个指针l1和l2来指向两条单链表,循环下面的操作

        1.如果l1指向的节点大于l2中的数据,l2向后移动一个节点

        2.如果l1指向的节点小于l2中的数据,l1向后移动一个节点

       3.否则代表l1中的数据与l2中的数据相等。直接遍历该节点,然后将l1和2都向后移动。

如果有一条链表走到空时,直接退出循环。

//求两个以排序单链表的中相同的数据
void UnionSet(Node *l1, Node *l2)
{
    LinkBubbleSort(&l1);
    LinkBubbleSort(&l2);
	while(l1 && l2)
	{
		if(l1->data > l2->data)
			l2 = l2->next;
		else if(l1->data < l2->data)
			l1 = l1->next;
		else
		{			
			printf("%d\n", l1->data);
			l1 = l1->next;
			l2 = l2->next;
		}
	}
}

 

LinkList.c

#include"Linklist.h"

void TextPushPop(pList *pplist)
{
	pNode NewNode = NULL;
	assert(pplist);
	lnitLinkList(pplist);
	PushBack(pplist, 1);
	PushBack(pplist, 2);
	PushBack(pplist, 3);
	PushFront(pplist, 4);
	PrintLinklist(*pplist);
	PopBack(pplist);
	PrintLinklist(*pplist);
	PopFront(pplist);
	PopFront(pplist);
	PrintLinklist(*pplist);

}	
void LinkFindInsert(pList plist)
{
	pList NewNode = NULL;
	NewNode = Find(plist, 2);
    Insert(&plist, NewNode, 5);
	PrintLinklist(plist);
}

void LinkErase(pList *pplist)
{
	PushBack(pplist, 2);
	PushBack(pplist, 3);
	PushBack(pplist, 4);
	PushFront(pplist, 1);
	Erase(pplist, Find(*pplist,3));
	PrintLinklist(*pplist);
	Erase(pplist, Find(*pplist,1));
	PrintLinklist(*pplist);
	Erase(pplist, Find(*pplist,4));
	PrintLinklist(*pplist);
	RemoveAll(pplist, 1);
	PrintLinklist(*pplist);
}
void LinkOrder(pList plist)
{
	PushBack(&plist, 2);
	PushBack(&plist, 3);
	PushBack(&plist, 4);
	PushFront(&plist, 1);
	Row(plist);
	printf("\n");
	PrintTailToHead(plist);
}
void DelpList(pList plist)
{	
	PushBack(&plist, 2);
	PushBack(&plist, 3);
	PushBack(&plist, 4);
	PushFront(&plist, 1);
	DelNode(Find(plist, 3));
	InsertNode(Find(plist,1), 5);
	ReverseList(&plist);
	PrintLinklist(plist);
}
void LinkYsfh(pList *pplist)
{
	pList ret = NULL;
	PushBack(pplist, 2);
	PushBack(pplist, 3);
	PushBack(pplist, 4);
	PushFront(pplist, 1);
	Find(*pplist, 4)->next = *pplist;
	ret = JosephCycle(pplist);
	printf("最后剩余的为%d\n", ret->data);
}
void LinkSort(pList plist)
{
	pList cur = NULL;
	pList ret = NULL;
	PushBack(&plist, 2);
	PushBack(&plist, 4);
	PushBack(&plist, 6);
	PushFront(&plist, 0);
	PushBack(&cur, 3);
	PushBack(&cur, 5);
	PushBack(&cur, 7);
	PushFront(&cur, 1);
	ret = MergeList(&plist, &cur);
	LinkBubbleSort(&plist);
	PrintLinklist(ret);
}
void LinkFind(pList plist)
{
	pList cur = NULL;
	int i = 0;
	scanf("%d", &i);
	PushBack(&plist, 1);
	PushBack(&plist, 2);
	PushBack(&plist, 3);
	PushBack(&plist, 4);
	cur = FindNode(&plist);
	cur = FindKNode(&plist, i);
	if(cur != NULL)
		printf("倒数第%d个数为%d\n", i, cur->data);
	else
		printf("链表没有%d个节点\n", i);
	EraseKNode(&plist, i);
	PrintLinklist(plist);
}

void Linkloop(pList plist)
{
	pList cur = NULL;
	PushBack(&plist, 1);
	PushBack(&plist, 2);
	PushBack(&plist, 3);
	PushBack(&plist, 4);
	PushBack(&plist, 5);
	PushBack(&plist, 6);
	PushBack(&plist, 7);
	Find(plist, 7)->next = Find(plist, 5);
	cur = IsCycle(plist);
	if(cur == NULL)
		printf("改单链表不带环\n");
	else
	{
		printf("带环\n");
		printf("环的长度为%d\n", Looplength(cur));
		printf("入口点为%d\n", LinkInNode(plist, cur)->data);
	}
}
void LinkInter(pList plist)
{
	pList cur = NULL;
	pList ret = NULL;
	PushBack(&cur, 8);
	PushBack(&cur, 9);
	PushBack(&cur, 10);
	PushBack(&plist, 1);
	PushBack(&plist, 2);
	PushBack(&plist, 3);
	PushBack(&plist, 4);
	PushBack(&plist, 5);
	PushBack(&plist, 6);
	PushBack(&plist, 7);
	PushBack(&plist, 11);
	PushBack(&plist, 12);
	PushBack(&plist, 13);
	Find(cur, 10)->next = Find(plist, 6);
    Find(plist, 13)->next = Find(plist, 11);
	ret = LinkIntersect1(plist, cur);
	ret = LinkIntersect2(plist,cur);
    ret = LinkInterLoop(plist, cur);
	if(ret != NULL)
		printf("交点为%d\n",ret->data );
	else
		printf("两个链表不相交\n");
}
void FindSameNode()
{
	pList cur = NULL;
	pList ret = NULL;
	PushBack(&cur, 8);
	PushBack(&cur, 9);
	PushBack(&cur, 10);
	PushBack(&ret, 9);
	PushBack(&ret, 10);
	PushBack(&ret, 11);
	PushBack(&ret, 12);

	UnionSet(cur, ret);

}

void Text()
{
	pNewList Node1 ;
	pNewList Node2 ;
	pNewList  Node3 ;
	pNewList Node4;
	pNewList *NewNode = NULL;

	Node1.data = 1;
	Node1.next = &Node2;
	Node1.random = &Node3;

	Node2.data = 2;
	Node2.next = &Node3;
	Node2.random = &Node1;

	Node3.data = 3;
	Node3.next = &Node4;
	Node3.random = &Node3;

	Node4.data = 4;
	Node4.random = NULL;
	Node4.next = NULL;

	NewNode = CopLinklist(&Node1);

}
int main()
{
	List *pHead = NULL;
	TextPushPop(&pHead);
	LinkFindInsert(pHead);
	LinkErase(&pHead);
	LinkOrder(pHead);
	DelpList(pHead);
	LinkYsfh(&pHead);
	LinkSort(pHead);
	LinkFind(pHead);
	Linkloop(pHead);
	LinkInter(pHead);
	FindSameNode();
	Text();
	DestroyLinkList(&pHead);
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值