leetcode刷题笔记

这篇笔记涵盖了数据结构的基础知识,包括数组、链表及其Python实现,详细介绍了哈希表(散列表)、集合(如HashSet、LinklistSet、TreeSet)的操作,以及堆(大根堆、小根堆)的常见操作。此外,还涉及到了图的概念,以及深度优先搜索(DFS)、广度优先搜索(BFS)和并查集(unionfind)的基本原理。最后,文章提到了几种算法,如双指针、回溯法、动态规划和贪心算法等。
摘要由CSDN通过智能技术生成

该笔记是以b站视频为基础,所做的笔记,详细内容请查看这个视频

数据结构

数组

链表

python 中的一些常用操作

# 创建链表
linkedlist = deque()

# 添加元素
# 时间复杂度为O(1)
linkedlist.append(1)
linkedlist.append(2)
linkedlist.append(3)
print(linkedlist) # [1,2,3]

# 插入元素
# 时间复杂度O(N):插入仍然是O(1),但是这里需要用到索引的值,找到其对应索引的位置进行插入,这个寻找索引位置的操作的时间复杂度为O(n)
# insert(索引位置,插入的值)
linkedlist.insert(2, 99)
print(linkedlist) # [1,2,99,3]

# 访问元素(access)
element = linkedlist[2]
print(element) # 99

# 搜索元素,输入一个值,返回这个值在链表中的索引位置
# 时间复杂度O(n)
index = linkedlist.index(99)
print(index) # 2

# 更新元素
# 时间复杂度O(n)
linkedlist[2] = 88
print(linkedlist) # [1,2,88,3]

# 删除元素
# 时间复杂度O(n)
# del linkedlist[2] # 删除索引为2的node
linkedlist.remove(88)	# 删除值为88的node
print(linkedlist)

# 获取长度
# 时间复杂度O(1),deque中创建链表时会有一个内置参数来统计链表的长度。
length = len(likedlist)
print(length) # 3

哈希表–散列表

key: value 键值对
Java–HashMap
Python–字典
key->哈希函数->内存地址->key/value对应的内存地址
会存在内存碰撞
常用操作:

# 创建哈希表
# 创建哈希表通过array
hashTable = ['']*4
# 创建哈希表通过dictionary
mapping = {}

# add element
# time complexity O(1)
hashTable[1] = 'xiaoming'
hashTable[2] = 'lihua'
mapping[1] = 'xiaoming'
mapping[2] = 'lihua'

# update element
# time complexity
hashTable[1] = 'bishi'
mapping[1] = 'bishi'

# remove element
# O(1)
hashTable[1] = ''
mapping.pop(1)
del mapping[1]

# get element
# O(1)
hashTable[3]
mapping[3]

# check key
# O(1)
3 in mapping # --> True or False

# length
# O(1)
len(mapping)

# is Empty
len(mapping) == 0

集合set

无序,不重复
set:

  • HashSet
  • LinklistSet
  • Tree Set

HashSet: 元素–> 哈希函数 -->哈希值 --> 哈希表:

  • 如果有元素:对比:如果相等,更新。如果不相等,哈希冲突–>链表法
  • 如果没有元素:直接存

搜索:无哈希冲突O(1),有哈希冲突O(k)
插入:无哈希冲突O(1),有哈希冲突O(k)
删除:无哈希冲突O(1),有哈希冲突O(k)

# create set
s = set()

# add element O(1)
s.add(10)
s.add(3)
s.add(2)
s.add(2)
print(s) 	#{2,3,10}

# search element O(1)
2 in s

# delete element O(1)
s.remove(2)

# length O(1)
len(s)

大根堆,小根堆
堆化: 一组数–> 完全二叉树–>最大堆/最小堆
堆化:O(n) 把一组无序的数加到堆里面去
添加是O(logn)

常用操作

import heapq
# creat minheap
minheap = [8,9,4]
heapq.heapify(minheap)

# add element
heapq.heappush(minheap, 10)

# peak
minheap[0]

# delete top
heapq.heappop(minheap)

# size
len(minheap)

# iteration
while len(minheap) != 0:
	print(heapq.heappop(minheap)

转化成最大堆时候将列表中的数值取反
minheap = [8,9,4]转化成new_minheap = [-8,-9,-4]
再对new_minheap进行最小堆化

节点之间相互连接,与其他节点的关系为朋友,不像树那样的父子关系。
图
每个边被称为一个度,节点与节点之间的关系为边,上图例子中,每个节点有两个边(度)
有向图
有向图
权重图:边之间会有权重值

权重图

Trie:字典树(前缀树)

前缀树
Trie
Trie:

  • root 节点 root = Trie()
  • 孩子节点:HashMap<字符为key, Trie为value>
  • 结束flag:boolean isEnd,判断是否为数组里面该有的单词
  • 对应的值:string val

算法

双指针

两个指针解决一道题。
普通双指针:两个指针往同一个方向移动
对撞双指针:两个指针面对面移动:有序数列的时候可以考虑一下对撞双指针
快慢双指针:慢指针+快指针
快慢指针的应用:判断环形链表
s = s.next
f = f.next.next

二分查找法

有序数列,left, right, mid = (left+right)/2

滑动窗口

目的减少while循环
a = [1,4,2,3,4,5,6] 三个为一组取最大的和。
一般想法是指针指向a[0],然后开始加上本身和后面的两个数,一直遍历到结尾。
如图
滑动窗口
在相加过程当中,其中4,2被加了两次,为了让计算次数减少,因此用滑动窗口来解决这个问题。
滑动
数组中定长的问题。

回溯法

应用查找全部子集
回溯法按深度优先策略搜索问题的解空间树。首先从根节点出发搜索解空间树,当算法搜索至解空间树的某一节点时,先利用剪枝函数判断该节点是否可行(即能得到问题的解)。如果不可行,则跳过对该节点为根的子树的搜索,逐层向其祖先节点回溯;否则,进入该子树,继续按深度优先策略搜索。

    回溯法的基本行为是搜索,搜索过程使用剪枝函数来为了避免无效的搜索。剪枝函数包括两类:1. 使用约束函数,剪去不满足约束条件的路径;2.使用限界函数,剪去不能得到最优解的路径。

    问题的关键在于如何定义问题的解空间,转化成树(即解空间树)。解空间树分为两种:子集树和排列树。两种在算法结构和思路上大体相同。

回溯法应用
当问题是要求满足某种性质(约束条件)的所有解或最优解时,往往使用回溯法。

   它有“通用解题法”之美誉。

例如:寻找[1, 2, 3]的全部子集
分为长度为0,1,2,3
0:[]
1:[1], [2], [3]
2:root

  • 1: 2, 3
  • 2: 3
  • 3

回溯

深度优先搜索(DFS)

支路搜索
深度优先

广度优先搜索(BFS)

层级搜索
广度优先

并查集(union find)

用索引表示数字,用索引部分的值表示该数字的根

class UnionFind:
	def __init__(self):
		self.root = [-1]*(row*col)
		for i in range(row*col):
		self.root[i] = i
	def find(self, x):
		if x == self.root[x]
			return self.root[x]
		else:
			return self.find(self.root[x])
	
	def union(self, x, y):
		rootX = self.find(x)
		rootY = self.find(y)
		if rootX != rootY:
			self.root[rootX] = rootY

并查集优化

Quick Find: 直接把索引部分的值改成根节点,而不是上一节点

def find(self, x):
	if x == self.root[x]
		return self.root[x]
	else:
		self.root[x] = self.find(self.root[x])
		return self.root[x]

Quick Union — 权重
思想:防止树太高
在这里插入图片描述
权重做法:首先比较哪个树高将高的树的根节点作为union之后的根节点
权重做法

def __init__(self):
	self.root = [-1] * n
	self.rank = [0] * n
def union(self, x, y):
	rootx = self.find(x)
	rooty = self.find(y)
	if rootx != rooty:
		if self.rank[rootx] > self.rank[rooty]:
			root[rooty] = rootx
		elif self.rank[rootx] < self.rank[rooty]:
			root[rootx] = rooty
		else:
			root[rooty] = rootx
				self.rank[rootx] += 1

贪心算法

每一步做的都是当前看起来最好的选择,只是局部最优

记忆化搜索

目的减少重复计算
把计算好的结果存起来,等下一次用的时候直接调用,而不需要重新计算
例:斐波那契数列
把每个值存起来
记忆化搜索
数组的索引代表F(0), F(1)…修改数组中的值来保存

动态规划

动态规划
[0][0] = 1 作为初始状态

三要素:

  • 初始状态
  • 方程式(状态转移方程式)
  • 终止状态

斐波那契,求F5
初始:F0=0, F1=1
方程式:Fn = Fn-1 + Fn-2
终止状态:F5

应用:

  1. 计数:有多少种方式方法:机器人从左上到右下有多少路径
  2. 求最值:最大值/最小值:机器人从左上到右下的最大数字之和(每个格子都有权重值)
  3. 求存在性:是否存在某个可能性:是否存在机器人从左到右的路径(每个格子都有通行的方向,并不是任何方向都可以通行)

递归:

  • 回溯
  • 分治
  • DFS: DFS,栈 | BFS,队列
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值