以JavaScript語言爲主,學習數據結構與算法。node
算法須要依靠數據結構計算.ios
什麼是算法
時間複雜度Tn
根據算法寫成的程序在執行時佔用存儲單源的長度算法
空間複雜度Sn
根據算法寫成的程序在執行時好費時間的長度編程
數據結構
Object
。Key value
)直接進行訪問的數據結構;它經過把關鍵碼值映射到表中一個位置來訪問記錄,以加快查找的速度;這個映射函數叫作散列函數,存放記錄的數組叫作散列表。棧是一種聽從先進後出 (LIFO) 原則的有序集合;新添加的或待刪除的元素都保存在棧的末尾,稱做棧頂,另外一端爲棧底。在棧裏,新元素都靠近棧頂,舊元素都接近棧底。數組
棧也被用在編程語言的編譯器和內存中保存變變量、方法調用。瀏覽器
class Stack { constructor() { this.items = [] } // 入棧 push(element) { this.items.push(element) } // 出棧 pop() { return this.items.pop() } // 末位 get peek() { return this.items[this.items.length - 1] } // 是否爲空棧 get isEmpty() { return !this.items.length } // 長度 get size() { return this.items.length } // 清空棧 clear() { this.items = [] } // 打印棧數據 print() { console.log(this.items.toString()) } }
隊列是一種遵循先進先出 (FIFO / First In First Out) 原則的一組有序的項;隊列在尾部添加新元素,並從頭部移除元素。最新添加的元素必須排在隊列的末尾。網絡
前面的人優先完成本身的事務,完成以後,下一我的才能繼續。數據結構
class Queue { constructor(items) { this.items = items || [] } enqueue(element) { this.items.push(element) } dequeue() { return this.items.shift() } front() { return this.items[0] } clear() { this.items = [] } get size() { return this.items.length } get isEmpty() { return !this.items.length } print() { console.log(this.items.toString()) } }
數組: 相同數據類型的元素按必定順序排列的集合併發
數組建立方式app
// C++ int array[] = {5, 3, 2, 5, 6, 0, 10} // JS [] new Array();
特性:存儲連續的內存單元
好處:
爲何須要有鏈表:
移動和插入,刪除,須要耗費性能和時間,就是鏈表產生的緣由
typeof
用來檢測數據類型的運算符
typeof true; // boolean
返回都是一個字符串,其次字符串中包含了對應的數據類型。
返回的類型:
侷限性:
console.log(typeof typeof typeof function() {}); // typeof function() {} --> 'function', typeof 'function' --> 'string' , typeof 'string' --> 'string'
if (typeof num2 == 'undefined') { num2 = 0; } num2 = num2 || 0; // 這種形式,和上面的默認值形式並不徹底相同
typeof callback === 'function' ? callback() : null; callback && callback();
instanceof
檢測某一個實例是否屬於某個類
彌補typeof
不能判斷引用類型。
特性:只要在當前實例的原型鏈上,均可以查詢獲得,檢測出來的結果都是true
var obj = [123, 123]; console.log(obj instanceof Array); // true console.log(obj instanceof RegExp); // false
instanceof
不少的缺陷:
第一個缺陷:
對於基本數據類型,字面量建立出來的結果檢測都爲false。
從嚴格意義上講,只有實例建立出來的結果纔是標準的對象數據類型值。
1 instanceof Number // false new Number(1) instanceof Number // true true instanceof Boolean // false '' instanceof String // false console.log(1 instanceof Number); console.log(new Number(1) instanceof Number); // instanceof 的侷限性,對於基本數據類型來講字面量方式建立出來的結果和實例方式建立出來的結果是有必定區別的, // 從嚴格意義上來說,只有實例建立出來的結果纔是標準的對象數據類型值,也是標準的Number這個類的一個實例;對於字面量方式建立出來的結果 // 是基本數據類型值,不是嚴謹的實例,可是因爲JS的鬆散特色,致使了可使用Number.prototype上提供的方法。
第二個缺陷:
特徵:
只要在當前實例的原型鏈上,均可以查詢獲得,檢測出來的結果都是true
oDiv instanceof EventTarget // true oDiv instanceof Node // true
缺點:不肯定性
// 在類的原型繼承中,最後檢測出來的結果未必正確 function Fn() {} Fn.prototype = new Array(); // 原型繼承 var f = new Fn(); console.log(f instanceof Function); // false console.log(f instanceof Array); // true // f->Fn.prototype -> Array.prototype -> Object.prototype
利用這個特性,能夠建立類數組(索引和length),可使用數組的方法(在它的原型鏈上就應該有Array.prototype了)
function arg() { // 存放私有屬性 this.index = 0; this.legnth = 0; } arg.prototype = Array.prototype;
constructor
構造函數,做用和instanceof很是的類似
能夠處理基本數據類型
var num = 10; console.log(num.constructor === Number); // true
construtor檢測Object和instanceof不同,通常狀況下是檢測不了的。
var reg = /\d+/; console.log(reg.constructor === RegExp); // true console.log(reg.constructor === Object); // false
侷限性:
把類的原型進行從新,在從新的過程當中,頗有可能出現,把以前的constructor覆蓋,這樣檢測出來的結果不許確。(原型繼承)
對於特殊的數據類型null
和undefined
,它們的所屬類是Null
和Undefined
,可是瀏覽器把這兩個類保護起來,不容許用戶使用。
Object.prototype.toString.call();
Object 瀏覽器內置類,全部對象數據的基類。
類型檢測最爲準確.
首先獲取Object原型上的toString方法,讓方法執行,而且改變方法中的this關鍵詞的指向.Object.prototype.toString
它的做用是返回當前方法的執行主體(方法中的this)所屬類的詳細信息
toString
的理解:
字面意思是轉化爲字符串,可是某些toString方法不只僅是轉換爲字符串。
console.log((1).toString()); // '1' // 使用的是:Number.prototype.toString(); // Number上的有toString()參數能夠有進制轉換 console.log((1).__proto__.__proto__.toString()); // '[object Object]' // 使用的是:Object.prototype.toString();
對於Number
,Boolean
,String
,Date
,RegExp
,Array
,Function
原型上的toString
都是把當前的數據類型轉換爲字符串類型(它們的做用僅僅使用轉換爲字符串的)
Object.prototype.toString.call();
並非用來轉換爲字符串,而是一種形式[object Object]
的格式的字符串
console.log(({}).toString()); // [object Object] console.log(Math.toString()); // [object Math] console.log(Object.prototype.toString.call([])); // [object Array]
返回當前主體的類的屬於信息
var obj = {}; console.log(obj.toString()); // toString中的this是誰的obj,返回的是obj所屬類的信息 --> [當前實例是那種數據類型(這個是固定死的,全部數據類型都是對象類型) 當前主體的所屬類]
冒泡排序
var arr = [1, 2, 29, 12, 12, 19, 230, 120, 22] function bubble_sort (arr) { var n = arr.length for (var i = 0; i < n - 1; i++) { for (var j = 0; j < n - 1 - i; j++) { if (arr[j] > arr[j+1]) { var tmp = arr[j] arr[j] = arr[j+1] arr[j+1] = tmp } } } } console.log(arr) bubble_sort(arr) console.log(arr)
選擇排序
var arr = [1, 2, 29, 12, 12, 19, 230, 120, 22] function select_sort (arr) { var n = arr.length for (var j = 0; j < n-1; j++) { var min_index = j for (var i = j+1; i < n; i++) { // 1 ~ n-1 時間複雜度:1-n, 2-n, 3-n if (arr[min_index] > arr[i]) { min_index = i } } var tmp = arr[min_index] arr[min_index] = arr[j] arr[j] = tmp } } console.log(arr) select_sort(arr) console.log(arr)
快速排序
function query_sort (arr, first, last) { if (first >= last) return mid_value = arr[first] low = first high = last while (low < high) { while (low < high && arr[high] >= mid_value) { high -= 1 } arr[low] = arr[high] while (low < high && arr[low] < mid_value) { low += 1 } arr[high] = arr[low] } arr[low] = mid_value query_sort(arr, first, low-1) query_sort(arr, low+1, last) } li = [54, 26, 93, 17, 77, 34] console.log(li) query_sort(li, 0, li.length-1) console.log(li)
鏈表: 一種物理存儲單元上非連續、非順序的存儲結構,數據元素的邏輯順序是經過鏈表中的指針連接次序實現的。
鏈表存儲有序的元素集合,但不一樣於數組,鏈表中的元素在內存中並非連續放置的。每一個元素由一個存儲元素本省的節點和一個指向下一個元素的引用(也稱指針或連接)組成。
鏈表優勢:
添加或移動元素的時候不須要移動其它元素。
鏈表缺點:
鏈表須要使用指針,所以實現鏈表時須要額外注意。數組的另外一個細節是能夠直接訪問任何位置的任何元素,而要想範文鏈表中間的一個元素,須要從起點(表頭)開始迭代列表知道找到所需的元素。
C++ struct node { int payload; node* next; } // JS {} new Object();
開闢內存,裏面存放key 和value, 經過指針指向下一個元素。是無序的,經過指針來尋找下一個位置。
解決插入和刪除的問題,經過指針直接指向某個一位置,尋找到該位置,插入或刪除。
對於鏈表來講,拿第一個元素和拿第一億零一個元素使用的時間是不一樣的,隨機訪問效率低下。
鏈表會指針每次判斷,直到尋找到位置相同。
鏈表,和數組爲基礎,能夠表現成或變形:隊列
,棧
,圖
,hash表
棧:只能加在頭部
隊列: 一頭近一頭出
圖:表現爲鏈接的形式,每個節點保存一堆的指針。 也能夠經過連接矩陣的方式保存
hash表須要隨機訪問,第一層使用的是數組,第二層使用的是鏈表.
時間複雜度O(n)表示程序運行時間跟n有關,而且是線性關係。
空間複雜度O(1),表示所需空間爲常量,而且與n無關。
// JavaScript鏈表 function LinkedList() { var Node = function(element) { // 輔助類 this.element = element; // 添加到列表的值 this.next = null; // 下一個節點項的指針 } var length = 0; // 存儲列表項的數量 var head = null; // 存儲第一個節點的引用 // 向列表尾部添加一個新的項 // 列表最後一個節點的下一個元素始終是 null this.append = function (element) { // 思路: // 1. 列表爲空,添加的是第一個元素 // 2. 列表不爲空,向其追加元素 var node = new Node(element), current; if (head == null) { // 列表中第一個節點 head = node; } else { current = head; // 循環列表,直到找到最後一項 while(current.next) { current = current.next; } // 找到最後一項,將其next賦爲node,創建連接 current.next = node; } // 更新列表的長度 length++; } // 向列表的特定位置插入一個新的項 this.insert = function(position, element) { // 檢查越界值 if (position >= 0 && position <= length) { var node = new Node(element), previous, index, current = head; // 在第一個位置添加 if (position == 0) { node.next = current; head = node; } else { while(index++ < position) { previous = current; current = current.next; } node.next = current; previous.next = node; } length++; // 更新列表長度 return true; } else { return false; } } // 從列表中移除一項 this.remove = function(element) { var index = this.indexOf(element); return this.removeAt(index); } // 從列表的特定位置移除一項 // 第一種是從特定位置移除一個元素,第二種是根據元素的值移除元素 // 給定位置移除一個元素 this.removeAt = function(position) { // 思路: 1. 移除第一個元素 // 2. 移除第一個之外的任一元素 // 檢查越界 if (position > -1 && position < length) { var current = head, previous, index = 0; // current做用:移除元素的引用 // previous 做用:移除元素的前一個元素的引用 // 移除第一項 if (position == 0) { // 移除方法,堆中不引用地址,GC回收機制自動回收. head = current.next; // head 指向列表的第二個元素。 效果:current變量就是對列表中第一個元素的引用。若是把head賦爲current.next,就會移除第一個元素。 } else { while(index++ < position) { previous = current; current = current.next; } // 將previous與current的下一項連接起來:跳過current,從而移除它 previous.next = current.next; } length--; return current.element; } else { return null; } } // 返回元素在列表中的索引。若是列表中沒有該元素則返回 -1 this.indexOf = function(element) { var current = head, index = -1; while(current) { if (element == current.element) { return index; } index++; current = current.next; } return -1; } // 若是鏈表中不包含任何元素,返回 true ,若是鏈表長度大於0則返回 false this.isEmpty = function() { return length === 0; } // 返回鏈表包含的元素個數。 this.size = function() { return length; } // 因爲列表項使用了 Node 類,就須要重寫繼承自JavaScript對象默認的toString 方法,讓其只輸出元素的值。 this.toString = function() { var current = head, string = ''; while(current) { string = current.element; current = current.next; } return stirng; } this.getHead = function() { return head; } } var list = new LinkedList(); list.append(15); list.append(10);
經過數組反轉,推導出大O表達式.
// 思路: // 1. 第一個和最後一個換 // 2. 第二個和倒二個換 // 3. 不斷往中間逼進 // 4. 奇數中間一個不動,完成反轉;偶數完成反轉 var arr = [12, 34, 5, 3, 34, 22, 4]; function reverse(arr) { let left = 0; let right = arr.length - 1; while(left < right) { // 循環給個終止條件, 左邊不小於右邊,跳出循環 // 位置置換 let tmp = arr[left]; arr[left] = arr[right]; arr[right] = tmp; left++; right--; } }
看執行的時候,須要分析指令,每條指令執行都須要時間,並非全部指令都根據數據量有關係。有的指令只執行一遍, 例如let left = 0;
,let right = arr.length - 1;
,無論數組再長,再大,這兩條指令執行的時間不會變化。
大O表達式:O(n) <= C1 * n + C2;
大O符號 :在計算機科學中,它在分析算法複雜性的方面很是有用。
斐波那契數列的遞歸實現
在數學上,菲波那切數列是以遞歸的方法來定義:
從第3項開始,每一項都等於前兩項之和
// JavaScript 實現 菲波那切數列算法 , 遞歸算法 /** * 斐波那契數列 * * 遞歸算法和如何計算時間複雜度 */ function fib(n) { // 經過天然概括法 遞歸算法 O(2^n) // <= c0*n + c1; c0*2^n + c1 // console.log(n); // n = 0, n=1的時候 爲 O(1), //T(n-1) , T(n) = O(2^n-1) + O(2^n-2) + O(1) = 1.5*O(2^n-1) + O(1) <= 2*O(2^n-1) + O(1) <= O(2^n-1) if (n < 2) { return n; } else { return fib(n-1) + fib(n-2); // T(n) = T(n-1) + T(n-2) + O(1); } } for (let i=0; i<50; i++) { console.log(fib(i)); }
遞歸算法計算時間複雜度
經過天然概括法計算時間複雜度
遞歸算法在菲波那切數列中應用時間複雜度很高,從內存的角度看,每次開闢一個新的內存空間,一直沒有被銷燬回收,佔用巨大內存。
時間複雜度爲:O(2*n)
// JavaScirpt 迭代方式實現 菲波那切數列算法 function fibonacci(n) { var f = []; f[1] = 1; f[2] = 1; for (var i=3; i<n; i++) { f[i] = f[i-1] + f[i-2]; } return f; }
差數淘汰:
已知n我的(以編號1,2,3...n分別表示)圍坐在一張圓桌周圍。從編號爲k的人開始報數,數到m的那我的出列;他的下一我的又從1開始報數,數到m的那我的又出列;依此規律重複下去,直到圓桌周圍的人所有出列。
單向鏈表: 第一個單元,第二個單元... 最後一個接地.
循環鏈表: 第一個單元,第二個單元... 最後一個鏈表接回到第一個鏈表單元.
// reset(), current(), next(), prev(), search(), end() Array.prototype.pointer = 0; // 模擬數組內部指針 // reset 函數,0將數組內部指針歸爲,(指向第一個元素) var reset = function (arrayObj) { if (!(arrayObj instanceof Array)) { console.warn('reset function arguments typeof error'); return; } // 重置指針 arrayObj.pointer = 0; } // current 函數,返回數組內部指針的當前元素 var current = function (arrayObj) { if (!(arrayObj instanceof Array)) { console.warn('current function arguments typeof error'); return; } return arrayObj[arrayObj.pointer]; } // end 函數,將數組內部指針指向最後一個元素,並返回最後一個元素的當前位置 var end = function (arrayObj) { if (!(arrayObj instanceof Array)) { console.warn('end function arguments typeof error'); return; } arrayObj.pointer = arrayObj.length - 1; return arrayObj[arrayObj.pointer]; } // next函數,將數組內部指針下移一位,若是已經指向最後一個元素則返回false var next = function (arrayObj) { if (!(arrayObj instanceof Array)) { console.warn('next function arguments typeof error'); return; } // 指針後移 arrayObj.pointer++; // 判斷指針是否在最後一個 if (typeof arrayObj[arrayObj.pointer] == 'undefined') { arrayObj.pointer--; // 重置回最後一個 return false; } return true; } // prev函數,將數組內部指針上移一位,若是已經指向第一個元素則返回false var prev = function (arrayObj) { if (!(arrayObj instanceof Array)) { console.warn('prev function arguments error'); return; } // 指針前移 arrayObj.pointer--; // 判斷指針是否第一個 if (typeof arrayObj[arrayObj.pointer] == 'undefind') { arrayObj.pointer++; // 重置回第一個 return false; } return arrayObj[arrayObj.pointer]; } // unset 函數, 刪除指定的數組元素 var unset = function (idx, arrayObj) { if (!(arrayObj instanceof Array)) { console.warn('unset function arguments error'); return; } if (typeof arrayObj[idx] == 'undefined') { console.warn("unset() 函數參數 idx 錯誤!不存在此元素!"); return false; } arrayObj.splice(idx, 1); return true; } // search 函數,經過數組鍵值返回數組鍵名 var search = function (value, arrayObj) { if (!(arrayObj instanceof Array)) { console.warn('search function arguments error'); return; } for (var i in arrayObj) { if (arrayObj[i] == value) { return i; // 返回鍵名 } } return false; } // getKingMonkey 主函數 // n 只猴子,數到 m // 4我的數,到3中止一次。 function getKingMonkey(n, m) { debugger; // 1. 構造元素 // 2. 循環 a = new Array(); for (var i = 1; i <= n; i++) { a[i] = i; } a[0] = 0; // 補第0個位置 // [undefined × 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] --> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] unset(0, a); // 刪除第0個元素 // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] reset(a); // 數組內部指針歸爲0 while (a.length > 1) { for (counter = 1; counter <= m; counter++) { if (next(a)) { // 是否具備後續指針 if (counter == m) { unset(search(prev(a), a), a); } } else { reset(a); // 重置指針到第0位 if (counter == m) { unset(search(end(a), a), a); reset(a); // 重置指針到第0位 } } } } return current(a); } console.log(getKingMonkey(4, 3)); // 1
對於代碼沒法去看時間複雜度,通常從邏輯上考慮。
function getKingMonkey(n, m) { debugger; // 1. 構造元素 // 2. 循環 a = new Array(); for (var i = 1; i <= n; i++) { a[i] = i; } a[0] = 0; // 補第0個位置 // [undefined × 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] --> [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] unset(0, a); // 刪除第0個元素 // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] reset(a); // 數組內部指針歸爲0 while (a.length > 1) { for (counter = 1; counter <= m; counter++) { if (next(a)) { // 是否具備後續指針 if (counter == m) { unset(search(prev(a), a), a); } } else { reset(a); // 重置指針到第0位 if (counter == m) { unset(search(end(a), a), a); reset(a); // 重置指針到第0位 } } } } return current(a); }
假設n次
主函數的getKingMonkey
中的外層while
循環會執行n-1
次, for循環會執行 k-1
次, 加上其它固定執行命令代碼時間。 整體爲:(n-1) * (k-1) + c
(n-1) * (k-1) + c ≈ nk - C0*n - C1*k + c ≈ nk - C0*n - C1*k 時間複雜度:O(nk) n可能很大,k可能很大
合併兩個已經排序好的鏈表
// C++ 代碼實現 #include <iostream> struct node{ int payload; node* next; node(int payload) {this->payload = payload; next = nullptr;}; }; class linkedlist{ node *head, *tail; public: //constructor, initialize head and tail to nullptr linkedlist() : head(nullptr),tail(nullptr){}; //push a new node at the end of the list void push_back(int value){ if(empty()){ head = tail = new node(value); }else{ tail->next = new node(value); tail = tail->next; } } //return the value stored in the first node int front(){ if(empty()){ throw "The list is empty."; } return head->payload; } //remove the first node void pop_front(){ if(empty()){ throw "The list is empty."; } node* first_node = head; head = head->next; delete first_node; } bool empty(){ return head == nullptr; } void output(){ node* iterator = head; while(iterator){ std::cout << iterator->payload << " "; iterator = iterator->next; } std::cout << std::endl; } }; //merge two sorted linked list, return a new list linkedlist merge(linkedlist a, linkedlist b){ linkedlist result; while(!a.empty() || !b.empty()){ // 考慮極端狀況, a 鏈表爲空,把b鏈表中的一個個添加 結果鏈表中; b 鏈表爲空,把a鏈表中的一個個添加 結果鏈表中 if(a.empty()){ result.push_back(b.front()); b.pop_front(); }else if(b.empty()){ result.push_back(a.front()); a.pop_front(); }else if(a.front() > b.front()){ // a,b 鏈表都不爲空, 看a和b 的head 誰大 (比較值的大小)。 a > b , b添加到結果鏈表中 result.push_back(b.front()); b.pop_front(); }else{ result.push_back(a.front()); // a < b , a添加到結果鏈表中 a.pop_front(); } } return result; } int main(){ linkedlist a,b; linkedlist result = merge(a, b); result.output(); return 0; }
時間複雜度計算:
linkedlist merge(linkedlist a, linkedlist b){ linkedlist result; while(!a.empty() || !b.empty()){ // 考慮極端狀況, a 鏈表爲空,把b鏈表中的一個個添加 結果鏈表中; b 鏈表爲空,把a鏈表中的一個個添加 結果鏈表中 if(a.empty()){ result.push_back(b.front()); b.pop_front(); }else if(b.empty()){ result.push_back(a.front()); a.pop_front(); }else if(a.front() > b.front()){ // a,b 鏈表都不爲空, 看a和b 的head 誰大 (比較值的大小)。 a > b , b添加到結果鏈表中 result.push_back(b.front()); b.pop_front(); }else{ result.push_back(a.front()); // a < b , a添加到結果鏈表中 a.pop_front(); } } return result; }
邏輯上的循環,並不知道具體會循環多少次,去看關鍵邏輯。
假設:a的長度是m,b的長度是n。
總共循環了m+n
次。
(m+n) * O(1) 時間複雜度爲: O(m+n)
雜亂無章的序列,能夠從小到大,能夠從大到小,處理以後返回.
歸併排序是比較次數最少的一種排序
二分法 時間複雜度: O(m+n)
二分法 使用遞歸方式也稱之爲 自頂向下的算法
二分法 使用普通循環的方式排序,也稱之爲 自低向上的算法
遞歸的第一種方式:
數組的位置二分法排序:
init(); var array; var left, right; function init() { array = [3, 4, 2, 1, 7, 5, 8, 9, 0, 6]; left = 0; right = array.length - 1; mergeSort(array, left, right); } function mergeSort(array, left, right) { if (left >= right) return; var middle = left + parseInt((right - left) / 2); mergeSort(array, left, middle); mergeSort(array, middle + 1, right); merge(array, left, middle, right); } function merge(array, left, middle, right) { var i = left, j = middle + 1; var aux = []; for (var k = left; k <= right; k++) { aux[k] = array[k]; } for (var a = left; a <= right; a++) { if (i > middle) { array[a] = aux[j++]; } else if (j > right) { array[a] = aux[i++]; } else if (parseInt(aux[i]) < parseInt(aux[j])) { array[a] = aux[i++]; } else { array[a] = aux[j++]; } } } console.log(array);
遞歸的第二種方式
var arr = [3, 4, 2, 1, 7, 5, 8, 9, 0, 6]; function mergeSort(arr) { var len = arr.length; var left, right, middle = Math.floor(arr.length / 2); // (left + right) / 2 if (len <= 1) { return arr; } left = arr.slice(0, middle); // 獲得下標從0~middle-1的數組 right = arr.slice(middle); // 獲得下標從index開始到末尾的數組 return merge(mergeSort(left), mergeSort(right)); // 遞歸 } function merge(left, right) { // 該函數與快排相似,每次left或者right都是要shift掉第一個元素,表示left或者right是會變化的,最後arr.concat, // 由於不知道left或者right其中一個哪一個剩下元素,因此要將剩下的元素給加上 var arr = []; while (left.length && right.length) { if (left[0] < right[0]) { arr.push(left.shift()); } else { arr.push(right.shift()); } } return arr.concat(left, right); } console.log(mergeSort(arr));
普通循環實現歸併排序
思路:
從第一項和第二項合併並排序,第三和第四合並並排序,一直循環反覆.
新出來的項的序列。
從第一項和第二項合併並排序,循環反覆。
循環反覆.
5, 2, 1, 4, 3 ↓ 2,5 1,4 3 ↓ 1,2,4,5 3, ↓ 1,2,3,4,5
實現代碼:
function isArray1(arr) { if (Object.prototype.toString.call(arr) == '[object Array]') { return true; } else { return false; } } function merge(left, right) { var result = []; if (!isArray1(left)) { left = [left]; } if (!isArray1(right)) { right = [right]; } while (left.length > 0 && right.length > 0) { if (left[0] < right[0]) { result.push(left.shift()); } else { result.push(right.shift()); } } return result.concat(left).concat(right); } function mergeSort(arr) { var len = arr.length; var lim, work = []; var i, j, k; if (len == 1) { return arr; } for (i = 0; i < len; i++) { work.push(arr[i]); } work.push([]); for (lim = len; lim > 1;) {// lim爲分組長度 for (j = 0, k = 0; k < lim; j++ , k = k + 2) { work[j] = merge(work[k], work[k + 1]); } work[j] = []; lim = Math.floor((lim + 1) / 2); } return work[0]; } var arr1 = [7, 5, 9, 8]; var arr2 = [7, 5, 9, 8, 3, 20, 6, 1, 2]; console.log(mergeSort(arr1)); // 5,7,9,8 console.log(mergeSort(arr2)); // 1,2,3,5,6,7,8,9,20
以一個項爲原始基點,全部分別放置左右大小,再次選擇基點,再分別放置左右大小,直至排序完成。
function quick_sort(array) { function sort(prev, numsize) { var nonius = prev; var j = numsize - 1; var flag = array[prev]; if ((numsize - prev) > 1) { while (nonius < j) { for (; nonius < j; j--) { if (array[j] < flag) { array[nonius++] = array[j]; //a[i] = a[j]; i += 1; break; }; } for (; nonius < j; nonius++) { if (array[nonius] > flag) { array[j--] = array[nonius]; break; } } } array[nonius] = flag; sort(0, nonius); sort(nonius + 1, numsize); } } sort(0, array.length); return array; }
時間複雜度:
快速排序是一分爲二的算法n/2
T(n) = 2T(n/2) + O(n) // 有左右兩次調用函數 // 根據主定理,推出: T(n) = O(n*logn)
遊戲規則:
在這個遊戲中,孩子們圍成一個圓圈,把花盡快地傳遞給旁邊的人。某一時刻傳花中止,這個時候花在誰手裏,誰就退出圓圈結束遊戲。重複這個過程,直到只剩一個孩子(勝者)。
//** Queue function Queue() { var items = []; this.enqueue = function (element) { items.push(element); }; this.dequeue = function () { return items.shift(); }; this.front = function () { return items[0]; }; this.isEmpty = function () { return items.length == 0; }; this.clear = function () { items = []; }; this.size = function () { return items.length; }; this.print = function () { console.log(items.toString()); }; } function hotPotato(nameList, num) { var queue = new Queue(); for (var i = 0; i < nameList.length; i++) { queue.enqueue(nameList[i]); } var eliminated = ''; while (queue.size() > 1) { for (var i = 0; i < num; i++) { queue.enqueue(queue.dequeue()); } eliminated = queue.dequeue(); console.log(eliminated + '在擊鼓傳花遊戲中被淘汰。'); } return queue.dequeue(); } var names = ['A', 'B', 'C', 'D', 'E', 'F']; var winner = hotPotato(names, 5); console.log('勝利者:' + winner);
是數組和鏈表的一種限制
棧,只能從一頭操做,(增,改都是隻能操做一頭),邏輯上是後進先出。
隊列,能夠認爲是容器,也有訪問的限制,邏輯上是先進先出. 新來的後面出去。
棧在實際中的應用,是函數的調用。隊列在實際中的應用,併發的產生數據,都放在隊列中。