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

文章目录

    • 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.xdnf.cn/news/1487179.html

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

相关文章

压测实操--kafka-consumer压测方案

作者&#xff1a;九月 环境信息&#xff1a; 操作系统centos7.9&#xff0c;kafka版本为hdp集群中的2.0版本。 Consumer相关参数 使用Kafka自带的kafka-consumer-perf-test.sh脚本进行压测&#xff0c;该脚本参数为&#xff1a; thread&#xff1a;测试时的单机线程数&…

数据结构(稀疏数组)

简介 稀疏数组是一种数据结构&#xff0c;用于有效地存储和处理那些大多数元素都是零或者重复值的数组。在稀疏数组中&#xff0c;只有非零或非重复的元素会被存储&#xff0c;从而节省内存空间。 案例引入 假如想把下面这张表存入文件&#xff0c;我们会怎么做&#xff1f;…

Ubuntu 修改源地址

注意事项&#xff1a;版本说明&#xff01;&#xff01;&#xff01; Ubuntu24.04的源地址配置文件发生改变。 不再使用以前的 sources.list 文件&#xff0c;该文件内容变成了一行注释&#xff1a; # Ubuntu sources have moved to /etc/apt/sources.list.d/ubuntu.sources…

U盘有盘符但是打不开?深度解析与双路径恢复策略

数字时代&#xff0c;U盘作为我们日常工作和生活中不可或缺的数据存储工具&#xff0c;其稳定性和可靠性直接关系到我们数据的安全。然而&#xff0c;当您遇到U盘已成功识别盘符&#xff0c;却无法正常访问的情况时&#xff0c;这无疑是一个令人头疼的问题。本文将围绕“U盘有盘…

我在百科荣创企业实践——简易函数信号发生器(5)

对于高职教师来说,必不可少的一个任务就是参加企业实践。这个暑假,本人也没闲着,报名参加了上海市电子信息类教师企业实践。7月8日到13日,有幸来到美丽的泉城济南,远离了上海的酷暑,走进了百科荣创科技发展有限公司。在这短短的一周时间里,我结合自己的教学经验和企业的…

【保姆级教程】油猴脚本的安装使用

目录 前言 一、油猴简介 1. 核心功能 2. 应用场景 3. 安全性与兼容性 4. 社区生态 二、教学开始&#xff08;嫌麻烦直接目录跳转开始学习&#xff09; 1.插件安装&#xff08;以Microsoft Edge浏览器为例&#xff09; 2.获取脚本 3.大展身手 三、扩展&#xff08;脚…

【23】Android高级知识之Window(四) - ThreadedRenderer

一、概述 在上一篇文章中已经讲了setView整个流程中&#xff0c;最开始的addToDisplay和WMS跨进程通信的整个过程做了什么。继文章Android基础知识之Window(二)&#xff0c;这算是另外一个分支了&#xff0c;接着讲分析在performTraversals的三个操作中&#xff0c;最后触发pe…

Ansible的脚本-----playbook剧本【上】

目录 1.playbook剧本组成 2.playbook剧本实战演练 2.1 实战演练一&#xff1a;给被管理主机安装httpd服务 2.2 实战演练二&#xff1a;定义、引用变量 2.3 实战演练三&#xff1a;指定远程主机sudo切换用户 2.4 实战演练四&#xff1a;when条件判断 2.5 实战演练五&…

一家银行数据库的六年攻坚战

前沿科技&#xff0c;数智经济 文&#xff5c;白 鸽 编&#xff5c;王一粟 从传统的商业数据库Oracle&#xff0c;到后来加入的MySQL数据库&#xff0c;再到现如今的分布式数据库&#xff0c;中国金融行业数据库的转型升级走过了多年时间。 “2018年&#xff0c;我们提出…

《你敢不学习?》numpy库——细细学<2>

续接上集: 1、reshape函数&#xff1a;重塑数组的形状 改变数组的维度 其语法为 numpy.reshape(arr, newshape, orderC) 如下图所示 首先生成一个1到17不包括17的16个元素的数组&#xff0c;然后对这个数组进行重塑&#xff0c;使其成为4行4列的二维数组&#xff0c;注意&…

【Micropython入门】Thoony安装并烧录固件到ESP32

文章目录 前言Thonny IDE 介绍Thoony的下载烧录固件到ESP32下载固件烧录固件烧录时的小问题 总结 前言 MicroPython 是一款为微控制器设计的精简版 Python 解释器&#xff0c;它以其简洁和强大的特性赢得了众多嵌入式开发者的青睐。ESP32 是一款功能强大且价格低廉的微控制器&…

React开发者并不存在

根本就没有所谓的React开发者 — 永远不要这样称呼自己。 这是许多软件开发者犯的一个巨大错误&#xff0c;浪费了你大量时间。 专注于工具而非概念。忽视了大局。 React只是一个JavaScript工具。JavaScript只是一个计算工具。计算只是一个解决问题的工具。 当我刚开始编码时&a…

hugging face 使用教程———快速入门

概述 本篇存在的意义是快速介绍hugging face使用&#xff0c;梳理主要部件&#xff0c;梳理易混淆概念。原因是&#xff1a;目前hugging face的使用&#xff0c;官方放在了3个地方&#xff08;参考链接部分&#xff09;&#xff1a;使用文档、NLP教程、Transformers git的readm…

PDF转Word后不能修改怎么办?是什么原因呢?

平时在生活中&#xff0c;很多朋友都会有将PDF转换成Word文档的需求&#xff0c;因为一般情况下PDF文件是不能直接编辑修改的&#xff0c;所以只能通过这种方式来解决问题。但是近期&#xff0c;有部分用户在后台反馈说PDF转Word后不能修改怎么办呢&#xff1f;其实这个问题也是…

前端页面:用户交互持续时间跟踪(duration)user-interaction-tracker

引言 在用户至上的时代&#xff0c;精准把握用户行为已成为产品优化的关键。本文将详细介绍 user-interaction-tracker 库&#xff0c;它提供了一种高效的解决方案&#xff0c;用于跟踪用户交互的持续时间&#xff0c;并提升项目埋点的效率。通过本文&#xff0c;你将了解到如…

云仓如何改变传统仓储模式?

云仓&#xff0c;即云仓储&#xff0c;是一种基于互联网技术的现代仓储模式&#xff0c;与传统的仓储模式相比&#xff0c;它在多个方面进行了创新和优化&#xff0c;包括&#xff1a; ———————————————————— 1、数据管理与实时监控&#xff1a; 云仓储利…

每日一题 LeetCode03 无重复字符的最长字串

1.题目描述 给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的最长字串的长度。 2 思路 可以用两个指针, 滑动窗口的思想来做这道题,即定义两个指针.一个left和一个right 并且用一个set容器,一个length , 一个maxlength来记录, 让right往右走,并且用一个set容器来…

【数据结构】链表(单链表实现 + 详解 + 原码)

&#x1f387;&#x1f389;&#x1f389;&#x1f389;点进来你就是我的人了 博主主页&#xff1a;&#x1f648;&#x1f648;&#x1f648;戳一戳&#xff0c;欢迎大佬指点&#xff01; 人生格言: 当你的才华撑不起你的野心的时候,你就应该静下心来学习! 欢迎志同道合的朋友…

Spring Boot配置文件的语法规则

主要介绍两种配置文件的语法和格式&#xff0c;properties和yml 目录 1.配置文件的作用 2.创建配置文件 3.properties语法 4.yml语法 5.配置文件格式 1.配置文件的作用 对于配置文件&#xff0c;也有独立的文件夹去存放&#xff0c;主要用来存放一些需要经过变动的数据&a…

IEDA怎么把springboot项目 启动多个

利用Idea提供的Edit Configurations配置应用参数。 点击Modify Options进行添加应用参数&#xff1a; 确保这里勾选