1 查找和排序
1.1 查找
1.1.1 顺序查找算法及算法分析
1.1.2 二分查找算法及算法分析
def search(num, num_list):
# 最小规模
if len(num_list) == 1:
return num_list[0] == num
middle = len(num_list)//2
if num_list[middle] == num:
return True
# 减小规模
elif num_list[middle] > num:
return search(num, num_list[:middle])
else:
return search(num, num_list[middle+1:])
if __name__ == '__main__':
print(search(44, [17, 20, 26, 31, 35, 44, 54, 55, 65]))
1.2 排序
1.2.1 冒泡排序
# 时间复杂度: O(n^2)
# 空间复杂度: O(n)
def bubble_sort(num_list):
length = len(num_list)
for i in range(length):
# 经过一次遍历没有发生交换说明已经排序完成,不需要继续遍历
sort_flag = False
for j in range(i+1, length):
if num_list[i] > num_list[j]:
sort_flag = True
num_list[i], num_list[j] = num_list[j], num_list[i]
if sort_flag is False:
break
return num_list
if __name__ == '__main__':
print(bubble_sort([54, 68, 18, 2, 5, 36, 99, 41, 25]))
1.2.2 选择排序
选择排序相对于冒泡排序只是减少了交换的次数。
def select_sort(num_list):
"""
时间复杂度:O(n^2)
空间复杂度:O(n)
算法流程:
① 外层循环一次,内存循环遍历[0, n]找到最小的元素与当前元素交换位置,即将最小值排在了开头;
② 第二次循环只需要遍历[1, n]找到剩余元素中最小的元素与当前元素交换位置;
③ 依次类推...
:param num_list: 要排序的列表
:return: 返回排序好的列表
"""
length = len(num_list)
for i in range(length):
# 找到 num_list[i+1:] 中最小的下标,与当前num_list[i]交换
min_index = i
for j in range(i+1, length):
if num_list[min_index] > num_list[j]:
min_index = j
num_list[i], num_list[min_index] = num_list[min_index], num_list[i]
return num_list
if __name__ == '__main__':
print(select_sort([54, 68, 18, 2, 5, 36, 99, 41, 25]))
1.2.3 插入排序
def insert_sort(num_list):
"""
时间复杂度:O(n^2)
空间复杂度:O(n)
算法流程:
① 遍历 1-n个元素;
② 遍历到k时,记录current_value = num_list[k],并向前比较如果
num_list[k-1] > num_list[k],则将num_list[k] = num_list[k-1];
③ 循环退出时,即找到了某个位置满足:num[index-1] < num[index] < num[index+1],
将记录current_value插入到num[index]
:param num_list: 要排序的列表
:return: 返回排序好的列表
"""
length = len(num_list)
for i in range(1, length):
# 1. 记录当前值
current_value = num_list[i]
index = i
# 2. 从num_list[index]开始倒序遍历,如果当前遍历的值大于current_value,
# 则向后移动一位
while index > 0 and num_list[index-1] > current_value:
num_list[index] = num_list[index-1]
index -= 1
# 3. 循环退出时,即找到了num[index-1] < num[index] < num[index+1]的位置,
# 将current_value插入
num_list[index] = current_value
return num_list
if __name__ == '__main__':
print(insert_sort([54, 68, 18, 2, 5, 36, 99, 41, 25]))
1.2.4 谢尔排序
def shell_sort(num_list):
"""
时间复杂度: O(n^1.5)
空间复杂度: O(n)
① 设置间隔从len(num_list) // 2开始;每次缩小间隔 sublist_gap //= 2
② 核心部分使用插入排序,对每个子列表进行插入排序
:param num_list:
:return:
"""
sublist_gap = len(num_list) // 2
while sublist_gap > 0:
for start in range(sublist_gap):
# 对子列表进行插入排序
gap_insert_sort(num_list, start, sublist_gap)
sublist_gap //= 2
return num_list
def gap_insert_sort(num_list, start, gap):
length = len(num_list)
for i in range(start+gap, length, gap):
current_value = num_list[i]
index = i
while index > 0 and num_list[index-1] > current_value:
num_list[index] = num_list[index-1]
index -= 1
num_list[index] = current_value
return num_list
if __name__ == '__main__':
print(shell_sort([54, 68, 18, 2, 5, 36, 99, 41, 25]))
1.2.5 归并排序
def merge_sort(num_list):
"""
1. 将num_list持续分解为left和right两部分,直到分解为1个元素的列表
2. 对比 left和right 两个子列表的0元素,将较小的加入到merged列表中,并从原列表弹出。
3. 最终left或right有剩余元素的直接extend到merged列表。
:param num_list:
:return:
"""
# 递归结束条件
if len(num_list) <= 1:
return num_list
# 分解
middle = len(num_list) // 2
left = merge_sort(num_list[:middle])
right = merge_sort(num_list[middle:])
# 合并
merged = []
while left and right:
if left[0] <= right[0]:
merged.append(left.pop(0))
else:
merged.append(right.pop(0))
merged.extend(right if right else left)
return merged
if __name__ == '__main__':
print(merge_sort([54, 68, 18]))
1.2.6 快速排序
def quick_sort(num_list, first, last):
# 递归结束条件
if first < last:
split_point = partition(num_list, first, last)
# 分裂点左右递归调用分裂
quick_sort(num_list, first, split_point-1)
quick_sort(num_list, split_point+1, last)
return num_list
def partition(num_list, first, last):
middle_value = num_list[first]
left_mark = first + 1
right_mark = last
while True:
# 左标向右移动
while left_mark <= right_mark and num_list[left_mark] <= middle_value:
left_mark += 1
# 右标向左移动
while right_mark >= left_mark and num_list[right_mark] >= middle_value:
right_mark -= 1
# 两标相交时停止
if right_mark < left_mark:
break
# 左右标指定的下标值兑换,即把小的值移到左边,大的值移到右边
else:
num_list[left_mark], num_list[right_mark] = num_list[right_mark], num_list[left_mark]
# 将中值与right_mark下标的值进行交换
num_list[first], num_list[right_mark] = num_list[right_mark], num_list[first]
# 中值点就是分裂点
return right_mark
if __name__ == '__main__':
sort_list = [54, 68, 18, 2, 5, 36, 99, 41, 25]
print(quick_sort(sort_list, 0, len(sort_list)-1))
2 散列Hashing
散列表又称为哈希表,是一种数据集,其中数据项的存储方式有利于将来快速的查找定位。
散列表中的每一个存储位置,称为槽(slot),可以用来保存数据项,每个槽有唯一的名称。
负载因子:
散列冲突:
2.1 完美散列函数
2.1.1 完美散列函数的用途
2.1.2 散列函数MD5/SHA
2.2 散列函数设计
2.2.1 折叠法
2.2.2 平方取中法
2.2.3 非数项
2.3 散列冲突的解决方案
2.3.1 线性探测 Linear Probing
线性探测的缺点:聚集。
2.3.2 数据项链Chaining
3 树
树是一种非线性的数据结构,在操作系统、数据库系统、计算机网络等领域中都有广泛应用。
3.1 树结构相关术语
3.1.1 树的定义1
3.1.2 树的定义2(递归定义)
3.2 树的嵌套列表实现
def binary_tree(root_node):
return [root_node, [], []]
def insert_left(root, new_branch):
left = root.pop(1)
if len(left) > 1:
root.insert(1, [new_branch, left, []])
else:
root.insert(1, [new_branch, [], []])
def insert_right(root, new_branch):
right = root.pop(2)
if len(right) > 1:
root.insert(2, [new_branch, [], right])
else:
root.insert(2, [new_branch, [], []])
def get_root_value(root):
return root[0]
def set_root_value(root, new_value):
root[0] = new_value
def get_left_child(root):
return root[1]
def get_right_child(root):
return root[2]
if __name__ == '__main__':
tree = binary_tree(3)
insert_left(tree, 4)
insert_left(tree, 5)
insert_right(tree, 6)
insert_right(tree, 7)
left_child = get_left_child(tree)
print(left_child)
set_root_value(tree, 9)
print(tree)
insert_left(left_child, 11)
print(tree)
print(get_right_child(get_right_child(tree)))
3.3 树的链表实现
class BinaryTree(object):
def __init__(self, root):
self.root = root
self.left_child = None
self.right_child = None
def insert_left(self, new_node):
if self.left_child is None:
self.left_child = BinaryTree(new_node)
else:
tree = BinaryTree(new_node)
tree.left_child = self.left_child
self.left_child = tree
def insert_right(self, new_node):
if self.right_child is None:
self.right_child = BinaryTree(new_node)
else:
tree = BinaryTree(new_node)
tree.right_child = self.right_child
self.right_child = tree
def get_root_value(self):
return self.root
def set_root_value(self, new_value):
self.root = new_value
def get_left_child(self):
return self.left_child
def get_right_child(self):
return self.right_child
if __name__ == '__main__':
tree = BinaryTree("a")
tree.insert_left("b")
tree.insert_right("c")
tree.get_right_child().set_root_value("hello")
tree.get_left_child().insert_right("d")
3.4 树的应用:表达式解析树
表达式解析树建立规则:
from data_structure.Stack.stack import Stack
from data_structure.Tree.binary_tree_link import BinaryTree
def build_parse_tree(exp):
pstack = Stack()
tree = BinaryTree("")
pstack.push(tree)
current_tree = tree
for i in exp:
if i == "(":
# 左括号,创建左子节点,当前节点入栈并下降为左节点
current_tree.insert_left("")
pstack.push(current_tree)
current_tree = current_tree.get_left_child()
elif i not in ["+", "-", "*", "/", ")"]:
# 操作数,设置当前节点数据数,从栈中拿到父节点并上升为父节点
current_tree.set_root_value(int(i))
parent = pstack.pop()
current_tree = parent
elif i in ["+", "-", "*", "/"]:
# 操作符,设置当前节点数据符,当前节点入栈并下降为右节点
current_tree.set_root_value(i)
current_tree.insert_right("")
pstack.push(current_tree)
current_tree = current_tree.get_right_child()
# 右括号,从栈中拿到父节点并上升为父节点
elif i == ")":
current_tree = pstack.pop()
else:
raise ValueError
return tree
if __name__ == '__main__':
build_parse_tree("(3+(4*5))")
import operator
from data_structure.Stack.stack import Stack
from data_structure.Tree.binary_tree_link import BinaryTree
def build_parse_tree(exp):
pstack = Stack()
tree = BinaryTree("")
pstack.push(tree)
current_tree = tree
for i in exp:
if i == "(":
# 左括号,创建左子节点,当前节点入栈并下降为左节点
current_tree.insert_left("")
pstack.push(current_tree)
current_tree = current_tree.get_left_child()
elif i not in ["+", "-", "*", "/", ")"]:
# 操作数,设置当前节点数据数,从栈中拿到父节点并上升为父节点
current_tree.set_root_value(int(i))
parent = pstack.pop()
current_tree = parent
elif i in ["+", "-", "*", "/"]:
# 操作符,设置当前节点数据符,当前节点入栈并下降为右节点
current_tree.set_root_value(i)
current_tree.insert_right("")
pstack.push(current_tree)
current_tree = current_tree.get_right_child()
# 右括号,从栈中拿到父节点并上升为父节点
elif i == ")":
current_tree = pstack.pop()
else:
raise ValueError
return tree
def evaluate(parse_tree):
opers = {
"+": operator.add,
"-": operator.sub,
"*": operator.mul,
"/": operator.truediv,
}
# 缩小规模
left = parse_tree.get_left_child()
right = parse_tree.get_right_child()
if left and right:
fn = opers[parse_tree.get_root_value()]
# 递归调用自身
return fn(evaluate(left), evaluate(right))
else:
# 递归结束条件:没有左右子节点,说明是叶子节点直接返回值
return parse_tree.get_root_value()
if __name__ == '__main__':
parse_tree = build_parse_tree("(3+(4*5))")
print(evaluate(parse_tree))
3.5 树的遍历
前序遍历例子:
class BinaryTree(object):
def __init__(self, root):
self.root = root
self.left_child = None
self.right_child = None
def insert_left(self, new_node):
if self.left_child is None:
self.left_child = BinaryTree(new_node)
else:
t = BinaryTree(new_node)
t.left_child = self.left_child
self.left_child = t
def insert_right(self, new_node):
if self.right_child is None:
self.right_child = BinaryTree(new_node)
else:
t = BinaryTree(new_node)
t.right_child = self.right_child
self.right_child = t
def get_root_value(self):
return self.root
def set_root_value(self, new_value):
self.root = new_value
def get_left_child(self):
return self.left_child
def get_right_child(self):
return self.right_child
# 前序
def preorder(self):
print(self.root, end="-->")
if self.left_child:
self.left_child.preorder()
if self.right_child:
self.right_child.preorder()
# 后序
def postorder(self):
if self.left_child:
self.left_child.postorder()
if self.right_child:
self.right_child.postorder()
print(self.root, end="-->")
# 中序
def inorder(self):
if self.left_child:
self.left_child.inorder()
print(self.root, end="-->")
if self.right_child:
self.right_child.inorder()
if __name__ == '__main__':
tree = BinaryTree("a")
tree.insert_left("b")
tree.insert_right("c")
tree.get_right_child().set_root_value("hello")
tree.get_left_child().insert_right("d")
tree.preorder() # a-->b-->d-->hello-->
print()
tree.postorder() # d-->b-->hello-->a-->
print()
tree.inorder() # b-->d-->a-->hello-->
3.6 优先队列Priority Queue
二叉堆python实现:
添加节点:
删除节点:
无序表生成堆:
class BinaryHeap(object):
def __init__(self):
self.heap_list = [0]
self.current_size = 0
# 将新的key加入到堆中
def insert(self, k):
self.heap_list.append(k)
self.current_size += 1
# 新的key上浮
self.perc_up(self.current_size)
# 返回堆中的最小项
def find_min(self):
pass
# 删除堆中的最小项,并返回---即删除堆顶
def del_min(self):
ret_val = self.heap_list[1]
# 将最后一个节点移到堆顶
self.heap_list[1] = self.heap_list[self.current_size]
self.current_size -= 1
self.heap_list.pop()
self.perc_down(1)
return ret_val
# 返回堆是否为空
def is_empty(self):
return self.current_size == 0
# 返回堆中key的个数
def size(self):
return self.current_size
# 从一个key列表创建新堆
def build_heap(self, lst):
i = len(lst) // 2
self.current_size = len(lst)
self.heap_list = [0] + lst[:]
print(len(self.heap_list), i)
while i > 0:
print(self.heap_list, i)
self.perc_down(i)
i -= 1
print(self.heap_list, i)
def perc_up(self, i):
while i // 2 > 0:
# 如果父节点大于当前节点,则与父节点交换位置
if self.heap_list[i] < self.heap_list[i//2]:
self.heap_list[i // 2], self.heap_list[i] = \
self.heap_list[i], self.heap_list[i//2]
i //= 2
else:
break
def perc_down(self, i):
while i*2 <= self.current_size:
# 找到左右子节点中较小的子节点进行交换
mc = self.min_child(i)
if self.heap_list[i] > self.heap_list[mc]:
# 交换下沉
self.heap_list[i], self.heap_list[mc] = \
self.heap_list[mc], self.heap_list[i]
i = mc
else:
break
def min_child(self, i):
# 只有一个子节点
if i*2 + 1 > self.current_size:
return i*2
else:
# 选择较小的子节点
if self.heap_list[i*2] < self.heap_list[i*2+1]:
return i*2
else:
return i*2 + 1
3.7 二叉查找树
二叉查找树实现: