二叉堆是一種特殊的二叉樹,可是不是一個二叉搜索樹,二叉堆 是計算機科學中很是著名的數據結構,又稱堆,因爲其高效,快速地查找出最大值和最小值,經常使用於優先隊列typescript
而且最後一層的葉節點儘量在都在左側
,這個叫作結構特徵儘管二叉堆是二叉樹,但並不必定是二叉搜索樹(BST).在二叉堆中,每一個子節點都要大於等於父節點(最小堆)或小於等於父節點(最大堆).然而二叉搜索樹中,左側子節點老是比父節點小,右側子節點也老是更大api
這裏能夠選擇和上一章樹同樣的數據類型(雙指針,一個左指針,一個右指針),可是也可使用數組數組
左節點: 當前節點下標 * 2 + 1數據結構
右節點:當前節點下標 * 2 + 2學習
父節點: (index - 1) / 2 (當位置可用時,這是一個邊界,注意 )ui
insert(value) : 這個方法向堆中插入一個新的值.若是插入成功,它返回true
,不然返回false
this
extract(): 這個方法移除最小值(最小堆的時候)或最大值(最大堆的時候),並返回這個值指針
findMinimum():這個方法返回最小值(最小堆的時候)或最大值(最大堆的時候),但不會刪除該值code
enum Compare { LESS_THAN = -1, BIGGER_THAN = 1, EQUALS = 0 } function defaultCompareFn<T>(parent: T, child: T) { if (parent === child) { return Compare.EQUALS } return parent > child ? Compare.BIGGER_THAN : Compare.LESS_THAN; } // 數組交換 function swap<T>(array: Array<T>, index: number, otherIndex: number) { [array[index], array[otherIndex]] = [array[otherIndex], array[index]]; }
這裏添加一個枚舉類型,表示
小於
,大於
,等於
blog添加一個默認比較方法
添加一個數組交換的方法,由於常常要進行數組交換
export class MinHeap<T> { private heap: Array<T>; compareFn: Function; constructor(compareFn: Function = defaultCompareFn) { //使用數組必定程度上不用去維護左右的指針 this.heap = []; //這個是一個比較大小的方法,與前面相似 this.compareFn = compareFn; } //獲取左邊子元素位置 getLeftIndex(index: number) { return index * 2 + 1; } //獲取右邊子元素位置 getRightIndex(index: number) { return index * 2 + 2; } //獲取父節點元素位置 getParentIndex(index: number) { if (index === 0) { return undefined; } return Math.floor((index - 1) / 2); } isEmpty() { return this.heap.length <= 0; } size() { return this.heap.length; } clear() { this.heap = []; } }
這裏是用的數組作存儲,至於左右指針就以上面說的那樣算
因此提供一個獲取左右指針以及父指針的方法 + 之前常常用的方法
// 插入值 insert(value: T) { if (value != null) { //先插入到堆裏去吧,這個是插入到最後,因此還要看這個數是否在這個位置 this.heap.push(value); //上移操做 this.siftUp(this.heap.length - 1); return true; } return false; }
這裏是把值先push到數組中,添加到最尾部了,可是這個並非說他就是在這個位置,因此須要進行數據
上移操做
siftUp(index: number) { //向上移動,因此與其父節點比較 let pIndex = this.getParentIndex(index); //父級節點大於當前節點,那麼就要交換位置 if (index > 0 && this.compareFn(this.heap[pIndex], this.heap[index]) === Compare.BIGGER_THAN) { //將當前元素與父元素交換 swap(this.heap, pIndex, index); //交換完成後咱們要進行下一步比較,咱們插入的元素在pIndex位置,可是咱們不知道他是否到了該在的位置,因此繼續 this.siftUp(pIndex); } }
其實所謂的上移就是最簡單的比較,咱們如今弄最小堆,因此只要咱們的
新插入的值 < 父節點值
,那就說明咱們須要上移,那麼就交換位置,注意:這裏上移一個位置後,咱們的目標就改到了父節點上,因此咱們遞歸的index變成了pIndex
let parent = this.getParentIndex(index); while ( index > 0 && this.compareFn(this.heap[parent], this.heap[index]) === Compare.BIGGER_THAN ) { swap(this.heap, parent, index); index = parent; parent = this.getParentIndex(index); }
注意事項同上
移除最小值,基本思路就是
- 交換最後一個和第一個的位置
- 而後移除最後一個,並保存做爲返回值
- 而後讓第一個值向下找合適的位置(這裏其實相似於上面的
siftUp
,區別不過是之前是一個數比較,如今子節點有兩個,可是要找到最小的那個,必定要記得是最小的那個哦!!!)- 爲何找最小的???[考慮第一次交換的狀況,當前元素須要下移,而且在第一個節點上,須要找一個最小的放上面,做爲根節點]
- 而後交換位置,而後繼續下移,直到最後
extract() { //若是空,就不用移除了 if (this.isEmpty()) { return undefined; } //若是有一個元素,就能夠直接移除返回 if (this.size() === 1) { return this.heap.shift(); } //最後一個和第一個交換 swap(this.heap, 0 , this.size() - 1) //彈出最後一個,並保留做爲返回值 let result = this.heap.pop(); //開始向下移動 this.siftDown(0); return result; }
siftDown(index: number) { //獲取左右的下標 let leftIndex = this.getLeftIndex(index); let rightIndex = this.getRightIndex(index); // 定義index值 element let element = index; // 在有效範圍內和左邊元素比較,若是大就向下移動 if(index < this.size() && this.compareFn(this.heap[element], this.heap[leftIndex]) === Compare.BIGGER_THAN){ element = leftIndex; } //*** 在有效範圍內和右元素比較,若是大就向下移動 這裏使用的是element,注意,由於element表明了最小的數 if(index < this.size() && this.compareFn(this.heap[element], this.heap[rightIndex]) === Compare.BIGGER_THAN){ element = rightIndex; } //肯定位置結束 if(element !== index){ // 交換元素,下移當前元素 swap(this.heap, index , element); // 而後遞歸,繼續移動當前元素,可是這個時候就再也不是index,而是element this.siftDown(element); } }
這裏必定要注意的是
***
標註點處的使用element
,由於index
可能在左節點的時候被替換了,這裏須要的是最小值
這個就不用多說了吧,就是取數組的第一個就好
//找最小的事 findMinimum() { return this.isEmpty() ? undefined : this.heap[0]; }
到此
堆
的全部方法就實現了,總體來講堆的邊界不是很複雜,因此仍是比較簡單的一種
你說還沒結束,哦,好像是的,忘記了兩件事,一個最喜歡的貼代碼,一個就是
堆排序
function heapify(array: any[], index: number, heapSize: number, compareFn: Function) { let largest = index; const left = (2 * index) + 1; const right = (2 * index) + 2; if (left < heapSize && compareFn(array[left], array[index]) > 0) { largest = left; } if (right < heapSize && compareFn(array[right], array[largest]) > 0) { largest = right; } if (largest !== index) { swap(array, index, largest); heapify(array, largest, heapSize, compareFn); } } function buildMaxHeap(array: any[], compareFn: Function) { for (let i = Math.floor(array.length / 2); i >= 0; i -= 1) { heapify(array, i, array.length, compareFn); } return array; } export function heapSort(array: any[], compareFn = defaultCompareFn) { let heapSize = array.length; buildMaxHeap(array, compareFn); while (heapSize > 1) { swap(array, 0, --heapSize); heapify(array, 0, heapSize, compareFn); } return array; }
方法就很少BB了,加油
/* * 二叉堆是二叉數,可是不必定是二叉搜索樹 * * insert(value) * extract():移除最大堆的最大值或者最小堆的最小值 * findMinimum():這個方法返回最小值(最小堆)或最大值(最大堆)且不會移除這個值 * * 二叉堆以數組存數據 下標找節點 * 初始節點: 0 * 1 2 * 3 4 5 6 * 7 8 9 10 11 12 13 14 * 0 * 第二排(下標1): 0 * 2 + 1 0 * 2 + 2 * 第三排(下標2): 1 * 2 + 1 1 * 2 + 2 2 * 2 + 1 2 * 2 + 2 * *總結: *左元素的位置: 當前下標 * 2 + 1 *右元素的位置: 當前下標 * 2 + 2 */ enum Compare { LESS_THAN = -1, BIGGER_THAN = 1, EQUALS = 0 } function defaultCompareFn<T>(parent: T, child: T) { if (parent === child) { return Compare.EQUALS } return parent > child ? Compare.BIGGER_THAN : Compare.LESS_THAN; } // 數組交換 function swap<T>(array: Array<T>, index: number, otherIndex: number) { [array[index], array[otherIndex]] = [array[otherIndex], array[index]]; } export class MinHeap<T> { private heap: Array<T>; compareFn: Function; constructor(compareFn: Function = defaultCompareFn) { this.heap = []; this.compareFn = compareFn; } //獲取左邊子元素位置 getLeftIndex(index: number) { return index * 2 + 1; } //獲取右邊子元素位置 getRightIndex(index: number) { return index * 2 + 2; } //獲取父節點元素位置 getParentIndex(index: number) { if (index === 0) { return undefined; } return Math.floor((index - 1) / 2); } isEmpty() { return this.heap.length <= 0; } size() { return this.heap.length; } clear() { this.heap = []; } // 插入值 insert(value: T) { if (value != null) { //先插入到堆裏去吧,這個是插入到最後,因此還要看這個數是否在這個位置 this.heap.push(value); this.siftUp(this.heap.length - 1); return true; } return false; } //以數組的形式返回數據 getAsArray() { return this.heap } // 向上移動數據 siftUp(index: number) { /* //向上移動,因此與其父節點比較 let pIndex = this.getParentIndex(index); //父級節點大於當前節點,那麼就要交換位置 if (index > 0 && this.compareFn(this.heap[pIndex], this.heap[index]) === Compare.BIGGER_THAN) { //將當前元素與父元素交換 swap(this.heap, pIndex, index); //交換完成後咱們要進行下一步比較,咱們插入的元素在pIndex位置,可是咱們不知道他是否到了該在的位置,因此繼續 this.siftUp(pIndex); } */ let parent = this.getParentIndex(index); while ( index > 0 && this.compareFn(this.heap[parent], this.heap[index]) === Compare.BIGGER_THAN ) { swap(this.heap, parent, index); index = parent; parent = this.getParentIndex(index); } } //找最小的事 findMinimum() { return this.isEmpty() ? undefined : this.heap[0]; } //移除堆中的最小值 extract() { //若是空,就不用移除了 if (this.isEmpty()) { return undefined; } //若是有一個元素,就能夠直接移除返回 if (this.size() === 1) { return this.heap.shift(); } //最後一個和第一個交換 swap(this.heap, 0 , this.size() - 1) //彈出最後一個,並保留做爲返回值 let result = this.heap.pop(); //開始堆化 this.siftDown(0); return result; } /** * 移動數據 * 使第一個元素找到其正確的位置 * 注意,此時的第一個元素是最大值(最小堆),因此這個時候只要向下找合適位置就能夠了 * * 這個方法其實和向上找(siftUp)是基本同樣的,可是區別就在是有左右兩個節點,因此咱們基於前面說的原則,找到最小的那個便可 */ siftDown(index: number) { //獲取左右的下標 let leftIndex = this.getLeftIndex(index); let rightIndex = this.getRightIndex(index); // 定義index值 element let element = index; // 在有效範圍內和左邊元素比較,若是大就向下移動 if(index < this.size() && this.compareFn(this.heap[element], this.heap[leftIndex]) === Compare.BIGGER_THAN){ element = leftIndex; } // 在有效範圍內和右元素比較,若是大就向下移動 這裏使用的是element,注意,由於element表明了最小的數 if(index < this.size() && this.compareFn(this.heap[element], this.heap[rightIndex]) === Compare.BIGGER_THAN){ element = rightIndex; } //肯定位置結束 if(element !== index){ // 交換元素,下移當前元素 swap(this.heap, index , element); // 而後遞歸,繼續移動當前元素,可是這個時候就再也不是index,而是element this.siftDown(element); } } heapify(array: T[]) { if (array) { this.heap = array; } const maxIndex = Math.floor(this.size() / 2) - 1; for (let i = 0; i <= maxIndex; i++) { this.siftDown(i); } return this.heap; } } function defaultMaxCompareFn<T>(parent: T, child: T) { if (parent === child) { return Compare.EQUALS } return parent > child ? Compare.LESS_THAN : Compare.BIGGER_THAN } export class MaxHeap<T> extends MinHeap<T> { constructor(compareFn = defaultMaxCompareFn) { super(compareFn); } } function heapify(array: any[], index: number, heapSize: number, compareFn: Function) { let largest = index; const left = (2 * index) + 1; const right = (2 * index) + 2; if (left < heapSize && compareFn(array[left], array[index]) > 0) { largest = left; } if (right < heapSize && compareFn(array[right], array[largest]) > 0) { largest = right; } if (largest !== index) { swap(array, index, largest); heapify(array, largest, heapSize, compareFn); } } function buildMaxHeap(array: any[], compareFn: Function) { for (let i = Math.floor(array.length / 2); i >= 0; i -= 1) { heapify(array, i, array.length, compareFn); } return array; } export function heapSort(array: any[], compareFn = defaultCompareFn) { let heapSize = array.length; buildMaxHeap(array, compareFn); while (heapSize > 1) { swap(array, 0, --heapSize); heapify(array, 0, heapSize, compareFn); } return array; }
enum Compare { LESS_THAN = -1, BIGGER_THAN = 1, EQUALS = 0 } function defaultCompare<T>(a: T, b: T): number { if (a === b) { return Compare.EQUALS; } return a < b ? Compare.LESS_THAN : Compare.BIGGER_THAN; } type ICompareFunction<T> = (a: T, b: T) => number; function reverseCompare<T>(compareFn: ICompareFunction<T>): ICompareFunction<T> { return (a, b) => compareFn(b, a); } function swap(array: any[], a: number, b: number) { /* const temp = array[a]; array[a] = array[b]; array[b] = temp; */ [array[a], array[b]] = [array[b], array[a]]; } export class MinHeap<T> { protected heap: T[] = []; constructor(protected compareFn: ICompareFunction<T> = defaultCompare) {} private getLeftIndex(index: number) { return 2 * index + 1; } private getRightIndex(index: number) { return 2 * index + 2; } private getParentIndex(index: number) { if (index === 0) { return undefined; } return Math.floor((index - 1) / 2); } size() { return this.heap.length; } isEmpty() { return this.size() <= 0; } clear() { this.heap = []; } findMinimum() { return this.isEmpty() ? undefined : this.heap[0]; } insert(value: T) { if (value != null) { const index = this.heap.length; this.heap.push(value); this.siftUp(index); return true; } return false; } private siftDown(index: number) { let element = index; const left = this.getLeftIndex(index); const right = this.getRightIndex(index); const size = this.size(); if (left < size && this.compareFn(this.heap[element], this.heap[left]) === Compare.BIGGER_THAN) { element = left; } if ( right < size && this.compareFn(this.heap[element], this.heap[right]) === Compare.BIGGER_THAN ) { element = right; } if (index !== element) { swap(this.heap, index, element); this.siftDown(element); } } private siftUp(index: number): void { let parent = this.getParentIndex(index); while ( index > 0 && this.compareFn(this.heap[parent], this.heap[index]) === Compare.BIGGER_THAN ) { swap(this.heap, parent, index); index = parent; parent = this.getParentIndex(index); } } extract() { if (this.isEmpty()) { return undefined; } if (this.size() === 1) { return this.heap.shift(); } const removedValue = this.heap[0]; this.heap[0] = this.heap.pop(); this.siftDown(0); return removedValue; } heapify(array: T[]) { if (array) { this.heap = array; } const maxIndex = Math.floor(this.size() / 2) - 1; for (let i = 0; i <= maxIndex; i++) { this.siftDown(i); } return this.heap; } getAsArray() { return this.heap; } } export class MaxHeap<T> extends MinHeap<T> { constructor(protected compareFn: ICompareFunction<T> = defaultCompare) { super(compareFn); this.compareFn = reverseCompare(compareFn); } }
表示這一週原本是說兩個章節的,但是工做還有生活緣由(這都是假的,主要是本身蠢,後面的圖,迷迷糊糊的,唉!!!)
而後發現本身胖了,胖得不行了,因此下週開始減肥,而且滿滿的日程,可是學習不能斷呀!!!仍是會盡可能基礎時間學習,畢竟如今太弱雞了,唉着急!!!! 下週排序,有興趣的能夠留意我一下哦!!!