- 首先给一个我之前写的双指针在链表类题中的妙用的link:双指针在链表中的妙用
tip1 来自“合并两个有序链表”
题目链接戳这里
- 这道题注意的就是如果是要返回一个新链表的头结点,一定要新建一个头结点:
ListNode* prehead = new ListNode(-1);
- 之后再对prehead的next进行添加,而不是对传进来的参数节点(比如list1)进行遍历、或其他改变后,再直接返回参数节点(当list1遍历的时候指向了尾结点,这样就找不到开始的头结点了)!
- 代码如下
/*** 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* mergeTwoLists(ListNode* list1, ListNode* list2) {ListNode* prehead = new ListNode(-1); // 申请一片新的空间的办法ListNode* pre = prehead; // 暂时指向这片区域,然后之后只更改nextwhile (list1 != nullptr && list2 != nullptr) {if (list1->val > list2->val) {pre->next = list2;list2 = list2->next;} else {pre->next = list1;list1 = list1->next;}pre = pre->next;}if (list1 != nullptr)pre->next = list1;else if (list2 != nullptr)pre->next = list2;return prehead->next;}
};
跟上上一道题的方法,如果是新建节点怎么做?
- 用“双数相加”来说,题目链接:link
- 必须用的方法是,不能自己猜想链表结构写别的代码:
//j%10的位置写新的val
//然后插入下一个点
ListNode* temp = new ListNode(j % 10);pre->next = temp;
- 题目的代码如下
/*** 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* addTwoNumbers(ListNode* l1, ListNode* l2) {ListNode* preHead = new ListNode(-1);ListNode* pre = preHead;int j = 0;// 模拟的思路while (l1 != nullptr || l2 != nullptr || j != 0) {int n1 = l1 ? l1->val : 0;int n2 = l2 ? l2->val : 0;j = j + n1 + n2;// 插入新节点一定要用这种新建的方式ListNode* temp = new ListNode(j % 10);pre->next = temp;j = j / 10;pre = pre->next;if (l1)l1 = l1->next;if (l2)l2 = l2->next;}// 结束条件 l1加完了 或者l2加完了return preHead->next;}
};
另一道用快慢指针的题
- 所以head不能动,但是可以新建指针指向head,并移动新的指针!
题目链接
- 思路就是快慢指针,快指针比慢指针先走n-1步,当慢指针指向末尾的时候,快指针指向的节点就是待删除的节点
- 该思路满足“进阶”思路中的“一遍扫描”!
/*** 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) {// 删除链表的倒数第n个节点// 想保留头结点,就新建一个节点去动!// head千万不能动,建立两个快慢指针!ListNode* preHead = new ListNode(-1);preHead->next = head;ListNode* fast = preHead;ListNode* slow = preHead;if (!head->next && n == 1)return nullptr;int count = n - 1; // 先让fastwhile (fast != nullptr && count) {fast = fast->next;count--;}// 直到fast 这个逻辑有点问题 改一下!while (fast != nullptr && fast->next != nullptr &&fast->next->next != nullptr) {fast = fast->next;slow = slow->next;}// 当前的fast指向倒数第二个节点ListNode* slow2 = slow; // 1slow2 = slow2->next; // 2// fast=fast->next;// 此时fast指向了最后一个节点;slow2这个点是要被去除的slow->next = slow2->next; // 成功去除return preHead->next;}
};
指针只是指针
以下代码是k个一组翻转链表的代码,一道困难题,而我之前总是觉得head指针指错了,直到加了这个代码:
- 为什么这句话至关重要?因为slow指向了head 但是不代表更新slow就更新了head
if (slow->next == head)head = p1;
题目链接是这个:题目链接
代码如下:
/*** 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* reverseKGroup(ListNode* head, int k) {if (k == 1)return head;ListNode* preHead = new ListNode(-1);preHead->next = head;ListNode* slow = preHead;ListNode* fast = preHead;int count = k;ListNode* p1;ListNode* p2;ListNode* nxt;fast = fast->next;while (fast) {while (count - 1) {// 如果在移动过程中就遇到空了,其实已经可以return了if (fast->next == nullptr)return head;fast = fast->next;count--;} // 让fast指向最后一个点 只是验证了还剩下这么多节点fast = fast->next; // 移到下一个节点p1 = slow->next;p2 = slow->next->next;while (p2 != fast) {nxt = p2->next; // 存下来的p2->next = p1;p1 = p2;p2 = nxt;}// 如果p2==next了slow->next->next = fast;// 至关重要if (slow->next == head)head = p1;slow->next = p1;while (slow->next != fast) {slow = slow->next;}count = k;}return head;}
};