算法(Algorithm): 是對特定問題求解步驟的一種描述,它是指令的有序序列
,其中每一條指令
表示一個或者多個操做
。算法
執行有窮步驟後結束,有窮時間內完成數組
每一條指令有確切含義,任何條件下只有惟一的執行路徑,相同的輸入相同的輸出markdown
一個算法是能行的, 描述的步驟基於已實現的基本運算
執行有限次
實現ide
有0個或者多個輸入,刻畫運算的對象的初始狀況函數
有一個個或者多個輸出, 這些輸出和輸入存在特定關係測試
效率指的是算法執行時間
,存儲量指的是算法執行過程當中須要的最大存儲空間
.經過設計好的測試程序和數據
,利用計算機計時器對不一樣算法編制的程序
的運行時間進行比較,從而肯定算法效率的高低.優化
缺陷:ui
因而可知,拋開這些與計算機硬件,軟件有關的因素,一個程序的運行時間依賴於算法的好壞和問題的輸入規模.(所謂的問題輸入規模是指輸入量的多少(一般用整數量n 表示))spa
一個算法是有控制結構(順序,分支,循壞)和原操做(固定數據類型的操做),算法時間取決於二者的綜合效果。設計
對所研究問題(算法類型)基本操做的原操做以及對基本操做重複執行的次數度量。算法的基本操做的重複次數是問題規模
的某個函數f(n)
. 算法時間量度記作
T(n) = O(f(n));
隨着問題規模的n 的增大,算法執行時間的增加率和f(n)的增加率相同。
T(n) = O(f(n));
某些控制語句的重複的執行的次數,(for, while, do while)。
問題的基本的原操做重複執行次數和算法的執行時間成正比。
經常使用的時間複雜度所耗費的時間從小到大依次是:
O(1) < O(logn) < (n) < O(nlogn) < O(n^2) < O(n^3) < O(2^n) < O(n!);
對全部可能的輸入數據集合的指望值
分析最壞狀況估算執行時間的上線
...
for(i =2; i <=n; i++){
for(j =2; j <=i-1; j++){
++x;
a[i][j] = x;
}
}
語句頻度:(n - 1)(n-2)/2
時間平均複雜度T arg(n) = O(n^2);
最壞時間複雜度T(n) = O(n^2);
複製代碼
S(n) = O(f(n));
n 問題的規模 f(n)爲語句關於n所佔存儲空間的函數。 一個上機程序除了需喲啊存儲空間寄存自己所用的指令、常數、變量和輸入數據外,對數據進行操做的工做單元和存儲一些爲實現計算所須要的信息和輔助空間
.
經過特定的算法因式
將一組或多組數據
按照既定模式
進行從新排序
。
當兩個相同的元素
同時出現於某個序列之中,則通過必定的排序算法以後,二者在排序先後的相對位置不發生變化
.
注:穩定性是一個特別重要的評估標準。穩定的算法在排序的過程當中不會改變元素彼此的位置的相對次序,反之不穩定的排序算法常常會改變這個次序,這是咱們不肯意看到的
非線性時間比較類排序:比較,由時間複雜度不能突破(nlogn)
交換排序(冒泡、快速)、插入排序(直接插入、希爾排序)、選擇排序(簡單選擇排序,堆排序), 歸併排序(二路歸併,多路歸併)
線性時間非比較類排序:不比較,突破基於比較排序的時間下界,以線性時間運行 基數排序、桶排序、計數。
內部排序:指的是待排序記錄存放在計算機隨機存儲器進行排序
外部排序:待排序記錄數量的數量很大,一次不能夠存放所有記錄,需對外存訪問
經過一趟排序將待排記錄分隔成獨立的兩部分,其中一部分記錄的關鍵字均比另外一部分的關鍵字小,則可分別對這兩部分記錄繼續進行遞歸排序,以達到整個序列有序。 這個關鍵字是從數列中挑出一個元素,也稱爲 「基準」(pivot).
實現步驟:
target
(通常選擇第一個數)target
小的元素移動到數組左邊,比target
大的元素移動到數組右邊target
左側和右側的元素進行快速排序時間複雜度:平均O(nlogn)
,最壞O(n2)
,實際上大多數狀況下小於O(nlogn)
空間複雜度:O(logn)
(遞歸調用消耗)
不穩定
記錄一個索引l
從數組最左側開始,記錄一個索引r
從數組右側開始
在l<r
的條件下,找到右側小於target
的值array[r]
,並將其賦值到array[l]
在l<r
的條件下,找到左側大於target
的值array[l]
,並將其賦值到array[r]
這樣讓l=r
時,左側的值所有小於target
,右側的值所有小於target
const quickSort = function(array, start , end) {
if(end - start < 1) return;
let l = start;
let r = end;
const target = array[l];
while(l < r) {
while(l < r && array[r] >= target) {
r--
};
array[l] = array[r];
while(l < r && array[l] < target) {
l++
};
array[r] = array[l];
}
array[l] = target
quickSort(array, start, l - 1);
quickSort(array, l+1, end);
}
const arr = [4, 1, 5, 7, 3, 9];
quickSort(arr, 0, 5);
複製代碼
單獨開闢兩個存儲空間left
和right
來存儲每次遞歸比target
小和大的序列
每次遞歸直接返回left、target、right
拼接後的數組
浪費大量存儲空間,寫法簡單
function quickSort(array) {
if(array.length < 2) {
return array;
}
const target = array[0];
const left = [];
const right = [];
for(let i = 1; i < array.length; i++) {
if(array[i] < target) {
left.push(array[i])
} else {
right.push(array[i]);
}
}
return quickSort(left).concat([target], quickSort(right));
}
const arr = [4, 1, 5, 7, 3, 9];
quickSort(arr, 0, 5);
複製代碼
將左側序列當作一個有序序列,每次將一個數字插入該有序序列。
插入時,從有序序列最右側開始比較,若比較的數較大,後移一位。
時間複雜度:O(n2)
空間複雜度:O(1)
穩定
function insertSort(array) {
for(let i = 1, len = array.length; i < len; i++) {
let target = i;
for(let j = i - 1; j >=0; j--) {
if(array[target] < array[j]) {
[array[target], array[j]] = [array[j], array[target]];
target = j;
}else break;
}
}
}
const arr = [4, 1, 5, 7, 3, 9];
insertSort(arr, 0, 5);
複製代碼
建立一個大頂堆,大頂堆的堆頂必定是最大的元素。
交換第一個元素和最後一個元素,讓剩餘的元素繼續調整爲大頂堆。
從後往前以此和第一個元素交換並從新構建,排序完成。
時間複雜度:O(nlogn)
空間複雜度:O(1)
不穩定
function heapSort(array) {
creatHeap(array);
for(let i = array.length - 1; i > 0; i--) {
[array[i], array[0]] = [array[0], array[i]];
adjust(array, 0, i);
}
return array;
}
function creatHeap(array) {
const len = array.length;
const start = parseInt(len/2) - 1;
for(let i = start; i >=0; i--) {
adjust(array, i, len);
}
}
// 將第target個元素進行下沉,孩子節點有比他大的就下沉
function adjust(array, target, len) {
for(let i = 2 * target + 1; i< len; i = 2 * i + 1) {
// 找到孩子節點中最大的
if(i + 1 < len && array[i + 1] > array[i]) {
i = i + 1;
}
// 下沉
if(array[i] > array[target]){
[array[i], array[target]] = [array[target], array[i]]
}else {
break;}
}
}
const arr = [4, 1, 5, 7, 3, 9];
heapSort(arr);
複製代碼
利用歸併
的思想實現的排序方法。
該算法是採用分治法(Divide and Conquer
)的一個很是典型的應用。(分治法將問題分紅一些小的問題而後遞歸求解,而治的階段則將分的階段獲得的各答案"修補"在一塊兒,即分而治之)。
2
須要合併,那麼左右兩數組已經有序了。
建立一個臨時存儲數組temp
,比較兩數組第一個元素,將較小的元素加入臨時數組
若左右數組有一個爲空,那麼此時另外一個數組必定大於temp
中的全部元素,直接將其全部元素加入temp
時間複雜度:O(nlogn)
空間複雜度:O(n)
穩定
解法一
分割數組時直接將數組分割爲兩個數組,合併時直接合並數組。 缺點:空間複雜度略高,須要複製多個數組
function mergeSort(array) {
if(array.length < 2) {
return array;
}
const mid = array.length >> 1;
const front = array.slice(0, mid);
const end = array.slice(mid);
return merge(mergeSort(front), mergeSort(end));
}
function merge(front, end) {
const temp = [];
while(front.length && end.length) {
if(front[0] < end[0]) {
temp.push(front.shift())
} else {
temp.push(end.shift())
}
}
while(front.length) {
temp.push(front.shift());
}
while(end.length) {
temp.push(end.shift())
}
return temp;
}
const arr = [4, 1, 5, 7, 3, 9];
mergeSort(arr);
複製代碼
解法2 記錄數組的索引
,使用left、right
兩個索引來限定當前分割的數組。
優勢:空間複雜度低,只需一個temp
存儲空間,不須要拷貝數組
function mergeSort(array, left, right, temp) {
if (left < right) {
const mid = Math.floor((left + right) / 2);
mergeSort(array, left, mid, temp)
mergeSort(array, mid + 1, right, temp)
merge(array, left, right, temp);
}
return array;
}
function merge(array, left, right, temp) {
const mid = Math.floor((left + right) / 2);
let leftIndex = left;
let rightIndex = mid + 1;
let tempIndex = 0;
while (leftIndex <= mid && rightIndex <= right) {
if (array[leftIndex] < array[rightIndex]) {
temp[tempIndex++] = array[leftIndex++]
} else {
temp[tempIndex++] = array[rightIndex++]
}
}
while (leftIndex <= mid) {
temp[tempIndex++] = array[leftIndex++]
}
while (rightIndex <= right) {
temp[tempIndex++] = array[rightIndex++]
}
tempIndex = 0;
for (let i = left; i <= right; i++) {
array[i] = temp[tempIndex++];
}
}
const arr = [4, 1, 5, 7, 3, 9];
mergeSort(arr, 0, 5, []);
複製代碼
循環數組,比較當前元素和下一個元素,若是當前元素比下一個元素大,向上冒泡。
這樣一次循環以後最後一個數就是本數組最大的數。
下一次循環繼續上面的操做,不循環已經排序好的數。
優化:當一次循環沒有發生冒泡,說明已經排序完成,中止循環。
時間複雜度:O(n2)
空間複雜度:O(1)
穩定
function bubbleSort(array) {
for(let j = 0, len = array.length; j < len; j++) {
let complete = true;
for(let i = 0; i < len - 1 - j; i++) {
if(array[i] > array[i+1]) {
[array[i], array[i+1]] = [array[i+1], array[i]];
complete = false;
}
}
if(complete) {
break;
}
}
return array;
}
const arr = [4, 1, 5, 7, 3, 9];
bubbleSort(arr);
複製代碼
每次循環選取一個最小的數字放到前面的有序序列中。
時間複雜度:O(n2)
空間複雜度:O(1)
不穩定
function selectionSort(array) {
for(let i = 0, len = array.length; i < len -1; i++) {
let minIndex = i;
for(let j = i+1; j < len; j++) {
if(array[j] < array[minIndex]) {
minIndex = j
}
}
[array[minIndex], array[i]] = [array[i], array[minIndex]];
}
}
const arr = [4, 1, 5, 7, 3, 9];
selectionSort(arr);
複製代碼