python进阶篇-day09-数据结构与算法(非线性结构与排序算法)

news2024/12/26 23:10:48

非线性结构(树状结构)

特点: 每个节点都可以有n个子节点(后继节点) 和 n个父节点(前驱节点)

代表: 树, 图......

概述

属于数据结构之 非线性结构的一种, 父节点可以有多个子节点(后续节点)

特点

  1. 有且只有1个根节点

  2. 每个节点都可以有1个父节点及任意个子节点, 前提: 根节点除外(没有父节点)

  3. 没有子节点的节点称之为: 叶子节点

二叉树概念和性质

每个节点最多只能有2个子节点

二叉树分类:
  1. 完全二叉树: 除了最后1层, 其他层的节点都是满的

  2. 满二叉树: 包括最后1层, 所有层的节点都是满的

  3. 不完全二叉树: 某层(不仅仅是最后1层)的节点数量不满

常用二叉树:
  1. 平衡二叉树: 防止树退化成链表, 指的是: 任意节点的两颗子树的高度差不超过1

  2. 排序二叉树: 主要是对元素排序的

存储方式

更推荐使用 链表的方式来存储, 每个节点有三部分组成, 分别是: 元素域(数值域), 左子树(地址域), 右子树(地址域)

针对于, 多叉树的情况, 可以将其转成二叉树, 然后再来存储

性质
  1. 在二叉树的第i层上至多有2i-1 个结点(i>0)eg:第3层最多结点个数2^((3-1))

  2. 深度为k的二叉树至多有2k - 1个结点(k>0)eg:层次2^((3))-1= 7

  3. 对于任意一棵二叉树,如果其叶结点数为N0,而度数为2的结点总数为N2,则N0 = N2+1

  4. 最多有n个结点的完全二叉树的深度必为log2(n+1)

  5. 对完全二叉树,若从上至下、从左至右编号,则编号为i的结点,其左孩子编号必为2i,其右孩子编号必为2i+1,其父节点的编号必为i//2(i=1时为根,除外)

二叉树的广度优先

广度优先可以找到最短路径:

相当于层次遍历,先把第层给遍历完,看有没有终点;再把第层遍历完,看有没有重点

插入节点
  1. 初始操作:

    初始化队列、将根节点入队、准备加入到二叉树的新结点

  2. 重复执行:

    获得并弹出队头元素

    1、若当前结点的左右子结点不为空,则将其左右子节点入队列

    2、若当前结点的左右子节点为空,则将新结点挂到为空的左子结点、或者右子节点

图示

二叉树的深度优先

深度优先往往可以很快找到搜索路径:

比如:先找一个结点看看是不是终点,若不是继续往深层去找,直到找到终点。、中序,先序,后序属于深度优先算法

示例

层次(广度)遍历: 0123456789

先序遍历: 0137849256 根 左 右

中序遍历: 7381940526 左 根 右

后序遍历: 7839415620 左 右 根

图示

二叉树代码演示

# 创建节点类
class Node(object):
    # 初始化属性
    def __init__(self, item):
        self.item = item  # 数值域
        self.lchild = None  # 左子树(地址域)
        self.rchild = None  # 左子树(地址域)
​
​
# 创建二叉树类
class BinaryTree(object):
    def __init__(self, root: Node = None):
        self.root = root  # 根节点
​
    # 添加元素函数(完全二叉树)
    def add(self, item):
        # 判断根节点是否为空
        if self.root is None:
            self.root = Node(item)
            return
        # 根节点不为空, 找到缺失节点
        # 创建队列, 用于记录已存在的节点
        queue = []
        # 把根节点添加到队列
        queue.append(self.root)
        # 循环遍历队列, 直至 把新元素添加到合适的位置
        while True:
            # 获取队列中的第一个元素
            node = queue.pop(0)
            # 左子树为空
            if node.lchild is None:
                node.lchild = Node(item)
                return  # 结束添加动作
            else:
                # 左子树存在, 就将其添加到队列中
                queue.append(node.lchild)
            # 右子树为空
            if node.rchild is None:
                node.rchild = Node(item)
                return  # 结束添加动作
            else:
                # 右子树存在, 就将其添加到队列中
                queue.append(node.rchild)
​
    # 广度优先
    def breadth_travel(self):
        # 判断根节点是否为空
        if self.root is None:
            return  # 为空直接结束
        # 创建队列, 记录所有节点
        queue = []
        # 将根节点, 添加到队列中
        queue.append(self.root)
        # 只要队列长度大于0, 就说明还有节点, 循环遍历
        while len(queue) > 0:
            # 获取队列中的第一元素
            node = queue.pop(0)
            # 输出当前节点
            print(node.item, end='\t')
            # 判断当前节点的左子树是否为空
            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='\t')
            # 递归左子树
            self.preorder_travel(root.lchild)
            # 递归右子树
            self.preorder_travel(root.rchild)
​
    # 深度优先: 中序(左根右)
    def mid_travel(self, root):
        if root is not None:
            self.mid_travel(root.lchild)
            print(root.item, end='\t')
            self.mid_travel(root.rchild)
​
    # 深度优先: 后序(左右根)
    def poster_travel(self, root):
        if root is not None:
            self.poster_travel(root.lchild)
            self.poster_travel(root.rchild)
            print(root.item, end='\t')
​

三. 算法

排序类相关

稳定性: 排序前后的相对位置(相同的元素位置)是否发生变化

稳定排序: 冒泡排序、插入排序、归并排序和基数排序

不稳定排序: 选择排序、快速排序、希尔排序、堆排序

冒泡排序

原理

相邻元素两辆比较, 大的往后走, 这样第一轮比较完毕后, 最大值就在最大索引处

核心
  1. 比较的总轮数 列表长度 - 1

  2. 每轮比较的总次数 列表长度 - 1 - 轮数(从0 开始)

  3. 谁和谁比较(交换) j 和 j + 1 比较

图解

代码
def buble_sort(my_list):
    # 定义变量n存储列表长度
    n = len(my_list)
    # 比较的轮数
    for i in range(n - 1):  # i: 0, 1, 2, 3
        # 记录交换的次数
        count = 0
        # 每轮比较的次数
        for j in range(n - 1 - i):  # j: 4, 3, 2, 1
            if my_list[j] > my_list[j + 1]:
                # 交换时计数器加一
                count += 1
                my_list[j], my_list[j + 1] = my_list[j + 1], my_list[j]
        print(f'第 {i} 轮交换了 {count} 次')
        # 如果当前轮次交换了0次, 则跳出外循环
        if count == 0:
            break
​
​
if __name__ == '__main__':
    list1 = [2, 1, 4, 5, 3]
    buble_sort(list1)
    print('list1', list1)
    print('-' * 21)
​
    list2 = [5, 3, 4, 7, 2]
    buble_sort(list2)
    print('list2', list2)
​
冒泡总结

时间复杂度: 最优O(n), 最差O(n²)

遍历一遍发现没有任何元素发生了位置交换,终止排序

算法稳定性:稳定算法

选择排序

原理

第一轮: 假设索引为0的元素时最小值, 依次和后续的元素比较, 只要比该值小, 就纪录住真正最小值的那个索引, 第一轮比较完毕后, 把最小值放到索引为0的位置即可

第二轮: 假设索引为1的元素时最小值, 依次和后续的元素比较, 只要比该值小, 就纪录住真正最小值的那个索引, 第一轮比较完毕后, 把最小值放到索引为1的位置即可

......

解释:

选择排序就是把符合要求的数据选择出来进行排序

核心
  1. 比较的总轮数 列表长度 - 1

  2. 每轮比较的总次数 i + 1 ~ 列表的最后1个元素

  3. 谁和谁比较(交换) j 和 min_index 位置的元素比较

比较过程

具体的比较过程, 假设共 5 个元素 比较的轮数 每轮比较的总次数 谁和谁比较 第0轮 4 0和1, 0和2, 0和3, 0和4 第1轮 3 1和2, 1和3, 1和4 第2轮 2 2和3, 2和4 第3轮 1 3和4

代码
def select_sort(my_list):
    # 定义变量n存储列表长度
    n = len(my_list)
    # 比较的轮数
    for i in range(n - 1):  # i: 0, 1, 2, 3
        # 记录最小值索引
        min_idx = i
        # 每轮比较的次数
        for j in range(i + 1, n):  # j: 4, 3, 2, 1
            # 判断min_idx后的元素是否比min_idx小
            if my_list[j] < my_list[min_idx]:
                # 将最小值索引设为j
                min_idx = j
        # 如果最小值索引等于i说明i的位置是正确的,不交换
        if min_idx != i:
            # 交换
            my_list[i], my_list[min_idx] = my_list[min_idx], my_list[i]
​
​
if __name__ == '__main__':
    list1 = [2, 1, 4, 5, 3]
    select_sort(list1)
    print('list1', list1)
    print('-' * 21)
​
    list2 = [5, 3, 4, 7, 2]
    select_sort(list2)
    print('list2', list2)
​
选择总结

算法稳定性: 不稳定算法

时间复杂度: 最优: O(n²), 最差: O(n²)

插入排序

原理

把要排序的列表分成两部分, 第一部分是有序的(拍好序的), 第二部分是无序的(待排序的), 然后从待排序的列表中, 依次取出每个值, 插入到 排好序的列表的 合适位置 .

核心
  1. 比较的总轮数 列表长度 - 1 for i in range(1, n)

  2. 每轮比较的总次数 i ~ 0(逆向遍历) for i in range(i, 0, -1)

  3. 谁和谁比较(交换) i 和 j 的每个值 比较

比较过程

具体的比较过程, 假设共 5 个元素 比较的轮数 每轮比较的总次数 谁和谁比较 第1轮 4 1和0 第2轮 3 2和1, 2和0 第3轮 2 3和2, 3和1, 3和0 第4轮 1 4和3, 4和2, 4和1, 4和0

代码
def insert_sort(my_list):
    # 定义变量n存储列表长度
    n = len(my_list)
    # 比较的轮数
    for i in range(1, n):           # i: 1,  2,   3,     4
        # 每轮比较的次数
        for j in range(i, 0, -1):   # j: 1  2,1  3,2,1  4,3,2,1
            # 判断当前元素(待排序)是否比前面的元素(排好序的)小
            if my_list[j] < my_list[j - 1]:
                # 交换当前元素和当前元素的前一个元素
                my_list[j], my_list[j - 1] = my_list[j - 1], my_list[j]
            else:
                break
​
​
if __name__ == '__main__':
    list1 = [2, 1, 4, 5, 3]
    insert_sort(list1)
    print('list1', list1)
    print('-' * 21)
​
    list2 = [5, 3, 4, 7, 2]
    insert_sort(list2)
    print('list2', list2)
​
插入总结

算法稳定性: 稳定算法

时间复杂度: 最优: O(n) 最坏: O(n²)

查找类相关(二分查找)

此处只记录二分查找, 并非查找只有二分查找这一种

介绍

  1. 概述: 他是一种高效的查找类算法, 也叫: 折半查找

  2. 细节: 要被查找的列表必须是有序的

  1. 原理:

    1. 获取列表的中间位置的元素, 然后和要查找的元素进行比较

    2. 如果相等, 直接返回结果即可

    3. 如果比中间值小, 去 中间前 范围查找

    4. 如果比中间值大, 去 中间后 范围查找

递归版

def binary_search(my_list, item):
    n = len(my_list)
    if n <= 0:
        return False
    mid = n // 2
    if item == my_list[mid]:
        return True
    elif item < my_list[mid]:
        return binary_search(my_list[:mid], item)
    else:
        return binary_search(my_list[mid + 1:], item)

非递归版

def binary_search(my_list, item):
    # 计算列表长度
    n = len(my_list)
    # 定义初始起始位置为0
    start = 0
    # 定义初始终止位置为列表长度减1
    end = n - 1
    # 当起始位置大于终止位置时结束循环
    while start <= end:
        # 定义中间位置为起始位置加终止位置除2
        mid = (start + end) // 2
        # 判断中间索引位置的元素是否为要查找的元素
        if my_list[mid] == item:
            return True
        # 判断中间索引位置的元素比要查找的位置小
        elif my_list[mid] < item:
            # 设置起始位置索引为中间位置加1
            start = mid + 1
        # 判断中间索引位置的元素比要查找的位置大
        else:
            # 设置终止位置索引为中间位置减1
            end = mid - 1
    return False
​
​
if __name__ == '__main__':
    list1 = [1, 2, 3, 4, 5, 6, 7]
    print(binary_search(list1, 4))
​
​

总结

  1. 必须采用顺序存储结构

  2. 必须按关键字大小有序排列

  3. 时间复杂度: 最优: O(1) 最坏: O(logn)

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

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

相关文章

C 408—《数据结构》算法题基础篇—链表(上)

目录 Δ前言 一、链表中特定值结点的删除 0.题目&#xff1a; 1.算法设计思想&#xff1a; 2.C语言描述&#xff1a; 3.算法的时间和空间复杂度&#xff1a; 二、链表链表最小值结点的删除 0.题目 : 1.算法设计思想 : 2.C语言描述 : 3.算法的时间和空间复杂度 : 三、链…

E32.【C语言】练习:指针运算习题集(下)(未完)

Exercise 6:阿里巴巴面试题 求下列代码的执行结果 #include <stdio.h> int main() {char *a[] {"work","at","alibaba"};char**pa a;pa;printf("%s\n", *pa);return 0; } 答案速查: 分析: char *a[] {"work",…

计算机组成原理(第一课)

计算机系统概述 1.发展史 摩尔定律&#xff1a;集成电路上可以容纳的晶体管数目在大约每经过18个月到24个月便会增加一倍 2.操作系统组成 存储程序程序控制 五个部分记住&#xff1a; 输入输出功能 I/O 记忆功能 访问 计算功能 计算 判断功能 判断 自我控制功能 自我控制…

冲呀!6款最佳企业文件加密软件排名

在当前数字化时代&#xff0c;企业数据的安全性和保密性已成为企业运营中不可忽视的重要环节。文件加密软件作为保护企业敏感数据的有效工具&#xff0c;其重要性日益凸显。以下是六款最佳企业文件加密软件的排名及详细介绍&#xff0c;这些软件均以其卓越的性能、安全性和易用…

Parsec被墙/800报错/无法访问/连接错误/被封解决方案

Parsec被墙老问题了&#xff0c;给小白们推荐一些解决方式&#xff1a; 800报错的话&#xff1a;把猫的tun模式打开&#xff0c;然后安装服务模式&#xff0c;再登录就可以了&#xff1b; 6023&#xff1a;开IPV6就能解决。 因为Parsec被墙不是一次两次了&#xff0c;我建议…

“简易不简单,全志H616开发环境配置,让创意快速转化为现实!“#全志H616开发板开发环境简易配置

"简易不简单&#xff0c;全志H616开发环境配置&#xff0c;让创意快速转化为现实&#xff01;"#全志H616开发板简易配置开发环境 前言预备知识一、 全志H616学习方向1.1 为什么学1.2 学什么 二、 H616开发板OrangePiZero2 介绍2.1 平台介绍2.2 平台特性2.3 配套操作系…

关于武汉芯景科技有限公司的IIC电平转换芯片XJ4300开发指南(兼容LTC4300)

一、芯片引脚介绍 1.芯片引脚 2.引脚描述 二、系统结构图 三、功能描述 1.电平转换

算法工程师重生之第二天(长度最小的子数组 螺旋矩阵II 区间和 开发商购买土地 总结 )

参考文献 代码随想录 一、长度最小的子数组 给定一个含有 n 个正整数的数组和一个正整数 target 。 找出该数组中满足其总和大于等于 target 的长度最小的 子数组 [numsl, numsl1, ..., numsr-1, numsr] &#xff0c;并返回其长度。如果不存在符合条件的子数组&#xff0c…

单细胞拟时序/轨迹分析原理及monocle2流程学习和整理

在生命演进的过程中机体会随着时间的变化而产生不同的变化。从婴幼儿长大为成年人再到老年人的过程中&#xff0c;我们的身体机能经历了从"弱-强-弱"的变化过程(宽泛的说)&#xff0c;以年为单位来看&#xff0c;有可能我们在10多岁的时候一年内一下子长高了几十厘米…

ArcGIS中怎么合并多个点图层并删除重复点?

最近&#xff0c;我接到了一个怎么合并多个点图层并删除其中的重复点的咨询。 下面是我对这个问题的解决思路&#xff1a; 1、合并图层 在地理处理工具里面 选择合并 并设置好要合并的图层即可 2、接下来在 数据管理工具→常规→删除相同项 即可 希望这些建议能对大家有所帮…

blender云渲染来了,blender云渲染教程!

朋友们&#xff0c;成都渲染101农场blender云渲染上线了&#xff0c;继3DMAX/C4D/maya/UE5云渲染上线后&#xff0c;又上线了blender云渲染&#xff0c;今天&#xff0c;成都渲染101渲染农场用四步教会您blender云渲染&#xff01; 第一步&#xff0c;云渲码6666注册个渲染101…

关于elasticsearch的terms查询超过最大terms数

当我使用es的terms查询时&#xff0c;报错了这段内容。 failed to create query: The number of terms [80306] used in the Terms Query request has exceeded the allowed maximum of [65536]. This maximum can be set by changing the [index.max_terms_count] index leve…

Web日志分析工具GoAccess

目录 1. 介绍 2. 功能 3. 支持的格式 4. 安装 从发布版本构建 从GitHub构建&#xff08;开发&#xff09; 命令行安装 5. 使用 5.1 监视Apache日志 5.2 通过web仪表板查看日志 浏览器访问 5.3 汉化设置 测试访问 1. 介绍 GoAccess是一个开源的实时网络日志分析器和…

VS Code 文件定位功能

1、取消“当前打开文件”的自动定位功能。 设置 ->搜索 Explorer: Auto Reveal -> 将配置改为 false 2.在vs2017中定位文件 Tools->Option->Projects And Solutions->General, tick “track Active Item in Solution Explorer” 工具-> 选项->项目和…

网络基础入门指南(一)

前言 在这个高度互联的世界里&#xff0c;互联网已成为日常生活不可或缺的一部分。然而&#xff0c;对于许多人来说&#xff0c;网络是如何工作的仍然是个谜。本文旨在为那些对网络基础知识感兴趣的朋友提供一个简单的介绍&#xff0c;帮助大家更好地理解互联网的基本原理和技…

输出CAD图中第一个图元类型——c#实现

复制改图元到一个新dwg中&#xff0c;启动代码可实现 如下图设置&#xff1a; using Autodesk.AutoCAD.ApplicationServices; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.Runtime; using System; using System.Collections.Generic; using System.Linq…

wordpress做后台的资讯类小程序源码

WordPress博客系统资讯资源变现下载小程序源码。这个就比较牛逼了&#xff0c;直接用wordpress做后台 因为由于微信的新规从2022-11月9号后新上线的小程序将不能获取用户头像和名字了 所以微信放需要适配全新的,支持让用户自定义头像和昵称了 不然统一返回默认头像和显示(微…

转义字符笔记

\ &#xff08;在行尾时&#xff09;续行符 \ 反斜杠符号 &#xff0c;用 \ 在字符串里表示反斜杠 ’ 单引号&#xff0c;用 ’ 在字符串里表示单引号 " 双引号&#xff0c;用 " 在字符串里表示双引号 \t 制表符&#xff0c;作用是列对齐&#xff0c;一个Tab键的…

力扣hot100速览(2)

全排列&#xff1a; 原理&#xff1a; 经典回溯。回溯的原理&#xff1a;用一个数组来记录路径&#xff0c;每次递归&#xff0c;都访问没访问到的元素&#xff08;怎么访问&#xff1a;当然是遍历&#xff09;&#xff0c;访问完弹出上一步。 解&#xff1a; 同原理&#…

动手学深度学习8.4. 循环神经网络-笔记练习(PyTorch)

本节课程地址&#xff1a;54 循环神经网络 RNN【动手学深度学习v2】_哔哩哔哩_bilibili 本节教材地址&#xff1a;8.4. 循环神经网络 — 动手学深度学习 2.0.0 documentation (d2l.ai) 本节开源代码&#xff1a;...>d2l-zh>pytorch>chapter_multilayer-perceptrons&…