day_2_排序算法和树

news2025/1/16 20:13:22

文章目录

  • 排序算法和树
    • 排序算法
      • 算法稳定性
      • 排序算法
        • ☆ 冒泡排序
          • 冒泡思路
          • 冒泡步骤
          • 代码实现
          • 效率优化
        • ☆ 选择排序
          • 排序思路
          • 排序步骤
          • 代码实现
        • ...
      • 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 ,冒泡排序的步骤如上图所示。

  1. 首先,对 n 个元素执行“冒泡”,将列表的最大元素交换至正确位置
  2. 接下来,对剩余 n−1 个元素执行“冒泡”,将第二大元素交换至正确位置
  3. 以此类推,经过 n−1 轮“冒泡”后,前 n−1 大的元素都被交换至正确位置
  4. 仅剩的一个元素必定是最小元素,无须排序,因此列表排序完成。

在这里插入图片描述

代码实现
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 ,选择排序的算法流程如下图所示:

第一轮:
在这里插入图片描述

第一轮:
在这里插入图片描述

第二轮:
在这里插入图片描述

第二轮:
在这里插入图片描述

第三轮:

在这里插入图片描述

第三轮:
在这里插入图片描述

第四轮:
在这里插入图片描述

第四轮:
在这里插入图片描述

第五轮:

在这里插入图片描述

第五轮:
在这里插入图片描述

完成排序,最终结果:

在这里插入图片描述

排序步骤
  1. 初始状态下,所有元素未排序,即未排序(索引)区间为 [0,n−1] 。
  2. 选取区间 [0,n−1] 中的最小元素,将其与索引 0 处的元素交换。完成后,数组前 1 个元素已排序。
  3. 选取区间 [1,n−1] 中的最小元素,将其与索引 1 处的元素交换。完成后,数组前 2 个元素已排序。
  4. 以此类推。经过 n−1 轮选择与交换后,数组前 n−1 个元素已排序。
  5. 仅剩的一个元素必定是最大元素,无须排序,因此数组排序完成。
代码实现
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-树的基本概念

树是一种一对多关系的数据结构,主要分为:

  1. 多叉树
    1. 每个结点有0、或者多个子节点
    2. 没有父节点的结点成为根节点
    3. 每一个非根节点有且只有一个父节点
    4. 除了根节点外,每个子节点可以分为多个互不相交的子树
  2. 二叉树
    1. 每个结点有0、1、2 个子节点
    2. 没有父节点的结点成为根节点
    3. 每一个非根节点有且只有一个父节点
    4. 除了根节点外,每个子节点可以分为多个互不相交的子树

02-树的相关术语

在这里插入图片描述

03-二叉树的种类

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

04-二叉树的存储

顺序存储、链式存储。树在存储的时候,要存储什么?

  1. 结点关系

如果树是完全二叉树、满二叉树,可以使用顺序存储。大多数构建出的树都不是完全、满二叉树,所以使用链式存储比较多。

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
  1. 深度优先遍历:沿着某一个路径遍历到叶子结点,再从另外一个路径遍历,直到遍历完所有的结点
  2. 广度优先遍历:按照层次进行遍历

08-添加节点思路分析

在这里插入图片描述

在这里插入图片描述

  1. 初始操作:初始化队列、将根节点入队、创建新结点
  2. 重复执行:
    1. 获得并弹出队头元素
      1. 如果当前结点的左右子结点不为空,则将其左右子节点入队
      2. 如果当前结点的左右子节点为空,则将新结点挂到为空的左子结点、或者右子节点

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()
A
B
C
D
E
F
G
H
I

10-二叉树的三种深度优先遍历

先序遍历:先访问根节点、再访问左子树、最后访问右子树

中序遍历:先访问左子树、再访问根节点、最后访问右子树

后序遍历:先访问左子树、再访问右子树、最后访问根节点

  1. 无论那种遍历方式,都是先访问左子树、再访问右子树
  2. 碰到根节点就输出、碰到左子树、右子树就递归 注意:左子树右子树是一棵树所以递归;根节点是一个节点所以打印输出

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2277686.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

蓝桥杯刷题第二天——背包问题

题目描述 有N件物品和一个容量是V的背包。每件物品只能使用一次。第i件物品的体积是Vi价值是Wi。 求解将哪些物品装入背包&#xff0c;可使这些物品的总体积不超过背包容量&#xff0c;且总价值最大。 输出最大价值。 输入格式 第一行两个整数&#xff0c;N&#xff0c;V&am…

Linux x86_64 程序动态链接之GOT 和 PLT

文章目录 前言一、动态链接二、位置无关代码三、GOT 和 PLT3.1 GOT3.2 PLT3.3 延时绑定3.4 示例 四、demo演示五、延迟绑定技术和代码修补参考资料 前言 这篇文章描述了&#xff1a;Linux x86_64 程序静态链接之重定位&#xff0c;接来本文描述Linux x86_64 程序动态链接之GOT…

学习记录-责任链模式验证参数

学习记录-责任链模式验证参数 1.什么是责任链模式 责任链模式&#xff08;Chain of Responsibility Pattern&#xff09;是一种行为设计模式&#xff0c;它允许将请求沿着一个处理链传递&#xff0c;直到链中的某个对象处理它。这样&#xff0c;发送者无需知道哪个对象将处理…

练习:MySQL单表查询与多表查询

一.单表查询 创建worke数据库&#xff0c;在数据库底下创建worker表 mysql> create database worke; Query OK, 1 row affected (0.00 sec)mysql> show databases; -------------------- | Database | -------------------- | information_schema | | mysql …

HarmonyOS NEXT应用开发边学边玩系列:从零实现一影视APP (四、最近上映电影滚动展示及加载更多的实现)

在HarmonyOS NEXT开发环境中&#xff0c;可以使用多种组件和库来构建丰富且交互友好的应用。本文将展示如何使用HarmonyOS NEXT框架和nutpi/axios库&#xff0c;从零开始实现一个简单的影视APP的首页&#xff0c;主要关注最近上映电影的滚动展示及加载更多功能的实现。 开源项目…

卷积神经05-GAN对抗神经网络

卷积神经05-GAN对抗神经网络 使用Python3.9CUDA11.8Pytorch实现一个CNN优化版的对抗神经网络 简单的GAN图片生成 CNN优化后的图片生成 优化模型代码对比 0-核心逻辑脉络 1&#xff09;Anacanda使用CUDAPytorch2&#xff09;使用本地MNIST进行手写图片训练3&#xff09;…

客户案例:某家居制造企业跨境电商,解决业务端(亚马逊平台)、易仓ERP与财务端(金蝶ERP)系统间的业务财务数据对账互通

一、系统定义 1、系统定位&#xff1a; 数据中台系统是一种战略选择和组织形式&#xff0c;通过有型的产品支撑和实施方法论&#xff0c;解决企业面临的数据孤岛、数据维护混乱、数据价值利用低的问题&#xff0c;依据企业特有的业务和架构&#xff0c;构建一套从数据汇聚、开…

服务器一次性部署One API + ChatGPT-Next-Web

服务器一次性部署One API ChatGPT-Next-Web One API ChatGPT-Next-Web 介绍One APIChatGPT-Next-Web docker-compose 部署One API ChatGPT-Next-WebOpen API docker-compose 配置ChatGPT-Next-Web docker-compose 配置docker-compose 启动容器 后续配置 同步发布在个人笔记服…

辅助云运维

为客户提供运维支持&#xff0c;保障业务连续性。 文章目录 一、服务范围二、服务内容三、服务流程四、 服务交付件五、责任分工六、 完成标志 一、服务范围 覆盖范围 云产品使用咨询、问题处理、配置指导等&#xff1b; 云产品相关操作的技术指导&#xff1b; 云相关资源日常…

[Qt]常用控件介绍-多元素控件-QListWidget、QTableWidget、QQTreeWidget

目录 1.多元素控件介绍 2.ListWidget控件 属性 核心方法 核心信号 细节 Demo&#xff1a;编辑日程 3.TableWidget控件 核心方法 QTableWidgetItem核心信号 QTableWidgetItem核心方法 细节 Demo&#xff1a;编辑学生信息 4.TreeWidget控件 核心方法 核心信号…

Windows部署NVM并下载多版本Node.js的方法(含删除原有Node的方法)

本文介绍在Windows电脑中&#xff0c;下载、部署NVM&#xff08;node.js version management&#xff09;环境&#xff0c;并基于其安装不同版本的Node.js的方法。 在之前的文章Windows系统下载、部署Node.js与npm环境的方法&#xff08;https://blog.csdn.net/zhebushibiaoshi…

基于STM32设计的粮食仓库(粮仓)环境监测系统

一、前言 1.1 项目开发背景 随着现代农业的发展和粮食储存规模的扩大&#xff0c;粮仓环境的智能化监控需求日益增长。传统的粮仓管理方式通常依赖人工检测和定期巡查&#xff0c;效率低下且容易出现疏漏&#xff0c;无法及时发现潜在问题&#xff0c;可能导致粮食受潮、霉变…

【Linux】--- 进程的等待与替换

进程的等待与替换 一、进程等待1、进程等待的必要性2、获取子进程status3、进程等待的方法&#xff08;1&#xff09;wait&#xff08;&#xff09;函数&#xff08;2&#xff09;waitpid函数 4、多进程创建以及等待的代码模型5、非阻塞接口 轮询 二、进程替换1、替换原理2、替…

Vue2+OpenLayers添加/删除点、点击事件功能实现(提供Gitee源码)

目录 一、案例截图 二、安装OpenLayers库 三、安装Element-UI 四、代码实现 4.1、添加一个点 4.2、删除所有点 4.3、根据经纬度删除点 4.4、给点添加点击事件 4.5、完整代码 五、Gitee源码 一、案例截图 可以新增/删除标记点&#xff0c;点击标记点可以获取到当前标…

HTML中如何保留字符串的空白符和换行符号的效果

有个字符串 储值门店{{thing3.DATA}}\n储值卡号{{character_string1.DATA}}\n储值金额{{amount4.DATA}}\n当前余额{{amount5.DATA}}\n储值时间{{time2.DATA}} &#xff0c; HTML中想要保留 \n的换行效果的有下面3种方法&#xff1a; 1、style 中 设置 white-space: pre-lin…

GB44495-2024 汽车整车信息安全技术要求 - V2X部分前置要求

背景 GB 44495-2024《汽车整车信息安全技术要求》中关于V2X&#xff08;车与外界通信&#xff09;的部分&#xff0c;主要关注于通信安全要求&#xff0c;旨在确保车辆在与外部设备进行数据交互时的信息安全。其测试大致可分为消息层&#xff08;数据无异常&#xff09;、应用…

[PAT 甲级] 1179 Chemical Equation (DFS)

​ 题目翻译&#xff08;GPT&#xff09;&#xff1a; 1179 化学方程式 化学方程式是一种用符号和公式表示化学反应的方法&#xff0c;其中反应物在方程式的左侧&#xff0c;生成物在右侧。例如&#xff1a; CH₄ 2O₂ -> CO₂ 2H₂O 表示反应物为甲烷和氧气&#xff…

android分区和root

线刷包内容&#xff1a; 线刷包是一个完整的android镜像&#xff0c;不但包括android、linux和用户数据&#xff0c;还包括recovery等。当然此图中没有recovery,但是我们可以自己刷入一个。 主要分区 system.img 系统分区&#xff0c;包括linux下主要的二进制程序。 boot.img…

每日一题(五):n个正整数排列,求组合后最大数

目录 一、题目 二、题目分析 &#xff08;一&#xff09;明确需求 背景知识&#xff1a;字符串的比较与连接 1.字符串的比较 2.字符串的连接 (二)分析思路 三、将思路转换为程序 四、总结 一、题目 给定n个正整数a1,a2,……,an对这n个数进行排列&#xff0c;求组合后可以得到…

【STM32-学习笔记-3-】TIM定时器

文章目录 TIM定时器Ⅰ、TIM定时器函数Ⅱ、TIM_TimeBaseInitTypeDef结构体参数①、TIM_ClockDivision②、TIM_CounterMode③、TIM_Period④、TIM_Prescaler⑤、TIM_RepetitionCounter Ⅱ、定时器配置Ⅲ、定时器外部中断NVIC配置 TIM定时器 Ⅰ、TIM定时器函数 // 将定时器寄存器…