常问的
- 数组/列表:排序
python 实现数据结构八种内部排序算法-CSDN博客 - 链表: 单链表插入、逆序等
- 栈:去重、逆序
- 二叉树:遍历、生成树
相关题:
https://ceshiren.com/t/topic/26007
https://ceshiren.com/t/topic/11100
https://ceshiren.com/t/topic/16646
算法和数据结构的区别:
数据结构是一组数据的存储结构(数组、链表、栈、队列等),数据结构是静态的,是为算法服务的
算法是操作数据结构的一组方法,算法是作用在特殊结构之上
一、数据结构
- 线性表:线性表是数据排成一条线一样的结构。每个线性表上的数据最多只有前和后两个方向。
包括:数组,链表、队列、栈。 - 非线性表:数据之间并不是简单的前后关系。
包括:二叉树、堆、图等。
1.数组:
- 连续的内存空间和相同类型的数据:使数组支持“随机访问”。
- 但在数组中删除、插入数据时,需要做大量的数据搬移工作。
python实现 2.数组与列表-python
初始化:
- 长度固定,需要申请一个固定长度的列表
- 存入/删除数据,要做计数操作(无需指针)
- 存入/删除数据,要做数据迁移
- 插入数据需要和已存在的数据挨着,不能有间隔
2.链表
链表和数组的区别是,内存地址不连续,节点上需要存储指向下一个/上一个节点的指针
分为:
单向链表 、循环链表、双向链表、 双向循环链表
https://ceshiren.com/t/topic/11561
3.队列
先进先出
分类:
顺序队列:普通队列、优化队列、循环队列——tail指针指向的空的位置,tail=head队列为空,tail>head队列不为空
链式队列——tail指针指向的位置有值的(除非是空队列)
python代码 4.队列-python
顺序队列初始化:
- 固定大小
- 存在两个指针(计数)head,tail,初始都是0(指向第一个节点)
- 通过tail变动入队,head出队
链式队列初始化:
- 不用声明队列大小
- head,tail不再计数,直接是指针
- 声明节点类,写初始化方法(节点值,和指向下一个节点的指针)
4.堆栈
先进后出
二、算法
程序 = 数据结构 + 算法
算法概念: 解决特定问题的有限求解步骤
1.算法的特性
输入、输出、有穷性、确定性、可能性
(1)输入输出
- 算法具有零个或者多个输入
- 算法至少有一个或者多个输出
(2)有穷性
执行有限步骤后自动结束,不会出现无限循环,并且每一个步骤都在可接受的时间内完成。
(3)确定性
算法的每一步骤都有确定含义,不会出现二义性
(4)可行性
算法的每一步都必须是可行的,每一步都能通过执行有限次数完成。
2.算法设计要求
同一个问题,可以有多种解决的算法(相对好的算法是存在的)
(1)正确性
(2)可读性
(3)健壮性
(4)时间效率高:算法执行时间
(5)存储量低
3.衡量算法性能的两个维度
3.1渐进时间复杂度:估算算法的 执行时间 随数据规模增大的变化趋势
3.1.1时间复杂度分类
根据输入数据的特点,时间复杂度具有「最差」、「平均」、「最佳」三种情况:
举例: 线性查找,即遍历整个数组,遇到 7 则返回 true
def find_seven(nums):
for num in nums:
if num == 7:
return True
return False
- 最佳情况 Ω(1)
nums = [7, a, b, c, …] ,即当数组首个数字为 7 时,无论 nums 有多少元素,线性查找的循环次数都为1次; - 最差情况 O(N) ——最常用的
nums = [a, b, c, …] 且 nums 中所有数字都不为 7 ,此时线性查找会遍历整个数组,循环 N 次; - 平均情况 Θ
需要考虑输入数据的分布情况,计算所有数据情况下的平均时间复杂度;例如本题目,需要考虑数组长度、数组元素的取值范围等;
大O表示法: T(n) = O(f(n)) 大O是最坏情况
说明:n指的是算法处理输入的数量。
根据不同算法,具有不同定义,例如:
排序算法: N 代表需要排序的元素数量;
搜索算法: N 代表搜索范围的元素总数,例如数组大小、矩阵大小、二叉树节点数、图节点和边数等;
均摊时间复杂度: 更特殊的场景,对一个数据结构进线一组连续操作,大部分情况时间复杂度都很低,只有个别情况时间复杂度较高,且这些操作之间存在前后连续的时序关系。
3.2渐进空间复杂度:估算算法的 存储空间 随数据规模增大的变化趋势
3.2.1 空间类型
空间复杂度涉及的空间类型
- 输入空间: 存储输入数据所需的空间大小;
- 暂存空间: 算法运行过程中,存储所有中间变量和对象等数据所需的空间大小;
- 输出空间: 算法运行返回时,存储输出数据所需的空间大小;
通常情况下,空间复杂度指在输入数据大小为 N 时,算法运行所使用的「暂存空间」+「输出空间」的总体大小。
3.2.1 内存类型
而根据不同来源,算法使用的内存空间分为三类:
- 指令空间:编译后,程序指令所使用的内存空间。
- 数据空间:算法中的各项变量使用的空间,包括:声明的常量、变量、动态数组、动态对象等使用的内存空间。
- 栈帧空间:程序调用函数是基于栈实现的,函数在调用期间,占用常量大小的栈帧空间,直至返回后释放。如以下代码所示,在循环中调用函数,每轮调用 test() 返回后,栈帧空间已被释放,因此空间复杂度仍为 O(1) 。
def test():
return 0
def algorithm(N):
for _ in range(N):
test()
算法中,栈帧空间的累计常出现于递归调用。如以下代码所示,通过递归调用,会同时存在 N 个未返回的函数 algorithm() ,此时累计使用 O(N) 大小的栈帧空间。
def algorithm(N):
if N <= 1: return 1
return algorithm(N - 1) + 1
通常情况下,空间复杂度统计算法在 “最差情况” 下使用的空间大小,以体现算法运行所需预留的空间量,使用符号 O 表示。——最差情况有两层含义,分别为「最差输入数据」、算法运行中的「最差运行点」
4.算法思想
(1)穷举/枚举算法思想:从所有可能的情况中搜索答案
(2)递推算法思想:根据已有数据和关系,逐步推导而得到结果
- 顺推法:从已知条件出发,逐步推算解决办法
- 逆推法:从已知结果出发,用迭代表达式逐步推出问题开始的条件
(3)递归算法思想:不断调用自身达到求解问题方法,要求待求解问题可以分解为相同问题的子问题。
定义介绍
猴子吃桃问题
说明:正常是由前一个推后一个,这个是倒推(后一个推前一个)
#第n天吃之前数量确认
def peach(n):
if n==10:
return 1
return (peach(n+1)+1)*2 #因为已知的是下一天的数量f(n+1),不知道前一天数据f(n-1)
f(1) #1534
(4)分治算法思想
将一个规模为N的问题分解为K个规模较小的子问题(这些问题相互独立且与原问题性质相同)
求出子问题的解,即可得到原问题解
(5)贪心算法思想
贪心算法不从整体最优上加以考虑,所做出的仅是在某种意义上的局部最优选择。使用贪心策略要注意局部最优与全局最优的关系,选择当前的局部最优并不一定能推导出问题的全局最优。
从问题的某一个初始解出发,逐步逼近给定目标。
饼干分发问题
python实现
class Solution:
def findContentChild(self,g :list,s :list):
# g 每个小朋友胃口值列表
# s 每个小饼干尺寸值列表
# 1.先排序,默认升序
g.sort()
s.sort()
count = 0 # 被满足孩子数量
i = len(g)-1 # 胃口值列表的最后一个索引值
j = len(s)-1 # 饼干尺寸列表的最后一个索引值
while i>=0 and j>=0:
# 用饼干尺寸s[j]去匹配孩子胃口g[i]
if g[i] <= s[j]: #当前孩子可以被满足
count += 1
j -= 1 # 用下一个饼干去找合适的胃口
# 无论胃口符合不符合i都要减1
# 以上是符合,不符合就是孩子胃口太大,找下一个合适的小一点的胃口
i -= 1
return count
其他贪心算法经典案例
(6)回溯/试探算法思想
(7)迭代
(8)动态规划