C++算法:链表

链表

1.两数相加

题目较为简单,模拟加法计算即可。要注意的是,当cur1,cur2都走到空时且t为0时,加法计算才结束。

cpp 复制代码
/**
 * 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* cur1=l1,*cur2=l2;
        ListNode* head=nullptr;
        ListNode* tail=nullptr;
        int t=0;

        while(cur1||cur2||t)
        {
            if(cur1)
            {
                t+=cur1->val;
                cur1=cur1->next;
            }
            if(cur2)
            {
                t+=cur2->val;
                cur2=cur2->next;
            }
            
            if(head==nullptr)
                head=tail=new ListNode(t%10);
            else
            {
                tail->next=new ListNode(t%10);
                tail=tail->next;
            }
            t/=10;
        }   
        return head;
    }
};

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

对于无头节点的单链表,添加一个哨兵卫头节点能更方便地解题。同时,可以多定义变量来记录节点,这样在链接节点时不易被绕晕。newHead为哨兵卫,cur为当前节点,prev为cur的前驱节点,Next为cur的下一个节点,nNext为Next的下一个节点,

第一次交换后如下所示。

如果是奇数个节点,那么交换结束后Next为空。

如果是偶数个节点,则交换后cur为空。

cpp 复制代码
class Solution2 {
public:
    ListNode* swapPairs(ListNode* head)
    {
        if (head == nullptr || head->next == nullptr)
            return head;

        ListNode* newHead = new ListNode(-1);
        newHead->next = head;
        ListNode* prev = newHead, * cur = prev->next, * Next = cur->next, * nNext = Next->next;

        while (cur && Next)
        {
            prev->next = Next;
            cur->next = nNext;
            Next->next = cur;

            prev = cur;
            cur = nNext;
            if (cur)
                Next = cur->next;
            if (Next)
                nNext = Next->next;
        }
        cur = newHead->next;
        delete newHead;
        return cur;
    }
};

3.重排链表

这道题考察了三个知识点,分别是找到链表中间节点、链表逆置、合并链表。解决这道题的思路是把链表从中间断开,再把后半部分链表逆置,最后按一定规则合并这两个链表。每一步的实现方法多样,如链表逆置可以用头插法或三指针法,合并链表时可以创建哨兵卫,把两个链表的节点链接到新链表中,也可以不创建哨兵卫交替链接到原链表。

cpp 复制代码
class Solution {
public:
    ListNode* GetMid(ListNode* head)//获取中间节点
    {
        ListNode* slow = head;
        ListNode* fast = head;
        while (fast && fast->next)
        {
            slow = slow->next;
            fast = fast->next->next;
        }
        return slow;
    }
    void reverseList(ListNode*& head)//链表逆置(三指针)
    {
        if (head == nullptr)
            return;
        ListNode* prev = nullptr, * cur = head, * Next = cur->next;
        while (cur)
        {
            cur->next = prev;
            prev = cur;
            cur = Next;
            if (Next)
                Next = Next->next;
        }
        head = prev;
    }
    void reorderList(ListNode* head)//重排链表
    {
        if (head == nullptr || head->next == nullptr || head->next->next == nullptr)
            return;

        ListNode* mid = GetMid(head);
        ListNode* Head = mid->next;
        mid->next = nullptr;

        reverseList(Head);
        ListNode* cur1 = head, * cur2 = Head;
        ListNode* n1 = cur1->next, * n2 = cur2->next;
        while (cur1 && cur2)
        {
            cur1->next = cur2;
            cur2->next = n1;
            cur1 = n1;
            cur2 = n2;
            if (n1)
                n1 = n1->next;
            if (n2)
                n2 = n2->next;
        }
    }
};

下面代码则采用头插法逆置链表,创建哨兵卫合并链表。

cpp 复制代码
class Solution4 {
public:
    void reorderList(ListNode* head)
    {
        if (head == nullptr || head->next == nullptr || head->next->next == nullptr)
            return;
		
        //找中间节点
        ListNode* slow = head, * fast = head;
        while (fast && fast->next)
        {
            slow = slow->next;
            fast = fast->next->next;
        }
		
        //头插法逆置
        ListNode* Head = new ListNode(-1);
        ListNode* cur = slow->next;
        slow->next = nullptr;
        while (cur)
        {
            ListNode* Next = cur->next;
            cur->next = Head->next;
            Head->next = cur;
            cur = Next;
        }

        //合并链表
        ListNode* ret = new ListNode(-1);
        ListNode* cur1 = head, * cur2 = Head->next, * prev = ret;
        while (cur1)
        {
            prev->next = cur1;
            cur1 = cur1->next;
            prev = prev->next;

            if (cur2)
            {
                prev->next = cur2;
                cur2 = cur2->next;
                prev = prev->next;
            }
        }
        delete Head;
        delete ret;
    }
};

4.合并k个升序链表

暴力解法,第一个链表与第二个链表合并,然后再和第三个链表合并,以此类推,这样的时间复杂度为O(n*k2)。

可以用优先级队列优化,建立小堆,先把链表第一个节点放入堆中,每次取堆顶元素,将堆顶元素重新链接成一个新链表。时间复杂度O(n * k * log k)。

cpp 复制代码
struct Greater
{
    bool operator()(ListNode* l1, ListNode* l2)
    {
        return l1->val > l2->val;
    }
};
class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists)
    {
        priority_queue<ListNode*, vector<ListNode*>, Greater> pq;//建小堆

        for (auto& e : lists)//入堆
            if (e)
                pq.push(e);

        ListNode* newHead = new ListNode(-1);
        ListNode* tail = newHead;
        while (!pq.empty())
        {
            ListNode* cur = pq.top();
            pq.pop();
            tail->next = cur;
            tail = cur;
            if (cur->next)
                pq.push(cur->next);
        }
        tail = newHead->next;
        delete newHead;
        return tail;
    }
};

也可以用分治的思想,把数组lists不断分成两部分,最后合并两个有序链表并回溯

cpp 复制代码
class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists)
    {
        return Merge(lists, 0, lists.size() - 1);
    }
    ListNode* Merge(vector<ListNode*>& lists, int left, int right)
    {
        if (left > right) //没有元素返回空
            return nullptr;
        if (left == right) //只有一个元素,直接返回
            return lists[left];

        int mid = (left + right) >> 1;
        ListNode* l1 = Merge(lists, left, mid);
        ListNode* l2 = Merge(lists, mid + 1, right);

        return MergeTwoList(l1, l2);
    }
    ListNode* MergeTwoList(ListNode* l1, ListNode* l2)//合并两个有序链表
    {
        if (l1 == nullptr)
            return l2;
        if (l2 == nullptr)
            return l1;

        ListNode head;
        ListNode* cur1 = l1, * cur2 = l2, * prev = &head;
        while (cur1 && cur2)
        {
            if (cur1->val <= cur2->val)
            {
                prev->next = cur1;
                prev = cur1;
                cur1 = cur1->next;
            }
            else
            {
                prev->next = cur2;
                prev = cur2;
                cur2 = cur2->next;
            }
        }
        if (cur1)
            prev->next = cur1;
        if (cur2)
            prev->next = cur2;
        return head.next;
    }
};

5.K个一组翻转链表

先根据链表长度求出需要逆置n组,执行n次循环。使用头插法逆置,用指针 t 记录每组逆置前的第一个节点,下一组逆置时应在 t 后头插。

cpp 复制代码
class Solution {
public:
    ListNode* reverseKGroup(ListNode* head, int k)
    {
        if (head == nullptr || head->next == nullptr || k == 1)
            return head;

        int len = 0;
        ListNode* cur = head;
        while (cur)
        {
            cur = cur->next;
            len++;
        }

        int n = len / k;
        cur = head;
        ListNode* newHead = new ListNode(-1), * prev = newHead, * Next = cur->next;
        while (n--)
        {
            ListNode* t = cur;
            int i = k;
            while (i--)
            {
                cur->next = prev->next;
                prev->next = cur;
                cur = Next;
                if (Next)
                    Next = Next->next;
            }
            prev = t;
        }
        prev->next = cur;
        prev = newHead->next;
        delete newHead;
        return prev;
    }
};
相关推荐
superkcl20221 小时前
【QT Thread】
c++·qt
凌波粒1 小时前
LeetCode--90.子集II(回溯算法)
数据结构·算法·leetcode
旖-旎1 小时前
《LeetCode 417 太平洋大西洋水流问题 FloodFill DFS 解法》
c++·算法·深度优先·力扣·floodfill
凌波粒2 小时前
LeetCode--46.全排列(回溯算法)
数据结构·算法·leetcode
鱼子星_2 小时前
C++从零开始系列篇(二):C++入门——函数重载,引用,inline与nullptr
开发语言·c++·笔记
2zcode2 小时前
项目文档:基于MATLAB语音信号变声算法设计与实现
算法·matlab·语音识别
指令集梦境2 小时前
图解:单调栈算法模板(Java语言)
java·开发语言·算法
小灰灰搞电子2 小时前
C++ boost::circular_buffer 详解:原理、用法与实战
开发语言·c++·boost
生成论实验室2 小时前
自动驾驶:一个自主运动的系统
人工智能·算法·机器学习·语言模型·机器人·自动驾驶·安全架构