算法:常见的链表算法

这篇具有很好参考价值的文章主要介绍了算法:常见的链表算法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本篇总结常见的链表算法题和看他人题解所得到的一些收获

链表算法

关于链表的算法:

  1. 画图:画图可以解决绝大部分的数据结构的问题,任何的算法题借助图都可以有一个比较清晰的思路
  2. 引入哨兵位头节点:头节点可以避免处理很多边界情况,在解决一些问题前很方便
  3. 多定义几个变量:可以很方便的解决问题
  4. 快慢指针:在处理带环的问题,环的入口,倒数节点的时候很好用

两数相加

算法:常见的链表算法,C++,# 算法,算法,链表,数据结构

在这里积累这个题的目的是学习代码的写法,学习他人的代码

这是自己的代码:

class Solution 
{
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) 
    {
        ListNode* newhead = new ListNode();
        ListNode* cur1 = l1;
        ListNode* cur2 = l2;
        ListNode* cur = newhead;
        int carry = 0, sum = 0, ret = 0;

        while(cur1 && cur2)
        {
            // 计算出这个节点要存的值是多少
            sum = cur1->val + cur2->val + carry;
            carry = sum / 10;
            ret = sum % 10;
            
            // 插入节点
            ListNode* newnode = new ListNode(ret);
            cur->next = newnode;
            cur = newnode;

            // 迭代
            cur1 = cur1->next;
            cur2 = cur2->next;
        }

        while(cur1)
        {
            // 计算出这个节点要存的值是多少
            sum = cur1->val + carry;
            carry = sum / 10;
            ret = sum % 10;

            // 插入节点
            ListNode* newnode = new ListNode(ret);
            cur->next = newnode;
            cur = newnode;

            // 迭代
            cur1 = cur1->next;
        }

        while(cur2)
        {
            // 计算出这个节点要存的值是多少
            sum = cur2->val + carry;
            carry = sum / 10;
            ret = sum % 10;

            // 插入节点
            ListNode* newnode = new ListNode(ret);
            cur->next = newnode;
            cur = newnode;

            // 迭代
            cur2 = cur2->next;
        }

        if(carry)
        {
            // 插入节点
            ListNode* newnode = new ListNode(carry);
            cur->next = newnode;
            cur = newnode;
        }

        return newhead->next;
    }
};

运行可以通过,但是代码量比较冗余,其实可以发现while循环和后面的if语句有相当多的相似语句,因此看下面的代码:

class Solution 
{
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) 
    {
        ListNode* newhead = new ListNode();
        ListNode* cur1 = l1;
        ListNode* cur2 = l2;
        ListNode* cur = newhead;
        int sum = 0;

        while(cur1 || cur2 || sum)
        {
            // 先加第一个链表
            if(cur1)
            {
               sum += cur1->val;
               cur1 = cur1->next;
            }

            // 再加第二个链表
            if(cur2)
            {
                sum += cur2->val;
                cur2 = cur2->next;
            }

            // 处理节点数据
            cur->next = new ListNode(sum % 10);
            cur = cur->next;
            sum /= 10;
        }

        // 处理掉不必要的内存空间
        cur = newhead->next;
        delete newhead;
        return cur;
    }
};

两两交换链表中的节点

算法:常见的链表算法,C++,# 算法,算法,链表,数据结构
其实这个题已经见过很多次了,有很多种解决方法,直接模拟,递归…

但是这里积累它的意义在于,引入虚拟头节点对于解题是很有帮助的

/**
 * 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* swapPairs(ListNode* head) 
    {
        if(head == nullptr || head->next == nullptr)
            return head;
        
        ListNode* newhead = new ListNode();
        newhead->next = head;
        ListNode* prev = newhead;
        ListNode* cur = newhead->next;
        ListNode* next = cur->next;
        ListNode* nnext = next->next;
        while(cur && next)
        {
            // 交换节点
            prev->next = next;
            next->next = cur;
            cur->next = nnext;

            // 迭代
            prev = cur;
            cur = prev->next;
            if(cur) next = cur->next;
            if(next) nnext = next->next;
        }

        ListNode* res = newhead->next;
        delete newhead;
        return res;
    }
};

不带虚拟头节点:

/**
 * 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* swapPairs(ListNode* head) 
    {
        if(head == nullptr || head->next == nullptr)
            return head;
        // 先找到新的头节点
        ListNode* newhead = head->next;
        // 找到当前待交换的两个节点
        ListNode* left = head;
        ListNode* right = head->next;
        while(left && right)
        {
            // 交换两个节点
            left->next = right->next;
            right->next = left;
            // 进行迭代
            ListNode* prev = left;
            left = left->next;
            if(left)
            {
                right = left->next;
                if(right)
                    prev->next = right;
            }
        }

        return newhead;
    }
};

递归解题

/**
 * 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* swapPairs(ListNode* head) 
    {
        if(head == nullptr || head->next == nullptr)
            return head;
        ListNode* left = head;
        ListNode* right = left->next;
        ListNode* tmp = right->next;
        right->next = left;
        left->next = swapPairs(tmp);
        return right;
    }
};

重排链表

算法:常见的链表算法,C++,# 算法,算法,链表,数据结构
积累本题的意义在于,本题综合了逆序链表,快慢指针,链表插入的问题,是一个综合性的题

/**
 * 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:
    void reorderList(ListNode* head) 
    {
        // 利用快慢双指针找到中间节点
        ListNode* slow = head;
        ListNode* fast = head;
        while(fast && fast->next)
        {
            slow = slow->next;
            fast = fast->next->next;
        }

        // 得到两个新链表
        ListNode* newheadleft = head;
        ListNode* newheadright = Reverse(slow->next);
        slow->next = nullptr;

        // 把这两个链表再组装起来
        ListNode* newhead = new ListNode();
        ListNode* tail = newhead;
        ListNode* cur1 = newheadleft;
        ListNode* cur2 = newheadright;
        while(cur1 || cur2)
        {
            if(cur1)
            {
                tail->next = cur1;
                tail = tail->next;
                cur1 = cur1->next;
            }
            if(cur2)
            {
                tail->next = cur2;
                tail = tail->next;
                cur2 = cur2->next;
            }
        }

        ListNode* res = newhead->next;
        delete newhead;
        head = res;
    }

    ListNode* Reverse(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* res = newhead->next;
        delete newhead;
        return res;
    }
};

合并K个升序链表

算法:常见的链表算法,C++,# 算法,算法,链表,数据结构

关于这个题,乍一看其实是很难想到用什么方法做的,而实际上如果要是使用优先级队列或者是递归的思想来解题是可以解决的,算法的思路难,但是实际的变现并不难

这里提供三种解题方法:暴力求解、利用优先级队列来解题、利用归并的思想来解题

暴力求解

/**
 * 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* mergeKLists(vector<ListNode*>& lists) 
    {
        ListNode* newhead = new ListNode();
        ListNode* tail = newhead;
        for(int i = 0; i < lists.size(); i++)
        {
            merge(newhead->next, lists[i]);
        }
        return newhead->next;
    }

    void merge(ListNode*& head, ListNode* cur)
    {
        ListNode* newhead = new ListNode();
        ListNode* tail = newhead;
        while(head && cur)
        {
            if(head->val < cur->val)
            {
                tail->next = head;
                tail = tail->next;
                head = head->next;
            }
            else
            {
                tail->next = cur;
                tail = tail->next;
                cur = cur->next;
            }
        }
        if(head) tail->next = head;
        if(cur) tail->next = cur;

        head = newhead->next;
    }
};

利用优先级队列的思想解题

/**
 * 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:
    struct cmp
    {
        bool operator()(ListNode* l1, ListNode* l2)
        {
            return l1->val > l2->val;
        }
    };
    // 利用优先级队列进行优化
    ListNode* mergeKLists(vector<ListNode*>& lists) 
    {
        priority_queue<ListNode*, vector<ListNode*>, cmp> pq;
        
        // 把每一个链表都塞进去
        for(int i = 0; i <lists.size(); i++)
        {
            if(lists[i])
                pq.push(lists[i]);
        }
        
        // 创建虚拟头节点,开始链入到链表中
        ListNode* newhead = new ListNode();
        ListNode* tail = newhead;

        // 找到最小的节点,拿出来,再把它的下一个节点再塞进去,直到队列为空
        while(!pq.empty())
        {
            ListNode* top = pq.top();
            pq.pop();
            if(top != nullptr)
            {
                ListNode* next = top->next;
                top->next = nullptr;
                // 塞到目标链表中
                tail->next = top;
                tail = tail->next;
                // 再把剩下的部分再塞进去
                if(next)
                    pq.push(next);
            }
        }

        // 返回信息
        ListNode* res = newhead->next;
        delete newhead;
        return res;
    }
};

利用归并的思想解题

/**
 * 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* 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 midi = left + (right - left) / 2;
        // [left, midi][midi + 1, right]
        ListNode* l1 = merge(lists, left, midi);
        ListNode* l2 = merge(lists, midi + 1, right);

        // 排序
        return mergetwonode(l1, l2);
    }

    // 合并链表
    ListNode* mergetwonode(ListNode* l1, ListNode* l2)
    {
        ListNode* newhead = new ListNode();
        ListNode* cur1 = l1, *cur2 = l2, *tail = newhead;
        
        while(cur1 && cur2)
        {
            if(cur1->val <= cur2->val)
            {
                tail->next = cur1;
                cur1 = cur1->next;
                tail = tail->next;
            }
            else
            {
                tail->next = cur2;
                cur2 = cur2->next;
                tail = tail->next;
            }
        }

        if(cur1) tail->next = cur1;
        if(cur2) tail->next = cur2;

        ListNode* res = newhead->next;
        delete newhead;
        return res;
    }
};

K个一组翻转链表

算法:常见的链表算法,C++,# 算法,算法,链表,数据结构

/**
 * 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 
{
    // 思路:把链表k个一组拿出来,再翻转,再链入一个新的链表中
public:
    ListNode* reverseKGroup(ListNode* head, int k) 
    {
        ListNode* newhead = new ListNode();
        ListNode* tail = newhead;
        ListNode* left = head, *right = left;

        while(left && right)
        {
            // 把链表k个一组分出来
            for(int i = 1; i < k; i++)
            {
                // 如果此时已经到最后了,那么这最后一组就不需要进行翻转
                if(right == nullptr)
                {
                    tail->next = left;
                    return newhead->next;
                }
                right = right->next;
            }
            // 断链
            if(right)
            {
                ListNode* newleft = right->next;
                right->next = nullptr;
                // 现在从[left, right]就是一段完整的不带头节点的链表了
                // 把这段链表进行翻转,并链入到新的链表中
                tail->next = reverse(left);
                tail = left;
                // 迭代,找下一组k个节点
                left = newleft, right = left;
            }
        }
        tail->next = left;
        return newhead->next;
    }

    // 进行链表的反转
    ListNode* reverse(ListNode* cur)
    {
        ListNode* newhead = new ListNode();
        
        // 把链表中的节点头插到新链表中
        while(cur)
        {
            ListNode* next = cur->next;
            cur->next = newhead->next;
            newhead->next = cur;
            cur = next;
        }

        ListNode* res = newhead->next;
        delete newhead;
        return res;
    }
};

这个题的难点在于细节问题的处理,其实思路并不难

总结

其实对于链表这一块的算法主要是体现在需要处理一些边界情况和循环调用带来的问题,通过借助虚拟头节点和多创建几个指针可以缓解这种情况,主要是细节要处理好,对于思维的需求没有那么高文章来源地址https://www.toymoban.com/news/detail-835452.html

到了这里,关于算法:常见的链表算法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 数据结构:队列的链表结构(含完整代码,可复制)

    1.输出队列 2.入队一个元素 3.出队一个元素 5.建立链表队列 6.完整代码

    2024年01月16日
    浏览(36)
  • 【数据结构】[LeetCode138. 复制带随机指针的链表]

    给你一个长度为  n  的链表,每个节点包含一个额外增加的随机指针  random  ,该指针可以指向链表中的任何节点或空节点。 构造这个链表的  深拷贝 。 深拷贝应该正好由  n  个  全新  节点组成,其中每个新节点的值都设为其对应的原节点的值。新节点的  next  指针和

    2024年02月04日
    浏览(31)
  • 【数据结构OJ题】复制带随机指针的链表

    原题链接:https://leetcode.cn/problems/copy-list-with-random-pointer/description/ 目录 1. 题目描述 2. 思路分析 3. 代码实现 此题可以分三步进行: 1. 拷贝链表的每一个结点,拷贝的结点先链接到被拷贝结点的后面。 2. 复制随机指针的链接:拷贝结点的随机指针指向被拷贝结点随机指针的下

    2024年02月12日
    浏览(30)
  • 利用C++超详细解释数据结构中的链表

    链表(Linked List)是一种常见的数据结构,它可以动态地插入和删除元素,不需要像数组那样预先分配固定大小的内存。链表中的每个元素称为节点(Node),每个节点包含一个数据值和一个指向下一个节点的指针。本教学将涵盖以下知识点: 单向链表(Singly Linked List) 双向

    2024年02月04日
    浏览(23)
  • 【LeetCode】数据结构题解(9)[复制带随机指针的链表]

    所属专栏:玩转数据结构题型❤️ 🚀 博主首页:初阳785❤️ 🚀 代码托管:chuyang785❤️ 🚀 感谢大家的支持,您的点赞和关注是对我最大的支持!!!❤️ 🚀 博主也会更加的努力,创作出更优质的博文!!❤️ 🚀 关注我,关注我,关注我,重要的事情说三遍!!!!!

    2024年02月11日
    浏览(39)
  • 【数据结构】LeetCode升级版的环形链表,复制带随机指针的链表

              1、题目说明           2、题目解析           1、题目说明           2、题目解析      1、题目说明 题目链接: 升级版的环形链表  给定一个链表的头节点 head ,返回链表开始入环的第一个节点。  如果链表无环,则返回NULL。 如果链表中有某个节点,可以通

    2024年01月16日
    浏览(42)
  • 【数据结构初阶】四、线性表里的链表(带头+双向+循环 链表 -- C语言实现)

    ========================================================================= 相关代码gitee自取 : C语言学习日记: 加油努力 (gitee.com)  ========================================================================= 接上期 : 【数据结构初阶】三、 线性表里的链表(无头+单向+非循环链表 -- C语言实现)-CSDN博客  ====

    2024年02月08日
    浏览(35)
  • 贪吃蛇项目(基于C语言和数据结构中的链表)

    首先先建立3个文件。 Snake.h 函数的声明 Snake.c 函数的定义 Test.c     贪吃蛇的测试    我们分析这整个项目 首先在我们实现游戏开始的部分之前,我们要先创建贪吃蛇的节点,再由此创建整个贪吃蛇所包含的一些信息: 我们枚举一下这个贪吃蛇中所有的一些状态: 然后我们

    2024年02月20日
    浏览(47)
  • 【数据结构初阶】三、 线性表里的链表(无头+单向+非循环链表 -- C语言实现)

    ========================================================================= 相关代码gitee自取 : C语言学习日记: 加油努力 (gitee.com)  ========================================================================= 接上期 : 【数据结构初阶】二、 线性表里的顺序表(C语言实现顺序表)-CSDN博客  =========================

    2024年02月08日
    浏览(44)
  • 算法:常见的链表算法

    本篇总结常见的链表算法题和看他人题解所得到的一些收获 关于链表的算法: 画图:画图可以解决绝大部分的数据结构的问题,任何的算法题借助图都可以有一个比较清晰的思路 引入哨兵位头节点:头节点可以避免处理很多边界情况,在解决一些问题前很方便 多定义几个变

    2024年02月22日
    浏览(30)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包