Leetcode刷题笔记(C++)——链表

Leetcode刷题笔记(C++)——链表

整理一下刷题过程中的思路,在这里进行一下总结与分享。
github地址:https://github.com/lvjian0706/Leetcode-solutions
github项目是刚刚新建的,陆续会将整理的代码以及思路上传上去,代码是基于C++与python的。同时会将基础的排序算法等也一并进行整理上传。

21. 合并两个有序链表

将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。

示例:
输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4

/**
 * 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* l1, ListNode* l2) {
        ListNode node(0);
        ListNode* new_head = &node;
        while(l1 && l2){
            if(l1->val <= l2->val){
                new_head->next = l1;
                l1 = l1->next;
            }
            else{
                new_head->next = l2;
                l2 = l2->next;
            }
            new_head = new_head->next;
        }
        while(l1){
            new_head->next = l1;
            l1 = l1->next;
            new_head = new_head->next;
        }
        while(l2){
            new_head->next = l2;
            l2 = l2->next;
            new_head = new_head->next;
        }
        return node.next;
    }
};

82. 删除排序链表中的重复元素 II

给定一个排序链表,删除所有含有重复数字的节点,只保留原始链表中 没有重复出现 的数字。

示例 1:
输入: 1->2->3->3->4->4->5
输出: 1->2->5
示例 2:
输入: 1->1->1->2->3
输出: 2->3

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    /*
    删除重复节点,只保留没有重复出现的数字:
    1. 为了避免头节点重复需要删除,新建一个头节点指向原来的头节点,最后返回新建节点的next;
    2. 定义last和fast节点用来遍历链表,并找出重复节点:
    2.1 last和fast初始化为head节点,循环判断last和fast的next是否相等,如果相等,fast前进一位;
    2.2 如果fast发生了变化,也就是last和fast不是同一个节点时,说明从last到fast之间是重复元素,new_head的next指向fast的下一个元素;
    2.3 如果last和fast是同一个节点时,说明该元素不是重复元素,new_head前进到该元素;
    2.4 last和fast前进一位,保证永远在new_head前面一位;
    */
    ListNode* deleteDuplicates(ListNode* head) {
        ListNode node(0);
        ListNode* new_head = &node;
        new_head->next = head;
        ListNode* last = head;
        ListNode* fast = head;
        while(fast && fast->next){
            while(fast->next && last->val == fast->next->val){
                fast = fast->next;
            }
            if(last==fast){
                new_head = last;
            }
            else{
                new_head->next = fast->next;
            }
            last = new_head->next;
            fast = new_head->next;
        }
        return node.next;
    }
};

83. 删除排序链表中的重复元素

给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。

示例 1:
输入: 1->1->2
输出: 1->2
示例 2:
输入: 1->1->2->3->3
输出: 1->2->3

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    /*
    使用一个指针进行遍历:
    1. 如果没有元素或者只有1个元素,直接返回;
    2. 遍历链表,当下一个元素与当前元素相等时,指针的next指向next的next,用以删除重复的元素,直到下一个元素与当前元素不相等为止;
    3. 指针指向下一个元素(与当前元素不相等的第一个元素);
    */
    ListNode* deleteDuplicates(ListNode* head) {
        if(!head || !head->next) return head;
        ListNode* newHead = head;
        while(newHead && newHead->next){
            if(newHead->next->val==newHead->val){
                newHead->next = newHead->next->next;
            }
            else{
                newHead = newHead->next;
            }
        }
        return head;
    }
};

86. 分隔链表

给定一个链表和一个特定值 x,对链表进行分隔,使得所有小于 x 的节点都在大于或等于 x 的节点之前。

你应当保留两个分区中每个节点的初始相对位置。

示例:
输入: head = 1->4->3->2->5->2, x = 3
输出: 1->2->2->4->3->5

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    /*
    分隔链表:
    定义两个节点small_head和large_head,前者作为头节点存储小于x的节点,后者作为头节点存储大于等于x的节点,最后进行拼接;
    注意结果链表的尾部应该为null;
    */
    ListNode* partition(ListNode* head, int x) {
        ListNode node1(0);
        ListNode* small_head = &node1;
        ListNode node2(0);
        ListNode* large_head = &node2;
        while(head){
            if(head->val<x){
                small_head->next = head;
                small_head = small_head->next;
            }
            else{
                large_head->next = head;
                large_head = large_head->next;
            }
            head = head->next;
        }
        small_head->next = node2.next;
        large_head->next = NULL;
        return node1.next;
    }
};

92. 反转链表 II

反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。

说明:
1 ≤ m ≤ n ≤ 链表长度。

示例:
输入: 1->2->3->4->5->NULL, m = 2, n = 4
输出: 1->4->3->2->5->NULL

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    /*
    反转从m到n的链表:迭代法
    使用first指针指向位置m,first_pre指向m-1,temp设为null,last指向位置m+1,last_next指向位置n+1;
    使用fist,last以及temp将位置m到n进行反转,接着将first_pre指向反转后的子链表头,反转后的子链表尾指向n+1;
    */
    ListNode* reverseBetween(ListNode* head, int m, int n) {
         ListNode* first = head;
         ListNode node(0);
         ListNode* first_pre = &node;
         first_pre->next = head;
         /*
        将first指针指向位置m,first_pre指向位置m-1;
         */
        for(int i=0; i<m-1; i++){
            first = first->next;
            first_pre = first_pre->next;
        }
        ListNode* last = first->next;
        ListNode* temp = NULL;
        /*
        使用fist,last以及temp将位置m到n进行反转,此时temp指向位置m,first指向位置m+1;
        */
        for(int i=m; i<n+1; i++){
            first->next = temp;
            temp = first;
            first = last;
            if(last) last = last->next;
        }
        first_pre->next->next = first;
        first_pre->next = temp;
        return node.next;
    }
};

138. 复制带随机指针的链表

给定一个链表,每个节点包含一个额外增加的随机指针,该指针可以指向链表中的任何节点或空节点。

要求返回这个链表的 深拷贝。

我们用一个由 n 个节点组成的链表来表示输入/输出中的链表。每个节点用一个 [val, random_index] 表示:

val:一个表示 Node.val 的整数。
random_index:随机指针指向的节点索引(范围从 0 到 n-1);如果不指向任何节点,则为 null 。

/*
// Definition for a Node.
class Node {
public:
    int val;
    Node* next;
    Node* random;
    
    Node(int _val) {
        val = _val;
        next = NULL;
        random = NULL;
    }
};
*/

class Solution {
public:
    /*
    复制带随机指针的链表:深拷贝
    1. 循环遍历链表,一边遍历一边新建节点,将新节点赋值为与该节点一样的值,然后插入到该节点后边;
    2. 再次循环遍历新链表,步长为2,一边遍历一边将后边的节点(1中新建的节点)的random赋值为与前边节点(原始节点)一致;
    3. 将新链表拆分
    */
    Node* copyRandomList(Node* head) {
        Node* insert_head = head;
        while(insert_head){
            Node* temp_node = new Node(insert_head->val);
            temp_node->next = insert_head->next;
            insert_head->next = temp_node;
            insert_head = insert_head->next->next;
        }
        Node* random_head = head;
        while(random_head && random_head->next){
            if(random_head->random) random_head->next->random = random_head->random->next;
            else random_head->next->random = NULL;
            random_head = random_head->next->next;
        }
        Node node(0);
        Node* copy_head = &node;
        Node* new_head = head;
        while(new_head && new_head->next){
            copy_head->next = new_head->next;
            new_head->next = new_head->next->next;
            new_head = new_head->next;
            copy_head = copy_head->next;
        }
        copy_head->next = NULL;
        return node.next;
    }
};

141. 环形链表

给定一个链表,判断链表中是否有环。

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。

示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:false
解释:链表中没有环。
进阶:
你能用 O(1)(即,常量)内存解决此问题吗?

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    /*
    判断链表是否有环,使用快慢指针
    快指针每次走两步,慢指针每次走一步,看是否可以碰面;主要关注边界情况
    */
    bool hasCycle(ListNode *head) {
        if(!head || !head->next) return false;
        ListNode* fast = head;
        ListNode* slow = head;
        while(fast && fast->next){
            fast = fast->next->next;
            slow = slow->next;
            if(fast==slow) return true;
        }
        return false;
    }
};

142. 环形链表 II

给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。

说明:不允许修改给定的链表。

示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:tail connects to node index 1
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:tail connects to node index 0
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:no cycle
解释:链表中没有环。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    /*
    循环链表:
    1. 首先判断是否是环,使用快慢指针,快指针每次走2步,慢指针每次走1步,看是否相遇,不相遇则返回null;
    2. 如果相遇,设慢指针走了s,则快指针已经走了2s,假设从head到环的入口为a,环的长度为b,则s=nb,快指针比慢指针多走了nb;
    3. 想要计算环的入口a,可以使用两个指针,一个在head处走a步,一个在相遇处走a步,此时,一个指针的位置为a,一个为a+nb,因为有环,所以a=a+nb;
    4. 最终的相遇位置为环的入口;
    */
    ListNode *detectCycle(ListNode *head) {
        if(!head || !head->next) return NULL;
        ListNode* fast = head;
        ListNode* slow = head;
        while(fast && fast->next){
            fast = fast->next->next;
            slow = slow->next;
            if(fast==slow){
                slow = head;
                while(fast != slow){
                    fast = fast->next;
                    slow = slow->next;
                }
                return slow;
            }
        }
        return NULL;
    }
};

143. 重排链表

给定一个单链表 L:L0→L1→…→Ln-1→Ln ,
将其重新排列后变为: L0→Ln→L1→Ln-1→L2→Ln-2→…

你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

示例 1:
给定链表 1->2->3->4, 重新排列为 1->4->2->3.
示例 2:
给定链表 1->2->3->4->5, 重新排列为 1->5->2->4->3.

/**
 * 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){
        if(!head || !head->next) return head;
        ListNode* new_head = reverseList(head->next);
        head->next->next = head;
        head->next = nullptr;
        return new_head;
    }
    /*
    重排列链表:将后半部分的链表倒序插入到前半部分链表中;
    1. 使用快慢指针找到后半部分链表;
    2. 反转后半部分链表;
    3. 将右半部分链表插入到左半部分链表中;
    */
    void reorderList(ListNode* head) {
        if(!head || !head->next) return;
        ListNode* fast = head->next;
        ListNode* slow = head;
        while(fast && fast->next){
            fast = fast->next->next;
            slow = slow->next;
        }
        ListNode* right_half = reverseList(slow->next);
        slow->next = nullptr;
        ListNode* new_head = head;
        while(new_head && right_half){
            ListNode* temp = right_half->next;
            right_half->next = new_head->next;
            new_head->next = right_half;
            right_half = temp;
            new_head = new_head->next->next;
        }
        /*
        当右半部分链表还有一个元素没有插入进去时,单独处理;
        */
        if(right_half){
            right_half->next = new_head->next;
            new_head->next = right_half;
        }
    }
};

148. 排序链表

在 O(n log n) 时间复杂度和常数级空间复杂度下,对链表进行排序。

示例 1:
输入: 4->2->1->3
输出: 1->2->3->4
示例 2:
输入: -1->5->3->4->0
输出: -1->0->3->4->5

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    /*
    新建头节点,进行归并排序;
    */
    ListNode* mergeSort(ListNode* left, ListNode* right){
        ListNode node(0);
        ListNode* new_head = &node;
        while(left && right){
            if(left->val<right->val){
                new_head->next = left;
                left =  left->next;
            }
            else{
                new_head->next = right;
                right = right->next;
            }
            new_head = new_head->next;
        }
        while(left){
            new_head->next = left;
            left =  left->next;
            new_head = new_head->next;
        }
        while(right){
            new_head->next = right;
            right =  right->next;
            new_head = new_head->next;
        }
        return node.next;
    }

    /*
    链表排序:
    O(nlogn) 时间复杂度以及常数级空间复杂度,考虑归并排序
    使用快慢指针找链表的中点,将链表分为前半部分和后半部分,递归的对前半部分以及后半部分进行排序,最后对两部分进行总的排序
    其中,在分割时,需要将前半部分的尾指针next赋为null,将链表切断,否则会无限递归;后半部分的起始点则为slow->next;
    */
    ListNode* sortList(ListNode* head) {
        if(!head || !head->next) return head;
        ListNode* fast = head->next;
        ListNode* slow = head;
        while(fast && fast->next){
            fast = fast->next->next;
            slow = slow->next;
        }
        ListNode* half = slow->next;
        slow->next = NULL;
        return mergeSort(sortList(head), sortList(half));
    }
};

206. 反转链表

反转一个单链表。

示例:
输入: 1->2->3->4->5->NULL
输出: 5->4->3->2->1->NULL
进阶:
你可以迭代或递归地反转链表。你能否用两种方法解决这道题?

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    /*
    原地反转链表:方法1.递归
    1. 递归结束条件:如果head为空或者链表只有1个节点,不需要反转,直接返回;
    2. 当head->next之后的节点已经反转好了,将新的头结点保存为new_head;
    3. 此时,头节点指向新链表的尾节点,需要将新链表的尾节点指向头节点(反转head和head->next),head->next->next = head;
    4. 将头节点的next赋为空;
    5. 返回新链表的头节点;
    */
    ListNode* reverseList(ListNode* head) {
        if(!head || !head->next) return head;
        ListNode* new_head = reverseList(head->next);
        head->next->next = head;
        head->next = NULL;
        return new_head;
    }
};



/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    /*
    原地反转链表:方法2.迭代
    1. 递归结束条件:如果head为空或者链表只有1个节点,不需要反转,直接返回;
    2. 使用三个指针,每次将当前指针指向前一个位置的指针,然后将三个指针同时向后移动一位;
    */
    ListNode* reverseList(ListNode* head) {
        ListNode* new_head = NULL;
        while(head){
            ListNode* head_next = head->next;
            head->next = new_head;
            new_head = head;
            head = head_next;
        }
        return new_head;
    }
};

234. 回文链表

请判断一个链表是否为回文链表。

示例 1:
输入: 1->2
输出: false
示例 2:
输入: 1->2->2->1
输出: true
进阶:
你能否用 O(n) 时间复杂度和 O(1) 空间复杂度解决此题?

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    /*
    递归反转链表
    */
    ListNode* reverseList(ListNode* head){
        if(!head || !head->next) return head;
        ListNode* new_head = reverseList(head->next);
        head->next->next = head;
        head->next = NULL;
        return new_head;
    }
    /*
    判断是否是回文链表:
    1. 使用快慢指针找到链表中点,将链表断开;
    2. 将后半部分链表反转;
    3. 比较两个链表的相同位置的值是否一致;
    */
    bool isPalindrome(ListNode* head) {
        if(!head || !head->next) return true;
        ListNode* fast = head->next;
        ListNode* slow = head;
        while(fast && fast->next){
            fast = fast->next->next;
            slow = slow->next;
        }
        ListNode* right = reverseList(slow->next);
        slow->next = NULL;
        while(head && right){
            if(head->val != right->val) return false;
            head = head->next;
            right = right->next;
        }
        return true;
    }
};

你可能感兴趣的:(Leetcode刷题笔记,c++,数据结构,算法,leetcode)