Leetcode exercise record 链表部分练习整理

🌟Leetcode exercise record🌟 LinkedList10 exercises

****刷题小结****
1.万事开头难,坚持刷题进度,加油,会越来越好
2.画图分析很重要
3.巧用哑结点dummy,
4.注意return语句
6.常用方法:递归,迭代,双指针(首尾指针,快慢指针,奇偶指针,奇偶首尾巴指针)
7.快慢指针:适合用于有中点相关的场景,有中点或倍数的时候可以考虑快慢指针解决问题,同时为了更加简便,可以采用一边移动一边将其倒置
8.借助数组,栈等数据结构,但是空间复杂度会有所改变
9.及时复盘

160 相交链表

Predestined person no matter how far will meet

🙊 EASY

Q&A
编写一个程序,找到两个单链表相交的起始节点。

示例:💭Language: C++

🍓🍓【WAY1 C++ 暴力法
时间复杂度O(mn) 空间复杂的O(1)

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode* cur_a = headA;
        while(cur_a){
            ListNode* cur_b = headB;
            while(cur_b){
                if(cur_a == cur_b){
                    return cur_a;
                }
                cur_b = cur_b -> next;
            }
            cur_a = cur_a -> next;
        }
        return nullptr;
        
    }
};

🍓🍓【WAY2 C++ 哈希表


 //使用一个hash set 遍历一个链表,set中存放其所有指针, 遍历另一个链表,去set中找相同指针,时间复杂度O(m + n),空间复杂度O(m) 或 O(n)
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        std::unordered_set<ListNode*> set ;
        ListNode* cur_a = headA;
        while(cur_a){
            set.insert(cur_a);
            cur_a = cur_a -> next;
        }
        ListNode* cur_b = headB;
        while(cur_b){
            if(set.find(cur_b) != set.end()){
                return cur_b;
            }
            cur_b = cur_b -> next;
        }
        return nullptr;
        
    }
};

🍓🍓【WAY 3C++ 双指针】效率高

 //时间复杂度O(m+n) 空间复杂度O(1)
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        if(headA == nullptr || headB == nullptr){
            return nullptr;
        }
        //采用双指针
        ListNode* cur_a = headA;
        ListNode* cur_b = headB;
        while(cur_b != cur_a){
            //A的指针遍历完A后,接着从headB遍历,B同理
            cur_a = (cur_a == nullptr ? headB : cur_a -> next);
            cur_b = (cur_b == nullptr ? headA : cur_b -> next);
        }

        return cur_a;  
    }
};

206 反转链表

🙊 EASY

Q&A
反转一个单链表。

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

注意:涉及到链表的操作,一定要在纸上把过程先画出来

🍓🍓【WAY 1 C++ 双指针

class Solution {
public:
    ListNode* reverseList(ListNode* head) {

        //null 1  -> 2 -> 3 -> 4 -> 5 -> null
        //cur  pre

        //null <- 1    2   ->  3 -> 4 -> 5 -> null
        //       cur   pre
        //直至pre循环到链表尾部
        ListNode* cur = NULL, * pre = NULL;
        while(pre != NULL){
            ListNode* t = pre -> next;
            pre -> next = cur;//双指针交换,并同时右移
            cur = pre;
            pre = t;
        }
        return cur;
    }
};

🍓🍓【WAY 2 C++ recursion

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if (head == NULL || head -> next == NULL){
            return head;
        }
        ListNode* ret = reverseList(head -> next);//反转后的头节点
        head -> next -> next = head;  //当前节点的下一个节点的next指针指向当前节点  
         head -> next = NULL;//当前节点的next指针指向null
         return ret;
}
};

🍓🍓【WAY3 C++ demonize double pointer

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if(head == NULL){
            return NULL;
        }
        ListNode* cur = head;
        while(head -> next != NULL){
            ListNode* t = head -> next -> next;
            head -> next -> next = cur;
            cur = head -> next;//cur和head的next指针同时往前移动一步
            head -> next = t;
        }
        return cur;
    }

    
};

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

🙊 EASY

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

示例:
输入: 1->1->2->3->3
输出: 1->2->3
**🍓🍓【WAY1 C++ 双指针(快慢指针) **】
小白解释 return head :p和head都是指针,p就像开路人一样,一路向尾排除重复元素,而head自始至终还站在原地,等p到尾后,中间排除重复元素的操作正确的话,那head前面也没重复元素了。所以head才是要返回的链表头。

class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        if(head == NULL || head -> next == NULL){
            return head;
        }
        ListNode* p = head;//慢指针
        ListNode* q = head -> next;//注意:快指针
        while(p -> next != NULL){
            if(p -> val  == q -> val){
                if(q -> next == NULL){//注意快指针后面是否有元素
                    p -> next = NULL;
                }
                else{
                    p -> next = q -> next;
                    q = q -> next;
                }
                
            }
            else{
                p = p -> next;
                q = q -> next;
            }
        }
            return head;
        } 
};

**🍓🍓【WAY2 C++ 递归 **】

class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        if(head == NULL || head -> next == NULL){
            return head;
        }
        head -> next = deleteDuplicates(head -> next);//head.next已经指向一个去重的链表
        if(head -> val == head -> next -> val) head = head -> next;//检验上一步的操作是否返回一个去重的链表的头节点
        return head;//返回的是已经去重的链表的头节点
        
    }
};

**🍓🍓【WAY3 C++ 直接法 **】

class Solution {
public:
    ListNode* deleteDuplicates(ListNode* head) {
        ListNode* cur = head;
        while(cur && cur -> next){//循环结束条件:1 空链表 2 尾节点
            if(cur -> next -> val == cur -> val){
                cur -> next = cur -> next -> next;
            }
            else{
                 cur = cur -> next;
            }
           
        }
        return head;
        
    }
};

19.删除链表的倒数第N个节点

🙊 MEDIUM

Q&A
给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。
示例:
给定一个链表: 1->2->3->4->5, 和 n = 2.
当删除了倒数第二个节点后,链表变为 1->2->3->5.

**🍓🍓【WAY1 C++ 一次遍历(有一定间隔距离的双指针) **】
哑节点的妙用
哑节点是在处理与链表相关的操作时,设置在链表头之前的指向链表头的节点,用于简化与链表头相关的操作。

ListNode dummy = new ListNode(0);
dummy.next = head;
//head是链表的头节点,dummy就是指向链表头部的哑节点。

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummy = new ListNode(0);//哑节点 语句1
        //LisNode dummyNode(0); dummyNode.next = head;设置哑结点,用栈变量极好from网友
        if(!head) return head;//语句2 建议:语句1和2互换位置,避免潜在的内存泄漏,return 之前没有delete dummy;
        dummy -> next = head;
        ListNode* p = dummy;
        ListNode* q = dummy;
        while(n--){
            p = p -> next;
        }
        while(p -> next != NULL){
            p = p -> next;
            q = q -> next;
        }
        q -> next = q -> next -> next;

        ListNode* res = dummy -> next;
        delete dummy;

        return res;
    }
};

**🍓🍓【WAY2 C++ 两次遍历 **】

基本方法:两次遍历:要删除倒数第n个节点,也就是顺数N = len-n+1个节点.

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        int len = 1;
        ListNode* temp = head;
        while(temp -> next){
            temp = temp -> next;
            len++;
            }
        if(n == len) return head -> next;
        ListNode* p = head;
        ListNode* q = head -> next;
        for(int i = 0; i < len - n - 1;i++){
            p = p -> next;
            //q = q -> next;

        }
        p -> next = p -> next -> next;
        return head;

        
    }
};

24.两两交换链表中的节点

🙊 MEDIUM

Q&A
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
你不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
示例:
给定 1->2->3->4, 你应该返回 2->1->4->3.

**🍓🍓【WAY1 C++ 迭代法 **】

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        ListNode* dummy = new ListNode(0);//新建一个哑节点,用来指向头节点
        dummy -> next = head;

        ListNode* curr = dummy;//新建一个和哑节点相同的节点,一个用作当前改变的节点,一个保持不动用来返回

        while(head != NULL && head -> next != NULL){
            ListNode* firstNode = head;//一号节点
            ListNode* secondNode = head -> next;//二号节点
             curr -> next = secondNode;//指向第二节点,用于交换后连接前部(个人理解)
             //1,2 节点交换
             firstNode -> next = secondNode -> next;
             secondNode -> next = firstNode;

             //每次循环curr都需要指向每次循环的一号节点
             curr = firstNode;
             //head移动,指向新一次循环的一号节点
             head = firstNode -> next;
        }
        
        return dummy -> next;

        
    }
};

**🍓🍓【WAY2 C++ 递归法 **】有疑惑

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        if(head == NULL || head -> next == NULL){
            return head;
        }
        ListNode* p = head -> next;
        ListNode* temp = p -> next;
        p -> next = head;
        head -> next = swapPairs(temp);//Q1

        return p;
        
    }
};

445. 两数相加 II

🙊 MEDIUM

Q&A
给你两个 非空 链表来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储一位数字。将这两数相加会返回一个新的链表。

你可以假设除了数字 0 之外,这两个数字都不会以零开头。

进阶:

如果输入链表不能修改该如何处理?换句话说,不能对列表中的节点进行翻转。

示例:
输入:(7 -> 2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 8 -> 0 -> 7

**🍓🍓【WAY C++ 栈 **】

class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
        if(!l1  || !l2) return l1 == NULL ? l1 : l2;
        stack<int> s1,s2;//利用栈的特性,先进后出
        while(l1){
            s1.push(l1 -> val);
            l1 = l1 -> next;
        }
        while(l2){
            s2.push(l2 -> val);
            l2 = l2 -> next;
        }
        int carry = 0;//标志进位的变量

        ListNode* ans = nullptr;//哑节点
        while(!s1.empty() or !s2.empty() or carry != 0){
            int a = s1.empty() ? 0 : s1.top();
            int b = s2.empty() ? 0 : s2.top();
            if( !s1.empty()) s1.pop();
            if( !s2.empty()) s2.pop();
            int cur = a + b + carry;
            carry = cur / 10;
            cur = cur % 10;
            
            auto curnode = new ListNode(cur);//头插法
            curnode -> next = ans;
            ans = curnode;
        }
        return ans;
    }
};

725. 分隔链表

🙊 MEDIUM

Q&A
给定一个头结点为 root 的链表, 编写一个函数以将链表分隔为 k 个连续的部分。

每部分的长度应该尽可能的相等: 任意两部分的长度差距不能超过 1,也就是说可能有些部分为 null。

这k个部分应该按照在链表中出现的顺序进行输出,并且排在前面的部分的长度应该大于或等于后面的长度。

返回一个符合上述规则的链表的列表。

举例: 1->2->3->4, k = 5 // 5 结果 [ [1], [2], [3], [4], null ]
例1:
输入:
root = [1, 2, 3], k = 5
输出: [[1],[2],[3],[],[]]
解释:
输入输出各部分都应该是链表,而不是数组。

例2
输入:
root = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], k = 3
输出: [[1, 2, 3, 4], [5, 6, 7], [8, 9, 10]]
解释:
输入被分成了几个连续的部分,并且每部分的长度相差不超过1.前面部分的长度大于等于后面部分的长度。

【知识补充】push_back
函数将一个新的元素加到vector的最后面,位置为当前最后一个元素的下一个元素

push_back() 在Vector最后添加一个元素(参数为要插入的值)

//在vec尾部添加27
 
vector<int> vec;
vec.push_back(27);

**🍓🍓【WAY C++ **】

class Solution {
public:
    vector<ListNode*> splitListToParts(ListNode* root, int k) {
        int sum = 0;
        ListNode* p = root;
        while(p){
            sum ++;
            p = p -> next;
        }
        //确定节点分配方案
        vector<int> num(k,sum/k);//将sum个节点分为k份,每份sum/k个节点
        for(int i = 0;i < (sum % k); i++){
            num[i] ++;//分配余数的节点,确定每份节点最终数目
        }

        //按照分配方案断链表的节点
        vector<ListNode * > res;
        ListNode* head = new ListNode(0);
        head -> next = root;
        ListNode* pre = head,* cur = root;
        //切断方法:设置pre指针,永远指向当前节点的前一个节点
        for(int i = 0;i < k;i++){
            if(num[i] != 0){
                while(num[i]){
                    cur = cur -> next;
                    pre = pre -> next;
                    num[i] --;

                }
                pre -> next = nullptr;
                res.push_back(head -> next);
                head -> next = cur;
                pre = head;
            }
            else res.push_back(nullptr);
        }
        return res;
    }
};

328. 奇偶链表

🙊 MEDIUM

Q&A
给定一个单链表,把所有的奇数节点和偶数节点分别排在一起。请注意,这里的奇数节点和偶数节点指的是节点编号的奇偶性,而不是节点的值的奇偶性。

请尝试使用原地算法完成。你的算法的空间复杂度应为 O(1),时间复杂度应为 O(nodes),nodes 为节点总数。

示例 1:
输入: 1->2->3->4->5->NULL
输出: 1->3->5->2->4->NULL
示例 2:
输入: 2->1->3->5->6->4->7->NULL
输出: 2->3->6->7->1->5->4->NULL
说明:
应当保持奇数节点和偶数节点的相对顺序。
链表的第一个节点视为奇数节点,第二个节点视为偶数节点,以此类推。

class Solution {
public:
    ListNode* oddEvenList(ListNode* head) {
        if(!head || !head -> next) return head;
        ListNode* even = head -> next;//even 偶数指针
        ListNode* odd = head;//odd 奇数指针
        ListNode* evenHead = even;//保存偶数链表头部
       while(even && even -> next){
           odd -> next = even -> next;
           odd = odd -> next;
           even -> next = odd -> next;//
           even = even -> next;
       } 
       odd -> next = evenHead;//奇偶链表链接:偶数头部追加至奇数链表尾部

       return head;
        
    }
};

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/add-two-numbers-ii

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值