本系列最開始是爲了本身面試準備的.後來發現整理愈來愈多,差很少有十二萬字符,最後決定仍是分享出來給你們.javascript
爲了分享整理出來,花費了本身大量的時間,起碼是隻本身用的三倍時間.若是喜歡的話,歡迎收藏,關注我!謝謝!html
前端面試查漏補缺--Index篇(12萬字符合集) 包含目前已寫好的系列其餘十幾篇文章.後續新增值文章不會再在每篇添加連接,強烈建議議點贊,關注合集篇!!!!,謝謝!~前端
後續還會繼續添加設計模式,前端工程化,項目流程,部署,閉環,vue常考知識點 等內容.若是以爲內容不錯的話歡迎收藏,關注我!謝謝!vue
目前本人也在準備跳槽,但願各位大佬和HR小姐姐能夠內推一份靠譜的武漢 前端崗位!郵箱:bupabuku@foxmail.com.謝謝啦!~java
時間複雜度和空間複雜度能夠查看這篇文章: 時間複雜度和空間複雜度詳解node
Object
。圖片名詞解釋: n: 數據規模 k:「桶」的個數 In-place: 佔用常數內存,不佔用額外內存 Out-place: 佔用額外內存github
只有這樣你才能作到正在自信地在面試官面前手寫代碼,還能邊寫邊和他講解思路!web
冒泡排序是一種簡單的排序算法。它重複地走訪過要排序的數列,一次比較兩個元素,若是它們的順序錯誤就把它們交換過來。走訪數列的工做是重複地進行直到沒有再須要交換,也就是說該數列已經排序完成。這個算法的名字由來是由於越小的元素會經由交換慢慢「浮」到數列的頂端。所以取名冒泡排序.面試
思路: 冒泡排序屬於基本排序算法,大體思路是兩層循環嵌套.結合下面的動圖,整理思路: 外循環遍歷數組的每一項,肯定兩兩比較循環的次數(其實最後一次能夠省略),內循環則用於肯定單次循環兩兩元素比較的次數,注意外層每循環一次,內循環兩兩比較的次數就會減1,即動圖中的黃色塊,表示已經排序好的柱形。
步驟:
JS代碼實現:
function bubbleSort(arr) {
var len = arr.length;
for (var i = 0; i < len; i++) {
for (var j = 0; j < len - 1 - i; j++) {
if (arr[j] > arr[j+1]) { //相鄰元素兩兩對比
[arr[j],arr[j+1]] = [arr[j+1],arr[j]] //經過解構完成元素交換
}
}
}
return arr;
}
var arr=[3,44,38,5,47,15,36,26,27,2,46,4,19,50,48];
console.log(bubbleSort(arr));//[2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]
複製代碼
說明:關於冒泡算法的其餘實現思路:逆序,雙向等實現方法,能夠查看這篇文章 ,這裏就不費筆墨了.
選擇排序(Selection-sort)是一種簡單直觀的排序算法。它的工做原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,而後,再從剩餘未排序元素中繼續尋找最小(大)元素,而後放到已排序序列的末尾。以此類推,直到全部元素均排序完畢。
選擇排序也是表現最穩定的排序算法之一,由於不管什麼數據進去都是O(n²)的時間複雜度.因此用到它的時候,數據規模越小越好。惟一的好處可能就是不佔用額外的內存空間了吧。 理論上講,選擇排序可能也是平時排序通常人想到的最多的排序方法了吧。
思路: 選擇排序也屬於基本排序算法,大體思路也是兩層循環嵌套.結合下面的動圖和它的工做原理:首先外循環,每循環一次就肯定了一個值在排序中的位置(動圖中爲從左依次肯定).那要通過多少次,這樣的循環?答案就是數列的長度減1. 接着是內循環: 肯定剩下的未排序的柱形須要逐個比較的次數.
步驟: n個記錄的直接選擇排序可通過n-1趟直接選擇排序獲得有序結果。具體算法描述以下:
R[1..i-1]
和R(i..n)。該趟排序從當前無序區中-選出關鍵字最小的記錄 R[k],將它與無序區的第1個記錄R交換,使R[1..i]
和R[i+1..n)
分別變爲記錄個數增長1個的新有序區和記錄個數減小1個的新無序區;JS代碼實現:
function selectionSort(arr) {
var len = arr.length;
var minIndex, temp;
for (var i = 0; i < len - 1; i++) {
minIndex = i; //用來保存最小數
for (var j = i + 1; j < len; j++) {
if (arr[j] < arr[minIndex]) { //尋找最小的數
minIndex = j; //將最小數的索引保存
}
}
[arr[minIndex],arr[i]] = [arr[i],arr[minIndex]] //經過解構完成元素交換
}
return arr;
}
var arr=[3,44,38,5,47,15,36,26,27,2,46,4,19,50,48];
console.log(selectionSort(arr));//[2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]
複製代碼
插入排序(Insertion-Sort)的算法描述是一種簡單直觀的排序算法。它的工做原理是經過構建有序序列,對於未排序數據,在已排序序列中從後向前掃描,找到相應位置並插入。插入排序在實現上,一般採用in-place排序(即只需用到O(1)的額外空間的排序),於是在從後向前掃描過程當中,須要反覆把已排序元素逐步向後挪位,爲最新元素提供插入空間。
插入排序核心--撲克牌思想: 就想着本身在打撲克牌,接起來第一張,放哪裏無所謂,再接起來一張,比第一張小,放左邊,繼續接,多是中間數,就插在中間.後面起的牌從後向前依次比較,並插入.
思路: 插入排序也屬於基本排序算法,大體思路也是兩層循環嵌套.首先,按照其撲克牌的思路.將要排序的數列分爲兩部分.左邊爲有序數列(起在手中的牌),剛開始爲空.右邊部分爲待排序的數列(即亂序的撲克牌).
有了上面大體思想後,開始設置循環.首先外循環爲你須要起多少張牌.那是多少?毫無疑問就是數列的長度,可是爲了方便,咱們能夠默認讓數列第一個數做爲有序數列,能夠減小一次循環.故外循環次數爲數列長度減1;內循環則循環有序數列,並從右往左,比較大小,將較小數插在前面(結合動圖)
步驟:
JS代碼實現:
function insertSort(arr) {
for(let i = 1; i < arr.length; i++) { //外循環從1開始,默認arr[0]是有序段
for(let j = i; j > 0; j--) { //j = i,表示此時你起在手上的那張牌,將arr[j]依次比較插入有序段中
if(arr[j] < arr[j-1]) {
[arr[j],arr[j-1]] = [arr[j-1],arr[j]]; //其實這裏內循環中,只要比它前一個數小就交換,直到沒有更小的,就break退出.這和動圖表示的插入仍是有點區別的,但最後結果實際上是同樣的.
} else {
break;
}
}
}
return arr;
}
var arr=[3,44,38,5,47,15,36,26,27,2,46,4,19,50,48];
console.log(insertSort(arr));//[2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]
複製代碼
快速排序的名字起的是簡單粗暴,由於一聽到這個名字你就知道它存在的意義,就是快,並且效率高! 它是處理大數據最快的排序算法之一了。
它是在冒泡排序基礎上的遞歸分治法。經過遞歸的方式將數據依次分解爲包含較小元素和較大元素的不一樣子序列。該算法不斷重複這個步驟直至全部數據都是有序的。
注意: 快速排序也是面試是最最最容易考到的算法題,常常就會讓你進行手寫.
思路: 快速排序屬於高級排序算法,此時就不是類似的循環嵌套.它的大概思想就是: 找到一個數做爲參考,比這個數字大的放在數字左邊,比它小的放在右邊; 而後分別再對左邊和右變的序列作相同的操做(遞歸).
注意: 涉及到遞歸的算法,必定要記得設置出口,跳出遞歸!
步驟:
JS代碼實現:
function quickSort (arr) {
if(arr.length <= 1) {
return arr; //遞歸出口
}
let left = [],
right = [],
//這裏咱們默認選擇數組第一個爲基準,PS:其實這樣偷懶是很差的,若是數組是已經排好序了的.則頗有可能變成最差狀況的時間複雜度
//pivotIndex = Math.floor(arr.length / 2),
pivot = arr[0]; //阮一峯版: arr.splice(pivotIndex, 1)[0]; 使用splice在大量數據時,會消耗大量內存;但也不至於被噴得一無可取! 它的思路是沒有任何問題的!
for (var i = 1; i < arr.length; i++) {
if (arr[i] < pivot) {
left.push(arr[i])
} else {
right.push(arr[i])
}
}
//concat也不適合大量數據的排序,會消耗大量內存
return quickSort(left).concat(pivot, quickSort(right))
}
var arr=[3,44,38,5,47,15,36,26,27,2,46,4,19,50,48];
console.log(quickSort(arr));//[2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]
//改進版:
function partition2(arr, low, high) {
let pivot = arr[low];
while (low < high) {
while (low < high && arr[high] > pivot) {
--high;
}
arr[low] = arr[high];
while (low < high && arr[low] <= pivot) {
++low;
}
arr[high] = arr[low];
}
arr[low] = pivot;
return low;
}
function quickSort2(arr, low, high) {
if (low < high) {
let pivot = partition2(arr, low, high);
quickSort2(arr, low, pivot - 1);
quickSort2(arr, pivot + 1, high);
}
return arr;
}
var arr=[3,44,38,5,47,15,36,26,27,2,46,4,19,50,48];
console.log(quickSort2(arr,0,arr.length-1));//[2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]
複製代碼
1959年Shell發明; 第一個突破O(n^2)的排序算法;是簡單插入排序的改進版;它與插入排序的不一樣之處在於,它會優先比較距離較遠的元素。 希爾排序又叫縮小增量排序.而且排序也是不穩定的
希爾排序是基於插入排序的如下兩點性質而提出改進方法的:
希爾排序的基本思想是:先將整個待排序的記錄序列分割成爲若干子序列分別進行直接插入排序,待整個序列中的記錄「基本有序」時,再對全體記錄進行依次直接插入排序。
思路: 希爾排序其實大致思路很簡單,就是將數組(長度爲len)分紅間隔爲t1的若干數組.進行插入排序;排完後,將數組再分紅間隔爲t2(逐步減少)的若干數組,進行插入排序;而後繼續上述操做,直到分紅間隔爲1的數組,再進行最後一次插入排序則完成.
方便理解能夠查看下圖:
步驟:
JS代碼實現:
function shellSort(arr) {
var len = arr.length,
temp,
gap = 1;
while(gap < len/5) { //動態定義間隔序列
gap =gap*5+1;
}
for (gap; gap > 0; gap = Math.floor(gap/5)) {
for (var i = gap; i < len; i++) {
temp = arr[i];
for (var j = i-gap; j >= 0 && arr[j] > temp; j-=gap) {
arr[j+gap] = arr[j];
}
arr[j+gap] = temp;
}
}
return arr;
}
var arr=[3,44,38,5,47,15,36,26,27,2,46,4,19,50,48];
console.log(shellSort(arr));//[2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]
複製代碼
歸併排序(Merge sort)是創建在歸併操做上的一種有效的排序算法。該算法是採用分治法(Divide and Conquer)的一個很是典型的應用。
歸併排序是一種穩定的排序方法。將已有序的子序列合併,獲得徹底有序的序列;即先使每一個子序列有序,再使子序列段間有序。若將兩個有序表合併成一個有序表,稱爲2-路歸併。
思路: 將數組分爲左和右兩部分,而後繼續將左右兩部分繼續(遞歸)拆分,直到拆分紅單個爲止;而後將拆分爲最小的兩個數組,進行比較,合併排成一個數組.接着繼續遞歸比較合併.直到最後合併爲一個數組.
步驟:
JS代碼實現:
function mergeSort(arr) { //採用自上而下的遞歸方法
var len = arr.length;
if(len < 2) {
return arr;
}
var middle = Math.floor(len / 2),
left = arr.slice(0, middle),
right = arr.slice(middle);
return merge(mergeSort(left), mergeSort(right));
}
function merge(left, right){
var result = [];
while (left.length && right.length) {
if (left[0] <= right[0]) {
result.push(left.shift());
} else {
result.push(right.shift());
}
}
while (left.length)
result.push(left.shift());
while (right.length)
result.push(right.shift());
return result;
}
var arr=[3,44,38,5,47,15,36,26,27,2,46,4,19,50,48];
console.log(mergeSort(arr));//[2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]
複製代碼
堆排序(Heapsort)是指利用堆這種數據結構所設計的一種排序算法。堆積是一個近似徹底二叉樹的結構,並同時知足堆積的性質:即子結點的鍵值或索引老是小於(或者大於)它的父節點。堆排序能夠說是一種利用堆的概念來排序的選擇排序。分爲兩種方法:
步驟:
JS代碼實現:
function buildMaxHeap(arr,len) { // 創建大頂堆
for (var i = Math.floor(len/2); i >= 0; i--) {
heapify(arr, i,len);
}
}
function heapify(arr, i,len) { // 堆調整
var left = 2 * i + 1,
right = 2 * i + 2,
largest = i;
if (left < len && arr[left] > arr[largest]) {
largest = left;
}
if (right < len && arr[right] > arr[largest]) {
largest = right;
}
if (largest != i) {
swap(arr, i, largest);
heapify(arr, largest,len);
}
}
function swap(arr, i, j) {
var temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
function heapSort(arr) {
var len = arr.length;
buildMaxHeap(arr,len);
for (var i = arr.length-1; i > 0; i--) {
swap(arr, 0, i);
len--;
heapify(arr, 0,len);
}
return arr;
}
var arr=[3,44,38,5,47,15,36,26,27,2,46,4,19,50,48];
console.log(heapSort(arr));//[2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]
複製代碼
計數排序幾乎是惟一一個不基於比較的排序算法, 該算法於1954年由 Harold H. Seward 提出. 使用它處理必定範圍內的整數排序時, 時間複雜度爲O(n+k), 其中k是整數的範圍, 它幾乎比任何基於比較的排序算法都要快( 只有當O(k)>O(n*log(n))的時候其效率反而不如基於比較的排序, 如歸併排序和堆排序)
.
計數排序的核心在於將輸入的數據值轉化爲鍵存儲在額外開闢的數組空間中。
思路: 計數排序利用了一個特性, 對於數組的某個元素, 一旦知道了有多少個其它元素比它小(假設爲m個), 那麼就能夠肯定出該元素的正確位置(第m+1位)
步驟:
注意: 若是原數組A是包含若干個對象的數組,須要基於對象的某個屬性進行排序,那麼算法開始時,須要將原數組A處理爲一個只包含對象屬性值的簡單數組simpleA, 接下來便基於simpleA進行計數、累加計數, 其它同上.
JS代碼實現:
//如下實現不只支持了數值序列的排序,還支持根據對象的某個屬性值來排序。
function countSort(array, keyName){
var length = array.length,
output = new Array(length),
max,
min,
simpleArray = keyName ? array.map(function(v){
return v[keyName];
}) : array; // 若是keyName是存在的,那麼就建立一個只有keyValue的簡單數組
// 獲取最大最小值
max = min = simpleArray[0];
simpleArray.forEach(function(v){
v > max && (max = v);
v < min && (min = v);
});
// 獲取計數數組的長度
var k = max - min + 1;
// 新建並初始化計數數組
var countArray = new Array(k);
simpleArray.forEach(function(v){
countArray[v - min]= (countArray[v - min] || 0) + 1;
});
// 累加計數,存儲不一樣值的初始下標
countArray.reduce(function(prev, current, i, arr){
arr[i] = prev;
return prev + current;
}, 0);
// 從原數組挨個取值(因取的是原數組的相應值,只能經過遍歷原數組來實現)
simpleArray.forEach(function(v, i){
var j = countArray[v - min]++;
output[j] = array[i];
});
return output;
}
var arr=[3,44,38,5,47,15,36,26,27,2,46,4,19,50,48];
console.log(countSort(arr));//[2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]
複製代碼
桶排序是計數排序的升級版。它利用了函數的映射關係,高效與否的關鍵就在於這個映射函數的肯定。爲了使桶排序更加高效,咱們須要作到這兩點:
同時,對於桶中元素的排序,選擇何種比較排序算法對於性能的影響相當重要。很顯然,桶劃分的越小,各個桶之間的數據越少,排序所用的時間也會越少。但相應的空間消耗就會增大。
思路: 桶排序 (Bucket sort)的工做的原理:假設輸入數據服從均勻分佈,將數據分到有限數量的桶裏,每一個桶再分別排序(有可能再使用別的排序算法或是以遞歸方式繼續使用桶排序進行排
步驟:
注意: 若是原數組A是包含若干個對象的數組,須要基於對象的某個屬性進行排序,那麼算法開始時,須要將原數組A處理爲一個只包含對象屬性值的簡單數組simpleA, 接下來便基於simpleA進行計數、累加計數, 其它同上.
JS代碼實現:
function bucketSort(arr, bucketSize) {
if (arr.length === 0) {
return arr;
}
var i;
var minValue = arr[0];
var maxValue = arr[0];
for (i = 1; i < arr.length; i++) {
if (arr[i] < minValue) {
minValue = arr[i]; // 輸入數據的最小值
} else if (arr[i] > maxValue) {
maxValue = arr[i]; // 輸入數據的最大值
}
}
//桶的初始化
var DEFAULT_BUCKET_SIZE = 5; // 設置桶的默認數量爲5
bucketSize = bucketSize || DEFAULT_BUCKET_SIZE;
var bucketCount = Math.floor((maxValue - minValue) / bucketSize) + 1;
var buckets = new Array(bucketCount);
for (i = 0; i < buckets.length; i++) {
buckets[i] = [];
}
//利用映射函數將數據分配到各個桶中
for (i = 0; i < arr.length; i++) {
buckets[Math.floor((arr[i] - minValue) / bucketSize)].push(arr[i]);
}
arr.length = 0;
for (i = 0; i < buckets.length; i++) {
insertionSort(buckets[i]); // 對每一個桶進行排序,這裏使用了插入排序
for (var j = 0; j < buckets[i].length; j++) {
arr.push(buckets[i][j]);
}
}
return arr;
}
var arr=[3,44,38,5,47,15,36,26,27,2,46,4,19,50,48];
console.log(bucketSort(arr));//[2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]
複製代碼
基數排序是一種非比較型整數排序算法,其原理是將整數按位數切割成不一樣的數字,而後按每一個位數分別比較。因爲整數也能夠表達字符串(好比名字或日期)和特定格式的浮點數,因此基數排序也不是隻能使用於整數。
按照優先從高位或低位來排序有兩種實現方案:
基數排序,計數排序,桶排序.這三種排序算法都利用了桶的概念,但對桶的使用方法上有明顯差別:
步驟:
JS代碼實現:
/** * 基數排序適用於: * (1)數據範圍較小,建議在小於1000 * (2)每一個數值都要大於等於0 * @author xiazdong * @param arr 待排序數組 * @param maxDigit 最大位數 */
//LSD Radix Sort
function radixSort(arr, maxDigit) {
var mod = 10;
var dev = 1;
var counter = [];
console.time('基數排序耗時');
for (var i = 0; i < maxDigit; i++, dev *= 10, mod *= 10) {
for(var j = 0; j < arr.length; j++) {
var bucket = parseInt((arr[j] % mod) / dev);
if(counter[bucket]== null) {
counter[bucket] = [];
}
counter[bucket].push(arr[j]);
}
var pos = 0;
for(var j = 0; j < counter.length; j++) {
var value = null;
if(counter[j]!=null) {
while ((value = counter[j].shift()) != null) {
arr[pos++] = value;
}
}
}
}
console.timeEnd('基數排序耗時');
return arr;
}
var arr = [3, 44, 38, 5, 47, 15, 36, 26, 27, 2, 46, 4, 19, 50, 48];
console.log(radixSort(arr,2)); //[2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]
複製代碼
樹是一種非順序數據結構,一種分層數據的抽象模型,它對於存儲須要快速查找的數據很是有用。
現實生活中最多見的樹的例子是家譜,或是公司的組織架構圖.
一個樹結構包含一系列存在父子關係的節點。每一個節點都有一個父節點(除了頂部的第一個 節點)以及零個或多個子節點.
樹常見結構/屬性:
二叉樹,是一種特殊的樹,即子節點最多隻有兩個,這個限制可使得寫出高效的插入、刪除、和查找數據。在二叉樹中,子節點分別叫左節點和右節點。
二叉查找樹是一種特殊的二叉樹,相對較小的值保存在左節點中,較大的值(或者等於)保存在右節點中,這一特性使得查找的效率很高,對於數值型和非數值型數據,好比字母和字符串,都是如此。 如今經過JS實現一個二叉查找樹。
節點:
二叉樹的最小元素是節點,因此先定義一個節點
function Node(data,left,right) {
this.left = left;
this.right = right;
this.data = data;
this.show = () => {return this.data}
}
複製代碼
這個就是二叉樹的最小結構單元
二叉樹
function BST() {
this.root = null //初始化,root爲null
}
複製代碼
BST初始化時,只有一個根節點,且沒有任何數據。 接下來,咱們利用二叉查找樹的規則,定義一個插入方法,這個方法的基本思想是:
BST.root === null
,那麼就將節點做爲根節點BST.root !==null
,將插入節點進行一個比較,小於根節點,拿到左邊的節點,不然拿右邊,再次比較、遞歸。這裏就出現了遞歸了,由於,老是要把較小的放在靠左的分支。換言之
最左變的葉子節點是最小的數,最右的葉子節點是最大的數
function insert(data) {
var node = new Node(data,null,null);
if(this.root === null) {
this.root = node
} else {
var current = this.root;
var parent;
while(true) {
parent = current;
if(data < current.data) {
current = current.left; //到左子樹
if(current === null) { //若是左子樹爲空,說明能夠將node插入在這裏
parent.left = node;
break; //跳出while循環
}
} else {
current = current.right;
if(current === null) {
parent.right = node;
break;
}
}
}
}
}
複製代碼
這裏,是使用了一個循環方法,不斷的去向子樹尋找正確的位置。 循環和遞歸都有一個核心,就是找到出口,這裏的出口就是當current 爲null的時候,表明沒有內容,能夠插入。
接下來,將此方法寫入BST便可:
function BST() {
this.root = null;
this.insert = insert;
}
複製代碼
這樣子,就可使用二叉樹這個自建的數據結構了:
var bst = new BST();
bst.insert(10);
bst.insert(8);
bst.insert(2);
bst.insert(7);
bst.insert(5);
複製代碼
複製代碼
可是這個時候,想要看樹中的數據,不是那麼清晰,因此接下來,就要用到遍歷了。
樹的遍歷:
按照根節點訪問的順序不一樣,樹的遍歷分爲如下三種:
先序遍歷:
先序遍歷是以優先於後代節點的順序訪問每一個節點的。先序遍歷的一種應用是打印一個結構化的文檔。
function preOrder(node) {
if(node !== null) {
//根節點->左子樹->右子樹
console.log(node.show());
preOrder(node.left);
preOrder(node.right);
}
}
複製代碼
中序遍歷:
中序遍歷是以從最小到最大的順序訪 問全部節點。中序遍歷的一種應用就是對樹進行排序操做。
function inOrder(node) {
if(node !== null) {
//若是不是null,就一直查找左變,所以遞歸
//左子樹->根節點->右子樹
inOrder(node.left);
//遞歸結束,打印當前值
console.log(node.show());
//上一次遞歸已經把左邊搞完了,右邊
inOrder(node.right);
}
}
複製代碼
後序遍歷:
後序遍歷則是先訪問節點的後代節點,再訪問節點自己。後序遍歷的一種應用是計算一個目錄和它的子目錄中全部文件所佔空間的大小。
function postOrder(node) {
if(node !== null) {
//左子樹->右子樹->根節點
postOrder(node.left);
postOrder(node.right);
console.log(node.show())
}
}
複製代碼
在二叉樹這種數據結構中進行數據查找是最方便的:
清楚思路後,就動手來寫:
//最小值
function getMin(bst) {
var current = bst.root;
while(current.left !== null) {
current = current.left;
}
return current.data;
}
//最大值
function getMax(bst) {
var current = bst.root;
while(current.right !== null) {
current = current.right;
}
return current.data;
}
複製代碼
最大、最小值都是很是簡單的,下面主要看下如何經過
function find(target,bst) {
var current = bst.root;
while(current !== null) {
if(target === current.data) {
return true;
}
else if(target > current.data) {
current = current.right;
} else if(target < current.data) {
current = current.left;
}
}
return -1;
}
複製代碼
其實核心,仍然是經過一個循環和判斷,來不斷的向下去尋找,這裏的思想其實和二分查找是有點相似的。
AVL樹是一種自平衡二叉搜索樹,AVL樹本質上是帶了平衡功能的二叉查找樹(二叉排序樹,二叉搜索樹),在AVL樹中任何節點的兩個子樹的高度最大差異爲一,也就是說這種樹會在添加或移除節點時儘可能試着成爲一棵徹底樹,因此它也被稱爲高度平衡樹。查找、插入和刪除在平均和最壞狀況下都是 O(log n)
,增長和刪除可能須要經過一次或屢次樹旋轉來從新平衡這個樹。
紅黑樹和AVL樹相似,都是在進行插入和刪除操做時經過特定操做保持二叉查找樹的平衡,從而得到較高的查找性能;它雖然是複雜的,但它的最壞狀況運行時間也是很是良好的,而且在實踐中是高效的:它能夠在O(log n)
時間內作查找,插入和刪除,這裏的 n 是樹中元素的數目。
紅黑樹是每一個節點都帶有顏色屬性的二叉查找樹,顏色或紅色或黑色。在二叉查找樹強制通常要求之外,對於任何有效的紅黑樹咱們增長了以下的額外要求:
這些約束強制了紅黑樹的關鍵性質:從根到葉子的最長的可能路徑很少於最短的可能路徑的兩倍長。結果是這個樹大體上是平衡的。由於操做好比插入、刪除和查找某個值的最壞狀況時間都要求與樹的高度成比例,這個在高度上的理論上限容許紅黑樹在最壞狀況下都是高效的,而不一樣於普通的二叉查找樹。
紅黑樹和AVL樹同樣都對插入時間、刪除時間和查找時間提供了最好可能的最壞狀況擔保。這不僅是使它們在時間敏感的應用如即時應用(real time application)中有價值,並且使它們有在提供最壞狀況擔保的其餘數據結構中做爲建造板塊的價值;例如,在計算幾何中使用的不少數據結構均可以基於紅黑樹。 紅黑樹在函數式編程中也特別有用,在這裏它們是最經常使用的持久數據結構之一,它們用來構造關聯數組和集合,在突變以後它們能保持爲之前的版本。除了O(log n)
的時間以外,紅黑樹的持久版本對每次插入或刪除須要O(log n)
的空間。