链表
一、链表节点定义
// 单链表
struct ListNode {
int val; // 节点上存储的元素
ListNode *next; // 指向下一个节点的指针
ListNode(int x) : val(x), next(NULL) {} // 节点的构造函数
};定义一个节点
ListNode* head = new ListNode(5);
二、设置虚拟头结点
作用:设置虚拟头结点使对头结点的处理普遍化
ListNode* dummyHead = new ListNode(0); // 设置一个虚拟头结点 dummyHead->next = head; // 将虚拟头结点指向head,使后续对头结点操作普遍化 ListNode* cur = dummyHead;
三、典型例题
1. Leetcode92. 反转链表 2
题目
给你单链表的头指针 head 和两个整数left 和right ,其中left <= right。请你反转从位置 left 到位置right 的链表节点,返回 反转后的链表 。
来源:力扣(LeetCode) 链接:力扣
思路
设置虚拟头结点,使对第一个节点考虑具有一般性;
找到left前面一个元素位置,利用头插法将left与right之间元素插入到left后面。
代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* reverseBetween(ListNode* head, int left, int right) {
ListNode* virtualhead;
virtualhead = new ListNode(0);
virtualhead ->next = head;
ListNode* cur = virtualhead;
while(--left){
cur = cur->next;
right--;
}
head = cur; //使head指向left左面一个元素
cur = head->next;
while(--right){ //头插法
ListNode* temp = cur->next;
cur->next = temp->next;
temp->next = head->next;
head->next = temp;
}
return virtualhead->next;
}
};2. Leetcode19 删除链表中倒数第N个结点
题目
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
进阶:你能尝试使用一趟扫描实现吗?
来源:力扣(LeetCode) 链接:力扣
思路
设置虚拟头结点,使对第一个节点考虑具有一般性;
利用快慢指针,初始时快慢指针均指向链表虚拟头结点,快指针先走N步,之后快慢指针一同移动,直至快指针指向空指针,慢指针指向元素即为要删除元素前一个元素,删除即可。
代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* virtualhead = new ListNode(0);
virtualhead ->next = head;
ListNode* cur = virtualhead;
ListNode* cur1 = virtualhead;
while(cur->next && n--){
cur = cur->next;
}
while(cur->next){
cur = cur->next;
cur1 = cur1->next;
}
cur1->next = cur1->next->next;
return virtualhead->next;
}
};3. Leetcode面试题 02.07. 链表相交
题目
给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。
来源:力扣(LeetCode) 链接:力扣
思路

设置两个指针
p1、p2分别指向A、B两个单链表的起始节点;假设两个指针
p1与p2分别从开始位置移动到链表的结束位置,则两个指针走过长度差值就为两链表起始节点到相交节点长度间的差值;若两个指针从出发点移动,到达终点后,继续从对方出发点出发,则当二者相遇时节点就为链表相交点,因为到达该点时二者走过相同的路径,正好相遇。
代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode *h1 = headA, *h2 = headB;
while(h1!=h2){
h1 = h1 == nullptr? headB: h1->next;
h2 = h2 == nullptr? headA: h2->next;
}
return h1;
}
};4.Leetcode142.环形链表2
题目
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果pos是 -1,则在该链表中没有环。注意,pos仅仅是用于标识环的情况,并不会作为参数传递到函数中。
说明:不允许修改给定的链表。
进阶:
你是否可以使用 O(1) 空间解决此题?
来源:力扣(LeetCode) 链接:力扣
思路

设置两个快慢指针
fast、slow,fast指针一次走两步,slow指针一次走一步;两个指针从出发点开始出发至两者相遇,
slow指针立即返回至出发点重新出发,fast则改为一次行走一步继续前进,则当两者相遇时节点就为环形入口;首先明确一点,当
fast指针与slow指针相遇时,slow指针一定正在走环形路径的第一圈,为得到这样的结论,可以假设最边界情况,即当slow指针刚进入环形时,fast正好在其前面一个,在这种情况下,slow走一圈,fast走两圈,两个指针在环形入口前附近相遇,则此时slow指针一定正在走环形路径的第一圈;两个指针从出发点开始至两者相遇,走过相同的路径为
x+y,由于fast指针速度是slow指针2倍,fast指针走过路程为slow指针走过路程2倍,当两个指针相遇时,fast指针比slow指针多走若干圈(整数),并且多走的圈的总长度就为slow所走过路程长度;当两个指针第一次相遇时,
fast指针以一次一步速度继续前进,slow指针从出发点重新出发,由于slow指针从出发点到达第一次相遇点路程长度与fast多走的圈(整数个)路程长度相同,则两个指针可同时到达第一次相遇点,但在到达第一次相遇节点前,两者实际已经相伴走过y路程,两者同时回退y步即为第二次相遇点,也就是环形路径入口点。
代码
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode *slow=head, *fast=head;
while(fast&&fast->next){
slow = slow->next;
fast = fast->next->next;
if(slow == fast){
slow = head;
while(slow!=fast){
slow = slow->next;
fast = fast->next;
}
return slow;
}
}
return nullptr;
}
};