1.删除链表中的节点
我们无法访问我们想要删除的节点 之前 的节点,我们始终不能修改该节点的 next 指针。相反,我们必须将想要删除的节点的值替换为它后面节点中的值,然后删除它之后的节点。
class Solution {
public:
void deleteNode(ListNode* node) {
node->val=node->next->val; //将想要删除的节点的值替换为它后面节点中的值
node->next=node->next->next; //删除next节点
}
};
2. 分隔链表
思路:如果我们在分割点将改后链表拆分,我们会得到两个更小的链表,其中一个包括全部值小于x
的元素,另一个包括全部值大于x
的元素。两个指针可以用于分别创建两个链表,然后将这两个链表连接即可获得所需的链表
- 初始化两个指针 front 和 back。在实现中,我们将两个指针初始化为哑 ListNode。这有助于减少条件判断。
- 利用
head
指针遍历原链表。 - 若
head
指针指向的元素值 小于x
,该节点应当是front
链表的一部分。因此我们将其移到front
中 - 否则,该节点应当是
back
链表的一部分。因此我们将其移到back
中 - 遍历完原有链表的全部元素之后,我们得到了两个链表
front
和back
。原有链表的元素或者在front
中或者在back
中,这取决于它们的值。 - 现在,可以将
front
和back
连接,组成所求的链表。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* partition(ListNode* head, int x) {
ListNode* front=new ListNode(0);
ListNode* back=new ListNode(0);
ListNode* p1=front,*p2=back;
while(head!=nullptr){
if(head->val<x){
p1->next=head;
p1=p1->next;
head=head->next;
}
else{
p2->next=head;
p2=p2->next;
head=head->next;
}
}
p2->next=nullptr;
p1->next=back->next;
return front->next;
}
};
3. 环形链表Ⅰ
思路:用快慢指针方法,快指针走两步慢指针走一步。如果有闭环,那么快慢指针一定会相遇;如果没有闭环,那么快指针一定会先走到头,直接return false就好了。
class Solution {
public:
bool hasCycle(ListNode *head) {
ListNode* slow=head;
ListNode* fast=head;
while(fast!=nullptr && fast->next!=nullptr){
slow=slow->next;
fast=fast->next->next;
if(slow==fast) return true;
}
return false;
}
};
4. 环形链表Ⅱ
双指针第一次相遇: 设两指针 fast
,slow
指向链表头部 head
,fast
每轮走 2 步,slow
每轮走 1 步;
-
第一种结果: fast 指针走过链表末端,说明链表无环,直接返回 null;
TIPS: 若有环,两指针一定会相遇。因为每走 1 轮,fast 与 slow 的间距 +1,fast 终会追上 slow;
-
第二种结果: 当fast == slow时, 两指针在环中 第一次相遇 。下面分析此时fast 与 slow走过的 步数关系
追上时: 快指针=慢指针+n*圈, 快指针=2*慢指针 ——> 慢指针走了n*圈
那么假设到环节点入口处有k个节点,那么所有走到环节点入口的步数为:k+n*圈(每经一圈回到入口点);所以可以让一个指针从链表头节点处和此时的慢指针一起走k步,那么相遇点即为入口点。
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
if(head==nullptr) return nullptr;
ListNode* slow=head;
ListNode* fast=head;
while(fast != nullptr && fast->next != nullptr){
fast=fast->next->next;
slow=slow->next;
if(slow==fast) break;
}
if(fast==nullptr||fast->next==nullptr) return nullptr;
else{
fast=head;
while(fast!=slow){
fast=fast->next;
slow=slow->next;
}
return fast;
}
return nullptr;
}
};
5. 反转链表
递归:解法直接看代码理解
迭代:在遍历列表时,将当前节点的 next 指针改为指向前一个元素。由于节点没有引用其上一个节点,因此必须事先存储其前一个元素。在更改引用之前,还需要另一个指针来存储下一个节点。不要忘记在最后返回新的头引用!
//递归
class Solution {
public:
ListNode* reverseList(ListNode* head) {
if(head==nullptr|| head->next==nullptr) return head;
ListNode* node=reverseList(head->next);
head->next->next=head;
head->next=nullptr;
return node;
}
};
//迭代
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* pre=nullptr;
ListNode* cur=head;
while(cur!=nullptr){
ListNode* tmp = cur->next;
cur->next=pre;
pre=cur;
cur=tmp;
}
return pre;
}
};
6.反转链表Ⅱ
思路:和反转整个链表类似。
class Solution {
public:
ListNode* reverseBetween(ListNode* head, int m, int n) {
ListNode* pre=nullptr;
int count=1;
ListNode* cur=head;
ListNode* pre0,*cur0;
while(cur!=nullptr && count<=n){
//当cur为m时,记录一下当前cur和pre,留作最后的连接
if(count==m){
pre0=pre;
cur0=cur;
pre=cur;
cur=cur->next;
}
else if(count > m && count<=n){ 反转链表4步骤
ListNode* tmp=cur->next;
cur->next=pre;
pre=cur;
cur=tmp;
}
else{ //不在[m,n]范围内,迭代前行
pre=cur;
cur=cur->next;
}
count++;
}
if(pre0==nullptr) head=pre; //头节点便开始反转
else pre0->next=pre; //连接
cur0->next=cur;
return head;
}
};