必须干掉这10道,面试100%遇到!(二)

joytrian
发布于 2022-7-7 16:53
浏览
0收藏

 

0X03环形链表
对应力扣141和力扣142,力扣141环形链表要求为:

给定一个链表,判断链表中是否有环,用O(1)内存解决。

详细分析:环形链表找入口,真的太妙了

这个问题利用快慢双指针比较高效,快指针fast每次走2步,slow每次走1步,慢指针走n步到尾时候快指针走了2n步,而环的大小一定小于等于n所以一定会相遇,如果相遇那么说明有环,如果不相遇fast先为null说明无环。

具体代码为:

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

力扣142是在力扣141拓展,如有有环,返回入环的那个节点,就想下图环形链表返回节点2。

必须干掉这10道,面试100%遇到!(二)-鸿蒙开发者社区

这个问题是需要数学转换的,具体的分析可以看上面的详细分析,这里面提一下大题的步骤。

如果找到第一个交汇点,其中一个停止,另一个继续走,下一次交汇时候刚好走一圈,可以算出循环部分长度为y。

所以我们知道的东西有:交汇时候fast走2x步,slow走x步,环长为y。并且快指针和慢指针交汇时候,多走的步数刚好是换长y的整数倍(它两此刻在同一个位置,快指针刚好多绕整数倍圈数才能在同一个位置相聚),可以得到2x=x+ny(x=ny)。其中所以说慢指针走的x和快指针多走的x是圈长y的整数倍。

必须干掉这10道,面试100%遇到!(二)-鸿蒙开发者社区

也就是说,从开头走到这个点共计x步,从这个点走x步也就是绕了几圈也回到这个点。如果说slow从起点出发,fast从这个点出发(每次走一步,相当于之前两步抵消slow走的路程),那么走x步还会到达这个点,但是这两个指针这次都是每次走一步,所以一旦slow到达循环圈内,两个指针就开始汇合了。

必须干掉这10道,面试100%遇到!(二)-鸿蒙开发者社区

实现代码为:

public class Solution {
    public ListNode detectCycle(ListNode head) {
        boolean isloop=false;
        ListNode fast=new ListNode(0);//头指针
        ListNode slow=fast;
        fast.next=head;
        if(fast.next==null||fast.next.next==null)
            return null;
        while (fast!=null&&fast.next!=null) {
            fast=fast.next.next;
            slow=slow.next;
            if(fast==slow)
            {
                isloop=true;
                break;
            }
        }
        if(!isloop)//如果没有环返回
            return null;
        ListNode team=new ListNode(-1);//头指针 下一个才是head
        team.next=head;
        while (team!=fast) {//slow 和fast 分别从起点和当前点出发
            team=team.next;
            fast=fast.next;
        }
        return team;
    }
}

0X04两个栈实现队列
对应剑指offer09,题意为:

用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )

分析:

解决这个问题,要知道栈是什么,队列是什么,两种常见数据结构格式很简单,栈的特点就是:后进先出,队列的特点就是:先进先出,栈可以想象成一堆书本,越在上面的取的越早,上面来上面出(比喻一下);队列就是想象成排队买东西,只能后面进前面出,所以两者数据结构还是有区别的,虽然都是单个入口进出,但是栈进出口相同,而队列不同。

上面描述的是一个普通栈和队列的数据结构,这里面让我们用两个栈实现一个队列的操作,这里比较容易想的方案就是其中一个栈stack1用作数据存储,插入尾时候直接插入stack1,而删除头的时候将数据先加入到另一个栈stack2中,返回并删除栈顶元素,将stack2顺序加入stack1中实现一个复原,但是这样操作插入时间复杂度为O(1),删除时间复杂度为O(n)比较高。

实现方式也给大家看下:

public class Solution {
    public ListNode detectCycle(ListNode head) {
        boolean isloop=false;
        ListNode fast=new ListNode(0);//头指针
        ListNode slow=fast;
        fast.next=head;
        if(fast.next==null||fast.next.next==null)
            return null;
        while (fast!=null&&fast.next!=null) {
            fast=fast.next.next;
            slow=slow.next;
            if(fast==slow)
            {
                isloop=true;
                break;
            }
        }
        if(!isloop)//如果没有环返回
            return null;
        ListNode team=new ListNode(-1);//头指针 下一个才是head
        team.next=head;
        while (team!=fast) {//slow 和fast 分别从起点和当前点出发
            team=team.next;
            fast=fast.next;
        }
        return team;
    }
}

这样的时间复杂度是不被喜欢的,因为删除太鸡儿耗时了,每次都要折腾一番,有没有什么好的方法能够让删除也方便一点呢?

有啊,stack1可以顺序保证顺序插入,stack1数据放到stack2中可以保证顺序删除,所以用stack1作插入,stack2作删除,因为题目也没要求数据必须放到一个容器中,所以就这样组合使用,完美perfect!

必须干掉这10道,面试100%遇到!(二)-鸿蒙开发者社区

具体实现的时候,插入直接插入到stack1中,如果需要删除从stack2中栈顶删除,如果stack2栈为空那么将stack1中数据全部添加进来(这样又能保证stack2中所有数据是可以顺序删除的了),下面列举几个删除的例子

必须干掉这10道,面试100%遇到!(二)-鸿蒙开发者社区

其实就是将数据分成两个部分,一部分用来插入,一部分用来删除,删除的那个栈stack2空了添加所有stack1中的数据继续操作。这个操作插入删除的时间复杂度是O(1),具体实现的代码为:

class CQueue {
    Deque<Integer> stack1;
    Deque<Integer> stack2;

    public CQueue() {
        stack1 = new LinkedList<Integer>();
        stack2 = new LinkedList<Integer>();
    }

    public void appendTail(int value) {
        stack1.push(value);
    }

    public int deleteHead() {
        // 如果第二个栈为空 将stack1数据加入stack2
        if (stack2.isEmpty()) {
            while (!stack1.isEmpty()) {
                stack2.push(stack1.pop());
            }
        } //如果stack2依然为空 说明没有数据
        if (stack2.isEmpty()) {
            return -1;
        } else {//否则删除
            int deleteItem = stack2.pop();
            return deleteItem;
        }
    }
}

 

文章转自公众号:bigsai

标签
已于2022-7-7 16:53:27修改
收藏
回复
举报
回复
    相关推荐