按访问频度排序
设有一个带头节点的非循环双链表L,其每个节点中除有prev,data,和next域外,还有一个访问频度域freq,其值初始化为0,每当在链表中进行一次Locate(L,x)运算时,令值为x的节点中freq域的值++,并使此链表中的节点保持按访问频度递减的顺序排列,且最近访问的节点排在频度相同的节点之前,以便使频繁访问的节点总是靠近表头
编写符合要求的Locate函数,返回找到节点的地址,类型为指针型
算法思想
首先在双向链表中查找数据值为x的节点,查到后,将节点从链表上摘下,然后顺着节点的前驱链查找该节点的插入位置,频度递减,且排在同频度的第一个,即向前找到第一个比它频度大的节点,插入位置在该节点之后,并插入到该位置
![![[Pasted image 20241113231732.png]]](https://i-blog.csdnimg.cn/direct/34b1894a8dd14a2387cd350d61e57ae6.png)
cur指针指向L的next
cur不为空且cur的data不等于x,遍历链表,直到cur指向空,或找到x
如果找不到,直接退出程序
将置为x的节点的freq域+1
如果cur是头节点或者cur的freq值小于前驱的freq值,直接返回cur
否则就需要对cur重新排序
如果cur不是最后一个节点
![![[Pasted image 20241113231756.png]]](https://i-blog.csdnimg.cn/direct/e9d8b8cf5fde4c84a8d35c575eca97af.png)
cur的后继的prev指针指向cur的前驱
![![[Pasted image 20241113231916.png]]](https://i-blog.csdnimg.cn/direct/55bc31efbffc4cb289793116f710ea57.png)
cur的前驱的next指向cur的next
![![[Pasted image 20241113231954.png]]](https://i-blog.csdnimg.cn/direct/2088d9bb7653448890356ebd64a9b82b.png)
将prev指向L
![![[Pasted image 20241114151836.png]]](https://i-blog.csdnimg.cn/direct/1ae48be233b34d98a606beb3c13a6ed8.png)
这样已经把cur节点断开
然后查找cur的插入位置
prev->freq >= cur->freq,要找到一个节点prev,freq比当前cur节点的freq大,从而将cur节点插入到prev后面
prev向前遍历,当prev的freq比cur的freq小或相等时继续遍历,直到freq比cur大时停止
![![[Pasted image 20241114151905.png]]](https://i-blog.csdnimg.cn/direct/537e9d5caa74402492a132232f9f5064.png)
找到prev,将cur节点插入到prev节点后
cur的next指向prev的next

如果prev有后继
prev的next的前驱指向cur
![![[Pasted image 20241114152123.png]]](https://i-blog.csdnimg.cn/direct/4a26ab4cfb5d409cbbc895662a62fb3b.png)
cur的prev指向prev
prev的next指向cur
![![[Pasted image 20241114152158.png]]](https://i-blog.csdnimg.cn/direct/5fd7b81d66ea42d0b45f7613b0a0af1b.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]]](https://i-blog.csdnimg.cn/direct/c9607006c0d2404ea341ce702ce91ea1.png)
fast的next指向NULL,结束
![![[Pasted image 20241114152928.png]]](https://i-blog.csdnimg.cn/direct/cacb1bc270e54267a1c901bef8c7407a.png)
slow指向n/2,第2个节点
节点为奇数
![![[Pasted image 20241114153023.png]]](https://i-blog.csdnimg.cn/direct/518e03311cbe4aeaa1664f809b4b3576.png)
fast指向NULL,结束
![![[Pasted image 20241114153048.png]]](https://i-blog.csdnimg.cn/direct/115523fda7974cf7b6d8782935a3a867.png)
slow指向第5/2+1,第3个节点
创建一个指针cur,指向后半部分的头节点,即slow的next
遍历后半部分,将后半部分头插到newHead指针上,完成逆置
![![[Pasted image 20241114203224.png]]](https://i-blog.csdnimg.cn/direct/85ec2f7c144a44488467d6bea52efb02.png)
用next保存cur的next
![![[Pasted image 20241114203307.png]]](https://i-blog.csdnimg.cn/direct/745e88ab417f439baf42b13ba83d9772.png)
将cur的next指向newHead
![![[Pasted image 20241114203337.png]]](https://i-blog.csdnimg.cn/direct/b10cb09a8d514604a128aa2b3ec003c6.png)
newHead指向cur节点
![![[Pasted image 20241114203428.png]]](https://i-blog.csdnimg.cn/direct/0aa1e4d508e84c1683fa1483b59bf7d0.png)
cur指向next
![![[Pasted image 20241114203507.png]]](https://i-blog.csdnimg.cn/direct/75f8ac51cb0a4eca8e91510139ef7106.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,…,an−2,an−1,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,an−1,a3,an−2)
算法思想
L‘是由第一个元素,倒数第一个元素,第二个元素,倒数第二个元素…,依次合并而成的,为了方便取后半段元素,需要先将后半段原地逆置
- 创建快慢指针,找出中间节点
- 将L的后半段节点原地逆置
- 从前后半段依次各取一个节点,重排
链表带头节点
![![[Pasted image 20241114204148.png]]](https://i-blog.csdnimg.cn/direct/b5335167898a44a1be192aa53c1cd814.png)
slow和fast都从头节点开始,节点数为偶数
![![[Pasted image 20241114204221.png]]](https://i-blog.csdnimg.cn/direct/d849045c6b7e4b3189585f31ce17bc1e.png)
当fast的next指向NULL,slow指向第n/2,第2个节点,是前半部分的最后一个节点
节点数为奇数
![![[Pasted image 20241114204315.png]]](https://i-blog.csdnimg.cn/direct/7553c1468ab048b0970ba325557fa265.png)
![![[Pasted image 20241114204336.png]]](https://i-blog.csdnimg.cn/direct/591e53c3d06a4b7098612f8db63bd7c0.png)
当fast指向NULL,slow指向3/2+1,第2个节点
原地逆置完毕后
![![[Pasted image 20241114205251.png]]](https://i-blog.csdnimg.cn/direct/84aa73fa08344d37996715979489c479.png)
slow的next置为NULL,断开链接
![![[Pasted image 20241114205348.png]]](https://i-blog.csdnimg.cn/direct/34c52a56c907406b8732710c5206d98d.png)
cur开始遍历,cur的next指向new的next
![![[Pasted image 20241114205448.png]]](https://i-blog.csdnimg.cn/direct/5f66f2e0d45d43848b436977a5609d53.png)
new的next指向cur
![![[Pasted image 20241114205508.png]]](https://i-blog.csdnimg.cn/direct/a485554be910462da0bcf54c69ad4a23.png)
new指向cur的next
cur指向next
![![[Pasted image 20241114205603.png]]](https://i-blog.csdnimg.cn/direct/7717ff3ef7f64475b6dcae8a574b5b95.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;
}
}

被折叠的 条评论
为什么被折叠?



