day20

【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指针 指向实际构成新结构体(如数组、链表等等)的元素

  • 以后还需要再看看 重写卡哥思路代码

posted @ 2022-11-17 21:27  跬步瑶  阅读(26)  评论(0)    收藏  举报