├── test.js ├── queue ├── queueBasedOnArray.js ├── QueueBasedOnLinkedList.js ├── QueueBasedOnObject.js └── DueueBasedOnObject.js ├── sorts ├── insertionSort.js ├── selectionSort.js ├── bubbleSort.js ├── mergeSort.js └── quickSort.js ├── stack ├── StackBasedOnLinkedList.js ├── StackBasedOnObject.js ├── SampleBrower.js └── StackBasedOnArray.js ├── linkedlist ├── SinglyLinkedList.js └── LinkedListAlgo.js ├── set └── Set.js ├── README.md ├── tree └── BinarySearchTree.js └── array └── Array.md /test.js: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /queue/queueBasedOnArray.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 基于数组实现队列 3 | */ 4 | let Queue = (function () { 5 | const items = new WeakMap(); 6 | class Queue { 7 | constructor() { 8 | items.set(this, []); 9 | } 10 | // 入队 11 | enqueue(element) { 12 | let q = items.get(this); 13 | q.push(element); 14 | } 15 | // 出队 16 | dequeue() { 17 | let q = items.get(this); 18 | let r = q.shift(); 19 | return r; 20 | } 21 | } 22 | return Queue; 23 | })(); -------------------------------------------------------------------------------- /sorts/insertionSort.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 插入排序 3 | */ 4 | const insertionSort = (arr) => { 5 | if(arr.length <= 1) return; 6 | const len = arr.length; 7 | for(let i = 1; i < len; i++) { 8 | const temp = arr[i]; 9 | let j = i - 1; 10 | for(; j >= 0; j--) { 11 | if(arr[j] > temp) { 12 | arr[j + 1] = arr[j]; 13 | }else{ 14 | break; 15 | } 16 | } 17 | arr[j + 1] = temp; 18 | } 19 | console.log(arr); 20 | } 21 | 22 | const test = [4, 5, 6, 3, 2, 1]; 23 | insertionSort(test) -------------------------------------------------------------------------------- /sorts/selectionSort.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 选择排序 3 | */ 4 | const selectionSort = (arr) => { 5 | if(arr.length <= 1) return; 6 | const len = arr.length; 7 | for(let i = 0; i < len - 1; i++) { 8 | let minIndex = i; 9 | for(let j = i + 1; j < len; j++) { 10 | if(arr[j] < arr[minIndex]) { 11 | minIndex = j; 12 | } 13 | } 14 | const temp = arr[i]; 15 | arr[i] = arr[minIndex]; 16 | arr[minIndex] = temp; 17 | } 18 | console.log(arr); 19 | } 20 | 21 | const test = [4, 5, 6, 3, 2, 1]; 22 | selectionSort(test) -------------------------------------------------------------------------------- /sorts/bubbleSort.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 冒泡排序 3 | */ 4 | const bubbleSort = (arr) => { 5 | if(arr.length <= 1) return; 6 | const len = arr.length; 7 | for(let i = 0; i < len; i++) { 8 | let hasChange = false; 9 | for(let j = 0; j < len - i - 1; j++) { 10 | if(arr[j] > arr[j + 1]) { 11 | const temp = arr[j + 1]; 12 | arr[j + 1] = arr[j]; 13 | arr[j] = temp; 14 | hasChange = true; 15 | } 16 | } 17 | if(!hasChange) break; 18 | } 19 | console.log(arr); 20 | } 21 | 22 | const test = [4, 5, 6, 3, 2, 1]; 23 | bubbleSort(test) -------------------------------------------------------------------------------- /sorts/mergeSort.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 归并排序 3 | */ 4 | const mergeSortRec = (arr) => { 5 | let len = arr.length; 6 | if(len === 1) { 7 | return arr; 8 | } 9 | let mid = Math.floor(len / 2); 10 | let left = arr.slice(0, mid); 11 | let right = arr.slice(mid, len); 12 | 13 | return merge(mergeSortRec(left), mergeSortRec(right)); 14 | } 15 | 16 | function merge(left, right) { 17 | let result = []; 18 | let il = 0; 19 | let ir = 0; 20 | 21 | while(il < left.length && ir < right.length) { 22 | if(left[il] <= right[ir]) { 23 | result.push(left[il++]); 24 | }else{ 25 | result.push(right[ir++]); 26 | } 27 | } 28 | 29 | while(il < left.length) { 30 | result.push(left[il++]); 31 | } 32 | 33 | while(ir < right.length) { 34 | result.push(right[ir++]); 35 | } 36 | 37 | return result; 38 | } 39 | 40 | const test = [11, 8, 3, 9, 7, 1, 2, 5, 3]; 41 | console.log(mergeSortRec(test)) 42 | 43 | -------------------------------------------------------------------------------- /sorts/quickSort.js: -------------------------------------------------------------------------------- 1 | const quickSort = (arr) => { 2 | quick(arr, 0, arr.length - 1); 3 | } 4 | 5 | function quick(arr, left, right) { 6 | let index; 7 | if(arr.length > 1) { 8 | index = partition(arr, left, right); 9 | if(left < index - 1) { 10 | quick(arr, left, index - 1); 11 | } 12 | if(index < right) { 13 | quick(arr, index, right); 14 | } 15 | } 16 | } 17 | 18 | function partition(arr, left, right) { 19 | let pivot = arr[Math.floor((left + right) / 2)]; 20 | let i = left; 21 | let j = right; 22 | 23 | while(i <= j) { 24 | while(arr[i] < pivot) { 25 | i++; 26 | } 27 | while(arr[j] > pivot) { 28 | j--; 29 | } 30 | if(i <= j) { 31 | swap(arr, i, j); 32 | i++; 33 | j--; 34 | } 35 | } 36 | 37 | return i; 38 | } 39 | 40 | function swap(arr, i, j) { 41 | // const temp = arr[i]; 42 | // arr[i] = arr[j]; 43 | // arr[j] = temp; 44 | [arr[i], arr[j]] = [arr[j], arr[i]] 45 | } 46 | 47 | const test = [11, 8, 3, 9, 7, 1, 2, 5]; 48 | quickSort(test); 49 | console.log(test) -------------------------------------------------------------------------------- /queue/QueueBasedOnLinkedList.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 基于链表实现的队列。 3 | */ 4 | 5 | class Node { 6 | constructor(element) { 7 | this.element = element 8 | this.next = null 9 | } 10 | } 11 | 12 | class QueueBasedOnLinkedList { 13 | constructor() { 14 | this.head = null 15 | this.tail = null 16 | } 17 | 18 | enqueue(value) { 19 | if (this.head === null) { 20 | this.head = new Node(value) 21 | this.tail = this.head 22 | } else { 23 | this.tail.next = new Node(value) 24 | this.tail = this.tail.next 25 | } 26 | } 27 | 28 | dequeue() { 29 | if (this.head !== null) { 30 | const value = this.head.element 31 | this.head = this.head.next 32 | return value 33 | } else { 34 | return -1 35 | } 36 | } 37 | } 38 | // Test 39 | const newQueue = new QueueBasedOnLinkedList() 40 | // 插入元素 41 | newQueue.enqueue(1) 42 | newQueue.enqueue(2) 43 | newQueue.enqueue(3) 44 | // 获取元素 45 | let res = 0 46 | console.log('-------获取dequeue元素------') 47 | while (res !== -1) { 48 | res = newQueue.dequeue() 49 | console.log(res) 50 | } -------------------------------------------------------------------------------- /queue/QueueBasedOnObject.js: -------------------------------------------------------------------------------- 1 | class Queue { 2 | constructor() { 3 | this.count = 0; 4 | this.lowestCount = 0; 5 | this.items = {}; 6 | } 7 | enqueue(element) { 8 | this.items[this.count++] = element; 9 | } 10 | dequeue() { 11 | if(this.isEmpty()) { 12 | return null; 13 | } 14 | let ret = this.items[this.lowestCount]; 15 | delete this.items[this.lowestCount]; 16 | this.lowestCount++; 17 | return ret; 18 | } 19 | peek() { 20 | if(this.isEmpty()) { 21 | return null; 22 | } 23 | return this.items[this.lowestCount]; 24 | } 25 | isEmpty() { 26 | return this.count === this.lowestCount; 27 | } 28 | size() { 29 | return this.count - this.lowestCount; 30 | } 31 | clear() { 32 | this.count = 0; 33 | this.lowestCount = 0; 34 | this.items = {}; 35 | } 36 | toString() { 37 | if(this.isEmpty()) { 38 | return ''; 39 | } 40 | let objString = `${this.items[this.lowestCount]}`; 41 | for(let i = this.lowestCount + 1; i < this.count; i++) { 42 | objString += `,${this.items[i]}`; 43 | } 44 | return objString; 45 | } 46 | } 47 | 48 | const queue = new Queue(); 49 | console.log(queue.isEmpty()); 50 | queue.enqueue('John'); 51 | queue.enqueue('Jack'); 52 | console.log(queue.toString()); 53 | queue.dequeue(); 54 | console.log(queue.toString()); 55 | -------------------------------------------------------------------------------- /stack/StackBasedOnLinkedList.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * 基于链表实现的栈。 4 | */ 5 | class Node { 6 | constructor(element) { 7 | this.element = element; 8 | this.next = null; 9 | } 10 | } 11 | 12 | class StackBasedOnLinkedList { 13 | constructor() { 14 | this.top = null; 15 | } 16 | push(value) { 17 | const node = new Node(value); 18 | if(this.top === null) { 19 | this.top = node; 20 | }else{ 21 | node.next = this.top; 22 | this.top = node; 23 | } 24 | } 25 | pop() { 26 | if(this.top === null) { 27 | return -1; 28 | } 29 | const value = this.top.element; 30 | this.top = this.top.next; 31 | return value; 32 | } 33 | clear() { 34 | this.top = null; 35 | } 36 | display() { 37 | if(this.top !== null) { 38 | let temp = this.top; 39 | while(temp !== null) { 40 | console.log(temp.element) 41 | temp = temp.next; 42 | } 43 | } 44 | } 45 | } 46 | 47 | // Test 48 | const newStack = new StackBasedOnLinkedList() 49 | newStack.push(1) 50 | newStack.push(2) 51 | newStack.push(3) 52 | // 获取元素 53 | let res = 0 54 | console.log('-------获取pop元素------') 55 | while (res !== -1) { 56 | res = newStack.pop() 57 | console.log(res) 58 | } 59 | 60 | module.exports = StackBasedOnLinkedList; -------------------------------------------------------------------------------- /stack/StackBasedOnObject.js: -------------------------------------------------------------------------------- 1 | class Stack { 2 | constructor() { 3 | this.items = {}; 4 | this.count = 0; 5 | } 6 | push(element) { 7 | this.items[this.count++] = element; 8 | } 9 | pop() { 10 | if(this.isEmpty()) { 11 | return undefined; 12 | } 13 | this.count--; 14 | let ret = this.items[this.count]; 15 | delete this.items[this.count]; 16 | return ret; 17 | } 18 | peek() { 19 | if(this.isEmpty()) { 20 | return undefined; 21 | } 22 | return this.items[this.count - 1]; 23 | } 24 | isEmpty() { 25 | return this.count === 0; 26 | } 27 | clear() { 28 | // this.items = {}; 29 | // this.count = 0; 30 | while(this.isEmpty()) { 31 | this.pop(); 32 | } 33 | } 34 | toString() { 35 | if(this.isEmpty()) { 36 | return ''; 37 | } 38 | let objString = `${this.items[0]}`; 39 | for(let i = 1; i < this.count; i++) { 40 | objString += `,${this.items[i]}`; 41 | } 42 | return objString; 43 | } 44 | } 45 | 46 | function decimalToBinary(decNumber) { 47 | const remStack = new Stack(); 48 | let number = decNumber; 49 | let rem; 50 | let binaryString = ''; 51 | 52 | while(number > 0) { 53 | rem = Math.floor(number % 2); 54 | remStack.push(rem); 55 | number = Math.floor(number / 2); 56 | } 57 | 58 | while(!remStack.isEmpty()) { 59 | binaryString += remStack.pop().toString(); 60 | } 61 | 62 | return binaryString; 63 | } 64 | 65 | console.log(decimalToBinary(11)) -------------------------------------------------------------------------------- /stack/SampleBrower.js: -------------------------------------------------------------------------------- 1 | const Stack = require('./StackBasedOnArray.js'); 2 | 3 | class SampleBrowser { 4 | constructor() { 5 | this.normalStack = new Stack(); 6 | this.backStack = new Stack(); 7 | } 8 | // 正常浏览页面 9 | push(url) { 10 | this.normalStack.push(url); 11 | this.backStack.clear(); 12 | this.displayAllStack(); 13 | } 14 | // 后退 15 | back() { 16 | const value = this.normalStack.pop(); 17 | if(value !== null) { 18 | this.backStack.push(value); 19 | this.displayAllStack() 20 | }else{ 21 | console.log('无法后退'); 22 | } 23 | } 24 | // 前进 25 | front() { 26 | const value = this.backStack.pop(); 27 | if(value !== null) { 28 | this.normalStack.push(value); 29 | this.displayAllStack() 30 | }else{ 31 | console.log('无法前进') 32 | } 33 | } 34 | // 打印栈内数据 35 | displayAllStack() { 36 | console.log('---后退页面---') 37 | this.backStack.display() 38 | console.log('---浏览页面---') 39 | this.normalStack.display() 40 | } 41 | } 42 | 43 | // Test 44 | const browser = new SampleBrowser() 45 | browser.push('www.google.com') 46 | browser.push('www.baidu.com') 47 | browser.push('www.github.com') 48 | // 后退 49 | browser.back() 50 | browser.back() 51 | browser.back() 52 | browser.back() 53 | browser.back() 54 | browser.front() 55 | browser.front() 56 | browser.front() 57 | browser.front() 58 | browser.front() 59 | browser.push('www.new.com') -------------------------------------------------------------------------------- /stack/StackBasedOnArray.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 基于数组实现栈 3 | */ 4 | let Stack = (function() { 5 | // items 为栈的私有变量,禁止外部访问 6 | const items = new WeakMap(); 7 | class Stack { 8 | constructor() { 9 | items.set(this, []); 10 | } 11 | // 入栈 12 | push(item) { 13 | let s = items.get(this); 14 | s.push(item); 15 | } 16 | // 出栈 17 | pop() { 18 | if(this.isEmpty()) { 19 | return null; 20 | } 21 | let s = items.get(this); 22 | let r = s.pop(); 23 | return r; 24 | } 25 | // 查看栈顶 26 | peek() { 27 | if(this.isEmpty()) { 28 | return null; 29 | } 30 | let s = items.get(this); 31 | return s[s.length - 1]; 32 | } 33 | // 检查栈是否为空 34 | isEmpty() { 35 | let s = items.get(this); 36 | return s.length === 0; 37 | } 38 | // 返回栈的长度 39 | size() { 40 | let s = items.get(this); 41 | return s.length; 42 | } 43 | // 打印栈 44 | display() { 45 | let s = items.get(this); 46 | console.log(s); 47 | } 48 | // 情况栈 49 | clear() { 50 | items.set(this, []); 51 | } 52 | } 53 | return Stack; 54 | }()); 55 | 56 | var stack = new Stack(); 57 | stack.push(5); 58 | stack.push(4); 59 | stack.push(3); 60 | stack.push(2); 61 | stack.push(1); 62 | console.log(stack.peek()) 63 | console.log(stack.size()) 64 | console.log(stack.pop()) 65 | console.log(stack.size()) 66 | stack.clear(); 67 | console.log(stack.peek()) 68 | console.log(stack.pop()) 69 | 70 | module.exports = Stack; 71 | 72 | -------------------------------------------------------------------------------- /queue/DueueBasedOnObject.js: -------------------------------------------------------------------------------- 1 | class Deque { 2 | constructor() { 3 | this.count = 0; 4 | this.lowestCount = 0; 5 | this.items = {}; 6 | } 7 | addFront(element) { 8 | if(this.isEmpty()) { 9 | this.addBack(element); 10 | }else if(this.lowestCount > 0) { 11 | this.items[--this.lowestCount] = element; 12 | }else{ 13 | for(let i = this.count; i > 0; i--) { 14 | this.items[i] = this.items[i - 1]; 15 | } 16 | this.count++; 17 | this.lowestCount = 0; 18 | this.items[0] = element; 19 | } 20 | } 21 | addBack(element) { 22 | this.items[this.count++] = element; 23 | } 24 | removeFront() { 25 | if(this.isEmpty()) { 26 | return null; 27 | } 28 | let ret = this.items[this.lowestCount]; 29 | delete this.items[this.lowestCount]; 30 | this.lowestCount++; 31 | return ret; 32 | } 33 | removeBack() { 34 | if(this.isEmpty()) { 35 | return null; 36 | } 37 | let ret = this.items[this.count - 1]; 38 | delete this.items[this.count - 1]; 39 | this.count--; 40 | return ret; 41 | } 42 | isEmpty() { 43 | return this.count === this.lowestCount; 44 | } 45 | peekFront() { 46 | if(this.isEmpty()) { 47 | return null; 48 | } 49 | return this.items[this.lowestCount]; 50 | } 51 | peekBack() { 52 | if(this.isEmpty()) { 53 | return null; 54 | } 55 | return this.items[this.count - 1]; 56 | } 57 | toString() { 58 | if(this.isEmpty()) { 59 | return ''; 60 | } 61 | let objString = `${this.items[this.lowestCount]}`; 62 | for(let i = this.lowestCount + 1; i < this.count; i++) { 63 | objString += `,${this.items[i]}`; 64 | } 65 | return objString; 66 | } 67 | } 68 | 69 | const deque = new Deque(); 70 | deque.addBack('Sun'); 71 | deque.addBack('Da'); 72 | deque.addBack('Shan'); 73 | deque.addFront('Name'); 74 | console.log(deque.toString()); 75 | deque.removeFront(); 76 | deque.removeFront(); 77 | deque.addFront('Test'); 78 | console.log(deque.toString()); 79 | deque.removeBack(); 80 | console.log(deque.toString()); -------------------------------------------------------------------------------- /linkedlist/SinglyLinkedList.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 1)单链表的插入、删除、查找操作; 3 | */ 4 | 5 | class ListNode { 6 | constructor(val) { 7 | this.val = val; 8 | this.next = null; 9 | } 10 | } 11 | 12 | class LinkedList { 13 | constructor() { 14 | this.head = new ListNode('head'); 15 | } 16 | 17 | // 根据 val 查找节点 18 | findByValue(val) { 19 | let currentNode = this.head; 20 | while(currentNode !== null && currentNode.val !== val) { 21 | currentNode = currentNode.next; 22 | } 23 | return currentNode === null ? -1 : currentNode; 24 | } 25 | 26 | // 根据 index 查找节点 27 | findByIndex(index) { 28 | let currentNode = this.head; 29 | let pos = 0; 30 | while(currentNode !== null && pos !== index) { 31 | currentNode = currentNode.next; 32 | pos++; 33 | } 34 | return currentNode === null ? -1 : currentNode; 35 | } 36 | 37 | // 在指定元素后插入 38 | insert(newVal, val) { 39 | let currentNode = this.findByValue(val); 40 | if (currentNode === -1) { 41 | console.log('未找到插入位置') 42 | return false; 43 | } 44 | const newNode = new ListNode(newVal); 45 | newNode.next = currentNode.next; 46 | currentNode.next = newNode; 47 | } 48 | 49 | // 查找前一个 50 | findPrev(val) { 51 | let currentNode = this.head; 52 | while(currentNode.next !== null && currentNode.next.val !== val) { 53 | currentNode = currentNode.next; 54 | } 55 | return currentNode === null ? -1 : currentNode; 56 | } 57 | 58 | // 根据值删除 59 | remove (val) { 60 | let prevNode = this.findPrev(val); 61 | prevNode.next = prevNode.next.next; 62 | } 63 | 64 | // 遍历显示所有节点 65 | display () { 66 | let currentNode = this.head; 67 | while (currentNode !== null) { 68 | console.log(currentNode.val) 69 | currentNode = currentNode.next; 70 | } 71 | } 72 | } 73 | 74 | const LList = new LinkedList() 75 | LList.insert('chen', 'head') 76 | LList.insert('curry', 'chen') 77 | LList.insert('sang', 'head') 78 | LList.insert('zhao', 'head') 79 | console.log('-------------show item------------') 80 | LList.display(); 81 | console.log('-------------remove item sang------------') 82 | LList.remove('sang'); 83 | console.log('-------------show item------------') 84 | LList.display(); -------------------------------------------------------------------------------- /set/Set.js: -------------------------------------------------------------------------------- 1 | class CunstomSet { 2 | constructor(element) { 3 | this.items = {}; 4 | } 5 | // 如果元素在集合中,返回 true,否则返回 false。 6 | has(element) { 7 | return Object.prototype.hasOwnProperty.call(this.items, element); 8 | } 9 | // 向集合添加一个新元素 10 | add(element) { 11 | if(this.has(element)) { 12 | return false; 13 | } 14 | this.items[element] = element; 15 | return true; 16 | } 17 | // 从集合移除一个元素 18 | delete(element) { 19 | if(this.has(element)) { 20 | delete this.items[element]; 21 | return true; 22 | } 23 | return false; 24 | } 25 | // 移除集合中的所有元素 26 | clear() { 27 | this.items = {}; 28 | } 29 | // 返回集合所包含元素的数量 30 | size() { 31 | return Object.keys(this.items).length; 32 | } 33 | // 返回一个包含集合中所有值(元素)的数组 34 | values() { 35 | return Object.values(this.items); 36 | } 37 | // 并集:对于给定的两个集合,返回一个包含两个集合中所有元素的新集合 38 | union(otherSet) { 39 | const unionSet = new CunstomSet(); 40 | this.values.forEach(value => unionSet.add(value)); 41 | otherSet.forEach(value => unionSet.add(value)); 42 | return unionSet; 43 | } 44 | // 交集:对于给定的两个集合,返回一个包含两个集合中共有元素的新集合 45 | intersection(otherSet) { 46 | const intersectionSet = new Set(); 47 | const values = this.values(); 48 | const otherValues = otherSet.values(); 49 | let biggerSet = values; 50 | let smallerSet = otherValues; 51 | if (otherValues.length - values.length > 0) { 52 | biggerSet = otherValues; 53 | smallerSet = values; 54 | } 55 | smallerSet.forEach(value => { 56 | if(biggerSet.has(value)) { 57 | intersectionSet.add(value); 58 | } 59 | }) 60 | return intersectionSet; 61 | } 62 | // 差集:对于给定的两个集合,返回一个包含所有存在于第一个集合且不存在于第二个集合的元素的新集合 63 | difference(otherSet) { 64 | const differenceSet = new Set(); 65 | this.values.forEach(value => { 66 | if(!otherSet.has(value)) { 67 | differenceSet.add(value); 68 | } 69 | }) 70 | return differenceSet; 71 | } 72 | // 子集:验证一个给定集合是否是另一集合的子集 73 | isSubsetOf(otherSet) { 74 | if (this.size() > otherSet.size()) { 75 | return false; 76 | } 77 | let isSubset = true; 78 | this.values().every(value => { 79 | if (!otherSet.has(value)) { 80 | isSubset = false; 81 | return false; 82 | } 83 | return true; 84 | }); 85 | return isSubset; 86 | } 87 | } 88 | 89 | const set = new CunstomSet(); 90 | 91 | set.add(1); 92 | console.log(set.values()); // 输出[1] 93 | console.log(set.has(1)); // 输出true 94 | console.log(set.size()); // 输出1 95 | 96 | set.add(2); 97 | console.log(set.values()); // 输出[1, 2] 98 | console.log(set.has(2)); // 输出true 99 | console.log(set.size()); // 输出2 100 | 101 | set.delete(1); 102 | console.log(set.values()); // 输出[2] 103 | 104 | set.delete(2); 105 | console.log(set.values()); // 输出[] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 数据结构与算法之美 2 | 3 | ## 导读 4 | 5 | ### 什么是数据结构和算法 6 | 7 | 1)数据结构,就是一组数据的存储结构 8 | 9 | 2)算法,就是操作数据的方法 10 | 11 | 3)数据结构和算法是相辅相成的,数据结构为算法服务,算法作用在特定的数据结构之上 12 | 13 | ### 为什么需要数据结构和算法 14 | 15 | 1)在计算机科学和互联网迅猛发展下,需要计算的数据量越来越庞大。但是计算机的计算能力是有限的,这么大量的数据计算,需要越来越多的计算机,需要越来越长的计算时间,注重效率的我们需要尽可能的提高计算效率。其中重要的一项,就是使用合适的数据结构和算法。选用合适的数据结构和算法,特别是在处理体量非常庞大的数据的时候,可以极大提高计算效率。 16 | 17 | 2)学好数据结构和算法,个人看来,会有3点好处: 18 | - 直接好处就是能够写出性能更优的代码 19 | - 算法其实是一种解决问题的思路和方法,有机会能应用到生活和工作中的其他方面 20 | - 长期看,大脑思考能力是个人最重要的核心竞争力,而算法是为数不多能够有效训练大脑思考能力的途径之一 21 | 22 | ### 怎么样衡量数据结构预算法 23 | 24 | 需要引入一个衡量的标准(metric)---时间复杂度和空间复杂度。 25 | 26 | 学习数据结构和算法的基石,就是要学会`复杂度分析`。知道怎么去分析复杂度,才能作出正确的判断,在特定的场景下选用合适的正确的算法。而不是盲目的死记烂背,机械操作。 27 | 28 | ### 学习方法 29 | 30 | 无他,唯手熟尔 31 | 32 | ### 学习目标 33 | 34 | **复杂度分析**: 35 | 36 | 1. 大O复杂度表示法 37 | - [复杂度分析(上)](https://github.com/sunbigshan/learnAlgorithm/issues/1) 38 | - [复杂度分析(中)](https://github.com/sunbigshan/learnAlgorithm/issues/2) 39 | - [复杂度分析(下)](https://github.com/sunbigshan/learnAlgorithm/issues/3) 40 | 41 | **数据结构**: 42 | 43 | 1. 数组 44 | - [为什么很多编程语言中数组都从0开始编号?](https://github.com/sunbigshan/learnAlgorithm/issues/4) 45 | - [JavaScript中的数组](https://github.com/sunbigshan/learnAlgorithm/blob/master/array/Array.md) 46 | 2. 链表 47 | - [如何实现LRU缓存淘汰算法?](https://github.com/sunbigshan/learnAlgorithm/issues/5) 48 | - [用JavaScript实现单链表(上)](https://github.com/sunbigshan/learnAlgorithm/blob/master/linkedlist/SinglyLinkedList.js) 49 | - [用JavaScript实现单链表(下)](https://github.com/sunbigshan/learnAlgorithm/blob/master/linkedlist/LinkedListAlgo.js) 50 | 3. 栈 51 | - [如何实现浏览器的前进和后退功能?](https://github.com/sunbigshan/learnAlgorithm/issues/6) 52 | - [基于数组实现栈](https://github.com/sunbigshan/learnAlgorithm/blob/master/stack/StackBasedOnArray.js) 53 | - [基于对象实现栈](https://github.com/sunbigshan/learnAlgorithm/blob/master/stack/StackBasedOnObject.js) 54 | - [基于链表实现栈](https://github.com/sunbigshan/learnAlgorithm/blob/master/stack/StackBasedOnLinkedList.js) 55 | 4. 队列 56 | - [基于数组实现队列](https://github.com/sunbigshan/learnAlgorithm/blob/master/queue/QueueBasedOnArray.js) 57 | - [基于对象实现队列](https://github.com/sunbigshan/learnAlgorithm/blob/master/queue/QueueBasedOnObject.js) 58 | - [基于链表实现队列](https://github.com/sunbigshan/learnAlgorithm/blob/master/queue/QueueBasedOnLinkedList.js) 59 | - [基于对象实现双端队列](https://github.com/sunbigshan/learnAlgorithm/blob/master/queue/DueueBasedOnObject.js) 60 | 5. 集合 61 | - [模拟实现ES6的Set数据结构](https://github.com/sunbigshan/learnAlgorithm/blob/master/set/Set.js) 62 | 6. 树 63 | - [二叉搜索树的实现](https://github.com/sunbigshan/learnAlgorithm/blob/master/tree/BinarySearchTree.js) 64 | 65 | 66 | **10个算法**: 67 | 68 | 1. 递归 69 | - [如何理解递归?](https://github.com/sunbigshan/learnAlgorithm/issues/7) 70 | 2. 排序 71 | - [如何分析一个排序算法?](https://github.com/sunbigshan/learnAlgorithm/issues/8) 72 | - [冒泡、插入、选择](https://github.com/sunbigshan/learnAlgorithm/issues/9) 73 | - [归并排序](https://github.com/sunbigshan/learnAlgorithm/issues/10) 74 | - [快速排序](https://github.com/sunbigshan/learnAlgorithm/issues/11) 75 | 3. 二分查找 76 | 4. 搜索 77 | 5. 哈希算法 78 | 6. 贪心算法 79 | 7. 分治算法 80 | 8. 回朔算法 81 | 9. 动态规划 82 | 10. 字符串匹配算法 83 | -------------------------------------------------------------------------------- /tree/BinarySearchTree.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 二叉搜索树 3 | * insert(key) 向树中插入一个新的键 4 | * search(key) 在树中查找一个键。如果节点存在,则返回 true;如果不存在,则返回 false 5 | * inOrderTraverse() 通过中序遍历方式遍历所有节点 6 | * preOrderTraverse() 通过先序遍历方式遍历所有节点 7 | * postOrderTraverse() 通过后序遍历方式遍历所有节点 8 | * min() 返回树中最小的值/键 9 | * max() 返回树中最大的值/键 10 | * remove(key) 从树中移除某个键 11 | */ 12 | function Node(key) { 13 | this.key = key; 14 | this.left = null; 15 | this.right = null; 16 | } 17 | 18 | class BinarySearchTree { 19 | constructor() { 20 | // 初始化根节点 21 | this.root = null; 22 | } 23 | insert(key) { 24 | if(this.root === null) { // 插入到根节点 25 | this.root = new Node(key); 26 | }else{ // 插入到根节点之外的位置 27 | this.insertNode(this.root, key); 28 | } 29 | } 30 | insertNode(node, key) { 31 | if(key < node.key) { // 插入左侧子节点 32 | if(node.left === null) { // 左侧子节点为空 33 | node.left = new Node(key); 34 | }else{ 35 | // 递归查找为空的左侧子节点 36 | this.insertNode(node.left, key); 37 | } 38 | }else{ // 插入右侧子节点 39 | if(node.right === null) { // 右侧子节点为空 40 | node.right = new Node(key); 41 | }else{ 42 | // 递归查找为空的右侧子节点 43 | this.insertNode(node.right, key); 44 | } 45 | } 46 | } 47 | search(key) { 48 | return this.searchNode(this.root, key); 49 | } 50 | searchNode(node, key) { 51 | if(node === null) { 52 | return false; 53 | } 54 | if(key < node.key) { // 遍历左侧子节点 55 | return this.searchNode(node.left, key); 56 | }else if(key > node.key) { // 遍历右侧子节点 57 | return this.searchNode(node.right, key); 58 | }else{ 59 | return true; 60 | } 61 | } 62 | // 中序遍历 63 | inOrderTraverse(callback) { 64 | this.inOrderTraverseNode(this.root, callback); 65 | } 66 | inOrderTraverseNode(node, callback) { 67 | if(node !== null) { 68 | this.inOrderTraverseNode(node.left, callback); 69 | callback(node.key); 70 | this.inOrderTraverseNode(node.right, callback); 71 | } 72 | } 73 | // 先序遍历 74 | preOrderTraverse(callback) { 75 | this.preOrderTraverseNode(this.root, callback); 76 | } 77 | preOrderTraverseNode(node, callback) { 78 | if(node !== null) { 79 | callback(node.key); 80 | this.preOrderTraverseNode(node.left, callback); 81 | this.preOrderTraverseNode(node.right, callback); 82 | } 83 | } 84 | // 后续遍历 85 | postOrderTraverse(callback) { 86 | this.postOrderTraverseNode(this.root, callback); 87 | } 88 | postOrderTraverseNode(node, callback) { 89 | if(node !== null) { 90 | this.postOrderTraverseNode(node.left, callback); 91 | this.postOrderTraverseNode(node.right, callback); 92 | callback(node.key); 93 | } 94 | } 95 | // 获取最小值 96 | min() { 97 | return this.minNode(this.root); 98 | } 99 | minNode(node) { 100 | let current = node; 101 | while(current.left !== null) { 102 | current = current.left; 103 | } 104 | return current.key; 105 | } 106 | // 获取最大值 107 | max() { 108 | return this.maxNode(this.root); 109 | } 110 | maxNode(node) { 111 | let current = node; 112 | while(current.right !== null) { 113 | current = current.right; 114 | } 115 | return current.key; 116 | } 117 | // 移出节点 118 | remove(key) { 119 | this.root = this.removeNode(this.root, key); 120 | } 121 | removeNode(node, key) { 122 | if(node === null) { 123 | return null; 124 | } 125 | if(key < node.key) { 126 | node.left = this.removeNode(node.left, key); 127 | return node; 128 | }else if(key > node.key) { 129 | node.right = this.removeNode(node.right, key); 130 | return node; 131 | }else{ 132 | if(node.left === null && node.right === null) { 133 | node = null; 134 | return node; 135 | } 136 | if(node.left === null) { 137 | node = node.right; 138 | return right; 139 | }else if(node.right === null) { 140 | node = node.left; 141 | return node; 142 | } 143 | const aux = this.minNode(node.right); 144 | node.key = aux.key; 145 | node.right = this.removeNode(node.right, aux.key); 146 | return node; 147 | } 148 | } 149 | } 150 | 151 | const tree = new BinarySearchTree(); 152 | 153 | tree.insert(11); 154 | tree.insert(7); 155 | tree.insert(15); 156 | tree.insert(5); 157 | tree.insert(3); 158 | tree.insert(9); 159 | tree.insert(8); 160 | tree.insert(10); 161 | tree.insert(13); 162 | tree.insert(12); 163 | tree.insert(14); 164 | tree.insert(20); 165 | tree.insert(18); 166 | tree.insert(25); 167 | 168 | console.log('-------------搜索6------------') 169 | console.log(tree.search(6)) 170 | console.log('-------------插入6------------') 171 | tree.insert(6); 172 | console.log('插入成功'); 173 | console.log('-------------再次搜索6------------') 174 | console.log(tree.search(6)) 175 | console.log('-------------最小值------------') 176 | console.log(tree.min()); 177 | console.log('-------------最大值------------') 178 | console.log(tree.max()); 179 | console.log('-------------中序遍历------------') 180 | tree.inOrderTraverse(val => console.log(val)); 181 | console.log('-------------先序遍历------------') 182 | tree.preOrderTraverse(val => console.log(val)); 183 | console.log('-------------后序遍历------------') 184 | tree.postOrderTraverse(val => console.log(val)); -------------------------------------------------------------------------------- /linkedlist/LinkedListAlgo.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * 1) 单链表反转 4 | * 2) 链表中环的检测 5 | * 3) 两个有序的链表合并 6 | * 4) 删除链表倒数第n个结点 7 | * 5) 求链表的中间结点 8 | */ 9 | class ListNode { 10 | constructor(val) { 11 | this.val = val; 12 | this.next = null; 13 | } 14 | } 15 | 16 | class LinkedList { 17 | constructor() { 18 | this.head = new ListNode('head'); 19 | } 20 | 21 | // 根据 val 查找节点 22 | findByValue(val) { 23 | let currentNode = this.head; 24 | while(currentNode !== null && currentNode.val !== val) { 25 | currentNode = currentNode.next; 26 | } 27 | return currentNode === null ? -1 : currentNode; 28 | } 29 | 30 | // 根据 index 查找节点 31 | findByIndex(index) { 32 | let currentNode = this.head; 33 | let pos = 0; 34 | while(currentNode !== null && pos !== index) { 35 | currentNode = currentNode.next; 36 | pos++; 37 | } 38 | return currentNode === null ? -1 : currentNode; 39 | } 40 | 41 | // 在指定元素后插入 42 | insert(newVal, val) { 43 | let currentNode = this.findByValue(val); 44 | if (currentNode === -1) { 45 | console.log('未找到插入位置') 46 | return false; 47 | } 48 | const newNode = new ListNode(newVal); 49 | newNode.next = currentNode.next; 50 | currentNode.next = newNode; 51 | } 52 | 53 | // 查找前一个 54 | findPrev(val) { 55 | let currentNode = this.head; 56 | while(currentNode.next !== null && currentNode.next.val !== val) { 57 | currentNode = currentNode.next; 58 | } 59 | return currentNode === null ? -1 : currentNode; 60 | } 61 | 62 | // 根据值删除 63 | remove (val) { 64 | let prevNode = this.findPrev(val); 65 | prevNode.next = prevNode.next.next; 66 | } 67 | 68 | // 遍历显示所有节点 69 | display () { 70 | //先检查是否为环 71 | if(this.hasCycle()) return false; 72 | 73 | let currentNode = this.head; 74 | while (currentNode !== null) { 75 | console.log(currentNode.val) 76 | currentNode = currentNode.next; 77 | } 78 | } 79 | 80 | // 单链表反转 81 | reverseList() { 82 | let root = new ListNode('head'); 83 | let currentNode = this.head.next; 84 | while(currentNode !== null) { 85 | const temp = currentNode.next; 86 | currentNode.next = root.next; 87 | root.next = currentNode; 88 | currentNode = temp; 89 | } 90 | this.head = root; 91 | } 92 | 93 | // 环检测 94 | hasCycle() { 95 | let slow = this.head; 96 | let fast = this.head.next; 97 | while(fast !== slow) { 98 | if(fast === null || fast.next === null) { 99 | return false; 100 | } 101 | fast = fast.next.next; 102 | slow = slow.next; 103 | } 104 | return true; 105 | } 106 | 107 | // 删除链表的倒数第N个节点 108 | removeNthFromEnd(n) { 109 | //先检查是否为环 110 | if(this.hasCycle()) return false; 111 | 112 | let root = new ListNode('head'); 113 | root.next = this.head.next; 114 | 115 | let length = 0; 116 | let currentNode = this.head; 117 | while(currentNode !== null) { 118 | currentNode = currentNode.next; 119 | length++; 120 | } 121 | length = length - n - 1; 122 | currentNode = root; 123 | while(length > 0) { 124 | currentNode = currentNode.next; 125 | length--; 126 | } 127 | currentNode.next = currentNode.next.next; 128 | 129 | this.head = root; 130 | } 131 | 132 | // 求链表的中间结点 133 | middleNode() { 134 | let fast = this.head.next; 135 | let slow = this.head.next; 136 | 137 | while(fast !== null && fast.next !== null) { 138 | fast = fast.next.next; 139 | slow = slow.next; 140 | } 141 | 142 | return slow; 143 | } 144 | } 145 | 146 | var mergeTwoLists = function(l1, l2) { 147 | if(l1 === null) return l2; 148 | if(l2 === null) return l1; 149 | 150 | let ret = new ListNode('head'); 151 | let currentNode = ret; 152 | 153 | let a = l1, 154 | b = l2; 155 | 156 | while(a !== null && b !== null) { 157 | if(a.val < b.val) { 158 | currentNode.next = a; 159 | a = a.next; 160 | }else{ 161 | currentNode.next = b; 162 | b = b.next; 163 | } 164 | currentNode = currentNode.next; 165 | } 166 | 167 | if(a !== null) { 168 | currentNode.next = a; 169 | }else{ 170 | currentNode.next = b; 171 | } 172 | 173 | return ret.next; 174 | 175 | }; 176 | 177 | 178 | const LList = new LinkedList() 179 | LList.insert('chen', 'head') 180 | LList.insert('curry', 'chen') 181 | LList.insert('sang', 'head') 182 | LList.insert('zhao', 'head') 183 | console.log('-------------show item------------') 184 | LList.display(); 185 | console.log('-------------reverse list------------') 186 | LList.reverseList(); 187 | LList.display(); 188 | console.log('-------------删除倒数第二个------------') 189 | LList.removeNthFromEnd(2); 190 | console.log('-------------show item------------') 191 | LList.display(); -------------------------------------------------------------------------------- /array/Array.md: -------------------------------------------------------------------------------- 1 | 与 Java 、PHP 等语言不同,在 JavaScript 中,数组其实是一种特殊的对象。 2 | #### 数组的创建与读写 3 | 以下两种方式都可创建数组: 4 | ``` 5 | // 字面量方式,常用 6 | var num = [1,5,6,10]; 7 | print(num.length); // 4 8 | 9 | // 构造函数方式 10 | var num = new Array(1,5,6,10); 11 | print(num.length); // 4 12 | ``` 13 | 值得注意的是,JavaScript 中的数组数据可以是不同类型,它的语法相对宽松,例如可以指定不同类型数据`var example = [1,"Mike",true,null];`另外,可以通过`Array.isArray()`来判断一个对象是否是数组,例如: 14 | ``` 15 | var num = [1,5,6,10]; 16 | print(Array.isArray(num)); // true 17 | ``` 18 | 如何读写数组呢?可以使用循环。 19 | ``` 20 | var num = [1,5,6,10]; 21 | for (var i = 0; i < num.length; i++) { 22 | console.log(num[i]+" "); 23 | } 24 | ``` 25 | #### 数组的深复制与浅复制 26 | 当我们把数组赋给另外一个数组,然后改变其中一个数组的值,另一数组也会随之改变,这就是数组的浅复制。而深复制指的就是不改变原来的数组而去创建一个新的数组,这种情况是经常使用的,为了不破坏原数组。下面的代码展示了这两种复制 27 | ``` 28 | // 浅复制 29 | var num = [1,2,3,4,5]; 30 | var newNum = num; 31 | num[0] = 10; 32 | console.log(newNum[0]); // 10 33 | 34 | // 深复制 35 | function copy (arr1,arr2) { 36 | for(var i=0;i= 60; 192 | } 193 | var grades = []; 194 | for(var i = 0;i < 11;i++){ 195 | grade[i] = Math.floor(Math.random() * 101); 196 | } 197 | var pass = grades.filter(passing); 198 | console.log("随机产生的 10 个同学的分数为:"); 199 | console.log(grades); 200 | console.log("及格的分数有:"); 201 | console.log(pass); 202 | ``` 203 | 上述代码的输出结果为 204 | > 随机产生的 10 个同学的分数为: 205 | 21, 4, 89, 45, 5, 51, 71, 7, 46, 53, 47 206 | 及格的分数有: 207 | 89, 71 208 | #### 二维数组 209 | JavaScript 可以通过在数组里在嵌套一个数组来形成二维数组。 210 | ``` 211 | var grades = [ 212 | [88,86,82], 213 | [91,82,83], 214 | [77,72,79], 215 | [86,80,82] 216 | ]; 217 | console.log(grades[1][2]); // 83 218 | ``` 219 | #### 处理二维数组 220 | 对于二维数组的处理可以分为两种,一种按列访问,一种是按行访问。 221 | 按列访问,外层循环对应行,内层循环对应列。例如,上述的数组,每一行对应一个学生三门科目的成绩记录,可以通过相加所有成绩,然后除以科目数来得到该生的平均成绩。 222 | ``` 223 | var grades = [ 224 | [88,86,82], 225 | [91,82,83], 226 | [77,72,79], 227 | [86,80,82] 228 | ]; 229 | var total = 0; 230 | var average = 0.0; 231 | for(var row = 0;row student 1 average: 85.33 243 | student 2 average: 85.33 244 | student 3 average: 76.00 245 | student 4 average: 82.67 246 | 247 | 248 | 对于按行访问,则外层循环对应列,内层循环对应行,例如还是上述数组,现在的数组表示一个学生三场考试四门科目的各科分数,我们来求每场考试的平均成绩 249 | ``` 250 | var grades = [ 251 | [88,86,82], 252 | [91,82,83], 253 | [77,72,79], 254 | [86,80,82] 255 | ]; 256 | var total = 0; 257 | var average = 0.0; 258 | //这里假设每场考试的科目都一样,所以可以通过grades[0].length来获取考试数量 259 | for(var col = 0;col exam 1 average: 85.50 271 | exam 2 average: 80.00 272 | exam 3 average: 81.50 273 | 274 | 其实只要调整 for 循环的顺序就可以控制是按行还是按列来输出,此外,JavaScript 还可以处理一些参差不齐的数组,比如一个二维数组中的数组,有的是两个元素,有的是四个元素,并不是都相同,在这种情况下,JavaScript 依然可以处理运行而不报错,这是因为不管多或少,都可以通过 length 属性来计算。 275 | #### 对象数组 276 | 如果你有阅读到这里,你应该可以发现上面的所有例子里数据类型都是基本数据类型,不是数字就是字符串。对象数组,顾名思义,就是数组里面的元素可以是对象,这个与 java 的语法很相似,基本上所有的编程语言都是相通的。看看下面这个例子: 277 | ``` 278 | function point(x,y){ 279 | this.x = x; 280 | this.y = y; 281 | } 282 | function show(arr){ 283 | for(var i=0;i Point 1: 1, 2 298 | Point 2: 2, 4 299 | Point 3: 8, 1 300 | Point 4: 2, 9 301 | 302 | 也可以用之前的 push() 等操作方法来操作对象数组 303 | ``` 304 | var p5 = new Point(11,13); 305 | point.push(p5); 306 | console.log("添加了 p5 后:"); 307 | show(point); 308 | point.shift(); 309 | console.log("删除第一个元素后:") 310 | show(point); 311 | ``` 312 | 输出结果为: 313 | > 添加了 p5 后: 314 | 1,2 315 | 2,4 316 | 8,1 317 | 2,9 318 | 11,13 319 | 删除第一个元素后: 320 | 2,4 321 | 8,1 322 | 2,9 323 | 11,13 324 | --------------------------------------------------------------------------------