Series article guide
- Kiner algorithm brush topic (a) : linked list and linked list ideas
- Kiner algorithm: Recursion and stack (solve the expression evaluation problem)
- Kiner algorithm brush topic (3) : thread pool and task queue
- Do you really know binary trees?
- Do you really know binary trees?
- Kiner algorithm: Heap and Priority queue
- Kiner algorithm brush topic (five) : Heap (priority queue)
The open source project
All articles in this series will be collected and managed in GitHub. Welcome ISSUE and Star.
GitHub portal: Kiner algorithm
preface
In our last article, we talked about the concept and properties of heap. I believe that you have a preliminary understanding of the data structure like heap. In order to deepen your understanding and familiarize yourself with the application scenarios of heap, I will take you to brush some algorithm problems to deepen your memory. Start with Heap and priority queues (Basics of Data Structures).
PS: their thinking is not necessarily the following algorithm to solve the optimal algorithm of train of thought, however, in order to consolidate the understanding of the use and characteristics of pile, all of us are achieved through the method of the heap, some topic do not use the heap, using other methods may be more efficient to complete the task, the classmate of your own thinking.
LeetCode refers to Offer 40.The smallest number of k
Their thinking
How do we use the heap to solve this problem? We said that the heap is suitable for maintaining maxima in sets, and we should use a big top heap to compare maxima in sets in real time. So let’s say that we have a set of length K, and we have all the numbers that we want to put in the set, and if the set is less than or equal to k, then we just add it, If length is greater than k, then we will compare the value of the entrance to the end than the maximum of a set of big or small, if is greater than the value of a set of, this is not what we want, put it to remove oil from the collection, if is smaller than the maximum value, then the value should be added to the collection, but because we as long as the number of k, So you have to pop the maximum out.
PS: Heap implementation is explained in detail in the last article, so I will not repeat it here. If there is any confusion, you can take a look at the last article
Code demo
// The implementation of the big top heap was explained in detail in the previous article, so it will not be repeated here. If there is any confusion, please refer to the previous article
class Heap {
private count: number = 0;
private arr: number[] = [];
constructor(){}
_shiftUp(idx: number) :void {
while(idx && this.arr[parseInt(String((idx - 1) / 2))] < this.arr[idx]) {
const pIdx = parseInt(String((idx - 1) / 2));
[this.arr[idx], this.arr[pIdx]] = [this.arr[pIdx], this.arr[idx]];
idx = pIdx;
}
}
_shiftDown(idx: number) :void {
const n = this.count - 1;
while(2*idx+1<=n) {
let max = idx;
if(this.arr[max] < this.arr[2*idx+1]) max = 2*idx+1;
if(2*idx+2<=n && this.arr[max] < this.arr[2*idx+2]) max = 2*idx+2;
if(max === idx) break;
[this.arr[max], this.arr[idx]] = [this.arr[idx], this.arr[max]];
idx = max;
}
}
push(x: number) :void {
this.arr[this.count++] = x;
this._shiftUp(this.count - 1);
}
pop(): number {
if(!this.size()) return -1;
const tmp = this.arr[0];
this.arr[0] = this.arr[this.count-1];
this.count--;
this._shiftDown(0);
return tmp;
}
size(): number {
return this.count;
}
top(): number {
return this.arr[0];
}
getArray(): number[] {
// Since we have no real physical deletion, we need to intercept this.count elements when we retrieve the array of the actual heap
return this.arr.slice(0.this.count); }}function getLeastNumbers(arr: number[], k: number) :number[] {
const heap = new Heap();
arr.forEach(item= >{
// Add each element of the array to the heap
heap.push(item);
// If the heap size is greater than k, pop the maximum value
if(heap.size()>k) { heap.pop(); }});// At the end of the loop, the heap contains the KTH smallest array we want
return heap.getArray();
};
Copy the code
LeetCode 1046. The weight of the last stone
Their thinking
This problem is also obviously to set the value of the problem, we just need to put all the stones in large heap, if the heap of stones is greater than 1, the number of every time out of the largest stone crushing and large rocks, if have the rest of the will add to the pile, as at the end of the cycle, or in the heap is lost a stone, or only one, If only one is left, the weight of the stone is what we want
Code demo
/* * @lc app=leetcode.cn id=1046 lang=typescript * * [1046] Weight of the last stone */
// @lc code=start
class Heap {
private count: number = 0;
private arr: number[] = [];
constructor(){}
_shiftUp(idx: number) :void {
while(idx && this.arr[parseInt(String((idx - 1) / 2))] < this.arr[idx]) {
const pIdx = parseInt(String((idx - 1) / 2));
[this.arr[idx], this.arr[pIdx]] = [this.arr[pIdx], this.arr[idx]];
idx = pIdx;
}
}
_shiftDown(idx: number) :void {
const n = this.count - 1;
while(2*idx+1<=n) {
let max = idx;
if(this.arr[max] < this.arr[2*idx+1]) max = 2*idx+1;
if(2*idx+2<=n && this.arr[max] < this.arr[2*idx+2]) max = 2*idx+2;
if(max === idx) break;
[this.arr[max], this.arr[idx]] = [this.arr[idx], this.arr[max]];
idx = max;
}
}
push(x: number) :void {
this.arr[this.count++] = x;
this._shiftUp(this.count - 1);
}
pop(): number {
if(!this.size()) return -1;
const tmp = this.arr[0];
this.arr[0] = this.arr[this.count-1];
this.count--;
this._shiftDown(0);
return tmp;
}
size(): number {
return this.count;
}
top(): number {
return this.arr[0];
}
getArray(): number[] {
// Since we have no real physical deletion, we need to intercept this.count elements when we retrieve the array of the actual heap
return this.arr.slice(0.this.count); }}function lastStoneWeight(stones: number[]) :number {
const heap = new Heap();
// Put all the stones in the big top pile first
stones.forEach(stone= > heap.push(stone));
// If the number of stones in the heap is greater than 1
while(heap.size()>1) {
// Pop out the largest stone and the second largest stone
const s1 = heap.pop();
const s2 = heap.pop();
// If it is equal, nothing is done
if(s1 == s2) continue;
// Otherwise put the number of remaining stones back into the heap
heap.push(s1-s2);
}
// If there are no elements in the heap, return 0
if(heap.size()===0) return 0;
// Otherwise return the heaptop element
return heap.top();
};
// @lc code=end
Copy the code
LeetCode 703. The KTH largest element in the data stream
Their thinking
If we want to find the KTH largest number in a set, then at least the element we add to the set can’t be smaller than the smallest element in the set. So whenever we get full, we’re going to strip out the smallest element, so to compare minima, we’re going to use the small top heap this time
Code demo
/* * @lc app=leetcode.cn id=703 lang=typescript * * the KTH element in the [703] data stream */
// @lc code=start
// The following is a definition of a structure that allows us to quickly create a large or small top heap. If you don't understand this, you can refer to the previous article, which explained it in detail.
export typeHeapDataStruct<T> = { idx? :number.data: T
};
export type CompareFn<T> = (x: HeapDataStruct<T>, y: HeapDataStruct<T>) = > boolean;
class Heap<T> {
private arr: HeapDataStruct<T>[] = [];
private count: number = 0;
constructor(private cmpFn: CompareFn<T>){}private _compare(curIdx: number.pIdx: number) :boolean {
return this.cmpFn(this.arr[curIdx], this.arr[pIdx])
}
private _shift_up(idx: number) :void {
// Then adjust the heap upward
// We need the coordinates of the parent node from the child node, because our subscript starts from 0, so the left child root node coordinates are: 2 * I + 1, and the right child root node coordinates are: 2 * I + 2. ParseInt ((2 * I + 1-1) / 2) = parseInt(I); parseInt((I + 1-1) / 2) = parseInt(I); ParseInt ((2* I + 2-1)/2) = parseInt((2* I +1)/2) = I
// As we said above, we need to swap the two values if the parent is less than the child in a big top heap
// while(idx && this.arr[parseInt(String((idx - 1) / 2))].data < this.arr[idx].data) {
while(idx && this._compare(parseInt(String((idx - 1) / 2)), idx)) {
// Number of the parent node
const pIdx = parseInt(String((idx - 1) / 2));
// Swap the parent node and the current node
[ this.arr[pIdx], this.arr[idx] ] = [ this.arr[idx], this.arr[pIdx] ];
// Then change the idx to the number of the parent node, so that it can be adjusted upwardidx = pIdx; }}private _shift_down(idx: number) :void {
// After the swap, the root node is adjusted down in turn
// The subscript of the largest child node
let n = this.count - 1;
// If the subscript of the idX child node is smaller than the subscript of the largest child node, it indicates that there are children
while(idx * 2 + 1 <= n) {
// Max represents the subscript of the maximum value in the root node, left subtree, and right subtree
let max = idx;
If the value of the root node of the left subtree is greater than the current value, update Max to the subscript of the left child root node
// if(this.arr[max].data < this.arr[2*idx+1].data) max = 2*idx+1;
if(this._compare(max, 2*idx+1)) max = 2*idx+1;
If the value of the right child root node is greater than the current node, update Max to the subscript of the right child root node
*idx+2<=n *idx+2<=n *idx+2<=n
if(2*idx+2<=n && this._compare(max, 2*idx+2)) max = 2*idx+2;
// if(2*idx+2<=n && this.arr[max].data < this.arr[2*idx+2].data) max = 2*idx+2;
// If the index of my maximum value is the current value, I don't need to adjust it down, and just end the loop
if(max === idx) break;
// Then swap the subscript and root of the maximum value
[ this.arr[idx], this.arr[max] ] = [ this.arr[max], this.arr[idx] ];
// Then change idx to the subscript of the original maximum value and continue to adjust downward
idx = max;
}
}
push(item: HeapDataStruct<T>): void {
this.arr[this.count++] = item;
this._shift_up(this.count-1);
}
pop(): HeapDataStruct<T>|null {
if(this.size()===0) return null;
const tmp = this.arr[0];
// As explained above, we first need to swap the end of the queue with the root node
[ this.arr[0].this.arr[this.count-1]] = [this.arr[this.count-1].this.arr[0]].// Remember to decrease count by 1 after the swap
this.count--;
// Make a downward adjustment
this._shift_down(0);
return tmp;
}
size(): number {
return this.count;
}
top(): HeapDataStruct<T> {
return this.arr[0];
}
output(): void {
console.log(JSON.stringify(this.arr)); }}class KthLargest {
// If we want to find the KTH largest number in a set, then at least the element we add to the set cannot be smaller than the smallest element in the set
// So whenever the collection is full, we have to strip out the smallest element. To compare minima, we use the small top heap this time
private heap = new Heap<number> ((x,y) = >x.data>y.data);
private k: number;
constructor(k: number, nums: number[]) {
this.k = k;
// Push all the elements of the array into the heap
nums.forEach(num= > this.add(num));
}
add(val: number) :number {
this.heap.push({data: val});
// If the heap size exceeds k, pop the minimum value
if(this.heap.size()>this.k) {
this.heap.pop();
}
// Return the top element of the heap as the KTH value we want
return this.heap.top().data; }}/** * Your KthLargest object will be instantiated and called as such: * var obj = new KthLargest(k, nums) * var param_1 = obj.add(val) */
// @lc code=end
Copy the code
LeetCode 215. The KTH largest element in the array
Their thinking
We add each element in turn to the heap, and we see if the size of the heap exceeds K, and if it exceeds K, we pop out the minimum, and finally, the top element is what we want
Code demo
/* * @lc app=leetcode.cn id=215 lang=typescript * * the KTH largest element in the [215] array */
// @lc code=start
export typeHeapDataStruct<T> = { idx? :number.data: T
};
export type CompareFn<T> = (x: HeapDataStruct<T>, y: HeapDataStruct<T>) = > boolean;
class Heap<T> {
private arr: HeapDataStruct<T>[] = [];
private count: number = 0;
constructor(private cmpFn: CompareFn<T>){}private _compare(curIdx: number.pIdx: number) :boolean {
return this.cmpFn(this.arr[curIdx], this.arr[pIdx])
}
private _shift_up(idx: number) :void {
// Then adjust the heap upward
// We need the coordinates of the parent node from the child node, because our subscript starts from 0, so the left child root node coordinates are: 2 * I + 1, and the right child root node coordinates are: 2 * I + 2. ParseInt ((2 * I + 1-1) / 2) = parseInt(I); parseInt((I + 1-1) / 2) = parseInt(I); ParseInt ((2* I + 2-1)/2) = parseInt((2* I +1)/2) = I
// As we said above, we need to swap the two values if the parent is less than the child in a big top heap
// while(idx && this.arr[parseInt(String((idx - 1) / 2))].data < this.arr[idx].data) {
while(idx && this._compare(parseInt(String((idx - 1) / 2)), idx)) {
// Number of the parent node
const pIdx = parseInt(String((idx - 1) / 2));
// Swap the parent node and the current node
[ this.arr[pIdx], this.arr[idx] ] = [ this.arr[idx], this.arr[pIdx] ];
// Then change the idx to the number of the parent node, so that it can be adjusted upwardidx = pIdx; }}private _shift_down(idx: number) :void {
// After the swap, the root node is adjusted down in turn
// The subscript of the largest child node
let n = this.count - 1;
// If the subscript of the idX child node is smaller than the subscript of the largest child node, it indicates that there are children
while(idx * 2 + 1 <= n) {
// Max represents the subscript of the maximum value in the root node, left subtree, and right subtree
let max = idx;
If the value of the root node of the left subtree is greater than the current value, update Max to the subscript of the left child root node
// if(this.arr[max].data < this.arr[2*idx+1].data) max = 2*idx+1;
if(this._compare(max, 2*idx+1)) max = 2*idx+1;
If the value of the right child root node is greater than the current node, update Max to the subscript of the right child root node
*idx+2<=n *idx+2<=n *idx+2<=n
if(2*idx+2<=n && this._compare(max, 2*idx+2)) max = 2*idx+2;
// if(2*idx+2<=n && this.arr[max].data < this.arr[2*idx+2].data) max = 2*idx+2;
// If the index of my maximum value is the current value, I don't need to adjust it down, and just end the loop
if(max === idx) break;
// Then swap the subscript and root of the maximum value
[ this.arr[idx], this.arr[max] ] = [ this.arr[max], this.arr[idx] ];
// Then change idx to the subscript of the original maximum value and continue to adjust downward
idx = max;
}
}
push(item: HeapDataStruct<T>): void {
this.arr[this.count++] = item;
this._shift_up(this.count-1);
}
pop(): HeapDataStruct<T>|null {
if(this.size()===0) return null;
const tmp = this.arr[0];
// As explained above, we first need to swap the end of the queue with the root node
[ this.arr[0].this.arr[this.count-1]] = [this.arr[this.count-1].this.arr[0]].// Remember to decrease count by 1 after the swap
this.count--;
// Make a downward adjustment
this._shift_down(0);
return tmp;
}
size(): number {
return this.count;
}
top(): HeapDataStruct<T> {
return this.arr[0];
}
output(): void {
console.log(JSON.stringify(this.arr)); }}function findKthLargest(nums: number[], k: number) :number {
// instantiate a small top heap
const heap = new Heap<number> ((x,y) = > x.data>y.data);
nums.forEach(num= >{
heap.push({data: num});
if(heap.size()>k) { heap.pop(); }});return heap.top().data;
};
// @lc code=end
Copy the code
LeetCode 373. Find and the smallest k-pair number
Their thinking
According to the formula, minimum K… , then use the big top heap, need the maximum K… , then use the small top heap, so build a big top heap, the big top heap is stored in an array of numeric type, if you want to and minimum, calculate and, and then according to: the big top heap with less than, small top heap with greater elements directly judge.
Then make a two-element array out of the two arrays and put it in the heap. When it’s full, pop up the largest element
Code demo
/* * @lc app=leetcode.cn id=373 lang=typescript * * [373] Find and min K pairs of digits */
// @lc code=start
export typeHeapDataStruct<T> = { idx? :number.data: T
};
export type CompareFn<T> = (x: HeapDataStruct<T>, y: HeapDataStruct<T>) = > boolean;
class Heap<T> {
private arr: HeapDataStruct<T>[] = [];
private count: number = 0;
constructor(private cmpFn: CompareFn<T>){}private _compare(curIdx: number.pIdx: number) :boolean {
return this.cmpFn(this.arr[curIdx], this.arr[pIdx])
}
private _shift_up(idx: number) :void {
// Then adjust the heap upward
// We need the coordinates of the parent node from the child node, because our subscript starts from 0, so the left child root node coordinates are: 2 * I + 1, and the right child root node coordinates are: 2 * I + 2. ParseInt ((2 * I + 1-1) / 2) = parseInt(I); parseInt((I + 1-1) / 2) = parseInt(I); ParseInt ((2* I + 2-1)/2) = parseInt((2* I +1)/2) = I
// As we said above, we need to swap the two values if the parent is less than the child in a big top heap
// while(idx && this.arr[parseInt(String((idx - 1) / 2))].data < this.arr[idx].data) {
while(idx && this._compare(parseInt(String((idx - 1) / 2)), idx)) {
// Number of the parent node
const pIdx = parseInt(String((idx - 1) / 2));
// Swap the parent node and the current node
[ this.arr[pIdx], this.arr[idx] ] = [ this.arr[idx], this.arr[pIdx] ];
// Then change the idx to the number of the parent node, so that it can be adjusted upwardidx = pIdx; }}private _shift_down(idx: number) :void {
// After the swap, the root node is adjusted down in turn
// The subscript of the largest child node
let n = this.count - 1;
// If the subscript of the idX child node is smaller than the subscript of the largest child node, it indicates that there are children
while(idx * 2 + 1 <= n) {
// Max represents the subscript of the maximum value in the root node, left subtree, and right subtree
let max = idx;
If the value of the root node of the left subtree is greater than the current value, update Max to the subscript of the left child root node
// if(this.arr[max].data < this.arr[2*idx+1].data) max = 2*idx+1;
if(this._compare(max, 2*idx+1)) max = 2*idx+1;
If the value of the right child root node is greater than the current node, update Max to the subscript of the right child root node
*idx+2<=n *idx+2<=n *idx+2<=n
if(2*idx+2<=n && this._compare(max, 2*idx+2)) max = 2*idx+2;
// if(2*idx+2<=n && this.arr[max].data < this.arr[2*idx+2].data) max = 2*idx+2;
// If the index of my maximum value is the current value, I don't need to adjust it down, and just end the loop
if(max === idx) break;
// Then swap the subscript and root of the maximum value
[ this.arr[idx], this.arr[max] ] = [ this.arr[max], this.arr[idx] ];
// Then change idx to the subscript of the original maximum value and continue to adjust downward
idx = max;
}
}
push(item: HeapDataStruct<T>): void {
this.arr[this.count++] = item;
this._shift_up(this.count-1);
// this.output('push: ');
}
pop(): HeapDataStruct<T>|null {
if(this.size()===0) return null;
const tmp = this.arr[0];
// As explained above, we first need to swap the end of the queue with the root node
[ this.arr[0].this.arr[this.count-1]] = [this.arr[this.count-1].this.arr[0]].// Remember to decrease count by 1 after the swap
this.count--;
// Make a downward adjustment
this._shift_down(0);
// this.output('pop: ');
return tmp;
}
size(): number {
return this.count;
}
top(): HeapDataStruct<T> {
return this.arr[0];
}
output(pre: string = "") :void {
console.log(pre+JSON.stringify(this.arr));
}
// Separate the data from the heap into an array
getArray(): T[] {
return this.arr.slice(0.this.count).map(item= >item.data); }}function kSmallestPairs(nums1: number[], nums2: number[], k: number) :number[] []{
// Minimum k... , then use the big top heap, need the maximum K... , then use the small top heap, so
// Create a small top heap. The big top heap contains an array of numeric types.
// The big heap uses less than, and the small heap uses greater
const cmp = (x, y) = > x.data[0] + x.data[1] < y.data[0] + y.data[1];
const heap = new Heap<number[]>(cmp);
nums1.forEach(num1= > {
nums2.forEach(num2= > {
const top = heap.top();
If the size of the heap is less than k and the data we want to add is smaller than the top element of the heap, otherwise we jump out of the current loop
// Why jump out of the loop? Nums1 and nums2 are ordered arrays. If the sum of the current num2 and num1 is greater than the sum of the top elements,
// If nums2 is added to nums1, there is no need to compare the result. Therefore, for performance reasons, if this happens, we can jump out of nums2 loop.
// Continue into the next nums1 loop
if(heap.size() < k || cmp({data: [num1, num2]}, top)) {
// Add data from both arrays to the heap
heap.push({
data: [num1, num2]
});
// If the heap size exceeds k, the maximum value is popped
if(heap.size()>k) heap.pop();
} else {
return false; }}); });// Finally, our heap contains the answers we need
return heap.getArray();
};
// @lc code=end
Copy the code
LeetCode 692. The first K high-frequency words
Their thinking
The answer to this question is: First calculate the frequency of each word, because we want to calculate the frequency up to k, see “how” we are going to think of using a small heap, therefore, we need to build a little heap, however, this problem is a special request, when two words appear frequency is same, need according to the word of the ASCII table sorting, therefore, When we define the comparison function of the small top heap and the final output array, we need to do a special treatment for this case. In js, String provides a very convenient according to ASCII String sort of auxiliary table method. The prototype. LocaleCompare () allows us to easily compare two String ASCII whoever playing small.
Code demo
/* * @lc app=leetcode.cn id=692 lang=typescript * * [692] */
// @lc code=start
export typeHeapDataStruct<T> = { idx? :number.data: T
};
export type CompareFn<T> = (x: HeapDataStruct<T>, y: HeapDataStruct<T>) = > boolean;
class Heap<T> {
private arr: HeapDataStruct<T>[] = [];
private count: number = 0;
constructor(private cmpFn: CompareFn<T>){}private _compare(curIdx: number.pIdx: number) :boolean {
return this.cmpFn(this.arr[curIdx], this.arr[pIdx])
}
private _shift_up(idx: number) :void {
// Then adjust the heap upward
// We need the coordinates of the parent node from the child node, because our subscript starts from 0, so the left child root node coordinates are: 2 * I + 1, and the right child root node coordinates are: 2 * I + 2. ParseInt ((2 * I + 1-1) / 2) = parseInt(I); parseInt((I + 1-1) / 2) = parseInt(I); ParseInt ((2* I + 2-1)/2) = parseInt((2* I +1)/2) = I
// As we said above, we need to swap the two values if the parent is less than the child in a big top heap
// while(idx && this.arr[parseInt(String((idx - 1) / 2))].data < this.arr[idx].data) {
while(idx && this._compare(parseInt(String((idx - 1) / 2)), idx)) {
// Number of the parent node
const pIdx = parseInt(String((idx - 1) / 2));
// Swap the parent node and the current node
[ this.arr[pIdx], this.arr[idx] ] = [ this.arr[idx], this.arr[pIdx] ];
// Then change the idx to the number of the parent node, so that it can be adjusted upwardidx = pIdx; }}private _shift_down(idx: number) :void {
// After the swap, the root node is adjusted down in turn
// The subscript of the largest child node
let n = this.count - 1;
// If the subscript of the idX child node is smaller than the subscript of the largest child node, it indicates that there are children
while(idx * 2 + 1 <= n) {
// Max represents the subscript of the maximum value in the root node, left subtree, and right subtree
let max = idx;
If the value of the root node of the left subtree is greater than the current value, update Max to the subscript of the left child root node
// if(this.arr[max].data < this.arr[2*idx+1].data) max = 2*idx+1;
if(this._compare(max, 2*idx+1)) max = 2*idx+1;
If the value of the right child root node is greater than the current node, update Max to the subscript of the right child root node
*idx+2<=n *idx+2<=n *idx+2<=n
if(2*idx+2<=n && this._compare(max, 2*idx+2)) max = 2*idx+2;
// if(2*idx+2<=n && this.arr[max].data < this.arr[2*idx+2].data) max = 2*idx+2;
// If the index of my maximum value is the current value, I don't need to adjust it down, and just end the loop
if(max === idx) break;
// Then swap the subscript and root of the maximum value
[ this.arr[idx], this.arr[max] ] = [ this.arr[max], this.arr[idx] ];
// Then change idx to the subscript of the original maximum value and continue to adjust downward
idx = max;
}
}
push(item: HeapDataStruct<T>): void {
this.arr[this.count++] = item;
this._shift_up(this.count-1);
// this.output('push: ');
}
pop(): HeapDataStruct<T>|null {
if(this.size()===0) return null;
const tmp = this.arr[0];
// As explained above, we first need to swap the end of the queue with the root node
[ this.arr[0].this.arr[this.count-1]] = [this.arr[this.count-1].this.arr[0]].// Remember to decrease count by 1 after the swap
this.count--;
// Make a downward adjustment
this._shift_down(0);
// this.output('pop: ');
return tmp;
}
size(): number {
return this.count;
}
top(): HeapDataStruct<T> {
return this.arr[0];
}
output(pre: string = "") :void {
console.log(pre+JSON.stringify(this.getArray()));
}
// Separate the data from the heap into an array
getArray(sort: (a: HeapDataStruct<T>, b: HeapDataStruct<T>) = > number = () = > 0): T[] {
return this.arr.slice(0.this.count).sort(sort).map(item= >item.data); }}function topKFrequent(words: string[], k: number) :string[] {
// Define a map to store the number of occurrences of each word. Key is the word and val is the number of occurrences
const map = {};
// Define the comparison rules for the small top heap
const cmp = (x,y) = > {
// If the frequency of the two words is different, because it is a small top heap, then the number of the current node should be greater than the number of the parent node
if(x.idx! ==y.idx)return x.idx>y.idx;
// If two words are equally frequent, we ascribe to them in descending order
return x.data<y.data;
};
// instantiate a small top heap
const heap = new Heap<string>(cmp);
// Count the occurrences of all words
words.forEach(word= >{
map[word]=(map[word]||0) +1;
});
Object.keys(map).forEach(key= >{
// Place each word and its corresponding number in the heap
heap.push({
idx: map[key],
data: key
});
// Pop up the minimum value when it is piled up
if(heap.size()>k) heap.pop();
});
/ / output is the result of the need to sort, in turn, the first is based on occurrences from big to small order, if the number is the same, is | | to the left of expression will be zero,
// Will enter the right-hand expression, which will compare the size of two strings based on local rules (ASCII)
return heap.getArray((a,b) = > {
return (b.idx - a.idx) || a.data.localeCompare(b.data);
});
};
// @lc code=end
Copy the code
LeetCode 295. The median of data streams
Their thinking
To do this, we need to use a structure called the opposite top heap, which is essentially a big top heap on the left and a small top heap on the right, and we can maintain the median of the set of two heaps by looking at the top elements of the big top heap and the small top heap.
Median chrysene ┬┬┬┬┬┬┬┬┬┬┬┬┬┬┬ exponent ┴┴┴┴┴┴┴┴┴┴┴┴┴┴┴ -- our r large top pile small top pile# As shown in the figure above, the top of the big heap and the top of the small heap are both on the dotted line side of the median. We try to keep the number of elements in the big heap and the small heap the same, but when the number of elements in the big heap is too large, we can move the top elements in the big heap to the small heap to make them as even as possible. Thus, when all of our trees are added to the two heaps, if the total number is odd, then the median is the top element of the larger heap of our two heaps, and if the total number is even, it is the average of the two top elements
Copy the code
Code demo
/* * @lc app=leetcode.cn id=295 lang=typescript * * [295] Median of data streams */
// @lc code=start
export typeHeapDataStruct<T> = { idx? :number.data: T
};
export type CompareFn<T> = (x: HeapDataStruct<T>, y: HeapDataStruct<T>) = > boolean;
class Heap<T> {
private arr: HeapDataStruct<T>[] = [];
private count: number = 0;
constructor(private cmpFn: CompareFn<T>){}private _compare(curIdx: number.pIdx: number) :boolean {
return this.cmpFn(this.arr[curIdx], this.arr[pIdx])
}
private _shift_up(idx: number) :void {
// Then adjust the heap upward
// We need the coordinates of the parent node from the child node, because our subscript starts from 0, so the left child root node coordinates are: 2 * I + 1, and the right child root node coordinates are: 2 * I + 2. ParseInt ((2 * I + 1-1) / 2) = parseInt(I); parseInt((I + 1-1) / 2) = parseInt(I); ParseInt ((2* I + 2-1)/2) = parseInt((2* I +1)/2) = I
// As we said above, we need to swap the two values if the parent is less than the child in a big top heap
// while(idx && this.arr[parseInt(String((idx - 1) / 2))].data < this.arr[idx].data) {
while(idx && this._compare(parseInt(String((idx - 1) / 2)), idx)) {
// Number of the parent node
const pIdx = parseInt(String((idx - 1) / 2));
// Swap the parent node and the current node
[ this.arr[pIdx], this.arr[idx] ] = [ this.arr[idx], this.arr[pIdx] ];
// Then change the idx to the number of the parent node, so that it can be adjusted upwardidx = pIdx; }}private _shift_down(idx: number) :void {
// After the swap, the root node is adjusted down in turn
// The subscript of the largest child node
let n = this.count - 1;
// If the subscript of the idX child node is smaller than the subscript of the largest child node, it indicates that there are children
while(idx * 2 + 1 <= n) {
// Max represents the subscript of the maximum value in the root node, left subtree, and right subtree
let max = idx;
If the value of the root node of the left subtree is greater than the current value, update Max to the subscript of the left child root node
// if(this.arr[max].data < this.arr[2*idx+1].data) max = 2*idx+1;
if(this._compare(max, 2*idx+1)) max = 2*idx+1;
If the value of the right child root node is greater than the current node, update Max to the subscript of the right child root node
*idx+2<=n *idx+2<=n *idx+2<=n
if(2*idx+2<=n && this._compare(max, 2*idx+2)) max = 2*idx+2;
// if(2*idx+2<=n && this.arr[max].data < this.arr[2*idx+2].data) max = 2*idx+2;
// If the index of my maximum value is the current value, I don't need to adjust it down, and just end the loop
if(max === idx) break;
// Then swap the subscript and root of the maximum value
[ this.arr[idx], this.arr[max] ] = [ this.arr[max], this.arr[idx] ];
// Then change idx to the subscript of the original maximum value and continue to adjust downward
idx = max;
}
}
push(item: HeapDataStruct<T>): void {
this.arr[this.count++] = item;
this._shift_up(this.count-1);
// this.output('push: ');
}
pop(): HeapDataStruct<T>|null {
if(this.size()===0) return null;
const tmp = this.arr[0];
// As explained above, we first need to swap the end of the queue with the root node
[ this.arr[0].this.arr[this.count-1]] = [this.arr[this.count-1].this.arr[0]].// Remember to decrease count by 1 after the swap
this.count--;
// Make a downward adjustment
this._shift_down(0);
// this.output('pop: ');
return tmp;
}
size(): number {
return this.count;
}
top(): HeapDataStruct<T> {
return this.arr[0];
}
output(pre: string = "") :void {
console.log(pre+JSON.stringify(this.getArray()));
}
// Separate the data from the heap into an array
getArray(sort: (a: HeapDataStruct<T>, b: HeapDataStruct<T>) = > number = () = > 0): T[] {
return this.arr.slice(0.this.count).sort(sort).map(item= >item.data); }}class MedianFinder {
private lHeap: Heap<number>;
private rHeap: Heap<number>;
constructor() {
// Maintain a large top heap on the left
this.lHeap = new Heap((x,y) = >x.data<y.data);
// Maintain a small top heap on the right side
this.rHeap = new Heap((x,y) = >x.data>y.data);
}
addNum(num: number) :void {
// We try to insert elements into the heap on the left
// Therefore, the left heap is inserted when the left heap is empty or the element to be inserted is less than or equal to the top element of the left heap
if(this.lHeap.size()===0 || num <= this.lHeap.top().data) {
this.lHeap.push({data: num});
} else {
this.rHeap.push({data: num});
}
// Adjust the number of elements in the left and right heaps so that the left heaps have at most one more element than the right heaps
this.transformHeap();
}
transformHeap(){
// We agree that the left heap can have at most one more element than the right heap
// When our total is odd, the top element of the heap on the left is the median we want
// When our total is even, the average of the top elements of the left and right heaps is the median we want
if(this.rHeap.size() > this.lHeap.size()) {
this.lHeap.push(this.rHeap.pop());
}
if(this.lHeap.size() === this.rHeap.size()+2) {
this.rHeap.push(this.lHeap.pop());
}
}
findMedian(): number {
// Because we try to insert elements into the left heap first, and there is at most one more element on the left than on the right, when the number of left heaps is greater than that on the right
// Return the top element of the left heap
if(this.lHeap.size()>this.rHeap.size()) {
return this.lHeap.top().data;
} else {// If it is even, we need to take the average of the left and right heaps
return (this.lHeap.top().data + this.rHeap.top().data) / 2; }}}/** * Your MedianFinder object will be instantiated and called as such: * var obj = new MedianFinder() * obj.addNum(num) * var param_2 = obj.findMedian() */
// @lc code=end
Copy the code
LeetCode Interview question 17.20. Median of continuity
Their thinking
This is exactly the same problem we solved in the last one, so I won’t repeat it here
LeetCode 264. The ugly number II
Their thinking
This problem is actually the key point is that, how do we avoid double counting of certain trees, which is to the pruning of the heap, actually we need only with a prime factors in only when the modulus is 0, only multiplied by itself and is greater than its prime factors, and for its smaller prime factors, before has been calculated, not repeated calculation.
Code demo
/* * @lc app=leetcode.cn id=264 lang=typescript * * [264] ugly number II */
// @lc code=start
export typeHeapDataStruct<T> = { idx? :number.data: T
};
export type CompareFn<T> = (x: HeapDataStruct<T>, y: HeapDataStruct<T>) = > boolean;
class Heap<T> {
private arr: HeapDataStruct<T>[] = [];
private count: number = 0;
constructor(private cmpFn: CompareFn<T>){}private _compare(curIdx: number.pIdx: number) :boolean {
return this.cmpFn(this.arr[curIdx], this.arr[pIdx])
}
private _shift_up(idx: number) :void {
// Then adjust the heap upward
// We need the coordinates of the parent node from the child node, because our subscript starts from 0, so the left child root node coordinates are: 2 * I + 1, and the right child root node coordinates are: 2 * I + 2. ParseInt ((2 * I + 1-1) / 2) = parseInt(I); parseInt((I + 1-1) / 2) = parseInt(I); ParseInt ((2* I + 2-1)/2) = parseInt((2* I +1)/2) = I
// As we said above, we need to swap the two values if the parent is less than the child in a big top heap
// while(idx && this.arr[parseInt(String((idx - 1) / 2))].data < this.arr[idx].data) {
while(idx && this._compare(parseInt(String((idx - 1) / 2)), idx)) {
// Number of the parent node
const pIdx = parseInt(String((idx - 1) / 2));
// Swap the parent node and the current node
[ this.arr[pIdx], this.arr[idx] ] = [ this.arr[idx], this.arr[pIdx] ];
// Then change the idx to the number of the parent node, so that it can be adjusted upwardidx = pIdx; }}private _shift_down(idx: number) :void {
// After the swap, the root node is adjusted down in turn
// The subscript of the largest child node
let n = this.count - 1;
// If the subscript of the idX child node is smaller than the subscript of the largest child node, it indicates that there are children
while(idx * 2 + 1 <= n) {
// Max represents the subscript of the maximum value in the root node, left subtree, and right subtree
let max = idx;
If the value of the root node of the left subtree is greater than the current value, update Max to the subscript of the left child root node
// if(this.arr[max].data < this.arr[2*idx+1].data) max = 2*idx+1;
if(this._compare(max, 2*idx+1)) max = 2*idx+1;
If the value of the right child root node is greater than the current node, update Max to the subscript of the right child root node
*idx+2<=n *idx+2<=n *idx+2<=n
if(2*idx+2<=n && this._compare(max, 2*idx+2)) max = 2*idx+2;
// if(2*idx+2<=n && this.arr[max].data < this.arr[2*idx+2].data) max = 2*idx+2;
// If the index of my maximum value is the current value, I don't need to adjust it down, and just end the loop
if(max === idx) break;
// Then swap the subscript and root of the maximum value
[ this.arr[idx], this.arr[max] ] = [ this.arr[max], this.arr[idx] ];
// Then change idx to the subscript of the original maximum value and continue to adjust downward
idx = max;
}
}
push(item: HeapDataStruct<T>): void {
this.arr[this.count++] = item;
this._shift_up(this.count-1);
// this.output('push: ');
}
pop(): HeapDataStruct<T>|null {
if(this.size()===0) return null;
const tmp = this.arr[0];
// As explained above, we first need to swap the end of the queue with the root node
[ this.arr[0].this.arr[this.count-1]] = [this.arr[this.count-1].this.arr[0]].// Remember to decrease count by 1 after the swap
this.count--;
// Make a downward adjustment
this._shift_down(0);
// this.output('pop: ');
return tmp;
}
size(): number {
return this.count;
}
top(): HeapDataStruct<T> {
return this.arr[0];
}
output(pre: string = "") :void {
console.log(pre+JSON.stringify(this.getArray()));
}
// Separate the data from the heap into an array
getArray(sort: (a: HeapDataStruct<T>, b: HeapDataStruct<T>) = > number = () = > 0): T[] {
return this.arr.slice(0.this.count).sort(sort).map(item= >item.data); }}function nthUglyNumber(n: number) :number {
// Define a small top heap
const heap = new Heap<number> ((x, y) = >x.data>y.data);
// Put the 1 in first
heap.push({data: 1});
let res;
while(n--) {
// Pop the top element of the heap as the result of this round
res = heap.pop();
// To avoid double counting, we can prune our heap
// Only when modulo is 0 with a prime factor, multiply by itself and prime factors larger than it
if(res.data % 5= = =0) {
heap.push({data: res.data * 5});
} else if(res.data % 3= = =0) {
heap.push({data: res.data * 5});
heap.push({data: res.data * 3});
} else {
heap.push({data: res.data * 5});
heap.push({data: res.data * 3});
heap.push({data: res.data * 2}); }}// After n rounds of traversal, the final result is the ugly number we want
return res.data;
};
// @lc code=end
Copy the code
LeetCode 1801. Total number of orders in backlog
Their thinking
For this problem, we definitely want our purchase order amount to be as large as possible, so we need to maintain the maximum value and use the large top heap, while sales order amount should be as small as possible, so as to earn more price difference, so we need to maintain the minimum value and use the small top heap.
Code demo
export typeHeapDataStruct<T> = { idx? :number.data: T
};
export type CompareFn<T> = (x: HeapDataStruct<T>, y: HeapDataStruct<T>) = > boolean;
class Heap<T> {
private arr: HeapDataStruct<T>[] = [];
private count: number = 0;
constructor(private cmpFn: CompareFn<T>){}private _compare(curIdx: number.pIdx: number) :boolean {
return this.cmpFn(this.arr[curIdx], this.arr[pIdx])
}
private _shift_up(idx: number) :void {
// Then adjust the heap upward
// We need the coordinates of the parent node from the child node, because our subscript starts from 0, so the left child root node coordinates are: 2 * I + 1, and the right child root node coordinates are: 2 * I + 2. ParseInt ((2 * I + 1-1) / 2) = parseInt(I); parseInt((I + 1-1) / 2) = parseInt(I); ParseInt ((2* I + 2-1)/2) = parseInt((2* I +1)/2) = I
// As we said above, we need to swap the two values if the parent is less than the child in a big top heap
// while(idx && this.arr[parseInt(String((idx - 1) / 2))].data < this.arr[idx].data) {
while(idx && this._compare(parseInt(String((idx - 1) / 2)), idx)) {
// Number of the parent node
const pIdx = parseInt(String((idx - 1) / 2));
// Swap the parent node and the current node
[ this.arr[pIdx], this.arr[idx] ] = [ this.arr[idx], this.arr[pIdx] ];
// Then change the idx to the number of the parent node, so that it can be adjusted upwardidx = pIdx; }}private _shift_down(idx: number) :void {
// After the swap, the root node is adjusted down in turn
// The subscript of the largest child node
let n = this.count - 1;
// If the subscript of the idX child node is smaller than the subscript of the largest child node, it indicates that there are children
while(idx * 2 + 1 <= n) {
// Max represents the subscript of the maximum value in the root node, left subtree, and right subtree
let max = idx;
If the value of the root node of the left subtree is greater than the current value, update Max to the subscript of the left child root node
// if(this.arr[max].data < this.arr[2*idx+1].data) max = 2*idx+1;
if(this._compare(max, 2*idx+1)) max = 2*idx+1;
If the value of the right child root node is greater than the current node, update Max to the subscript of the right child root node
*idx+2<=n *idx+2<=n *idx+2<=n
if(2*idx+2<=n && this._compare(max, 2*idx+2)) max = 2*idx+2;
// if(2*idx+2<=n && this.arr[max].data < this.arr[2*idx+2].data) max = 2*idx+2;
// If the index of my maximum value is the current value, I don't need to adjust it down, and just end the loop
if(max === idx) break;
// Then swap the subscript and root of the maximum value
[ this.arr[idx], this.arr[max] ] = [ this.arr[max], this.arr[idx] ];
// Then change idx to the subscript of the original maximum value and continue to adjust downward
idx = max;
}
}
push(item: HeapDataStruct<T>): void {
this.arr[this.count++] = item;
this._shift_up(this.count-1);
// this.output('push: ');
}
pop(): HeapDataStruct<T>|null {
if(this.size()===0) return null;
const tmp = this.arr[0];
// As explained above, we first need to swap the end of the queue with the root node
[ this.arr[0].this.arr[this.count-1]] = [this.arr[this.count-1].this.arr[0]].// Remember to decrease count by 1 after the swap
this.count--;
// Make a downward adjustment
this._shift_down(0);
// this.output('pop: ');
return tmp;
}
size(): number {
return this.count;
}
top(): HeapDataStruct<T> {
return this.arr[0];
}
output(pre: string = "") :void {
console.log(pre+JSON.stringify(this.getArray()));
}
// Separate the data from the heap into an array
getArray(sort: (a: HeapDataStruct<T>, b: HeapDataStruct<T>) = > number = () = > 0): T[] {
return this.arr.slice(0.this.count).sort(sort).map(item= >item.data); }}function getNumberOfBacklogOrders(orders: number[] []) :number {
// Store the purchase order with the big top heap
const buyHeap = new Heap<number[] > ((x, y) = > x.data[0]<y.data[0]);
// Use small top heap to store sales orders
const sellHeap = new Heap<number[] > ((x, y) = > x.data[0]>y.data[0]);
// Iterate through all orders
orders.forEach(order= > {
if(order[2= = =0) {// Purchase order
// 1. The quantity of purchase order is not 0
// 2. The number of sales orders is not 0
// 3. The amount of the purchase order should be greater than or equal to that of the sales order
while(order[1] && sellHeap.size() && order[0] >= sellHeap.top().data[0]) {
// We need to consider the number of sales orders and purchase orders. We should take the minimum value from these two orders as the number of orders to operate this time
const count = Math.min(order[1], sellHeap.top().data[1]);
// Once you know the number of orders to operate, subtract it from both the purchase order and the sales order
order[1] -= count;
sellHeap.top().data[1] -= count;
// At this point, if our sales order quantity is 0, we pop the order from the heap
if(sellHeap.top().data[1= = =0) sellHeap.pop();
}
// If, after a cycle, our current order order still has a surplus, it indicates that there is a backlog of orders, and we put the backlog into the purchase order
order[1]! = =0 && buyHeap.push({data: order});
} else {// Sales order
while(order[1] && buyHeap.size() && order[0] <= buyHeap.top().data[0]) {
const count = Math.min(order[1], buyHeap.top().data[1]);
order[1] -= count;
buyHeap.top().data[1] -= count;
if(buyHeap.top().data[1= = =0) buyHeap.pop();
}
order[1]! = =0 && sellHeap.push({data: order}); }});let res = 0;
// According to the problem, the value may be too large, we need to model the order quantity and mod
const mod = 1000000007;
// Get the heap array data
const buyArray = buyHeap.getArray();
// Get the sales heap in array format
const sellArray = sellHeap.getArray();
// Add total orders to res
buyArray.forEach(order= > {
res += (res + order[1]) % mod;
});
sellArray.forEach(order= > {
res += (res + order[1]) % mod;
});
return res;
};
Copy the code