dsa-python

工具

算法可视化
https://pythontutor.com/
https://visualgo.net/zh

参考

数据结构脑图: https://naotu.baidu.com/file/b832f043e2ead159d584cca4efb19703?token=7a6a56eb2630548c
算法脑图 :https://naotu.baidu.com/file/0a53d3a5343bd86375f348b2831d3610?token=5ab1de1c90d5f3ec
https://shimo.im/docs/9ty8pjk6ckxGrkQt/read

概念

存取/存储 – 还需要再看

  • 随机存取

数据读取与写入,需要的时间与该数据在存储器中的物理地址无关
典型数据类型:数组(因为根据线性公式可以轻松的访问任何一个内存地址下的数据)

  • 顺序存取

数据的读取与写入,所需的时间与该数据在存储器中的物理地址有关,其是按记录的逻辑顺序进行操作的
典型数据类型:链表 (因为访问第n个数据必须先访问前n-1个数据)

  • 顺序存储

把逻辑上相邻的数据元素存储在物理位置上相邻的存储单元中,数据元素之间的逻辑关系由存储单元的邻接关系表达
节省存储空间;可以实现数据元素的随机存取
不便于对数据的修改,如插入、删除等
产生磁盘碎片,因为顺序存储只能使用相邻的一整块存储单元
典型数据类型:数组

  • 随机存储

用一组任意的存储单元存储线性表的元素,存储单元可以是连续的或不连续的。不要求逻辑上相邻的元素在物理地址上也相邻,而是借助指示元素存储地址的指针来表示元素的逻辑关系
不产生磁盘碎片,数据修改方便
占用内存大,随机存储的每个节点需要数据域和指针域组成
只能实现顺序存取
典型数据类型:链表

2. 算法分析

目标:程序运行时间如何随问题规模变化

控制语句与计算资源无关
赋值语句包含表达式计算变量存储两个基本资源

例子:求和运算 T ( n ) = n + 1 T(n)=n+1 T(n)=n+1

n:变量的规模,影响算法执行时间的主要因素
n+1:算法操作步骤数
T(n):执行时间

2.1 复杂度

2.1.1 时间复杂度

输入数据大小为N时,算法运行所需要的时间;衡量计算操作随数据大小N变化时的变化情况

时间:算法的计算操作数

类型

最差:大 O O O表示法,平均,最佳

2.1.2 空间复杂度

输入数据大小为N时,算法运行所使用的的暂存空间+输出空间大大小

输入空间:存储输入数据所需的空间大小
暂存空间:算法运行过程中,存储所有中间变量和对象等数据所需的空间大小
输出空间:算法运行返回时,存储输出数据所需的空间大小

暂存空间 https://leetcode.cn/leetbook/read/illustration-of-algorithm/r8ytog/

指令空间

编译后,程序指令所使用的内存空间

数据空间

变量使用的空间
声明的常量、变量、动态数据、动态对象

栈帧空间

程序调用函数是基于栈实现的,函数再调用期间,占用常量大小的栈帧空间,直至返回后释放
栈帧空间的累计常出现于递归调用中

2.2 大 O O O表示法

数量级函数 O ( f ( n ) ) O(f(n)) O(f(n))

描述 T ( n ) T(n) T(n)中,随规模n增加时, T ( n ) T(n) T(n)中增长最快的部分

例子:
二分查找 l o g 2 n log_{2}^{n} log2n

2.3 数据类型性能

list

索引、赋值、append()添加都是 O ( 1 ) O(1) O(1),与执行时间无关
a=list()+[k] O ( n + k ) O(n+k) O(n+k)

二分查找

https://blog.csdn.net/qq_45978890/article/details/116094046

数组/字符串

解题方法

  • 逆序遍历
  • 双指针吧,首选双指针

快慢指针:fast快速遍历,slow标志已完成的列表尾部,或待处理的元素头部
对撞指针:left指针,right指针

  • 哈希表(字典),第二考虑字典
  • 位(元素位)运算
  • 结合排序算法
  • 匹配相关KMP-还没看

先进后出

深度优先DFS

队列

先进先出

from collections import deque
queue=deque() # 实例化
quque.append(1) # 入队
queue.popleft() # 出队

广度优先BFS

位运算

双端队列

首尾均能进出

链表

边+节点组成
每个节点(必须)包括

数据本身
指向下一个节点的引用信息

python 链表是基于list数据结构实现的
伪头(使得数据结构中永不为空)
双链表不仅包括伪头还包括伪尾

class ListNode:
	def __init__(self,x):
		self.val=x
		self.next=None

解题方法

  • 遍历法
  • 双指针,优化遍历
  • 递归,!!!

递归

实际上使用的是调用栈进行递归

  • 必须有一个基本结束条件(最小规模问题的直接解决)
  • 改变状态,向基本结束条件演变(减小问题规模)
  • 调用自身
import sys

sys.getrecursionlimit() # 系统调用栈最大深度
sys.setrecursionlime(number) # 设置调用栈最大深度

递归与分而治之

分而治之是递归的思想,递归是分而治之的代码实现

递归与递推

递归自顶向下,直接面向问题,直接解决问题
递推自底向上,从这个问题最开始的样子出发,一点点的逐步演化成为最终想要解决问题的样子

自底向上思考的方向直接从一个问题的源头开始,逐步求解
自顶向下,先拆分,需要借助数据结构(栈)记录拆分过程中的每个子问题

动态规划

问题的最优解包含了更小规模子问题的最优解,该问题可以能够用动态规划求解

在问题可被分解为彼此独立且离散的子问题时,可以使用动态规划,子问题依赖动态规划不行

动态规划的两个思考方向

记忆化递归:对应了上述自顶向下的问题解决方向
递推:对应了自底向上的逐步求解问题的方向

动态规划与递归

递归是减小规模
动态规划是增加规模求解

贪婪算法

每一步选择局部最优解

排序

可视化
https://visualgo.net/zh/

冒泡排序

每个epoch逐次交换相邻的顺序

for j in range(len(nums)-1,0,-1):
    for i in range(j): # j=len(nums)-1
        if nums[i]>nums[i+1]:
            nums[i],nums[i+1]=nums[i+1],nums[i]

选择排序

每个epoch只交换最大的顺序

for j in range(len(nums)-1,0,-1):
    num_max_idx=j
    for i in range(j):
        if nums[i]>nums[num_max_idx]:
            num_max_idx=i
    nums[j],nums[num_max_idx]=nums[num_max_idx],nums[j]

归并排序

先分解:二分查找
然后递归 归并(合并排序)

快速排序

分治思想–递归
中位值
左右游标,交换,递归

def quicksort(arr,left=None,right=None):
    left=0 if not isinstance(left,(int,float)) else left
    right=len(arr)-1 if not isinstance(right,(int,float)) else right
    
    if left<right:
        partitionIndex=partition(arr,left,right) # 进行划分 
        quicksort(arr,left,partitionIndex-1) # 左半部分
        quicksort(arr,partitionIndex+1,right) # 右半部分
        
    return arr

def partition(arr,left,right): # 以最左边为参考基准,在传入right的时候,这里并不包括分割点
    pivot=left
    index=pivot+1
    i=index
    
    while i<=right:
        if arr[i]<arr[pivot]:
            swap(arr,i,index)
            index+=1
        i+=1
        
    swap(arr,pivot,index-1)
    return index-1

def swap(arr,i,j): # 交换数值,inplace
    arr[i],arr[j]=arr[j],arr[i]

计数排序

堆排序

希尔排序

基数排序

稳定排序

相同数据下,相对位置不发生改变

稳定:冒泡,插入,归并
不稳定:选择,快速

散列表(字典)

散列表

建立了关键字存储地址之间的一种直接映射关系
根据给定的关键字来计算出关键字在表中的地址的数据结构

散列函数

将数据项映射到一个散列槽中
就是一个 把查找表中的 关键字 映射成该关键字对应的地址 的函数 address=hash(key)
完美散列函数,能够保证所有数据都能够确定一个唯一的散列槽,没有任何冲突

见哈希算法

冲突解决

开放地址方法,线性探测

会出现数据聚集的现象

跳跃探测

散列表示一个稀疏数组(总是有空白元素的数组称为稀疏数组)(注释:散列表的单元叫做表元,在dict的散列表中,每个键值对都占用一个表元,每个表元有两个部分,一个是对键的引用,一个是对值得引用,因为所有表元的大小一致,所以可以通过偏移量来读取某个表元)
如果要把一个对象放入散列表中,首先要计算这个元素键的散列值,利用hash()算法

  • 节点

每个节点具有名称,或键值对,节点还可以保存额外数据项,数据项根据不同的应用而变
兄弟节点:同一父节点的节点

根节点:只有出边
叶节点:只有入边

  • 层级

根节点算层级0
从根节点开始到达一个节点的路径所包含的边的数量

  • 高度

树中所有节点的最大层级
(树种所有节点的最大层级数+1)

class TreeNode:
	def __init__(self,val=0,left=None,right=None):
		self.val=val
		self.left=left
		self.right=right

遍历方法

  • 栈 – 前序遍历
  • 递归 树实际上具有递归定义

二叉树

节点从根开始的路径是唯一的
每个节点最多有两个子节点(最多有n个节点叫做n叉树)

遍历

  • 先序遍历:根-左-右
  • 中序遍历:左-根-右
  • 后序遍历:左-右-根
  • 层序遍历:广度优先,队列数据结构(先、中、后序遍历利用的都是深度优先

迭代遍历-栈深度优先

补充,统一迭代写法https://leetcode.cn/problems/binary-tree-postorder-traversal/solution/er-by-forestsking-17uq/
https://leetcode.cn/problems/binary-tree-inorder-traversal/solution/yan-se-biao-ji-fa-yi-chong-tong-yong-qie-jian-ming/

# 前序遍历
# res 遍历的结果
# stack 模拟栈
res,stack=[],[]
stack.append(root)
while stack:
	node=stack.pop()
	res.append(node.val)
	
	if node.right is not None:
		stack.append(node.right)
	if node.left is not None:
		stack.append(node.left)

# 中序遍历 这个版本不好理解
res,stack=[],[]

while stack or curr:
	while curr:
		stack.append(curr)
		curr=curr.left
	node=stack.pop()
	res.append(node.val)
	curr=node.right

# 后序遍历,逆向思维-巧妙解法(非正规)
res,stack=[],[]
stack.append(root)
while stack:
	node=stack.pop()
	res.append(node.val)
	
	if node.left is not None:
		stack.append(node.left)
	
	if node.right is not None:
		stack.append(node.right)
res.reverse()

树与递归

# 前序遍历
node=root
res=[]
def preorder(node,res):
	if node is None:
		return
	res.append(node.val)
	self.preorder(node.left,res)
	self.preorder(node.right,res)

# 中序遍历
def inorder(node,res):
	if node is None:
		return 
	self.inorder(node.left,res)
	res.append(node.val)
	self.indorder(node.right,res)

# 后序遍历
def inorder(node,res):
	if node is None:
		return 
	self.inorder(node.left,res)
	self.indorder(node.right,res)
	res.append(node.val)

二叉搜索树

对于二叉搜索树,可以通过中序遍历得到一个递增的有序序列

完全二叉树

https://www.bilibili.com/video/BV1W54y1X7A9?p=8

哈希/加密

哈希

哈希函数可以把任意长度的数据(字节串)计算出一个固定长度的结果数据

特点

相同的源数据,采用相同的哈希算法,计算出来的哈希值一定相同
不能的源数据,使用相同的哈希算法,可能会产生相同的值,叫做碰撞率
不管源数据多大,相同的哈希算法,计算出来的哈希值长度一样长
哈希值结果长度越长,碰撞率越低,(计算的市场也越长)
算法不可逆,不能通过哈希值反算出源数据(哈希不同于加密解密)

应用场景

  • 校验信息

计算哈希值https://www.mobilefish.com/services/hash_generator/hash_generator.php

import hashlib

加密解密

加解密算法,可逆
哈希算法对很大的数据产生比较小的哈希值,而加密算法,如果源数据很大,加密后的数据也很大

分类

对称加密:加密和解密使用相同的秘钥
不对称加密:加解密使用不同的秘钥,公钥(用于加密),私钥(用于解密)

第三方库

https://www.byhy.net/tut/py/etc/hash_encrypt/

公钥私钥

逻辑关系https://zhuanlan.zhihu.com/p/113522792

SSH

https://zhuanlan.zhihu.com/p/108161141

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值