二叉排序树的基本操作以及应用

关于二叉排序树的创建查找删除功能实现及随机生成二叉排序树(作者mml lwx)

二叉排序树的建立对于查找和删除的实现更加容易实现,它的速度更快,因而它对数据的查找删除十分重要,所以我们应该掌握这种二叉排序树。

二叉排序树的建立,查找,插入,删除功能及其实现从大到小的遍历的主要思路

使用的数据结构

typedef struct BiTNode {
    int   data;
    struct BiTNode* lchild, * rchild;
} BiTNode, * BiTree;

使用int型的数据作为节点所储存的数据,再分别设置两指针指向节点的左右孩子。

二叉排序树的建立和插入

二叉排序树的插入的主要思路是根据二叉排序树的性质(以树根为起始点,左子树的值比树根的值小,而右子树的值比树根的值大)使用递归,当插入数据大于节点数据时进入右子树否则进入左子树,当前节点为空时建立所插入的节点。下面给出函数代码

void charu(BiTree& T, int i)
{
    if (T == NULL)
    {
        T = new BiTNode;
        T->data = i;
        T->lchild = T->rchild = NULL;
        return;
    }
    if (i > T->data)
    {
        charu(T->rchild, i);
    }
    if (i < T->data)
    {
        charu(T->lchild, i);
    }
}

在二叉排序树插入的基础上树的建立就变得简单许多只需反复调用插入函数将数据一个个插入到树中即可完成二叉排序树的建立。下面给出代码

cin >> e;
while (e!=-1)
{
    charu(T, e);
    cin >> e;
}

二叉排序树的查找功能

对于二叉排序树的查找也不难,只需将树进行遍历,当前节点等于所要查找的数据时,返回找到的节点即可
下面给出代码

BiTree find(BiTree& T, int k) {
    if (!T)
        return NULL;//空树返回假
    if (T->data == k)
        return T;//找到返回真
    if (T->data >= k)
        return find(T->lchild, k);//去左子树里面找
    if (T->data < k)
        return find(T->rchild, k);//去右子树里面找
}

二叉排序树的删除

对于二叉排序树的删除操作比较复杂,为什么呢?因为对于一个节点来说根据它左右子树的关系可分为4种情况下面我将一一罗列。

1)删除节点为叶子节点,即左右孩子节点都为空。

该种情况将该节点直接删除即可,如图所示
图片来源于网络
图片来源于网络

2)删除节点为单孩子节点,即左右孩子其中一个为空。

该种情况直接将该节点的孩子替代该节点即可,如图所示
图片来源于网络

图片来源于网络
图片来源于网络

3)删除节点为双孩子节点,即左右孩子都存在。

该种情况因为要保存二叉排序树的特性即以树根为起始点,左子树的值比树根的值小,而右子树的值比树根的值大,所以将根节点删除需要将右子树中的最小节点代替根(或左子树最大节点)。如图所示
图片来源于网络
图片来源于网络
下面给出实现代码,因为删除中需记录双亲节点所以查找的函数需略微改变。

BiTree find(BiTree& T, int k, BiTree& parent) {
    if (!T) return NULL; // 空树返回假
    if (T->data == k) return T; // 找到返回真
    parent = T; // 更新父节点
    if (T->data >= k) return find(T->lchild, k, parent); // 去左子树里面找
    return find(T->rchild, k, parent); // 去右子树里面找
}

void Delete1(BiTree& parent, BiTree& q) {
    if (q == NULL) return; // 空树不做处理

    // 情况1: 是叶子节点
    if (q->lchild == NULL && q->rchild == NULL) {
        if (parent != NULL) {
            if (parent->lchild == q) {
                parent->lchild = NULL;
            }
            else {
                parent->rchild = NULL;
            }
        }
        delete q;
        q = NULL;
        return;
    }

    // 情况2: 只有一个子节点
    if (q->lchild == NULL) {
        if (parent != NULL) {
            if (parent->lchild == q) {
                parent->lchild = q->rchild;
            }
            else {
                if (parent->lchild == q) {
                    parent->lchild = q->rchild;
                }
                else {
                    parent->rchild = q->rchild;
                }
            }
            delete q;
            q = NULL;
            return;
        }

        // 情况3: 有两个子节点
        if (q->lchild != NULL && q->rchild != NULL) {
            BiTree temp = q->rchild;
            while (temp->lchild != NULL) {
                temp = temp->lchild; // 找到右子树中的最小节点
            }
            q->data = temp->data; // 用找到的最小节点的数据替换要删除的节点
            Delete1(q, temp); // 删除找到的最小节点
        }
    }
}
完整代码实现
#include <iostream>
#include<stack>
using namespace std;
typedef struct BiTNode {
    int   data;
    struct BiTNode* lchild, * rchild;
} BiTNode, * BiTree;
void charu(BiTree& T, int i);
void Tree(BiTree& T, stack<BiTree>& s);
void Delete1(BiTree& parent, BiTree& q);
BiTree find(BiTree& T, int k, BiTree& parent);
int main()
{
    stack<BiTree> s;
    BiTree T = NULL, q, parent = NULL;
    int e, k;
    cout << "输入数字:";
    cin >> e;
    while (e != -1)
    {
        charu(T, e);
        cin >> e;
    }
    Tree(T, s);
    cout << "从大到小进行遍历:";
    while (!s.empty())
    {
        BiTree p;
        p = s.top();
        cout << p->data << ' ';
        s.pop();
    }
    cout << '\n';
    cout << "选择要删除的数:";
    cin >> k;
    q = find(T, k, parent);
    Delete1(parent, q);
    Tree(T, s);
    cout << "删除后的结果";
    while (!s.empty())
    {
        BiTree p;
        p = s.top();
        cout << p->data << ' ';
        s.pop();
    }
    return 0;
}
void charu(BiTree& T, int i)
{
    if (T == NULL)
    {
        T = new BiTNode;
        T->data = i;
        T->lchild = T->rchild = NULL;
        return;
    }
    if (i > T->data)
    {
        charu(T->rchild, i);
    }
    if (i < T->data)
    {
        charu(T->lchild, i);
    }
}
void Tree(BiTree& T, stack<BiTree>& s)
{
    if (T != NULL) {
        Tree(T->lchild, s);
        s.push(T);
        Tree(T->rchild, s);
    }
}
BiTree find(BiTree& T, int k, BiTree& parent) {
    if (!T) return NULL; // 空树返回假
    if (T->data == k) return T; // 找到返回真
    parent = T; // 更新父节点
    if (T->data >= k) return find(T->lchild, k, parent); // 去左子树里面找
    return find(T->rchild, k, parent); // 去右子树里面找
}

void Delete1(BiTree& parent, BiTree& q) {
    if (q == NULL) return; // 空树不做处理

    // 情况1: 是叶子节点
    if (q->lchild == NULL && q->rchild == NULL) {
        if (parent != NULL) {
            if (parent->lchild == q) {
                parent->lchild = NULL;
            }
            else {
                parent->rchild = NULL;
            }
        }
        delete q;
        q = NULL;
        return;
    }

    // 情况2: 只有一个子节点
    if (q->lchild == NULL) {
        if (parent != NULL) {
            if (parent->lchild == q) {
                parent->lchild = q->rchild;
            }
            else {
                if (parent->lchild == q) {
                    parent->lchild = q->rchild;
                }
                else {
                    parent->rchild = q->rchild;
                }
            }
            delete q;
            q = NULL;
            return;
        }

        // 情况3: 有两个子节点
        if (q->lchild != NULL && q->rchild != NULL) {
            BiTree temp = q->rchild;
            while (temp->lchild != NULL) {
                temp = temp->lchild; // 找到右子树中的最小节点
            }
            q->data = temp->data; // 用找到的最小节点的数据替换要删除的节点
            Delete1(q, temp); // 删除找到的最小节点
        }
    }
}

代码运行结果如图

![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/d12645cdd66d4db2bd9805b34adc8f3b.png在这里插入图片描述

对于第一个模块的总结:由于对于树我们常采用递归的方式进行操作,常常会有新的节点创建和删除对于创建节点时我们切记要将新建节点的左右孩子设为空,否则在其他函数的条件判定时会出现错误;同时对于删除节点时不仅仅使用delete BiTree就行,delete尽能消除节点所占的内存不能将指针指为空,所以切记在删除指针时需要将指针设为NULL

随机10万个一定范围的数,建立一个二叉排序树,求高度,再从中随机查找1万个数,统计查找这些数的平均比较次数,并且计算该树的ASL

我们使用 srand(static_cast< double>(time(NULL)))来初始化随机数种子,再定义两个上下限 const double MIN_VALUE = 0;const double MAX_VALUE =100000;然后生成十万个随机数并插入到BST中

for (double i = 0; i < MAX_VALUE; ++i) { double randomValue = MIN_VALUE + (rand() / (RAND_MAX + 1.0)* (MAX_VALUE - MIN_VALUE); InsertBst(T, randomValue); } 来建立一个二叉排序树,
而对于求高度只需要通过递归求出左右子树最大的那个值再加一就能求得

int Height(BiTree T) {
	if (T == NULL) return 0;
	int m = Height(T->lchild);
	int n = Height(T->rchild);
	return max(m, n) + 1;
}

对于查找1万个随机数,统计查找这些数的平均比较次数,首先我们要在1到10w中生成1万个随机数,然后在searchBST函数中定义一个compareCount用来统计比较次数每次调用都加上compareCount,最后将总的比较次数除于1万即可

//计算查找1万个随机数的平均比较次数
	double totalCompareCount = 0;
	for (double i = 0; i <10000; ++i) {
		double randomValue = MIN_VALUE + (rand() / (RAND_MAX + 1.0)) * (MAX_VALUE - MIN_VALUE);
		double compareCount = 0;
		SearchBST(T, randomValue, compareCount);
		totalCompareCount += compareCount;
}
        double average1=totalCompareCount/10000;
BiTNode* SearchBST(BiTree T, ElemType key, double& compareCount) {
	compareCount++; // 每次调用函数时增加比较次数
	if (T == NULL) return NULL;
	if (T->data == key) return T;
	else if (T->data > key) return SearchBST(T->lchild, key, compareCount);
	else return SearchBST(T->rchild, key, compareCount);
}

那么对于求这整棵数的ASL就类似上面求查找1w个数平均查找次数了,我们只需要将随机数的生成变成10万个

// 计算树的ASL
	double totalCompareCount1=0;
	for (double i = 0; i < MAX_VALUE; ++i) {
		double randomValue1 = MIN_VALUE + (rand() / (RAND_MAX + 1.0)) * (MAX_VALUE - MIN_VALUE);
		double compareCount1 = 0;
		SearchBST(T, randomValue1, compareCount1);
		totalCompareCount1 += compareCount1;
	}
	double average2=totalCompareCount1/100000;

具体代码实现

#include<iostream>
#include <random>
#include<ctime>
#include<stack>
using namespace std;
typedef double ElemType;
typedef struct BiTNode {
	ElemType   data;
	struct BiTNode* lchild, * rchild;
} BiTNode, * BiTree;

BiTNode* CreateNode(ElemType key);
BiTNode* SearchBST(BiTree T, ElemType key, double& compareCount);
BiTNode* InsertBst(BiTree& T, ElemType key);
int Height(BiTree T);
int main()
{
	srand(static_cast< double>(time(NULL))); // 初始化随机数种子
	const  double MIN_VALUE = 0;
	const  double MAX_VALUE = 100000;
	BiTree T = NULL;
	// 生成十万个随机数并插入到BST中
	for (double i = 0; i < MAX_VALUE; ++i) {
		double randomValue = MIN_VALUE + (rand() / (RAND_MAX + 1.0)) * (MAX_VALUE - MIN_VALUE);
		InsertBst(T, randomValue);
	}
	
	// 查找随机数并统计比较次数
	double totalCompareCount = 0;
	for (double i = 0; i <10000; ++i) {
		double randomValue = MIN_VALUE + (rand() / (RAND_MAX + 1.0)) * (MAX_VALUE - MIN_VALUE);
		double compareCount = 0;
		SearchBST(T, randomValue, compareCount);
		totalCompareCount += compareCount;
		
	}
	double average1=totalCompareCount/10000;
	// 计算树的ASL
	double totalCompareCount1=0;
	for (double i = 0; i < MAX_VALUE; ++i) {
		double randomValue1 = MIN_VALUE + (rand() / (RAND_MAX + 1.0)) * (MAX_VALUE - MIN_VALUE);
		double compareCount1 = 0;
		SearchBST(T, randomValue1, compareCount1);
		totalCompareCount1 += compareCount1;
	}
	double average2=totalCompareCount1/100000;
	cout << "Height of the BST: " << Height(T) << endl;
	
	
	cout << "Average compare count: " << average1<< endl;
	cout << "Average Search Length (ASL): " << average2 << endl;
	return 0;
}

BiTNode* CreateNode(ElemType key) {
	BiTree T = new BiTNode;
	T->data = key;
	T->lchild = NULL;
	T->rchild = NULL;
	return T;
}

BiTNode* SearchBST(BiTree T, ElemType key, double& compareCount) {
	compareCount++; // 每次调用函数时增加比较次数
	if (T == NULL) return NULL;
	if (T->data == key) return T;
	else if (T->data > key) return SearchBST(T->lchild, key, compareCount);
	else return SearchBST(T->rchild, key, compareCount);
}

BiTNode* InsertBst(BiTree& T, ElemType key) {
	if (T == NULL) {
		T = CreateNode(key);
		return T;
	}
	if (T->data == key) return T;
	if (T->data < key) return InsertBst(T->rchild, key);
	return InsertBst(T->lchild, key);
}

int Height(BiTree T) {
	if (T == NULL) return 0;
	int m = Height(T->lchild);
	int n = Height(T->rchild);
	return max(m, n) + 1;
}

代码运行结果如下

在这里插入图片描述
注:该代码需要在小熊猫c++中运行

  • 24
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值