- 逻辑结构
- 线性结构:顺序表、栈、队列
- 非线性结构:树、图
- 物理结构
- 顺序存储结构:数组
- 链式存储结构:链表
在计算机领域里,算法是一系列程序执行,用于处理特定的运算和逻辑问题。
衡量算法优劣的主要表示标准是时间复杂度和空间复杂度
数据结构是数据的组织、管理和存格式,其使用目的是为了搞笑低访问和修改数据。
数据结构包含数组、链表这样的线性数据结构,也包含树、图这样的复杂数据结构
是对一个算法运行时间长短的量度,用大O表示,记作 T(n) = O(f(n))
常见4种执行方式
- 执行次数是线性的,时间复杂度:O(n)
- 执行次数是对数的,时间复杂度:O(
$logn$ ) - 执行次数是常量,时间复杂度:O(1)
- 执行次数是多项式计算,时间复杂度:O(
$n^2$ )
时间排序(从小到大)
O(1) < O(
是对一个算法在运行过程中临时占用存储空间大小的量度,用大O表示,记作S(n) = O(f(n))。
常见空间复杂度几种情形:
-
常量空间 O(1)
当算法的存储空间大小固定,和输入规模没有直接的关系时,空间负责度记O(1)
-
线性空间 O(n)
当算法分配的存储空间是一个线性的集合(如数组),并且集合大小和输入规模n成正比时,空间负责度记O(n)
-
二维空间 O(
$n^2$ )当算法分配的空间是一个二位数组集合,并且集合的长度和宽度都与输入规模n成正比时,空间复杂度即O(
$n^2$ ) -
递归空间 O(n)
递归时计算机在执行程序时,会专门分配一块内存,用来存储“方法调用栈”。
“方法调用栈”包括进栈和出栈两个行为,如:
void fun4(int n){ if(n<=1){ return; } fun4(n-1); ... }
当持续调用fun4方法会执行入栈操作,当入参n=1时执行出栈操作
执行递归操作所需要的内存空间和递归的深度成正比,即空间复杂度是线性的,如果递归的深度是n,那么空间复杂度就是O(n)
特点:在内存中顺序存储
-
读取元素
随机读取即可,T(n)=O(1)
-
更新元素
下标更新即可,T(n)=O(1)
-
插入元素
3种情况
尾部插入:
直接插入即可等同于更新元素 T(n) = O(1)
中间插入:
需要挪动插入位置后元素向后移动,腾出地方 T(n)=O(n)
超范围插入
需要扩容,创建一个新数组,长度是旧数组的2倍,在把旧数组中元素统统赋值过去,T(n) = O(n)
-
删除元素
如果删除的元素位于数组中间,其后的元素需要向前挪动1位,T(n) = O(n)
- 非常高效的随机访问能力,有一个搞笑查找元素的算法二分查找就是用的数组优势
- 插入、删除元素会导致大量元素被迫移动,影响效率
适合读操作多,写操作少
是一种在屋里上非连续、非顺序的数据结构,有若干节点(node)所组成
一部分是存放数据的变量data,另一部分是指向下一个节点的指针next
private static class Node{
int data;
Node next;
}
链表的第一个节点叫头节点,最有一个节点叫尾结点,尾结点的next指针指向null
private static class Node{
int data; //存储的数据
Node prev; //前一节点地址,头节点指向链表的最后节点地址
Node next; //下一节点地址
}
在内存中是随机存储,分布在内存的不同位置,依靠next指针关联起来,可以灵活的利用零散的碎片空间
-
查找节点
1)将查找的指针定位到头节点 2)根据头节点的next指针,定位到第2个节点 3)依次类推,知道查找到第n个节点
最坏的T(n) = O(n)
-
更新节点
找到更新节点,直接旧数据替换成新数据即可
-
插入节点
尾部插入
把最后一个节点的next指向新插入的节点即可
头部插入
1)把新节点的next指针指向原先的头节点 2)把新节点变为链表的头节点
中间插入
1)新节点的next指针,指向插入位置的节点 2)插入位置前置节点的next指针指向新节点
T(n) = O(1)
-
删除元素
尾部删除
把倒数第2个节点next指针指向空即可
头部删除
把链表的头节点设为原先头节点的next指针即可
中间删除
把要删除节点的前置节点的next指针,指向要删除元素的下一个节点即可
删除元素时间复杂度T(n) = O(1)
TODO
查找 | 更新 | 插入 | 删除 | |
---|---|---|---|---|
数组 | O(1) | O(1) | O(n) | O(n) |
链表 | O(n) | O(1) | O(1) | O(1) |
数组优势在快速定位元素,针对读多,写少场景
链表优势在灵活插入和删除,针对频繁插入、删除元素场景
这两者都属于逻辑结构,他们的物理实现既可以利用数组,也可以利用链表来完成。
一种线性结构,先入后出(First In Last Out 简称FILO)。最早计入的元素存放的位置叫作栈底(bottom),最后进入的元素存放的位置叫作栈顶(top)。
-
入栈操作(push) T(n) = O(1)
把新元素放入栈中,只允许从栈顶一侧放入元素,新元素的位置将会成为新的栈顶
-
出栈操作(pop) T(n) = O(1)
把元素从栈中弹出,只有栈顶元素才允许出栈,出栈元素的前一个元素将会成为新的栈顶
FIFO。度列的出口端叫作队头(front),入口段叫作队尾(rear)
-
入队(enqueue) T(n) = O(1)
只允许在队尾放入元素,新元素的下一个位置会成为新的队尾
-
出队(dequeue) T(n) = O(1)
只允许在队头一侧移除元素,出队元素的最后一个元素将会成为新的队头
(队尾下标+1) % 数组长度 = 队头下标,代表队列满了
- 栈:输出顺序和输入顺序相反
- 历史的回溯,例如实现递归逻辑
- 面包屑导航,浏览页面时可轻松回溯到上一级或更上一级页面
- 队列:输出顺河和输入顺序相同
- 对历史的回访,照历史书序,把历史重演一遍,例如多线程中,争夺公平锁的等待队列,顺序就是按照队列中排序的
- 网络爬虫,待转区网站URL存入队列,再按照有序队列的顺序来一次抓取和解析
- 双端队列:综合栈和队列的有点,从队头一端可以入队和出队,从队一端也可以入队和出队
- 优先队列:按照优先级最高,谁先出队(需了解二叉堆)
提供了**键(key)和值(value)**的映射关系,查找时间复杂度接近O(1)