文章目录
- 排序算法和树
- 排序算法
- 算法稳定性
- 排序算法
- ☆ 冒泡排序
- 冒泡思路
- 冒泡步骤
- 代码实现
- 效率优化
 
- ☆ 选择排序
- 排序思路
- 排序步骤
- 代码实现
 
- ...
 
 
- 树
- 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









![[Qt]常用控件介绍-多元素控件-QListWidget、QTableWidget、QQTreeWidget](https://i-blog.csdnimg.cn/direct/85a50c646d24470b9233c73202ee5e51.png)






![[PAT 甲级] 1179 Chemical Equation (DFS)](https://i-blog.csdnimg.cn/direct/e02afaf95e724c5f8d69d46d8509af08.png)


