以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);
是数组和链表的一种限制
栈,只能从一头操做,(增,改都是只能操做一头),逻辑上是后进先出。
队列,能够认为是容器,也有访问的限制,逻辑上是先进先出. 新来的后面出去。
栈在实际中的应用,是函数的调用。队列在实际中的应用,并发的产生数据,都放在队列中。