数据结构之二元查找树转有序双向链表详解与示例(C/C++)

news2024/11/16 4:37:10

文章目录

    • 1. 二元查找树(BST)简介
    • 2. 有序双向链表(DLL)简介
    • 3. 二元查找树的实现
    • 4. 转换为有序双向链表的步骤
    • 5. C++实现代码
    • 6. C实现代码
    • 7. 效率与空间复杂度比较
    • 8. 结论

在这里插入图片描述


在数据结构与算法中,树和链表都是非常重要的数据结构。二元查找树(Binary Search Tree,简称BST)是一种特殊的二叉树,它具有很好的查找性能。而双向链表(Doubly Linked List)则是一种线性结构,它允许我们在两个方向上遍历。本文将详细介绍如何将一个二元查找树转换为有序双向链表。

1. 二元查找树(BST)简介

二元查找树是一种数据结构,其中每个节点最多有两个子节点:左子树节点值小于当前节点,右子树节点值大于当前节点。这种性质使得BST能够支持高效的查找操作。

2. 有序双向链表(DLL)简介

有序双向链表是一种链表结构,除了具有链表节点的基本特征外,它还具有指向前一个节点的指针,这使得链表节点可以双向访问。此外,链表节点按照节点值的顺序排列,形成有序链表。

3. 二元查找树的实现

首先定义二元查找树的节点结构体:

struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;
    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};

class BinarySearchTree {
public:
    BinarySearchTree() : root(nullptr) {}

    void insert(int val) {
        root = insertIntoTree(root, val);
    }

    TreeNode* insertIntoTree(TreeNode* node, int val) {
        if (node == nullptr) return new TreeNode(val);
        if (val < node->val) {
            node->left = insertIntoTree(node->left, val);
        } else {
            node->right = insertIntoTree(node->right, val);
        }
        return node;
    }

    // 这里可以添加更多的操作,例如查找、删除等...
};

4. 转换为有序双向链表的步骤

转换过程如下:

  1. 从二元查找树的根节点开始,进行中序遍历(左-根-右)。
  2. 遍历过程中,将节点转换为链表节点,并维护前一个节点和后一个节点的指针关系。
  3. 遍历完成后,头节点的前驱节点指向nullptr,最后一个节点的后继节点也指向nullptr,形成一个闭合的双向链表。

5. C++实现代码

下面是一个C++示例,展示如何实现二元查找树到有序双向链表的转换:

class Node {
public:
    int val;
    Node *prev;
    Node *next;
    Node(int x) : val(x), prev(nullptr), next(nullptr) {}
};

class BinarySearchTreeToDoublyLinkedList {
public:
    Node* convert(TreeNode* root) {
        if (root == nullptr) return nullptr;
        
        Node* head = nullptr, *prev = nullptr;
        convertTreeToList(root, &head, &prev);
        
        // 闭合双向链表
        if (prev != nullptr) {
            prev->next = head;
            head->prev = prev;
        }
        return head;
    }

    void convertTreeToList(TreeNode* node, Node** headRef, Node** prevRef) {
        if (node == nullptr) return;
        
        // 左子树转换
        convertTreeToList(node->left, headRef, prevRef);
        
        // 处理当前节点
        Node* newNode = new Node(node->val);
        if (*headRef == nullptr) {
            *headRef = newNode;
        } else {
            (*prevRef)->next = newNode;
            newNode->prev = *prevRef;
        }
        *prevRef = newNode;
        
        // 右子树转换
        convertTreeToList(node->right, headRef, prevRef);
    }
};

示例

假设我们有以下二元查找树:

    4
   / \
  2   5
 / \
1   3

通过上述代码转换后,我们得到以下有序双向链表:

1 <-> 2 <-> 3 <-> 4 <-> 5

6. C实现代码

以下是一个完整的C语言示例,展示了如何将二元查找树转换为有序双向链表:

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

// 二元查找树节点定义
typedef struct Node {
    int data;
    struct Node* left;
    struct Node* right;
} Node;

// 双向链表节点定义
typedef struct DListNode {
    int data;
    struct DListNode* prev;
    struct DListNode* next;
} DListNode;

// 创建双向链表节点
DListNode* createDListNode(int data) {
    DListNode* newNode = (DListNode*)malloc(sizeof(DListNode));
    newNode->data = data;
    newNode->prev = newNode->next = NULL;
    return newNode;
}

// 插入节点
Node* insert(Node* root, int data) {
    if (root == NULL) {
        return createNode(data);
    }
    if (data < root->data) {
        root->left = insert(root->left, data);
    } else if (data > root->data) {
        root->right = insert(root->right, data);
    }
    return root;
}

// 查找节点
Node* search(Node* root, int data) {
    if (root == NULL || root->data == data) {
        return root;
    }
    if (data < root->data) {
        return search(root->left, data);
    }
    return search(root->right, data);
}

// 将二元查找树转换为双向链表
DListNode* bstToDList(Node* root) {
    if (root == NULL) {
        return NULL;
    }
    
    DListNode* head = NULL;
    DListNode* tail = NULL;
    
    // 递归遍历树
    if (root->left != NULL) {
        head = bstToDList(root->left);
    }
    if (head != NULL) {
        head->next = createDListNode(root->data);
        head->next->prev = head;
    } else {
        head = createDListNode(root->data);
    }
    
    if (root->right != NULL) {
        tail = bstToDList(root->right);
    }
    if (tail != NULL) {
        tail->prev = head;
        head->next = tail;
    } else {
        tail = head;
    }
    
    return head;
}

// 打印双向链表
void printDList(DListNode* head) {
    DListNode* current = head;
    while (current != NULL) {
        printf("%d ", current->data);
        current = current->next;
    }
    printf("\n");
}

int main() {
    Node* root = NULL;
    root = insert(root, 50);
    insert(root, 30);
    insert(root, 70);
    insert(root, 20);
    insert(root, 40);
    insert(root, 60);
    insert(root, 80);

    printf("BST elements: ");
    Node* temp = root;
    while (temp != NULL) {
        printf("%d ", temp->data);
        temp = temp->right;
    }
    printf("\n");

    DListNode* dlist = bstToDList(root);
    printf("Doubly linked list elements: ");
    printDList(dlist);

    return 0;
}

7. 效率与空间复杂度比较

二元查找树:

  1. 插入、删除和查找的时间复杂度平均为O(log n),在最坏的情况下(树退化为链表)为O(n)。
  2. 空间复杂度为O(n),用于存储树中的节点。

有序双向链表:

  1. 插入和删除操作在O(1)时间复杂度内完成,因为每个节点都包含前驱和后继指针。
  2. 查找操作的时间复杂度为O(n),因为可能需要遍历整个链表。
  3. 空间复杂度同样为O(n),用于存储链表中的节点。

在实际应用中,二元查找树在搜索效率方面具有优势,特别是在数据量较大且分布均匀时。然而,当树高度不平衡时,其性能会显著下降。另一方面,有序双向链表在插入和删除操作上具有更好的性能保证,但查找操作效率较低。

8. 结论

二元查找树和有序双向链表各有优缺点,适用于不同的场景。二元查找树更适合需要频繁搜索的场景,而有序双向链表则在插入和删除操作更频繁的场景中表现更好。选择哪种数据结构取决于具体应用的需求和性能考量。

在实现时,二元查找树的结构简单,逻辑清晰,而有序双向链表需要额外的指针来维护前后关系,这可能会增加代码的复杂性。然而,有序双向链表的一个优点是它不会像二元查找树那样出现平衡问题,这使得它在某些应用中更加稳定。

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

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

相关文章

八股文之java基础

jdk9中对字符串进行了一个什么优化&#xff1f; jdk9之前 字符串的拼接通常都是使用进行拼接 但是的实现我们是基于stringbuilder进行的 这个过程通常比较低效 包含了创建stringbuilder对象 通过append方法去将stringbuilder对象进行拼接 最后使用tostring方法去转换成最终的…

C# 基础语法(一篇包学会的)

C#&#xff08;读作"C Sharp"&#xff09;是一种现代的、通用的面向对象编程语言&#xff0c;由微软公司开发。它结合了C和C的强大特性&#xff0c;并去掉了一些复杂性&#xff0c;使得开发者可以更加高效地编写代码。 一、入坑C# (一) 安装和设置 首先&#xff0c…

Modbus转BACnet/IP网关BA100-配硬件说明

在现代自动化系统中&#xff0c;不同设备和系统之间的通信至关重要&#xff0c;Modbus和BACnet/IP协议虽然各有优势&#xff0c;但它们之间的直接通信存在障碍。钡铼Modbus转BACnet/IP网关作为连接这两种协议的桥梁&#xff0c;允许不同系统之间的无缝数据交换。 一、Modbus转…

喜讯丨泰迪智能科技实力中标湖北民族大学数学与统计学院一流专业实验室建设项目

近日&#xff0c;泰迪智能科技凭借其卓越的技术实力与解决方案&#xff0c;在湖北民族大学数学与统计学院的一流专业实验室建设项目招标中脱颖而出&#xff0c;成功揽获该项目的建设权&#xff0c;中标项目金额达人民币355万元。 项目建设成果 一、实验室建设内容&#xff1a; …

提升无线网络安全:用Python脚本发现并修复WiFi安全问题

文章目录 概要环境准备技术细节3.1 实现原理3.2 创建python文件3.3 插入内容3.4 运行python脚本 加固建议4.1 选择强密码4.2 定期更换密码4.3 启用网络加密4.4 关闭WPS4.5 隐藏SSID4.6 限制连接设备 小结 概要 在本文中&#xff0c;我们将介绍并展示如何使用Python脚本来测试本…

数据结构(队列及其实现)

概念与结构 概念&#xff1a;只允许在⼀端进⾏插⼊数据操作&#xff0c;在另⼀端进⾏删除数据操作的特殊线性表&#xff0c; 队列具有先进先出FIFO(First In First Out)原则。 ⼊队列&#xff1a;进⾏插⼊操作的⼀端称为队尾 出队列&#xff1a;进⾏删除操作的⼀端称为队头…

TypeScript体操(二):Utility Type手写实现

目录 前言常用 Utility Types 及其实现Partial<T>Required<T>Readonly<T>Pick<T, K>Omit<T, K>Record<K, T>Exclude<T, U>Extract<T, U>NonNullable<T>ReturnType<T>InstanceType<T>Parameters<T>Con…

yolo5图片视频、摄像头推理demo

yolo5图片、视频推理demo 图片 import torch# 加载预训练模型 model torch.hub.load(./yolo5, custom, pathyolov5s.pt, sourcelocal)# 加载图片 img 1.jpg# 进行推理 results model(img)# 解析结果 detections results.xyxy[0].cpu().numpy() # [x1, y1, x2, y2, confid…

Windows下载、安装、部署Redis服务的详细流程

本文介绍在Windows电脑中&#xff0c;下载、安装、部署并运行Redis数据库服务的方法。 Redis&#xff08;Remote Dictionary Server&#xff09;是一个开源、高性能的键值存储系统&#xff0c;最初由Salvatore Sanfilippo在2009年发布&#xff0c;并由Redis Labs维护。Redis因其…

【爱上C++】list用法详解、模拟实现

文章目录 一&#xff1a;list介绍以及使用1.list介绍2.基本用法①list构造方式②list迭代器的使用③容量④元素访问⑤插入和删除⑥其他操作image.png 3.list与vector对比 二&#xff1a;list模拟实现1.基本框架2.节点结构体模板3.__list_iterator 结构体模板①模板参数说明②构…

如何在Ubuntu上安装并启动SSH服务(Windows连接)

在日常的开发和管理工作中&#xff0c;通过SSH&#xff08;Secure Shell&#xff09;连接到远程服务器是一个非常常见的需求。如果你在尝试通过SSH连接到你的Ubuntu系统时遇到了问题&#xff0c;可能是因为SSH服务未安装或未正确配置。本文将介绍如何在Ubuntu上安装并启动SSH服…

气膜工业仓储与气膜体育馆的配置区别—轻空间

气膜工业仓储和气膜体育馆在配置上有明显的区别&#xff0c;这主要是由于它们的使用功能和环境不同所导致的。 结构设计 气膜工业仓储&#xff1a; 主要设计为大跨度、大空间&#xff0c;以便容纳大量货物。 气膜体育馆&#xff1a; 设计注重支撑观众席、运动场地和相关设施&…

安全与便捷并行,打造高效易用的用户支付体验

在当今数字时代&#xff0c;快捷、安全的支付方式已经成为用户日常生活中不可或缺的一部分。不论是在线购物、订阅服务&#xff0c;还是线下消费&#xff0c;用户都期望享受流畅且安全的支付体验。作为开发者&#xff0c;选择适合的支付服务不仅关乎用户体验&#xff0c;更直接…

android13禁用某个usb设备

总纲 android13 rom 开发总纲说明 目录 1.前言 2.触摸设备查看 3.功能修改 3.1 禁用usb触摸 3.2 禁用usb键盘 3.3 禁用usb遥感 4.查看生效与否 5.彩蛋 1.前言 用户想要禁止使用某些usb设备,需要系统不能使用相关的usb设备,例如usb触摸屏,usb键盘,usb遥感等等usb…

收银系统源码-线上商城diy装修

线下线上一体化收银系统越来越受门店重视&#xff0c;尤其是连锁多门店&#xff0c;想通过线下线上相互带动&#xff0c;相互引流&#xff0c;提升门店营业额。商城商城如何装修呢&#xff1f; 1.收银系统开发语言 核心开发语言: PHP、HTML5、Dart后台接口: PHP7.3后合管理网…

【系统架构设计 每日一问】四 如何对关系型数据库及NoSql数据库选型

根据不同的业务需求和场景&#xff0c;选择适合的数据库类型至关重要。以下是一个优化后的表格展示&#xff0c;涵盖了管理型系统、大流量系统、日志型系统、搜索型系统、事务型系统、离线计算和实时计算七大类业务系统的数据库选型建议。先明确下NoSQL的分类 NoSQL数据库分类…

微信小程序开发--点击圆圈小问号弹注解tip 点击其他区域关闭(组件 w-tip 弹框在小圆圈的 上下左右 可以自己控制 )

引言 在微信小程序开发中&#xff0c;实现用户交互的多样性是提升用户体验的关键之一。本文将详细介绍如何在微信小程序中实现点击圆圈小问号弹出注解&#xff08;Tip&#xff09;的功能。这种功能常见于帮助信息、提示说明等场景&#xff0c;能够为用户提供即时的帮助和反馈。…

昇思25天学习打卡营第17天|LLM-基于MindSpore的GPT2文本摘要

打卡 目录 打卡 环境准备 准备阶段 数据加载与预处理 BertTokenizer 部分输出 模型构建 gpt2模型结构输出 训练流程 部分输出 部分输出2&#xff08;减少训练数据&#xff09; 推理流程 环境准备 pip install -i https://pypi.mirrors.ustc.edu.cn/simple mindspo…

两个数组的dp问题

目录 最长公共子序列 不相交的线 不同的子序列 通配符匹配 正则表达式匹配 交错字符串 两个字符串的最小ASCII删除和 最长重复子数组 声明&#xff1a;接下来主要使用动态规划来解决问题&#xff01;&#xff01;&#xff01; 最长公共子序列 题目 思路 根据经验题目…

项目笔记| 基于Arduino和IR2101的无刷直流电机控制器

本文介绍如何使用 Arduino UNO 板构建无传感器无刷直流 &#xff08;BLDC&#xff09; 电机控制器或简单的 ESC&#xff08;电子速度控制器&#xff09;。 无刷直流电机有两种类型&#xff1a;有传感器和无传感器。有感无刷直流电机内置3个霍尔效应传感器&#xff0c;这些传感…