从二叉排序树到平衡二叉树再到红黑树系列1

2024-05-16 05:08

本文主要是介绍从二叉排序树到平衡二叉树再到红黑树系列1,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

最近想写一些关于红黑树的博客,既想写的全面,又直观,但是又不知道从哪里入手。斟酌再三,还是从最简单的二叉排序树开始写。

二叉排序树(Binary Sort Tree)又叫二叉查找树。它是一种特殊结构的二叉树。其或为空树,或具备下列性质:

(1)若它的左子树不为空,则左子树上所有结点的值均小于它的根节点的值。

(2)若它的右子树不为空,则左子树上所有结点的值均大于它的根节点的值。

显然,它的中序遍历就是一个递增的有序序列。

定理:在一棵高度为h的二叉搜索树上,动态集合上的操作serach,minmum,maxmum,successor,predecessor可以在O(h)时间内完成。

即查找给定结点,查找最大值最小值,以及给定结点的前驱后继,他们均可以在O(h)时间内完成。具体见算法导论第三版第12章


插入和删除

为了保持二叉排序树的有序性质,插入和删除操作会引起二叉排序树的动态变化,由于插入的结点总是成为叶子节点,而删除的结点还要修改其左右子树的指向,所以插入一个结点相比删除一个结点的处理相对复杂。


插入:首先要查找要插入节点的位置,然后插入即可。

删除:删除一个结点node要考虑三种情况:

1 node没有左右孩子节点,这种情况最为简单,直接删除它,再将父节点原来指向node的指针置NULL就行。

2 node只有一个孩子,那么将这个孩子提升到node的位置上,并修改node的父节点即可。

3 node有两个孩子。那么寻找node的后继y(中序遍历的下一个元素),让y占据node的位置。同时node原来的右子树部分称为y的新的右子树,

node的左子树称为y的新的左子树。简单点说:就是交换要删除的node节点与node节点右子树的最左边节点位置。这样满足中序遍历递增性质。

  时刻记住二叉排序树中序是递增的,当删除一个结点时,该结点的位置就要被其后继,也就是要删除节点右子树的中序遍历第一个节点(同时也是右子树的最左节点)

下面举例说明,如图所示:



下面直接贴代码:

#include<stdio.h>
#include<stdlib.h>
#define error -1
#define OK 1
typedef struct BSTNode
{struct BSTNode *lchild,*rchild;int data;
}BSTNode,*BSTree;
/**
*@auther:jungege 2015 5.13
**/
//插入元素到二叉排序树中
BSTree InsertBST(BSTree bst,int key)
{BSTree node,parent,new_node;if(bst == NULL)//为空树,创建根节点{bst = (BSTree)malloc(sizeof(BSTNode));bst->data = key;bst->lchild = NULL;bst->rchild = NULL;return bst;}node = bst;//找到插入位置,保存bstwhile(node != NULL){if(node->data == key)return bst;else{parent = node;//记录父节点,最后保存的是插入节点的父节点if(node->data < key)node = node->rchild;elsenode = node->lchild;}}//创建节点,将新key插入到指定位置,插入的节点一定是叶子节点new_node = (BSTree) malloc (sizeof(BSTNode));if(parent->data < key)//插入到parent右孩子parent->rchild = new_node;elseparent->lchild = new_node;new_node->data = key;new_node->lchild = NULL;new_node->rchild = NULL;return bst;
}
//删除二叉排序树中元素
int deleteBST(BSTree bst, int key)
{BSTree node=bst,pre=NULL,nearestkey,nearest_pre;if(bst == NULL)return -1;//空树//寻找删除节点位置while(node != NULL && node->data != key){pre = node;//保存父结点if(node->data > key)node = node->lchild;elsenode = node->rchild;}if(node == NULL)return 0;//没有要删除的节点//找到要删除的节点node和其父节点preif((node->lchild != NULL) && (node->rchild !=NULL))//***要删除节点有左右孩子{nearestkey = node->rchild;nearest_pre = node;while(nearestkey->lchild != NULL){nearest_pre = nearestkey;//pre指向被nearestkey节点的父节点nearestkey = nearestkey->lchild;}node->data = nearestkey->data;//直接赋值,避免交换节点//处理nearestkey节点的子节点,显然其为最左节点,所以不存在左子树,只需处理其右子树nearest_pre->lchild = nearestkey->rchild;free(nearestkey);}else if(node->lchild == NULL && node->rchild !=NULL)//***要删除节点只有右孩子{if(pre->rchild == node)// node是其父节点的右子树pre->rchild = node->rchild;else pre->lchild = node->rchild;free(node);//释放节点}else if(node->lchild != NULL && node->rchild ==NULL)//***要删除节点只有左孩子{if(pre->rchild == node)// node是其父节点的右子树pre->rchild = node->lchild;else pre->lchild = node->lchild;free(node);//释放节点}else//***被删除节点没有子树{if(pre->lchild == node)pre->lchild = NULL;if(pre->rchild == node)pre->rchild = NULL;free(node);}return 1;//删除成功
}//查找
int SearchBST(BSTree bst,int key)
{printf("%d ",bst->data);if(bst==NULL)return error;if(bst->data == key)return OK;else if(key < bst->data)SearchBST(bst->lchild,key);elseSearchBST(bst->rchild,key);
}//中序遍历
void inorder(BSTree root)
{if(root){inorder(root->lchild);printf("%d ",root->data);inorder(root->rchild);}
}void main()
{int a[10]={3,2,6,5,1,9,7,7,10,8};int i;BSTree root=NULL;//初始化非常重要,不然插入时就无法判断树空//按数组构建二叉排序树for(i=0;i<10;i++)root = InsertBST(root,a[i]);//中序遍历inorder(root);printf("\n删除节点6:");deleteBST(root,6);printf("\n删除节点6后中序遍历:");inorder(root);printf("\n");
}


运行结果如下:



这篇关于从二叉排序树到平衡二叉树再到红黑树系列1的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/993963

相关文章

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

【数据结构】——原来排序算法搞懂这些就行,轻松拿捏

前言:快速排序的实现最重要的是找基准值,下面让我们来了解如何实现找基准值 基准值的注释:在快排的过程中,每一次我们要取一个元素作为枢纽值,以这个数字来将序列划分为两部分。 在此我们采用三数取中法,也就是取左端、中间、右端三个数,然后进行排序,将中间数作为枢纽值。 快速排序实现主框架: //快速排序 void QuickSort(int* arr, int left, int rig

usaco 1.3 Mixing Milk (结构体排序 qsort) and hdu 2020(sort)

到了这题学会了结构体排序 于是回去修改了 1.2 milking cows 的算法~ 结构体排序核心: 1.结构体定义 struct Milk{int price;int milks;}milk[5000]; 2.自定义的比较函数,若返回值为正,qsort 函数判定a>b ;为负,a<b;为0,a==b; int milkcmp(const void *va,c

科研绘图系列:R语言扩展物种堆积图(Extended Stacked Barplot)

介绍 R语言的扩展物种堆积图是一种数据可视化工具,它不仅展示了物种的堆积结果,还整合了不同样本分组之间的差异性分析结果。这种图形表示方法能够直观地比较不同物种在各个分组中的显著性差异,为研究者提供了一种有效的数据解读方式。 加载R包 knitr::opts_chunk$set(warning = F, message = F)library(tidyverse)library(phyl

hdu 1285(拓扑排序)

题意: 给各个队间的胜负关系,让排名次,名词相同按从小到大排。 解析: 拓扑排序是应用于有向无回路图(Direct Acyclic Graph,简称DAG)上的一种排序方式,对一个有向无回路图进行拓扑排序后,所有的顶点形成一个序列,对所有边(u,v),满足u 在v 的前面。该序列说明了顶点表示的事件或状态发生的整体顺序。比较经典的是在工程活动上,某些工程完成后,另一些工程才能继续,此时

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入(Embedding)方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节:嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以被看作是自然语言处理中的“翻译机”,它将文本中的单词或短语转换成计算机能够理解的数学形式,即向量。 正如翻译机将一种语言

flume系列之:查看flume系统日志、查看统计flume日志类型、查看flume日志

遍历指定目录下多个文件查找指定内容 服务器系统日志会记录flume相关日志 cat /var/log/messages |grep -i oom 查找系统日志中关于flume的指定日志 import osdef search_string_in_files(directory, search_string):count = 0

《数据结构(C语言版)第二版》第八章-排序(8.3-交换排序、8.4-选择排序)

8.3 交换排序 8.3.1 冒泡排序 【算法特点】 (1) 稳定排序。 (2) 可用于链式存储结构。 (3) 移动记录次数较多,算法平均时间性能比直接插入排序差。当初始记录无序,n较大时, 此算法不宜采用。 #include <stdio.h>#include <stdlib.h>#define MAXSIZE 26typedef int KeyType;typedef char In

leetcode105 从前序与中序遍历序列构造二叉树

根据一棵树的前序遍历与中序遍历构造二叉树。 注意: 你可以假设树中没有重复的元素。 例如,给出 前序遍历 preorder = [3,9,20,15,7]中序遍历 inorder = [9,3,15,20,7] 返回如下的二叉树: 3/ \9 20/ \15 7   class Solution {public TreeNode buildTree(int[] pr

【软考】希尔排序算法分析

目录 1. c代码2. 运行截图3. 运行解析 1. c代码 #include <stdio.h>#include <stdlib.h> void shellSort(int data[], int n){// 划分的数组,例如8个数则为[4, 2, 1]int *delta;int k;// i控制delta的轮次int i;// 临时变量,换值int temp;in