一、二叉堆和优先级队列
1、特性简介和应用
二叉堆:一种存储在数组中的二叉树(建堆:下沉)
优先级队列:本质是二叉堆,主要两种操作:删除(下沉)、插入(上浮)
注意二叉堆仅能查找最小/大值,不能保证堆有序;优先级队列仅能查找/删除最值
2、建堆示例:
主要涉及到下沉,几个注意点
(1)正常来说左子应该是2*i,但数组下标是从0开始,所以左子2*i+1,右子2*i+2
(2)从第一个非叶子结点处理,即n/2-1,n为数组长度,实际应用画一下即可
(3)如果右子比左子小,用右子交换(其实是用最小去做交换,省的后续处理麻烦)
(4)交换完之后可能会导致该结点的子树不再有序,一并调整(while循环),如果调整完记得提前break
1 void buildHeap(int[] nums){ 2 int n = nums.length; 3 for(int i = n/2 - 1;i>=0; i--){ 4 System.out.println(nums[i]); 5 moveDown(nums, i); 6 } 7 } 8 9 void moveDown(int[] nums, int i){ 10 int maxIndex = nums.length-1; 11 int j = 2*i+1; // 左 12 while(j<=maxIndex) { 13 if(j+1 <= maxIndex && nums[j+1]<nums[j]){ // 如果右子比左子小,用右子交换(用最小的交换,省的后续处理麻烦) 14 j=j+1; 15 } 16 if(nums[j]<nums[i]){ 17 swap(nums, i, j); 18 i=j; //别忘了! 19 j=2*j+1; 20 } else{ 21 break; 22 } 23 } 24 }
3、优先级队列的删除和插入:
删除:把最后一个元素放入nums[0],之后对其进行下沉,即调用一次sink即可
插入:把元素插入到数组最后,之后对其进行上浮(和父结点对比交换,直到结束--到root或者不再符合交换条件)
面试题 17.14. 最小K个数 优先级队列的应用
1 class Solution { 2 public int[] smallestK(int[] arr, int k) { 3 int len = arr.length; 4 int[] res = new int[k]; 5 if(len == 0 || len < k || k == 0){ 6 return res; 7 } 8 9 PriorityQueue<Integer> maxHeap = new PriorityQueue<>(k, new Comparator<Integer>(){ 10 @Override 11 public int compare(Integer o1, Integer o2){ 12 return o2.compareTo(o1); // 逆序 13 } 14 }); 15 16 for(int i = 0; i < len ; i++) { 17 if(maxHeap.size() < k){ 18 maxHeap.offer(arr[i]); 19 } else{ 20 if(maxHeap.peek()>arr[i]){ 21 maxHeap.poll(); 22 maxHeap.offer(arr[i]); 23 } 24 } 25 } 26 int i = 0; 27 for(Integer item : maxHeap){ 28 res[i]=item; 29 i++; 30 } 31 return res; 32 } 33 }
二、单调栈
1、代码框架:
从后遍历:栈的属性后进先出。而查找某一个元素的next是要前往后找,即从栈顶找,所以栈顶保存的应该是最近的。
1 for(int i = nums.length-1; i>=0; i--){ 2 while(!stack.empty() && stack.peek() <= nums[i]){ 3 stack.pop(); 4 } 5 int next = stack.empty()? -1 : stack.peek(); 6 map.put(nums[i], next); 7 stack.push(nums[i]); 8 }
2、主要应用:Next Greater Element(找下一个比它大的元素)
应用范围比较窄,主要处理这一类问题
496. Next Greater Element I
1 class Solution { 2 public int[] nextGreaterElement(int[] nums1, int[] nums2) { 3 int[] res = new int[nums1.length]; 4 Stack<Integer> stack = new Stack<>(); 5 Map<Integer, Integer> map = new HashMap<>(); 6 for(int i = nums2.length-1; i>=0; i--){ 7 while(!stack.empty() && stack.peek() <= nums2[i]){ 8 stack.pop(); 9 } 10 int next = stack.empty()? -1 : stack.peek(); 11 map.put(nums2[i], next); 12 stack.push(nums2[i]); 13 } 14 for(int i=0; i<nums1.length; i++){ 15 res[i]=map.get(nums1[i]); 16 } 17 return res; 18 } 19 }
739. Daily Temperatures
变体,返回的是索引的距离。注意细节,细节害死人。
1 class Solution { 2 public int[] dailyTemperatures(int[] T) { 3 int[] res = new int[T.length]; 4 Stack<Integer> stack = new Stack<>(); 5 for(int i = T.length-1; i >=0 ; i--){ 6 while(!stack.empty() && T[stack.peek()] <= T[i]){ 7 stack.pop(); 8 } 9 int diff = stack.empty() ? 0 : stack.peek()-i; 10 res[i] = diff; 11 stack.push(i); 12 } 13 return res; 14 } 15 }
3、环形数组
数组成环,也就是说cur的next可能在其右侧,也可能再其左侧。
503. Next Greater Element II
思路:
把数组拉长为原来的两倍,即重复两遍,然后按照原思路来找,但是只返回前半截。
因为后半截其实就是前半截的重复,所以元素是一样的,可以不用真实拉长,通过取模找到相应的元素,res其实会相应的赋值两遍。
1 class Solution { 2 public int[] nextGreaterElements(int[] nums) { 3 int n = nums.length; 4 int[] res = new int[n]; 5 Stack<Integer> stack = new Stack<>(); 6 for(int i = 2*n - 1; i >= 0; i--){ 7 while(!stack.empty() && stack.peek() <= nums[i%n]){ 8 stack.pop(); 9 } 10 int next = stack.empty() ? -1 : stack.peek(); 11 res[i%n] = next; 12 stack.push(nums[i%n]); 13 } 14 return res; 15 } 16 }
2、单调队列
4、队列和栈互相实现
5、LRU done
6、并查集
突然发现大学会好多...