1.如何判断两个单链表是否相交,如果相交,找出交点(两个链表都不存在环)
如果两个单链表相交,那应该呈“Y”字形,则交点之后的节点是相同的。
所以判断是否相交,只需看两个链表的最后一个节点是否为同一个即可。
假设两个单链表的长度分别为L1、L2(L1 > L2),则(L1-L2)的值就是交汇之前两个链表的长度差;
因此,只有让更长的链表先走L1-L2步,然后两个链表开始一起走,如果某次走到一个相同的节点,该节点即为交点。
typedef struct ListNode{
int data;
struct ListNode *next;
}ListNode; //定义节点
static int GetListLength(ListNode *head)
{
int n=0;
while(head != NULL)
{
n++;
}
return n;
}
static ListNode * FindCommonNode(ListNode *a,ListNode *b)
{
int i;
int n1 = GetListLength(a);
int n2 = GetListLength(b);
//如果a<b,则互换a,b位置重新取长度
if(a<b)
{
return FindCommonNode(b,a);
}
for(i=0;i<n1-n2;i++) //较长的先走多出长度
{
a = a->next;
}
//相同长度时,同时向下走,指向相同时,则为交点
while(a != NULL && a != b)
{
a = a->next;
b = b->next;
}
return a;
}
2.判断一个链表是否有环,并找到环的入口点
如果一个单链表有环,那应该呈“6”字形或“O”形。
设置两个指针(fast, slow),初始值都指向头节点,slow每次前进一步,fast每次前进二步,如果链表存在环,则fast必定先进入环,而slow后进入环,两个指针必定 相遇:如果链表是呈"O"字形,则slow刚好遍历完一次的时候,与fast相遇;如果呈“6”字形,则更早相遇。
当fast若与slow相遇时,slow还没有遍历完链表,而fast已经在环内循环了n圈(1<=n)。假设slow走了s步,则 fast走了2s步(fast步数还等于s 加上在环上多转的n圈),设环长为r,则:
2s = s + nr,简化为 s= nr
s = x + y,x为链表起点到环入口点的距离,y是slow在环内走过的距离;
可以得到 x = y - s = y - nr,从链表头、相遇点分别设一个指针(p1, p2),每次各走一步,当p1走过距离x时到达入口点,而p2走过的距离为y-nr,y是相遇点与入口点的距离,因此y也走到了入口点,也就是说p1、p2在环入口点相遇了。
//链表是否有环
static ListNode* FindLoopPort(ListNode* head)
{
ListNode* slow = head;
ListNode* fast = head;
//找相遇节点
while(fast != NULL && fast->next != NULL)
{
slow = slow->next;
fast = fast->next->next;
if(slow == fast)
break;
}
if(fast == NULL || fast->next ==NULL) //走完链表,无环
return NULL;
//有环时,此时fast在相遇点,slow设置为起点,再相遇时,为环入口点
slow = head;
while(slow != fast)
{
slow = slow->next;
fast = fast->next;
}
return slow;
}
3.求一个单链表(无环)的中间节点
设置两个指针(fast, slow),初始值都指向头节点,slow每次前进一步,fast每次前进二步,当fast走到末尾时,slow刚好指向中间节点。
4.假如链表长度为N,如何返回链表的倒数第K个结点
假设用两个指针,指针P1先走K-1步,然后指针P2才开始走,当指针P1遍历完链表时,P2还剩K-1个结点没有遍历,此时,P2为第K个节点,返回P2即可。
//求倒数第n个节点
ListNode *FindLastKNode(ListNode *Head, int n)
{
ListNode* p1=head;
ListNode* p2=head;
while(--n == 0 && p1 != NULL)
{
p1 = p1->next;
}
if(p1 == NULL) return NULL;
while(p1->next != NULL)
{
p2 = p2->next;
p1 = p1->next;
}
return p2;
}
5.如何反转一个单链表(或逆序输出)
static Node* ReverseList(Node* Head)
{
Node* pNode = Head;
Node* pNext = NULL;
Node* pPrev = NULL;
while (pNode)
{
pNext = pNode->next;
if (NULL == pNext)
{
Head = pNode;
}
pNode->next = pPrev; //断开原本的链,反转指向
pPrev = pNode; //将逆置后的头,付给pPrev,pPrev始终为逆置后链表的第一位
pNode = pNext; //将节点后推一位,循环反转
}
return Head;
}
//递归实现逆置,此时默认为有头结点的链表
Node* reverselist1(Node* head)
{
static Node *h = head; //记录头结点
Node *c = NULL;
//当遍历到链表结尾部分是,a为倒数第二位,b为倒数第一位
if (head == NULL || head->next == NULL)
return head;
c = reverselist1(head->next); //开始递归
if (head != h) //没有返回到头结点时,从最后一位依次逆转指向,并把逆转后的节点返回,继续进行逆转
{
head->next->next = head;
head->next = NULL;
return c;
}
else //返回到头结点时,将已经全部逆置的链表附在头结点后面,完成逆置。返回头结点。
{
head->next = c;
return head;
}
}