特殊数据结构

一、二叉堆和优先级队列

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、并查集

突然发现大学会好多...

猜你喜欢

转载自www.cnblogs.com/naonaoling/p/12405421.html