剑指offer——链表 + 栈 + 队列合集(LeetCode)

面试题06. 从尾到头打印链表

题目

输入:head = [1,3,2]
输出:[2,3,1]

方法一:直接遍历链表的同时反转链表,最后进行一次遍历得到反转后链表的值。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public int[] reversePrint(ListNode head) {
        ListNode pre=null;
        ListNode now=head;
        int cnt=0;
        while(now!=null){
            cnt++;
            ListNode next=now.next;
            now.next=pre;
            pre=now;
            now=next;
        }

        now=pre;
        int[] ans=new int[cnt];
        for(int i=0;i<cnt;i++){
            ans[i]=now.val;
            now=now.next;
        }
        return ans;
    }
}

运行结果
方法二:栈。遍历时将链表值存入栈内,然后读出即可。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public int[] reversePrint(ListNode head) {
        Stack<Integer> st=new Stack<Integer>();
        ListNode node=head;
        while(node!=null){
            st.push(node.val);
            node=node.next;
        }

        int[] ans=new int[st.size()];
        int i=0;
        while(!st.empty()){
            ans[i++]=st.pop();
        }
        return ans;
    }
}

执行结果

面试题09. 用两个栈实现队列

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

输入:
[“CQueue”,“appendTail”,“deleteHead”,“deleteHead”]
[[],[3],[],[]]
输出:[null,null,3,-1]

一个栈记录元素,一个栈用于输出元素。

class CQueue {

    Stack<Integer> a;
    Stack<Integer> b;

    public CQueue() {
        a=new Stack<Integer>();
        b=new Stack<Integer>();
    }
    
    public void appendTail(int value) {
        a.push(value);
    }
    
    public int deleteHead() {
        if(a.empty()&&b.empty()){
            return -1;
        }
        else if(b.empty()&&!a.empty()){
            while(!a.empty()){
                b.push(a.pop());
            }
        }
        return b.pop();
    }
}

/**
 * Your CQueue object will be instantiated and called as such:
 * CQueue obj = new CQueue();
 * obj.appendTail(value);
 * int param_2 = obj.deleteHead();
 */

面试题18. 删除链表的节点

题目

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode deleteNode(ListNode head, int val) {
        if(head==null) return null;
        if(head.val==val){
            return head.next;
        }

        ListNode pre=head;
        ListNode node=head;
        while(node!=null){
            if(node.val==val){
                ListNode next=node.next;
                pre.next=next;
            }
            pre=node;
            node=node.next;
        }
        return head;
    }
}

面试题22. 链表中倒数第k个节点

题目

给定一个链表: 1->2->3->4->5, 和 k = 2.
返回链表 4->5.

双指针

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode getKthFromEnd(ListNode head, int k) {
        if(head==null){
            return null;
        }
        ListNode pre=head;
        ListNode node=head;
        for(int i=0;i<k;i++){
            node=node.next;
        }    
        while(node!=null){
            node=node.next;
            pre=pre.next;
        }
        return pre;
    }
}

面试题24. 反转链表

题目

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        if(head==null){
            return head;
        }
        ListNode pre=null;
        ListNode node=head;
        while(node!=null){
            ListNode next=node.next;
            node.next=pre;
            pre=node;
            node=next;
        }
        return pre;
    }
}

面试题25. 合并两个排序的链表

题目
输入两个递增排序的链表,合并这两个链表并使新链表中的节点仍然是递增排序的。

输入:1->2->4, 1->3->4
输出:1->1->2->3->4->4

注意当某一个链表判断结束时,要将另一个未判断完的链表整个连在现有链表后面。

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) { val = x; }
 * }
 */
class Solution {
    public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
        ListNode head=new ListNode(1);
        ListNode ret=head;
        while(l1!=null && l2!=null){
            if(l1.val<l2.val){
                head.next=l1;
                l1=l1.next;
            }
            else{
                head.next=l2;
                l2=l2.next;
            }
            head=head.next;
        }
        head.next=l1==null?l2:l1;
        return ret.next;
    }
}

面试题30. 包含min函数的栈

题目

class MinStack {

    Stack<Integer> a;
    Stack<Integer> b;

    /** initialize your data structure here. */
    public MinStack() {
        a=new Stack<Integer>();
        b=new Stack<Integer>();
    }
    
    public void push(int x) {
        a.add(x);
        if(b.empty()||x<=b.peek()){
            b.add(x);
        }
    }
    
    public void pop() {
        if(a.pop().equals(b.peek())){
            b.pop();
        }
    }
    
    public int top() {
        return a.peek();
    }
    
    public int min() {
        if(b.empty()) return 0;
        return b.peek();
    }
}

/**
 * Your MinStack object will be instantiated and called as such:
 * MinStack obj = new MinStack();
 * obj.push(x);
 * obj.pop();
 * int param_3 = obj.top();
 * int param_4 = obj.min();
 */

面试题31. 栈的压入、弹出序列

题目
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如,序列 {1,2,3,4,5} 是某栈的压栈序列,序列 {4,5,3,2,1} 是该压栈序列对应的一个弹出序列,但 {4,3,5,1,2} 就不可能是该压栈序列的弹出序列。

输入:pushed = [1,2,3,4,5], popped = [4,5,3,2,1]
输出:true

输入:pushed = [1,2,3,4,5], popped = [4,3,5,1,2]
输出:false

使用辅助栈模拟,在出栈过程中根据popped栈进行模拟弹出。最后若辅助栈为空,证明可以通过pop栈的弹出顺序全部弹出。

class Solution {
    public boolean validateStackSequences(int[] pushed, int[] popped) {
        Stack<Integer> st=new Stack<>();
        int popIndex=0;
        for(int i=0;i<pushed.length;i++){
            st.add(pushed[i]);
            while(!st.empty()&&st.peek()==popped[popIndex]){
                st.pop();
                popIndex++;
            }
        }
        if(st.empty()) return true;
        return false;
    }
}

面试题35. 复杂链表的复制

题目
请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。

在这里插入图片描述
输入:head = [[7,null],[13,0],[11,4],[10,2],[1,0]]
输出:[[7,null],[13,0],[11,4],[10,2],[1,0]]

待填坑~~

面试题52. 两个链表的第一个公共节点

输入两个链表,找出它们的第一个公共节点。

在这里插入图片描述

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,0,1,8,4,5], skipA = 2, skipB = 3
输出:Reference of the node with value = 8
输入解释:相交节点的值为 8 (注意,如果两个列表相交则不能为 0)。从各自的表头开始算起,链表 A 为 [4,1,8,4,5],链表 B 为 [5,0,1,8,4,5]。在 A 中,相交节点前有 2 个节点;在 B 中,相交节点前有 3 个节点。

这里一开始想不明白如果两个链表没有交集不是会陷入死循环吗?
其实如果没有交集,那么第二次的时候会同时走到结尾null,也会PA=PB的

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode(int x) {
 *         val = x;
 *         next = null;
 *     }
 * }
 */

public class Solution {
    public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
        if(headA==null||headB==null)
            return null;
        ListNode pA=headA;
        ListNode pB=headB;
        while(pA!=pB){
            pA=pA==null?headB:pA.next;
            pB=pB==null?headA:pB.next;
        }
        return pA;
    }
}

面试题59 - I. 滑动窗口的最大值

给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。

输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7]
解释:

滑动窗口的位置 最大值
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7

使用双端队列,维护当前窗口内有效最大值的排列。
这里很巧妙的地方就是怎样维护队列的,队列中又是维护的什么值;而且不需要用两个指针记录窗口长度,直接遍历时当前下标-k即可。
具体思想如图所示:
在这里插入图片描述
这里回顾java的LinkedList

  • LinkedList 是一个继承于AbstractSequentialList的双向链表。它也可以被当作堆栈、队列或双端队列进行操作。
  • LinkedList 实现 List 接口,能进行队列操作。
  • LinkedList 实现 Deque 接口,即能将LinkedList当作双端队列使用。
  • ArrayList底层是由数组支持,而LinkedList 是由双向链表实现的,其中的每个对象包含数据的同时还包含指向链表中前一个与后一个元素的引用。
class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if (nums == null || k < 1 || nums.length < k) {
            return new int[0];
        }

        LinkedList<Integer> queue=new LinkedList<>();
        int res[]=new int[nums.length-k+1];
        int id=0;
        for(int i=0;i<nums.length;i++){
            while(!queue.isEmpty() && nums[i]>=nums[queue.peekLast()]){
                queue.pollLast();
            }
            queue.addLast(i);
            if(queue.peekFirst()==(i-k)){
                queue.pollFirst();
            }
            if(i>=k-1){
                res[id++]=nums[queue.peekFirst()];
            }
        }
        return res;
    }
}

面试题59 - II. 队列的最大值

题目
请定义一个队列并实现函数 max_value 得到队列里的最大值,要求函数max_value、push_back 和 pop_front 的均摊时间复杂度都是O(1)。
若队列为空,pop_front 和 max_value 需要返回 -1。

输入:
[“MaxQueue”,“push_back”,“push_back”,“max_value”,“pop_front”,“max_value”]
[[],[1],[2],[],[],[]]
输出: [null,null,null,2,1,2]

和59-I的思想可以说一模一样,这里的数字是一个一个给出的,所以要用一个队列存储。push_back 和 pop_front的操作实际就是上面移动窗口的操作,这里是将整个序列看作一个不定长的时间窗口的移动。

好好感悟这两道题对于双端队列的使用和维护~
个人感觉c++的deque使用更顺手。

class MaxQueue {
private:
    queue<int> que;
    deque<int> deq;
public:
    MaxQueue() {

    }
    
    int max_value() {
        return que.empty()?-1:deq.front();
    }
    
    void push_back(int value) {
        que.push(value);
        while(!deq.empty() && deq.back()<value){
            deq.pop_back();
        }
        deq.push_back(value);
    }
    
    int pop_front() {
        if(que.empty()){
            return -1;
        }
        int value=que.front();
        que.pop();
        if(value==deq.front()){
            deq.pop_front();
        }
        return value;
    }
};

/**
 * Your MaxQueue object will be instantiated and called as such:
 * MaxQueue* obj = new MaxQueue();
 * int param_1 = obj->max_value();
 * obj->push_back(value);
 * int param_3 = obj->pop_front();
 */
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值