【Java 数据结构】单链表经典面试题 (动图解析)

news2024/10/6 16:25:26

🎉🎉🎉点进来你就是我的人了
博主主页:🙈🙈🙈戳一戳,欢迎大佬指点!

人生格言:当你的才华撑不起你的野心的时候,你就应该静下心来学习!

欢迎志同道合的朋友一起加油喔🦾🦾🦾
目标梦想:进大厂,立志成为一个牛掰的Java程序猿,虽然现在还是一个🐒嘿嘿
谢谢你这么帅气美丽还给我点赞!比个心


目录

 

1.反转链表

    1.1 题目展示及写题链接

    1.2 解决思路+过程展示

 1.3 实现代码

2.求单链表的中间结点

2.1 题目展示及写题链接

  2.2 解决思路+过程展示

    2.3 实现代码

 3.求单链表的倒数第k个结点

    3.1 题目展示及写题链接

  3.2 解决思路+过程展示

    3.3 实现代码

4.合并两个有序链表

    4.1 题目展示及写题链接

    4.2 解决思路+过程展示

    4.3 实现代码

5.回文串

    5.1 题目展示及写题链接

    5.2 解决思路+过程展示

    5.3 实现代码

6.链表分割

6.1 题目展示及写题链接

6.2 解决思路+过程展示

6.3 实现代码

7.相交链表

7.1 题目展示及写题链接

7.2 解决思路+过程展示

7.3 实现代码

8.环形链表

8.1 题目展示及写题链接

8.2 解决思路+过程展示

8.3 实现代码

9.环形链表||

9.1 题目展示及写题链接

9.2 解决思路+过程展示

9.3 实现代码



 

1.反转链表

    1.1 题目展示及写题链接

           ● 题目展示

  ● 写题链接:力扣   

    1.2 解决思路+过程展示

           ● 解决思路:

              1).将整个链表结点一一取出进行头插
              2).使用curNext结点保存cur的下一个结点,避免后续结点丢失
              3).当前的头结点的next要置空,否则会使单链表成环

           ● 过程展示(已排除特殊情况,特殊情况于代码中解释):           

 1.3 实现代码

​​​​class Solution {
    public ListNode reverseList(ListNode head) {
     //当链表为空直接返回null,如果链表只有一个结点,则直接返回该结点
     if(head == null || head.next == null) return head;
 
     //游标结点
     ListNode cur = head.next;
     head.next = null; //避免链表成环
     //进行头插
     while(cur != null) {
         ListNode curNext = cur.next; //保存游标结点的下一结点
         cur.next = head; //当前cur的next域指向头(头插)
         head = cur; //更新head
         cur = curNext; //游标更新到下一个结点
     }
     return head; //反转完毕,返回头结点
     }
}

在数据结构和算法中,游标节点通常是指一个临时变量,用于在遍历、查找、修改或删除数据结构(如链表、树等)中的元素时,作为当前处理或访问的节点。 

2.求单链表的中间结点

2.1 题目展示及写题链接

           ● 题目展示:

● 写题链接:力扣 

  2.2 解决思路+过程展示

           ● 解决思路:
              ★​​快慢指针(重点掌握,有大用处!):

              慢指针走一步,快指针走两步。这里分两种情况。

              情况一:当链表结点个数为偶数时,快指针为null结束遍历

              情况二:当链表结点个数为奇数时,快指针的next域为空结束遍历。

            ● 过程展示(以情况一做展示)

    2.3 实现代码

class Solution {
    public ListNode middleNode(ListNode head) {
        //如果链表为空或只有一个结点都返回它本身
        if(head == null || head.next == null) return head;
 
        //快慢指针
        ListNode slow = head; //慢指针
        ListNode fast = head;  //快指针
        while(fast != null && fast.next != null) {
            slow = slow.next; //慢指针走一步
            fast = fast.next.next; //快指针走两步
        }
        return slow;
    }
}

 3.求单链表的倒数第k个结点

    3.1 题目展示及写题链接

           ● 题目展示:

 ● 写题链接:链表中倒数第k个结点_牛客题霸_牛客网

  3.2 解决思路+过程展示

           ● 解决思路(这里不借用整型变量进行计数,即让游标结点走 :链表总长度 - k 步):

              ★快慢指针:

              1) 先让快指针走k - 1步。

              注:设慢指针为倒数第k个结点,那么它与快指针直接总是相差k - 1个结点。

              2) 然后再和慢指针一起走,直到快指针的next域为空,那么慢指针就是倒数第k个结点。

           ● 过程展示(假设k = 3):

    3.3 实现代码

public class Solution {
    public ListNode FindKthToTail(ListNode head,int k) {
        //只要链表为空或k小于等于0链表的倒数第k个结点都为null
        if(head == null || k <= 0) return null;
        
        //让fast走k-1步
        ListNode fast = head;
        ListNode slow = head;
        while(k - 1 != 0){
            fast = fast.next;
            k--;
        }
        
        //当k>链表结点总个数时,倒数第k个结点为空
        if(fast == null) return null;
 
        //和slow同时走,直到fast.next为空
        while(fast.next != null) {
            slow = slow.next;
            fast = fast.next;
        }
 
        return slow;//返回slow
    }
}

4.合并两个有序链表

    4.1 题目展示及写题链接

           ● 题目展示:

   ● 写题链接:力扣

    4.2 解决思路+过程展示

           ● 解决思路:

              1) 设置虚拟哨兵结点用于返回合并之后的链表,游标结点用于进行合并链表结点的新增

              2) 链表一和链表二进行一起遍历,它们的哪个结点值小,就往合并链表中插入,它们也不断后移,直到其中一个链表的结点遍历完毕。

              3) 两个链表的结点个数不一定相同,结点个数多的那一个链表需要继续添加到合并链表中

           ● 过程展示:

    4.3 实现代码

class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
     //三种特殊情况   
     if(list1 == null) return list2; //链表一为空,则合并之后的链表为链表二
     if(list2 == null) return list1; //链表二为空,则合并之后的链表为链表一
     if(list1 == null && list2 == null) return null; //两个链表都为空,则合并之后的链表也为空
 
     //虚拟哨兵结点
     ListNode head = new ListNode();
     ListNode cur = head; //游标结点
     while(list1 != null && list2 != null) {
        if(list1.val < list2.val) { //哪个链表的结点值小,则插入哪个结点
            cur.next = list1;
            list1 = list1.next;
            cur = cur.next;
        }else {    
            cur.next = list2;
            list2 = list2.next;
            cur = cur.next;
        }
     }
     //链表一剩下的结点
     if(list1 != null){
         cur.next = list1;
     }
 
     //链表二剩下的结点
     if(list2 != null){
         cur.next = list2;
     }
     return head.next; //返回哨兵结点的下一个
    }
}

5.回文串

    5.1 题目展示及写题链接

           ● 题目展示:

 写题链接:链表的回文结构_牛客题霸_牛客网

    5.2 解决思路+过程展示

           ● 解决思路:

              1)  先找到整个链表的中间结点。

              2)  将整个链表子区间(中间结点~最后一个结点)反转

              3)  从两边向中间进行判断,两端结点值是否相等,不相等则返回false,在整个遍历结束后,如果没有返回false,那么该链表是回文结构,返回true。

           ● 过程展示: 

    5.3 实现代码

public class PalindromeList {
    public boolean chkPalindrome(ListNode A) {
        //特殊情况:链表为空和链表只有一个结点的时候都返回true
        if(A == null || A.next == null) return true;
 
        //1.求链表的中间结点(快慢指针)
        ListNode slow = A;
        ListNode fast = A;
        while(fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
        }
 
        //2.反转链表子区间(slow~最后一个结点)
        ListNode cur = slow.next;
        while(cur != null){
            ListNode curNext = cur.next;
            cur.next = slow;
            slow = cur;
            cur = curNext;
        }
 
        //从两边向中间进行判断
        while(A != slow) {
            if(A.val != slow.val) return false;
            if(A.next == slow) return true; //结点个数为偶数时,避免空指针异常
            A = A.next;
            slow = slow.next;
        }
        return true;
    }
}

6.链表分割

6.1 题目展示及写题链接

        ● 题目展示

   ● 写题链接:链表分割

6.2 解决思路+过程展示

        ● 解决思路

           1)准备两个链表,一个链表存值小于x的结点(aHead首结点),另一个链表值存大于x的结点(bHead首结点)。

           注:为方便最后将两个链表链接所以要保存两个链表的首结点,而尾结点是为了便于尾插。

           2)对原始链表进行遍历,通过判断游标值的大小,分别对两个子链表进行尾插。

           3)遍历完毕后,分为三种情况返回:

                 ① 情况一:原始链表中所有结点都大于x → 返回bHead

                 ② 情况二:原始链表中所有结点都小于x → 返回aHead

                 ③ 情况三:原始链表中既有小于x的结点也有大于x的结点 → 与bHead链接后,返回aHead。

           注:当原始链表中最后一个结点值小于x时,那么bHead链表最后一个结点的next就会不为空,那么链接到aTail后,形成的链表就为环形链表,所以不管原始链表中最后一个结点值是否小于x,我们都要将bTail(最后一个结点).next域赋空。

        ● 过程展示

6.3 实现代码

public class Partition {
    public ListNode partition(ListNode pHead, int x) {
        // write code here
        //1.准备要分割的俩个区间的头和尾
        ListNode aHead = null,aTail = null; //小于x的结点子链表的头和尾
        ListNode bHead = null,bTail = null; //大于x的结点子链表的头和尾
 
        //2.遍历整个链表进行两个子链表的尾插(不会改变原来的数据顺序)
        ListNode cur = pHead; //游标结点
        while(cur != null) {
            if(cur.val < x) {
                if(aHead == null) { //子链表为空游标结点则直接等于其首结点
                    aHead = cur;
                    aTail = cur;
                }else {
                    aTail.next = cur; //尾插
                    aTail = aTail.next;
                }
            }else {
                if(bHead == null) { //子链表为空游标结点则直接等于其首结点
                    bHead = cur;
                    bTail = cur;
                }else {
                    bTail.next = cur; //尾插
                    bTail = bTail.next;
                }
            }
            cur = cur.next;
        }
 
        //3.如果两个子链表都不为空则将两个子链表进行链接
        if(aTail == null) return bHead; //特殊情况一:pHead链表中所有结点均大于x
 
        //链接(普通情况:pHead链表中有大于也有小于x的结点 
        //和特殊情况二:所有结点都小于x皆返回aHead)
        //注:如果pHead链表中最后一个结点如果小于x的话,一定要将bTail.next置空不然会成为环形链表
        if(bHead != null) bTail.next = null; 
        aTail.next = bHead; 
        return aHead;
    }
}

7.相交链表

7.1 题目展示及写题链接

        ● 题目展示 

  ● 写题链接:相交链表

7.2 解决思路+过程展示

        ● 解决思路

           1)因为两个原始链表不可改动,所以我们使用两个游标结点遍历它们,求出它们的长度。

           注:遍历完毕后一定要恢复两个游标结点,方便最后重新进行遍历。

           2)求出它们之间的长度差值,让链表长度较大的那个链表先走差值步。

           3)最后让两个游标结点同时走,直到它们相等,那么它们所在的结点就是两个链表的相交结点,返回它们中任意一个都可以。

        ● 过程展示

7.3 实现代码

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        //1.准备遍历headA和headB的游标结点
        ListNode curA = headA,curB = headB;
 
        //2.在相交之前,让两个链表的结点个数一样
        int lenA = 0,lenB = 0;
        while(curA != null) {
            lenA++;
            curA = curA.next;
        }
 
        while(curB != null) {
            lenB++;
            curB = curB.next;
        }
 
        //恢复两个游标结点
        curA = headA;
        curB = headB;
 
        int len = lenA - lenB;
        if(len < 0) {
            curA = headB;//让curA一直都是存链表长度较大的那个链表
            curB = headA;//让curB一直都是存链表长度较小的那个链表
            len = lenB - lenA; //len一直大于或等于0
        }
 
        //3.让链表长度较大的那个链表先走len步,让它和链表长度较小的链表同步起来
        while(len != 0) {
            curA = curA.next;
            len--;
        }
 
        //4.同时走直到到相交结点
        while(curA != curB) {
            curA = curA.next;
            curB = curB.next;
        }
        return curA;
    }
}

8.环形链表

8.1 题目展示及写题链接

        ● 题目展示

  ● 写题链接:环形链表

8.2 解决思路+过程展示

        ● 解决思路

           快慢指针:慢指针走一步,快指针走两步。

           如果快指针和慢指针最后相遇,则链表中存在环,反之,则不存在。

           注:快指针走的速度是慢指针走的速度的两倍,如果链表中有环的话,进入环之后,它们的距离是一定的(最大为环的长度),那么慢指针和快指针的距离会随着它们的不断移动,距离会慢慢缩小,直到它们两相遇,则能链表中有环。

        ● 过程展示

8.3 实现代码

public class Solution {
    public boolean hasCycle(ListNode head) {
       ListNode slow = head,fast = head;
 
       while(fast != null && fast.next != null) {
           slow = slow.next; //走一步
           fast = fast.next.next; //走两步
           if(slow.equals(fast)) return true; //是否相遇
       }
       return false; //没有相遇
    }
}

9.环形链表||

9.1 题目展示及写题链接

        ● 题目展示

  ● 写题链接:环形链表||

9.2 解决思路+过程展示

        ● 解决思路

           快慢指针:慢指针走一步,快指针走两步。

           如果快指针和慢指针最后相遇,则链表中存在环,不存在环直接返回null,将慢指针恢复到原始链表的头,fast在链表环中它们的相遇点,两个指针同时(慢指针和快指针都一步一步地走)行走,再次相遇点就是链表开始入环的第一个节点。

        ● 过程展示

这道题需要推导出一个公式:看完图解基本上就能懂了: 

9.3 实现代码

public class Solution {
    public ListNode detectCycle(ListNode head) {
        if(head == null) return null;
        //1.先判断链表是否有环(快慢指针)
        ListNode slow = head,fast = head;
        boolean flag = false;
        while(fast != null && fast.next != null) {
            slow = slow.next;
            fast = fast.next.next;
            if(slow.equals(fast)) {
                flag = true;
                break;
            }
        }
 
        if(flag) {
            //2.再同时走直到相遇
            slow = head;//恢复慢指针
            while(!slow.equals(fast)) { //同时走(都一步一步的走)
                slow = slow.next;
                fast = fast.next;
            }
            return slow;
        }else {
            return null;
        }
    }
}

不管是大环还是小环,其实我们只要推导出 X = Y 这个公式,让其中一个指针指向 head,另一个指针指向相遇点,两者以相同的速度走,大环的情况,两个指针走的路程分别是 X,Y, 最后在入环点相遇;小环的情况,两个指针走的路程分别为 X, (N-1)C, 也就是说,从相遇点开始走的那个指针在小环里面转了很多圈之后,最后走个 的距离就与另一指针相遇了。 

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

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

相关文章

Redis问题

一、认识Redis 1. 什么是 Redis&#xff1f; Redis 是一种基于内存的数据库&#xff0c;对数据的读写操作都是在内存中完成&#xff0c;因此读写速度非常快&#xff0c;常用于缓存&#xff0c;消息队列、分布式锁等场景。Redis 提供了多种数据类型来支持不同的业务场景&#…

vue-seamless-scroll无缝滚动组件使用方法详解+解决轮播空白缝隙问题(最后面)

下载安装 1.npm npm install vue-seamless-scroll --save 2.yarn yarn add vue-seamless-scroll 使用 1、全局注册 import Vue from vue import scroll from vue-seamless-scroll Vue.use(scroll) //或者 //Vue.use(scroll,{componentName: scroll-seamless}) 2、局部注册 im…

刘强东的“百亿补贴” 被指“雷”声大雨点小

京东集团2022年财报显示&#xff0c;2022年第四季度京东收入为2954亿元&#xff0c;同比增加7.1%&#xff0c;与2021年四季度23%的同比增速确有不小的差距。前三季度对应的同比增速分别为17.95%、5.44%和11.35%&#xff0c;与2021年相比均有回落。从财报中可以看出&#xff0c;…

什么是Selenium?使用Selenium进行自动化测试

什么是 Selenium&#xff1f; Selenium 是一种开源工具&#xff0c;用于在 Web 浏览器上执行自动化测试&#xff08;使用任何 Web 浏览器进行 Web 应用程序测试&#xff09;。   等等&#xff0c;先别激动&#xff0c;让我再次重申一下&#xff0c;Selenium 仅可以测试Web应用…

Unity VFX -- (2)玩一玩粒子系统

增加火花 复杂的VFX通常是由多个单独的粒子系统所组成。当它们组合到一起时&#xff0c;这些独立的个体会产生出更加有趣的效果。下面我们来为火焰增加火花效果。 1. 在Hierarchy中&#xff0c;展开Fire_ParticleSystem_Prefab物体&#xff0c;选择VFX_Sparks子物体。 2. 激活V…

java springboot工程引导类,简单认识程序入口

我们在创建springboot项目时 总会看到这么一个启动类 首先 我们要知道 我们的程序最后就成立一个spring容器 而你所有的类都是交给这个spring容器去管理的 做 springboot 程序 也会有这个spring容器 为了方便大家看 我们将启动类的代码改成这样 package com.example.thres…

HTTP和第三方模块

私人博客 许小墨のBlog —— 菜鸡博客直通车 系列文章完整版&#xff0c;配图更多&#xff0c;CSDN博文图片需要手动上传&#xff0c;因此文章配图较少&#xff0c;看不懂的可以去菜鸡博客参考一下配图&#xff01; 系列文章目录 前端系列文章——传送门 后端系列文章——传送…

zookeepr 简介

简介&#xff1a; zookeeper是为分布式应用提供协调服务的高性能组件。zookeeper通过简单的接口暴露了一些公共服务(命名、配置管理、同步和分组服务), 因此你不需要从头开始写这些服务。你可以现成得使用zookeeper来实现共识、组管理、领导者选举和存在协议。你可以根据自己的…

ConcurrentHashMap分段锁

1.分段锁的设计目的 ConcurrentHashMap 是支持高并发的线程安全的 HashMap。相较于 HashTable 使用 synchronized 方法来保证线程安全&#xff0c;ConcurrentHashMap 采用分段锁的方式&#xff0c;在线程竞争激烈的情况下 ConcurrentHashMap 的效率高很多。 ConcurrentHashMa…

考勤系统的最佳实践 - 静态活体检测 API 技术

引言 静态活体检测&#xff08;Static Liveness Detection&#xff09;API 是一种基于人脸识别技术&#xff0c;用于判断面部图像或视频是否为真实人脸的 API 接口。它基于图片中人像的破绽&#xff08;摩尔纹、成像畸形等&#xff09;&#xff0c;判断目标是否为活体&#xf…

abpvnext 创建数据时发布本地事件ILocalEventBus.PublishAsync ,创建的数据被阻塞的问题解决

一、问题背景描述&#xff1a; 我有一个需求&#xff0c;需要在字典服务里创建字典类型成功后执行ILocalEventBus.PublishAsync 发布一个事件&#xff0c;让主业务服务订阅这个事件&#xff0c;然后执行业务代码将字典类型同步给所有租户。 最开始我在字典服务员的applicatio…

数据结构实验 C语言 一元二项式操作

东莞理工学院请勿抄袭 1.实验目的 通过实验达到&#xff1a; ⑴ 理解和掌握线性结构的概念及其典型操作的算法思想&#xff1b; ⑵ 熟练掌握基本线性结构-线性表的顺序存储结构、链式存储结构及其操作的实现&#xff1b; ⑶ 理解和掌握受限线性结构——堆栈、队列、串、数…

HTTP 1 2 3 的演变过程

1 HTTP/1.1 相比 HTTP/1.0 提高了什么性能&#xff1f; HTTP/1.1 相比 HTTP/1.0 性能上的改进&#xff1a; 使用长连接的方式改善了 HTTP/1.0 短连接造成的性能开销。支持管道&#xff08;pipeline&#xff09;网络传输&#xff0c;只要第一个请求发出去了&#xff0c;不必等…

【ECharts+Vue】学习笔记(快速入门版)

一、ECharts 1.1 什么是Echarts ECharts 是一个使用 JavaScript 实现的开源可视化库&#xff0c;涵盖各行业图表&#xff0c;满足各种需求。提供了丰富的可视化图标&#xff0c;帮助你轻松实现大屏展示。 官网地址&#xff1a;Apache ECharts 1.2 ECharts的安装 直接下载 下载官…

分布式之搜索解决方案es

一 ES初识 1.1 概述 ElasticSearch&#xff1a;是基于 Lucene 的 Restful 的分布式实时全文搜索引擎&#xff0c;每个字段都被索引并可被搜索&#xff0c;可以快速存储、搜索、分析海量的数据。是ELK的一个组成,是一个产品&#xff0c;而且是非常完善的产品&#xff0c;ELK代表…

webRtc播放rtsp视频流(vue2、vue3+vite+ts)

一、下载webRtc 开发环境用的win10版本的。 github上直接下载&#xff0c;速度感人。 Releases mpromonet/webrtc-streamer GitHub 提供资源下载&#xff0c;0积分 https://download.csdn.net/download/weiqiang915/87700892 二、启动&#xff0c;测试 webrtc-streame…

PDD滑块分析

文章目录 1.流程分析2.关键点分析3.结果展示 声明&#xff1a;本文只作学习研究&#xff0c;禁止用于非法用途&#xff0c;否则后果自负&#xff0c;如有侵权&#xff0c;请告知删除&#xff0c;谢谢&#xff01; 欢迎大佬加群一起交流哇&#xff08; Q群&#xff1a;985475126…

基于web的电动车租赁管理系统C#+asp.net+sqlserver

具体功能如下&#xff1a;个人信息管理&#xff1a;实现登陆后对个人信息进行修改和查看的功能。 修改登录密码&#xff1a;实现登陆后对个人密码进行修改的功能。 申请租车订单&#xff1a;客户用户登陆后可以申请租车订单。同时可以查看租赁订单信息。 售后评价管理&#xff…

深度学习中的一阶段目标检测

博主简介 博主是一名大二学生&#xff0c;主攻人工智能研究。感谢让我们在CSDN相遇&#xff0c;博主致力于在这里分享关于人工智能&#xff0c;c&#xff0c;Python&#xff0c;爬虫等方面知识的分享。 如果有需要的小伙伴可以关注博主&#xff0c;博主会继续更新的&#xff0c…

飞行机器人专栏(十二)-- 提高机器人系统可靠性的关键要素与实践

本文将介绍如何在机器人系统的开发过程中融入关键要素&#xff0c;从而提高系统的可靠性。我们将从需求分析、设计阶段、开发与调试、验证与优化、迭代与升级等方面进行详细讨论&#xff0c;并提供示例代码以帮助您更好地理解相关概念。 目录 一、需求分析与规划 二、…