基础
算法、数据结构、程序
- 算法就是计算或解决问题的步骤
- 数据结构:计算机存储,组织数据的方式
- 程序:计算机能够理解的编程语言编写的,可以在计算机上运行
- 程序 = 数据结构 + 算法
- 数据结构为算法提供服务,算法围绕数据结构进行操作
复杂度
- 一个函数,用大写O来表示,比如:O(1)、O(n)、O(n^2)...等等
时间复杂度
如上图可以粗略的分为两类,多项式量级和非多项式量级。其中,非多项式量级只有两个:O(2n) 和 O(n!) 对应的增长率如下图所示
当数据规模 n 增长时,非多项式量级的执行时间就会急剧增加,所以,非多项式量级的代码算法是非常低效的算法。
O(1)
一次数量(数量级)例 obj.key
let i = 0;
i = i+ 1;
O(n)
和传输的数据量一样, 例数组循环
for(let i =0; i< n; i++) {
console.log(i)
}
// 在一个for循环体里面,这个循环做了n次
O(login)
数据量的对数 例二分
int cnt = 1;
while (cnt < n)
{
cnt *= 2;
//时间复杂度为O(1)的程序步骤序列
}
// 由于cnt每次在乘以2之后都会更加逼近n,也就是说,在有x次后,cnt将会大于n从而跳出循环,所以2𝑥 = 𝑛 ,也就是𝑥=𝑙𝑜𝑔2𝑛,所以这个循环的复杂度为O(logn)
O(nlogn)
数据量 * 数据量的对数 一次循环* 二分,简单来说就是时间复杂度 为O(logn) 的代码执行了 n 次。
O(n^2)
数据量的平方 例嵌套循环
for(let i = 0; i < n; i++) {
for(let j = 0; j < n; j++) {
console.log(i,j)
}
}
空间复杂度
空间复杂度的话和时间复杂度类似推算即可。 所谓空间复杂度就是表示算法的存储空间和数据规模之间的关系。
O(1)
let i = 0;
i = i + 1;
O(n)
let arr = [];
for(let i = 0; i < n; i++) {
arr.push(i);
}
O(n^2)
let arr = [];
for(let i = 0; i < n; i++) {
arr.push([]);
for(let j = 0; j < n; j++) {
arr[i].push(j);
}
}
// 矩阵,本质二维数组
数据结构
链表
链表是数据结构之一,其数据呈线性排列;在内存空间中,数据是分散存储于内存中的,每个数据都由两部分组成,一部分是数据本身,另一部分是一个指针,它指向下一块存储空间。当对数据进行访问时,只能顺着指针指向一一往下访问,直到找到或者访问到末尾,如果链表中的数据量是n,那么,查找到一个数据,最快需要一次,最多需要查找n次;当需要在链表中添加或者删除一个数据时,只需要改变其中某一个或两个的数据指针即可,与链表的数据量无关,是常量级的。
链表数据是线性的,存储空间是不连续的,访问的时间复杂度为O(n),增删的时间复杂度为O(1)。
数组
数组也是线性排列的数据结构之一,它与链表不同的地方在于,数组在内存空间的存储是连续的。当访问数组时,只需要根据数组索引找到对应位置即可,查找复杂度是常量级的,表示为O(1);而当对数组进行增加时,如果在数组头部增加,则需要先将数组扩容。然后将每一个元素都依次向后移动,这个过程复杂度是O(n),而如果在数组尾部增加一个元素,复杂度变成了O(1),同理,删除一个元素,尾部删除时为O(1),头部删除时为O(n)。可以看出,相比于链表,数组虽然查询方便了,但是操作复杂度却高了。
API复杂度
*** 时间复杂度O(1)**
- push
- pop
- slice
- concat(一个参数为空数组时)
- entries
- values
- isArray
*** 时间复杂度O(n)**
- unshift
- splice
- shift
-
indexOf
-
includes
-
find
-
findIndex
-
findLast
-
findLastIndex
-
join
-
keys
-
every
-
fill
-
filter
-
some
-
forEach
-
map
-
reduce
-
includes
-
indexOf
...
*** 时间复杂度为 O(n log n)**
- sort(快排)
栈
栈是一种线性数据结构,当为栈添加一个元素时,这个元素被添加到了栈的最顶端,当取出元素时,只能单向的从最前面的位置读取,然后才能读取后面的元素,也就是说,最后被添加的,反而是最先被读取的,因此,栈被称为是后进先出(LIFO)模式,添加和删除数据的方式也被称为是入栈和出栈。由于栈具有的LIFO的特点,它常常被用来保存最新的数据。
队列
队列也是线性结构的数据结构,它与栈很像,都是单向的有序操作,但是,后进先出,而队列就像排队,先来的排在前面,后来的排在后面,属于先进先出(FIFO),要访问后面的元素,只能把前面的元素都访问完了,才能访问到目标元素。添加和删除队列的操作也被称为入队和出队。
哈希表
哈希表存储的是以键值对组合的数据,一般,把键当做数据的标识,而把值当做数据的内容。哈希表通常与哈希函数组合使用,在建立哈希表的过程中,需要使用哈希函数计算数据的哈希值,将其存在数组中,这样在访问时就可以快速使用数组的特性访问到;如果在建立数组的时候存在多个位于同一个数组位置的值,则再次使用链表存储相同的值。 哈希表的使用,加快了数组查询的速度,在灵活性和高效性上有很大的优势。在编程中,关联数组时常常会用到哈希表。
堆
堆是图的一种,是二维的数据结构,其示意可以用二位的树状图表示,子节点的数据值总比父节点大。在堆中,顶端的数据始终是最小的,所以无论多少数据量,取出最小值的复杂度始终都是O(1)。另外,由于取出数据后需要将最后的数据移动到最顶端,然后一边比较它与子节点数据的大小,一边往下移动,所以,取出数据需要的运行时间和树的高度成正比,假设数据量为n,根据堆的形状特点可知,树的高度为log2n,那么重构树的复杂度就是O(logn).添加数据也一样,在堆的最后添加数据,数据一边比较它与父节点的大小,一边往上移动,知道满足堆的条件为止。 如果需要频繁的从数据中取出最小值,那么,堆,是一种很好的选择。
二叉查找树
二叉查找树也叫作二叉搜索树,或二叉排序树。是二维的图结构的一种。它的特点是:每个节点最多有两个节点,分别称为左子树和右子树,每一个节点上的值均大于其左子树上的值,每个节点上的值均小于其右子树上的值。 根据这两个特点可知:二叉查找树查找最小值要往左下末端找,查找最大值要往右下末端找。