算法:时间复杂度和空间复杂度?时间复杂度和空间复杂度取舍?哪些时间复杂度是需要被优化的?优化的方法是什么?时间复杂度看最高的那个复杂度?
数据结构:有哪些数据结构?关系式是什么?
一、时间复杂度
1. O(1) 常数复杂度
不需要任何运算,其实就是一次运算。
2. O(n) 线性复杂度
循环语句,从1循环到n,不管n为多少,只要循环n次,与n一个数量级,时间复杂度均为n。
注:复杂度看最高的那个
let a = 0; //时间复杂度为O(1);
a += i; //时间复杂度为O(1);
循环时间复杂度是O(n)
,那么嵌套循环时间复杂度是O(n²)
3. O(n²) 平方复杂度
注意
:
(1):O(n)+O(n)=O(2n) 仍是O(n),前面的系数2忽略;
(2):继承循环共同完成一个循环,仍是O(n);
4. O(logn) 和 O(nlogn) 时间复杂度
与一些特定解题算法相关联,如常见的4中算法的时间复杂度:
二分查找
:O(logn)
二叉树遍历
:O(n) 因为每个节点仅遍历一次
排序查找/二维矩阵查找
:O(n) 若一维则是O(logn)
排序
:O(nlogn)
5. O(n³) 和 O(n!) 时间复杂度
常需要优化的
二、空间复杂度
在解题中,创建的变量、数组、链表等,为方便解题的数据结构,增加了空间复杂度,比如var、let、const
定义了一个变量,里面存放某些值。
1. O(1) 空间复杂度
2. O(n) 空间复杂度
注意
:(1)是自己创建的,不是题目给的。(2)一个数组里的数是若固定的,就不是O(n),而是O(1):比如数组里有两个数。
3. O(n²) 空间复杂度
三、时间复杂度和空间复杂度的取舍
一般取时间复杂度低
的,因为空间内存相对时间较廉价一些,除非题目说明要求局限在这个空间中
,如:原地算法、或不增加数据结构等。
四、复杂度优化方法
五、数据结构
1. 数组
访问
任何一个未知的数组元素,时间复杂度都为O(1)
,一次操作即可,是硬件保证的;- 但是
删除、插入
数组元素,要动后面所有的元素,因此时间复杂度都为O(n)
,但若插入的元素位置在数组的最后一个,则时间复杂度都为O(1); - 那么如何改进数组的删除和插入呢?
------->链表:单链表、双链表
2. 链表
-
单链表
:每个元素,它的指针指向它的后期节点,一般用next
指针,由于指针的存在,则删除和插入比较容易,时间复杂度为O(1)
;但是访问较慢,时间复杂度为O(n)
,从头到尾访问,与数组相反;
链表每个节点node包含两部分,第一部分存储的是值data,第二个部分是指针next; -
双链表
:有next
指针和previous
指针;删除和插入仍为时间复杂度为O(1)
;查询仍为时间复杂度为O(n)
;
-
实战项目
:力扣206
------反转链表
3. 栈(Stack)、队列(Quene)
查询
时间复杂度均为O(n)
,插入和删除
的时间复杂度均为O(1)
;- 栈(Stack) :先进后出;
- 队列(Quene): 先进先出;
- 实战------
力扣232、225
用栈实现队列
或用队列实现栈
4. 优先队列(Priority Quene)
- 正常进,按优先级出(元素大小、元素出现次数等);
- 实现机制:堆(heap)、二叉搜索树;
- 实战------
力扣703、239
5. 映射(Map)、集合(Set)
ES6中新增的存储数据的两个方式:Map、Set
;虽然JS中的Object
也是用(key:value)
存储数据的,与hashMap
类似,但相对Object
的优点是:(1)纯hash,性能比Object好很多;(2)本身提供的一些方法,高效简介;(3)与java中的用法类似;
1. 映射(Map)
与Object存储数据类似(Key,Value)。
(1)映射就是存储`(键,值)数据对的数据结构`(Key,Value)。根据键(Key),寻找值(Value);
(2)有序映射中的键具有顺序性,基于搜索树实现;
(3)无序映射中的键没有顺序性,基于哈希表实现;
(4)`key不可以重复,value可以重复`;
(5)`Map` 可以直接利用之前学习的`链表、二叉搜索树`(AVL树、红黑树)等数据结构来实现;
2. 集合(Set)
(1)不存放重复的元素;
(2)常用于去重;
(3)存放新增IP,统计新增IP 量;
(4)存放词汇,统计词汇量;
(5)集合(Set)可用动态数组、链表、二叉搜索树(AVL树、红黑树)来实现;
补充:箭头函数用法
3. 哈希表、哈希函数、哈希碰撞
-
哈希表
哈希表可以存储各种类型的数据
,当我们从哈希表中查找所需要的数据时,理想情况是不经过任何比较,一次存取便能得到所查记录,那就必须在记录的存储位置(value)和它的关键字(key)之间建立一个确定的对应关系 f,使每个关键字和结构中一个唯一的存储位置相对应。(关键字Key就是所要存储的数据
,存储位置value相当于数组的索引); -
当然,可以把哈希表理解为一个数组,每个
索引value
由关键字key(数据本身)
通过哈希函数得到;
链接:https://www.jianshu.com/p/4e64fce04a38 -
“散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
-
哈希函数
给定表M,存在函数f(key),对任意给定的关键字值key,代入函数后若能得到包含该关键字的记录在表中的地址,则称表M为哈希(Hash)表,函数f(key)为哈希(Hash) 函数。” -
哈希表是数组的一种扩展,没有数组,就没有哈希表;数组中查找某个数据时,要从头开始查找,麻烦,而哈希表通过哈希函数可以直接查找到;
-
哈希碰撞
通过哈希函数计算出的索引,即使关键字不同,索引也会有可能相同。这就是哈希冲突;
4. 实战
力扣-----242 变位词
力扣-----1 两数之和 用set解
力扣-----15 三数之和
力扣-----18 四数之和
6. 树和图
1. 字典和数组都是线性结构
(1)字典和数组,链表都是按照位置来存储数据的,例如字段是通过哈希计算下标,数组是使用下标,链表则是一个一个的链接在一起;
(2)字典和数组,链表都是 实现的几乎都是一对一的结构,既数据之间的关系的关系是一一对应。
2. 树
上述的数据结构几乎是一对一的,而实际中常常存在一对多或者是多对多的情况。
原文链接:https://blog.csdn.net/iamsongyu/article/details/101775453
3. 树的分类 和 图的遍历(深度优先遍历、广度优先遍历)
见参考链接:https://blog.csdn.net/JonyHwang/article/details/80956840
- (1)满二叉树(full binary tree):除了叶节点,其它节点都有左右子节点;
- (2)完全二叉树(complete binary tree):若设二叉树的高度为h,除第 h 层外(最后一层),其它各层 (1~h-1) 的结点数都达到最大个数,且最后一层节点集中在左边,这就是完全二叉树。
- (3)二叉搜索树(binary search tree): BST 也被称为二叉排序树,是一种
特殊类型的容器
: 在内存中存储“项目”(例如数字,名称等)的数据结构。它们允许快速查找、添加和删除项目,所有左子树结点的元素小于根节点的数据,所有右子树结点的元素大于根节点数据
。常与递归
相关联。 - (4)平衡二叉树(AVL树): 是一种特殊的二叉排序树。AVL树或者是一棵空树,或是具有以下性质的二叉树:
(1)左子树和右子树都是平衡二叉树;
(2)左子树和右子树的深度(高度)之差的绝对值不超过1。
- (5)红黑树:红黑树也是一种自平衡的二叉查找树。
1.每个结点要么是红的要么是黑的。(红或黑)
2.根结点是黑的。 (根黑)
3.每个叶结点(叶结点即指树尾端NIL指针或NULL结点)都是黑的。 (叶黑)
4.如果一个结点是红的,那么它的两个儿子都是黑的。 (红子黑)
5.对于任意结点而言,其到叶结点树尾端NIL指针的每条路径都包含相同数目的黑结点。(路径下黑相同)
保证红黑树满足它的基本性质,就是在调整数据结构自平衡。
而红黑树自平衡的调整
操作方式就有旋转
和变色
两种
原文链接:https://blog.csdn.net/wannuoge4766/article/details/83998377
- (6)二叉树的遍历方式
先序遍历:先根节点->遍历左子树->遍历右子树
中序遍历:遍历左子树->根节点->遍历右子树
后序遍历:遍历左子树->遍历右子树->根节点
4. 红黑树:特性?旋转?
见参考链接:
https://blog.csdn.net/qq_36610462/article/details/83277524
7. 实战----力扣704 二分查找
[-1,0,3,5,9,12] target=9
(1)给定一个数组,和一个目标值,在数组中查找该值,若找到返回该值对应下标;找不到,返回-1;
(2)看到排序好的数组,考虑二分搜索树。
或者,排好序数组,稍作一些变化,也可考虑二分查找方法。
但若是个完全乱序的数组,不适合二分查找。
(3)二分搜索:利用排好序的数组,直接比较数组中间(下标用mid表示)的数值与目标值,直接砍掉数组一半的值,再接着比较,时间复杂度O(logn);
而不是一个一个搜索,时间复杂度是O(n);
- 防止结果超过整型数的最大值;mid、left、right是数组下标
- Math.floor.mid 取mid值的向下值;
- 返回值为-1情况时:
- 考题:left、right根据题有时会等于mid值,变化,防止边界问题、死循环。