13 Python数据结构与算法
文章目录
- 1 数据结构
- 1.1 栈
- 1.2 队列
- 1.3 单链表
- 2 排序
- 2.1 冒泡排序
- 2.2 选择排序
- 2.3 插入排序
- 2.4 桶排序
- 2.5 快速排序
- 2.6 归并排序
- 3 二分查找
- 4 二叉树
鉴于C++章节已做详细说明,本节只做记录,不做详细讲解。
1 数据结构
1.1 栈
import timeclass Stack:def __init__(self):self.items = []def __len__(self):return len(self.items)def __str__(self):if self.empty():return '栈为空'return '栈顶-> ' + ', '.join(map(str, self.items[::-1]))def empty(self):"""判断栈是否为空:return: 空返回True,否则返回False"""return not self.itemsdef push(self, data):"""入栈"""self.items.append(data)def pop(self):"""出栈"""if self.empty():raise IndexError('栈为空,不能执行 pop 操作!')return self.items.pop()def top(self):"""返回栈顶元素"""if self.empty():raise IndexError('栈为空,不能执行 top 操作!')return self.items[-1]if __name__ == '__main__':st = Stack()while True:print('1、入栈')print('2、出栈')print('3、取栈顶')print('4、判断是否为空')print('5、返回栈的大小')print('6、遍历栈')print('0、退出程序')op = input('请输入操作:').strip()match op:case '1': # 入栈try:x = input('请输入要入栈的元素:').strip()st.push(x)except Exception as e:print(f'错误:{e}')else:print(f'入栈成功,当前栈为:{st}')case '2': # 出栈try:x = st.pop()except Exception as e:print(f'错误:{e}')else:print(f'元素{x}出栈成功,当前栈为:{st}')case '3': # 取栈顶try:x = st.top()except Exception as e:print(f'错误:{e}')else:print(f'栈顶元素为:{x}')case '4': # 判断是否为空if st.empty():print('当前栈为空!')else:print('当前栈非空!')case '5': # 返回栈的大小print(f'栈中有{len(st)}个元素')case '6': # 遍历栈print(f'当前栈为:{st}')case '0': # 退出程序con = input('确认要退出吗?(y/n)')if con.upper() == 'Y':breakcase _:print('该操作不存在!')time.sleep(1)print()
1.2 队列
import timeclass Queue:def __init__(self):self.items = []def __len__(self):return len(self.items)def __str__(self):if self.empty():return '队列为空'return '队首-> ' + '->'.join(map(str, self.items)) + ' <-队尾'def empty(self):"""判断队列是否为空:return: 空返回True,否则返回False"""return not self.itemsdef push(self, data):"""入队"""self.items.append(data)def pop(self):"""出队"""if self.empty():raise IndexError('队列为空,不能执行 pop 操作!')return self.items.pop(0)def front(self):"""返回队首元素"""if self.empty():raise IndexError('队列为空,不能执行 front 操作!')return self.items[0]def rear(self):"""返回队尾元素"""if self.empty():raise IndexError('队列为空,不能执行 rear 操作!')return self.items[-1]if __name__ == '__main__':q = Queue()while True:print('1、入队列')print('2、出队列')print('3、取队首')print('4、取队尾')print('5、判断是否为空')print('6、返回队列的大小')print('7、遍历队列')print('0、退出程序')op = input('请输入操作:').strip()match op:case '1': # 入队try:x = input('请输入要入队列的元素:').strip()q.push(x)except Exception as e:print(f'错误:{e}')else:print(f'入队成功,当前队列为:{q}')case '2': # 出队try:x = q.pop()except Exception as e:print(f'错误:{e}')else:print(f'元素{x}出队成功,当前队列为:{q}')case '3': # 取队首try:x = q.front()except Exception as e:print(f'错误:{e}')else:print(f'队首元素为:{x}')case '4': # 取队尾try:x = q.rear()except Exception as e:print(f'错误:{e}')else:print(f'队尾元素为:{x}')case '5': # 判断是否为空if q.empty():print('当前队列为空!')else:print('当前队列非空!')case '6': # 返回队列的大小print(f'队列中有{len(q)}个元素')case '7': # 遍历队列print(f'当前队列为:{q}')case '0': # 退出程序con = input('确认要退出吗?(y/n)')if con.upper() == 'Y':breakcase _:print('该操作不存在!')time.sleep(1)print()
1.3 单链表
import timeclass Node(object):def __init__(self, data):self.data = dataself.next = Noneclass LinkedList(object):def __init__(self):self.head = Noneself.tail = Noneself.size = 0def __len__(self):return self.sizedef __str__(self):elem = [data for data in self] # 借助 迭代器 使用 列表推导式return '>'.join(map(str, elem)) + '->None' if elem else '空链表'def __iter__(self):current = self.headwhile current:yield current.datacurrent = current.nextdef append(self, data):"""链表尾部添加元素"""new_node = Node(data)if self.head: # 有头结点self.tail.next = new_nodeself.tail = new_nodeelse: # 无头结点,空链表self.head = new_nodeself.tail = new_node# 更新链表长度self.size += 1def prepend(self, data):"""链表头部添加元素"""new_node = Node(data)if self.head: # 有头结点new_node.next = self.headself.head = new_nodeelse: # 无头结点,空链表self.head = new_nodeself.tail = new_node# 更新链表长度self.size += 1def insert(self, data, pos):'''向链表指定下标插入元素:param data: 要插入的元素值:param pos: 要插入的下标,从0开始:return: 无'''# 处理越界问题if pos > len(self) or pos < 0:raise IndexError('索引超出界限!')new_node = Node(data)# 处理插入头结点和空链表插入的特殊情况if pos == 0:self.prepend(data)# 其他情况else:current = self.head# 遍历到要插入位置的前一个节点for idx in range(pos - 1):current = current.nextnew_node.next = current.nextcurrent.next = new_node# 如果插入到了尾节点,维护tailif new_node.next is None:self.tail = new_node# 更新链表长度self.size += 1def remove(self, data, num=1):"""删除链表中值为data的指定数量的节点:param data: 要删除的值:param num: 要删除的数量,1为默认,0表示删除所有:return: 返回删除成功的数目"""# 1、处理链表为空的情况if self.size == 0:raise ValueError('当前链表为空,无法删除!')# 已删节点数cnt = 0# 2、处理头结点为目标值的情况# 有头结点 且 头结点数据=要删除的数据 且 (要删除所有 或 已删除数目<要删除数目)while self.head and self.head.data == data and (num == 0 or cnt < num):self.head = self.head.nextcnt += 1# 更新链表长度self.size -= 1# 3、链表被删除为空,更新尾节点if self.head is None:self.tail = None# 4、处理后续节点为目标值的情况current = self.head# 当前节点非空 且 有下一个节点 且 (要删除所有 或 已删除数目<要删除数目)while current and current.next and (num == 0 or cnt < num):if current.next.data == data:# 删除下一个节点,导致当前节点的下一个节点已经改变current.next = current.next.nextcnt += 1# 更新链表长度self.size -= 1# 如果删除了尾结点,需要更新当前节点为尾结点if current.next is None:self.tail = current# 只有没删除节点时才移动else:current = current.nextif cnt == 0:raise ValueError(f'未找到值为{data}的节点!')return cntdef replace(self, data, new_data, num=1):"""修改并替换链表中的指定值为新值:param data: 要修改的值:param new_data: 修改后的新值:param num: 修改num个,1为默认,0表示替换所有:return: 返回修改成功的数目"""# 处理链表为空的情况if self.size == 0:raise ValueError('当前链表为空,无法修改!')cnt = 0current = self.head# 当前节点非空 且 (要替换所有 或 已替换数目<要删除数目)while current and (num == 0 or cnt < num):if current.data == data:current.data = new_datacnt += 1current = current.nextif cnt == 0:raise ValueError(f'未找到值为{data}的节点!')return cntdef find(self, data, all=False):"""查找链表指定元素的位置:param data: 要查找的元素:param all: 是否要查询所有符合条件的下标,True表示查询所有,False只查询一个,默认为False:return: 返回下标位置,没找到抛出ValueError错误"""idx = 0idxes = []current = self.headwhile current:if current.data == data:# 查找全部符合条件的下标if all:idxes.append(idx)# 只查一个下标else:return idxcurrent = current.nextidx += 1if idxes:return idxesraise ValueError(f'未找到值为{data}的节点!')def is_empty(self):"""判断链表是否为空:return: 为空返回True,否则返回False"""return self.head is Nonedef clear(self):"""清空当前链表"""self.head = Noneself.tail = Noneself.size = 0if __name__ == '__main__':ls = LinkedList()while True:print('1、链表尾部插入元素')print('2、链表头部插入元素')print('3、指定下标插入元素')print('4、删除链表元素')print('5、修改链表元素')print('6、查找链表元素')print('7、显示链表长度')print('8、显示所有元素')print('9、查询链表是否为空')print('10、清空链表')print('0、退出程序')op = input('请输入要执行的操作:').strip()match op:case '1': # 链表尾部插入元素try:x = input('请输入要插入的元素值:')ls.append(x)except Exception as e:print(f'错误:{e}')else:print(f'插入成功,当前链表为:{ls}')case '2': # 链表头部插入元素try:x = input('请输入要插入的元素值:')ls.prepend(x)except Exception as e:print(f'错误:{e}')else:print(f'插入成功,当前链表为:{ls}')case '3': # 指定下标插入元素try:x, pos = input('请输入要插入的元素值和下标(从0开始):').strip().split()ls.insert(x, int(pos))except Exception as e:print(f'错误:{e}')else:print(f'插入成功,当前链表为:{ls}')case '4': # 删除链表元素try:elems = input('请输入要删除的元素值 [可选:删除个数]:').strip().split()if len(elems) == 2:n = ls.remove(elems[0], int(elems[1]))else:n = ls.remove(elems[0])except Exception as e:print(f'错误:{e}')else:print(f'成功删除{n}个值为{elems[0]}的节点,当前链表为:{ls}')case '5': # 修改链表元素try:elems = input('请输入要替换的元素值和替换后的新值 [可选:替换个数]:').strip().split()if len(elems) == 3:n = ls.replace(elems[0], elems[1], int(elems[2]))else:n = ls.replace(elems[0], elems[1])except Exception as e:print(f'错误:{e}')else:print(f'成功将{n}个值为{elems[0]}的节点替换为{elems[1]},当前链表为:{ls}')case '6': # 查找链表元素elems = input('请输入要查找的元素 [可选:是否查询所有]:').strip().split()try:if len(elems) == 2:i = ls.find(elems[0], True)else:i = ls.find(elems[0])except Exception as e:print(f'错误:{e}')else:print(f'查找成功!该元素下标为{i}')case '7': # 显示链表长度print(f'链表长度为:{len(ls)}')case '8': # 显示所有元素print(f'当前链表为:{ls}')case '9': # 查询链表是否为空if ls.is_empty():print('当前链表为空!')else:print('当前链表非空!')case '10': # 清空链表try:ls.clear()except Exception as e:print(f'错误:{e}')else:print('链表清空成功!')case '0': # 退出程序con = input('确认要退出吗?(y/n)')if con.upper() == 'Y':breakcase _:print('该操作不存在!')time.sleep(1)print()
2 排序
2.1 冒泡排序
def bubble_sort(n): for i in range(n): swaped = False for j in range(n - i - 1): if ls[j] > ls[j + 1]: ls[j], ls[j + 1] = ls[j + 1], ls[j] swaped = True if not swaped: break
2.2 选择排序
def select_sort(n): for i in range(n - 1): idx = i for j in range(i + 1, n): if ls[j] < ls[idx]: idx = j ls[idx], ls[i] = ls[i], ls[idx]
2.3 插入排序
def insert_sort(n): for i in range(1, n): x = ls[i] j = i - 1 while j >= 0 and x < ls[j]: ls[j + 1] = ls[j] j -= 1 ls[j + 1] = x
2.4 桶排序
def bucket_sort(n): bucket = [0 for i in range(1000000)] for i in ls: bucket[i] += 1 ls.clear() for i in range(1000000): if bucket[i] != 0: for j in range(bucket[i]): ls.append(i)
2.5 快速排序
def quick_sort(l, r): if l >= r: return i = l - 1 j = r + 1 pivot = ls[l + r >> 1] while i < j: i += 1 while ls[i] < pivot: i += 1 j -= 1 while ls[j] > pivot: j -= 1 if i < j: ls[i], ls[j] = ls[j], ls[i] quick_sort(l, j) quick_sort(j + 1, r)
2.6 归并排序
def merge_sort(l, r): if l >= r: return mid = l + r >> 1 merge_sort(l, mid) merge_sort(mid + 1, r) k = l i = l j = mid + 1 tmp = [0 for i in range(n)] while i <= mid and j <= r: if ls[i] <= ls[j]: tmp[k] = ls[i] k += 1 i += 1 else: tmp[k] = ls[j] k += 1 j += 1 while i <= mid: tmp[k] = ls[i] k += 1 i += 1 while j <= r: tmp[k] = ls[j] k += 1 j += 1 for i in range(l, r + 1): ls[i] = tmp[i]
3 二分查找
ls = [1, 1, 3, 5, 7, 8, 9, 9, 9, 9, 10, 11, 11, 13, 13, 15, 17, 19]
print(ls)
x = int(input('要查找的元素:')) l, r = -1, len(ls)
while l + 1 < r: mid = l + r >> 1 if ls[mid] < x: l = mid else: r = mid print(f'第一次出现{x}的下标为{r}')
4 二叉树
import time
from collections import deque# 闭包捕获异常
def catch_exception(func):def wrapper(*args, **kwargs):try:return func(*args, **kwargs)except Exception as e:print(f'错误:{e}')return wrapperclass Node(object):def __init__(self, val):self.val = valself.left = Noneself.right = Nonedef __str__(self):return f'Node({self.val})'def __repr__(self):return f"Node(val={self.val}, left={self.left.val if self.left else None}, right={self.right.val if self.right else None})"class BinaryTree(object):def __init__(self):self.root = Noneself.size = 0def __len__(self):return self.size@catch_exceptiondef add(self, data):"""向二叉树插入新元素,以逐层遍历的方式找到第一个出现的叶子结点插入只用此方法构建二叉树,会构建出完全二叉树:param data: 要插入的值:return: 无"""new_node = Node(data)# 空树特殊处理if self.root is None:self.root = new_nodeself.size = 1return True# 用队列实现逐层遍历,先将根节点入队queue = deque([self.root]) # 借助双端队列实现while True:# 取出队首元素node = queue.popleft() # O(1)时间完成# 左子树非空,则将左子树入队等待搜索if node.left:queue.append(node.left)# 左子树为空,将新节点放入该位置,返回else:node.left = new_nodeself.size += 1return True# 右子树非空,则将右子树入队等待搜索if node.right:queue.append(node.right)# 右子树为空,则将新节点放入该位置,返回else:node.right = new_nodeself.size += 1return True@catch_exceptiondef bfs(self):"""广度优先搜索,逐层遍历"""if self.is_empty():raise ValueError('当前树为空!')queue = deque([self.root])res = []while len(queue) != 0:# 取出当前节点node = queue.popleft()res.append(node.val)# 有左子树,将左子树加入队列,等待访问if node.left:queue.append(node.left)# 有右子树,将右子树加入队列,等待访问if node.right:queue.append(node.right)return res@catch_exceptiondef preorder(self):"""包装方法,处理初始调用"""if self.is_empty():raise ValueError('当前树为空!')# 存放遍历结果res = []self.__preorder(self.root, res)return resdef __preorder(self, root, res):"""私有方法,执行递归逻辑。先序遍历,根左右顺序"""# 遍历结果放入列表res.append(root.val)# 有左子树访问左子树if root.left:self.__preorder(root.left, res)# 有右子树访问右子树if root.right:self.__preorder(root.right, res)@catch_exceptiondef inorder(self):"""包装方法,处理初始调用"""if self.is_empty():raise ValueError('当前树为空!')# 存放遍历结果res = []self.__inorder(self.root, res)return resdef __inorder(self, root, res):"""中序遍历,左根右顺序"""# 有左子树访问左子树if root.left:self.__inorder(root.left, res)# 输出当前节点的值res.append(root.val)# 有右子树访问右子树if root.right:self.__inorder(root.right, res)@catch_exceptiondef postorder(self):"""包装方法,处理初始调用"""if self.is_empty():raise ValueError('当前树为空!')# 存放遍历结果res = []self.__postorder(self.root, res)return resdef __postorder(self, root, res):# 有左子树访问左子树if root.left:self.__postorder(root.left, res)# 有右子树访问右子树if root.right:self.__postorder(root.right, res)# 输出当前节点的值res.append(root.val)@catch_exceptiondef find(self, data):"""在二叉树查找指定元素:param data: 待查找的元素:return: 返回该元素所在节点"""if self.is_empty():raise ValueError('当前树为空!')queue = deque([self.root])while len(queue) != 0:node = queue.popleft()# 查找到目标元素,返回该节点if node.val == data:return node# 有左子树,将左子树加入搜索队列if node.left:queue.append(node.left)# 有右子树,将右子树加入搜索队列if node.right:queue.append(node.right)raise ValueError('找不到该元素!')def clear(self):self.root = Noneself.size = 0def is_empty(self):return self.size == 0if __name__ == '__main__':bt = BinaryTree()bt.add('10')bt.add('12')bt.add('15')bt.add('20')bt.add('30')bt.add('45')bt.add('65')while True:print('1、二叉树添加节点')print('2、逐层遍历')print('3、先序遍历')print('4、中序遍历')print('5、后序遍历')print('6、查找元素')print('7、返回节点数')print('8、树是否为空')print('9、清空树')print('0、退出程序')op = input('请输入要执行的操作:').strip()match op:case '1': # 二叉树添加节点x = input('请输入要插入的元素值:').strip()flag = bt.add(x)if flag:print('插入成功!')case '2': # 逐层遍历ls = bt.bfs()if ls:print(f'逐层遍历的结果为:{"->".join(map(str, ls))}')case '3': # 先序遍历ls = bt.preorder()if ls:print(f'先序遍历的结果为:{"->".join(map(str, ls))}')case '4': # 中序遍历ls = bt.inorder()if ls:print(f'中序遍历的结果为:{"->".join(map(str, ls))}')case '5': # 后序遍历ls = bt.postorder()if ls:print(f'后序遍历的结果为:{"->".join(map(str, ls))}')case '6': # 查找元素x = input('请输入要查找的元素值:').strip()addr = bt.find(x)if addr:print(f'查找成功,该元素所在节点为:{addr}')print(f'左孩子值为:{addr.left.val if addr.left else None} 右孩子值为:{addr.right.val if addr.right else None}')case '7': # 返回节点数print(f'当前节点数:{len(bt)}')case '8': # 树是否为空if bt.is_empty():print('当前树为空')else:print('当前树非空')case '9': # 清空树bt.clear()print('当前树已清空!')case '0': # 退出程序con = input('确认要退出吗?(y/n)')if con.lower() == 'y':breakcase _:print('该操作不存在!')time.sleep(1)print()