学习JavaScript数据结构和算法(第二版) 读后感
总体概述
本书是由[巴西] 格罗纳(Loiane Groner)[著作],全书总共238页;
本书从介绍javascript语言入手,然后分别介绍了数组(列表)、栈、队列、链表等顺序数据结构,然后依次介绍了集合、字典、散列表、树和图等非顺序结构,最后介绍了排序和搜索算法,以及算法复杂度;
接下来,我将从 (简述基础部分) 和 (详述 树、排序及其复杂度) 两方面进行分享,有不足的欢迎讨论:
一、简述基础部分
数组
1)数组其实就是内存里连续的一块地址,当删除某一个块时,其他内容向前移动,我理解为数组塌陷;
2)数组中常用的方法:增、删、改、查、克隆、遍历等栈
1)后进先出,例如:坐电梯,用数组表示 push / pop队列
1)先进先出,例如:排队上厕所,用数组表示 push/shift链表
1)链表其实是内存里不连续的地址
2)单项链表:一个节点有两个地址(一个放内容,一个放next指针)
3)双向链表:一个节点有三个地址(一个放内容,一个放next指针,一个放pre指针)集合
1)一个对象,里面内容无序且唯一;可以求并集、差集、子集;字典和散列表
1)查找数组内容,需要循环,浪费时间,最好有找到key,立马找到value的效果
2)字典也叫映射,是通过散列算法来实现的,key是唯一的,然后通过key就可以找到value,需要得到一种key不冲突且查得快;有两种方式计算key:分离连接、线性探测
二、详述-二叉树
增加和遍历
增加思路:
1、先判断是否有根节点root,如果没有,直接赋值新节点;
2、如果有了,判断左子树是否为空,不为空时逐层递归,直至找到进行赋值;
3、同理判断右子树
遍历思路:
1、中序:左中右,以左分支的最小值为起点遍历2、先序:中左右,以root根节点为起始点遍历
image.png
3、后序:左右中,以左分支最小值为起点遍历image.png
;(function () { var root = null class Node { // 节点类 constructor (key) { this.key = key this.l = null this.r = null } } class BinarySearchTree { // 二叉树类 constructor () {} insert (key) { // 插入节点 let newNode = new Node(key) if (root === null) { // 没有根节点情况 root = newNode } else { // 有根节点,进行节点比较 this.insertNode(root, newNode) } } insertNode (node, newNode) { if (newNode.key < node.key) {//新节点小于node节点 if (node.l === null) { node.l = newNode } else { this.insertNode(node.l, newNode) //跟node.l递归比较 } } else {//新节点大于node节点 if (node.r === null) { node.r = newNode } else { this.insertNode(node.r, newNode) // 跟node.r字节点递归比较 } } } inOrderTraverse (callback) { // 遍历节点 this.inOrderTraverseNode(root, callback) } inOrderTraverseNode (node, callback) { // 中序遍历:左中右 if (node !== null) { this.inOrderTraverseNode(node.l, callback) callback(node.key) this.inOrderTraverseNode(node.r, callback) } } preOrderTraverseNode (node, callback) { // 先序遍历:中左右 if (node !== null) { callback(node.key) this.inOrderTraverseNode(node.l, callback) this.inOrderTraverseNode(node.r, callback) } } postOrderTraverseNode (node, callback) { // 先序遍历:左右中 if (node !== null) { this.inOrderTraverseNode(node.l, callback) this.inOrderTraverseNode(node.r, callback) callback(node.key) } } }// 打印输出,进行代码测试 function printNode (value) { console.log(value) } let tree = new BinarySearchTree() tree.insert(11) tree.insert(7) tree.insert(15) tree.insert(5) tree.insert(3) tree.insert(9) tree.insert(10) tree.insert(13) tree.insert(12) tree.inOrderTraverse(printNode) })()
二分查找法
思路:
1、先将数组进行排序后,设置必要的两个指针,low和high
2、当low<high时,取中间索引min,并求出中间值element
3、当element<item,让low指针左移;反之,high指针右移;若两者都不是,直接返回min(即找到的元素的索引)
注意:
1、因为不知道要比较和移动多少次,所以使用while循环
2、min值是不断变化的(它随low和high指针的变动而变动Math.floor((low + high) / 2))
this.binarySearch = function(item){ this.quickSort(); // 先将数组进行排序,一会会介绍排序的多种思路 var low = 0, high = array.length - 1, //low,height为指针 mid, element; while (low <= high){ mid = Math.floor((low + high) / 2); element = array[mid]; if (element < item) { low = mid + 1; } else if (element > item) { high = mid - 1; } else { return mid; } return -1; };
AVL平衡二叉树
整个实现过程通过在一棵平衡二叉树中依次插入元素,若出现不平衡,则要根据新插入的节点与最低不平衡节点的位置关系进行响应的调整;分为:
1、LL型:向右单旋转image.png
2、RR型:向左单旋转
image.png
3、LR型:向右的双旋转image.png
4、RL型:向左的双旋转image.png
三、详述-排序
冒泡排序:
function bubbleSort(arr){ var length = array.length; for (var i=0; i<length; i++){ //外循环代表轮数 for (var j=0; j<length-1-i; j++ ){ //内循环代表比较次数 if (array[j] > array[j+1]){//拿当前项跟它的后一项进行比较 [array[j] , array[j+1]] = [array[j+1], array[j]] } } } }
选择排序
思路:用当前项跟他后面每一项进行对比,如果小了,就交换位置
function selectSort (arr) { for (var i = 0; i < arr.length; i++) { let minIndex = i for (var j = i; j < arr.length; j++) { if (arr[minIndex] > arr[j]) { minIndex = j } } if (minIndex !== i) { [arr[i], arr[minIndex]] = [arr[minIndex], arr[i]] } } return arr }
插入排序
思路:
1、左数组随机取出一张
2、拿右数组每一张跟左数组从后往前的比较,比他小,就继续往前比较,比他大,就插入到当前项的下一项的前面
function insertSort (arr) { var left = arr.splice(0, 1) // 左数组 for (var i = 0; i < arr.length; i++) { // 右数组每一项跟左数组每一项从后往前进行比较 var cur = arr[i] for (var j = left.length - 1; j >= 0;) { if (cur < left[j]) { j-- if (j === -1) { left.unshift(cur) } }else { left.splice(j + 1, 0 , cur) break } } } return left }
归并排序
思路:先拆后合
function mergeSortRec (arr) { var len = arr.length if (len === 1) { return arr } var mid = Math.floor(len / 2), left = arr.slice(0, mid), right = arr.slice(mid, len) return merge(mergeSortRec(left), mergeSortRec(right)) }function merge (left, right) { var result = [], il = 0, ir = 0 while(il < left.length && ir < right.length){ if (left[il] < right[ir]) { result.push(left[il++]) }else { result.push(right[ir++]) } } while(il < left.length){ result.push(left[il++]) } while (ir < right.length) { result.push(right[ir++]) } return result }
快速排序
思路:
1、通过中间位置mid,找到中间值midVal
2、遍历数组每一项跟midVal比较,比他小的push到left数组,反之,push到right数组
3、返回 left.concat([midVal], right) 但是因left和right数组未必能拼接好,我们还需要对他进行相同思路的拆分,所以,进行递归处理
function quickSort (arr) { if (arr.length <= 1) { return arr } var mid = Math.floor(arr.length / 2) var midVal = arr.splice(mid, 1)[0] var left = [] var right = [] for (var i = 0; i < arr.length; i++) { var cur = arr[i] if (cur < midVal) { left.push(cur) }else { right.push(cur) } } return quickSort(left).concat([midVal], quickSort(right)) }
以下列出常用的排序算法的时间复杂度和空间复杂度
排序法 | 最差时间 | 平均时间复杂度 | 稳定度 | 空间复杂度 |
---|---|---|---|---|
冒泡排序 | O(n^2) | O(n^2) | 稳定 | O(1) |
选择排序 | O(n^2) | O(n^2) | 不稳定 | O(1) |
插入排序 | O(n^2) | O(n^2) | 不稳定 | O(1) |
归并排序 | O(nlog2n) | O(nlog2n) | 稳定 | O(1) |
快速排序 | O(n^2) | O(n*log2n) | 稳定 | O(log2n)~O(n) |
作者:张蕾_
链接:https://www.jianshu.com/p/9c5ef91192a8
共同学习,写下你的评论
评论加载中...
作者其他优质文章