偶数个节点时返回中间两个节点的前一个
我们可以这样考虑,设想两个人跑步,一个人的速度是另外一个人的两倍,当速度快的人到达了终点,速度慢的人就在赛程的正中间。同样地,我们设置两个游动的指针,慢的指针移动步长为1,快的指针移动的步长为2。一开始都指向链表的头部,当遍历开始时,进行这样的操作:如果快的指针可以向前移动两步并且没有到达链表的尾部的话,快指针就向前移动2个节点,同时慢的指针向前移动1个节点;否则退出,返回慢指针。该算法的实现如下,注意区别链表长度为偶数和奇数的情况:
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <iostream.h>
struct ListNode
{
int data;
ListNode* next;
};
void InitList(ListNode** head)
{
*head = (ListNode*)malloc(sizeof(ListNode));
(*head)->next = NULL;
}
void InsertList(ListNode* head,int d)
{
assert(head!=NULL);
ListNode* pNewNode = (ListNode*)malloc(sizeof(ListNode));
pNewNode->data = d;
pNewNode->next = head->next;
head->next = pNewNode;
}
void PrintList(ListNode* head)
{
if(head == NULL)
{
return ;
}
ListNode* Next = head->next;
while(Next!=NULL)
{
cout<<Next->data<<endl;
Next = Next->next;
}
}
/*
查找中间结点核心代码
*/
ListNode* FindMidFront(ListNode* head)
{
if(NULL == head || NULL == head->next)
return head;
//ListNode* p1 = head;
//ListNode* p2 = head;
/*
因为我自己实现的链表,头结点没有放数据,所以要从下一个开始。
如果,头结点有数据,则直接从头结点开始就可以。
*/
ListNode* p1 = head->next;
ListNode* p2 = head->next;
while(NULL != p2->next && NULL != p2->next->next)
{
p1 = p1->next;
p2 = p2->next->next;
//cout<<"p1->data="<<p1->data<<endl;仅仅是为了测试
//cout<<"p2->data="<<p2->data<<endl;
}
return p1;
}
int main()
{
ListNode* pListHead = NULL;
InitList(&pListHead);
for(int i=8;i>=0;i--)
{
InsertList(pListHead,i);
}
ListNode* mid= FindMidFront(pListHead);
cout<<"链表中的元素为:"<<endl;
PrintList(pListHead);
cout<<"中间结间是"<<mid->data<<endl;
return 0;
}
偶数个节点时返回中间两个节点的后一个
上面的算法中的循环需要稍微调整一下。前面的算法中,快指针p2每次都移动2步,而本问题中,我们需要改变下策略,变成:只要快指针p2能向前移动一步,那么快指针和慢指针就都同时向前移动一步,再看看快指针是否还能补上一步,如果能补上一步,那么就补上,然后进行下一轮循环,否则就表示到达了链表尾部,返回慢指针即可。实现如下
ListNode* FindMidAfter(ListNode* head)
{
if(NULL == head || NULL == head->next)
return head;
//ListNode* p1 = head;
//ListNode* p2 = head;
ListNode* p1 = head->next;
ListNode* p2 = head->next;
while(NULL != p2->next)
{
p2 = p2->next;
p1 = p1->next;
if(NULL == p2->next)
return p1;
else
p2 = p2->next;
}
return p1;
}