美文网首页
大厂算法面试之leetcode精讲18.队列

大厂算法面试之leetcode精讲18.队列

作者: 全栈潇晨 | 来源:发表于2021-12-03 08:05 被阅读0次

    大厂算法面试之leetcode精讲18.队列

    视频讲解(高效学习):点击学习

    目录:

    1.开篇介绍

    2.时间空间复杂度

    3.动态规划

    4.贪心

    5.二分查找

    6.深度优先&广度优先

    7.双指针

    8.滑动窗口

    9.位运算

    10.递归&分治

    11剪枝&回溯

    12.堆

    13.单调栈

    14.排序算法

    15.链表

    16.set&map

    17.栈

    18.队列

    19.数组

    20.字符串

    21.树

    22.字典树

    23.并查集

    24.其他类型题

    • 队列的特点:先进先出(FIFO)
    • 队列的时间复杂度:入队和出队O(1),查找O(n)
    • 优先队列:priorityQueue,按优先级出队,实现 Heap(Binary,Fibonacci...)
    • js里没有队列,但是可以用数组模拟
    ds_29

    225. 用队列实现栈 (easy)

    方法1.使用两个 Queue 实现
    • 思路:还是考察栈和队列的熟悉程度,没有什么具体的工程实际意义,可以用两个队列来实现栈的功能,但是一个队列的数据导入另一个队列顺序还是没有改变,所以其中一个队列只是用来做备份的,在代码里queue2就是备份队列,入栈的时候,队列1入队,出栈的时候,如果队列1为空,则交换队列1和队列2,为的是将备份队列的元素全部加入队列1,然后将队列1中除了最后一个元素外全部出队,并且加入备份队列,
    • 复杂度分析:push的时间复杂度为O(1),pop的时间复杂度为O(n)。空间复杂度O(n),其中n是栈内元素的个数,用两个队列来存储

    动画过大,点击查看

    Js:

    var MyStack = function() {
        this.queue1 = [];
        this.queue2 = [];//备份的队列
    };
    
    MyStack.prototype.push = function(x) {
        this.queue1.push(x);
    };
    
    MyStack.prototype.pop = function() {
        // 减少两个队列交换的次数, 只有当queue1为空时,交换两个队列
        if(!this.queue1.length) {
            [this.queue1, this.queue2] = [this.queue2, this.queue1];
        }
        while(this.queue1.length > 1) {//当队列1的元素数量大于1的时候不断将元素push进备份队列
            this.queue2.push(this.queue1.shift());
        }
        return this.queue1.shift();//最后将队列1最后一个元素出队
    };
    
    MyStack.prototype.top = function() {
        const x = this.pop();//查看栈顶,队列出队,然后在push进队列1
        this.queue1.push(x);
        return x;
    };
    
    MyStack.prototype.empty = function() {
        return !this.queue1.length && !this.queue2.length;
    };
    
    

    Java:

    class MyStack {
        Queue<Integer> queue1; 
        Queue<Integer> queue2; 
    
        public MyStack() {
            queue1 = new LinkedList<>();
            queue2 = new LinkedList<>();
        }
        
        public void push(int x) {
            queue2.offer(x);
            while (!queue1.isEmpty()){
                queue2.offer(queue1.poll());
            }
            Queue<Integer> queueTemp;
            queueTemp = queue1;
            queue1 = queue2;
            queue2 = queueTemp;
        }
        
        public int pop() {
            return queue1.poll(); 
        }
    
        public int top() {
            return queue1.peek();
        }
    
        public boolean empty() {
            return queue1.isEmpty();
        }
    }
    
    
    方法2.使用一个 队列 实现

    动画过大,点击查看

    • 思路:使用一个 队列 实现,入栈的时候直接push进队列就行,出栈的时候将除了最后一个元素外的元素全部加入到队尾。
    • 复杂度分析:push的时间复杂度为O(1),pop的时间复杂度为O(n),空间复杂度O(n)

    js:

    var MyStack = function() {
        this.queue = [];
    };
    
    MyStack.prototype.push = function(x) {
        this.queue.push(x);
    };
    
    MyStack.prototype.pop = function() {
        let size = this.queue.length;
        while(size-- > 1) {//将除了最后一个元素外的元素全部加入到队尾。
            this.queue.push(this.queue.shift());
        }
        return this.queue.shift();
    };
    
    MyStack.prototype.top = function() {
        const x = this.pop();//先出栈,然后在加入队列
        this.queue.push(x);
        return x;
    };
    
    MyStack.prototype.empty = function() {
        return !this.queue.length;
    };
    
    

    java:

    class MyStack {
        Deque<Integer> queue1;
    
        public MyStack() {
            queue1 = new ArrayDeque<>();
        }
    
        public void push(int x) {
            queue1.addLast(x);
        }
        
        public int pop() {
            int size = queue1.size();
            size--;
            while (size-- > 0) {
                queue1.addLast(queue1.peekFirst());
                queue1.pollFirst();
            }
    
            int res = queue1.pollFirst();
            return res;
        }
        
        public int top() {
            return queue1.peekLast();
        }
        
        public boolean empty() {
            return queue1.isEmpty();
        }
    }
    
    

    703. 数据流中的第 K 大元素 (easy)

    方法1:暴力排序
    • 思路:当数据流有新的元素的时候,重新按升序排序数组,倒数第k个元素就是第k大的数
    • 复杂度分析:时间复杂度O(c*zlogz),z为数据流的最长长度,c为加入元素的个数,排序复杂度是O(zlogz),加入c次排序就需要排序c次。
    方法2:堆
    ds_32
    • 思路:用一个size是k的小顶堆来存贮前k个元素,堆顶是最小的元素,在循环数组的过程中,不断加入元素然后调整元素在堆中的位置,如果此时优先队列的大小大于 k,我们需要将优先队列的队头元素弹出,以保证优先队列的大小为 k,最后堆顶就是第K大元素的位置
    • 复杂度分析:时间复杂度O(nlogk),n是初始数组的大小,k是堆的大小,初始堆化和单次插入堆的复杂度都是O(logk),数组的每个数都要插入堆中一次,所以是O(nlogk)。 空间复杂度:O(k), 即堆的大小

    js:

    var KthLargest = function (k, nums) {
        this.k = k;
        this.heap = new Heap();
        for (const x of nums) {//将数组中的数加入小顶堆
            this.add(x);//加入小顶堆
        }
    };
    
    KthLargest.prototype.add = function (val) {
        this.heap.offer(val);//加入堆
        if (this.heap.size() > this.k) {//大小超过了小顶堆的size,就从小顶堆删除一个最小的元素
            this.heap.poll();//删除最小的元素
        }
        return this.heap.peek();//返回堆顶
    };
    
    class Heap {
        constructor(comparator = (a, b) => a - b, data = []) {
            this.data = data;
            this.comparator = comparator;//比较器
            this.heapify();//堆化
        }
    
        heapify() {
            if (this.size() < 2) return;
            for (let i = Math.floor(this.size()/2)-1; i >= 0; i--) {
                this.bubbleDown(i);//bubbleDown操作
            }
        }
    
        peek() {
            if (this.size() === 0) return null;
            return this.data[0];//查看堆顶
        }
    
        offer(value) {
            this.data.push(value);//加入数组
            this.bubbleUp(this.size() - 1);//调整加入的元素在小顶堆中的位置
        }
    
        poll() {
            if (this.size() === 0) {
                return null;
            }
            const result = this.data[0];
            const last = this.data.pop();
            if (this.size() !== 0) {
                this.data[0] = last;//交换第一个元素和最后一个元素
                this.bubbleDown(0);//bubbleDown操作
            }
            return result;
        }
    
        bubbleUp(index) {
            while (index > 0) {
                const parentIndex = (index - 1) >> 1;//父节点的位置
                //如果当前元素比父节点的元素小,就交换当前节点和父节点的位置
                if (this.comparator(this.data[index], this.data[parentIndex]) < 0) {
                    this.swap(index, parentIndex);//交换自己和父节点的位置
                    index = parentIndex;//不断向上取父节点进行比较
                } else {
                    break;//如果当前元素比父节点的元素大,不需要处理
                }
            }
        }
    
        bubbleDown(index) {
            const lastIndex = this.size() - 1;//最后一个节点的位置
            while (true) {
                const leftIndex = index * 2 + 1;//左节点的位置
                const rightIndex = index * 2 + 2;//右节点的位置
                let findIndex = index;//bubbleDown节点的位置
                //找出左右节点中value小的节点
                if (
                    leftIndex <= lastIndex &&
                    this.comparator(this.data[leftIndex], this.data[findIndex]) < 0
                ) {
                    findIndex = leftIndex;
                }
                if (
                    rightIndex <= lastIndex &&
                    this.comparator(this.data[rightIndex], this.data[findIndex]) < 0
                ) {
                    findIndex = rightIndex;
                }
                if (index !== findIndex) {
                    this.swap(index, findIndex);//交换当前元素和左右节点中value小的
                    index = findIndex;
                } else {
                    break;
                }
            }
        }
    
        swap(index1, index2) {//交换堆中两个元素的位置
            [this.data[index1], this.data[index2]] = [this.data[index2], this.data[index1]];
        }
    
        size() {
            return this.data.length;
        }
    }
    

    java:

    class KthLargest {
        PriorityQueue<Integer> pq;
        int k;
    
        public KthLargest(int k, int[] nums) {
            this.k = k;
            pq = new PriorityQueue<Integer>();
            for (int x : nums) {
                add(x);
            }
        }
        
        public int add(int val) {
            pq.offer(val);
            if (pq.size() > k) {
                pq.poll();
            }
            return pq.peek();
        }
    }
    
    

    23. 合并K个升序链表 (hard)

    方法1.分治
    ds_33
    • 思路:自底而上归并,第一次归并2个链表,第二次归并4个链表...,每次归并不断合并两个有序链表,直到合并完所有分治后的链表
    • 复杂度:时间复杂度O(n * logk),n是每个链表节点数,k个链表个数,每次归并,链表数量较少一半,复杂度是O(logk),将两个链表合并成一个顺序链表复杂度是O(2n),所以时间复杂度是 O(n * logk)。空间复杂度是O(logk),即递归的空格复杂度

    js:

    //自顶而下归并 先分在合
    var mergeKLists = function (lists) {
        // 当是空数组的情况下
        if (!lists.length) {
            return null;
        }
        // 合并两个排序链表
        const merge = (head1, head2) => {
            let dummy = new ListNode(0);
            let cur = dummy;
            // 新链表,新的值小就先接谁
            while (head1 && head2) {
                if (head1.val < head2.val) {
                    cur.next = head1;
                    head1 = head1.next;
                } else {
                    cur.next = head2;
                    head2 = head2.next;
                }
                cur = cur.next;
            }
            // 如果后面还有剩余的就把剩余的接上
            cur.next = head1 == null ? head2 : head1;
            return dummy.next;
        };
        const mergeLists = (lists, start, end) => {
            if (start + 1 == end) {
                return lists[start];
            }
            // 输入的k个排序链表,可以分成两部分,前k/2个链表和后k/2个链表
            // 如果将这前k/2个链表和后k/2个链表分别合并成两个排序的链表,再将两个排序的链表合并,那么所有链表都合并了
            let mid = (start + end) >> 1;
            let head1 = mergeLists(lists, start, mid);
            let head2 = mergeLists(lists, mid, end);
            return merge(head1, head2);
        };
        return mergeLists(lists, 0, lists.length);
    };
    
    //自底而上合并
    var mergeKLists = function (lists) {
        if (lists.length <= 1) return lists[0] || null;//当归并的节点只有一个时 返回这个节点
        const newLists = [];
        //自底而上归并,第一次归并大小为2的链表,第二次归并大小4的链表...
        for (let i = 0; i < lists.length; i += 2) {
            newLists.push(merge(lists[i], lists[i + 1] || null));
        }
        return mergeKLists(newLists);
    };
    
    const merge = (list_1, list_2) => {//合并两个有序链表
        const dummyNode = new ListNode(0);
        let p = dummyNode;
    
        while (list_1 && list_2) {
            if (list_1.val < list_2.val) {//先将小的节点加入
                p.next = list_1;
                list_1 = list_1.next;
            } else {
                p.next = list_2;
                list_2 = list_2.next;
            }
            p = p.next;
        }
    
        p.next = list_1 ? list_1 : list_2;//遍历完成还有节点剩余
        return dummyNode.next;
    };
    

    java:

    class Solution {
        public ListNode mergeKLists(ListNode[] lists) {
            return mergeLists(lists, 0, lists.length - 1);
        }
    
        public ListNode mergeLists(ListNode[] lists, int start, int end) {
            if (start == end) {
                return lists[start];
            }
            if (start > end) {
                return null;
            }
            int mid = (start + end) >> 1;
            return merge(mergeLists(lists, start, mid), mergeLists(lists, mid + 1, end));
        }
    
        public ListNode merge(ListNode head1, ListNode head2) {
            if (head1 == null || head2 == null) {
                return head1 != null ? head1 : head2;
            }
            ListNode dummyNode = new ListNode(0);
            ListNode cur = dummyNode;
            while (head1 != null && head2 != null) {
                if (head1.val < head2.val) {
                    cur.next = head1;
                    head1 = head1.next;
                } else {
                    cur.next = head2;
                    head2 = head2.next;
                }
                cur = cur.next;
            }
            cur.next = (head1 != null ? head1 : head2);
            return dummyNode.next;
        }
    }
    
    方法2.优先队列
    ds_34
    • 思路:新建小顶堆,小顶堆的大小是k,不断从每个链表的头节点开始不断加入小顶堆中,然后取出堆顶值,也就是最小值,然后继续往小顶堆中插入这个最小值在链表的next节点
    • 复杂度:时间复杂度O(kn * logk),优先队列的大小是k,每次插入和删除是O(logk),总共k * n的节点个数,每个节点插入删除一次,所以总的复杂度是O(kn*logk)。空间复杂度是O(k),即优先队列的大小

    js:

    class Heap {    constructor(comparator = (a, b) => a - b, data = []) {        this.data = data;        this.comparator = comparator;//比较器        this.heapify();//堆化    }    heapify() {        if (this.size() < 2) return;        for (let i = Math.floor(this.size()/2)-1; i >= 0; i--) {            this.bubbleDown(i);//bubbleDown操作        }    }    peek() {        if (this.size() === 0) return null;        return this.data[0];//查看堆顶    }    offer(value) {        this.data.push(value);//加入数组        this.bubbleUp(this.size() - 1);//调整加入的元素在小顶堆中的位置    }    poll() {        if (this.size() === 0) {            return null;        }        const result = this.data[0];        const last = this.data.pop();        if (this.size() !== 0) {            this.data[0] = last;//交换第一个元素和最后一个元素            this.bubbleDown(0);//bubbleDown操作        }        return result;    }    bubbleUp(index) {        while (index > 0) {            const parentIndex = (index - 1) >> 1;//父节点的位置            //如果当前元素比父节点的元素小,就交换当前节点和父节点的位置            if (this.comparator(this.data[index], this.data[parentIndex]) < 0) {                this.swap(index, parentIndex);//交换自己和父节点的位置                index = parentIndex;//不断向上取父节点进行比较            } else {                break;//如果当前元素比父节点的元素大,不需要处理            }        }    }    bubbleDown(index) {        const lastIndex = this.size() - 1;//最后一个节点的位置        while (true) {            const leftIndex = index * 2 + 1;//左节点的位置            const rightIndex = index * 2 + 2;//右节点的位置            let findIndex = index;//bubbleDown节点的位置            //找出左右节点中value小的节点            if (                leftIndex <= lastIndex &&                this.comparator(this.data[leftIndex], this.data[findIndex]) < 0            ) {                findIndex = leftIndex;            }            if (                rightIndex <= lastIndex &&                this.comparator(this.data[rightIndex], this.data[findIndex]) < 0            ) {                findIndex = rightIndex;            }            if (index !== findIndex) {                this.swap(index, findIndex);//交换当前元素和左右节点中value小的                index = findIndex;            } else {                break;            }        }    }    swap(index1, index2) {//交换堆中两个元素的位置        [this.data[index1], this.data[index2]] = [this.data[index2], this.data[index1]];    }    size() {        return this.data.length;    }}var mergeKLists = function(lists) {    const res = new ListNode(0);    let p = res;    const h = new Heap(comparator = (a, b) => a.val - b.val);    lists.forEach(l => {//插入每个链表的第一个节点        if(l) h.offer(l);    })    while(h.size()) {//        const n = h.poll();//取出最小值        p.next = n;//最小值加入p的next后        p = p.next;//移动p节点        if(n.next) h.offer(n.next);//插入最小节点的后一个节点    }    return res.next;};
    

    java:

    class Solution {
        class Status implements Comparable<Status> {
            int val;
            ListNode ptr;
    
            Status(int val, ListNode ptr) {
                this.val = val;
                this.ptr = ptr;
            }
    
            public int compareTo(Status status2) {
                return this.val - status2.val;
            }
        }
    
        PriorityQueue<Status> h = new PriorityQueue<Status>();
    
        public ListNode mergeKLists(ListNode[] lists) {
            for (ListNode node: lists) {
                if (node != null) {
                    h.offer(new Status(node.val, node));
                }
            }
            ListNode res = new ListNode(0);
            ListNode p = res;
            while (!h.isEmpty()) {
                Status n = h.poll();
                p.next = n.ptr;
                p = p.next;
                if (n.ptr.next != null) {
                    h.offer(new Status(n.ptr.next.val, n.ptr.next));
                }
            }
            return res.next;
        }
    }
    
    

    347. 前 K 个高频元素 (medium)

    方法1:优先队列
    ds_127
    • 思路:循环数组,加入小顶堆,当堆的size超过k时,出堆,循环完成之后,堆中所有的元素就是前k大的数字
    • 复杂度:时间复杂度O(nlogk),循环n次,每次堆的操作是O(logk)。空间复杂度O(k)

    js:

    class Heap {
        constructor(comparator = (a, b) => a - b, data = []) {
            this.data = data;
            this.comparator = comparator;//比较器
            this.heapify();//堆化
        }
    
        heapify() {
            if (this.size() < 2) return;
            for (let i = Math.floor(this.size()/2)-1; i >= 0; i--) {
                this.bubbleDown(i);//bubbleDown操作
            }
        }
    
        peek() {
            if (this.size() === 0) return null;
            return this.data[0];//查看堆顶
        }
    
        offer(value) {
            this.data.push(value);//加入数组
            this.bubbleUp(this.size() - 1);//调整加入的元素在小顶堆中的位置
        }
    
        poll() {
            if (this.size() === 0) {
                return null;
            }
            const result = this.data[0];
            const last = this.data.pop();
            if (this.size() !== 0) {
                this.data[0] = last;//交换第一个元素和最后一个元素
                this.bubbleDown(0);//bubbleDown操作
            }
            return result;
        }
    
        bubbleUp(index) {
            while (index > 0) {
                const parentIndex = (index - 1) >> 1;//父节点的位置
                //如果当前元素比父节点的元素小,就交换当前节点和父节点的位置
                if (this.comparator(this.data[index], this.data[parentIndex]) < 0) {
                    this.swap(index, parentIndex);//交换自己和父节点的位置
                    index = parentIndex;//不断向上取父节点进行比较
                } else {
                    break;//如果当前元素比父节点的元素大,不需要处理
                }
            }
        }
    
        bubbleDown(index) {
            const lastIndex = this.size() - 1;//最后一个节点的位置
            while (true) {
                const leftIndex = index * 2 + 1;//左节点的位置
                const rightIndex = index * 2 + 2;//右节点的位置
                let findIndex = index;//bubbleDown节点的位置
                //找出左右节点中value小的节点
                if (
                    leftIndex <= lastIndex &&
                    this.comparator(this.data[leftIndex], this.data[findIndex]) < 0
                ) {
                    findIndex = leftIndex;
                }
                if (
                    rightIndex <= lastIndex &&
                    this.comparator(this.data[rightIndex], this.data[findIndex]) < 0
                ) {
                    findIndex = rightIndex;
                }
                if (index !== findIndex) {
                    this.swap(index, findIndex);//交换当前元素和左右节点中value小的
                    index = findIndex;
                } else {
                    break;
                }
            }
        }
    
        swap(index1, index2) {//交换堆中两个元素的位置
            [this.data[index1], this.data[index2]] = [this.data[index2], this.data[index1]];
        }
    
        size() {
            return this.data.length;
        }
    }
    
    var topKFrequent = function (nums, k) {
        const map = new Map();
    
        for (const num of nums) {//统计频次
            map.set(num, (map.get(num) || 0) + 1);
        }
    
        //创建小顶堆
        const priorityQueue = new Heap((a, b) => a[1] - b[1]);
    
        //entry 是一个长度为2的数组,0位置存储key,1位置存储value
        for (const entry of map.entries()) {
            priorityQueue.offer(entry);//加入堆
            if (priorityQueue.size() > k) {//堆的size超过k时,出堆
                priorityQueue.poll();
            }
        }
    
        const ret = [];
    
        for (let i = priorityQueue.size() - 1; i >= 0; i--) {//取出前k大的数
            ret[i] = priorityQueue.poll()[0];
        }
    
        return ret;
    };
    
    
    
    

    java:

    class Solution {
        public int[] topKFrequent(int[] nums, int k) {
            int[] ret = new int[k];
            HashMap<Integer, Integer> map = new HashMap<>();
            for (int num : nums) {
                map.put(num, map.getOrDefault(num, 0) + 1);
            }
    
            Set<Map.Entry<Integer, Integer>> entries = map.entrySet();
            PriorityQueue<Map.Entry<Integer, Integer>> priorityQueue = new PriorityQueue<>((o1, o2) -> o1.getValue() - o2.getValue());
            for (Map.Entry<Integer, Integer> entry : entries) {
                priorityQueue.offer(entry);
                if (priorityQueue.size() > k) {
                    priorityQueue.poll();
                }
            }
            for (int i = k - 1; i >= 0; i--) {
                ret[i] = priorityQueue.poll().getKey();
            }
            return ret;
        }
    }
    

    692. 前K个高频单词(medium)

    方法1:排序

    js:

    var topKFrequent = function (words, k) {
        const map = {};
        words.forEach(v => map[v] = (map[v] || 0) + 1);
        const keys = Object.keys(map).sort((a, b) => map[b] - map[a] || a.localeCompare(b))
        return keys.slice(0, k);
    };
    
    方法2:堆

    js:

    class Heap {
        constructor(comparator = (a, b) => a - b, data = []) {
            this.data = data;
            this.comparator = comparator;//比较器
            this.heapify();//堆化
        }
    
        heapify() {
            if (this.size() < 2) return;
            for (let i = Math.floor(this.size()/2)-1; i >= 0; i--) {
                this.bubbleDown(i);//bubbleDown操作
            }
        }
    
        peek() {
            if (this.size() === 0) return null;
            return this.data[0];//查看堆顶
        }
    
        offer(value) {
            this.data.push(value);//加入数组
            this.bubbleUp(this.size() - 1);//调整加入的元素在小顶堆中的位置
        }
    
        poll() {
            if (this.size() === 0) {
                return null;
            }
            const result = this.data[0];
            const last = this.data.pop();
            if (this.size() !== 0) {
                this.data[0] = last;//交换第一个元素和最后一个元素
                this.bubbleDown(0);//bubbleDown操作
            }
            return result;
        }
    
        bubbleUp(index) {
            while (index > 0) {
                const parentIndex = (index - 1) >> 1;//父节点的位置
                //如果当前元素比父节点的元素小,就交换当前节点和父节点的位置
                if (this.comparator(this.data[index], this.data[parentIndex]) < 0) {
                    this.swap(index, parentIndex);//交换自己和父节点的位置
                    index = parentIndex;//不断向上取父节点进行比较
                } else {
                    break;//如果当前元素比父节点的元素大,不需要处理
                }
            }
        }
    
        bubbleDown(index) {
            const lastIndex = this.size() - 1;//最后一个节点的位置
            while (true) {
                const leftIndex = index * 2 + 1;//左节点的位置
                const rightIndex = index * 2 + 2;//右节点的位置
                let findIndex = index;//bubbleDown节点的位置
                //找出左右节点中value小的节点
                if (
                    leftIndex <= lastIndex &&
                    this.comparator(this.data[leftIndex], this.data[findIndex]) < 0
                ) {
                    findIndex = leftIndex;
                }
                if (
                    rightIndex <= lastIndex &&
                    this.comparator(this.data[rightIndex], this.data[findIndex]) < 0
                ) {
                    findIndex = rightIndex;
                }
                if (index !== findIndex) {
                    this.swap(index, findIndex);//交换当前元素和左右节点中value小的
                    index = findIndex;
                } else {
                    break;
                }
            }
        }
    
        swap(index1, index2) {//交换堆中两个元素的位置
            [this.data[index1], this.data[index2]] = [this.data[index2], this.data[index1]];
        }
    
        size() {
            return this.data.length;
        }
    }
    
    var topKFrequent = function (nums, k) {
        const map = new Map();
    
        for (const num of nums) {//统计频次
            map.set(num, (map.get(num) || 0) + 1);
        }
    
        //创建小顶堆
        const priorityQueue = new Heap((a, b) => {
            return a[1] === b[1] ? b[0].localeCompare(a[0]) : a[1] - b[1]
        });
    
        //entry 是一个长度为2的数组,0位置存储key,1位置存储value
        for (const entry of map.entries()) {
            priorityQueue.offer(entry);//加入堆
            if (priorityQueue.size() > k) {//堆的size超过k时,出堆
                priorityQueue.poll();
            }
        }
    
        const ret = [];
    
        for (let i = priorityQueue.size() - 1; i >= 0; i--) {//取出前k大的数
            ret[i] = priorityQueue.poll()[0];
        }
    
        return ret;
    };
    
    

    933. 最近的请求次数 (easy)

    • 思路:将请求加入队列,如果队头元素请求的时间在[t-3000,t]之外 就不断出队
    • 复杂度:时间复杂度O(q),q是ping的次数。空间复杂度O(w),w是队列中最多的元素个数

    js:

    var RecentCounter = function() {
        this.queue = []
    };
    
    RecentCounter.prototype.ping = function(t) {
        this.queue.push(t);//新请求入队
        const time = t-3000;//计算3000ms前的时间
        while(this.queue[0] < time){//如果队头元素请求的时间在[t-3000,t]之外 就不断出队
            this.queue.shift();
        }
        return this.queue.length;//在[t-3000,t]区间内队列剩余的元素就是符合要求的请求数
    };
    
    

    java:

    class RecentCounter {
        Queue<Integer> q;
        public RecentCounter() {
            q = new LinkedList();
        }
    
        public int ping(int t) {
            q.add(t);
            while (q.peek() < t - 3000)
                q.poll();
            return q.size();
        }
    }
    
    

    622. 设计循环队列 (medium)

    • 复杂度:时间复杂度O(1),空间复杂度O(k)
    ds_200

    js:

    var MyCircularQueue = function(k) {
        this.front = 0
        this.rear = 0
        this.max = k
        this.list = Array(k)
    };
    
    MyCircularQueue.prototype.enQueue = function(value) {
        if(this.isFull()) {
            return false
        } else {
            this.list[this.rear] = value
            this.rear = (this.rear + 1) % this.max
            return true
        }
    };
    
    MyCircularQueue.prototype.deQueue = function() {
        let v = this.list[this.front]
        this.list[this.front] = undefined
        if(v !== undefined ) {
            this.front = (this.front + 1) % this.max
            return true
        } else {
            return false
        }
    };
    
    MyCircularQueue.prototype.Front = function() {
        if(this.list[this.front] === undefined) {
            return -1
        } else {
            return this.list[this.front]
        }
    };
    
    MyCircularQueue.prototype.Rear = function() {
        let rear = this.rear - 1
        if(this.list[rear < 0 ? this.max - 1 : rear] === undefined) {
            return -1
        } else {
            return this.list[rear < 0 ? this.max - 1 : rear] 
        }
    };
    
    MyCircularQueue.prototype.isEmpty = function() {
        return this.front === this.rear && !this.list[this.front]
    };
    
    MyCircularQueue.prototype.isFull = function() {
        return (this.front === this.rear) && !!this.list[this.front]
    };
    
    
    

    相关文章

      网友评论

          本文标题:大厂算法面试之leetcode精讲18.队列

          本文链接:https://www.haomeiwen.com/subject/afrpxrtx.html