1. 反转链表
输入一个链表,反转链表后,输出新链表的表头。
分析:初始时,将 preN 指向 nullptr,curN 指向链表首部,提前记录好链表的下一个节点。为了反转链表,要将 curN 的 next 指针指向 preN。
class Solution {
public:
ListNode* ReverseList(ListNode* pHead) {
ListNode* preN = nullptr, *curN = nullptr;
while(pHead){
curN = pHead;
pHead = pHead->next;
curN->next = preN;
preN = curN;
}
return curN;
}
};
2. 链表倒数第k个结点
输入一个链表,输出该链表中倒数第k个结点。
分析:设置两个指向链表首部的指针,快指针先移动 k 步,随后快慢指针一起移动,直到快指针指向链表尾部后一个结点,此时慢指针指向的就是倒数第 k 个结点。
class Solution {
public:
ListNode* FindKthToTail(ListNode* pListHead, unsigned int k) {
ListNode *fast = pListHead, *slow = pListHead;
while(k--){
if(!fast) return nullptr;
fast = fast->next;
}
while(fast)
{
fast = fast->next;
slow = slow->next;
}
return slow;
}
};
3. 合并两个排序的链表
输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则。
//非递归版本
class Solution {
public:
ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
{
ListNode* head = new ListNode(0), *headptr = head;
while(pHead1 && pHead2)
{
if(pHead1->val <= pHead2->val)
{
headptr->next = pHead1;
pHead1 = pHead1->next;
}
else{
headptr->next = pHead2;
pHead2 = pHead2->next;
}
headptr = headptr->next;
}
headptr->next = pHead1?pHead1:pHead2;
return head->next;
}
};
//递归版本
/*
链接:https://www.nowcoder.com/questionTerminal/d8b6b4358f774294a89de2a6ac4d9337?answerType=1&f=discussion
来源:牛客网
*/
class Solution {
public:
ListNode* Merge(ListNode* pHead1, ListNode* pHead2)
{
if (!pHead1) return pHead2;
if (!pHead2) return pHead1;
if (pHead1->val <= pHead2->val) {
pHead1->next = Merge(pHead1->next, pHead2);
return pHead1;
}
else {
pHead2->next = Merge(pHead1, pHead2->next);
return pHead2;
}
}
};
4. 链表中环的入口结点
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
分析:第一个想法是将遍历到的节点指针一一添加到 set 中,第一个重复的节点就是环的入口结点了。
判断链表中是否有环可以使用快慢指针,但怎么判断入口结点呢?快慢指针相遇前,快指针已经在环中转了至少 1 圈,而慢指针进入环内的第 1 圈就会与快指针相遇,也就是说快慢指针相遇时,快指针比慢指针多走了 n 个环(n>=1)。
如图,假设链表起始节点 A 距环入口结点 B 长度为 X,而慢指针在环内走了长度 Y 后与快指针相遇在 C 节点(由前面的分析可知,环长度L>Y>=0)。在这个过程中慢指针走了 X+Y,快指针速度是慢指针的 2 倍,所以快指针走了 2X+2Y。这时由图我们可以推断出:X = nL+(C->B)。如果此时我们让快指针定位到起始结点,并以与慢指针同样的速度沿链表运动,则当快指针到达环入口结点 B 时,慢指针也恰好到达 B 点,快慢指针再次相遇。
注意链表无环、链表起始节点即为环入口节点几种特殊情况。
/*
struct ListNode {
int val;
struct ListNode *next;
ListNode(int x) :
val(x), next(NULL) {
}
};
*/
//解法一:用set记录已遍历过的节点
class Solution {
public:
ListNode* EntryNodeOfLoop(ListNode* pHead)
{
set<ListNode*> s;
while(pHead){
if(s.find(pHead)==s.end())
s.insert(pHead);
else break;
pHead = pHead->next;
}
return pHead;
}
};
//解法二:快慢指针
class Solution {
public:
ListNode* EntryNodeOfLoop(ListNode* pHead)
{
if(!pHead) return nullptr;
ListNode* slow = pHead, *fast = pHead;
while(1){
slow = slow->next;
if(!fast) return nullptr;//如果无环则退出
fast = fast->next;
if(!fast) return nullptr;
fast = fast->next;
if(fast==slow) break;
}
fast = pHead;
while(1)
{
if(fast==slow) break;
slow = slow->next;
fast = fast->next;
}
return fast;
}
};