算法基礎知識
算法思想
分治法
分而治之,前端常用的一種。對一個大的問題進行求解時,將這個大問題分成相同的小問題,然後對這些小問題分別求解,這些解可以合併爲該問題的解。
前提:
1. 該問題縮小到一定的程度之後可以被很容易的解決。
2. 該問題可以被分解爲若干規模較小的相同問題
3. 利用該問題分解出的子問題的解可以合併爲該問題的解
動態規劃
與分治法相似,不同點在於動態規劃一般用於有大量重疊子問題的情況。分治法中所有的子問題都會被重新計算一遍,而動態規劃會保留子問題的解,遇上重複的就不需要計算了。用空間來換時間效率。
貪心算法
貪心算法把眼前看到的最優解當做最優解。貪心算法不一定會得到最優解但是效率高。
數據結構(數據的組織形式)
哈希(hash)
利用鍵值對進行存儲。哈希可以用對象表示,也可以用數組表示。
數組a[],a[0]=43,a[1]=12,a[2]=58.這種一一對應的關係就是鍵值對,一個鍵對應着一個值,我們需要用到值12的時候直接引用鍵a[1]即可。
棧
先進後出爲棧,可以用數組表示。a.push('wcy');
入棧。a.pop()
出棧。
隊列
先進先出爲隊列,也可以用數組表示。a.push('wcy');
入隊列。a.shift()
出隊列。
鏈表
帶有指針指向下一個元素,如果要對一串數字進行頻繁的增刪改操作並且不是在數據的尾部,那麼用鏈表是一個很好的選擇。因爲數組裏邊,如果想單獨刪除裏邊的某一項,必須要把那一項刪除,然後再把後邊的全部往前提。
數組查詢快,鏈表刪除快。
樹
樹是層級結構,有根節點子節點以及葉子結點,沒有子節點的節點稱爲葉子節點。
如果一棵樹沒有分支,那它就是一個鏈表。
對於完全二叉樹和滿二叉樹用數組的形式存儲,這樣比較好取出數據,因爲這兩種樹的節點在這棵樹中的第幾位可以計算出來。而其他不規則的樹都用鏈表表示。
二叉樹:每個節點有最多兩個子節點。
滿二叉樹:除葉子結點外每個節點有兩個子節點
完全二叉樹:在滿二叉樹的基礎上從最後一個葉子節點開始依次往左拿掉葉子結點,不管拿掉幾個剩下的二叉樹都叫做完全二叉樹。
排序算法
冒泡排序
第一位和第二位比較,大的往後邊排。然後此時的第二位再與第三位進行比較,以此類推。每一趟都得到一個最大值,length減一。重複執行直至所有的數字都排序完畢。平均時間複雜度爲O(n*n)。
js實現:
var a = [4,6,3,2,7,9,8]
var index
var length = a.length
for(;length>1; length--){
for(index = 0; index<length-1; index++){
a[index] > a[index+1]?(swap(index,index+1)):undefined
}
}
function swap(i,j){
let temp = a[i]
a[i] = a[j]
a[j] = temp
return a
}
console.log(a) //[2,3,4,6,7,8,9]
選擇排序
遍歷數組,找到最大值然後與數組的第一位進行交換,第二趟找到剩下的數當中的最大值,然後與此時這個數組的第二位(除去第一趟找到的那個最大值之外的第一位)進行交換,最後得到排好序的數組。
快速排序
快速排序的的平均時間複雜度爲O(m=n log2 n),採用遞歸實現。設置一個mid數組,一個left數組和一個right數組,mid裏邊存一個數,然後拿數組裏的其它數分別與它比較,小於mid[0]的push到left裏邊,大於mid[0]的push到right裏邊,然後再分別對left和right遞歸調用該方法,在函數裏邊添加判斷,如果傳入的數組的length小於或等於1就將其return出去,然後將三個數組連接就可以得到一個排好序的數組了。
function quickSort(a){
if (a.length <= 1)
return a.slice(0);
var left = []
var right = []
var mid = [a[0]]
for (let i = 1; i < a.length; i++) {
if (mid[0] > a[i]) {
left.push(a[i])
} else {
right.push(a[i])
}
}
return quickSort(left).concat(mid.concat(quickSort(right)))
}
var a = [4, 3, 5, 2, 7, 9, 1, 0, 12]
console.log(quickSort(a)) // [0, 1, 2, 3, 4, 5, 7, 9, 12]
歸併排序
計數排序vs桶排序vs基數排序
計數排序必須要有一個hash作爲計數的工具,無法對小數和負數進行排序。適合有確定範圍的正整數,計數排序只需要遍歷一遍,速度非常快,但是很耗空間。
計數排序的時間複雜度爲O(n + max),用一個哈希來存儲數組中每個元素出現的個數。哈希的長度是數組中元素的最大值,遍歷需要排序的數組,數組中元素的值是我們新建的哈希表的key,數組中元素出現的次數是哈希表中對應的value。遍歷完數組後將哈希表中的元素依次輸出就可以得到一個排好序的數組。
僞代碼:
int a[]={2,6,5,4,5,8,9,7}; //或者輸入a的值,設置只能爲正整數
int aMax = a[0];
for(int i=1; i<a.length; i++){
if(aMax<a[i]){
aMax = a[i];
}
}//算出數組a的最大值aMax , aMax+1是數組b的長度
int b[aMax+1];
for(int i=0; i<a.length; i++){
if(b[a[i]]==null){
b[a[i]]=1;
}else{
b[a[i]]++;
}//此時b中是已經排好序的數組
//將排好序的數組輸出到a中。
int x = 0;
for(int i=0; i<b.length; i++){
if(b[i] != null){
for(int j=0; j<b[i]; j++){
a[x++] = i;
}
}
}
print(a);//將排好序的數組a輸出
}
桶排序,在桶裏邊放一定範圍的數字,桶的個數由自己決定。計數排序是每個桶裏邊只放一個數字,桶的個數爲待排序的數組的最大值加一(數組是從0開始的,如果你想有a[12],那就必須要有13個桶),這種比較浪費桶(空間)。但是桶排序必須要對每個桶裏的數字進行新一輪的排序。沒有計數排序快。時間換空間。
基數排序適用於大範圍的特別隨機的數。如果是十進制數就分爲十個桶0,1,2,3,4,5,6,7,8,9,先將個位數相同的放到一個桶裏,然後按隊列出桶(先進先出),然後再將十位數相同的放到一個桶裏,然後再出桶,依次類推直到排完最大的位數。入桶出桶操作次數多。
堆排序
堆排序的可視化
算法描述:
1. 將一維數組視作完全二叉樹,從最後一個非葉子結點開始構建最大堆,比較該結點的左子樹和右子樹,將大的跟該結點進行比較,如果子節點大於父節點的話將兩者進行交換。
2. 如果交換過後的結點不是葉子結點,再將其視爲父節點與它的左右子樹進行最大堆構建。保證調整過的子樹依然是最大堆。
3. 遍歷一遍之後將最大堆堆頂元素與最後一個葉子結點進行交換,然後用除去最後一個結點的新的完全二叉樹重新進行最大堆的構建。
4. 重複上述步驟直至只剩最後一個結點。
5. 輸出排好序的數組。