【算法系列】链表

news2024/11/25 10:35:10

目录

常用技巧

常用操作

 leetcode/牛客题目

一、移除链表元素

二、反转链表

三、链表的中间结点

四、返回倒数第k个节点

五、合并两个有序链表

六、链表分割

七、链表的回文结构

八、相交链表

九、环形链表

十、环形链表 II

十一、随机链表的复制

十二、两数相加

十三、两两交换链表中的节点

十四、重排链表

十五、合并K个升序链表

十六、K个一组翻转链表


常用技巧

1.画图 --- 直观,便于理解,代码不容易出错

2.引入虚拟头节点(带哨兵位的头节点) --- 便于处理边界情况,方便对链表操作

3.不要吝啬空间,大胆去定义变量

如上图所示,要将cur插入到两个节点之间,那么①与②的顺序就不能颠倒,但是如果定义了一个指针变量next,就完全不用考虑链接顺序了!

①②③④可以任意颠倒~

4.快慢双指针~

比如判环,找链表中环的入口,找链表倒数第n个节点

常用操作

1.创建一个新节点

2.尾插

3.头插

 leetcode/牛客题目

一、移除链表元素

203. 移除链表元素 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/remove-linked-list-elements/description/

1.题目解析

删除链表中值为val的所有节点

2.算法分析

法一: 把所有值不等于val的节点进行尾插

法二: 在原链表进行删除操作

3.算法代码

法一: 把所有值不等于val的节点进行尾插

class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) 
    {
        ListNode* newHead = new ListNode(); //虚拟头节点
        ListNode* cur = head, *tail = newHead;
        while(cur)
        {
            if(cur->val != val)
            {
                tail->next = cur;
                tail = tail->next;
                cur = cur->next;
            }
            else
            {
                ListNode* next = cur->next;
                delete cur;
                cur = next;
            }
        }
        tail->next = nullptr;
        return newHead->next;
    }
};

法二: 在原链表进行删除操作

class Solution {
public:
    ListNode* removeElements(ListNode* head, int val) 
    {
        ListNode* newHead = new ListNode(); //虚拟头节点
        newHead->next = head;
        ListNode* cur = head, *prev = newHead; //prev用于记录当前节点的前一个节点
        while(cur)
        {
            if(cur->val == val)
            {    
                prev->next = cur->next;
                delete cur;
                cur = prev->next;
            }
            else
            {
                prev = cur;
                cur = cur->next;
            }
        }
        return newHead->next;
    }
};

二、反转链表

206. 反转链表 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/reverse-linked-list/1.题目解析

反转链表

2.算法分析

法一:头插法

法二:三指针反转链表

3.算法代码

法一:头插法

class Solution {
public:
    ListNode* reverseList(ListNode* head) 
    {
        ListNode* newHead = new ListNode();
        ListNode* cur = head;
        while(cur)
        {
            ListNode* next = cur->next;
            cur->next = newHead->next;
            newHead->next = cur;
            cur = next;
        }
        ListNode* ret = newHead->next;
        delete newHead;
        return ret;
    }
};

法二:三指针反转链表

class Solution {
public:
    ListNode* reverseList(ListNode* head) 
    {
        if(head == nullptr) return head;

        ListNode* n1 = nullptr;
        ListNode* n2 = head;
        ListNode* n3 = head->next;
        while(n2)
        {
            //1.改变指针指向
            n2->next = n1;
            //2.循环迭代
            n1 = n2;
            n2 = n3;
            if(n3) 
                n3 = n3->next;
        }
        return n1;
    }
};

三、链表的中间结点

876. 链表的中间结点 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/middle-of-the-linked-list/1.题目解析

返回链表的中间节点,如果有两个中间节点,返回第二个中间节点

2.算法分析
这类题目是典型的双指针解法,定义快慢指针,快指针每次走两步,慢指针每次走一步,当快指针走到空或者走到最后一个节点,慢指针指向的就是中间节点

3.算法代码

class Solution {
public:
    ListNode* middleNode(ListNode* head) 
    {
        ListNode* fast = head, *slow = head;
        while(fast && fast->next)
        {
            fast = fast->next->next;
            slow = slow->next;
        }
        return slow;
    }
};

四、返回倒数第k个节点

面试题 02.02. 返回倒数第 k 个节点 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/kth-node-from-end-of-list-lcci/1.题目解析

返回链表倒数第k个节点中的值

2.算法分析

法一:先遍历一遍链表,求出节点总个数n, 再从头遍历,  循环n-k次,求出倒数第k个节点的值

法二:快慢双指针策略

fast和slow指针开始都在起始位置,fast先走k步,然后slow和fast再一起走,每次都走一步,当fast走到空时,slow的位置就是倒数第k个节点

3.算法代码

法一:

class Solution {
public:
    int kthToLast(ListNode* head, int k) 
    {
        ListNode* cur = head;
        int n = 0;
        while(cur)
        {
            cur = cur->next;
            n++;
        }
        cur = head;
        for(int i = 0; i < n-k; i++)
        {
            cur = cur->next;
        }    
        return cur->val;
    }
};

法二:

class Solution {
public:
    int kthToLast(ListNode* head, int k) 
    {
        ListNode* slow = head, *fast = head;
        while(k--)
            fast = fast->next;
        while(fast)
        {
            fast = fast->next;
            slow = slow->next;
        }
        return slow->val;
    }
};

五、合并两个有序链表

21. 合并两个有序链表 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/merge-two-sorted-lists/description/1.题目解析

将两个升序链表合并成1个升序的链表

2.算法分析

本题和合并两个有序数组非常相似,采用的方法都是遍历两个链表(数组), 每次取小的进行尾插

3.算法代码

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2)
    {
        if(list1 == nullptr) return list2;
        if(list2 == nullptr) return list1;

        ListNode* newHead = new ListNode(), *tail = newHead;
        ListNode* cur1 = list1, *cur2 = list2;
        while(cur1 && cur2)
        {
            if(cur1->val <= cur2->val)
            {
                tail->next = cur1;
                tail = tail->next;
                cur1 = cur1->next;
            }
            else
            {
                tail->next = cur2;
                tail = tail->next;
                cur2 = cur2->next;
            }
        }  
        if(cur1) tail->next = cur1;
        if(cur2) tail->next = cur2;

        ListNode* ret = newHead->next;
        delete newHead;
        return ret;
    }
};

六、链表分割

链表分割_牛客题霸_牛客网 (nowcoder.com)icon-default.png?t=N7T8https://www.nowcoder.com/practice/0e27e0b064de4eacac178676ef9c9d70?tpId=8&&tqId=11004&rp=2&ru=/activity/oj&qru=/ta/cracking-the-coding-interview/question-ranking1.题目解析

给定一个值x, 将所有值小于x的节点排在其余节点之前

2.算法分析

我们只需要遍历一遍原始链表,将值小于x的节点尾插到一个新的链表中,将值>=x的节点尾插到另一个新的链表中,然后将前一个新的链表和后一个新的链表链接起来即可,但是为了方便处理,我们仍然给两个新的链表都添加了虚拟头节点

3.算法代码

class Partition {
public:
    ListNode* partition(ListNode* pHead, int x) 
    {
        ListNode* greaterHead = new ListNode(-1), *greatertail = greaterHead;
        ListNode* lessHead = new ListNode(-1), *lesstail = lessHead;
        ListNode* cur = pHead;
        while(cur)
        {
            if(cur->val < x)
            {
                lesstail->next = cur;
                lesstail = lesstail->next;
                cur = cur->next;
            }
            else 
            {
                greatertail->next = cur;
                greatertail = greatertail->next;
                cur = cur->next;
            }
        }
        lesstail->next = greaterHead->next;
        greatertail->next = nullptr;
        return lessHead->next;
    }
};

七、链表的回文结构

链表的回文结构_牛客题霸_牛客网 (nowcoder.com)icon-default.png?t=N7T8https://www.nowcoder.com/practice/d281619e4b3e4a60a2cc66ea32855bfa?tpId=49&&tqId=29370&rp=1&ru=/activity/oj&qru=/ta/2016test/question-ranking1.题目解析

判断链表是否是回文链表

2.算法分析

本题可以拆解成以下几个问题

1.求链表的中间节点

2.翻转中间节点以后的部分

3.判断前半部分链表和翻转后的后半部分链表是否相等

3.算法代码

class PalindromeList {
public:
    bool chkPalindrome(ListNode* A) 
    {
        //1.找链表的中间节点
        ListNode* fast = A, *slow = A;
        while(fast && fast->next)
        {
            fast = fast->next->next;
            slow = slow->next;
        }
        //2.翻转后半部分
        ListNode* newHead = new ListNode(-1);
        ListNode* cur = slow;
        while(cur)
        {
            ListNode* next = cur->next;
            cur->next = newHead->next;
            newHead->next = cur;
            cur = next;
        }
        //3.判断两个链表是否相等
        ListNode* B = newHead->next;
        while(B)
        {
            if(A->val != B->val)
                return false;
            A = A->next;
            B = B->next;
        }
        return true;
    }
};

八、相交链表

160. 相交链表 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/intersection-of-two-linked-lists/1.题目解析

求两个相交链表的相交的起始节点

2.算法分析

解法一:

1.分别遍历两个链表,求出两个链表的长度(节点的个数), 并求出长度差 gap

2.定义快慢指针,让fast指针指向较长链表的第一个节点,先走gap步

3.快慢指针同时向后走,当快慢指针相遇时,就是相交链表的起始节点

解法二:

3.算法代码

解法一:

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) 
    {
        ListNode* cur1 = headA, *cur2 = headB;
        int lenA = 0, lenB = 0;
        while(cur1)
        {
            cur1 = cur1->next;
            lenA++;
        }
        while(cur2)
        {
            cur2 = cur2->next;
            lenB++;
        }

        ListNode* fast = headA, *slow = headB;
        if(lenA < lenB)
        {
            fast = headB;
            slow = headA;
        }
        int gap = abs(lenA - lenB);
        while(gap--)
            fast = fast->next;

        while(slow != fast)
        {
            slow = slow->next;
            fast = fast->next;
        }
        return slow;
    }
};

解法二:

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB)
    {
        ListNode* cur1 = headA, *cur2 = headB;
        while(cur1 != cur2)
        {
            cur1 = cur1 != nullptr ? cur1->next : headB;
            cur2 = cur2 != nullptr ? cur2->next : headA;
        }  
        return cur1;
    }
};

九、环形链表

141. 环形链表 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/linked-list-cycle/1.题目解析

判断链表中是否有环

2.算法分析

定义快慢指针,快指针每次走两步,慢指针每次走一步,如果快慢指针可以相遇,说明链表中有环;如果fast指针走到了空/fast的下一个节点就是空,说明链表中不带环

简单证明: 如果有环,fast必然先进入环,slow后进入环,所以当slow即将入环时,假设相距x, 由于fast每次走两步,slow每次走一步,所以fast和slow之间的距离每次缩小1,因此最后距离总会减小到0,也就是相遇!

 拓展: fast每次走3步是不可以的,因为如果fast每次走3步,slow走1步,距离每次缩小2,如果x是偶数,那么x会减小到0,slow和fast相遇,但是如果x是奇数,那么距离会减小到1, -1 ...., 就会错过距离0, 因此本轮slow和fast没有相遇; 下一轮slow和fast相距N-1( N代表环的长度 ), 如果N是奇数,那么N-1是偶数,则本轮可以相遇;若N是偶数,N-1是奇数,则fast和slow永远都相遇不了!

3.算法代码

class Solution {
public:
    bool hasCycle(ListNode *head) 
    {
        ListNode* fast = head, *slow = head;
        while(fast && fast->next)
        {
            fast = fast->next->next;
            slow = slow->next;
            if(fast == slow) return true;
        }   
        return false;
    }
};

十、环形链表 II

142.环形链表 IIicon-default.png?t=N7T8https://leetcode.cn/problems/linked-list-cycle-ii/description/1.题目解析

如果链表不带环,返回nullptr, 如果链表带环,返回环形链表的第一个入环节点的指针

2.算法分析

如何判断链表是否带环,题目九已经讲解过了,因此我们接下来的关键就是求入环节点

法一: 快慢指针

如果有环, slow和fast必在环内相遇,此时再定义一个指针cur2, cur2从fast和slow相遇位置开始走,与此同时,cur1(原先链表的head)从头开始走,两个指针每次都走一步,当cur1和cur2相遇时,相遇的地方就是入环的第一个节点

简单证明:

法二: 转化成题目八 ---> 相交链表

如果有环, slow和fast必在环内相遇,此时可以将环断开,形成两个链表,此时问题就转化成了相交链表的问题了~

3.算法代码

法一:

class Solution {
public:
    ListNode *detectCycle(ListNode *head) 
    {
        ListNode* fast = head, *slow = head;
        while(fast && fast->next)
        {
            fast = fast->next->next;
            slow = slow->next;
            if(slow == fast) //一定有环
            {
                ListNode* cur1 = head, *cur2 = fast;
                while(cur1 != cur2)
                {
                    cur1 = cur1->next;
                    cur2 = cur2->next;
                }
                return cur1;
            }
        }  
        return nullptr;
    }
};

法二:

​

class Solution {
public:
    //求相交链表的第一个交点
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) 
    {
        ListNode* cur1 = headA, *cur2 = headB;
        int lenA = 0, lenB = 0;
        while(cur1)
        {
            cur1 = cur1->next;
            lenA++;
        }
        while(cur2)
        {
            cur2 = cur2->next;
            lenB++;
        }

        ListNode* fast = headA, *slow = headB;
        if(lenA < lenB)
        {
            fast = headB;
            slow = headA;
        }
        int gap = abs(lenA - lenB);
        while(gap--)
            fast = fast->next;

        while(slow != fast)
        {
            slow = slow->next;
            fast = fast->next;
        }
        return slow;
    }

    ListNode *detectCycle(ListNode *head) 
    {
        ListNode* fast = head, *slow = head;
        while(fast && fast->next)
        {
            fast = fast->next->next;
            slow = slow->next;
            if(slow == fast) //一定有环
            {
                ListNode* head2 = fast->next;
                fast->next = nullptr; //断开,形成两个链表
                return getIntersectionNode(head, head2);
            }
        }  
        return nullptr;
    }
};

十一、随机链表的复制

138. 随机链表的复制 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/copy-list-with-random-pointer/description/

1.题目解析

题目给定一个链表,链表中除了有指向下一个节点的指针之外,还有指向链表中随机一个节点的指针random, 题目要求深拷贝原链表,并返回

2.算法分析

1.将所有的新节点链接到原节点后面

2.控制所有复制节点中的random指向

3.解开新链表和原链表绑定,恢复原链表

3.算法代码

class Solution {
public:
    Node* copyRandomList(Node* head) 
    {
        //1.将所有的新节点链接到原节点后面
        Node* cur = head;
        while(cur)
        {
            Node* next = cur->next; //先保存下一个节点
            Node* copy = new Node(cur->val); //开辟复制节点
            cur->next = copy; //将复制节点链接到原链表对应节点后面
            copy->next = next; //将下一个节点链接到复制节点的后面
            cur = next; //更新cur到下一个节点
        } 

        //2.控制所有复制节点中的random指向
        cur = head;
        while(cur)
        {
            Node* copy = cur->next; //找到复制节点
            if(cur->random == nullptr) //原节点的random为空,则复制节点的random为空
            {
                copy->random = nullptr; 
            }
            else //原节点的random不为空,则复制节点的random指向cur->random->next
            {
                copy->random = cur->random->next;
            }
            cur = copy->next; //更新cur指针
        }

        //3.解开原链表和新链表的绑定,恢复原链表
        cur = head;
        Node* copyHead = NULL, *copyTail = NULL;  //copyHead记录复制链表的开始, copyTail记录复制链表的结尾
        while(cur)
        {
            Node* copy = cur->next; //记录复制节点
            Node* next = copy->next; //记录原链表下一个节点

            if(copyTail == nullptr) //开始时copyTail为空
            {
                copyHead = copyTail = copy;
            }
            else
            {
                copyTail->next = copy; //链接复制链表两个相邻节点
                copyTail = copyTail->next; //更新copyTail指针
            }
            cur->next = next; //链接原链表的相邻两个节点
            cur = next; //更新cur指针
        }
        return copyHead;
    }
};

十二、两数相加

2. 两数相加 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/add-two-numbers/description/1.题目解析

两个单链表,每个链表存储了一个整数(逆序存储), 返回两数相加之后的结果(依旧是链表)

2.算法分析

题目中的逆序存储是为了方便我们操作,我们只需要从头开始向后遍历链表相加即可

解法就是模拟两数相加即可

3.算法代码

class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2)
    {
        ListNode* head = new ListNode(); //创建虚拟头节点
        ListNode* cur1 = l1, *cur2 = l2, *tail = head;
        int t = 0; //记录当前位置相加结果
        while(cur1 || cur2 || t) //有可能当cur1和cur2都为空时,t中还保留了一个进位
        {
            if(cur1)
            {
                t += cur1->val;
                cur1 = cur1->next;
            }
            if(cur2) 
            {
                t += cur2->val;
                cur2 = cur2->next;
            }
            //链接新节点
            tail->next = new ListNode(t % 10);
            tail = tail->next;
            t /= 10; //进位情况
        }
        //释放虚拟头节点
        tail = head->next;
        delete head;
        return tail;
    }
};

十三、两两交换链表中的节点

24. 两两交换链表中的节点 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/swap-nodes-in-pairs/description/1.题目解析

两两交换链表中的节点, 但是不能直接交换节点中的值, 而是要改变指针指向

2.算法分析

2.1增加虚拟头节点,统一了交换1,2节点和交换后面两两节点的操作

2.2采用循环的方式完成整个过程

2.3 循环结束的条件是 cur为空或者 next为空

2.2所给图示是next为空就结束了,但是还有可能是cur为空结束~

3.算法代码

class Solution {
public:
    ListNode* swapPairs(ListNode* head)
    {
        if(head == nullptr || head->next == nullptr) return head;

        ListNode* newHead = new ListNode();
        newHead->next = head;
        ListNode* prev = newHead, *cur = head, *next = head->next, *nnext = next->next;

        while(cur && next)
        {
            //1.交换节点
            prev->next = next;
            next->next = cur;
            cur->next = nnext;
            //2.修改指针(注意顺序)
            prev = cur;
            cur = nnext;
            if(cur) next = cur->next;
            if(next) nnext = next->next;
        }
        cur = newHead->next;
        delete newHead;
        return cur;
    }
};

十四、重排链表

143. 重排链表 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/reorder-list/1.题目解析

按题目要求将链表节点重新排列顺序

2.算法分析

这题目可以拆成以下3个子问题~

1.找到链表的中间节点 --- 双指针

2.把后面的部分逆序 --- 头插法

3.合并两个链表 --- 双指针

注意:找到中间节点之后,我们可以逆序连同中间节点之后的部分,也可以逆序不包含中间节点之后的部分

但是我们逆序完之后需要形成两个链表,如果采用头插法逆序包含中间节点及以后的部分,那么链表就无法断开,因为我们没有办法得到mid的前一个节点地址prev,从而无法将prev的next置成空指针,而如果采用头插法逆序中间节点以后的部分, 断开链表只需要将mid->next置成空指针即可

如果一定要逆序包含mid及以后的部分,可以添加虚拟头节点,此时slow的落点就是上面的prev了!

3.算法代码

class Solution {
public:
    void reorderList(ListNode* head)
    {
        //特殊处理,不需要重排
        if(head == nullptr || head->next == nullptr || head->next->next == nullptr) return;

        //1.找到链表的中间节点
        ListNode* slow = head, *fast = head;
        while(fast && fast->next)
        {
            slow = slow->next;
            fast = fast->next->next;
        }

        //2.把slow后面的部分逆序
        ListNode* head2 = new ListNode();
        ListNode* cur = slow->next;
        slow->next = nullptr; //断开链表
        while(cur)
        {
            ListNode* next = cur->next;
            cur->next = head2->next;
            head2->next = cur;
            cur = next;
        }

        //3.合并两个链表
        ListNode* ret = new ListNode(), *tail = ret;
        ListNode* cur1 = head, *cur2 = head2->next;
        while(cur1) //由于第一个链表一定比第二个链表长, 因此判断cur1即可
        {
            //尾插第一个链表
            tail->next = cur1;
            cur1 = cur1->next;
            tail = tail->next;
            //尾插第二个链表
            if(cur2)
            {
                tail->next = cur2;
                cur2 = cur2->next;
                tail = tail->next;
            }
        }
    }
};

十五、合并K个升序链表

23. 合并 K 个升序链表 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/merge-k-sorted-lists/description/1.题目解析

将所有的升序链表合并成一个升序链表,并返回

2.算法分析

法一:暴力解法:链表两两合并,直至所有链表合并完成, 时间复杂度:  假设每个链表长度为n, 则时间复杂度为 O(n*(k-1) + n*(k-2) + ... +)  =  O(n*k^2)

法二:利用优先级队列优化暴力枚举策略

我们可以创建一个小堆,开始时将所有的链表头节点指针都放进去,然后创建一个新的虚拟头节点,出堆顶元素尾插,然后让堆顶元素指针的下一个节点指针进入堆,依次类推,  时间复杂度为O(n*k*logk)

法三:分治-递归

从中间劈开,如果左右部分已经合并完成,那么最后只需要合并左右两个链表即可,而左右链表如何合并的呢???和原问题是相同的,依旧从中间劈开,让左右部分合并完成即可

3.算法代码

法二:优先级队列

class Solution 
{
public:
    //仿函数
    struct cmp
    {
        bool operator()(const ListNode* l1, const ListNode* l2)
        {
            return l1->val > l2->val;
        }
    };

    ListNode* mergeKLists(vector<ListNode*>& lists)
    {
        //创建小根堆
        priority_queue<ListNode*, vector<ListNode*>, cmp> heap; //小堆
        for(auto& l : lists) 
            if(l) heap.push(l);

        //合并所有链表
        ListNode* newHead = new ListNode(), *tail = newHead;
        while(!heap.empty())
        {
            ListNode* top = heap.top();
            heap.pop();
            tail->next = top;
            tail = tail->next;
            if(top->next) heap.push(top->next);
        }
        return newHead->next;
    }
};

法三:分治-递归

class Solution 
{
public:
    ListNode* mergeKLists(vector<ListNode*>& lists)
    {
        return merge_sort(lists, 0, lists.size()-1);
    }
    ListNode* merge_sort(vector<ListNode*>& lists, int left, int right)
    {
        if(left > right) return nullptr;
        if(left == right) return lists[left];

        //1.求中间节点
        int mid = (left + right) >> 1;
        //2.递归左右区间
        ListNode* l1 = merge_sort(lists, left, mid);
        ListNode* l2 = merge_sort(lists, mid+1, right);
        //3.合并两个有序链表
        return merge_two_lists(l1, l2);
    }

    ListNode* merge_two_lists(ListNode* l1, ListNode* l2)
    {
        if(l1 == nullptr) return l2;
        if(l2 == nullptr) return l1;
        ListNode* cur1 = l1, *cur2 = l2;
        ListNode* newHead = new ListNode(), *tail = newHead;
        while(cur1 && cur2)
        {
            if(cur1->val <= cur2->val)
            {
                tail->next = cur1;
                tail = tail->next;
                cur1 = cur1->next;
            }
            else  
            {
                tail->next = cur2;
                tail = tail->next;
                cur2 = cur2->next;
            }  
        }
        if(cur1) tail->next = cur1;
        if(cur2) tail->next = cur2;
        return newHead->next;
    }
};

十六、K个一组翻转链表

25. K 个一组翻转链表 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/reverse-nodes-in-k-group/1.题目解析

K个一组翻转链表,最后不够K个节点,就不用再翻转了~

2.算法分析

1.求出需要翻转多少组:n  ---> 链表节点总个数 / k 即可

2.重复n次, 长度为k的链表的翻转即可

3.算法代码

class Solution {
public:
    ListNode* reverseKGroup(ListNode* head, int k) 
    {
        //1.求需要翻转多少组
        int n = 0;
        ListNode* cur = head;
        while(cur)
        {
            cur = cur->next;
            n++;
        }
        n /= k;

        //2.重复n次,长度为k的链表的翻转
        ListNode* newHead = new ListNode(), *prev = newHead;
        cur = head;
        for(int i = 0;i < n; i++)
        {
            ListNode* tmp = cur;
            for(int j = 0; j < k; j++)
            {
                ListNode* next = cur->next;
                cur->next = prev->next;
                prev->next = cur;
                cur = next;
            }
            prev = tmp;
        }
        //把不需要翻转的节点接上
        prev->next = cur;
        return newHead->next;
    }
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1647111.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

英语学习笔记2——Is this your ...?

Is this your …? 这是你的 … 吗&#xff1f; 词汇 Vocabulary pen n. 笔 不仅指钢笔&#xff0c;是笔的统称 相关&#xff1a;ball pen n. 圆珠笔    pencil n. 铅笔    marker n. 记号笔 book n. 书 横着翻的本子或书 补充&#xff1a;pad n. 本子 竖着翻的本子或…

在Codelab对llama3做Lora Fine tune微调

Unsloth 高效微调大模型的工具&#xff0c;通过Unsloth微调Llama3, Mistral, Gemma 速度提升2-5倍&#xff0c;内存减少70%&#xff01; Codelab 创建一个jupyter notebook 选择 T4 GPU 安装Fine tune 相关的lib %%capture import torch major_version, minor_version torch…

软考中级-软件设计师(九)数据库技术基础 考点最精简

一、基本概念 1.1数据库与数据库系统 数据&#xff1a;是数据库中存储的基本对象&#xff0c;是描述事物的符号记录 数据库&#xff08;DataBase&#xff0c;DB&#xff09;&#xff1a;是长期存储在计算机内、有组织、可共享的大量数据集合 数据库系统&#xff08;DataBas…

Navicat for MySQL Mac:数据库管理与开发的理想工具

Navicat for MySQL Mac是一款功能强大的数据库管理与开发工具&#xff0c;专为Mac用户设计&#xff0c;旨在提供高效、便捷的数据库操作体验。 它支持创建、管理和维护MySQL和MariaDB数据库&#xff0c;通过直观的图形界面&#xff0c;用户可以轻松进行数据库连接、查询、编辑和…

力扣:221. 最大正方形

221. 最大正方形 在一个由 0 和 1 组成的二维矩阵内&#xff0c;找到只包含 1 的最大正方形&#xff0c;并返回其面积。 示例 1&#xff1a; 输入&#xff1a;matrix [["1","0","1","0","0"],["1","0"…

数字藏品平台遭受科技攻击时的防护策略与攻击类型判定

随着区块链技术和数字经济的飞速发展&#xff0c;数字藏品平台逐渐成为炙手可热的投资领域。然而&#xff0c;这也使其成为了黑客攻击的重要目标。本文将深入探讨数字藏品平台可能遭遇的几种主要科技攻击类型&#xff0c;并提出相应的防护措施和判定方法。 一、51%攻击 攻击描…

太速科技-FMC377_双AD9361 射频收发模块

FMC377_双AD9361 射频收发模块 FEATURES&#xff1a; ◆ Coverage from 70M ~ 6GHz RF ◆ Flexible rate 12 bit ADC/DAC ◆ Fully-coherent 4x4 MIMO capability, TDD/FDD ◆ RF ports: 50Ω Matched ◆ support both internal reference and exter…

cmake进阶:变量的作用域说明二(从函数作用域方面)

一. 简介 前一篇文章从函数作用域方面学习了 变量的作用域。文章如下&#xff1a; cmake进阶&#xff1a;变量的作用域-CSDN博客 本文继续从函数作用域方面学习了 变量的作用域。 二. 变量的作用域 1. 函数内定义与外部同名的变量 向顶层 CMakeLists.txt添加如下代码&a…

leetcode-缺失的第一个正整数-96

题目要求 思路 1.这里的题目要求刚好符合map和unordered_map 2.创建一个对应map把元素添加进去&#xff0c;用map.find(res)进行查找&#xff0c;如果存在返回指向该元素的迭代器&#xff0c;否则返回map::end()。 代码实现 class Solution { public:int minNumberDisappeare…

项目管理-干系人管理

项目管理&#xff1a;每天进步一点点~ 活到老&#xff0c;学到老 ヾ(◍∇◍)&#xff89;&#xff9e; 何时学习都不晚&#xff0c;加油 1.干系人管理-主要框架 重点内容&#xff1a; ①ITTO 输入&#xff0c;输出工具和技术。 ②问题和解决方案。 ③论文可以结合范围&am…

[Linux][网络][TCP][三][超时重传][快速重传][SACK][D-SACK][滑动窗口]详细讲解

目录 1.超时重传1.什么是超时重传&#xff1f;2.超时时间是如何确定的&#xff1f; 2.快速重传3.SACK4.D-SACK1.ACK丢失2.网络延迟 5.滑动窗口0.问题抛出1.发送方的滑动窗口2.如何表示发送方的四个部分&#xff1f;3.接收方的滑动窗口4.滑动窗口的完善理解 1.超时重传 1.什么是…

14【PS作图】像素画尺寸大小

【背景介绍】本节介绍像素图多大合适 下图是160*144像素大小,有一个显示文本的显示器,还有一个有十几个键的键盘 像素画布尺寸 电脑16像素,但还有一个显示屏 下图为240*160 在场景素材,和对话素材中,用的是不同尺寸的头像,对话素材中的头像会更清楚,尺寸会更大 远处…

五月节放假作业讲解

目录 作业1&#xff1a; 问题&#xff1a; 结果如下 作业2&#xff1a; 结果: 作业1&#xff1a; 初始化数组 问题&#xff1a; 如果让数组初始化非0数会有问题 有同学就问了&#xff0c;我明明已经初始化定义过了&#xff0c;为啥还有0呀 其实这种初始化只会改变第一个…

服务攻防-数据库安全RedisCouchDBH2database未授权访问CVE漏洞

#知识点&#xff1a; 1、数据库-Redis-未授权RCE&CVE 2、数据库-Couchdb-未授权RCE&CVE 3、数据库-H2database-未授权RCE&CVE#章节点&#xff1a; 1、目标判断-端口扫描&组合判断&信息来源 2、安全问题-配置不当&CVE漏洞&弱口令爆破 3、复现对象-数…

强化学习:时序差分法【Temporal Difference Methods】

强化学习笔记 主要基于b站西湖大学赵世钰老师的【强化学习的数学原理】课程&#xff0c;个人觉得赵老师的课件深入浅出&#xff0c;很适合入门. 第一章 强化学习基本概念 第二章 贝尔曼方程 第三章 贝尔曼最优方程 第四章 值迭代和策略迭代 第五章 强化学习实例分析:GridWorld…

《Video Mamba Suite》论文笔记(4)Mamba在时空建模中的作用

原文翻译 4.4 Mamba for Spatial-Temporal Modeling Tasks and datasets.最后&#xff0c;我们评估了 Mamba 的时空建模能力。与之前的小节类似&#xff0c;我们在 Epic-Kitchens-100 数据集 [13] 上评估模型在zero-shot多实例检索中的性能。 Baseline and competitor.ViViT…

解决Win10家庭版找不到组策略gpedit.msc的·方法

因为电脑出问题&#xff0c;一开机就会自动开启ie浏览器&#xff0c;所以就想找有没有方法解决&#xff0c;然后就了解到了gpedit.msc的作用以及相关的一些方法&#xff0c;也是为之后也许有人遇到相同的问题有个提供方法的途径。 首先我们直接运行gpedit.msc 是找不到的&…

Win10彻底关闭Antimalware Service Executable解决cpu内存占用过高问题

1&#xff0c;win键R打开运行输入gpedit.msc&#xff0c;即可打开本地组策略编辑器 2.依次打开&#xff1a;管理模板----windows组件----windows Defender-----实时保护 3.然后鼠标双击右侧的“不论何时启用实时保护&#xff0c;都会启用进程扫描。勾选 已禁用&#xff0c;就可…

embedding介绍和常用三家模型对比

Embedding(嵌入)是一种在计算机科学中常用的技术,尤其是在自然语言处理(NLP)领域。在NLP中,embedding通常指的是将文本中的单词、短语或句子转换为固定维度的向量(vector)。这些向量代表了文本中的语义和上下文信息。 1.embedding 介绍 1.1 为什么需要Embedding? 在…

基于鸢尾花数据集的四种聚类算法(kmeans,层次聚类,DBSCAN,FCM)和学习向量量化对比

基于鸢尾花数据集的四种聚类算法&#xff08;kmeans&#xff0c;层次聚类&#xff0c;DBSCAN,FCM&#xff09;和学习向量量化对比 注&#xff1a;下面的代码可能需要做一点参数调整&#xff0c;才得到所有我的运行结果。 kmeans算法&#xff1a; import matplotlib.pyplot a…