笔记|数据结构与算法(python)

Python数据结构与算法

递归实例:汉诺塔问题

分析:
		n个盘子时:
			1.把 n-1 个圆盘从A经过C移动到B(保留最底下的大圆盘)
			2.把第 n 个圆盘从A移动到C
			3.把 n-1 个小圆盘从B经过A移动到C
代码:
def hanoi(n, a, b, c):		# 把n个圆盘从a经过b移动到c
	if n > 0:
		# 把上面的n-1个圆盘从a经过c移动到b
		hanoi(n-1, a, c, b)
		# 将最底下的第n个圆盘移动到c
		print("moving from %s to %s" % (a, c))
		# 把n-1个圆盘从b经过a移动到c
		hanoi(n-1, b, a, c)

hanoi(3, 'A', 'B', 'C')

列表查找 二分查找

列表查找(线性查找)

def linear_search(li, val):
	for ind, v in enumerate(li):		# enumerate 可对列表(字符串)进行遍历,同时获得索引和值
		if v == val:
			return ind
	else:
		return Mone

二分查找|时间复杂度O(log(n))

	注意:二分查找要求列表有序排列
def binary_search(li, val):
	left = 0
	right = len(li) - 1
	while(left <= right):		# 候选区有值
		mid = (left + right) // 2
		if li[mid] == val:
			return mid
		elif li[mid] > val:		# 待查找的值在mid的左边,更新右边界right
			right = mid - 1
		else:					# 待查找的值在mid的右边,更新左边界left
			left = mid + 1
	else:
		return None

li = [1, 2, 3, 4, 5, 6, 7, 8, 9]
binary_search(li, 3) 

测试两个列表查找的速度

# 在同一个文件夹下面新建一个cal_time.py文件,用来测量函数的运行速度
import time

def cal_time(func):
	def wrapper(*args, **kwargs):
		t1 = time.time()
		result = func(*args, **kwargs)
		t2 = time.time()
		print("%s running time: %s secs." % (func.__name__, t2 - t1))
		return result
	return wrapper


# 将上面的查找函数的文件里面导入cal_time
from cal_time import *

@cal_time
def linear_search(li, val):
	for ind, v in enumerate(li):		# enumerate 可对列表(字符串)进行遍历,同时获得索引和值
		if v == val:
			return ind
	else:
		return Mone

@cal_time
def binary_search(li, val):
	left = 0
	right = len(li) - 1
	while(left <= right):		# 候选区有值
		mid = (left + right) // 2
		if li[mid] == val:
			return mid
		elif li[mid] > val:		# 待查找的值在mid的左边,更新右边界right
			right = mid - 1
		else:					# 待查找的值在mid的右边,更新左边界left
			left = mid + 1
	else:
		return None

li = list(range(100000))
linear_search(li, 38900)
binary_search(li, 38900) 

列表排序

排序:将一组"无序"的记录序列调整为"有序"的记录序列
内置排序函数:sort()

排序Low B三人组

冒泡排序

1.列表每两个相邻的数,如果前面比后面大,则交换这两个数
2.一趟排序完成后,则无序区域减少一个数,有序区增加一个数
代码关键点:趟 [n-i],无序区范围[n-i-1]

import random

def bubble_sort(li):
	for i in range(len(li)-1):		# 第i趟
		for j in range(len(li)-i-1):
			if li[j] > li[j+1]:
				li[j], li[j+1] = li[j+1], li[j]

li = [random.randint(0, 10000) for i in range(10)]		# 随机生成长度为1000的范围在(0,10000)的列表
print(li)
bubble_sort(li)
print(li)

# 优化
def bubble_sort(li):
	for i in range(len(li)-1):		# 第i趟
		exchange = False
		for j in range(len(li)-i-1):
			if li[j] > li[j+1]:
				li[j], li[j+1] = li[j+1], li[j]
				exchange = True
		print(li)
		if not exchange:
			return
选择排序

1.一趟排序记录最小的数,放在第一个位置
2.再一趟排序记录列表无序区最小的数,放在第二个位置
关键:有序区和无序区,无序区最小值的位置

# 简单移动的选择排序
def select_sort_simple(li):
	li_new = []
	for i in range(len(li)):		# 需要n遍
		min_val = min(li)
		li_new.append(min_val)
		li.remove(min_val)
	return li_new
缺点:1.要开辟一个新的等大的列表空间,占用空间内存;	2.min() remove()两个函数还是会遍历一遍列表,耗时

# 正常的选择排序
def select_sort(li):
	for i in range(len(li)-1):
		min_loc = i		# 定义一个初始值位置,用来与最小值交换位置
		for j in range(i+1, len(li)):
			if li[j] < li[min_loc]:
				li[j], li[min_loc] = li[min_loc], li[j]

li = [3, 4, 2, 1, 6, 5, 8, 7, 9]
select_sort(li)
print(li)

插入排序
def insert_sort(li):
	for i in range(1, len(li)):		# i 表示摸到的牌的下标
		tmp = li[i]
		j = i - 1		# j 指的是手里面的牌的下标
		while li[j] > tmp and j >=0:
			li[j+1] = li[j]
			j -= 1
		li[j+1] = tmp

排序NB三人组

快速排序

快速排序思路:
1.取一个元素p(第一个元素),使元素p归位
2.列表被p分成两部分,左边都比p小,右边都比p大
3.递归完成排序

时间复杂度:O(log(n))

def partition(li, left, right):
	tmp = li[left]
	while left < right:
		while left < right and li[right] >= tmp:		# 从右边找比tmp小的数
			right -= 1				# 往左走一步
		li[left] = li[right]		# 把右边的值写在左边的空位上
		while left < right and li[left] <= tmp:
			left += 1
		li[right] = li[left]		# 把左边的值写在右边空位上
	li[left] = tmp			# 将tmp归位
	return left

def quick_sort(li, left, right):
	if left < right:
		mid = partition(li, left, right)
		quick_sort(li, left, mid-1)
		quick_sort(li, mid+1, right)
 
堆排序

树与二叉树的知识:
树是一种数据结果,如:目录结构。树是一种可以递归定义的数据结构。
树是由n个节点组成的集合:
如果n=0,那这是一棵空树;
如果n>0,那存在1个节点作为树的根节点,其他点可以分为m个集合,每个集合本身又是一棵树。
树的深度:从根节点到最下叶子节点的步长
树的度:树中根节点存在最多 叶子节点的数量

二叉树:度不超过2的树
两个孩子节点被区分为左孩子节点和右孩子节点
满二叉树:一个二叉树,如果每一层的节点数都达到最大值,则这个二叉树就是满二叉树
完全二叉树:叶子节点只能出现在最下层和次下层,并且最下面一层的节点都集中在该层最左边 的若干位置的二叉树

堆:一种特殊的完全二叉树结构
大根堆:一棵完全二叉树,满足任一节点都比其孩子节点大
小根堆:一棵完全二叉树,满足任一节点都比其孩子节点小
堆排序:堆的向下调整性质
堆排序过程:
1.建立堆
2.得到堆顶元素,为最大元素
3.去掉堆顶,将堆最后一个元素放到堆顶,此时棵=可通过一次调整重新使用堆有序
4.堆顶元素为第二大元素
5.重复步骤3,直到堆变空
代码实现:

# 调整 
def sift(li, low, high):
	"""
	:param li: 列表
	:param low: 堆的根节点位置
	:param high: 堆最后一个元素的位置
	:return:
	"""
	i = low		# 最开始指向根节点
	j = 2 * 1 - 1		# j最开始为i节点的左孩子
	tmp = li[low]		# 把堆顶存起来
	while j <= high:	# 只要j位置有数
		if j + 1 <= high and li[j] < li[j+1]:	# 如果右孩子有并且比较大
			j = j + 1	# j指向右孩子
		if li[j] > tmp:		# 比较孩子与堆顶的大小,目的将堆顶元素找到合适的位置
			li[i] = li[j]
			i = j		# 往下一次找
			j = 2 * i - 1
		else:		# 堆顶元素更大,将tmp的位置放在i位置上
			li[i] = tmp
			break
	else:
		li[i] = tmp		#把tmp放到叶子节点上

# 使用sift实现堆排序的过程
def heap_sort(li):
	n = len(li)
	for i in range((n // 2 - 1), -1, -1):
		# i表示建对滴时候调整的部分根的下标
		sift(li, i, (n - 1))
	# 递推完成
	for i in range(n - 1, -1, -1):
		# i指向当前堆的最后一个元素
		li[0], li[i] = li[i], li[0]
		sift(li, 0, i - 1)		# i-1是最新的high
	print(li)

li = [i for i in range(100)]
import random
randpm.shuffle(li)

heap_sort(li)

python自带的堆排序模块
import heapq	# q->queue 优先队列
import random

li = list(range(100))
random.shuffle(li)
print(li)

heapq.heapify(li)	# 建堆

for i in range(len(li)):
	print(heapq.heappop(li), end=',')

应用:topk,从n个数中,选出k个大的数

# 调整小顶堆函数
def sift(li, low, high):
	"""
	:param li: 列表
	:param low: 堆的根节点位置
	:param high: 堆最后一个元素的位置
	:return:
	"""
	i = low		# 最开始指向根节点
	j = 2 * 1 - 1		# j最开始为i节点的左孩子
	tmp = li[low]		# 把堆顶存起来
	while j <= high:	# 只要j位置有数
		if j + 1 <= high and li[j] > li[j+1]:	# 如果右孩子有并且比较大
			j = j + 1	# j指向右孩子
		if li[j] < tmp:		# 比较孩子与堆顶的大小,目的将堆顶元素找到合适的位置
			li[i] = li[j]
			i = j		# 往下一次找
			j = 2 * i - 1
		else:		# 堆顶元素更大,将tmp的位置放在i位置上
			li[i] = tmp
			break
	else:
		li[i] = tmp		#把tmp放到叶子节点上

# 使用sift实现堆排序的过程
def topk(li, k):
	heap = li[0: k]		# 先切k个数,建立小顶堆
	for i in range((k // 2 - 1), -1, -1):
		sift(heap, i, (k - 1))
	# 1.建堆	将剩下的数遍历一遍与heap[0]比较,大的留下
	for i in range(k, len(li) - 1):	
		if li[i] > heap[0]:
		heap[0] = li[i]
		sift(heap, 0, k - 1)		
	# 2.遍历
	for i in range(k-1, -1, -1):
		heap[0], heap[i] = heap[i], heap[0]
		sift(heap, 0, i-1)
	# 3.出数
	return heap

import random
li = list(range(1000))
random.shuffle(li)

print(topk(li, 10))

归并排序
# 一次归并
def merge(li, low, mid, high):	# low为列表最左边位置,mid为中间位置,high为最右边位置
	i = low
	j = mid + 1
	ltmp = []
	while i <= mid and j <= high:	#只要左右两边都有数
		if li[i] < li[j]:
			ltmp.append(li[i])
		else:
			ltmp.append(li[j])
	# while执行完。肯定有一部分没有数
	while i <= mid:
		ltmp.append(li[i])
		i += 1
	while j <= high:
		ltmp.append(li[j])
		j += 1
	li[low: high + 1] = ltmp

# li = [2, 4, 5, 7, 1, 3, 6, 8]
# merge(li, 0, 3, 7)
# print(li)

"""
使用归并:
	分解:将列表越分越小,直至分成一个元素
	终止条件:一个元素是有序的
	合并:将两个序列归并,列表越来越大
"""

def merge_sort(li, low, high):
	if low < high:		# 至少有两个元素,递归
		mid = (low + high) // 2
		merge_sort(li, low, mid)
		merge_sort(li, mid + 1, high)
		merge(li, low, mid, high)

NB三人组小结:
三种排序算法的时间复杂度都是O(nlogn)
一般情况下,就运行时间而言:
快速排序<归并排序<堆排序
三种排序算法的缺点:
快速排序:极端情况下排序效率抵
归并排序:需要额外的内存开销
堆排序:在快的排序算法中相对较慢

其他排序

希尔排序

希尔排序(Shell Sort)是一种分组插入排序算法
首先取一个整数a=n/2,将元素分为a个组,每组相邻量元素之间距离为a,在各组内进行直接插入排序
取第二个整数b=a/2,重复上述分组排序过程,直到c=1,即所有元素在同一组内进行直接插入排序
希尔排序每趟并不使某些元素有序,而是使整体数据越来越接近有序;最后一趟排序使得所有数据有序

def insert_sort_gap(li, gap):
	for i in range(gap, len(li)):	# i表示摸到的牌的下标
		tmp = li[i]
		j = i - gap
		while j >= 0 and lil[j] > tmp:
			li[j + gap] = li[j]
			j -= gap
		li[j + gap] = tmp

def shell_sort(li):
	d = len(li) // 2
	while d >= 1:
		insert_sort_gap(li, d)
		d //= 2
计数排序
def count_sort(li, max_count=100):
	count = [0 for _ in range(max_count + 1)]
	for val in li:
		count[val] += 1
	li.clear()
	for ind, val in enumerate(count):
		for i in range(val):
			li.append(ind)

import random
li = [random.randint(0, 100) for _ in range(1000)]
print(li)
count_sort(li)
print(li)
桶排序

首先将元素分在不同的桶中,再 对每个桶中的元素排序

def bucket_sort(li, n = 100, max_num = 10000):
	buckets = [[] for _ in range(n)]		# 创建桶
	for var in li:
		i = min(var // (max_num // n), n - 1)	# i 表示var放到几号桶里
		buckets[i].append(var)	# 把var放到对应桶里
		for j in range(len(bucket[i]) - 1, 0, -1):
			if buckets[i][j] < buckets[i][j - 1]:
				buckets[i][j], buckets[i][j - 1] = buckets[i][j - 1], buckets[i][j]
			else:
				break
	sorted_li = []
	for buc in buckets:
		sorted_li.extend(buc)
	return sorted_li
基数排序
def radix_sort(li):
	max_num = max(li)	# 最大值
	it = 0
	while 10 ** it <= max_num:
		buckets = [[] for _ in range(10)]
		for var in li:
			digit = (var // 10 ** it) % 10
			buckets[digit].apend(var)
		# 分桶完成
		li.clear()
		for buc in buckets:
			li.extend(buc)
		# 把数重新写回li
		it += 1

import random
li = list(range(10000))
random.shuffle(li)
radix_sort(li)
print(li)

数据结构

数据结构按照其逻辑结构可分为线性结构、树结构、图结构
线性结构:数据结构中的元素存在一对一的相互关系
树结构:数据结构中的元素存在一对多的相互关系
图结构:数据结构中的元素存在多对多的相互关系

栈:

栈是一个数据集合,可以理解为只能在一端进行插入或删除操作的列表
栈的特点:后进先出 LIFO
栈的概念:栈顶、栈底
栈的基本操作:
	进栈(压栈):push
	出栈:pop
	取栈顶:gettop
"""
用列表实现栈
"""
class Stack:
	def __init__(self):
		self.stack = []
	
	def push(self, element):
		self.stack.append(element)
	
	def pop(self):
		return self.stack.pop()
	
	def get_top(self):
		if len(self.stack) > 0:
			return self.stack[-1]
		else:
			return None
	
	def is_empty(self):
		return len(self.stack) == 0

stack = Stack()
stack.push(1)
stack.push(2)
stack.push(3)
print(stack.pop())
print(stack.get_top())

栈的应用:括号匹配问题
给出一个字符串,其中包含小括号、中括号、大括号,求该字符串中括号是否匹配

class Stack:
	def __init__(self):
		self.stack = []
	
	def push(self, element):
		self.stack.append(element)
	
	def pop(self):
		return self.stack.pop()
	
	def get_top(self):
		if len(self.stack) > 0:
			return self.stack[-1]
		else:
			return None
	
	def is_empty(self):
		return len(self.stack) == 0

def brace_match(s)
	match = {"}": "{", "]": "[", ")": "("}
	stack = Stack()
	for ch in s:
		if ch in {'(', '[', '{'}:
			stack.push(ch)
		else:		# ch in {')', ']', '}'}
			if stack.is_empty():
				return False
			elif stack.get_top() == match[ch]:
				stack.pop()
			else:
				return False
	if stack.is_empty():
		return True
	else:
		return False	

队列

  • 队列(Queue)是一个数据集合,仅允许在列表的一端进行插入,另一端进行删除
  • 进行插入的一端称为队尾(rear),插入动作称为进队或入队
  • 进行删除的一端称为队头(front),删除动作称为出队
  • 队列的性质:先进先出(First-in, First-out)

队列的实现方式——环形队列

class Queue:
	def __init__(self, size=100):
		self.queue = [0 for _ in range(size)]
		self.size = size
		self.rear = 0	# 队尾指针
		self.front = 0	# 队首指针
	
	# 入队
	def push(self, element):
		if not self.is_filled():
			self.rear = (self.rear + 1) % self.size
			self.queue[self.rear] = elemnet
		else:
			raise IndexError("Queue is filled.")
	
	# 出队
	def pop(self):
		if not self.isempty():
			self.front = (self.front + 1) % self.size
			return self.queue[self.front]
		else:
			raise IndexError("Queue is empty.")
	
	# 判断是否为空队列
	def is_empty(self):
		return self.rear == self.front

	# 判断是否为满队
	def is_filled(self):
		return (self.rear + 1) % self.size == self.front

python的内置队列-双向队列

from collections import deque

q = deque()		# 队列满时,队首会自动出队
q.append(1)				# 队尾进队
print(q.popleft())		# 队首出队
print(q.popleft())		# 报错

# 用于双向队列
q.appendleft(1)		# 队首进队
q.pop()				# 队尾出队

栈和队列的应用——迷宫问题
在这里插入图片描述

"""
栈————深度优先搜索
回溯法
	思路:从一个节点开始,任意找下一个能走的点,当找不到能走的点时,退回上一个点寻找是否有其他方向的点。
	使用栈存储当前路径
"""
maze = [
	[1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
	[1, 0, 0, 1, 0, 0, 0, 1, 0, 1],
	[1, 0, 0, 1, 0, 0, 0, 1, 0, 1],
	[1, 0, 0, 0, 0, 1, 1, 0, 0, 1],
	[1, 0, 1, 1, 1, 0, 0, 0, 0, 1],
	[1, 0, 0, 0, 1, 0, 0, 0, 0, 1],
	[1, 0, 1, 0, 0, 0, 1, 0, 0, 1],
	[1, 0, 1, 1, 1, 0, 1, 1, 0, 1],
	[1, 1, 0, 0, 0, 0, 0, 0, 0, 1],
	[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
]

dirs = [
	lambda x, y: (X + 1, y),
	lambda x, y: (X - 1, y),
	lambda x, y: (X, y + 1),
	lambda x, y: (X, y - 1)
]

def maze_path_stack(x1, y1, x2, y2):
	stack = []
	stack.append((x1, y1))
	while(len(stack) > 0):
		curNode = stack[-1]		# 当前节点
		# 判断是否走到终点
		if curNode[0] == x2 and curNode == y2:
			for p in stack:
				print p
			return True
			
		# x,y四个方向x-1,y; x+1,y; x,y-1; x,y+1
		for dir in dirs:
			nextNode = dir(curNode[0], curNode[1])
			# 如果下一个节点能走
			if maze[nextNode[0]][nextNode[1]] == 0:
				stack.append(nextNode)
				maze[nextNode[0]][nextNode[1]] = 2	# 2表示为已经走过
				break
		else:
			maze[nextNode[0]nextNode[1]] = 2
			stack.pop()
	else:
		print("没有路")
		return False


"""
队列————广度优先搜索
	思路:从一个节点开始,寻找所有接下来能继续走的点,继续不断寻找,直到找到出口
	使用队列存储当前正在考虑的节点
"""
from collections import deque

def print_r(path):
	curNode = path[-1]		# 找到的终点
	realpath = []			# 建立一个列表,存储最短路径上面的值
	while curNode[2] == -1:		# 与起点重合
		realpath.append(curNode[0: 2])	# 将节点的x,y值写入realpath中
		curNode = path[curNode[2]]		# 使用节点对应的上一个节点的指针位置,找到上一个节点
		
	realpath.append(curNode[0: 2])		# 起点
	realpath.reverse()
	
	for node in realpath:
		print(node)
	

def maze_path_queue(x1, y1, x2, y2):
	 queue = deque()
	 queue.append((x1, y1, -1))		# 第三个位置用来存储上上一步对应的位置指针
	 path = []
	 while len(queue) > 0:
	 	curNode = queue.pop()
	 	path.append(curNode)
	 	# 到达终点
	 	if curNode[0] == x2 and curNode[1] == y2:
	 		print_r(path)
	 		return True
		for dir in dirs:
			nextNode = dir(curNode[0], curNode[1])
			if maze[nextNode[0]][nextNode[1]] == 0:
				queue.append((nextNode[0], nextNode[1], len(path) - 1))		# 后续节点进队,记录哪个节点带他来的
				maze[nextNode[0]][nextNode[1]] = 2
	else:
		print("没有路")
		return False

链表

链表是由一系列节点组成的元素集合。每个节点包含两部分,数据域item和指向下一个节点的指针next。通过节点之间的相互连接,最终串联成一个链表

"""
创建链表:头插法、尾插法
"""

class Node:
	def __init__(self, item):
		self.item = item
		self.next = None

def create_linklist_head(li):		# 头插法
	head = Node(list[0])
	for element in li[1: ]:
		node = Node(element)
		node.next = head
		head = node
	return head

def create_linklist_tail(li):		# 尾插法
	head = Node(li[0])
	tail = head
	for element in li[1: ]:
		node = Node(element)
		tail.next = node
		tail = node
	return head

def print_linklist(lk):
	while lk:
		print(lk.item, end=',')
		lk = lk.next

链表的插入和删除操作

# 插入
p.next = curNode.next
curNode.next = p
# 删除
p = curNode.next
curNode.next = curNode.next.next
del p

双链表
双链表的每个节点有两个指针:一个指向后一个节点,另一个指向前一个节点

# 双链表的创建、插入和删除
# 创建
class Node:
	def __init__(self, item=None):
		self.item = item
		self.next = None
		self.prior = None

# 插入
p.next = curNode.next
curNode.next.prior = p
curNode.next = p
p.prior = curNode

# 删除
p = curNode.next
p.next.prior = curNode
curNode.next = p.next
del p

哈希表

哈希表一个通过哈希函数来计算数据存储位置的数据结构,通常支持如下操作:
insert(key, value):插入键值对(key, value)
get(key):如果存在键为key的键值对则返回其value,否则返回空值
delete(key):删除键为key的键值对

解决哈希冲突——拉链法
拉链法:哈希表每个位置都连接一个链表,当冲突发生时,冲突的元素将被加到该位置链表的最后
在这里插入图片描述
常见的哈希函数
除法哈希表:
h(k) = k % m
乘法哈希法:
h(k) = floor(m*(A*key%1))
全域哈希法:
在这里插入图片描述

class LinkList:
	class Node:
		def __init__(self, item=None):
			self.item = item
			self.next = None
	
	class LinkListIterator:
		def __init__(self, node):
			self.node = node
		
		def __next__(self):
			if self.node:
				cur_node = self.node
				self.node = cur_node.next
				return cur_node.item
			else:
				raise StopIteration
		
		def __iter__(self):
			return self
	
	def __init__(self, iterable=None):
		self.head = None
		self.tail = None
		if iterable:
			self.extend(iterable)
		
	def append(self, obj):
		s = LinkList.Node(obj)
		if not self.head:
			self.head = s
			self.tail = s
		else:
			self.tail.next = s
			self.tail = s
	
	def extend(self, iterable):
		for obj in iterable:
			self.append(obj)
	
	def find(self, obj):
		for n in self:
			if n == obj:
				return True
		else:
			return False
	
	def __iter__(self):
		return self.LinkListIterator(self, head)
	
	def __repr__(self):
		return "<<" + "+",join(map(str, self)) + ">>"

# 类似于集合的结构
class HashTable:
	def __init__(self, size=101):
		self.size = size
		self.T = [LinkList() for i in range(self, size)]
	
	def h(self, k):
		return k % self.size

	def insert(self, k):
		i = self.h(k)
		if self.find(k)
			print("Duplicated Insert.")
		else:
			self.T[i].append(k)
	
	def find(self, k):
		i = self.h(k)
		return self.T[i].find(k)

# 实现文件夹
class Node:		# 文件节点的累
	def __init__(self, name, type='dir')
		self.name = name
		self.type = type	# "die" or "file"
		self.children = []
		self.parent = None

	def __repr__(self):
		return self.name

class FileSystemTree:		# 树的类
	def __init__(self, name, type='dir')
		self.root = name
		self.now = self.root
	
	def mkdir(self, name):
		# name 以/结尾
		if name[-1] != "/":
			name += '/'
		node = Node(name)
		self.now.children.append(node)
		node.parent = self.now

	def ls(self):
		return self.now.children

	def cd(self, name):
		if name[-1] != "/":
			name += "/"
		
		for child in self.now.children:
			if child.name == name:
				self.now = child
				return
		raise ValueError("invalid dir")
		

二叉树

# 前序遍历
def pre_order(root):
	if root:
		print(root.data, end=',')
		pre_order(root.lchild)
		pre_order(root.rchild)

# 中序遍历
def in_order(root):
	if root:
		in_order(root.lchild)
		print(root.data, end=',')
		in_order(root.rchild)
	
# 后序遍历
def post_order(root):
	if root:
		post_order(root.lchild)
		post_order(root.rchild)
		podt_order(root.data)
		
# 层次遍历
from collections import deque

def level_order(root):
	queue = deque()
	queue.append(root)
	while len(queue) > 0:	# 只要队不空
		node = queue.popleft()
		print(node.data, end=',')
		if node.lchild:
			queue.append(node.lchild)
		if node.rchild:
			queue.append(node.rchild)

二叉搜索树:
二叉搜索树是一颗二叉树且满足性质:设x是二叉树的一个节点。如果y是x左子树的一个节点,那么y.key <= x.key;如果y是x右子树的一个节点,那么y.key >= x.key。

class BiTreeNode:
	def __init__(self, data):
		self.data = data
		self.lchild = None
		self.rchild = None
		self.parent = None

class BST:
	def __init__(self, li=None):
		self.root = None
		if li:
			for val in li:
				self.insert_no_rec(val)

	def insert(self, node, val):
		if not node:
			node = BiTreeNode(val)
		elif val < node.data:
			node.lchild = insert(node.lchild, val)
			node.lchild.parent = node
		elif val > node.data:
			node.rchild = insert(node.rchild, val
			node.rchild.parent = node
		return node
	
	def insert_no_rec(self, val):
		p = self.root
		if not p:
			self.root = BiTreeNode(val)
			return
		while True:
			if val < p.data:
				if p.lchild:
					p = p.lchild
				elif:
					p.lchild = BiTreeNode(val)
					p.lchild.parent = p
					return
			elif val > p.data:
				if p.rchild:
					p = p.rchild
				elif:
					p.rchild = BiTreeNode(val)
					p.rchild.parent = p
					return
			else:
				return

	def query(self, node, val):
		if not node:
			return None
		if node.data < val:
			return self.query(node.rchild, val)
		elif node.data > val:
			return self.query(node.lchild, val)
		else:
			return node

	def __remove_node_1(self, node):
		# 情况1:node是叶子节点
		if not node.parent:
			self.root = None
		if node == node.parent.lchild:	# node是他父亲的左孩子
			node.parent.lchild = None
		else:		# node是他父亲的右孩子
			node.parent.rchild = None

	def __remove_node_21(self, node):
		# 情况2.1:node只有一个左孩子
		if not node.parent:		# 根节点
			self.root = node.lchild
			node.lchild.parent = None
		elif node == node.parent.lchild:
			node.parent.lchild = node.lchild
			node.lchild.parent = node.parent
		else:
			node.parent.rchild = node.lchile
			node.lchild.parent = node.parent

	def __remove_node_22(self, node):
		# 情况2 .2:node只有一个右孩子
		if not node.parent:
			self.root = node.rchild
			node.rchild.parent = None
		elif node == node.parent.lchild:
			node.parent.lchild = node.rchild
			node.rchild.parent = node.parent
		else:
			node.parent.rchild = node.rchild
			node.rchile.parent = node.parent
		
	def delete(self, val):
		if self.root:		# 不是空树
			node = self.query(val)
			if not node:		# 不存在
				return False
			if not node.lchild and not node.rchild:
				self.__remove_node_1(node)
			elif not node.rchild:		# 只有左孩子
				self.__remove_node_21(node)
			elif not node.lchild:		# 只有一个右孩子
				self.__remove_node_22(node)
			else:		# 两个孩子都有.找到右边最下边的左孩子,这个节点满足比node的最孩子都大,比右孩子都小
				min_node = node.rchild
				while min_node.lchild:
					min_node = min_node.lchild
				node.data = min_node.data
				# 删除min_node
				if min_node.rchild:
					self.__remove_node_22(min_node)
				else:
					self.__remove_node_1(min_node)
			

AVL树:AVL树是一棵自平衡的二叉树
AVL树具有以下性质:

  1. 根的左右子树的高度之差的绝对值不能超过1
  2. 根的左右子树都是平衡二叉树
    在这里插入图片描述
from bst import BiTreeNode, BST

class AVLNode(BiTreeNode):
	def __init__(self, data):
		BiTreeNode.__init__(self, data)
		self.bf = 0

class AVLTree(BST):
	def __init__(self, li=None):
	
	def rotate_left(self, p, c):
		s2 = c.lchild
		p.rchild = s2
		if s2:
			s2.parent = p

		c.lchild = p
		p.parent = c
		p.bf = 0
		c.bf = 0
		return c

	def rotate_right(self, p, c):
		s2 = c.rchild
		p.lchild = s2
		if s2:
			s2.parent = p
		c.rchild = p
		p.parent = c
		p.bf = 0
		c.bf = 0
		return c
	
	def rotate_right_left(self, p, c):
		g = c.lchild

		s3 = g.rchild
		c.lchild = s3
		if s3:
			s3.parent = c
		g.rchild = c
		c.parent = g

		s2 = g.lchild
		p.rchild = s2
		if s2:
			s2.parent = p
		g.lchild = p
		p.parent = g

		# 更新后的bf
		if g.bf > 0:
			p.bf = -1
			g.bf = 0
		elif g.bf < 0:
			p.bf = 0
			c.bf = 1
		else:		# 此时S1、S2、S3、S4 的高度都为0
			p.bf = 0
			c.bf = 0
		return g

	def rotate_left_right(self, p, c):
		g = c.rchild

		s2 = g.lchild
		c.rchild = s2
		if s2:
			s2.parent = c
		g.lchild = c
		c.parent = g
		
		s3 = g.rchild
		p.lchild = s3
		if s3:
			s3.parent = p
		g.rchild = p
		p.parent = g

		# 更新bf
		if g.bf > 0:
			p.bf = 0
			c.bf = -1
		elif g.bf < 0:
			p.bf = 1
			c.bf = 0
		else:
			p.bf = 0
			c.bf = 0
		return g

	def insert_no_rec(self, val):
		# 1.和BST一样,先插入
		p = self.root
		if not p:
			self.root = BiTreeNode(val)
			return
		while True:
			if val < p.data:
				if p.lchild:
					p = p.lchild
				elif:	# 左孩子不存在
					p.lchild = BiTreeNode(val)
					p.lchild.parent = p
					node = p.lchild		# node 存储的就是插入的节点
			elif val > p.data:
				if p.rchild:
					p = p.rchild
				elif:
					p.rchild = BiTreeNode(val)
					p.rchild.parent = p
					node = p.rchild
			else:		# val = p.data
				return
		# 2.更新bf
		while node.parent:
			if node.parent.lchild == node:		# 传递是从左子树来的,左子树更沉了
				# 更新node.parentd 的bf = -1
				if node.parent.bf < 0:		# 原来node.parent.bf = -1, 更新后变成-2
					# 旋转
					# 看node哪边沉
					g = node.parent.parent	# 为了连接旋转后的子树
					x = node.parent		# 旋转前的子树根
					if node.bf > 0:	# 连接
						n = self.rotate_lefr_right(node.parent, node)
					else:
						n = self.rotate_right(node.parent, node)
					# 记得:将n和g连起来
				elif node.parent.bf > 0:		# 原来node.parent.bf = 1,更新后变成0
					node.parent.bf = 0
					break
				else:		# 原来node.parent.bf = 0,更新后变成-1
					node.parent.bf = -1
					node = node.parent
					continue
			else:		# 传递是从右子树来的,右子树更沉了
				# 更新node.parent.bf += 1
				if node.parent.bf > 0:	# 原来node.parent.bf =1,更新后变成2
					# 旋转
					# 看node哪边沉
					g = node.parent.parent	# 为了连接旋转后的子树
					x = node.parent		# 旋转前的子树根
					if node.bf < 0:	# node.bf = 1
						n = self.rotate_right_left(node.parent, node)
					else:		# node.bf = -1
						n = self.rotate_left(node.parent, node)
				elif node.parent.bf < 0:	# 原来node.parent.bf = -1,更新后变成0
					node.parent.bf = 0
					break
				else:	# 原来node.parent.bf = 0,更新后变成1
					node.parent.bf = 1
					node = node.parent
					continue
			# 连接旋转后的子树
			n.parent = g
			if g:	# g 不为空
				if x == g.lchild:
					g.lchild = n
				else:
					g.rchild = n
				break
			else:
				self.root = n
				break
贪心算法

在这里插入图片描述

# 找零问题
t = [100. 50, 20, 5, 1]

def change(t, n):
	m = [0 for _ in range(t)]
	for i, money in enumerate(t):
		m[i] = n // money
		n = n % money
	reutrn m, n

print(change(t, 367))
背包问题——贪心算法

在这里插入图片描述

# 分数背包————分数背包

good = [(60, 10), (100, 20), (120, 30)]
good.sort(key=lambda x: x[0]/x[1], reverse=True)

def fractional_backoack(goods, w):
	m = [0 for _ in range(len(goods))]
	total_v = 0
	for i, (prize, weight) in enumerate(goods):
		if w >= weight:
			m[i] = 1
			total_v += prize
			w -= weight
		else:
			m[i] = w / weight
			total_v += m[i] * prize
			w = 0
			break
	return total_v, m

print(fraction_backpack(goods, 50))
拼接最大数字问题——贪心算法
from functools import cmp_to_key

li = [32, 94, 128, 1286, 6, 71]

def xy_cmp(x, y):
	if x + y < y + X:
		return 1
	elif x + y > y + x:
		return -1
	else:
		return 0

def number_join(li):
	li = list(map(str, li))
	li.sort(key=cmp_to_key(xy_cmp))
	return "".join(li)

print(number_join(li))
活动选择问题——贪心算法

在这里插入图片描述
在这里插入图片描述

activities = [(1, 4), (3, 5), (0, 6), (5, 7), (3, 9), (5, 9), (6, 10), (8, 11), (8, 12), (2, 14), (12, 16)]
# 保证活动是按照结束时间排好序的
activities.sort(key=lambda x: x[1])

def activity_selection(a):
	res = [a[0]]
	for i in range(1, len(a)):
		if a[i][0] >= res[-1][1]:	# 当前活动的开始时间大于等于最后一个入选活动的结束时间
			res.appent(a[i])
	return res

print(activity_selection(activities))

动态规划

钢条切割问题——动态规划

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

 import time

def cal_time(func):
	def wrapper(*args, **kwargs):
		t1 = time.time()
		result = func(*args, **kwarge)
		t2 = time.time()
		print("%s running time: %s secs." % (func.__name__,t2 - t1))
		return result
	return wrapper

p = [0, 1, 5, 8, 9, 10, 17, 17, 20, 21, 23, 24, 26, 27, 27, 28, 30, 33, 36, 39, 40]

def cut_rod_recurision_1(p, n):
	if n == 0:
		return 0
	else:
		res = p[n]
		for i in range(1, n):
			res = max(res, cut_rod_recurision_1(p, i) + cut_rod_recurision_1(p, n - i))
		return res

@cal_time
def c1(p, n):
	return cut_rod_recurision_1(p, n)

# 自顶向下递归实现
def cut_rod_recurision_2(p, n):
	if n == 0:
		return 0
	else:
		res = 0
		for i in range(1, n + 1):
			res = max(res, p[i] + cut_rod_recurision_2(p, n - i))
		return res

@cal_time
def c2(p, n):
	return cut_rod_recurision_2(p, n)

# 动态规划——自底向上
def cut_rod_dp(p, n):
	r = [0]
	for iin range(1, n + 1):
		res = 0
		for j in range(1, 1 + 1):
			res = max(res, p[j] + r[i - j])
		r.append(res)
	return r[n]

# 钢条切割问题——重构解
# 对每个子问题,保存切割一次时左边切下的长度
def cut_rod_extend(p, n):
	r = [0]
	s = [0]
	for i in range(1, n + 1):
		res_r = 0	# 价格的最大值
		res_s = 0	# 价格最大值对应方案的左边不切割部分的长度
		for j in range(1, i - j)
			if p[j] + r[i - j] > res_r:
				res_r = p[j] + r[i - j]
				res_s = j
		r.append(res_r)
		s.append(res_s)
	return r[n], s

def cut_rod_solution(p, n):
	r, s = cut_rod_extend(p, n)
	ans = []
	while n > 0:
		ans.append(s[n])
		n -= s[n]
	return ans

r, s = cut_rod_extend(p, 10)
print(r, s)
print(cut_rod_solution(p, 9))

在这里插入图片描述
动态规划:最优化的问题试用(最大…最小…)

最长公共子序列——动态规划

在这里插入图片描述
在这里插入图片描述

# 最长公共子序列

def lcs_length(x, y):
	m = len(x)
	n = len(y)
	c = [[0 for _ in range(n + 1)] for _ in range(m + 1)]
	for i in range(1, m + 1):
		for j in range(1, n + 1):
			if x[i - 1] == y[j - 1]:
				c[i][j] = c[i - 1][j - 1] + 1
			else:
				c[i][j] = max(c[i][j - 1], c[i - 1][j])
	return c[m][n]

在这里插入图片描述

def lcs(x, y):
	m = len(x)
	n = len(y)
	c = [[0 for _ in range(n + 1)] for _ in range(m + 1)]
	b = [[0 for _ in range(n + 1)] for _ in range(m + 1)]	# 1-左上方,2-上方,3-左方
	for i in range(1, m + 1):
		for j in range(1, n + 1):
			if x[i - 1] == y[j - 1]:	# i j位置上的字符匹配的时候,来自于左上方
				c[i][j] = c[i - 1][j - 1] + 1
				b[i][j] = 1
			elif:c[i - 1][j] >= c[i][j - 1]:		# 来自上方
				c[i][j] = c[i - 1][j]
				b[i][j] = 2
			else:
				c[i][j] = c[i][j - 1]
				b[i][j] = 3
	return c[m][n], b

def lcs_trackback(x, y):
	c, b = lcs(x, y)
	i = len(x)
	j = len(y)
	res - []
	while i > 0 and j > 0:
		if b[i][j] == 1:	# 来自左上方->匹配
			res.append(x[i - 1])
			i -= 1
			j -= 1
		elif b[i][j] == 2:	# 来自于上方->不匹配
			i -= 1
		else:		# =3,来自于左方->不匹配
			j -= 1
	return "".join(reversed(res))

print(loc_trackback("ABCBDAB", "BDCABA"))

欧几里得算法

最大公约数——欧几里得算法
def gcd(a, b):
	if b == 0:
		return a
	else:
		return gcd(b, a % b)

def gcd2(a, b):
	while b > 0:
		r = a % b
		a = b
		b = r
	return a

分数的四则运算

class Fraction:
	def __init__(self, a, b):
		self.a = a
		self.b = b
		x = self.gcd(a, b)
		self.a /= x
		self.b /= x
	
	def gcd(self, a, b):
		while b > 0:
			r = a % b
			a = b
			b = r
		return a
	
	def zgs(self, a, b):
		x = self.gcd(a, b)
		return (a / x) * b

	def __add__(self, other):
		a = self.a
		b = self.b
		c = other.a
		d = other.b
		fenmu = self.zgs(b, d)
		fenzi = a * fenmu / b + c * fenmu / d
		return Fraction(fenzi, fenmu)

	def __str__(self):
		return "%d/%d" % (self.a, self.b)
	

RSA加密算法
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

设计模式

设计模式:对软件设计中普遍存在(反复出现)的各种问题,所提出的解决方案。每个设计模式系统的命名、解释和评价了面向对象系统中一个重要的和重复出现的设计

接口

接口:若干抽象方法的集合
作用:限制实现接口的类必须按照接口给定的调用方式实现这些方法;对高层模板隐藏了类的内部是实现

面向对象设计SLOLID原则


在这里插入图片描述

设计模式分类

在这里插入图片描述
在这里插入图片描述

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

北屿807

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值