数据结构-链表的简单操作代码实现2【Java版】

news2024/10/7 8:19:43

目录

写在前:

此篇讲解关于单链表的一些面试题目,续上节。

11.反转一个单链表

12.给定一个带有头结点的head的非空单链表,返回链表的中间结点,如果有两个中间结点,则返回第二个中间结点

13.输入一个链表,输出该链表中倒数第k个结点

14.将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有结点组成的。

15.编写代码,以给定值x为基准将链表分为两部分,所有小于x的结点排在大于或等于x的结点之前

16.链表的回文结构

17.输入两个链表,找出他们的第一个公共结点

18.链表中是否有环

19.给定一个链表,返回链表开始入环的第一个结点,如果链表没有环,则返回null


11.反转一个单链表

给定一个链表:

进行反转后的链表:

这里我们采用的是“头插法”来解决问题。

首先需要进行判断

(1)当头结点不存在的时候返回null;【说明链表中没有元素】

(2)当头结点的next域为空时,返回头结点;【说明链表中只有一个结点】

当上面的两种情况都不是的时候

(1)定义一个cur结点指向head.next;

(2)将head.next的值置为null;

定义结点curNext指向cur.next;

进行“头插操作”:

第一步:

第二步:

第三步:

最后返回head。

    public ListNode reverseList(){
        if(head == null){
            return null;
        }
        //走到这里就说明链表里只有一个节点
        if(head.next == null){
            return head;
        }
        ListNode cur = head.next;
        head.next = null;
        while (cur != null){
            ListNode curNext = cur.next;
            //头插法插入cur(任何的插入都是先绑后面)
            cur.next = head;
            head = cur;
            cur = curNext;
        }
        return head;
    }

12.给定一个带有头结点的head的非空单链表,返回链表的中间结点,如果有两个中间结点,则返回第二个中间结点

将问题具象化:

如图,当我们有奇数个节点的时候,返回中间结点。

当我们有偶数个结点的时候返回第二个中间结点:

本题求解采用“快慢指针”

具体思路:

定义两个指针fast和slow,同时指向头结点的位置;每次让fast指针向前走两步,slow指针向前走一步。

当fast指针指向最后的位置的时候,此时fast.next域为空。这个时候slow指针指向的位置就是中间位置。

当链表中是偶数个结点的情况下同理:

以四个结点为例,同样的fast走两步,slow走一步;

当fast走到最后的时候此时fast为空。此时,slow所指向的位置就是中间结点中的第二个位置。

结合以上的分析,可以进行代码的编写:

定义两个结点指向头结点的位置。进入循环的条件为当fast指针域和fast.next域均不为空的时候,执行fast向后走两步,slow向后走一步的。

最后返回slow即为所求。

    public ListNode middleNode(){
        ListNode fast = head;
        ListNode slow = head;
        while (fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
        }
        return slow;
    }

13.输入一个链表,输出该链表中倒数第k个结点

本题仍然使用“快慢指针”的方式来进行编写代码。

这里需要明确的是:在一个链表中倒数第k个结点需要走k-1步到链表的最后一个结点。

定义两个指针fast和slow;先让fast走k-1步,slow和fast再同时向前走;当fast.next为空的时候,此时slow所指的位置就是要求的倒数第k个结点。

编写代码:

首先进行合法性的判断当求得位置小于0或者当头结点为null的时候返回null;

定义两个指针fast和slow;

先让快指针向前走k-1步:这里需要判断的是当fast为null的时候就证明k的值大了,找不到对应的结点,此时也是返回null;

fast指针和slow指针同时向前走,当fast.next为空的时候,证明此时fast指向的链表的末尾,此时slow指向的位置就是需要找到的倒数第k个结点。

    public ListNode findKthToTail(int k){
        //当k不合法的时候
        if(k <= 0 || head == null){
            return null;
        }

        //当k合法时
        ListNode fast = head;
        ListNode slow = head;
        //1.fast走k-1步
        while (k - 1 != 0){
            fast = fast.next;
            if(fast == null){
                return null;
            }
            k--;
        }
        while (fast.next != null){
            fast = fast.next;
            slow = slow.next;
        }
        return slow;
    }

14.将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有结点组成的。

    public static MySingleList.ListNode mergeTwoLists
            (MySingleList.ListNode head1,MySingleList.ListNode head2){
        MySingleList.ListNode newHead = new MySingleList.ListNode(0);
       MySingleList.ListNode tmp = newHead;
        while (head1 != null && head2 != null){
            if(head1.val < head2.val){
                tmp.next = head1;
                head1 = head1.next;
                tmp = tmp.next;
            }else {
                tmp.next = head2;
                head2 = head2.next;
                tmp = tmp.next;
            }

        }
        if(head1 != null){
            tmp.next = head1;
        }
        if(head2 != null){
            tmp.next = head2;
        }
        return newHead.next;
    }

15.编写代码,以给定值x为基准将链表分为两部分,所有小于x的结点排在大于或等于x的结点之前

现在我们有一个下图所示的链表:

根据所给题目的要求,我们最终实现的链表应该是如下的样子:

以上是一种情况:最后的末尾本身就是比28大的数据,所以这里不需要进行置空操作。

当末尾的元素需要变换的时候如图:(这里需要手动将最后一个元素的next域设置为null)

需要注意的点:

(1)是否需要进行对最后一个点进行处理

(2)怎么保证顺序不改变

定义一个cur,遍历链表。分成两段--如果小于28,就将数据放在第一段;如果大于28,就将数据放到第二段。

定义4个指针bs、be、as、ae.

定义cur遍历,当cur为空的时候表示链表遍历完成。

编写代码时需要注意:

当遍历完成后,需要考虑到:

有可能所给出的数据都大于给定的x值,或者都小于给定的x值。

如果第一段为空(bs == null)返回第二段as【这样同时解决了两段均为空的情况】

第一段不为空:be.next指向as;

最后将末尾置空【这里需要判空】

    public ListNode partition(int x){
        ListNode as = null;
        ListNode ae = null;
        ListNode bs = null;
        ListNode be = null;

        ListNode cur = head;
        while(cur != null){
            if(cur.val < x){
                if(bs == null){
                    bs = cur;
                    be = cur;
                }else{
                    be.next = cur;
                    be = be.next;

                }
            }else {
                if(as == null){
                    as = cur;
                    ae = cur;
                }else{
                    ae.next = cur;
                    ae = ae.next;
                }
            }
            cur = cur.next;
        }
        //有可能不会同时存在小于x 和大于等于x的数据
        if(bs == null){
            return as;
        }
        //第一段不为空
        be.next = as;
        //第2段不为空的问题
        if(as != null){
            ae.next = null;
        }
        return bs;
    }

16.链表的回文结构

链表的回文结构:

此题的解题思路是:首先找到链表的中间结点。

将后半部的链表进行反转。

定义两个指针,一个从头开走,一个从尾走。

走完后head在头部,slow走到了链表末尾。一个向前,一个向后

最后当两个指针如果相遇,则链表是回文结构的。

需要注意的是以上情况是链表中有奇数个结点的时候,

当存在偶数个结点的时候,需要在判断两个指针是否相等的时候进行进一步的进行判断。

    public boolean chkPalindrome(){
        if(head == null){
            return  false;
        }
        if(head.next == null){
            return true;
        }
        ListNode fast = head;
        ListNode slow = head;
        //1.找中间节点
        while (fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
        }
        //2.翻转
        ListNode cur = slow.next;//cur代表当前需要翻转的节点
        while(cur != null){
            ListNode curNext = cur.next;
            cur.next = slow;
            slow = cur;
            cur = curNext;
        }
        //3.一个从头往后,一个从后往前
        while (slow != head){
            if(head.val != slow.val){
                return false;
            }
            //偶数情况的判断
            if(head.next == slow){
                return true;
            }
            slow = slow.next;
            head = head.next;
        }
        return true;
    }

17.输入两个链表,找出他们的第一个公共结点

求两个链表的公共结点。

首先分别求出两个链表的长度。定义两个指针pl、ps分别指向两个链表的头结点headA和headB;

遍历完后将pl和ps指回两个链表的头部。

根据求得的len值,进行修改指针的指向:pl指向长的链表头结点;ps指向短的链表头结点;

让pl先走len步,然后二者同时向后走。

当相等的时候进行判空,为空返回null,不为空返回对应结点pl;

    public static MySingleList.ListNode getIntersectionNode
            (MySingleList.ListNode headA,MySingleList.ListNode headB){
        //1.分别求两个链表的长度
        int lenA = 0;
        int lenB = 0;
        MySingleList.ListNode pl = headA;
        MySingleList.ListNode ps = headB;

        while (pl != null){
            lenA++;
            pl = pl.next;
        }
        while(ps != null){
            lenB++;
            ps = ps.next;
        }
        //求完长度之后将pl和ps指回来
        pl = headA;
        ps = headB;
        int len = lenA - lenB;
        //根据len的值,修改指向
        if(len < 0){
            pl = headB;
            ps = headA;
            len = lenB - lenA;
        }
        //1.可以保证len一定是一个正数
        //2.pl指向的链表一定是最长的,ps指向的链表一定是最短的
        while (len != 0){
            pl = pl.next;
            len -- ;
        }
        while (pl != ps){
            pl = pl.next;
            ps = ps.next;
        }
        //pl == ps
        if(pl == ps && pl == null){
            return null;
        }
        return pl;
    }

18.链表中是否有环

判断环的问题其实本质上就是数学上的追击问题。

思路:快慢指针,慢指针一次走一步,快指针一次走两步。两个指针从链表起始位置开始走。如果链表有环则一定会在环中相遇,否则快指针率先到达链表的末尾。

拓展1:

为什么快指针每次走两步,慢指针每次走一步是可行的?

假设链表带环,两个指针最后都会进入环,快指针先进环,慢指针后进环。当慢指针刚进环时,可能就和快指针相遇了。最差情况下两个指针之间的距离刚好是环的长度,此时两个指针每移动一次,之间的距离就缩小了一步,不会出现每次刚好套圈的情况。因此,在慢指针走到一圈之前,快指针一定是可以追的上慢指针的。

拓展2:

快指针一次走3步,走4步,...n步行么?

假设:快指针每次走三步,慢指针每次走一步,此时快指针一定是先进环的,慢指针后来才进环,假设慢指针进环的时候,快指针的位置:

此时按照上述方法来环绕移动,每次快指针走3步,慢指针走1步,是永远都不会相遇的,快指针刚好将慢指针套圈了,因此是不行的。

只有快指针走2步,慢指针走一步才可以,因为换的最小长度是1,即使套圈了两个也在相同的位置。

编写代码:

定义两个结点:fast和slow,均指向头结点。

当fast不等于null并且fast.next不等于null时进入循环,fast指针向后走两步,slow指针向后走一步,当fast和slow的指针指向同一位置的时候就说明链表存在环,返回true;否则返回11false。

    public boolean hasCycle(){
        ListNode fast = head;
        ListNode slow = head;
        while (fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
            if(fast == slow){
                return true;
            }
        }
        return false;
    }

另外一种写法是在循环中判断fast和slow的指向的时候,break跳出循环。

这时在循环外面判断:如果是因为while循环跳出来的,就返回false;

如果是因为if条件跳出来的,返回true。

    public boolean hasCycle(){
        ListNode fast = head;
        ListNode slow = head;
        while (fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
            if(fast == slow){
               break;
            }
        }
        if(fast == null || fast.next == null){
            return false;
        }
        return true;
    }

19.给定一个链表,返回链表开始入环的第一个结点,如果链表没有环,则返回null

本题的思路:

涉及到一些数学的内容,下面进行分析

   public ListNode detectCycle(){
        ListNode fast = head;
        ListNode slow = head;
        while (fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
            if(fast == slow){
                break;
            }
        }
        if(fast == null || fast.next == null){
            return null;
        }
        //有环,此时fast和slow就相遇了
        slow = head;
        while(fast != slow){
            fast = fast.next;
            slow = slow.next;
        }
        return fast;
    }

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

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

相关文章

Nginx 使用笔记大全(唯一入口)

Linux服务器因为Nginx日志access.log文件过大项目无法访问 项目处于运行状态下无法访问&#xff0c;第一步查看磁盘状态 1、查看磁盘状态 df -h 2、查找100M以上的文件 find / -size 100M |xargs ls -lh 3、删除文件 rm -rf /usr/local/nginx/logs/access.log 4、配置nginx.…

Linux 性能调优之硬件资源监控

写在前面 考试整理相关笔记博文内容涉及 Linux 硬件资源监控常见的命名介绍&#xff0c;涉及硬件基本信息查看查看硬件错误信息查看虚拟环境和云环境资源理解不足小伙伴帮忙指正 对每个人而言&#xff0c;真正的职责只有一个&#xff1a;找到自我。然后在心中坚守其一生&#x…

centos7下载python3离线安装包

下载离线安装工具 yum install yum-utils -y下载python3离线安装包 yumdownloader --destdir/root/python3 --resolve python3将python3下面的离线安装文件拷贝到其他服务器上就可以安装 离线安装 先进入到离线程序rpm文件存储路径 # 离线安装 rpm -Uvh --force --nodep…

Cnyunwei

运维管理系统&#xff1a;监控系统 Cnyunwei Centos 6 封装 Cacti、Nagios、Centreon&#xff08;中英文自己切换&#xff09;、Check_MK、Nconf英文版本全部采用与国外官方同步的最新版本&#xff0c;会发布32位和64位两个版本。 安装很简单&#xff0c;直接回车即可全自动安…

AttributeError: module ‘matplotlib‘ has no attribute ‘get_data_path‘

【报错】使用 AutoDL 下 Notebook 调用 matplotlib 时遇到 AttributeError: module matplotlib has no attribute get_data_path 报错&#xff1a; --------------------------------------------------------------------------- AttributeError …

《深入浅出进阶篇》——空间换时间优化——P2671 求和

链接&#xff1a;https://www.luogu.com.cn/problem/P2671 上题干&#xff1a; 题目描述 一条狭长的纸带被均匀划分出了n个格子&#xff0c;格子编号从11到n。每个格子上都染了一种颜色colori​用[1,m]当中的一个整数表示&#xff09;&#xff0c;并且写了一个数字numberi​。…

asp.net core mvc之 布局

一、布局是什么&#xff1f; 布局是把每个页面的公共部分&#xff0c;提取成一个布局页面&#xff08;头、导航、页脚&#xff09;。 二、默认布局 _Layout.cshtml 默认的布局是在 /Views/Shared 目录的 _Layout.cshtml文件。通常Shared目录中的视图都是公共视图。该目录下的…

部分背包问题【贪心算法】

部分背包问题是一种经典的贪心问题&#xff0c;物品可以取一部分&#xff0c;也就是可以随意拆分的物品。 算法思路&#xff1a; 用列表保存每个物品的价值及总重量、平均价值&#xff08;性价比&#xff09;。输入数据同时计算每种物品的平均价值。使用自定义的compare函数以…

PADS快速调整器件的位号

选择元器件&#xff0c; ctrlA 全选器件&#xff0c;右击菜单选择特性 如下三个信息&#xff0c;确认 配置标签信息&#xff0c;如图界面信息&#xff0c;点击应用&#xff0c;器件全部归位

Slax Linux 强化了会话管理和引导参数选项

导读Slax Linux 的创始人和维护者Tomas Matejicek 宣布 了他的微型和便携 GNU/Linux 发行版的新版本&#xff0c;带来了各种增强和错误修复。 新的 Slax Linux 版本&#xff08;基于 Debian 的 12.1 版本和基于 Slackware 的 15.0.2 版本&#xff09;引入了在可写设备上运行发…

vue3 ref 与shallowRef reactive与shallowReactive

ref 给数据添加响应式&#xff0c;基本类型采用object.defineProperty进行数据劫持&#xff0c;对象类型是借助reactive 实现响应式&#xff0c;采用proxy 实现数据劫持&#xff0c;利用reflect进行源数据的操作 let country ref({count:20,names:[河南,山东,陕西],objs:{key…

AI大模型时代,开发工程师与项目管理者面对的机遇和挑战,文末送书3本

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;CSDN领军人物&#xff0c;全栈领域优质创作者✌&#xff0c;CSDN博客专家&#xff0c;阿里云社区专家博主&#xff0c;2023年6月CSDN上海赛道top4。 &#x1f3c6;数年电商行业从业经验&#xff0c;历任核心研发工程师…

centos7 在线安装python3

在线安装命令 yum install -y python3 输入命令之后等待安装完成 查看版本 查看版本3 输入命令 python3 看到版本号为3.6.8 查看版本2 输入命令 python2 看到版本号为2.7.5

网络运维Day13

文章目录 部署web服务器部署虚拟机web1安装依赖包解压NGINX压缩包初始化编译编译安装查看验证配置动静分离 部署虚拟机web2安装依赖包解压NGINX压缩包初始化编译编译安装查看验证配置动静分离 配置NGINX七层代理测试健康检查功能 配置NGINX四层代理部署代理服务器 总结 部署web…

vue中cli组件如何自定义定义

目录 创建自定义组件 注册并使用自定义组件 注册组件&#xff1a; 在需要使用该组件的页面或父组件中注册并引入自定义组件。 使用 Props 传递数据 总结步骤&#xff1a; 前言 在Vue CLI中使用自定义组件是构建交互式和模块化Web应用的重要一环。Vue CLI为开发者提供了使用…

RocketMQ底层通信机制

分布式系统各个角色间的通信效率很关键&#xff0c;通信效率的高低直接影响系统性能&#xff0c;基于Socket实现一个高效的TCP通信协议是很有挑战的&#xff0c;本节介绍RocketMQ是如何解决这个问题的。 1.Remoting模块 RocketMQ的通信相关代码在Remoting模块里&#xff0c;先…

Linkage Mapper 报错

1 . 错误提示&#xff1a;“No module named lm_config” 错误原因&#xff1a;**** 2.错误提示&#xff1a;“Cannot find an installation of Circuitscape in your Program Files directory.” 错误原因&#xff1a;***** 3. 错误提示&#xff1a;UnicodeEncodeError: ‘asc…

Windows 微PE WePE_64_V2.3 PE模式下启用账号和修改密码

启动PE后&#xff0c;进入桌面打开运行dism程序 选择带有系统的盘符&#xff08;默认选的是PE盘&#xff09;&#xff0c;然后打开会话 选择左侧工具箱&#xff0c;然后右侧找到账户管理 然后就可以对已有账号进行管理了 结束。

Java SE 封装、包、static关键字和代码块

1.封装 1.1封装的概念 面向对象程序三大特性&#xff1a;封装、继承、多态。而类和对象阶段&#xff0c;主要研究的就是封装特性。何为封装呢&#xff1f;简单来说 就是套壳屏蔽细节。 封装&#xff1a;将数据和操作数据的方法进行有机结合&#xff0c;隐藏对象的属性和实现细…

springboot-error

Invalid bound statement (not found): com.example.demo.mapper.UserMapper.findAll 一直报错&#xff0c;找不到相应的mapper文件。 排除以下原因之后&#xff0c;还是不对&#xff1a; https://blog.csdn.net/xxpxxpoo8/article/details/127548543 最后发现是因为我的mapp…