数组
二分法:1.数组是有序数组 2.数组中无重复元素
时间复杂度:O(1)常数阶 < O(logn)对数阶 < O(n)线性阶 < O(nlogn)线性对数阶 < O(n^2)平方阶 < O(n^3)立方阶 < O(2^n)指数阶
空间复杂度:空间复杂度是考虑程序运行时占用内存的大小,而不是可执行文件的大小
数组是存放在连续内存空间上的相同类型数据的集合
数组可以方便的通过下标索引的方式获取到下标下对应的数据
数组下标都是从0开始的
数组内存空间的地址是连续的
因为数组的在内存空间的地址是连续的,所以我们在删除或者增添元素的时候,就难免要移动其他元素的地址。数组的元素是不能删的,只能覆盖
链表
数组是在内存中是连续分布的,但是链表在内存中可不是连续分布的。
链表是通过指针域的指针链接在内存中各个节点。
所以链表中的节点在内存中不是连续分布的 ,而是散乱分布在内存中的某地址上,分配机制取决于操作系统的内存管理
性能分析
数组在定义的时候,长度就是固定的,如果想改动数组的长度,就需要重新定义一个新的数组。频繁查询,较少增删。
链表的长度可以是不固定的,并且可以动态增删, 适合数据量不固定,频繁增删,较少查询的场景
哈希表
一般哈希表都是用来快速判断一个元素是否出现集合里。
常见的三种哈希结构:数组、set(集合)、map(映射)
当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法。但是哈希法也是牺牲了空间换取了时间,因为我们要使用额外的数组,set或者是map来存放数据,才能实现快速的查找。如果在做面试题目的时候遇到需要判断一个元素是否出现过的场景也应该第一时间想到哈希法!
如果哈希值比较少、特别分散、跨度非常大,使用数组就造成空间的极大浪费。
基础知识复习:列表(list)和元组(tuple)比较相似,它们都按顺序保存元素,所有的元素占用一块连续的内存,每个元素都有自己的索引,因此列表和元组的元素都可以通过索引(index)来访问。它们的区别在于:列表是可以修改的,而元组是不可修改的。字典(dict)和集合(set)存储的数据都是无序的,每份元素占用不同的内存,其中字典元素以 【key-value 】的形式保存。创建一个空集合必须用 set( ) 而不是 { } ,因为 { } 是用来创建一个空字典
[]添加元素用.append 集合添加元素用.add
字符串
KMP
解决类似这种字符串匹配问题:
现 strStr() 函数。
给定一个 haystack 字符串和一个 needle 字符串,在 haystack 字符串中找出 needle 字符串出现的第一个位置 (从0开始)。如果不存在,则返回 -1。
示例 1: 输入: haystack = "hello", needle = "ll" 输出: 2
示例 2: 输入: haystack = "aaaaa", needle = "bba" 输出: -1
字符串的前缀是指不包含最后一个字符的所有以第一个字符开头的连续子串。
后缀是指不包含第一个字符的所有以最后一个字符结尾的连续子串。
前缀表也就是next表,就是记录下标i之前(包括i)的字符串中,有多大长度的相同前缀后缀。
// 前缀表(不减一)Python实现
class Solution:
def strStr(self, haystack: str, needle: str) -> int:
if len(needle) == 0:
return 0
next = self.getNext(needle)
j = 0
for i in range(len(haystack)):
while j >= 1 and haystack[i] != needle[j]:
j = next[j-1]
if haystack[i] == needle[j]:
j += 1
if j == len(needle):
return i - len(needle) + 1
return -1
def getNext(self, needle):
next = [0] * len(needle)
j = 0
next[0] = j
for i in range(1, len(needle)):
while j >= 1 and needle[i] != needle[j]:
j = next[j-1]
if needle[i] == needle[j]:
j += 1
next[i] = j
return next
栈与队列
队列是先进先出,栈是先进后出
由于栈结构的特殊性,非常适合做对称匹配类的题目
字典中的.get()
python字典的get方法会返回指定键的值,dict.get(‘键’),返回“键”对应的“值”,如果键不在字典中则返回默认值None。
如果键不在字典中,想要自己设置返回值,可以这样处理,例如dict.get(‘键’,‘never’),键在字典中,则返回键对应的值,键不在字典中,则返回never。
dict.items()用法
python字典(Dictionary) items() 函数以列表返回可遍历的(键, 值) 元组数组。
返回值:返回可遍历的(键, 值) 元组数组。
dict = { 'Google': 'www.google.com', 'Runoob': 'www.runoob.com', 'taobao': 'www.taobao.com'}
print( "字典值 :",dict.items())
for key,values in dict.items():
print(key,values)
结果:
字典值 : dict_items([( 'Google', 'www.google.com'), ( 'Runoob', 'www.runoob.com'), ( 'taobao', 'www.taobao.com')])
Google www.google.com
taobao www.taobao.com
Runoob www.runoob.com
优先队列
import heapq
pqueue = []
heapq.heappush(pqueue,(0,"A"))
heapq.heappush(pqueue,(2,"B"))
heapq.heappush(pqueue,(10,"C"))
heapq.heappush(pqueue,(6,"D"))
heapq.heappush(pqueue,(15,"E"))
print(pqueue)
"""
result:
[(0, 'A'), (2, 'B'), (10, 'C'), (6, 'D'), (15, 'E')]
"""
上面是我给优先队列里面添加元素,下面我们从优先队列里面弹出元素,请看
print(heapq.heappop(pqueue))
print(heapq.heappop(pqueue))
print(heapq.heappop(pqueue))
print(heapq.heappop(pqueue))
print(heapq.heappop(pqueue))
"""
results:
(0, 'A')
(2, 'B')
(6, 'D')
(10, 'C')
(15, 'E')
"""
这里弹出的顺序,就是按照优先队列里面元素对应权值的大小弹出的。也就是元组当中的第一个值的大小由小到大排列
二叉树
树是一种数据结构
节点的度:节点有几个分支,(有两个孩子节点,那么度为2)
树的度:节点度的最大值。
二叉树:度不超过2的树。每个节点最多有两个孩子节点,两个孩子节点被区分为左孩子节点和右孩子节点。
满二叉树:一个二叉树,如果每一层的节点数都达到了最大值,那么这个二叉树就是满二叉树。
完全二叉树:叶子节点只能出现在最下层和次下层,并且最下面一层的节点都集中在该层的最左边。
例下图:(a)满二叉树;(b)完全二叉树;(c)(d)非完全二叉树。
二叉搜索树:
二叉搜索树是有数值的了,二叉搜索树是一个有序树。
- 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
- 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
- 它的左、右子树也分别为二叉排序树
平衡二叉搜索树:
它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵二叉搜索树。
回溯
组合
分割
子集
排列
动态规划
一般没有规律的动态规划
背包问题:
正常背包:
1.正常背包只能选一次,单维,遍历顺序为len(weight) 第二个为bag_weight, weight[i]-1,-1
2.是否装满问题:也就是先算最大能装多少,求出来以后是否与满额值相等
3.填满有几种方法首先要注意0的初始化,遍历顺序是一样的,递归函数:dp[j] += dp[j - nums[i]]
4.零和一是二维的背包问题
完全背包:
1.完全背包就是物品有无限件,与之前的不同主要体现在遍历顺序上(背包的遍历顺序,由逆序变成正序)for j in range(weight[i], bag_weight + 1)
2.排列的话是需要看顺序的,所以区别就是遍历顺序上的改变(主要就是先遍历背包在便利物品:举例:for j in range(bag_weight + 1)第二列:for i in range(len(weight))第三列:if j>=weight[i]:递归函数
3.计算最小价值的时候,注意初始化,初始化想办法让初始化的值不被取到(非0下标的时候)0下标根据具体情况进行选取
打家劫舍:
1.正常的打家劫舍就是这个点偷与不偷的区别,偷的话就加上前2点的,不偷的话就直接继承前1点的
2.成环了以后就是要考虑两种情况,然后取最大值。(第一种情况就是不考虑第一个,第二种情况就是不考虑最后一个(其实是三种情况,但是第三种情况就是既不考虑第一个也不考虑最后一个,但是这种情况已经被包含在第一种和第二种情况之中,所以只需要考虑前两种情况就行,然后取最大值)
3.dp与二叉树的结合,递归方式去遍历二叉树,用后序,因为需要先指导左右孩子的情况去进行根节点的判断,所以用后序。递归中的结束条件相当于动态规划中的初始化,递归顺序相当于动态规划中的遍历顺序,处理节点相当于递归函数。
股票:
1.股票中的dp是二维的,应为会包含持有股票和不持有股票两种状态,以一个参数是第几天,第二个参数就是状态
2.买限定次数的相对难一些,但是有一道算限定K次的题,把这题弄懂就可以套公式了。这道题主要分了2*k+1状态,0为不进行任何操作的状态,1为第一次买入状态,2为第一次卖出状态,3为第二次买入状态,4为第二次卖出状态,以此类推
3.冷冻期:这题出现四种状态:0持有1不持有(不是卖出的那种)2不持有(是卖出的那种)3冷冻期
子序列:
子序列中大多数都是翼结尾的形式设置dp
二维的一般都会多设置一个0(len(A) + 1)
1.
- 递增子序列最长长度(不连续) dp[i]表示i之前包括i的以nums[i]结尾的最长递增子序列的长度
- 连续递增序列最长长度(连续)dp[i]:以下标i为结尾的连续递增的子序列长度为dp[i]。
- 重复子数组最长长度(连续的)dp[i][j] :以下标i - 1为结尾的A,和以下标j - 1为结尾的B,最长重复子数组长度为dp[i][j]。 (特别注意: “以下标i - 1为结尾的A” 标明一定是 以A[i-1]为结尾的字符串 )
- 重复子序列最长长度(不连续的)dp[i][j]:长度为[0, i - 1]的字符串text1与长度为[0, j - 1]的字符串text2的最长公共子序列为dp[i][j]
2. 回文:都是范围