求链表中的中点、上中点、下中点

求链表中的中点、上中点、下中点

提示:


题目

一共四个题目:
(1)当链表个数N为奇数时,求链表中的中点、当链表个数N为偶数时,求链表中的中点;
(2)当链表个数N为奇数时,求链表中的中点、当链表个数N为偶数时,求链表中的中点;
(3)当链表个数N为奇数时,求链表中的中点的前一个点、当链表个数N为偶数时,求链表中的中点的前一个点;
(4)当链表个数N为奇数时,求链表中的中点的前一个点、当链表个数N为偶数时,求链表中的中点的前一个点;


一、审题

(1)当链表个数N为奇数时,求链表中的中点、当链表个数N为偶数时,求链表中的中点;
在这里插入图片描述
在这里插入图片描述

(2)当链表个数N为奇数时,求链表中的中点、当链表个数N为偶数时,求链表中的中点;
在这里插入图片描述

(3)当链表个数N为奇数时,求链表中的中点的前一个点、当链表个数N为偶数时,求链表中的中点的前一个点;
在这里插入图片描述
在这里插入图片描述

(4)当链表个数N为奇数时,求链表中的中点的前一个点、当链表个数N为偶数时,求链表中的中点的前一个点;
在这里插入图片描述
下中点的前一个点,就是上中点


二、笔试代码求AC:不管空间

明白了案例的意思,四个题,就要有各自的解题方案

咱们有一个解题的原则:
笔试求AC,通过测试案例,就能进面试!故不在乎空间复杂度,只考虑时间复杂度。
而面试力求最优解;故优先考虑时间复杂度,然后也要考虑空间复杂度。唯有更好的最优解,方得面试官的青睐!!

如果笔试场上遇到本题:
那就用最傻的办法
将链表转化为数组,然后求中点,上中点,下中点,返回结果

这四道题,全部代码很相似,只是求中点,上中点,下中点的公式有区别:
(1)当链表个数N为奇数时,求链表中的中点、当链表个数N为偶数时,求链表中的中点;
如果数组长度为N,则中点的位置就是:(N-1)/2
在这里插入图片描述
在这里插入图片描述

(2)当链表个数N为奇数时,求链表中的中点、当链表个数N为偶数时,求链表中的中点;
如果数组长度为N,则中点的位置就是:(N)/2
在这里插入图片描述
在这里插入图片描述

(3)当链表个数N为奇数时,求链表中的中点的前一个点、当链表个数N为偶数时,求链表中的中点的前一个点;
如果数组长度为N,则中点的位置就是:(N-3)/2
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

(4)当链表个数N为奇数时,求链表中的中点的前一个点、当链表个数N为偶数时,求链表中的中点的前一个点;
如果数组长度为N,则中点的位置就是:(N-2)/2

在这里插入图片描述

ok:总结一波:笔试求AC,不用节约空间,直接将链表转化为数组即可,根据数组长度N,索引中点即可:
笔试就简单画个图,看一下公式就行,非常容易
(1)当链表个数N为奇数时,求链表中的中点、当链表个数N为偶数时,求链表中的中点;
如果数组长度为N,则中点的位置就是:(N-1)/2
(2)当链表个数N为奇数时,求链表中的中点、当链表个数N为偶数时,求链表中的中点;
如果数组长度为N,则中点的位置就是:(N)/2
(3)当链表个数N为奇数时,求链表中的中点的前一个点、当链表个数N为偶数时,求链表中的中点的前一个点;
如果数组长度为N,则中点的位置就是:(N-3)/2
(4)当链表个数N为奇数时,求链表中的中点的前一个点、当链表个数N为偶数时,求链表中的中点的前一个点;
如果数组长度为N,则中点的位置就是:(N-2)/2

手撕代码:
本题要用的节点,通用的链表节点:

//基础数据结构Node
    public static class Node{
        public Node next;//自己有指针,也是连接Node的
        public int value;
        public Node(int v){
            value = v;
        }
    }

(1)当链表个数N为奇数时,求链表中的中点、当链表个数N为偶数时,求链表中的中点;
如果数组长度为N,则中点的位置就是:(N-1)/2

//(1)当链表个数N为奇数时,求链表中的中点、当链表个数N为偶数时,求链表中的**上**中点;
    //如果数组长度为N,则中点的位置就是:**(N-1)/2**
    public static Node midOrUpMidPoint(Node head){
        if (head == null || head.next == null) return head;
        
        ArrayList<Node> arr = new ArrayList<>();//动态数组,中点可以索引的
        Node cur = head;
        while (cur != null){
            arr.add(cur);
            cur = cur.next;
        }
        int N = arr.size();
        return arr.get((N - 1) >> 1);
    }

(2)当链表个数N为奇数时,求链表中的中点、当链表个数N为偶数时,求链表中的中点;
如果数组长度为N,则中点的位置就是:(N)/2

//(2)当链表个数N为奇数时,求链表中的中点、当链表个数N为偶数时,求链表中的**下**中点;
    //如果数组长度为N,则中点的位置就是:**(N)/2**
    public static Node midOrDownMidPoint(Node head){
        if (head == null || head.next == null) return head;

        ArrayList<Node> arr = new ArrayList<>();//动态数组,中点可以索引的
        Node cur = head;
        while (cur != null){
            arr.add(cur);
            cur = cur.next;
        }
        int N = arr.size();
        return arr.get(N >> 1);
    }

(3)当链表个数N为奇数时,求链表中的中点的前一个点、当链表个数N为偶数时,求链表中的中点的前一个点;
如果数组长度为N,则中点的位置就是:(N-3)/2

//(3)当链表个数N为奇数时,求链表中的中点的前一个点、当链表个数N为偶数时,求链表中的**上**中点的前一个点;
    //如果数组长度为N,则中点的位置就是:**(N-3)/2**
    public static Node midPreOrUpMidPrePoint(Node head){
        if (head == null || head.next == null) return head;

        ArrayList<Node> arr = new ArrayList<>();//动态数组,中点可以索引的
        Node cur = head;
        while (cur != null){
            arr.add(cur);
            cur = cur.next;
        }
        int N = arr.size();
        return arr.get((N - 3) >> 1);
    }

(4)当链表个数N为奇数时,求链表中的中点的前一个点、当链表个数N为偶数时,求链表中的中点的前一个点;
如果数组长度为N,则中点的位置就是:(N-2)/2

//(4)当链表个数N为奇数时,求链表中的中点的前一个点、当链表个数N为偶数时,求链表中的**下**中点的前一个点;
    //如果数组长度为N,则中点的位置就是:**(N-2)/2**
    public static Node midPreOrDownMidPrePoint(Node head){
        if (head == null || head.next == null) return head;

        ArrayList<Node> arr = new ArrayList<>();//动态数组,中点可以索引的
        Node cur = head;
        while (cur != null){
            arr.add(cur);
            cur = cur.next;
        }
        int N = arr.size();
        return arr.get((N - 2) >> 1);
    }

四个代码一起测试:

//造一个链表
    public static Node createLinkNode(boolean odd){
        Node head = new Node(1);
        Node n2 = new Node(2);
        Node n3 = new Node(3);
        Node n4 = new Node(4);
        head.next = n2;
        n2.next = n3;
        if (!odd) n3.next = n4;//是偶数长度N,连n4

        return head;
    }
    //测试一波:
    public static void test2(){
        Node head = createLinkNode(true);//N=3
        Node head2 = createLinkNode(false);//N=3

        System.out.println("链表:1-2-3或者1-2-3-4,其中点或者上中点");
        System.out.println(midOrUpMidPoint(head).value);
        System.out.println(midOrUpMidPoint(head2).value);
        System.out.println("链表:1-2-3或者1-2-3-4,中点或者下中点");
        System.out.println(midOrDownMidPoint(head).value);
        System.out.println(midOrDownMidPoint(head2).value);
        System.out.println("链表:1-2-3或者1-2-3-4,中点前一个或者上中点的前一个");
        System.out.println(midPreOrUpMidPrePoint(head).value);
        System.out.println(midPreOrUpMidPrePoint(head2).value);
        System.out.println("链表:1-2-3或者1-2-3-4,中点前一个或者下中点的前一个");
        System.out.println(midPreOrDownMidPrePoint(head).value);
        System.out.println(midPreOrDownMidPrePoint(head2).value);
    }

    public static void main(String[] args) {
//        test1();
        test2();
    }

看结果:

链表:1-2-3或者1-2-3-4,其中点或者上中点
2
2
链表:1-2-3或者1-2-3-4,中点或者下中点
2
3
链表:1-2-3或者1-2-3-4,中点前一个或者上中点的前一个
1
1
链表:1-2-3或者1-2-3-4,中点前一个或者下中点的前一个
1
2

全部AC,easy,非常easy!!!


三、面试代码求最优解:时间空间最优

面试呢,咱就只能在这链表上操作了,绝不可以用额外的空间,否则面试官就会扣你分!!!
争取在时间复杂度高的情况下,又能节约空间

暴力解

先让cur指针,过一遍链表,统计一下链表有多少个节点N,然后
跟上面类似:
咱只需要再走这么多步:
(1)当链表个数N为奇数时,求链表中的中点、当链表个数N为偶数时,求链表中的中点;
如果数组长度为N,则中点的位置就是:(N-1)/2
(2)当链表个数N为奇数时,求链表中的中点、当链表个数N为偶数时,求链表中的中点;
如果数组长度为N,则中点的位置就是:(N)/2
(3)当链表个数N为奇数时,求链表中的中点的前一个点、当链表个数N为偶数时,求链表中的中点的前一个点;
如果数组长度为N,则中点的位置就是:(N-3)/2
(4)当链表个数N为奇数时,求链表中的中点的前一个点、当链表个数N为偶数时,求链表中的中点的前一个点;
如果数组长度为N,则中点的位置就是:(N-2)/2
就能找到中点返回!
这可以,但是这还不是最优解!

最优解:快慢指针行走法

啥意思呢?
什么是快慢指针?
所谓快指针:就是一个指针fast,它每次可以走2步;
所谓慢指针:就是一个指针slow,它每次可以走1步;
在这里插入图片描述
今后遇到这个,跟面试官聊题的时候,直接用笔画案例,找边界,然后就知道代码怎么写了!
也就知道初始化这个快慢指针该怎么办了。

(1)当链表个数N为奇数时,求链表中的中点、当链表个数N为偶数时,求链表中的中点;
——如果head=null,或者head只有1个点,或者head有2个点,他们都只需要返回head,你画个图瞅瞅是不是?
在这里插入图片描述
——当N为奇数或者偶数时,初始化:先让fast走2步,slow走1步
然后快慢指针走,必然,fast先走完,一旦快指针fast走不动了,此时slow就是我们要的中点或者上中点!
【fast指针走不动:fast.next=null或者fast.next.next=null】
在这里插入图片描述
在这里插入图片描述

//复习,面试代码:
    //(1)当链表个数N为奇数时,求链表中的中点、当链表个数N为偶数时,求链表中的**上**中点;
    //——如果head=null,或者head只有1个点,或者head有2个点,他们都只需要返回head,你画个图瞅瞅是不是?
    //当N为奇数或者偶数时,初始化:先让fast走2步,slow走1步
    //然后快慢指针走,必然,fast先走完,一旦快指针fast走不动了,此时slow就是我们要的中点或者上中点!
    public static Node midOrUpMidPointFace(Node head){
        if (head == null || head.next == null || head.next.next == null) return head;

        Node fast = head.next.next;
        Node slow = head.next;
        while (fast.next != null && fast.next.next != null){
            fast = fast.next.next;//快指针
            slow = slow.next;//满指针
        }

        return slow;
    }

(2)当链表个数N为奇数时,求链表中的中点、当链表个数N为偶数时,求链表中的中点;
——当head=null或者head只有1个点时,返回head就行
在这里插入图片描述
N>=2的话:
——当N时奇数或者偶数时,初始化:fast和slow都要走一步
然后快慢指针走,fast走不动了,slow必然是中点或者下中点。
在这里插入图片描述
在这里插入图片描述

//(2)当链表个数N为奇数时,求链表中的中点、当链表个数N为偶数时,求链表中的**下**中点;
    //——当head=null或者head只有1个点时,返回head就行
    //N>=2的话:
    //——当N时奇数或者偶数时,初始化:fast和slow都要走一步
    //然后快慢指针走,fast走不动了,slow必然是中点或者下中点。
    public static Node midOrDownMidPointFace(Node head){
        if (head == null || head.next == null) return head;

        Node fast = head.next;//1步哦
        Node slow = head.next;
        while (fast.next != null && fast.next.next != null){
            fast = fast.next.next;//快指针
            slow = slow.next;//满指针
        }

        return slow;
    }

(3)当链表个数N为奇数时,求链表中的中点的前一个点、当链表个数N为偶数时,求链表中的中点的前一个点;
——当head=null或者,head就1个点,或者head有2个点时,那中点前一个点为null,上中点前一个点也是null
在这里插入图片描述
——当N>=3时,咱们初始化:让fast先走2步,slow就在head不动
然后快慢指针走,fast走不动,slow就是中点前一个,或者上中点前一个
在这里插入图片描述

//(3)当链表个数N为奇数时,求链表中的中点的前一个点、当链表个数N为偶数时,求链表中的**上**中点的前一个点;
    //——当head=null或者,head就1个点,或者head有2个点时,那中点前一个点为null,上中点前一个点也是null
    //——当N>=3时,咱们初始化:让fast先走2步,slow就在head不动
    //然后快慢指针走,fast走不动,slow就是中点前一个,或者上中点前一个
    public static Node midPreOrUpMidPrePointFace(Node head){
        if (head == null || head.next == null || head.next.next == null) return null;

        Node fast = head.next.next;//2步
        Node slow = head;//不动哦
        while (fast.next != null && fast.next.next != null){
            fast = fast.next.next;//快指针
            slow = slow.next;//满指针
        }

        return slow;
    }

(4)当链表个数N为奇数时,求链表中的中点的前一个点、当链表个数N为偶数时,求链表中的中点的前一个点;
——当head=null或者head就1个点时,null就是中点前一个点
在这里插入图片描述
——当N>=2,初始化:fast先走1步,而slow先不动
快慢指针正常走,fast走不动了,此时slow就是中点前一个点,或者下中点前一个点
在这里插入图片描述

//(4)当链表个数N为奇数时,求链表中的中点的前一个点、当链表个数N为偶数时,求链表中的**下**中点的前一个点;
    //——当head=null或者head就1个点时,null就是中点前一个点
    //——当N>=2,初始化:fast先走1步,而slow先不动
    //快慢指针正常走,fast走不动了,此时slow就是中点前一个点,或者下中点前一个点
    public static Node midPreOrDownMidPrePointFace(Node head){
        if (head == null || head.next == null) return null;

        Node fast = head.next;//1步
        Node slow = head;//不动哦
        while (fast.next != null && fast.next.next != null){
            fast = fast.next.next;//快指针
            slow = slow.next;//满指针
        }

        return slow;
    }

所以 总结一下,遇到这个题,直接动笔画一下,就知道边界,和快慢指针的初始化,该怎么办了。
搞懂了核心思想,写代码完全不是问题!!!
测试:

public static void test4(){
        Node head = createLinkNode(true);//N=3
        Node head2 = createLinkNode(false);//N=3

        System.out.println("链表:1-2-3或者1-2-3-4,其中点或者上中点");
        System.out.println(midOrUpMidPointFace(head).value);
        System.out.println(midOrUpMidPointFace(head2).value);
        System.out.println("链表:1-2-3或者1-2-3-4,中点或者下中点");
        System.out.println(midOrDownMidPointFace(head).value);
        System.out.println(midOrDownMidPointFace(head2).value);
        System.out.println("链表:1-2-3或者1-2-3-4,中点前一个或者上中点的前一个");
        System.out.println(midPreOrUpMidPrePointFace(head).value);
        System.out.println(midPreOrUpMidPrePointFace(head2).value);
        System.out.println("链表:1-2-3或者1-2-3-4,中点前一个或者下中点的前一个");
        System.out.println(midPreOrDownMidPrePointFace(head).value);
        System.out.println(midPreOrDownMidPrePointFace(head2).value);
    }

    public static void main(String[] args) {
//        test1();
        test2();
    }

总结

提示:重要经验:

1)链表相关问题基础的知识要牢固,另外像本题这种涉及中点,上中点,下中点的操作,今后要敏感地想到用快慢指针解决,抠清楚边界条件和初始化快慢指针的点,画图举例,自己搞清楚,就很明白了。
2)大厂的笔试代码求AC,不管空间复杂度,面试要求最优解,既要考虑速度快,还要节约空间。

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冰露可乐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值