首页 > 其他分享 >数据结构之——选择树

数据结构之——选择树

时间:2024-10-29 13:48:05浏览次数:7  
标签:结点 排序 选择 二叉树 数据结构 root 节点

一、选择树的概念

选择树是一种完全二叉树,在数据处理和排序等领域有着重要的应用。其中,胜者树和败者树是选择树的两种常见形式。

胜者树的每个中间结点记录的是胜者的标号。例如在一个有多个选手(叶子节点)的胜者树中,每个非叶子节点表示一场比赛,记录胜者的标号,每一层相当于一轮比赛。如果一个选手的值改变了,可以很容易地修改这棵胜者树,只需要沿着从该结点到根结点的路径修改这棵二叉树,而不必改变其他比赛的结果。

败者树则是胜者树的一种变体。在败者树中,用父结点记录其左右子结点进行比赛的败者,而让胜者参加下一轮的比赛。败者树的根结点记录的是败者,需要加一个结点来记录整个比赛的胜利者。采用败者树可以简化重构的过程。例如当一个叶子结点的值改变后,败者树的重构只需要与其父结点比较,而胜者树的重构还与该结点的兄弟结点有关。

在实际应用中,胜者树与败者树可以在log(n) 的时间内找到最值。例如在 k 路归并排序中经常用到选择树。当有 k 个已经排序的序列需要合并成一个单独的排序序列时,选择树可以高效地找出每个归并段中的最值,从而实现快速排序。假设总共有 n 个数字,每次取 k 个归并串最小或者最大的一个数,若使用暴力算法,比较 k-1 次得到所有数中最大或者最小的数,存入新空间中,接着一直这样比较,需要比较的次数是 n*(k - 1)。而选择树算法可以构造完全二叉树的数组表示法,每次的比较次数是 O(logk),时间复杂度是 O(logk)。

二、选择树的特点

(一)易于修改

胜者树在选手值发生改变时,确实展现出了高效的可修改性。这是因为胜者树的结构特点决定了其修改过程的简洁性。当一个叶子结点的值发生变化时,只需要从该结点开始,沿着到根结点的路径进行修改。在这个过程中,只涉及到路径上的节点,不会对其他无关的节点产生影响。例如,在一个有 n 个选手的胜者树中,如果某个叶子结点的值从 x 变为 y,首先判断这个叶子结点的父结点,根据新的值与兄弟结点的值进行比较,确定新的胜者,然后继续向上比较,直到根结点。这个过程的时间复杂度相当于二分,仅为 O(logN)。相比之下,如果没有这样的结构特点,对于整个树进行重新计算最值,那么时间复杂度可能会高得多。

(二)快速找最值

选择树能够在 log(n)的时间内找到最值,这一特性使其在实际应用中具有很大的优势。以 路归并排序为例,当有 k 个已经排序的序列需要合并成一个单独的排序序列时,选择树可以高效地找出每个归并段中的最值。在这个过程中,任何一个叶子结点的值改变后,利用中间结点的信息,还是能够快速地找到最值。假设我们有 k 个长度为 m 的已排序序列,总共有 n = k * m 个数字。如果使用暴力算法,每次取 k 个归并串最小或者最大的一个数,需要比较 k-1 次得到所有数中最大或者最小的数,存入新空间中,接着一直这样比较,需要比较的次数是 n*(k - 1)。而选择树算法可以构造完全二叉树的数组表示法,每次的比较次数是 O(logk),时间复杂度是 O(logk)。这大大提高了排序的效率,尤其是在处理大规模数据时,优势更加明显。

三、选择树与其他数据结构的比较

(一)与数组、链表比较

数组在存储数据时,通过下标访问元素速度快,对于有序数组还可以使用二分查找提高检索速度。然而,当要检索具体某个值或者插入值时,会整体移动数据,效率较低。例如,一个长度为 n 的数组,在中间位置插入一个元素,需要将后面的 n/2 个元素依次向后移动一位,时间复杂度为 O(n)。

链表在一定程度上对数组的插入和删除操作进行了优化,插入一个数值节点只需将插入节点连接到链表中即可,删除效率也较高。但是,链表在进行检索的时候效率比较低,需要从头节点开始遍历。假设有一个包含 m 个节点的链表,要查找特定值,最坏情况下需要遍历所有 m 个节点,时间复杂度为 O(m)。

而选择树结合了数组和链表的优点,弥补了它们的缺点。选择树在查找最值时能够在 log(n)的时间内完成,与平衡二叉树类似,既保证了数据的检索速度,同时也保证了数据的插入、删除、修改的速度。例如在 k 路归并排序中,选择树可以高效地找出每个归并段中的最值,实现快速排序。

(二)与平衡二叉树比较

平衡二叉树在满足二叉查找树特性的基础上,任何一个结点的左子树与右子树都是平衡二叉树,并且高度之差的绝对值不超过 1。平衡二叉树的查找效率更稳定,总体的查找速度也更快。但是,平衡二叉树存在空间利用不足的问题。每个节点只存储一个键值和数据,当存储海量数据时,二叉树的节点将会非常多,高度也会及其高,进行数据查找时会进行很多次磁盘 IO,查找效率将会极低。

选择树则不同,例如在 k 路归并排序中,选择树可以构造完全二叉树的数组表示法,待比较的数据都存储在最后一层,根节点是根据左右子树其中一个生成,因此根节点是最大或者是最小的。选择树的每个磁盘块可以存储多个关键字,这样可以减少 IO 次数,提高数据查找效率。例如,当有 k 个已经排序的序列需要合并成一个单独的排序序列时,选择树可以高效地找出每个归并段中的最值,时间复杂度是 O(nlogk),大大提高了排序的效率。

四、选择树的应用场景

(一)数据文件压缩

哈夫曼树是一种特殊的二叉树,广泛用于数据文件压缩。哈夫曼树的构建过程基于哈夫曼算法,主要包括初始化、选择最小权值节点和重复合并等步骤。以 10 个权值为 1,2,3,4,5,6,7,8,9,10 的叶子结点为例,经过一系列的合并操作,最终构建出哈夫曼树。

哈夫曼树在数据压缩和编码中有着广泛的应用。通过采用不等长编码方式,将出现频率高的字符用较短的二进制编码表示,而出现频率低的字符用较长的编码表示,从而实现数据的高效压缩。在实际应用中,哈夫曼编码常被用于文件压缩、图像压缩、音频压缩等领域。它不仅可以减小数据的存储空间,还可以提高数据传输速度。

例如,在文件压缩的过程中,我们需要首先对文件进行分析,得到文件中每个字符的频率分布。然后,根据这些频率分布建立哈夫曼树,并对文件中的每个字符进行哈夫曼编码。最后,将哈夫曼编码后的文件写入到新的文件中,就可以得到压缩后的文件了。在解压缩文件时,我们只需要对文件进行哈夫曼解码,就可以恢复出原始的文件内容。

(二)数据库索引

MySQL 数据库索引使用排序二叉树中的 B + 树,提高查询效率。在 MySQL 中,InnoDB 存储引擎的最小存储单元是页,页可以用于存放数据也可以用于存放键值 + 指针,在 B + 树中叶子节点存放数据,非叶子节点存放键值 + 指针。

索引组织表通过非叶子节点的二分查找法以及指针确定数据在哪个页中,进而在去数据页中查找到需要的数据。通常一棵 B + 树可以存放大量的数据记录,在查找数据时一次页的查找代表一次 IO,所以通过主键索引查询通常只需要 1 - 3 次 IO 操作即可查找到数据。

可以通过实际操作得到 InnoDB 主键索引 B + 树的高度。在 InnoDB 的表空间文件中,约定 page number 为 3 的代表主键索引的根页,而在根页偏移量为 64 的地方存放了该 B + 树的 page level。如果 page level 为 1,树高为 2,page level 为 2,则树高为 3。即 B + 树的高度 = page level + 1。

(三)路由协议

在路由协议中,如 STP(生成树协议)确保网络无环路,SPF(最优树协议)保障网络路径最优。

组播转发模式中的密集模式使用到的分布树类型为 SPT,支持该模式的组播路由协议有 DVMRP、MOSPF、PIM。隐式加入,推模型 —Push model,3 分钟一次的泛红和修剪。DM 优点在于在组播源与组成员之间建立最短路径。

(四)其他领域

文件系统目录结构采用树形结构组织文件。Linux 文件系统是采用级层式的树状目录结构,在此结构上的最上层是根目录 “/”,然后在此目录下再创建其他的目录。例如 /bin 存放着最常使用的命令,/sbin 存放系统管理员使用的系统管理程序,/home 存放普通用户的主目录等。

深度优先搜索算法在树结构中也有广泛应用。深度优先搜索是一种用于遍历或搜索树或图的算法,在树结构中,DFS 意味着我们从根节点出发,沿着一条路径尽可能深入地访问节点,直到到达叶子节点。常见的树的遍历方式包括先序遍历、中序遍历和后序遍历,这些遍历方式在深度优先搜索中起着关键作用。

此外,C4.5 算法和 CART 算法等也用到树结构。这些算法在机器学习中用于分类和回归问题,通过构建决策树来进行预测和分类。决策树是一种树形结构,每个内部节点表示一个属性上的测试,每个分支代表一个测试输出,每个叶节点代表一种类别或预测值。

五、如何实现选择树

以下是使用 C 语言实现选择树的示例代码:

#include <stdio.h>
#include <stdlib.h>

// 定义选择树节点结构体
typedef struct TreeNode 
{
    int value;
    struct TreeNode *left;
    struct TreeNode *right;
} TreeNode;

// 创建新节点
TreeNode* createNode(int value) 
{
    // 动态分配内存以创建一个新的 TreeNode 结构体对象
    TreeNode *node = (TreeNode*)malloc(sizeof(TreeNode));
    // 为新节点赋值
    node->value = value;
    // 初始化新节点的左右子节点为 NULL,表示当前没有子节点
    node->left = NULL;
    node->right = NULL;
    // 返回新创建的节点指针
    return node;
}

// 插入节点到选择树
void insertNode(TreeNode **root, int value) 
{
    // 如果当前根节点为 NULL,说明树为空,创建一个新节点并将其作为根节点
    if (*root == NULL) {
        *root = createNode(value);
        return;
    }
    // 如果要插入的值小于当前根节点的值,递归地在左子树中插入节点
    if (value < (*root)->value) 
    {
        insertNode(&((*root)->left), value);
    } 
    else 
    {
        // 如果要插入的值大于等于当前根节点的值,递归地在右子树中插入节点
        insertNode(&((*root)->right), value);
    }
}

// 中序遍历选择树
void inorderTraversal(TreeNode *root) 
{
    // 如果当前节点为 NULL,直接返回,结束递归
    if (root == NULL) return;
    // 先递归遍历左子树
    inorderTraversal(root->left);
    // 输出当前节点的值
    printf("%d ", root->value);
    // 再递归遍历右子树
    inorderTraversal(root->right);
}

int main() 
{
    TreeNode *root = NULL;
    // 向选择树中插入节点
    insertNode(&root, 5);
    insertNode(&root, 3);
    insertNode(&root, 7);
    insertNode(&root, 2);
    insertNode(&root, 4);
    insertNode(&root, 6);
    insertNode(&root, 8);

    printf("中序遍历选择树:");
    // 对选择树进行中序遍历并输出节点值
    inorderTraversal(root);
    printf("\n");

    return 0;
}

这段代码实现了一个简单的选择树(二叉查找树),包括创建节点、插入节点和中序遍历的功能。

标签:结点,排序,选择,二叉树,数据结构,root,节点
From: https://blog.csdn.net/m0_71974552/article/details/143327302

相关文章

  • 颜色选择器的简单实现(附完整代码)
    颜色选择器的简单实现使用渐变背景实现一个颜色选择器关键知识点HSV(Hue,Saturation,Value)使用渐变色实现色相选择器使用3个背景叠加实现Saturation(饱和度),Value(色调,纯度)的选择关键代码色相渐变背景background:linear-gradient(180deg,red0,#ff017%,#0......
  • 青否数字人直播带货的五大互动规则,给予商家的全新选择 !
    在直播带货的浪潮中,AI数字人正成为商家的新宠儿。这一趋势的出现,背后有着多方面的原因,让我们一探究竟。首先,从成本角度来看,AI数字人具有明显优势。与传统的真人主播相比,AI数字人无需支付高昂的底薪、提成和坑位费。商家只需一次性投入技术开发费用,或者按使用时间支付相对较......
  • 数据结构 之 哈夫曼树及其应用(八)
    提示:本节重点是哈夫曼树的构造文章目录哈夫曼树的基本概念举例※构造哈夫曼树基本思想※构造哈夫曼树过程哈夫曼树的应用1------用于最佳判断过程哈夫曼树应用2----用于通信编码哈夫曼编码总结哈夫曼树的基本概念路径长度:由树中一个结点到另一结点的分支构成这......
  • 算法与数据结构——计数排序
    计数排序计数排序(countingsort)通过统计元素数量来实现排序,通常应用于整数数组。简单实现给定一个长度为n的数组nums,其中的元素都是“非负整数”,计数排序的整体流程如下:遍历数组,找出其中最大的数组,记为m,然后创建一个长度为m+1的辅助数组counter。借助counter统计nums中各......
  • GaussDB的行存表与列存表的选择
    一、前言行存表和列存表是数据库中两种常见的数据存储方式。随着信息技术的飞速发展,数据存储和管理以及如何高效地存储和处理大量的数据已经成为了我们的一大挑战。为了解决这个问题,行存表与列存表应运而生,它们以其独特的优势在各个场景得到了高效的应用。GaussDB支持行、列存储......
  • 【数据结构与算法】图(Graph)
    文章目录图的逻辑结构一.图的定义二.图的基本概念和术语图的存储结构一.邻接矩阵(数组)二.邻接表(链式)三.十字链表四.邻接多重表五.边集数组图的遍历一.深度优先遍历二.广度优先遍历三.图的遍历与图的连通性图的逻辑结构在线性表中,数据元素之间是被串起来的,仅有线......
  • (1)程序设计与数据结构连续剧
    《程序设计与数据结构》C程序基本构成、变量定义与变量名规则C程序基本构成示例#include<stdio.h>//预处理指令,包含标准输入输出头文件//全局变量声明intglobal_variable=10;//函数原型声明intadd(inta,intb);intmain(){//局部变量定义......
  • DICOM 基础知识:深入理解DICOM数据结构与标签说明
    目录DICOM图像概念DICOM图像关键特性:DICOM文件结构常见数据元素:数据元素示例详解DICOM-VR数据类型说明DICOM标准支持的数据集结语     DICOM图像概念        DICOM(DigitalImagingandCommunicationsinMedicine)是一种用于存储、传输和处......
  • Milvus 与 Faiss:选择合适的向量数据库
    向量数据库Milvus和Faiss都是处理大规模向量数据的工具,尤其适用于需要相似性搜索的场景,比如推荐系统、图像检索和自然语言处理等。但它们各自的设计初衷和功能有所不同,适用于不同的使用场景。下面,我们从性能、功能特性、部署和使用难度、适用场景等方面对它们进行对比。......
  • httpsok:自动续期SSL证书的最佳选择!
    一、引言        在数字化时代,网站的安全性至关重要,而SSL证书是保护用户数据、提升网站信誉的关键。然而,证书的续期往往令人头痛。今天,我们为你介绍一款高效的SSL证书自动续期工具——httpsok,让你的证书管理变得轻松无忧。二、什么是httpsok?httpsok是一款专为网站......