【從蛋殼到滿天飛】JS 數據結構解析和算法實現,所有文章大概的內容以下: Arrays(數組)、Stacks(棧)、Queues(隊列)、LinkedList(鏈表)、Recursion(遞歸思想)、BinarySearchTree(二分搜索樹)、Set(集合)、Map(映射)、Heap(堆)、PriorityQueue(優先隊列)、SegmentTree(線段樹)、Trie(字典樹)、UnionFind(並查集)、AVLTree(AVL 平衡樹)、RedBlackTree(紅黑平衡樹)、HashTable(哈希表)html
源代碼有三個:ES6(單個單個的 class 類型的 js 文件) | JS + HTML(一個 js 配合一個 html)| JAVA (一個一個的工程)git
所有源代碼已上傳 github,點擊我吧,光看文章可以掌握兩成,動手敲代碼、動腦思考、畫圖才能夠掌握八成。github
本文章適合 對數據結構想了解而且感興趣的人羣,文章風格一如既往如此,就以爲手機上看起來比較方便,這樣顯得比較有條理,整理這些筆記加源碼,時間跨度也算將近半年時間了,但願對想學習數據結構的人或者正在學習數據結構的人羣有幫助。算法
MyQueue
void enqueue(e)
E dequeue()
E getFront()
int getSize()
boolean isEmpty()
MyPriorityQueue
堆
來進行實現。O(1)
級別的操做O(n)
級別的操做,O(n)
級別的操做,O(1)
級別的操做,O(n)
級別的操做了,O(n)
的複雜度,O(1)
的複雜度,O(n)
的複雜度。O(n)
級別的操做,O(logn)
這種級別的操做,O(logn)
的時間複雜度,O(logn)
的時間複雜度,O(logn)
這樣的時間複雜度
O(nlog(n))
這個級別的,堆這種結構自己也是一棵樹數組
滿二叉樹與徹底二叉樹數據結構
教材中對於徹底二叉樹的定義很是的拗口架構
二叉堆知足的性質dom
實現二叉堆必須知足的要求,ide
以數組的方式表現一棵徹底二叉樹的規律函數
parent(i) = i / 2
,left child (i) = 2 * i
,right child (i) = 2 * i + 1
。徹底二叉樹的好處
在不少教科書中實現堆的時候,
// 原來是這樣的 空了數組中索引爲0的位置
parent(i) = i / 2
left child (i) = 2 * i
right child (i) = 2 * i + 1
// 偏移以後是這樣的 沒空數組中索引爲0的位置
parent(i) = (i - 1) / 2
left child (i) = 2 * i + 1
right child (i) = 2 * i + 2
複製代碼
(class: Myarray, class: MaxHeap)
Myarray
// 自定義類
class MyArray {
// 構造函數,傳入數組的容量capacity構造Array 默認數組的容量capacity=10
constructor(capacity = 10) {
this.data = new Array(capacity);
this.size = 0;
}
// 獲取數組中的元素實際個數
getSize() {
return this.size;
}
// 獲取數組的容量
getCapacity() {
return this.data.length;
}
// 判斷數組是否爲空
isEmpty() {
return this.size === 0;
}
// 給數組擴容
resize(capacity) {
let newArray = new Array(capacity);
for (var i = 0; i < this.size; i++) {
newArray[i] = this.data[i];
}
// let index = this.size - 1;
// while (index > -1) {
// newArray[index] = this.data[index];
// index --;
// }
this.data = newArray;
}
// 在指定索引處插入元素
insert(index, element) {
// 先判斷數組是否已滿
if (this.size == this.getCapacity()) {
// throw new Error("add error. Array is full.");
this.resize(this.size * 2);
}
// 而後判斷索引是否符合要求
if (index < 0 || index > this.size) {
throw new Error(
'insert error. require index < 0 or index > size.'
);
}
// 最後 將指定索引處騰出來
// 從指定索引處開始,全部數組元素所有日後移動一位
// 從後往前移動
for (let i = this.size - 1; i >= index; i--) {
this.data[i + 1] = this.data[i];
}
// 在指定索引處插入元素
this.data[index] = element;
// 維護一下size
this.size++;
}
// 擴展 在數組最前面插入一個元素
unshift(element) {
this.insert(0, element);
}
// 擴展 在數組最後面插入一個元素
push(element) {
this.insert(this.size, element);
}
// 其實在數組中添加元素 就至關於在數組最後面插入一個元素
add(element) {
if (this.size == this.getCapacity()) {
// throw new Error("add error. Array is full.");
this.resize(this.size * 2);
}
// size其實指向的是 當前數組最後一個元素的 後一個位置的索引。
this.data[this.size] = element;
// 維護size
this.size++;
}
// get
get(index) {
// 不能訪問沒有存放元素的位置
if (index < 0 || index >= this.size) {
throw new Error('get error. index < 0 or index >= size.');
}
return this.data[index];
}
// 擴展: 獲取數組中第一個元素
getFirst() {
return this.get(0);
}
// 擴展: 獲取數組中最後一個元素
getLast() {
return this.get(this.size - 1);
}
// set
set(index, newElement) {
// 不能修改沒有存放元素的位置
if (index < 0 || index >= this.size) {
throw new Error('set error. index < 0 or index >= size.');
}
this.data[index] = newElement;
}
// contain
contain(element) {
for (var i = 0; i < this.size; i++) {
if (this.data[i] === element) {
return true;
}
}
return false;
}
// find
find(element) {
for (var i = 0; i < this.size; i++) {
if (this.data[i] === element) {
return i;
}
}
return -1;
}
// findAll
findAll(element) {
// 建立一個自定義數組來存取這些 元素的索引
let myarray = new MyArray(this.size);
for (var i = 0; i < this.size; i++) {
if (this.data[i] === element) {
myarray.push(i);
}
}
// 返回這個自定義數組
return myarray;
}
// 刪除指定索引處的元素
remove(index) {
// 索引合法性驗證
if (index < 0 || index >= this.size) {
throw new Error('remove error. index < 0 or index >= size.');
}
// 暫存即將要被刪除的元素
let element = this.data[index];
// 後面的元素覆蓋前面的元素
for (let i = index; i < this.size - 1; i++) {
this.data[i] = this.data[i + 1];
}
this.size--;
this.data[this.size] = null;
// 若是size 爲容量的四分之一時 就能夠縮容了
// 防止複雜度震盪
if (Math.floor(this.getCapacity() / 4) === this.size) {
// 縮容一半
this.resize(Math.floor(this.getCapacity() / 2));
}
return element;
}
// 擴展:刪除數組中第一個元素
shift() {
return this.remove(0);
}
// 擴展: 刪除數組中最後一個元素
pop() {
return this.remove(this.size - 1);
}
// 擴展: 根據元素來進行刪除
removeElement(element) {
let index = this.find(element);
if (index !== -1) {
this.remove(index);
}
}
// 擴展: 根據元素來刪除全部元素
removeAllElement(element) {
let index = this.find(element);
while (index != -1) {
this.remove(index);
index = this.find(element);
}
// let indexArray = this.findAll(element);
// let cur, index = 0;
// for (var i = 0; i < indexArray.getSize(); i++) {
// // 每刪除一個元素 原數組中就少一個元素,
// // 索引數組中的索引值是按照大小順序排列的,
// // 因此 這個cur記錄的是 原數組元素索引的偏移量
// // 只有這樣纔可以正確的刪除元素。
// index = indexArray.get(i) - cur++;
// this.remove(index);
// }
}
// @Override toString 2018-10-17-jwl
toString() {
let arrInfo = `Array: size = ${this.getSize()},capacity = ${this.getCapacity()},\n`;
arrInfo += `data = [`;
for (var i = 0; i < this.size - 1; i++) {
arrInfo += `${this.data[i]}, `;
}
if (!this.isEmpty()) {
arrInfo += `${this.data[this.size - 1]}`;
}
arrInfo += `]`;
// 在頁面上展現
document.body.innerHTML += `${arrInfo}<br /><br /> `;
return arrInfo;
}
}
複製代碼
MaxHeap
// 自定義二叉堆之最大堆
class MyMaxHeap {
constructor(capacity = 10) {
this.myArray = new MyArray(capacity);
}
// 輔助函數 計算出堆中指定索引位置的元素其父節點的索引 -
calcParentIndex(index) {
if (index === 0)
// 索引爲0是根節點,根節點沒有父親節點,小於0就更加不能夠了
throw new Error("index is 0. doesn't have parent.");
return Math.floor((index - 1) / 2);
}
// 輔助函數 計算出堆中指定索引位置的元素其左孩子節點的索引 -
calcLeftChildIndex(index) {
return index * 2 + 1;
}
// 輔助函數 計算出堆中指定索引位置的元素其右孩子節點的索引 -
calcRightChildIndex(index) {
return index * 2 + 2;
}
// 獲取堆中實際的元素個數
getSize() {
return this.myArray.getSize();
}
// 返回堆中元素是否爲空的判斷值
isEmpty() {
return this.myArray.isEmpty();
}
}
複製代碼
(class: Myarray, class: MaxHeap)
Myarray
// 自定義類
class MyArray {
// 構造函數,傳入數組的容量capacity構造Array 默認數組的容量capacity=10
constructor(capacity = 10) {
this.data = new Array(capacity);
this.size = 0;
}
// 獲取數組中的元素實際個數
getSize() {
return this.size;
}
// 獲取數組的容量
getCapacity() {
return this.data.length;
}
// 判斷數組是否爲空
isEmpty() {
return this.size === 0;
}
// 給數組擴容
resize(capacity) {
let newArray = new Array(capacity);
for (var i = 0; i < this.size; i++) {
newArray[i] = this.data[i];
}
// let index = this.size - 1;
// while (index > -1) {
// newArray[index] = this.data[index];
// index --;
// }
this.data = newArray;
}
// 在指定索引處插入元素
insert(index, element) {
// 先判斷數組是否已滿
if (this.size == this.getCapacity()) {
// throw new Error("add error. Array is full.");
this.resize(this.size * 2);
}
// 而後判斷索引是否符合要求
if (index < 0 || index > this.size) {
throw new Error(
'insert error. require index < 0 or index > size.'
);
}
// 最後 將指定索引處騰出來
// 從指定索引處開始,全部數組元素所有日後移動一位
// 從後往前移動
for (let i = this.size - 1; i >= index; i--) {
this.data[i + 1] = this.data[i];
}
// 在指定索引處插入元素
this.data[index] = element;
// 維護一下size
this.size++;
}
// 擴展 在數組最前面插入一個元素
unshift(element) {
this.insert(0, element);
}
// 擴展 在數組最後面插入一個元素
push(element) {
this.insert(this.size, element);
}
// 其實在數組中添加元素 就至關於在數組最後面插入一個元素
add(element) {
if (this.size == this.getCapacity()) {
// throw new Error("add error. Array is full.");
this.resize(this.size * 2);
}
// size其實指向的是 當前數組最後一個元素的 後一個位置的索引。
this.data[this.size] = element;
// 維護size
this.size++;
}
// get
get(index) {
// 不能訪問沒有存放元素的位置
if (index < 0 || index >= this.size) {
throw new Error('get error. index < 0 or index >= size.');
}
return this.data[index];
}
// 擴展: 獲取數組中第一個元素
getFirst() {
return this.get(0);
}
// 擴展: 獲取數組中最後一個元素
getLast() {
return this.get(this.size - 1);
}
// set
set(index, newElement) {
// 不能修改沒有存放元素的位置
if (index < 0 || index >= this.size) {
throw new Error('set error. index < 0 or index >= size.');
}
this.data[index] = newElement;
}
// contain
contain(element) {
for (var i = 0; i < this.size; i++) {
if (this.data[i] === element) {
return true;
}
}
return false;
}
// find
find(element) {
for (var i = 0; i < this.size; i++) {
if (this.data[i] === element) {
return i;
}
}
return -1;
}
// findAll
findAll(element) {
// 建立一個自定義數組來存取這些 元素的索引
let myarray = new MyArray(this.size);
for (var i = 0; i < this.size; i++) {
if (this.data[i] === element) {
myarray.push(i);
}
}
// 返回這個自定義數組
return myarray;
}
// 刪除指定索引處的元素
remove(index) {
// 索引合法性驗證
if (index < 0 || index >= this.size) {
throw new Error('remove error. index < 0 or index >= size.');
}
// 暫存即將要被刪除的元素
let element = this.data[index];
// 後面的元素覆蓋前面的元素
for (let i = index; i < this.size - 1; i++) {
this.data[i] = this.data[i + 1];
}
this.size--;
this.data[this.size] = null;
// 若是size 爲容量的四分之一時 就能夠縮容了
// 防止複雜度震盪
if (Math.floor(this.getCapacity() / 4) === this.size) {
// 縮容一半
this.resize(Math.floor(this.getCapacity() / 2));
}
return element;
}
// 擴展:刪除數組中第一個元素
shift() {
return this.remove(0);
}
// 擴展: 刪除數組中最後一個元素
pop() {
return this.remove(this.size - 1);
}
// 擴展: 根據元素來進行刪除
removeElement(element) {
let index = this.find(element);
if (index !== -1) {
this.remove(index);
}
}
// 擴展: 根據元素來刪除全部元素
removeAllElement(element) {
let index = this.find(element);
while (index != -1) {
this.remove(index);
index = this.find(element);
}
// let indexArray = this.findAll(element);
// let cur, index = 0;
// for (var i = 0; i < indexArray.getSize(); i++) {
// // 每刪除一個元素 原數組中就少一個元素,
// // 索引數組中的索引值是按照大小順序排列的,
// // 因此 這個cur記錄的是 原數組元素索引的偏移量
// // 只有這樣纔可以正確的刪除元素。
// index = indexArray.get(i) - cur++;
// this.remove(index);
// }
}
// 新增: 交換兩個索引位置的變量 2018-11-6
swap(indexA, indexB) {
if (
indexA < 0 ||
indexA >= this.size ||
indexB < 0 ||
indexB >= this.size
)
throw new Error('Index is Illegal.'); // 索引越界異常
let temp = this.data[indexA];
this.data[indexA] = this.data[indexB];
this.data[indexB] = temp;
}
// @Override toString 2018-10-17-jwl
toString() {
let arrInfo = `Array: size = ${this.getSize()},capacity = ${this.getCapacity()},\n`;
arrInfo += `data = [`;
for (var i = 0; i < this.size - 1; i++) {
arrInfo += `${this.data[i]}, `;
}
if (!this.isEmpty()) {
arrInfo += `${this.data[this.size - 1]}`;
}
arrInfo += `]`;
// 在頁面上展現
document.body.innerHTML += `${arrInfo}<br /><br /> `;
return arrInfo;
}
}
複製代碼
MaxHeap
// 自定義二叉堆之最大堆
class MyMaxHeap {
constructor(capacity = 10) {
this.myArray = new MyArray(capacity);
}
// 添加操做
add(element) {
// 追加元素
this.myArray.push(element);
// 將追加的元素上浮到堆中合適的位置
this.siftUp(this.myArray.getSize() - 1);
}
// 堆的上浮操做 -
siftUp(index) {
// this.nonRecursiveSiftUp(index);
this.recursiveSiftUp(index);
// 不管是遞歸仍是非遞歸都有一個
// 元素上浮後結束的條件 當前節點元素值 小於其父節點元素值
// 和
// 索引即將越界的終止條件 要上浮的元素索引 小於等於0
}
// 堆的上浮操做 遞歸算法 -
recursiveSiftUp(index) {
// 解決最基本的問題, 遞歸終止條件
if (index <= 0) return;
let currentValue = this.myArray.get(index);
let parentIndex = this.calcParentIndex(index);
let parentValue = this.myArray.get(parentIndex);
// 遞歸寫法
if (this.compare(currentValue, parentValue) > 0) {
this.swap(index, parentIndex);
this.recursiveSiftUp(parentIndex);
}
}
// 堆的上浮操做 非遞歸算法 -
nonRecursiveSiftUp(index) {
if (index <= 0) return;
let currentValue = this.myArray.get(index);
let parentIndex = this.calcParentIndex(index);
let parentValue = this.myArray.get(parentIndex);
while (this.compare(currentValue, parentValue) > 0) {
// 交換堆中兩個元素位置的值
this.swap(index, parentIndex);
// 交換了位置以後,元素上浮後的索引變量也要進行相應的變動
index = parentIndex;
// 若是索引小於等於0了 那就結束循環
if (index <= 0) break;
currentValue = this.myArray.get(index);
parentIndex = this.calcParentIndex(index);
parentValue = this.myArray.get(parentIndex);
}
}
// 堆中兩個元素的位置進行交換
swap(indexA, indexB) {
this.myArray.swap(indexA, indexB);
}
// 輔助函數 計算出堆中指定索引位置的元素其父節點的索引 -
calcParentIndex(index) {
if (index === 0)
// 索引爲0是根節點,根節點沒有父親節點,小於0就更加不能夠了
throw new Error("index is 0. doesn't have parent.");
return Math.floor((index - 1) / 2);
}
// 輔助函數 計算出堆中指定索引位置的元素其左孩子節點的索引 -
calcLeftChildIndex(index) {
return index * 2 + 1;
}
// 輔助函數 計算出堆中指定索引位置的元素其右孩子節點的索引 -
calcRightChildIndex(index) {
return index * 2 + 2;
}
// 比較的功能 -
compare(elementA, elementB) {
if (elementA === null || elementB === null)
throw new Error("element is error. element can't compare.");
if (elementA > elementB) return 1;
else if (elementA < elementB) return -1;
else return 0;
}
// 獲取堆中實際的元素個數
getSize() {
return this.myArray.getSize();
}
// 返回堆中元素是否爲空的判斷值
isEmpty() {
return this.myArray.isEmpty();
}
}
複製代碼
O(logn)
,O(logn)
的,(class: Myarray, class: MaxHeap, class: Main)
Myarray
// 自定義類
class MyArray {
// 構造函數,傳入數組的容量capacity構造Array 默認數組的容量capacity=10
constructor(capacity = 10) {
this.data = new Array(capacity);
this.size = 0;
}
// 獲取數組中的元素實際個數
getSize() {
return this.size;
}
// 獲取數組的容量
getCapacity() {
return this.data.length;
}
// 判斷數組是否爲空
isEmpty() {
return this.size === 0;
}
// 給數組擴容
resize(capacity) {
let newArray = new Array(capacity);
for (var i = 0; i < this.size; i++) {
newArray[i] = this.data[i];
}
// let index = this.size - 1;
// while (index > -1) {
// newArray[index] = this.data[index];
// index --;
// }
this.data = newArray;
}
// 在指定索引處插入元素
insert(index, element) {
// 先判斷數組是否已滿
if (this.size == this.getCapacity()) {
// throw new Error("add error. Array is full.");
this.resize(this.size * 2);
}
// 而後判斷索引是否符合要求
if (index < 0 || index > this.size) {
throw new Error(
'insert error. require index < 0 or index > size.'
);
}
// 最後 將指定索引處騰出來
// 從指定索引處開始,全部數組元素所有日後移動一位
// 從後往前移動
for (let i = this.size - 1; i >= index; i--) {
this.data[i + 1] = this.data[i];
}
// 在指定索引處插入元素
this.data[index] = element;
// 維護一下size
this.size++;
}
// 擴展 在數組最前面插入一個元素
unshift(element) {
this.insert(0, element);
}
// 擴展 在數組最後面插入一個元素
push(element) {
this.insert(this.size, element);
}
// 其實在數組中添加元素 就至關於在數組最後面插入一個元素
add(element) {
if (this.size == this.getCapacity()) {
// throw new Error("add error. Array is full.");
this.resize(this.size * 2);
}
// size其實指向的是 當前數組最後一個元素的 後一個位置的索引。
this.data[this.size] = element;
// 維護size
this.size++;
}
// get
get(index) {
// 不能訪問沒有存放元素的位置
if (index < 0 || index >= this.size) {
throw new Error('get error. index < 0 or index >= size.');
}
return this.data[index];
}
// 擴展: 獲取數組中第一個元素
getFirst() {
return this.get(0);
}
// 擴展: 獲取數組中最後一個元素
getLast() {
return this.get(this.size - 1);
}
// set
set(index, newElement) {
// 不能修改沒有存放元素的位置
if (index < 0 || index >= this.size) {
throw new Error('set error. index < 0 or index >= size.');
}
this.data[index] = newElement;
}
// contain
contain(element) {
for (var i = 0; i < this.size; i++) {
if (this.data[i] === element) {
return true;
}
}
return false;
}
// find
find(element) {
for (var i = 0; i < this.size; i++) {
if (this.data[i] === element) {
return i;
}
}
return -1;
}
// findAll
findAll(element) {
// 建立一個自定義數組來存取這些 元素的索引
let myarray = new MyArray(this.size);
for (var i = 0; i < this.size; i++) {
if (this.data[i] === element) {
myarray.push(i);
}
}
// 返回這個自定義數組
return myarray;
}
// 刪除指定索引處的元素
remove(index) {
// 索引合法性驗證
if (index < 0 || index >= this.size) {
throw new Error('remove error. index < 0 or index >= size.');
}
// 暫存即將要被刪除的元素
let element = this.data[index];
// 後面的元素覆蓋前面的元素
for (let i = index; i < this.size - 1; i++) {
this.data[i] = this.data[i + 1];
}
this.size--;
this.data[this.size] = null;
// 若是size 爲容量的四分之一時 就能夠縮容了
// 防止複雜度震盪
if (Math.floor(this.getCapacity() / 4) === this.size) {
// 縮容一半
this.resize(Math.floor(this.getCapacity() / 2));
}
return element;
}
// 擴展:刪除數組中第一個元素
shift() {
return this.remove(0);
}
// 擴展: 刪除數組中最後一個元素
pop() {
return this.remove(this.size - 1);
}
// 擴展: 根據元素來進行刪除
removeElement(element) {
let index = this.find(element);
if (index !== -1) {
this.remove(index);
}
}
// 擴展: 根據元素來刪除全部元素
removeAllElement(element) {
let index = this.find(element);
while (index != -1) {
this.remove(index);
index = this.find(element);
}
// let indexArray = this.findAll(element);
// let cur, index = 0;
// for (var i = 0; i < indexArray.getSize(); i++) {
// // 每刪除一個元素 原數組中就少一個元素,
// // 索引數組中的索引值是按照大小順序排列的,
// // 因此 這個cur記錄的是 原數組元素索引的偏移量
// // 只有這樣纔可以正確的刪除元素。
// index = indexArray.get(i) - cur++;
// this.remove(index);
// }
}
// 新增: 交換兩個索引位置的變量 2018-11-6
swap(indexA, indexB) {
if (
indexA < 0 ||
indexA >= this.size ||
indexB < 0 ||
indexB >= this.size
)
throw new Error('Index is Illegal.'); // 索引越界異常
let temp = this.data[indexA];
this.data[indexA] = this.data[indexB];
this.data[indexB] = temp;
}
// @Override toString 2018-10-17-jwl
toString() {
let arrInfo = `Array: size = ${this.getSize()},capacity = ${this.getCapacity()},\n`;
arrInfo += `data = [`;
for (var i = 0; i < this.size - 1; i++) {
arrInfo += `${this.data[i]}, `;
}
if (!this.isEmpty()) {
arrInfo += `${this.data[this.size - 1]}`;
}
arrInfo += `]`;
// 在頁面上展現
document.body.innerHTML += `${arrInfo}<br /><br /> `;
return arrInfo;
}
}
複製代碼
MaxHeap
// 自定義二叉堆之最大堆
class MyMaxHeap {
constructor(capacity = 10) {
this.myArray = new MyArray(capacity);
}
// 添加操做
add(element) {
// 追加元素
this.myArray.push(element);
// 將追加的元素上浮到堆中合適的位置
this.siftUp(this.myArray.getSize() - 1);
}
// 堆的上浮操做 -
siftUp(index) {
// this.nonRecursiveSiftUp(index);
this.recursiveSiftUp(index);
// 不管是遞歸仍是非遞歸都有一個
// 元素上浮後結束的條件 當前節點元素值 小於其父節點元素值
// 和
// 索引即將越界的終止條件 要上浮的元素索引 小於等於0
}
// 堆的上浮操做 遞歸算法 -
recursiveSiftUp(index) {
// 解決最基本的問題, 遞歸終止條件
if (index <= 0) return;
let currentValue = this.myArray.get(index);
let parentIndex = this.calcParentIndex(index);
let parentValue = this.myArray.get(parentIndex);
// 遞歸寫法
if (this.compare(currentValue, parentValue) > 0) {
this.swap(index, parentIndex);
this.recursiveSiftUp(parentIndex);
}
}
// 堆的上浮操做 非遞歸算法 -
nonRecursiveSiftUp(index) {
if (index <= 0) return;
let currentValue = this.myArray.get(index);
let parentIndex = this.calcParentIndex(index);
let parentValue = this.myArray.get(parentIndex);
while (this.compare(currentValue, parentValue) > 0) {
// 交換堆中兩個元素位置的值
this.swap(index, parentIndex);
// 交換了位置以後,元素上浮後的索引變量也要進行相應的變動
index = parentIndex;
// 若是索引小於等於0了 那就結束循環
if (index <= 0) break;
currentValue = this.myArray.get(index);
parentIndex = this.calcParentIndex(index);
parentValue = this.myArray.get(parentIndex);
}
}
// 找到優先級最大的元素 (查找元素)操做
findMax() {
if (this.myArray.isEmpty())
throw new Error('can not findMax when heap is empty.');
return this.myArray.getFirst();
}
// 提取優先級最大的元素(刪除元素)操做
extractMax() {
// 獲取堆頂的元素
let maxElement = this.findMax();
// 獲取堆底的元素
let element = this.myArray.getLast();
// 讓堆底的元素替換掉堆頂的元素
this.myArray.set(0, element);
// 移除堆底的元素
this.myArray.pop();
// 讓堆頂的元素開始下沉,從而可以正常知足堆的性質
this.siftDown(0);
// 返回堆頂的元素
return maxElement;
}
// 堆的下沉操做 -
siftDown(index) {
// this.nonRecursiveSiftDown(index);
this.recursiveSiftDown(index);
}
// 堆的下沉操做 遞歸算法 -
recursiveSiftDown(index) {
// 遞歸終止條件
// 若是當前索引位置的元素沒有左孩子就說也沒有右孩子,
// 那麼能夠直接終止,由於沒法下沉
if (this.calcLeftChildIndex(index) >= this.myArray.getSize()) return;
const leftChildIndex = this.calcLeftChildIndex(index);
const leftChildValue = this.myArray.get(leftChildIndex);
const rightChildIndex = this.calcRightChildIndex(index);
let rightChildValue = null;
// let maxIndex = 0;
// if (rightChildIndex >= this.myArray.getSize())
// maxIndex = leftChildIndex;
// else {
// rightChildValue = this.myArray.get(rightChildIndex);
// if (this.compare(leftChildValue, rightChildValue) > 0)
// maxIndex = leftChildIndex;
// else
// maxIndex = rightChildIndex;
// }
// 這段代碼是上面註釋代碼的優化
let maxIndex = leftChildIndex;
if (rightChildIndex < this.myArray.getSize()) {
rightChildValue = this.myArray.get(rightChildIndex);
if (this.compare(leftChildValue, rightChildValue) < 0)
maxIndex = rightChildIndex;
}
let maxValue = this.myArray.get(maxIndex);
let currentValue = this.myArray.get(index);
if (this.compare(maxValue, currentValue) > 0) {
// 交換位置
this.swap(maxIndex, index);
// 繼續下沉
this.recursiveSiftDown(maxIndex);
}
}
// 堆的下沉操做 非遞歸算法 -
nonRecursiveSiftDown(index) {
// 該索引位置的元素有左右孩子節點才能夠下沉,
// 在徹底二叉樹中 若是一個節點沒有左孩子必然沒有右孩子
while (this.calcLeftChildIndex(index) < this.myArray.getSize()) {
let leftChildIndex = this.calcLeftChildIndex(index);
let leftChildValue = this.myArray.get(leftChildIndex);
let rightChildIndex = this.calcRightChildIndex(index);
let rightChildValue = null;
let maxIndex = leftChildIndex;
if (rightChildIndex < this.myArray.getSize()) {
rightChildValue = this.myArray.get(rightChildIndex);
if (this.compare(leftChildValue, rightChildValue) <= 0)
maxIndex = rightChildIndex;
}
let maxValue = this.myArray.get(maxIndex);
let currentValue = this.myArray.get(index);
if (this.compare(maxValue, currentValue) > 0) {
this.swap(maxIndex, index);
index = maxIndex;
continue;
} else break;
}
}
// 堆中兩個元素的位置進行交換
swap(indexA, indexB) {
this.myArray.swap(indexA, indexB);
}
// 輔助函數 計算出堆中指定索引位置的元素其父節點的索引 -
calcParentIndex(index) {
if (index === 0)
// 索引爲0是根節點,根節點沒有父親節點,小於0就更加不能夠了
throw new Error("index is 0. doesn't have parent.");
return Math.floor((index - 1) / 2);
}
// 輔助函數 計算出堆中指定索引位置的元素其左孩子節點的索引 -
calcLeftChildIndex(index) {
return index * 2 + 1;
}
// 輔助函數 計算出堆中指定索引位置的元素其右孩子節點的索引 -
calcRightChildIndex(index) {
return index * 2 + 2;
}
// 比較的功能 -
compare(elementA, elementB) {
if (elementA === null || elementB === null)
throw new Error("element is error. element can't compare.");
if (elementA > elementB) return 1;
else if (elementA < elementB) return -1;
else return 0;
}
// 獲取堆中實際的元素個數
size() {
return this.myArray.getSize();
}
// 返回堆中元素是否爲空的判斷值
isEmpty() {
return this.myArray.isEmpty();
}
}
複製代碼
Main
// main 函數
class Main {
constructor() {
this.alterLine('MyMaxHeap Area');
const n = 100;
const maxHeap = new MyMaxHeap();
const random = Math.random;
// 循環添加隨機數的值
for (let i = 0; i < n; i++) maxHeap.add(random() * n);
console.log('MaxHeap maxHeap size:' + maxHeap.size());
this.show('MaxHeap maxHeap size:' + maxHeap.size());
// 使用數組取值
let arr = [];
for (let i = 0; i < n; i++) arr[i] = maxHeap.extractMax();
console.log(
'Array arr size:' +
arr.length +
',MaxHeap maxHeap size:' +
maxHeap.size()
);
this.show(
'Array arr size:' +
arr.length +
',MaxHeap maxHeap size:' +
maxHeap.size()
);
console.log(arr, maxHeap);
// 檢驗一下是否符合要求
for (let i = 1; i < n; i++)
if (arr[i - 1] < arr[i]) throw new Error('error.');
console.log('test maxHeap completed.');
this.show('test maxHeap completed.');
}
// 將內容顯示在頁面上
show(content) {
document.body.innerHTML += `${content}<br /><br />`;
}
// 展現分割線
alterLine(title) {
let line = `--------------------${title}----------------------`;
console.log(line);
document.body.innerHTML += `${line}<br /><br />`;
}
}
// 頁面加載完畢
window.onload = function() {
// 執行主函數
new Main();
};
複製代碼
O(logn)
級別的
logn
這個級別的關係,O(logn)
O(logn)