欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

数据结构学习笔记2——链表练习

程序员文章站 2022-07-04 08:19:21
...

数据结构学习笔记2——链表练习

链表数据结构定义:

typedef struct student
{//以学生信息作为链表节点
	int num;
	float score;
	struct student* pnext;
}*pstu, stu;
  • 链表逆置
    链表逆置思路:1原地逆转,2定义一个新链表,将旧链表以头插法放入新链表
    法2思路图:
    数据结构学习笔记2——链表练习

链表逆置函数实现如下:

pstu list_reverse(pstu phead)
{//法1原地逆转
	pstu ptemp, ppre;
	ppre = NULL;
	ptemp = phead;
	if (NULL == phead)
	{
		printf("Link list is empty\n");
	}
	else
	{
		while (phead)
		{
			ptemp = ptemp->pnext;//指向下一个要剪切的链表节点。链表循环连接操作
			phead->pnext = ppre;//链表头节点指向ppre。
			ppre = phead;//头节点的值赋给ppre,头插
			phead = ptemp;//phead指向下一个要剪切的头节点。
		}
	}
	return ppre;
	
}
pstu list_reverse2(pstu phead)
{//法2
	pstu ptemp = (pstu)calloc(1, sizeof(stu));
	pstu pcur,pnew;
	pcur = phead;
	if (NULL == phead)
	{
		printf("Link list is empty\n");
	}
	while (pcur&&pcur->pnext)
	{
		pnew = (pstu)calloc(1, sizeof(stu));
		ptemp->num = pcur->num;//ptemp作为新链表的尾节点
		pcur = pcur->pnext;
		pnew->num = pcur->num;//pnew从旧链表取值
		pnew->pnext = ptemp;//pnew头插ptemp
		ptemp = pnew;
	}
	return ptemp;
}

链表逆置的使用主函数如下:


int main()
{
	pstu phead, ptail;
	pstu pnow = (pstu)calloc(1, sizeof(stu));
	phead = NULL;
	ptail = NULL;
	phead = list_sort_insert(phead, ptail);//参见上一篇文章链表的增删查改
	list_print(phead);
	pnow = list_reverse2(phead);
	list_print(pnow);
	return 0;
}
  • 链表的合并
    思路:将两个链表放入一个新的链表当中
    链表合并函数实现如下:
pstu mergelist(pstu phead1, pstu phead2)
{
	pstu pcur = NULL, ppre = NULL, head = NULL;
	if (NULL==phead1)
	{
		return (head = phead2);
	}
	else if(NULL==phead2)
	{
		return (head = phead1);
	}
	head = phead1->num < phead2->num ? phead1 : phead2;
	ppre = head;
	while (phead1!=NULL&&phead2!=NULL)
	{//将链表1和链表2都合并到链表3
		if (phead1->num < phead2->num)
		{
			pcur = phead1;//学号101赋给pcur
			phead1 = phead1->pNext;//phead1指向学号103
			ppre->pNext = pcur;//ppre为链表3
			ppre = pcur;//尾插链表3
			pcur->pNext = NULL;//清空链表3尾部
		}
		else
		{
			pcur = phead2;
			phead2 = phead2->pNext;
			ppre->pNext = pcur;
			ppre = pcur;
			pcur->pNext = NULL;
		}	
	}
	if (NULL == phead1)
	{//遍历完链表1
		ppre->pNext = phead2;
		return head;
	}
	else if (NULL == phead2)
	{//遍历完链表2
		ppre->pNext = phead1;
		return head;
	}
}
  • 链表是否相交
    思路:分别求出链表1和2的长度,让较长的链表先走(假如长度相差1,则较长的链表先走1步),之后再链表1链表2一同走。注意:链表相交最后一个节点必然相同。
    数据结构学习笔记2——链表练习

判断链表相交函数实现如下:

void list_cross(pstu phead1, pstu phead2)
{
	pstu pcur1, pcur2, ptemp;
	pcur1 = phead1;
	pcur2 = phead2;
	int len1, len2;
	len1 = len2 = 0;
	if (NULL == phead1 || NULL == phead2)
	{
		printf("条件不足,链表不相交\n");
	}
	//分别计算两个链表的长度
	for (; pcur1->pnext!=NULL; pcur1 = pcur1->pnext)
	{//遍历链表1
		len1++;
	}
	for ( ;pcur2->pnext!=NULL ; pcur2=pcur2->pnext)
	{//遍历链表2
		len2++;
	}
	int diff;
	diff = abs(len1 - len2);
	if (pcur1->num != pcur2->num)
	{//最后一个节点不相同
		printf("链表不相交\n");
	}else if (len1 < len2)
	{
		pcur1 = phead1;//较短的链表赋给pcur1
		ptemp = phead2;//较长的链表赋给ptemp
	}
	else if (len1 > len2)
	{
		pcur1 = phead2;
		ptemp = phead1;
	}
	for (int i = 0; i < diff; i++)
	{//ptemp先走,让ptemp和pcur1平齐
		ptemp = ptemp->pnext;
	}
	while (pcur1!=ptemp)
	{
		pcur1 = pcur1->pnext;
		ptemp = ptemp->pnext;
	}
	if (!pcur1 && !ptemp)
	{//如果链表1和链表2都走到尾部代表链表相交。
		printf("链表相交\n");
	}
}
  • 链表是否有环
    数据结构学习笔记2——链表练习
    思路:让刘备(pcur)先走两步,关羽(ppre)走一步,刘备每走两步,关羽走一步,当最终刘备撞上关羽时,链表有环
    判断链表是否有环函数实现如下:
bool circle(pstu phead)
{
	pstu pcur,ppre;
	pcur = phead;
	ppre = pcur;
	while (pcur)
	{
		pcur = pcur->pnext->pnext;//刘备走两步
		ppre = ppre->pnext;//关羽走一步
		if (pcur == ppre)
		{//刘备撞见关羽
			printf("链表有环\n");
			return true;
		}
		else if(!pcur||!pcur->pnext)
		{
			printf("链表无环\n");
			return false;
		}
		
	}
}
//主函数如下
int main()
{
	pstu phead, ptail;
	pstu pcur;
	phead = NULL;
	ptail = NULL;
	list_tail_insert(&phead, &ptail);
	list_print(phead);
	//链表建环
	ptail->pnext = phead->pnext->pnext;
	circle(phead);
	return 0;
}
  • 删除单链表中的重复数
    思路:遍历链表,刘备(pcur)走两步,关羽(ppre)走一步,刘备拿到的锦囊(pcur->num)和关羽拿到的锦囊(pcur->num)一样时,则丢弃一个锦囊(断链)。
    删除单链表中重复数的函数实现如下:
pstu list_delete_repeat(pstu phead)
{//利用刘备pcur和关羽ppre遍历链表,当找到相同的武器num时,则删除
	pstu ppre, pcur;
	pcur = phead;
	ppre = pcur;
	if (NULL==phead)
	{
		printf("Link List is empty\n");
	}
	else
	{
		while (pcur)
		{
			pcur = pcur->pnext;
			if (NULL == pcur)
			{
				break;
			}else if (pcur->num == ppre->num)
			{
				ppre->pnext = pcur->pnext;//断链
				pcur = pcur->pnext;
			}
			ppre = pcur;
		}
		pcur = NULL;
	}
	return phead;
}
  • 寻找链表节点
    1寻找链表倒数第4个节点
    函数实现如下:
void find_node4(pstu phead)
{
	//找出链表的倒数第四个节点
	//思路:刘备pcur先走4步,此后刘备每走1步,关羽ppre也走1步,刘备走到终点,则关羽就是倒四个节点。
	pstu ppre, pcur;
	pcur = phead;
	ppre = phead;
	int cnt = 0;
	while (pcur&&pcur->pnext)
	{
		while (cnt < 3)
		{
			pcur = pcur->pnext;
			cnt++;
		}
		ppre = ppre->pnext;
		pcur = pcur->pnext;
	}
	printf("倒数第四个节点是%d\n", ppre->num);
	//return ppre;//需要倒数第四个节点的指针则使用本句
}

2寻找链表的中间节点
函数实现如下:

void find_mid(pstu phead)
{
	//(2)找出链表的中间节点
	//思路:刘备pcur每走2步,关羽ppre走1步,当刘备走到终点,关羽正好在中间节点。
	int cnt = 0;
	pstu pcur, ppre;
	pcur = phead;
	ppre = pcur;
	if (NULL == phead)
	{
		printf("Link list is empty\n");
	}
	while (pcur&&pcur->pnext)
	{
		while (cnt < 2)
		{
			pcur = pcur->pnext;
			cnt++;
		}
		ppre = ppre->pnext;
		cnt = 0;
	}
	printf("中间节点是:%d", ppre->num);
	//return ppre;//需要中间节点的地址则使用本句
}
  • 拆分一个链表,将链表中的奇数构成链表1,偶数构成链表2
    数据结构学习笔记2——链表练习
    链表拆分后:pcur1则为1-3-5-7,pcur2则为2-4-6-8
    链表拆分函数实现如下:
void list_detach(pstu phead)
{
	pstu ppre1, pcur1, ppre2, pcur2;//ppre1和ppre2作为分身用于遍历链表
	//pcur1,pcur2作为返回值用于打印
	pcur1 = phead, ppre1 = pcur1;
	pcur2 = phead->pnext, ppre2 = pcur2;
	while (ppre1&&ppre2)
	{
		pstu ptemp;
		ppre1->pnext = ppre2->pnext;//1->3,和2断链
		ppre1 = ppre1->pnext;//1->3
		ptemp = ppre2->pnext;//ptemp作为小兵先走向3
		if (!ptemp)
		{//小兵指向NULL时
			ppre2->pnext = NULL;
			break;
		}
		else
		{//小兵指向数值时
			ppre2->pnext = ptemp->pnext;
			ppre2 = ppre2->pnext;
		}
	}
	list_print(pcur1);
	list_print(pcur2);
}
//主函数
int main()
{
	pstu phead, ptail;
	phead = NULL;
	ptail = NULL;
	phead=list_sort_insert(phead, ptail);
	list_print(phead);
	list_detach(phead);
	return 0;
}