算法思想总结:链表

一、链表的常见技巧总结

二、两数相加

. - 力扣(LeetCode)

class Solution {
public:
    ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
     //利用t来存进位信息
     int t=0;
     ListNode*newhead=new ListNode(0);//创建一个哨兵节点,方便尾插
     ListNode*ptail=newhead;//ptail方便尾插
     ListNode* cur1=l1,*cur2=l2;
     while(cur1||cur2||t==1)//t==1防止后面有进位没加上
     {
        if(cur1)  {t+=cur1->val; cur1=cur1->next;}
        if(cur2)  {t+=cur2->val;cur2=cur2->next;}
        ptail->next=new ListNode(t%10);
        ptail=ptail->next;
        t/=10;
     }
     ListNode*ret=newhead->next;
     delete newhead;
     return ret;
    }
};

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

 四、重排链表

. - 力扣(LeetCode)

class Solution {
public:
    void reorderList(ListNode* head) 
    {
      //方法1,利用一个数据结构将每个节点存起来,通过下标去访问
      //方法2, (1)利用快慢指针,找中点 (2) 拆开链表 从中点开始往后翻转 (3)进行合并成新链表
      if(head==nullptr||head->next==nullptr||head->next->next==nullptr) return;
      ListNode*mid=midnode(head);//找到中间节点
      //断开链表
      ListNode*l1=head;
      ListNode*l2=mid->next;
      mid->next=nullptr;
      //然后反转2
      l2=reverseList(l2);
      //合并链表
      mergeList(l1,l2);
    }

    ListNode*midnode(ListNode* head)
    {
       ListNode*fast=head;
       ListNode*slow=head;
       while(fast->next!=nullptr&&fast->next->next!=nullptr)//确保后面两步能走
       {
        fast=fast->next->next;
        slow=slow->next;
       }
       return slow;//此时慢指针指向的就是最小的节点
    }

     ListNode* reverseList(ListNode* head)
     {
        ListNode*p1=nullptr;
        ListNode*p2=head;
        ListNode*p3=head->next;//记录下一个要遍历的点
        while(p2)
        {
            p2->next=p1;
            p1=p2;
            p2=p3;
            if(p3) p3=p3->next ;
        }
        return p1;
     }

     void mergeList(ListNode* l1, ListNode* l2)
     {
        ListNode* temp1,*temp2;
        while(l1!=nullptr&&l2!=nullptr)
        {
            temp1=l1->next;
            temp2=l2->next;
            l1->next=l2;
            l1=temp1;//回到原链表0
            l2->next=l1;
            l2=temp2;//回到原链表
        }
     }
};

五、合并k个升序链表

. - 力扣(LeetCode)

 优先级队列:

class Solution {
public:
   //建小堆需要greater
   struct greater //构造一个仿函数
   {
      bool operator()(const ListNode*l1,const ListNode*l2)
      {
        return l1->val>l2->val;
      }
   };

    ListNode* mergeKLists(vector<ListNode*>& lists) 
    {
      //建立优先级队列(小堆),每次将堆顶元素插入进去,然后再删除堆顶元素,插入下个位置
       priority_queue<ListNode*,vector<ListNode*>,greater> heap;//建立一个小堆
       //入堆
       for(auto l:lists) if(l) heap.push(l);//因为有可能里面存的是一个空链表
       //开始合并k个有序链表
       ListNode*newnode=new ListNode(0);
       ListNode*ptail=newnode;//用于帮助我们进行尾插
       while(!heap.empty())
       {
        //进行尾插
        ListNode*it=heap.top();
         ptail->next=it;
         ptail=it;//去到下一个位置准备尾插
         //删除堆顶元素并将该节点的下一个节点入堆 ,为空就不入
        heap.pop();
        if(it->next) heap.push(it->next);
       }
       //此时全部的元素都插入完成了,返回最终的链表
       ListNode*ret=newnode->next;
       delete newnode;
       return ret;
       //时间复杂度o(n*k*logk)
    }
};

分治思想:

//策略,利用递归解决问题,结合归并排序,合并两个有序链表  (利用分治思想)
class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists)
    {
        int n=lists.size();
        return merge(lists,0,n-1);//让merge帮助我们完成整个区间的归并
    }
    
    ListNode* merge(vector<ListNode*>& lists,int left,int right)
    {
        //首先,处理边界情况,如果不存在链表或者是只有一个链表,此时没有必要进行下去
        if(left>right) return nullptr;
        if(left==right) return lists[left];
        //让merge帮助我们归并左右区间
        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*newhead=new ListNode(0);//哨兵节点
       ListNode*ptail=newhead;//帮助我们进行尾插
       ListNode*cur1=l1,*cur2=l2;//两个指针分别指向两个链表
       while(cur1&&cur2)//当两个都不为空的时候
       {
         if(cur1->val<cur2->val) 
         {
            //此时要尾插cur1
             ptail->next=cur1;
             ptail=cur1;//更新到下一个位置
             cur1=cur1->next;//继续去下一个节点遍历
         }
         else
         {
             ptail->next=cur2;
             ptail=cur2;//更新到下一个位置
             cur2=cur2->next;//继续去下一个节点遍历
         }
       }
       //可能有的链表没有遍历完
       if(cur1) ptail->next=cur1;
       if(cur2) ptail->next=cur2;
       //此时返回到目标的位置
        ListNode*ret=newhead->next;
        delete newhead;
        return ret;
    }
};

六、k个一组翻转链表

. - 力扣(LeetCode)

class Solution {
public:
    ListNode* reverseKGroup(ListNode* head, int k) 
    {
       int n=0;//记录总数
       ListNode*cur=head;
       while(cur)//统计节点个数,并推测有多少组
       {
        cur=cur->next;
        ++n;
       }
       n/=k;//看看一共需要几组
       ListNode*newhead=new ListNode(0);//创建一个哨兵节点
       ListNode*prev=newhead;//记住被头插的点
       cur=head;//从head开始进行头插
       //翻转n组,每组翻转k个
       for(int i=0;i<n;++i)
         {
            ListNode*temp=cur;
            for(int j=0;j<k;++j)
            {
                //用头插的逻辑
                ListNode*next=cur->next;;
                cur->next=prev->next;
                prev->next=cur;
                cur=next;//继续去链表的下一个点
            }
            prev=temp;//更新prev
         }
         //循环结束后,将后面的不需要逆序的部分接上
         prev->next=cur;
         ListNode*ret=newhead->next;
         delete newhead;
         return ret;
    }
};

七、旋转链表

. - 力扣(LeetCode)

思路1:截断再连接

class Solution {
public:
    ListNode* rotateRight(ListNode* head, int k) 
    {
       //让链表成环(闭合成环),然后在指定位置断开
       if(head==nullptr||head->next==nullptr||k==0) return head;
       int count=1;//数节点数量
       ListNode*ptail=head;
       while(ptail->next!=nullptr) //找到尾节点,并统计节点数
       {
        ptail=ptail->next;
        ++count;
       }
        int add=count-k%count;//看看具体是翻转几次
        if(add==count) return head;//避免不需要翻转的情况
       //截断重连
       ListNode*cur=head;
       while(--add) cur=cur->next; //找到被截断的位置
       ListNode*ret=cur->next;
       cur->next=nullptr;//断开
       cur=ret;
       while(cur->next!=nullptr) cur=cur->next;//找到尾节点
       cur->next=head;//连接
       return ret; 
    }
};

思路2:链表成环,指定位置截断

class Solution {
public:
    ListNode* rotateRight(ListNode* head, int k) 
    {
       //让链表成环,然后在指定位置断开
       if(head==nullptr||head->next==nullptr||k==0) return head;
       int count=1;//数节点数量
       ListNode*ptail=head;
       while(ptail->next!=nullptr) //找到尾节点,并统计节点数
       {
        ptail=ptail->next;
        ++count;
       }
        int add=count-k%count;//看看具体是翻转几次
        ptail->next=head;//头尾相连
        while(add--) ptail=ptail->next;
        ListNode*ret=ptail->next;
        ptail->next=nullptr;
        return ret; 

    }
};

思路3:逆置前n-k个,再逆置后k个,最后整体逆置

class Solution {
public:
    ListNode* rotateRight(ListNode* head, int k) 
    {
        if(head==nullptr||head->next==nullptr||k==0) return head;
       //先逆置前n-k个,再逆置后k个,再整体逆置
         int count=1;//数节点数量
         ListNode*ptail=head;
       while(ptail->next!=nullptr) //找到尾节点,并统计节点数
       {
        ptail=ptail->next;
        ++count;
       }
        int add=count-k%count;//看看具体是翻转几次
        if(add==count) return head;
        //开始找前n-k个节点
        ListNode*cur=head;
        while(--add) cur=cur->next;
         ListNode*l2=cur->next;//第二个链表
         cur->next=nullptr;//断开
          ListNode* l1=reverse(head);
          l2=reverse(l2);
          head->next=ptail;//连接起来
          return reverse(l1);//然后整体翻转
    }

    ListNode*reverse(ListNode* head)
    { //只有一个节点,没什么好逆置的
        if(head==nullptr||head->next==nullptr) return head;
        ListNode*p1=nullptr,*p2=head,*p3=head->next;
        while(p2)
        {
           p2->next=p1;
           p1=p2;
           p2=p3;
           if(p3) p3=p3->next;
        }
        return p1;
    }
};

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

网工基础协议——TCP/UDP协议

TCP和UDP的不同点&#xff1a; TCP(Transmission Control Protocol&#xff0c;传输控制协议)&#xff1b; UDP(User Data Protocol&#xff0c;用户数据报协议)&#xff1b; TCP&#xff1a;传输控制协议&#xff0c;面向连接可靠的协议&#xff0c;只能适用于单播通信&…

【教程】一个比较良心的C++代码混淆器

这是一个比较良心的C代码混淆器&#xff0c;用于信息竞赛训练和保护代码免受抄袭。本文将介绍这个混淆器的使用方法、混淆效果和已知的一些bug。同时&#xff0c;我们也会给出一些示例来演示混淆器的具体操作。 引言 在信息竞赛训练和实际开发中&#xff0c;保护代码的安全性和…

闲不住,手写一个数据库文档生成工具

shigen坚持更新文章的博客写手&#xff0c;擅长Java、python、vue、shell等编程语言和各种应用程序、脚本的开发。记录成长&#xff0c;分享认知&#xff0c;留住感动。 个人IP&#xff1a;shigen 逛博客的时候&#xff0c;发现了一个很有意思的文章&#xff1a;数据库表结构导…

JL-32 土壤速测仪 手持便携可移动 多要素参数可选配

产品概述 土壤速测仪是一款携带方便&#xff0c;操作简单&#xff0c;集采集与存储于一体的可移动式观测仪器。由手持式速测主机、土壤类传感器、USB数据线、电源适配器、便携式手提箱等部分组成。速测仪主机可通过集线器接入不同类型的传感器&#xff0c;互不影响精度&#x…

【二分查找】Leetcode 74. 搜索二维矩阵【中等】

搜索二维矩阵 给你一个满足下述两条属性的 m x n 整数矩阵&#xff1a; 每行中的整数从左到右按非严格递增顺序排列。每行的第一个整数大于前一行的最后一个整数。 给你一个整数 target &#xff0c;如果 target 在矩阵中&#xff0c;返回 true &#xff1b;否则&#xff0c…

记录Python链接mysql数据的增删改查方法

一、添加方法 db pymysql.connect(hostlocalhost,userroot,password123456,dbpython) cursor db.cursor() sql """insert into EMPLOYEEVALUES(3,张,天爱,35,F,8000) """ try:cursor.execute(sql)db.commit() #提交后&#xff0c;数据才会变 …

Springboot+Vue项目-基于Java+MySQL的网上超市系统(附源码+演示视频+LW)

大家好&#xff01;我是程序猿老A&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f49e;当前专栏&#xff1a;Java毕业设计 精彩专栏推荐&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb;&#x1f447;&#x1f3fb; &#x1f380; Python毕业设计 &…

Jackson 2.x 系列【28】Spring Boot 集成之 Long 精度损失

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Jackson 版本 2.17.0 本系列Spring Boot 版本 3.2.4 源码地址&#xff1a;https://gitee.com/pearl-organization/study-jaskson-demo 文章目录 1. 问题场景2. 原因分析3. 解决方案4. 案例演示4.…

Python 物联网入门指南(七)

原文&#xff1a;zh.annas-archive.org/md5/4fe4273add75ed738e70f3d05e428b06 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 第二十四章&#xff1a;基本开关 到目前为止一定是一段史诗般的旅程&#xff01;回想一下你开始阅读这本书的时候&#xff0c;你是否曾想象…

v-for中涉及的key

一、为什么要用key&#xff1f; key可以标识列表中每个元素的唯一性&#xff0c;方便Vue高效地更新虚拟DOM&#xff1b;key主要用于dom diff算法&#xff0c;diff算法是同级比较&#xff0c;比较当前标签上的key和标签名&#xff0c;如果都一样&#xff0c;就只移动元素&#…

(十二)C++自制植物大战僵尸游戏多用户存档实现(一)

植物大战僵尸游戏开发教程专栏地址http://t.csdnimg.cn/8UFMs 游戏存档 游戏存档允许玩家保存游戏进度&#xff0c;以便在之后的时间继续游戏。通过存档&#xff0c;玩家可以暂停游戏并在需要时重新开始&#xff0c;而不必从头开始或重新完成已经完成的任务。游戏通常提供多个…

VAR:自回归家族文生图新SOTA,ImageNet上超越Diffusion与DiTs

一、背景&#xff1a; 在人工智能领域&#xff0c;尤其是计算机视觉和自然语言处理中&#xff0c;自回归&#xff08;AR&#xff09;大型模型&#xff08;如GPT系列&#xff09;因其强大的生成能力和在多种任务上的通用性而受到广泛关注。这些模型通过自监督学习策略&#xff0…

PMP有用吗,PMP含金量,如何转型项目经理?

为什么要学习PMP知识&#xff0c;PMP培训哪家好&#xff1f; IT行业项目管理一枚&#xff0c;曾在做技术的时候对自己的职业发展越来越迷茫&#xff0c;不想干到35岁就参与到失业潮中&#xff0c;一直在想着办法提升自己的能力和竞争力&#xff0c;直到了解到了PMP认证。也就是…

二维码门楼牌管理应用平台建设:场所维护的新篇章

文章目录 前言一、二维码门楼牌管理应用平台的兴起二、民警与网格员的角色定位三、场所信息审核的重要性四、技术支持与创新应用五、未来展望与挑战 前言 随着信息技术的飞速发展&#xff0c;二维码门楼牌管理应用平台的建设正成为城市管理的新宠。该平台不仅提高了场所管理的…

HR招聘人才测评,如何考察候选人的内驱力?

HR的日常招聘工作中&#xff0c;如何去评估候选人的内驱力。人的内驱力&#xff0c;在职业生涯中&#xff0c;是极为重要的品质&#xff0c;也被列入综合素质测评。 内驱力&#xff0c;是指一个人出于内心深处的热情和追求&#xff0c;自发驱动自己持续学习、不断进步&#xf…

jenkins从节点配置说明

目的 打包构建时使用从节点&#xff0c;从节点所在服务器配置4C8G5000G&#xff08;服务器2&#xff09; 前提 首先在服务器1上部署jenkins服务&#xff0c;即主节点&#xff0c;默认节点名称为master 步骤 1&#xff09;登录进入jenkins平台&#xff0c;在系统设置中&…

项目风采展示【车酷-保时捷第二屏】

桌面功能介绍&#xff1a; 1&#xff1a;支持本地app桌面展示 2&#xff1a;支持本地音乐控制

LeetCode 每日一题 Day 123-136

1379. 找出克隆二叉树中的相同节点 给你两棵二叉树&#xff0c;原始树 original 和克隆树 cloned&#xff0c;以及一个位于原始树 original 中的目标节点 target。 其中&#xff0c;克隆树 cloned 是原始树 original 的一个 副本 。 请找出在树 cloned 中&#xff0c;与 tar…

自学Java的第二十四次笔记

一,方法重载 1.基本介绍 java 中允许同一个类中&#xff0c;多个同名方法的存在&#xff0c;但要求 形参列表不一致&#xff01; 比如&#xff1a; System.out.println(); out 是 PrintStream 类型 2.重载的好处 1) 减轻了起名的麻烦 2) 减轻了记名的麻烦 3.快速入门案…

git 小记

一、 github新建仓库 git clone 。。。。。。。。。。。 &#xff08;增删查补&#xff0c;修改&#xff09; git add . git commit -m "修改” git push (git push main) 二、branch 分支 branch并不难理解&#xff0c;你只要想像将代码拷贝到不同目录…