【0206.翻转链表】
/**
* 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* reverseList(ListNode* head) {
ListNode *slow = nullptr;
ListNode *fast = head;
while (fast != nullptr) {
ListNode * temp = fast->next;
fast->next = slow;
slow = fast;
fast = temp;
}
return slow;
}
};
-
较快,一遍过
-
在slow 和 fast赋值初值的时候 考量较久 最后发现slow赋值空指针 而不是一贯的假头指针 fast赋值头指针 并且fast!=nullptr 而不是fast->next!=nullptr
-
也考量片刻 链表初始为空的情况 那么这个时候返回slow指针也是空 符合情况 因此不需要多余的操作
-
-
对比卡哥 思路代码基本一致
- 注意一点
while (fast != nullptr){...}
也可以等价写成while (fast){...}
- 注意一点
【0019.删除链表的倒数第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:
int countNode(ListNode *head) {
ListNode *fast = head;
int sum = 0;
while(fast) {
sum++;
fast = fast->next;
}
return sum;
}
ListNode* removeNthFromEnd(ListNode* head, int n) {
int sum = countNode(head);
//sum == 5 n==2 deleteTarget == 5 - 2 beforeDeleteTarget == 5 - 3 == sum - n - 1
ListNode *shead = new ListNode(0, head);
ListNode *fast = shead;
int i = -1;
while (fast) {
if (i == (sum - n - 1)) {
ListNode *temp = fast->next;
fast->next = temp->next;
delete temp;
}
fast = fast->next;
i++;
}
return shead->next;
}
};
-
较慢,算是 一遍过
-
主要思路 把倒数第n个结点 转换为 正数第 sum - n个结点 那么必然需要找到该节点前面紧跟的结点 即下标为 sum - n - 1 (默认下标从0开始)。 这里的分析思路 还是值得学习的 具体见代码注释部分!!
-
此外 对主函数 中如果有分任务 那么写成子函数由主函数去调用完成 会看起来代码更易读
-
设置头结点 除了统一操作 这里的一个作用 是方便返回链表 即return shead->next; 即可
-
-
问题出在 while循环的结束条件 一开始写的是
while (fast->next != nullptr) {...}
但其实循环结束条件可以先不着急写上去 或者先暂时写上去 总之在写完循环体之后是需要检查循环结束条件的 那么重点来了 怎么检查呢 这个问题很好 在我看来 从循环体执行结果 得到结束条件 再把这个结束条件拿去和之前暂时写下的结束条件对比即可 ——如果符合 那么后者不变 如果不符合 那么改变后者- 比如
- 一开始 我不写 或暂时写 结束条件为
while (fast->next != nullptr) {...}
- 经过多次执行循环体会先得到
fast == nullptr
所以此时结束条件应该是while(fast != nullptr)
- 对比之前暂时写的 是不正确的 需要改变
- 因为先得到fast == nullptr 所以如果是此时fast->next就相当于nullptr->next 这是会报错的
- 一开始 我不写 或暂时写 结束条件为
- 比如
-
对比卡哥思路 我竟然又完全没想到这个思路 可以说 我上面的代码并不是双指针法
-
卡哥思路:如果要删除倒数第n个节点,让fast移动n步,然后让fast和slow同时移动,直到fast指向链表末尾。删掉slow所指向的节点就可以了
-
为什么呢 我思考了一下
-
删除倒数第n个结点 因为链表的单向性 所以这个问题 并不等同于删除正数第n个结点 不能通过一个循环 或者说一个指针就可以删除 但是双指针就可以一个循环解决!
-
fast先移动n步(fast先移动距离)
-
接下来fast从第n+1步到结尾结点之间这一段距离(fast后移动的距离) 让slow随之从0开始移动(slow先移动的距离) slow随fast开始而开始 随fast停止而停止
-
因为
- fast先移动距离 + fast后移动距离 == 总长度
slow先移动距离 + slow后移动距离 == 总长度
- 因为 fast后移动距离 == slow先移动距离
- 所以 fast先移动距离 == slow后移动距离==删除倒数第n个元素需要特别移动的那一段距离长度
-
-
fast指针 遍历元素 控制节奏 slow指针 指向实际构成新结构体(如数组、链表等等)的元素
-
-
以后还需要再看看 重写卡哥思路代码