一.时间复杂度与空间复杂度
1.1 .时间复杂度
- 若存在函数f(n),使得当n趋近于无穷大时,T(n)/f(n)的极限值为不等于零的常数,则称f(n)是T(n)的同数量级函数。记作T(n)=O(f(n)),称为O(f(n)),O为算法的渐进时间复杂度,简称时间复杂度。因为渐进时间复杂度用大写O来表示,也称为大O表示法。
- 如果运行时间是常数,则用常数1表示。
- 只保留时间函数中的最高阶项。
- 如果最高阶项存在,则省去最高阶项前面的函数。
- 示例:
- T(n)=3n -> T(n)=O(n)
- T(n) = 5logn -> T(n)=O(logn)
- T(n) = 2 -> T(n)=O(1)
- T(n)=0.5n^2+0.5n -> T(n)=O(n^2)
- 当n足够大时:O(1)<O(logn)<O(n)<O(n^2)
1.2 空间复杂度
- 空间复杂度是对一个算法在运行过程中临时占用存储空间大小的量度,同样用大O表示法,记作S(n) = O(f(n)),其中n为问题的规模,f(n)为算法所占存储空间的函数。
- 示例:
- 常量空间:O(1)
- 线性空间:O(n)
- 二维空间:O(n^2)
- 递归空间:如果递归深度为n,那空间复杂度为O(n)
二.数据结构基础
2.1 数组
数组是有限个相同类型的变量组成的有序集合,每一变量称为元素
数组的特点:
-
有限,类型相同,有序
-
顺序存储(内存单元连续)
💡 ps:在python的list本质是对数组的封装。
⭐ 数组的优势:拥有高效的随机访问能力,只需下标就可以用常量时间找到对应的元素。有一种高效查找元素的算法叫做二分法,就是利用了数组的这个优势。
❌ 数组的劣势:数组的插入和删除元素方便,由于顺序存储,因此增删都需要移动大量的元素,影响效率。
📃 总结:数组适合读操作多,写操作少的场景。
2.2链表
链表是一种在物理上非连续、非顺序的数据结构,由若干节点(node)组成。
- 单向列表:
- 单向列表的每个节点包含两部分,一是存放数据的data,一是指向下一节点的指针next。
- 第1节点称为头节点,最后1个节点称为尾节点,尾节点的next指针指向空。
- 双向列表:
- 双向列表的每个节点比单向列表多了一个指向前置节点的prev指针。
列表是随机存储,每个节点分布在内存中的不同位置,依靠指针,将各个节点关联起来,灵活有效地利用零碎的碎片空间。
| 查找 | 更新 | 插入 | 删除 | |
|---|---|---|---|---|
| 数组 | O(1) | O(1) | O(n) | O(n) |
| 链表 | O(n) | O(1) | O(1) | O(1) |
存储结构(物理结构):存储结构是数据元素及其关系在计算机中的存储方式。
逻辑结构:逻辑结构是数据元素之间的关系。
| 线性结构 | 非线性结构 | |
|---|---|---|
| 逻辑结构 | 如:顺序表、栈、队 | 如:树、图 |
| 存储结构 | 如:数组 | 如:链表 |
2.3 栈
栈是一种线性数据结构,栈中得元素只能先入后入(简称FILO)。最早进入的元素存放的位置叫作栈底(bottom),最后进入的元素存放的位置叫做栈顶(top)。
栈既可以用数组来实现,也可以用链表实现。
-
入栈:入栈操作(push)即将新元素放入栈中,只允许从栈顶一侧放入元素,新的元素将会成为新的栈顶。
-
出栈:出栈操作(pop)即将元素从栈中弹出,只有栈顶才允许出栈,出栈元素的前一个元素将成为新的栈顶。
💡 Python中,append方法相当于入栈,pop方法相当于出栈。
栈的应用:栈的输出顺序和输入顺序相反,所以栈通常用于对“历史”的回溯,也就是逆流而上追溯“历史”。如:面包屑导航。
2.4 队列
队列是一种线性结构,队列中的元素只能先进先出(简称FIFO)。队列中的出口端叫做队头(front),队列的入口端叫作队尾(rear)。
队列可以用数组来实现,也可以用链表实现。
- 入队(enqueue):把新元素放入队列中,只允许在队尾的位置放入元素,新元素的下一个位置将会成为新的队尾。
- 出队(dequeue):把元素移除队列,只允许在队头一侧移出元素,出队元素的后一个元素将会成为新的对头。
队列的应用:队列的输出顺序和输入顺序相同,所以队列通常用于对“历史”的回放,也就是按照“历史”顺序,把“历史”重演一遍。
2.5 栈和队列的应用
- 双端队列
- 把栈和队列的特点结合起来,既可以先入先出,也可以先入后出。
- 优先队列
- 遵循谁优先级最高,谁先出队。
2.6 哈希表
哈希表也叫左散列表,这种数据结构提供了键(key)和值(value)的映射关系。只要给出一个key就可以高效查找对应的value,时间复杂度接近于O(1)。
2.6.1 写操作(put)
- 写操作就是往哈希表中插入新的键值对(也被称为Entry)
2.6.2 读操作(get)
- 读操作就是通过给定的key,查找在哈希表中的值。
若发生哈希冲突:
- Java中哈希表采用的是链表法,当前Key被占用则通过next指针指向下一个节点
- Python中的哈希表采用的是寻址法,当前Key被占用则往后寻找,直到寻找下标值为空。