文章目录
- 排序算法和树
- 排序算法
- 算法稳定性
- 排序算法
- ☆ 冒泡排序
- 冒泡思路
- 冒泡步骤
- 代码实现
- 效率优化
- ☆ 选择排序
- 排序思路
- 排序步骤
- 代码实现
- ...
- 树
- 01-树的基本概念
- 02-树的相关术语
- 03-二叉树的种类
- 04-二叉树的存储
- 05-树的应用场景_数据库索引
- 06-二叉树的概念和性质
- 07-广度优先遍历
- 08-添加节点思路分析
- 09.遍历方法的实现
- 10-二叉树的三种深度优先遍历
- 11-二叉树的三种深度优先遍历代码实现
- 12-二叉树由遍历结果反推二叉树的结构
- 01_python代码模拟栈.py
- 结果
- 02_python代码模拟队列.py
- 结果
- 02_直接用queue模拟队列.py
- 结果
- 03_直接用deque模拟栈.py
- 结果
- 03_直接用deque模拟队列.py
- 结果
- 04_模拟链表.py
- 结果
- 05_冒泡排序.py
- 结果
- 05_冒泡排序_优化.py
- 结果
- 06_选择排序.py
- 结果
- 07_python代码模拟二叉树.py
- 结果
排序算法和树
排序算法
算法稳定性
所谓排序,使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作
排序算法,就是如何使得记录按照要求排列的方法
排序算法在很多领域是非常重要
在大量数据的处理方面:一个优秀的算法可以节省大量的资源。
在各个领域中考虑到数据的各种限制和规范,要得到一个符合实际的优秀算法,得经过大量的推理和分析
举个例子:
把以上数据进行升序排列:
☆ 假定在待排序的记录序列中,存在多个具有相同的关键字的记录
☆ 若经过排序,这些记录的相对次序保持不变,则称这种排序算法是稳定的, 否则称为不稳定的
记忆:具有相同关键字的纪录经过排序后,相对位置保持不变,这样的算法是稳定性算法
再来看一个例子:
升序排序后的结果
无序数据具有2个关键字,
先按照关键字1排序,
若关键字1值相同,再按照关键字2排序。
稳定性算法具有良好的作用。
排序算法
排序犹如一把将混乱变为秩序的魔法钥匙,使我们能以更高效的方式理解与处理数据。
无论是简单的升序,还是复杂的分类排列,排序都向我们展示了数据的和谐美感。
☆ 冒泡排序
冒泡思路
冒泡排序(bubble sort)通过连续地比较与交换相邻元素实现排序。这个过程就像气泡从底部升到顶部一样,因此得名冒泡排序。
冒泡过程可以利用元素交换操作来模拟:从数组最左端开始向右遍历,依次比较相邻元素大小,如果“左元素 > 右元素”就交换二者。遍历完成后,最大的元素会被移动到数组的最右端。
第一轮第一步排序:
第一轮第二步排序:
第一轮第三步排序:
第一轮第四步排序:
第一轮第五步排序:
第一轮最终排序结果:
重复以上步骤,直至完成最终排序。
冒泡步骤
设列表的长度为 n ,冒泡排序的步骤如上图所示。
- 首先,对 n 个元素执行“冒泡”,将列表的最大元素交换至正确位置。
- 接下来,对剩余 n−1 个元素执行“冒泡”,将第二大元素交换至正确位置。
- 以此类推,经过 n−1 轮“冒泡”后,前 n−1 大的元素都被交换至正确位置。
- 仅剩的一个元素必定是最小元素,无须排序,因此列表排序完成。
代码实现
def bubble_sort(nums: list[int]):
"""冒泡排序"""
n = len(nums)
# 外循环:未排序区间为 [0, i]
for i in range(n - 1, 0, -1):
# 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
for j in range(i):
if nums[j] > nums[j + 1]:
# 交换 nums[j] 与 nums[j + 1]
nums[j], nums[j + 1] = nums[j + 1], nums[j]
效率优化
我们发现,如果某轮“冒泡”中没有执行任何交换操作,说明数组已经完成排序,可直接返回结果。因此,可以增加一个标志位 flag
来监测这种情况,一旦出现就立即返回。
经过优化,冒泡排序的最差时间复杂度和平均时间复杂度仍为 O(n2) ;但当输入数组完全有序时,可达到最佳时间复杂度 O(n)
def bubble_sort_with_flag(nums: list[int]):
"""冒泡排序(标志优化)"""
n = len(nums)
# 外循环:未排序区间为 [0, i]
for i in range(n - 1, 0, -1):
flag = False # 初始化标志位
# 内循环:将未排序区间 [0, i] 中的最大元素交换至该区间的最右端
for j in range(i):
if nums[j] > nums[j + 1]:
# 交换 nums[j] 与 nums[j + 1]
nums[j], nums[j + 1] = nums[j + 1], nums[j]
flag = True # 记录交换元素
if not flag:
break # 此轮“冒泡”未交换任何元素,直接跳出
☆ 选择排序
选择排序(selection sort)的工作原理非常简单:开启一个循环,每轮从未排序区间选择最小的元素,将其放到已排序区间的末尾。
排序思路
设列表的长度为 n ,选择排序的算法流程如下图所示:
第一轮:
第一轮:
第二轮:
第二轮:
第三轮:
第三轮:
第四轮:
第四轮:
第五轮:
第五轮:
完成排序,最终结果:
排序步骤
- 初始状态下,所有元素未排序,即未排序(索引)区间为 [0,n−1] 。
- 选取区间 [0,n−1] 中的最小元素,将其与索引 0 处的元素交换。完成后,数组前 1 个元素已排序。
- 选取区间 [1,n−1] 中的最小元素,将其与索引 1 处的元素交换。完成后,数组前 2 个元素已排序。
- 以此类推。经过 n−1 轮选择与交换后,数组前 n−1 个元素已排序。
- 仅剩的一个元素必定是最大元素,无须排序,因此数组排序完成。
代码实现
def selection_sort(nums: list[int]):
"""选择排序"""
n = len(nums)
for i in range(n - 1):
# 内循环:找到未排序区间内的最小元素
k = i
for j in range(i + 1, n):
if nums[j] < nums[k]:
k = j # 记录最小元素的索引
# 仅当最小元素不在未排序区间的首个位置时,才进行交换
if k != i:
nums[i], nums[k] = nums[k], nums[i]
…
树
01-树的基本概念
树是一种一对多关系的数据结构,主要分为:
- 多叉树
- 每个结点有0、或者多个子节点
- 没有父节点的结点成为根节点
- 每一个非根节点有且只有一个父节点
- 除了根节点外,每个子节点可以分为多个互不相交的子树
- 二叉树
- 每个结点有0、1、2 个子节点
- 没有父节点的结点成为根节点
- 每一个非根节点有且只有一个父节点
- 除了根节点外,每个子节点可以分为多个互不相交的子树
02-树的相关术语
03-二叉树的种类
04-二叉树的存储
顺序存储、链式存储。树在存储的时候,要存储什么?
- 值
- 结点关系
如果树是完全二叉树、满二叉树,可以使用顺序存储。大多数构建出的树都不是完全、满二叉树,所以使用链式存储比较多。
class TreeNode:
def __init__(self):
self.item = value
self.parent = 父亲
self.lchild = 左边树
self.rchild = 右边树
对于树而言,只要拿到根节点,就相当于拿到整棵树。
.
完全二叉树适合顺序结构存储,但其插入删除元素效率较差。
大多数的二叉树都是使用链式结构存储。
05-树的应用场景_数据库索引
06-二叉树的概念和性质
07-广度优先遍历
class Node(object):
"""节点类"""
def __init__(self, item):
self.item = item
self.lchild = None
self.rchild = None
class BinaryTree(object):
"""二叉树"""
def __init__(self, node=None):
self.root = node
def add(self, item):
"""添加节点"""
pass
def bradh_travel(self):
"""广度优先遍历"""
pass
- 深度优先遍历:沿着某一个路径遍历到叶子结点,再从另外一个路径遍历,直到遍历完所有的结点
- 广度优先遍历:按照层次进行遍历
08-添加节点思路分析
- 初始操作:初始化队列、将根节点入队、创建新结点
- 重复执行:
- 获得并弹出队头元素
- 如果当前结点的左右子结点不为空,则将其左右子节点入队
- 如果当前结点的左右子节点为空,则将新结点挂到为空的左子结点、或者右子节点
- 获得并弹出队头元素
09.遍历方法的实现
class Node(object):
"""节点类"""
def __init__(self, item):
self.item = item
self.lchild = None
self.rchild = None
class BinaryTree(object):
"""完全二叉树"""
def __init__(self, node=None):
self.root = node
def add(self, item):
"""添加节点"""
# 初始操作:初始化队列
if self.root == None:
self.root = Node(item)
return
# 队列
queue = []
# 根节点入队
queue.append(self.root)
while True:
# 从头部取出数据
node = queue.pop(0)
# 判断左节点是否为空
if node.lchild == None:
node.lchild = Node(item)
return
else:
queue.append(node.lchild)
if node.rchild == None:
node.rchild = Node(item)
return
else:
queue.append(node.rchild)
if __name__ == '__main__':
tree = BinaryTree()
tree.add("A")
tree.add("B")
tree.add("C")
tree.add("D")
tree.add("E")
tree.add("F")
tree.add("G")
tree.add("H")
tree.add("I")
tree.breadh_travel()
10-二叉树的三种深度优先遍历
先序遍历:先访问根节点、再访问左子树、最后访问右子树
中序遍历:先访问左子树、再访问根节点、最后访问右子树
后序遍历:先访问左子树、再访问右子树、最后访问根节点
- 无论那种遍历方式,都是先访问左子树、再访问右子树
- 碰到根节点就输出、碰到左子树、右子树就递归 注意:左子树右子树是一棵树所以递归;根节点是一个节点所以打印输出
11-二叉树的三种深度优先遍历代码实现
class Node(object):
"""节点类"""
def __init__(self, item):
self.item = item
self.lchild = None
self.rchild = None
class BinaryTree(object):
"""完全二叉树"""
def __init__(self, node=None):
self.root = node
def add(self, item):
"""添加节点"""
if self.root == None:
self.root = Node(item)
return
# 队列
queue = []
# 从尾部添加数据
queue.append(self.root)
while True:
# 从头部取出数据
node = queue.pop(0)
# 判断左节点是否为空
if node.lchild == None:
node.lchild = Node(item)
return
else:
queue.append(node.lchild)
if node.rchild == None:
node.rchild = Node(item)
return
else:
queue.append(node.rchild)
def breadh_travel(self):
"""广度优先遍历"""
if self.root == None:
return
# 队列
queue = []
# 添加数据
queue.append(self.root)
while len(queue)>0:
# 取出数据
node = queue.pop(0)
print(node.item, end="")
# 判断左右子节点是否为空
if node.lchild is not None:
queue.append(node.lchild)
if node.rchild is not None:
queue.append(node.rchild)
def preorder_travel(self, root):
"""先序遍历 根 左 右"""
if root is not None:
# 先访问根节点
print(root.item, end="")
# 递归再访问左子树
self.preorder_travel(root.lchild)
# 递归访问右子树
self.preorder_travel(root.rchild)
def inorder_travel(self, root):
"""中序遍历 左 根 右"""
if root is not None:
self.inorder_travel(root.lchild)
print(root.item, end="")
self.inorder_travel(root.rchild)
def postorder_travel(self, root):
"""后序遍历 根 左 右"""
if root is not None:
self.postorder_travel(root.lchild)
self.postorder_travel(root.rchild)
print(root.item, end="")
if __name__ == '__main__':
tree = BinaryTree()
tree.add("0")
tree.add("1")
tree.add("2")
tree.add("3")
tree.add("4")
tree.add("5")
tree.add("6")
tree.add("7")
tree.add("8")
tree.add("9")
tree.preorder_travel(tree.root)
print()
tree.inorder_travel(tree.root)
print()
tree.postorder_travel(tree.root)
12-二叉树由遍历结果反推二叉树的结构
我们需要知道先序遍历结果和中序遍历结果、或者后序遍历结果和中序遍历结果才能够确定唯一一棵树。
只知道先序遍历、后序遍历结果,不能保证确定唯一一棵树。
通过先序遍历可以确定哪个元素是根节点,通过中序遍历可以知道左子树都有那些结点、右子树都有那些结点。
01_python代码模拟栈.py
# 栈特点: 先进后出 类似于:子弹夹
# 举例: 依次输入[A,B,C] ctrl+z撤销 撤销顺序CBA
# 注意: 自定义类名必须符合标识符命名规则,建议见名知意
# 1.先定义类
class My_Stack:
def __init__(self):
self.items = []
# 入栈/压栈
def push(self, item):
print("压栈:", item)
self.items.append(item)
# 出栈/弹栈
def pop(self):
print("弹栈:", self.items[-1])
# pop()不传索引,默认删除列表中最后一个
return self.items.pop()
# 判断栈是否为空
def is_empty(self):
return self.items == []
# 2.再根据类创建对象
s = My_Stack()
# 3.压栈
s.push('A')
s.push('B')
s.push('C')
s.push('D')
print(s.items)
print('-----------------------------')
# 4.弹栈
# 手动调用pop()方法,如果元素多,不仅麻烦还容易错
# s.pop()
# s.pop()
# s.pop()
# s.pop()
print('-----------------------------')
# 思路: 先判断是否为空,不为空就继续弹栈,为空就停止
while not s.is_empty():
s.pop()
结果
压栈: A
压栈: B
压栈: C
压栈: D
['A', 'B', 'C', 'D']
-----------------------------
-----------------------------
弹栈: D
弹栈: C
弹栈: B
弹栈: A
02_python代码模拟队列.py
# 对列特点: 先进先出 类似于:火车过山洞
# 注意: 自定义类名必须符合标识符命名规则,建议见名知意
# 1.先定义类
# 注意: python中默认有一个queue模块Queue类
import queue
class My_Queue():
def __init__(self):
self.items = []
# 入队列
def input(self, item):
print("进队列:", item)
self.items.append(item)
# 出队列
def output(self):
print("出队列:", self.items[0])
return self.items.pop(0)
# 判断队列是否为空
def is_empty(self):
return self.items == []
# 2.再根据类创建对象
s = My_Queue()
# 3.进队列
s.input('A')
s.input('B')
s.input('C')
s.input('D')
print(s.items)
print('-----------------------------')
# 4.出队列
# 手动调用output()方法,如果元素多,不仅麻烦还容易错
# s.output()
# s.output()
# s.output()
# s.output()
print('-----------------------------')
# 思路: 先判断是否为空,不为空就继续出,为空就停止
while not s.is_empty():
s.output()
结果
进队列: A
进队列: B
进队列: C
进队列: D
['A', 'B', 'C', 'D']
-----------------------------
-----------------------------
出队列: A
出队列: B
出队列: C
出队列: D
进程已结束,退出代码为 0
02_直接用queue模拟队列.py
# 对列特点: 先进先出 类似于:火车过山洞
# 注意: 自定义类名必须符合标识符命名规则,建议见名知意
# 1.先定义类
# 注意: python中默认有一个queue模块Queue类
from queue import Queue
# 2.再根据类创建对象
s = Queue()
# 3.进队列
s.put('A')
s.put('B')
s.put('C')
s.put('D')
print(s)
print('-----------------------------')
# 4.出队列
# 手动调用output()方法,如果元素多,不仅麻烦还容易错
print(s.get())
print(s.get())
print(s.get())
print(s.get())
print('-----------------------------')
# 思路: 先判断是否为空,不为空就继续出,为空就停止
while not s.empty():
print(s.get())
结果
<queue.Queue object at 0x0000022DCBF33220>
-----------------------------
A
B
C
D
-----------------------------
进程已结束,退出代码为 0
03_直接用deque模拟栈.py
# 栈特点: 先进后出 类似于:子弹夹
# 举例: 依次输入[A,B,C] ctrl+z撤销 撤销顺序CBA
# 注意: 自定义类名必须符合标识符命名规则,建议见名知意
# 1.导包
from collections import deque
# 2.创建队列
s = deque()
# 3.进队列
s.append('A')
s.append('B')
s.append('C')
s.append('D')
print(s)
# 4.出队列
print(s.pop())
print(s.pop())
print(s.pop())
print(s.pop())
结果
deque(['A', 'B', 'C', 'D'])
D
C
B
A
进程已结束,退出代码为 0
03_直接用deque模拟队列.py
# 对列特点: 先进先出 类似于:火车过山洞
# 注意: 自定义类名必须符合标识符命名规则,建议见名知意
# 1.导包
from collections import deque
# 2.创建队列
s = deque()
# 3.进队列
s.append('A')
s.append('B')
s.append('C')
s.append('D')
print(s)
# 4.出队列
print(s.popleft())
print(s.popleft())
print(s.popleft())
print(s.popleft())
结果
deque(['A', 'B', 'C', 'D'])
A
B
C
D
进程已结束,退出代码为 0
04_模拟链表.py
# 1.先定义类
# 定义节点类
class SingleNode():
def __init__(self, item):
# 当前节点数据
self.item = item
# 下一个节点(链接)
self.next = None
# 定义链表类
class SingleLinkList():
def __init__(self):
# __head默认为None, 表示链表为空,以后就是代表头节点
self.__head = None
# 链表是否为空
def is_empty(self):
return self.__head is None
# 链表长度
def length(self):
# 初始化计数器为0,用于后续统计链表节点的数量
count = 0
# 将当前节点指向链表的头部,作为遍历链表的起点
cur = self.__head
# 遍历链表,直到当前节点为空,表示已到达链表末尾
while cur is not None:
# 对每个遍历到的节点,计数器加1
count += 1
# 将当前节点移动到下一个节点,继续遍历
cur = cur.next
# 注意:代码只要走到这里,说明遍历结束,count就是最终个数
# 返回链表节点的总数
return count
# 遍历整个链表
def travel(self):
# 将当前节点指向链表的头部,作为遍历链表的起点
cur = self.__head
# 遍历链表,直到当前节点为空,表示已到达链表末尾
while cur is not None:
# 打印当前节点数据
print(cur.item, end=" ")
# 以下条件控制,类似while普通循环中i+=1
cur = cur.next
print()
# 链表头部添加元素 仅此功能无需遍历,时间复杂度O(1)
def add_head(self, item):
# 创建一个单链表节点实例
node = SingleNode(item)
# 将新节点的next指向当前的头节点,实现节点的插入
node.next = self.__head
# 更新链表的头节点为新创建的节点,完成头插法操作
self.__head = node
# 链表尾部添加元素
def append(self, item):
# 创建一个单链表节点实例
node = SingleNode(item)
if self.is_empty():
# 如果链表为空,新添加的既是尾节点又是头节点
# 则头节点指向新节点
self.__head = node
else:
# 如果链表不为空,则找到尾节点,将尾节点的next指向新节点
cur = self.__head
# 循环找到尾节点
while cur.next is not None:
cur = cur.next
# 最后把原来的尾节点的next指向新节点
cur.next = node
# 指定位置添加元素
def insert(self, pos, item):
# pos: 插入数据的索引位置
# item: 要插入的数据
if pos <= 0:
# 如果pos小于等于0,则执行头插法
self.add_head(item)
elif pos >= self.length():
# 如果pos大于链表长度,则执行尾插法
self.append(item)
else:
# 否则,执行在中间插入操作
node = SingleNode(item)
# pre代表当前节点的前一个节点
pre = self.__head
# 循环找到pos位置的前一个节点
# 初始化计数器为0,用于定位到链表中特定的位置
count = 0
# 循环直到计数器达到目标位置前一个位置
while count < pos - 1:
# 计数器递增,以遍历链表
count += 1
# 更新pre指针到下一个节点,为后续操作(如删除或插入)做准备
pre = pre.next
# 新节点先链接到下一节点
node.next = pre.next
# 最后把新节点链接到pre节点
pre.next = node
# 删除节点
def remove(self, item):
if self.is_empty():
# 如果链表为空,则直接返回
print("链表为空,无法删除")
return
else:
# 定义cur和pre两个变量,cur代表当前节点,pre代表前一个节点
cur = self.__head
pre = None
# 循环遍历找到要删除节点的前一个节点
while cur is not None:
if cur.item == item:
if cur == self.__head:
# 如果删除的是头节点,则直接更新头节点
self.__head = cur.next
else:
# 不是头节点,则更新pre下一个节点
pre.next = cur.next
break
else:
pre = cur
cur = cur.next
"""
# 如果链表不为空,则找到要删除节点的前一个节点
pre = self.__head
# 如果要删除的节点是头节点,则直接更新头节点
if pre.item == item:
self.__head = pre.next
else:
# 否则,循环找到要删除节点的前一个节点
# 注意: pre.next就是当前节点
while pre.next is not None:
# 如果找到了要删除节点的前一个节点,则更新pre指针
if pre.next.item == item:
# 删除节点(本质就是跳过)
# 前一个节点的next指向当前节点的下一个节点
pre.next = pre.next.next
break
else:
# 类似循环条件控制i+=1
pre = pre.next
"""
# 查找节点是否存在
def search(self, item):
# 初始化当前节点为链表的头节点
cur = self.__head
# 遍历链表直到当前节点为空
while cur is not None:
# 如果当前节点的数据与待查找的项匹配,则返回True
if cur.item == item:
return True
# 类似循环条件控制i+=1
cur = cur.next
# 如果遍历完整个链表都没有找到匹配的项,则返回False
return False
if __name__ == '__main__':
# 2.再根据类创建对象
# 创建链表对象
ls = SingleLinkList()
# 1.测试is_empty()功能
print(ls.is_empty())
# 2.测试length()功能
print(ls.length())
# 3.测试travel()功能
ls.travel()
print('------------------------')
# 4.测试add_head()功能
ls.add_head(100)
ls.add_head(78)
ls.add_head(98)
ls.travel()
print('------------------------')
# 5.测试append()功能
ls.append(1)
ls.travel()
print('------------------------')
# 6.测试insert()功能
ls.insert(1, 'abc')
ls.travel()
print('------------------------')
# 7.测试remove()功能
ls.remove(78)
ls.travel()
print('------------------------')
# 8.测试search()功能
print(ls.search(99))
结果
D:\software\python38\install\python.exe D:\黑马Pyspark-v1.8\扩展\数据结构和算法入门\笔记\day07\代码\day7_project\04_模拟链表.py
True
0
------------------------
98 78 100
------------------------
98 78 100 1
------------------------
98 abc 78 100 1
------------------------
98 abc 100 1
------------------------
False
进程已结束,退出代码为 0
05_冒泡排序.py
# 冒泡排序核心思想: 循环比较相邻的两个元素,如果前一个元素大于后一个元素,则交换位置,直到循环结束。
"""
分析: 外层循环控制比几轮,内层循环控制每轮比较次数
假设原始数据: 53472
一共有5个数据
第1轮 比4次
第2轮 比3次
第3轮 比2次
第4轮 比1次
"""
# 冒泡排序
def bubble_sort(list):
# 获取长度
n = len(list)
# 外层控制轮数
for i in range(1, n):
# 内层循环控制比较次数 注意: 内层循环同时作为索引使用 必须从0开始
for j in range(0, n - i): # 0 1 2 3
# 如果前一个元素大于后一个元素,则交换位置,直到循环结束!!!
# 如果大于才交换,两个数相同会交换吗? 不会
if list[j] > list[j + 1]:
# 利用元组拆包
list[j], list[j + 1] = list[j + 1], list[j]
# 最终返回结果
return list
# 调用函数
new_list = bubble_sort([5, 3, 4, 7, 2])
print(new_list)
new_list = bubble_sort([2, 3, 4, 5, 7])
print(new_list)
结果
[2, 3, 4, 5, 7]
[2, 3, 4, 5, 7]
进程已结束,退出代码为 0
05_冒泡排序_优化.py
# 冒泡排序核心思想: 循环比较相邻的两个元素,如果前一个元素大于后一个元素,则交换位置,直到循环结束。
"""
分析: 外层循环控制比几轮,内层循环控制每轮比较次数
假设原始数据: 53472
一共有5个数据
第1轮 比4次
第2轮 比3次
第3轮 比2次
第4轮 比1次
"""
# 冒泡排序
def bubble_sort(list):
# 获取长度
n = len(list)
# 定义计数器,用于记录比较次数
count = 0
# 外层控制轮数
for i in range(1, n):
# 用于记录是否交换过
# num = 0
flag = False
# 内层循环控制比较次数 注意: 内层循环同时作为索引使用 必须从0开始
for j in range(0, n - i): # 0 1 2 3
# 如果前一个元素大于后一个元素,则交换位置,直到循环结束!!!
# 如果大于才交换,两个数相同会交换吗? 不会
# 每交换一次计数器加1
count += 1
if list[j] > list[j + 1]:
# 利用元组拆包
list[j], list[j + 1] = list[j + 1], list[j]
# num += 1
flag = True
# 如果第一次循环没有交换过,则说明已经排好序,可以提前结束循环,无需后面的循环了
# if num == 0:
# break
if flag == False:
break
print(f"一共比了{count}次")
# 最终返回结果
return list
# 调用函数
new_list = bubble_sort([5, 3, 4, 7, 2])
print(new_list)
new_list = bubble_sort([2, 3, 4, 5, 7])
print(new_list)
结果
一共比了10次
[2, 3, 4, 5, 7]
一共比了4次
[2, 3, 4, 5, 7]
进程已结束,退出代码为 0
06_选择排序.py
# 核心思想: 拿着自己和后面的所有元素比较,找到最小的元素,放到起始位置,
# 然后继续寻找最小的元素, 将最小的元素放到数组的第二个位置, 依此类推...
"""
分析: 外层循环控制比几轮,内层循环控制每轮比较次数
假设原始数据: 53472
一共有5个数据
第1轮 比4次
第2轮 比3次
第3轮 比2次
第4轮 比1次
"""
# 定义函数
def select_fort(list):
# 获取列表长度
n = len(list)
# 外层循环控制轮数
for i in range(0, n - 1):
# 内层循环控制比较
# 每次循环把未经过排序的数据元素作为最小值
min_index = i
# 每次循环都和后面的元素比较
for j in range(i + 1, n):
# 如果比最小值小,则记录最小值的索引
if list[j] < list[min_index]:
min_index = j
# 如果循环结束i发生了变化,说明有更小的值,则交换位置
if min_index != i:
list[i], list[min_index] = list[min_index], list[i]
# 最终返回结果
return list
# 调用函数
new_list = select_fort([5, 3, 4, 7, 2])
print(new_list)
结果
[2, 3, 4, 5, 7]
进程已结束,退出代码为 0
07_python代码模拟二叉树.py
# 1.先定义类
# 定义节点类
class Node:
def __init__(self, item):
self.item = item
self.lchild = None
self.rchild = None
# 定义树类
class TwoXTree:
def __init__(self):
self.root = None
# 定义添加节点方法
# 注意: 只要添加成功就结束当前函数
def add(self, item):
# 1.先判断树的根节点是否为空
if self.root is None:
self.root = Node(item) # A
print(f'添加节点位置1,添加了{item}')
return
# 2.如果根节点存在了,后续需要再添加元素,需要判断左右
# 提前创建临时列表作为队列使用,以后从此队列中取出节点判断存放位置
queue = [self.root]
# 3.遍历队列,从队列中取出第一个元素,直到队列为空
# 注意: 边取边判断,同时再把最新节点放到队列中
while True:
# 取出队列中第一个元素(默认第一次是根节点,后面就是根节点的孩子们了...)
node = queue.pop(0)
# 如果当前节点左孩子为空,就把item所在节点添加到左孩子中,结束当前函数
if node.lchild is None:
node.lchild = Node(item) # B D F H J
print(f'添加节点位置2,添加了{item}')
return
# 如果当前节点右孩子为空,就把item所在节点添加到右孩子中,结束当前函数
elif node.rchild is None:
node.rchild = Node(item) # C E G I
print(f'添加节点位置3,添加了{item}')
return
else:
queue.append(node.lchild)
queue.append(node.rchild)
# 定义广度优先遍历方法
def breadth_travel(self):
# 1.先判断根节点是否为空,如果为空就没有遍历的必要了
if self.root is None:
print('对不起,二叉树为空,不能遍历')
return
# 创建队列,把根节点放入队列
queue = [self.root] # []
# 遍历队列,直到队列为空
while len(queue) > 0:
# 取出队列第一个元素
node = queue.pop(0)
# 打印元素
print(node.item, end=' ') # A B C D E F G H I J
# 判断左孩子是否存在,存在就放入队列
if node.lchild is not None:
queue.append(node.lchild)
if node.rchild is not None:
queue.append(node.rchild)
# 换行
print()
# 定义深度优先遍历方法
"""
注意: 深度优先有三种遍历方式:
先序(根左右):ABDHIEJCFG
中序(左根右):HDIBJEAFCG
后序(左右根):HIDJEBFGCA
递归思想: 函数内部自己调用自己,注意:必须有出口!!!
"""
# 先序(根左右): ABDHIEJCFG
def pre_travel(self, root):
# 注意:首次root是根节点,后续就是它的孩子们...
if root is not None:
# 根
print(root.item, end=' ')
# 左
self.pre_travel(root.lchild)
# 右
self.pre_travel(root.rchild)
# 中序(左根右): HDIBJEAFCG
def in_travel(self, root):
# 注意:首次root是根节点,后续就是它的孩子们...
if root is not None:
# 左
self.in_travel(root.lchild)
# 根
print(root.item, end=' ')
# 右
self.in_travel(root.rchild)
# 后序(左右根): HIDJEBFGCA
def back_travel(self, root):
# 注意:首次root是根节点,后续就是它的孩子们...
if root is not None:
# 左
self.back_travel(root.lchild)
# 右
self.back_travel(root.rchild)
# 根
print(root.item, end=' ')
if __name__ == '__main__':
# 2.再根据类创建对象
tree = TwoXTree()
# 3.使用对象
# 测试添加功能
tree.add('A')
tree.add('B')
tree.add('C')
tree.add('D')
tree.add('E')
tree.add('F')
tree.add('G')
tree.add('H')
tree.add('I')
tree.add('J')
print('---------------------------------')
# 测试广度优先遍历功能
tree.breadth_travel()
print('---------------------------------')
# 测试深度优先遍历功能
# 先序(根左右): ABDHIEJCFG
tree.pre_travel(tree.root)
print()
# 中序(左根右): HDIBJEAFCG
tree.in_travel(tree.root)
print()
# 后序(左右根): HIDJEBFGCA
tree.back_travel(tree.root)
print()
结果
添加节点位置1,添加了A
添加节点位置2,添加了B
添加节点位置3,添加了C
添加节点位置2,添加了D
添加节点位置3,添加了E
添加节点位置2,添加了F
添加节点位置3,添加了G
添加节点位置2,添加了H
添加节点位置3,添加了I
添加节点位置2,添加了J
---------------------------------
A B C D E F G H I J
---------------------------------
A B D H I E J C F G
H D I B J E A F C G
H I D J E B F G C A
进程已结束,退出代码为 0