【LeetCode热题100】打卡第35天:最小栈&相交链表

【LeetCode热题100】打卡第35天:最小栈&相交链表

⛅前言

大家好,我是知识汲取者,欢迎来到我的LeetCode热题100刷题专栏!

精选 100 道力扣(LeetCode)上最热门的题目,适合初识算法与数据结构的新手和想要在短时间内高效提升的人,熟练掌握这 100 道题,你就已经具备了在代码世界通行的基本能力。在此专栏中,我们将会涵盖各种类型的算法题目,包括但不限于数组、链表、树、字典树、图、排序、搜索、动态规划等等,并会提供详细的解题思路以及Java代码实现。如果你也想刷题,不断提升自己,就请加入我们吧!QQ群号:827302436。我们共同监督打卡,一起学习,一起进步。

博客主页💖:知识汲取者的博客

LeetCode热题100专栏🚀:LeetCode热题100

Gitee地址📁:知识汲取者 (aghp) - Gitee.com

题目来源📢:LeetCode 热题 100 - 学习计划 - 力扣(LeetCode)全球极客挚爱的技术成长平台

PS:作者水平有限,如有错误或描述不当的地方,恳请及时告诉作者,作者将不胜感激

最小栈

🔒题目

原题链接:155.最小栈

image-20230711093221216

🔑题解

  • 解法一:使用两个栈

    这个方式是真的巧妙,这次算是开眼界了。现在就让我来讲解一下这个思路的具体实现方式吧(●ˇ∀ˇ●)

    首先我们要创建两个栈,一个 stack,一个minStack,stack是存储真实数据的栈,对外界是透明的,minStack是存储当前最小值的,对外界是不透明的。每次存储数据,stack是直接存,但是minStack是存储当前最小值,也就是要将要存储的值与minStack的栈顶元素进行比较,选较小值存,这样我们调用getMin时,直接返回minStack的栈顶元素即可

    image-20230711100418398

    /**
     * @author ghp
     * @title
     * @description
     */
    class MinStack {
    
        Deque<Integer> stack;
        Deque<Integer> minStack;
    
        public MinStack() {
            this.stack = new LinkedList<>();
            minStack = new LinkedList<>();
            // 初始化,防止出现NPE
            minStack.push(Integer.MAX_VALUE);
        }
    
        public void push(int val) {
            stack.push(val);
            minStack.push(Math.min(minStack.peek(), val));
        }
    
        public void pop() {
            stack.pop();
            minStack.pop();
        }
    
        public int top() {
            return stack.peek();
        }
    
        public int getMin() {
            return minStack.peek();
        }
    }
    

    复杂度分析:

    • 时间复杂度: O ( 1 ) O(1) O(1)
    • 空间复杂度: O ( n ) O(n) O(n)

    其中 n n n 为栈中元素的个数

拓展:为什么一般栈使用Deque而不是Stack?

这就需要考虑到两者的底层实现了,算法中一般使用Deque(双端队列)而不是Stack(栈)的主要原因如下:

  1. Stack是Java集合框架中提供的一种特殊数据结构,仅限于在栈顶进行元素的插入和删除操作,而Deque具有更加丰富的操作方法,可以在队首和队尾进行元素的插入、删除和检索。
  2. Stack类在Java中继承自Vector类,并且Stack类的方法都是同步的。因此,在并发环境下使用Stack可能会带来性能上的开销。而Deque的实现类LinkedList不是同步的,可以更好地满足多线程环境下的需求。
  3. 在Java 6之后,Deque被引入到Java集合框架中,将Stack类推荐用Deque接口的实现类来代替,以便提高代码的可读性和一致性。

总结起来,使用Deque而不是Stack有助于代码的可读性和一致性,同时避免了可能的性能开销和线程安全问题。因此,在大多数情况下,建议使用Deque来实现栈数据结构。

  • 解法二:使用 一个栈 + 一个变量

    不得不感慨这种方法更加巧妙,栈这个数据结构简直可以玩出花了🤣

    image-20230711151144936

    class MinStack {
    
        private Deque<Integer> stack;
        private int min;
    
        public MinStack() {
            this.stack = new LinkedList<>();
            this.min = Integer.MAX_VALUE;
        }
    
        public void push(int val) {
            if (val <= min) {
                // 当前值是更小,更新最小值,并且将之前的最小值入栈
                // 当前值等于最小值时,也需要进行入栈,否则后面会出现NPE
                stack.push(min);
                min = val;
            }
            stack.push(val);
        }
    
        public void pop() {
            if (stack.pop() == min){
                // 栈顶元素等于最小值,则更新最小值
                min = stack.pop();
            }
        }
    
        public int top() {
            return stack.peek();
        }
    
        public int getMin() {
            return min;
        }
    }
    

    复杂度分析:

    • 时间复杂度: O ( 1 ) O(1) O(1)
    • 空间复杂度: O ( n ) O(n) O(n)

    其中 n n n 为栈中元素的个数

相交链表

🔒题目

原题链接:160.相交链表

image-20230711141805585

🔑题解

  • 解法一:哈希表

    public class Solution {
        public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
            Set<ListNode> set = new HashSet<>();
            // 将链表A的所有节点存入Set集合中
            while (headA != null){
                set.add(headA);
                headA = headA.next;
            }
            // 找出链表B和链表A的共有节点
            while (headB != null){
                if (set.contains(headB)){
                    // 链表B的节点在链表A中也出现了,说明两者相交了
                    return headB;
                }
                headB = headB.next;
            }
            return null;
        }
    }
    

    复杂度分析:

    • 时间复杂度: O ( n + m ) O(n+m) O(n+m)
    • 空间复杂度: O ( n ) O(n) O(n)

    其中 n n n 为链表A的节点个数, m m m为链表B的节点个数

  • 解法二:双指针

    双指针解法,是使用两个指针,遍历两遍链表,第一遍遍历让两个指针处于同一水平位置,第二遍遍历就可以直接找到交点了

    image-20230711202041886

    备注:红色代表第一次遍历,蓝色代表第二次遍历

    如果你一上来就来看这个方法,你可能会感觉优点不可思议,但事实如此。

    现在就让我们来证明一下:

    假设A链表的头节点a距离交点的距离为 p,B链表的头节点b距离交点 q,公共的长度为 t,则有一下推导

    1. 如果A和B不相交:

      ①A和B一样长,p=p.next, q=q.next,最终都为null

      ②A和B不一样长,A长B短,首先B走到终点,A和B走了q,然后B放到A的起点,A和B走了p,然后将A放到B的起点,循环往复,最后A走了 p+q、B走了 q+p ,A来到B的终点,B来到A的终点,最终都是null

    2. 如果A和B相交:

      ①A和B一样长,有 p=q,所以两者在 p(q)处相遇

      ②A和B不一样长,A长B短,首先B走到终点,A和B走了 q+t,B放到A的起点置0,此后再走p,此时B来到交点处,而A走了 q+t+p,而 t+p 表示A走到终点,要放到B链表的起点,但此时还剩 q 步,A从B的起点走 q 步,不正是交点处吗,所以两者走

      p+q+t 处时,两者在交点相遇

    当然我只是比较简单的推导了一下,如果想要看更加严格的数学证明,可以参考 Krahets 题解,链接附文末

    PS:这个推导十分类似于【LeetCode热题100】打卡第33天的题目环形链表II,这题我同样也是参考了 Krahets 的题解

    public class Solution {
        public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
            ListNode p = headA, q = headB;
            while (p != q) {
                p = p != null ? p.next : headB;
                q = q != null ? q.next : headA;
            }
            return p;
        }
    }
    

    复杂度分析:

    • 时间复杂度: O ( n + m ) O(n+m) O(n+m)
    • 空间复杂度: O ( 1 ) O(1) O(1)

    其中 n n n 为链表A的节点个数, m m m为链表B的节点个数

    从这题我们也可以明白一个道理人生哲理:如果你和我有交点,走过你来时的路,我们终会相遇,如果你和我没有交点,走遍整个人生我们都不会相遇而是走向各自人生的终点null

参考题解

在此致谢LeetCode哪些无私共享题解的Coder,也欢迎大家给他们点赞

最后,如果觉得本文对你有帮助,欢迎三连(点赞👍+评论✍+收藏⭐)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

知识汲取者

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

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

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

打赏作者

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

抵扣说明:

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

余额充值