做几个链表相关的练习题吧!!

简介: 做几个链表相关的练习题吧!!

对于链表,笔者在之前就已经有过几篇文章,详细的讲解了!感兴趣的各位老铁,请进入博主的主页进行查找!

https://blog.csdn.net/weixin_64308540/?type=blog

言归正传!对于链表,光学不做,也是白搭!所以,今日来几道小小的练习题顺顺手吧!!

温馨小提示一下:该题为数据结构的题目!必须画图!请大家思考时候也在画图!!

206. 反转链表

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

示例 1:

输入:head = [1,2,3,4,5]

输出:[5,4,3,2,1]

示例 2:

输入:head = [1,2]

输出:[2,1]

示例 3:

输入:head = []

输出:[]

提示:

  • 链表中节点的数目范围是 [0, 5000]
  • -5000 <= Node.val <= 5000

进阶:链表可以选用迭代或递归方式完成反转。你能否用两种方法解决这道题?

其实刚看见这个题目的时候,笔者也是一脸懵逼!但是,后来想了想,用头插法是一个不错的选择,相信各位老铁也可以想出来更好的办法的!

下面请看笔者用头插法书写的简单代码:

public ListNode reverseList( ListNode head){
        if (head == null){
            return null;  //说明一个节点都没有
        }
        if (head.next == null){
            return null;//说明只有一个节点
        }
        ListNode cur = head.next;//先记录头节点后的第一个节点
        head.next =null;
        while (cur !=null){
            ListNode curNext =cur.next;  //一定要有
            //头插法,插入cur
            cur.next =head;
            head=cur;
            cur=curNext;
        }
        return head;
    }
876. 链表的中间结点

给定一个头结点为 head 的非空单链表,返回链表的中间结点。

如果有两个中间结点,则返回第二个中间结点。

示例 1:

输入:[1,2,3,4,5]

输出:此列表中的结点 3 (序列化形式:[3,4,5])

返回的结点值为 3 。 (测评系统对该结点序列化表述是 [3,4,5])。

注意,我们返回了一个 ListNode 类型的对象 ans,这样:

ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, 以及 ans.next.next.next = NULL.

示例 2:

输入:[1,2,3,4,5,6]

输出:此列表中的结点 4 (序列化形式:[4,5,6])

由于该列表有两个中间结点,值分别为 3 和 4,我们返回第二个结点。

提示:

  • 给定链表的结点数介于 1 和 100 之间。

显而易见,该题可以用快慢指针来进行!!需要保持的是:快到速度是慢的速度的2倍!!

思路:同时在head位置出发,fast的速度是slow的速度的2倍,路程相同,所以,在fast在终点的时候,slow在中间位置

分析:当节点为奇数的情况下:

fast与slow第一次往后走时:

再次往后走时候:

此时fast.next==null,再往后走的话就会进行空指针异常了!所以,这个算是一个判断的条件吧!

当节点为偶数的情况下:

fast与slow第一次往后走时:

再次往后走时候:

此时fast==null,再往后走的话就会进行空指针异常了!所以,这个算是第二个判断的条件吧!

因此,经过上述两个条件的分析,我们可以有:循环结束的情况:fast==null  ||fast.next==null这两个条件!

经过分析,我们可以得出,fast !=null && fast.next !=null,而且这两个条件的先后顺序不能互换!!原因在于:当fast为null的时候,进行fast.next != null的判断会造成空指针异常!

因此,我们有着一下的代码:

public ListNode middleNode(ListNode head){
        ListNode fast = head;
        ListNode slow = head;
        while (fast != null && fast.next != null){
            fast = fast.next.next;
            slow = slow.next;
        }
        return slow;//循环结束的时候,此时slow正好在中间节点!
    }

或许有着老铁会有小小的疑惑!当只有一个节点的时候,是如何判断的??或许大家忘了:当只有一个节点的时候,此时,fast.next == null,则不会进入循环!!

链表中倒数第k个结点

知识点链表双指针

描述

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

示例1

输入:

1,{1,2,3,4,5}

返回值:

{5}

若是我们要求:仅仅遍历一遍链表,就能得出出后的倒数第K个节点,不知道你会怎么思考??

思路:快慢指针!!

如下图所示:

此时fast为最后一个节点,slow为倒数第三个节点,slow距fast需要走2步!!因此,想要倒数第K个节点,我们可以:

  1. 先让fast走K-1步(或者fast先走K步,此时,判断条件就变为fast为null)
  2. fast走完K-1步之后,fast和slow开始一步一步往后走
  3. 当fast.next为null 的时候,slow所指的位置就是倒数第K个节点!

经过上述的分析,我们有着一下的代码:

//链表的倒数第k个节点
    public ListNode FindKthToTail(ListNode head,int k) {
        //判断是否合法
        if (k<=0 || head == null){
            return null;
        }
        ListNode fast =head;
        ListNode slow =head;
        //让fast先走k-1步
        while (k-1 !=0){
            fast =fast.next;
            if (fast == null){
                return null;
            }
            k--;
        }
        //fast和slow一块一步一步走
        while (fast.next != null){
            fast =fast.next;
            slow =slow.next;
        }
        return slow;
    }
21. 合并两个有序链表

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

示例 1:

输入:l1 = [1,2,4], l2 = [1,3,4]

输出:[1,1,2,3,4,4]

示例 2:

输入:l1 = [], l2 = []

输出:[]

示例 3:

输入:l1 = [], l2 = [0]

输出:[0]

提示:

  • 两个链表的节点数目范围是 [0, 50]
  • -100 <= Node.val <= 100
  • l1 和 l2 均按 非递减顺序 排列

这个思路理解起来有点儿难度!表达不怎么样!笔者仅仅提供自己的思路吧!

这个解题便是笔者的思路!尴尬

下面请看代码:

//合并两个有序链表
    public ListNode mergeTwoLists(ListNode head1 , ListNode head2){
        ListNode newhead =new ListNode();//实列化一个虚拟的节点
        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;
    }
OR36 链表的回文结构

知识点链表

描述

对于一个链表,请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法,判断其是否为回文结构。

给定一个链表的头指针A,请返回一个bool值,代表其是否为回文结构。保证链表长度小于等于900。

测试样例:

1->2->2->1

返回:true

思路:对于该题目,我们可以先找到中间位置,然后再将中间位置往后的进行反转一下!!

因此,我们有着下列的简单代码:

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

对于这个题目,想必最难的地方在于,找到中间节点之后的翻转!!这个感觉有点儿难度!其他的想必都是很简单的思路了吧!!

CM11 链表分割

描述

现有一链表的头指针 ListNode* pHead,给一定值x,编写一段代码将所有小于x的结点排在其余结点之前,且不能改变原来的数据顺序,返回重新排列后的链表的头指针。

在这个题目中,我们需要注意的是,x可能是链表里面的数据,也可能不是链表里面的数据!另外:原链表的最后一个节点所存储的数据跟x比的大小??(需不需要置null问题)

在下列的讲述中,假设x的值为15!

情况1:假设初始状态下的链表数据为:(链表的最后一个节点所存储的数据小于x的值)

经过链表分割之后:

情况2:假设初始状态下的链表数据为:(链表的最后一个节点所存储的数据大于x的值)

经过链表分割之后:

那么,问题来了,怎么将小于x的节点放在前面,并且还能保证顺序不变呢??

思路:假设有2段,第一段放<x的节点,第2段放>=x的节点,最后再将这2段串起来,不就可以了蛮??

因此,对于该用列:

我们有着:

因此,在这个思路中,我们只需要维护各个节点的next便可以了!!

在这个过程中,用到的还是原来的节点,没有实列化新的节点,所以,还是链表本身的修改!!不要忘记,将最后一个节点置为null!!

下面请看笔者的简单代码:

public ListNode partition(ListNode head,int x){
         ListNode bs=null;
         ListNode be=null;  //bs be用来记录<x的节点
         ListNode as=null;
         ListNode ae=null;  //as ae用来记录>=x的节点
         ListNode cur =head;//滴滴代跑
         while (cur !=null){//循环的总条件
             if(cur.val<x){
                 if (bs==null){  //判断是否为第一次插入
                     bs=cur;//确保bs在头节点保持不动
                     be=cur;
                 }else {
                     be.next=cur;  //尾插法插入节点
                     be=be.next;
                 }
             }else {
                 if (as==null){
                     as=cur;//确保as在头节点保持不动
                     ae=cur;
                 }else {
                     ae.next=cur;
                     ae=ae.next;
                 }
             }
             cur=cur.next;  //在上述的if……else语句中,都用到了该语句,所以可以提取出来!
         }
         //有可能不会同时存在即大于又小于x的数据!(即一边倒的情况)
         if (bs==null){
             return as;
         }
         //没有经过上述的if(be==null)语句,就说明,第一段不为空 
         be.next=as; //此时第一段不为空
         if (as!=null){
             ae.next=null; //如果第二段不为空,则将最后的一个节点置为空
         }
         return bs;
     }

经过该列题,我们可以看出来:与链表相关的练习题很难!很难,但是我们要想成功的做出来,一定要学会思考,学会画图解决!

160. 相交链表

给你两个单链表的头节点 headAheadB ,请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点,返回 null

图示两个链表在节点 c1 开始相交

题目数据 保证 整个链式结构中不存在环。

注意,函数返回结果后,链表必须 保持其原始结构

自定义评测:

评测系统 的输入如下(你设计的程序 不适用 此输入):

  • intersectVal - 相交的起始节点的值。如果不存在相交节点,这一值为 0
  • listA - 第一个链表
  • listB - 第二个链表
  • skipA - 在 listA 中(从头节点开始)跳到交叉节点的节点数
  • skipB - 在 listB 中(从头节点开始)跳到交叉节点的节点数

评测系统将根据这些输入创建链式数据结构,并将两个头节点 headAheadB 传递给你的程序。如果程序能够正确返回相交节点,那么你的解决方案将被 视作正确答案

示例 1:

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3

输出:Intersected at '8'

解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。

从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,6,1,8,4,5]。

在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。

— 请注意相交节点的值不为 1,因为在链表 A 和链表 B 之中值为 1 的节点 (A 中第二个节点和 B 中第三个节点) 是不同的节点。换句话说,它们在内存中指向两个不同的位置,而链表 A 和链表 B 中值为 8 的节点 (A 中第三个节点,B 中第四个节点) 在内存中指向相同的位置。

示例 2:

输入:intersectVal = 2, listA = [1,9,1,2,4], listB = [3,2,4], skipA = 3, skipB = 1

输出:Intersected at '2'

解释:相交节点的值为 2 (注意,如果两个链表相交则不能为 0)。

从各自的表头开始算起,链表 A 为 [1,9,1,2,4],链表 B 为 [3,2,4]。

在 A 中,相交节点前有 3 个节点;在 B 中,相交节点前有 1 个节点。

示例 3:

输入:intersectVal = 0, listA = [2,6,4], listB = [1,5], skipA = 3, skipB = 2

输出:null

解释:从各自的表头开始算起,链表 A 为 [2,6,4],链表 B 为 [1,5]。

由于这两个链表不相交,所以 intersectVal 必须为 0,而 skipA 和 skipB 可以是任意值。

这两个链表不相交,因此返回 null 。

提示:

  • listA 中节点数目为 m
  • listB 中节点数目为 n
  • 1 <= m, n <= 3 * 104
  • 1 <= Node.val <= 105
  • 0 <= skipA <= m
  • 0 <= skipB <= n
  • 如果 listAlistB 没有交点,intersectVal0
  • 如果 listAlistB 有交 点,intersectVal == listA[skipA] == listB[skipB]

进阶:你能否设计一个时间复杂度 O(m + n) 、仅用 O(1) 内存的解决方案?

解题思路:先求出两个节点的长度,在球场两个节点的长度的差值(长度的差值体现在—》在节点相交之前的部分)若差值为0,则两个节点一块走,相遇的时候为相交的节点,若差值不为0,则长的节点先走差值个长度的步数,然后在一块走,相遇的时候为相交的节点!!

那么问题来了:

  1. 你知道哪个链表长吗??
  2. 你怎么判断相遇??是用headA==headB吗??但是,需要注意的是:当headA==headB==null的情况!走完了都不一定相交!

经过上述的分析,我们有着以下的代码:

public ListNode getIntersectionNode(ListNode headA , ListNode headB){
         //分别求出两个链表的长度
         int lenA=0;
         int lenB=0;
         ListNode pl=headA;//假设pl对应的为长链表
         ListNode ps=headB;//假设ps对应的为短链表
         while (pl !=null){
             lenA++;
             pl=pl.next;
         }
         while (ps !=null){
             lenB++;
             ps=ps.next;
         }
         //求出长度以后,此时pl和ps此时都指向null,所以要指回来
         pl=headA;
         ps=headB;
         //通过len的正负,可以判断两个链表的长度
         int len=lenA-lenB;
         //根据len的正负,来修改指向
         if (len<0){
             pl=headB;//确保pl指向的为长链表
             ps=headA;//确保ps指向的为短链表
             len=lenB-lenA;
         }
         //此时,len一定是个正数!
         //pl指向的为长链表  并且  ps指向的为短链表
         //让长链表先走len步
         while (len !=0){
             pl=pl.next;
             len--;
         }//出while循环的时候,长链表与短链表距离相交的或者null有着相同的距离
         while (pl != ps){
             pl=pl.next;
             ps=ps.next;
         }//此时pl与ps要么相交,要么都为null
         return pl;
     }

思路很重要哟!!加油

相关文章
|
7月前
|
存储
链表练习题-1
链表练习题
|
7月前
|
算法 索引
链表经典练习题
链表经典练习题
|
7月前
|
安全
链表练习题-2
链表练习题
|
7月前
【链表之练习题】
【链表之练习题】
49 1
|
7月前
【无头双向链表和链表练习题2】
【无头双向链表和链表练习题2】
38 0
|
7月前
【移除链表元素】LeetCode第203题讲解
【移除链表元素】LeetCode第203题讲解
|
6月前
|
存储 SQL 算法
LeetCode力扣第114题:多种算法实现 将二叉树展开为链表
LeetCode力扣第114题:多种算法实现 将二叉树展开为链表
|
6月前
|
存储 SQL 算法
LeetCode 题目 86:分隔链表
LeetCode 题目 86:分隔链表
|
6月前
|
存储 算法 Java
【经典算法】Leetcode 141. 环形链表(Java/C/Python3实现含注释说明,Easy)
【经典算法】Leetcode 141. 环形链表(Java/C/Python3实现含注释说明,Easy)
66 2
|
7月前
<数据结构>五道LeetCode链表题分析.环形链表,反转链表,合并链表,找中间节点.
<数据结构>五道LeetCode链表题分析.环形链表,反转链表,合并链表,找中间节点
64 1