数据结构之二元查找树转有序双向链表详解与示例(C/C++)
在数据结构与算法中,树和链表都是非常重要的数据结构。二元查找树(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. 转换为有序双向链表的步骤
转换过程如下:
- 从二元查找树的根节点开始,进行中序遍历(左-根-右)。
- 遍历过程中,将节点转换为链表节点,并维护前一个节点和后一个节点的指针关系。
- 遍历完成后,头节点的前驱节点指向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. 效率与空间复杂度比较
二元查找树:
- 插入、删除和查找的时间复杂度平均为O(log n),在最坏的情况下(树退化为链表)为O(n)。
- 空间复杂度为O(n),用于存储树中的节点。
有序双向链表:
- 插入和删除操作在O(1)时间复杂度内完成,因为每个节点都包含前驱和后继指针。
- 查找操作的时间复杂度为O(n),因为可能需要遍历整个链表。
- 空间复杂度同样为O(n),用于存储链表中的节点。
在实际应用中,二元查找树在搜索效率方面具有优势,特别是在数据量较大且分布均匀时。然而,当树高度不平衡时,其性能会显著下降。另一方面,有序双向链表在插入和删除操作上具有更好的性能保证,但查找操作效率较低。
8. 结论
二元查找树和有序双向链表各有优缺点,适用于不同的场景。二元查找树更适合需要频繁搜索的场景,而有序双向链表则在插入和删除操作更频繁的场景中表现更好。选择哪种数据结构取决于具体应用的需求和性能考量。
在实现时,二元查找树的结构简单,逻辑清晰,而有序双向链表需要额外的指针来维护前后关系,这可能会增加代码的复杂性。然而,有序双向链表的一个优点是它不会像二元查找树那样出现平衡问题,这使得它在某些应用中更加稳定。
原文地址:https://blog.csdn.net/qq_35320456/article/details/140588720
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!