链表相关其他算法题1|按访问频度排序|求链表的最大孪生和|后半段原地逆置|找链表中间节点(C)

按访问频度排序

设有一个带头节点的非循环双链表L,其每个节点中除有prev,data,和next域外,还有一个访问频度域freq,其值初始化为0,每当在链表中进行一次Locate(L,x)运算时,令值为x的节点中freq域的值++,并使此链表中的节点保持按访问频度递减的顺序排列,且最近访问的节点排在频度相同的节点之前,以便使频繁访问的节点总是靠近表头
编写符合要求的Locate函数,返回找到节点的地址,类型为指针型

算法思想

首先在双向链表中查找数据值为x的节点,查到后,将节点从链表上摘下,然后顺着节点的前驱链查找该节点的插入位置,频度递减,且排在同频度的第一个,即向前找到第一个比它频度大的节点,插入位置在该节点之后,并插入到该位置
![[Pasted image 20241113231732.png]]

cur指针指向L的next
cur不为空且cur的data不等于x,遍历链表,直到cur指向空,或找到x
如果找不到,直接退出程序
将置为x的节点的freq域+1
如果cur是头节点或者cur的freq值小于前驱的freq值,直接返回cur
否则就需要对cur重新排序
如果cur不是最后一个节点
![[Pasted image 20241113231756.png]]

cur的后继的prev指针指向cur的前驱
![[Pasted image 20241113231916.png]]

cur的前驱的next指向cur的next
![[Pasted image 20241113231954.png]]

将prev指向L
![[Pasted image 20241114151836.png]]

这样已经把cur节点断开
然后查找cur的插入位置
prev->freq >= cur->freq,要找到一个节点prev,freq比当前cur节点的freq大,从而将cur节点插入到prev后面
prev向前遍历,当prev的freq比cur的freq小或相等时继续遍历,直到freq比cur大时停止
![[Pasted image 20241114151905.png]]

找到prev,将cur节点插入到prev节点后
cur的next指向prev的next
在这里插入图片描述

如果prev有后继
prev的next的前驱指向cur
![[Pasted image 20241114152123.png]]

cur的prev指向prev
prev的next指向cur
![[Pasted image 20241114152158.png]]

DLinkList Locate(DLinkList &L, ElemType x)
{
	// cur为工作指针,prev为cur的前缀,用于查找插入的位置
	DNode* cur = L->next, *prev;
	// 查找值为x的节点
	while (cur && cur->data != x)
	{
		cur = cur->next;
	}
	// 不存在值为x的节点
	if (!cur)
	{
		return NULL;
	}
	else
	{
		// 令元素值为x的节点的freq域+1
		cur->freq++;
		// 如果cur是链表头节点,或者freq值小于前驱的freq,直接返回cur
		if (cur->prev == L || cur->prev->freq > cur->freq)
			return cur;
		// 如果cur不是最后一个节点
		if (cur->next)
			cur->next->prev = cur->prev;
		cur->prev->next = cur->next;
		prev = cur->prev;
		// 查找cur节点的插入位置
		while (prev != L && prev->freq <= cur->freq)
		{
			prev = prev->prev;
		}
		cur->next = prev->next;
		if (prev->next)
			prev->next->prev = cur;
		cur->prev = prev;
		prev->next = cur;
	}
	// 返回值为x的节点的指针
	return cur;
}

求链表的最大孪生和

设有一个长度n的不带头结点的单链表,且节点值都大于0
求这个链表的最大孪生和,定义为一个节点值与其孪生节点值之和,对于第i个节点,其孪生节点为第n-i-1个节点

算法思想

设置快慢指针fast和slow,初始时slow指向L,fast指向L的next,slow每次走一步,fast每次走两步,当fast指向表尾时,slow正好指向链表的中间节点,第n/2个节点,即slow刚好指向链表前半部分的最后一个节点,将链表的后半部分逆置,然后设置两个指针分别指向链表前半部分和后半部分的首节点,在遍历过程中计算两个指针所指节点元素之和,并维护最大值
节点为偶数
![[Pasted image 20241114152917.png]]

fast的next指向NULL,结束
![[Pasted image 20241114152928.png]]

slow指向n/2,第2个节点

节点为奇数
![[Pasted image 20241114153023.png]]

fast指向NULL,结束
![[Pasted image 20241114153048.png]]

slow指向第5/2+1,第3个节点

创建一个指针cur,指向后半部分的头节点,即slow的next
遍历后半部分,将后半部分头插到newHead指针上,完成逆置
![[Pasted image 20241114203224.png]]

用next保存cur的next
![[Pasted image 20241114203307.png]]

将cur的next指向newHead
![[Pasted image 20241114203337.png]]

newHead指向cur节点
![[Pasted image 20241114203428.png]]

cur指向next
![[Pasted image 20241114203507.png]]

之后依次循环
同时遍历前半部分和逆置后的后半部分,保存最大的孪生和

int PairSum(LinkList &L)
{
	LNode* fast = L->next, *slow = L;
	// 利用快慢指针找到链表的中间节点
	while (fast && fast->next)
	{
		fast = fast->next->next;
		slow = slow->next;
	}
	LNode* newHead = NULL, *cur = slow->next, *next;
	// 反转链表后半部分元素,用头插法
	while (cur)
	{
		next = cur->next;
		cur->next = newHead;
		newHead = cur;
		cur = next;
	}
	int mval = 0, cur = L;
	LNode* cur2 = newHead;
	// 分别遍历两个链表,mval记录最大值
	while (cur)
	{
		if (cur->data + cur2->data > mval)
			mval = cur->data + cur2->data;
		cur = cur->next;
		cur2 = cur2->next;
	}
	return mval;
}

后半段原地逆置

设线性表L=(a1,a2,a3,…,an−2,an−1,an)L=(a_{1},a_{2},a_{3},\dots,a_{n-2},a_{n-1},a_{n})L=(a1,a2,a3,,an2,an1,an),采用带头节点的单链表保存
重新排列L中的各点,得到线性表L′=(a1,an,a2,an−1,a3,an−2)L'=(a_{1},a_{n},a_{2},a_{n-1},a_{3},a_{n-2})L=(a1,an,a2,an1,a3,an2)

算法思想

L‘是由第一个元素,倒数第一个元素,第二个元素,倒数第二个元素…,依次合并而成的,为了方便取后半段元素,需要先将后半段原地逆置

  1. 创建快慢指针,找出中间节点
  2. 将L的后半段节点原地逆置
  3. 从前后半段依次各取一个节点,重排
    链表带头节点
    ![[Pasted image 20241114204148.png]]

slow和fast都从头节点开始,节点数为偶数
![[Pasted image 20241114204221.png]]

当fast的next指向NULL,slow指向第n/2,第2个节点,是前半部分的最后一个节点

节点数为奇数
![[Pasted image 20241114204315.png]]

![[Pasted image 20241114204336.png]]

当fast指向NULL,slow指向3/2+1,第2个节点

原地逆置完毕后
![[Pasted image 20241114205251.png]]

slow的next置为NULL,断开链接
![[Pasted image 20241114205348.png]]

cur开始遍历,cur的next指向new的next
![[Pasted image 20241114205448.png]]

new的next指向cur
![[Pasted image 20241114205508.png]]

new指向cur的next
cur指向next
![[Pasted image 20241114205603.png]]

完成插入

void changeList(LinkList &h)
{
	LNode* slow, *fast, *next, *new;
	slow = fast = h;
	// 寻找中间节点
	while (fast && fast->next)
	{
		slow = slow->next;
		fast = fast->next->next;
	}
	// slow所指为中间节点,cur为后半段首节点
	LNode* cur = slow->next;
	slow->next = NULL;
	// 将后半段逆置
	while (cur)
	{
		next = cur->next;
		cur->next = slow->next;
		slow->next = cur;
		cur = next;
	}
	// new指向前半段的第一个数据节点,即插入点
	new = h->next;
	// cur指向后半段的第一个数据节点
	cur = slow->next;
	slow->next = NULL;
	// 将后半段的节点插入到指定位置
	while (cur)
	{
		next = cur->next;
		cur->next = new->next;
		new->next = cur;
		new = cur->next;
		cur = next;
	}
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值