-
Notifications
You must be signed in to change notification settings - Fork 22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
导师计划--数据结构和算法系列(上) #14
Labels
blog
a single blog
Comments
link-list的移出元素需要让length-- |
@KAO-96 谢谢提醒,已加上 |
很棒!看了一遍打算star,结果发现已经star过了 |
@puddlejumper26 Thank you! |
您好,我想转载到我自己的GitHub上,翻译成英文版本,会直接注明翻译自您的文章,图片打算直接用您的,汉语的部分用英文翻译遮住。 请问是否可以? |
@puddlejumper26 👌,著名出处就行了 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
导师计划已经开始一个月了,自己的讲解的课程选择了数据结构和算法。这个系列的讲解分为
上下两章
,javascript
语言辅助。本篇文章为上章,涉及的内容是基本的数据结构。在日本,晚上没事安排@…@,时间还是充足的...,于是自己整理下本系列知识点的上章内容。以下为正文:
数据结构是计算机存储、组织数据的方式。数据结构是指相互直接存在一种或多种特殊关系的数据元素的集合。通常情况下,精心选择数据结构可以带来更高的运行或者存储效率。作为一名程序猿,更需要了解下数据结构。AND WHY?可以参考这篇文章【译】编程不容易中的
性能和优化
部分内容。讲到数据结构,我们都会谈到线性结构和非线性结构。
1.线性结构是一个有序数据元素的集合。它应该满足下面的特征:
按照百度百科的定义,我们知道符合条件的数据结构就有栈、队列和其它。
2.非线性结构其逻辑特征是一个节点元素可以有多个直接前驱或多个直接后继。
那么,符合条件的数据结构就有图、树和其它。
嗯~了解一下就行。我们进入正题:
数组
数组是一种线性结构,以十二生肖(鼠、牛、虎、兔、龙、蛇、马、羊、猴、鸡、狗、猪)排序为例:
我们来创建一个数组并打印出结果就一目了然了:
数组中常用的属性和一些方法如下,直接调用相关的方法即可。这里不做演示~
常用的属性
常用的方法
splice(index, howmany, item, ... itemx)
splice方法自认为是数组中最强大的方法。可以实现数组元素的添加、删除和替换。参数
index
为整数且必需,规定添加/删除项目的位置,使用负数可从数组结尾处规定位置;参数howmany
为必需,为要删除的项目数量,如果设置为 0,则不会删除项目;item1, ... itemx
为可选,向数组添加新的项目。indexOf(searchValue, fromIndex)
indexOf方法返回某个指定字符串值在数组中的位置。
searchValue
是查询的字符串;fromIndex
是查询的开始位置,默认是0。如果查询不到,会返回-1。concat(array1, ... arrayn)
concat方法用于连接两个或者多个数组。
push(newElement1, ... newElementN)
push方法可向
数组的末尾
添加一个或者多个元素。unshift(newElement1, ... newElementN)
unshift方法可向
数组的开头
添加一个或者多个元素。pop()
pop方法用于删除并返回
数组的最后一个元素
。shift()
shift方法可以删除数组的
第一个元素
。reverse()
reverse方法用于数组的反转
sort(sortFn)
sort方法是对数组的元素排序。参数
sortFn
可选,其规定排序顺序,必须是函数。forEach(fn(currentValue, index, arr), thisValue)
forEach方法用于调用数组的每个元素,并将元素传递给回调函数。参数
function(currentValue, index, arr){}
是一个回调函数。thisValue
可选,传递给函数的值一般用 "this" 值,如果这个参数为空, "undefined" 会传递给 "this" 值。every(fn(currentValue, index, arr), thisValue)
every方法用于检测数组中所有元素是否符合指定条件,如果数组中检测到有一个元素不满足,则整个表达式返回
false
,且剩余的元素不再检查。如果所有的元素都满足条件,则返回true
。some(fn(currentValue,index,arr),thisValue)
some方法用于检测数组中元素是否满足指定条件。只要有一个符合就返回
true
,剩余的元素不再检查。如果所有元素都不符合条件,则返回false
。reduce(fn(accumulator, currentValue, currentIndex, arr), initialValue)
reduce方法接收一个函数作为累加器,数组中的每个值(从左到右)开始缩减,最终为一个值。回调函数的四个参数的意义如下:
accumulator
,必需,累计器累计回调的返回值, 它是上一次调用回调时返回的累积值,或initialValue;currentValue
,必需,数组中正在处理的元素;currentIndex
,可选,数组中正在处理的当前元素的索引,如果提供了initialValue,则起始索引号为0,否则为1;arr
,可选,当前元素所属的数组对象。initialValue
,可选,传递给函数的初始值。栈
栈是一种后进先出(LIFO)线性表,是一种基于数组的数据结构。(ps:其实后面讲到的数据结构或多或少有数组的影子)
我们代码写下,熟悉下栈:
说到栈,这也让我想到了翻译的一篇文章JS的执行上下文和环境栈是什么?,感兴趣的话可以戳进去看下。
队列
队列是一种先进先出(FIFO)受限的线性表。受限体现在于其允许在表的前端(front)进行删除操作,在表的末尾(rear)进行插入【优先队列这些排除在外】操作。
代码走一遍:
链表
在进入正题之前,我们先来聊聊数组的优缺点。
优点:
缺点:
相对数组,链表亦可以存储多个元素,而且存储的元素在内容中不必是连续的空间;在插入和删除数据时,时间复杂度可以达到O(1)。在查找元素的时候,还是需要从头开始遍历的,比数组在知道下表的情况下要快,但是数组如果不确定下标的话,那就另说了...
我们使用十二生肖来了解下链表:
链表是由一组节点组成的集合。每个节点都使用一个对象的引用指向它的后继。如上图。下面用代码实现下:
字典
字典的主要特点是键值一一对应的关系。可以比喻成我们现实学习中查不同语言翻译的
字典
。这里字典的键(key)理论上是可以使用任意的内容,但还是建议语意化一点,比如下面的十二生肖图:集合
集合通常是由一组无序的,不能重复的元素构成。 一些常见的集合操作如图:
es6中已经封装好了可用的Set类。我们手动来写下相关的逻辑:
散列表/哈希表
散列是一种常用的存储技术,散列使用的数据结构叫做散列表/哈希表。在散列表上插入、删除和取用数据都非常快,但是对于查找操作来说却效率低下,比如查找一组数据中的最大值和最小值。查找的这些操作得求助其它数据结构,比如下面要讲的二叉树。
切入个案例感受下哈希表:
假如一家公司有1000个员工, 现在我们需要将这些员工的信息使用某种数据结构来保存起来。你会采用什么数据结构呢?
方案一:数组
编号
对应的就是员工的下标值
。方案二:链表
最终方案:
那么散列表的原理和实现又是怎样的呢,我们来聊聊。
我们的哈希表是基于数组完成的,我们从数组这里切入解析下。
数组可以通过下标直接定位到相应的空间
,哈希表的做法就是类似的实现。哈希表把key(键)
通过一个固定的算法函数(此函数称为哈希函数/散列函数)转换成一个整型数字,然后就将该数字对数组长度进行取余,取余结果就当作数组的下标,将value(值)
存储在以该数字为下标的数组空间里,而当使用哈希表进行查询的时候,就是再次使用哈希函数将key
转换为对应的数组下标,并定位到该空间获取value
。结合下面的代码,也许你会更容易理解:
针对上面的问题,我们存储数据的时候,产生冲突的话我们可以像下面这样解决:
1. 线性探测法
当发生碰撞(冲突)时,线性探测法检查散列表中的下一个位置【有可能非顺序查找位置,不一定是下一个位置】是否为空。如果为空,就将数据存入该位置;如果不为空,则继续检查下一个位置,直到找到一个空的位置为止。该技术是基于一个事实:每个散列表都有很多空的单元格,可以使用它们存储数据。
2. 开链法
但是,当发生碰撞时,我们任然希望将
key(键)
存储到通过哈希函数产生的索引位置上,那么我们可以使用开链法。开链法是指实现哈希表底层的数组中,每个数组元素又是一个新的数据结构,比如另一个数组(这样结合起来就是二位数组了),链表等,这样就能存储多个键了。使用这种技术,即使两个key(键)
散列后的值相同,依然是被保存在同样的位置,只不过它们是被保存在另一个数据结构上而已。以另一个数据结构是数组为例,存储的数据如下:二叉查找树
树的定义:
树(Tree):
n(n >= 0)
个节点构成的有限集合。n = 0
时,称为空树;(n > 0)
,它具备以下性质:r(root)
表示;m(m > 0)
个互不相交的有限集T1,T2,...Tm
,其中每个集合本省又是一棵树,称为原来树的子树(SubTree)注意:
不可以相交
;N
个节点的树有N-1
条边。树的术语:
N-1
)。0
的节点(也称叶子节点)。A
节点是B
节点的父节点,则称B
节点是A
节点的子节点。n1
到nk
的路径为一个节点序列n1,n2,n3,...,nk
,ni
是ni+1
的父节点。路径所包含边的个数为路径长度。第0层
,它的子节点是第1层
,子节点的子节点是第2层
,以此类推。如下图:
二叉树的定义:
TL
和右子树RT
的两个不相交的二叉树组成两个
二叉树的五种形态:
对应下图(从左至右):
我们接下来要讲的是二叉查找树(BST,Binary Search Tree)。二叉查找树,也称二叉搜索树或二叉排序树,是一种特殊的二叉树,相对值较
小
的值保存在左
节点中,较大
的值保存在右
节点中。二叉查找树特殊的结构使它能够快速的进行查找、插入和删除数据。下面我们来实现下:看了上面的代码之后,你是否有些懵圈呢?我们借助几张图来了解下,或许你就豁然开朗了。
在遍历的时候,我们分为三种遍历方法--先序遍历,中序遍历和后序遍历:
删除节点是一个比较复杂的操作,考虑的情况比较多:
左
子树找节点值最大
的节点A,替换待删除节点值,并删除节点A右
子树找节点值最小
的节点A,替换待删除节点值,并删除节点A【👆上面的示例代码中就是这种方案】删除两个节点的图解如下:
图
图由边的集合及顶点的集合组成。
我们来了解下图的相关术语:
0
顶点和其它两个顶点相连,0
顶点的度就是2
v1,v2...,vn
的一个连续序列。边
是有
方向的。边
是无
方向的。有权重
。无权重
。如下图:
图可以用于现实中的很多系统建模,比如:
图既然这么方便,我们来用代码实现下:
对于搜索图,在上面我们介绍了深度优先搜索 - DFS(Depth First Search)和广度优先搜索 - BFS(Breadth First Search),结合下面的图再回头看下上面的代码,你会更加容易理解这两种搜索图的方式。
后话
参考
coderwhy的数据结构和算法系列文章
《数据结构与算法JavaScript描述》
The text was updated successfully, but these errors were encountered: