二叉搜索树建立、插入、删除、前继节点、后继节点之c++实现

一、前言

一直以来,都对树有关的东西望而却步。以前每次说要看一看,都因为惰性,时间就那么荒废掉了。今天下个决心,决定好好的数据结构中的东西看一下。不知道看这篇文章的你,是不是和我有同样的感受,空有一颗努力的心,却迟迟没有付出行动。如果是的话,如果也想好好的把树的知识巩固一下的话,就让我们一起好好儿地把知识点过一遍吧。本文争取让看完的每一个没有基础的同学,都能有所收获。在正文开始前,先给自己加个油。加油(^ω^)

二、二叉搜索树的定义

二叉搜索树是指,对于某一个节点而言,它左边的节点都小于或等于它。而它右边的节点都大于或等于它。借用一下网上的图片(懒得自己画图了,请见谅)。如下图所示,是一个二叉搜索树,大家可以自己找几个节点试一试验证一下这个定义。

三、二叉搜索树有关的常见的操作

1、建立;

2、插入一个节点;

3、删除一个节点;

4、找出最小和最大的节点;

5、找出某个节点的前继节点和后继节点。

前面4条所做的工作比较好理解,但要理解第一条所做的工作,则需要明白什么是前继节点和后继节点。下面我们就一起回顾一下什么是前继节点,什么是后继节点把。\(^o^)/~,加油!

前继节点:某个节点的前继节点,是指比该节点小的所有节点中的最大的一个节点。如在上图中,比节点“8”小的节点有很多个,但是最大的一个节点为“7”,那么节点“7”则为节点“8”的前继节点;

后继节点:某个节点的后继节点,是指比该节点大的所有节点中的最小的一个节点。如在上如中,比节点“1”大的节点有很多歌,但是最小的一个节点为“3”,那么节点“3”则为节点“1”的后继节点。

【插入】下面我们来分别理解一下,每种操作实现的具体步骤。建立一个二叉搜索树,其实是插入n个节点。而插入一个节点,我们是这么做的:

  1. 如果一个节点小于根节点,且根节点的左孩子为空的话,那么直接将该节点作为根节点的左孩子;
  2. 如果一个节点小于根节点,但是根节点的左孩子不为的话,那么我们将根节点的左孩子作为新的根节点,递归进行1、2两步。
  3. 同理,我们可以知道该怎么处理一个节点大于根节点的情况。
  4. 在这里有一个特殊的情况需要说明一下,那就是如果碰到重复的节点,本文采用软插入和软删除的办法。即我们设置一个变量用来表示该节点出现的次数,如果要插入的节点已经在二叉搜索树中存在的话,那么我们只需要把这个节点出现的次数变量加1。

如下图所示

【最大值&最小值】

找一个二叉树的最大值和最小值比较简单,因为二叉树的最小值肯定在二叉树的最左边,最大值肯定在二叉树的最右边。我们只需要使用一个循环,一直分别循环到节点的左节点或右节点为空,则可以得到二叉树的最大值和最小值了。

如上图中,最小节点为2,最大节点为18.

【前继节点】找一个节点的前继节点,我们分为两种情况

  • 若该节点有左孩子,那么前继节点则为以该节点的左孩子为根节点所寻找到的最大值;
  • 若该节点没有左孩子,那么前继节点需要往上寻找节点为右孩子。

如上图中,12的前继节点为10。

【后继节点】找一个节点的后继节点,我们分为两种情况

  • 若该节点有右孩子,那么前继节点则为以该节点的左孩子为根节点所寻找到的最小值;
  • 若该节点没有右孩子,那么前继节点需要往上寻找节点为左孩子。

如上图中,3的后继节点为4。

【删除】删除一个节点,我们也分为三种情况

  1. 如果待删除的节点即没有左孩子,也没有右孩子的话,则直接删除该节点,但是请注意需要将相应的父节点的修改。如:如果待删除的节点是父节点的左孩子的话,那么需要将父节点的左孩子节点置为空;
  2. 如果待删除的节点只有一个孩子,那么直接用该孩子代替这个节点。这里,同样需要注意修改父节点。即,如果待删除是父节点的左孩子,且它只有一个右孩子,那么删除这个节点需要将父节点的左节点指向待删除节点的右孩子;
  3. 如果待删除的节点有两个孩子的话,我们可以选择使用该节点的前继节点或者后继节点来代替该节点。大家可以在笔下试一试,就会发现两种替代方式,都能保证更新之后的二叉树依然满足二叉搜索树的定义。

如下图所示

完整代码

#include "stdafx.h"
#include <string>
#include <stdlib.h>
#include <iostream>

using namespace std;

typedef struct Node
{
	int key;                //键值
	struct Node *left;		//左节点
	struct Node *right;     //右节点
	struct Node *father;	//父节点
	int times;              //节点出现的次数
} Node, *pNode;

void creatBinarySearchTree(pNode &pBSTTree, int *ptr, int len);
void insertNode(pNode &pBSTTree, int value);
void mallocInitNode(pNode &pInsertNode, int value);
pNode findMinNode(pNode &pTree);
pNode findMaxNode(pNode &pTree);
pNode findPredecessor(pNode &pSearchNode);
pNode findSuccessor(pNode &pSearchNode);
void deleteNode(pNode& pdeleteNode);
void changeFatherChildNode(pNode& pdeleteNode, pNode& pNewChildNode);

int main()
{
	int a[] = {15,15, 6, 18, 3, 7, 17, 20, 2, 4, 13, 9};
	int len = sizeof(a) / sizeof(int);
	pNode pBSTTree = NULL;
	pNode pPreNode = NULL;
	pNode pSuccessor = NULL;

	/* 创建二叉查找树 */
	creatBinarySearchTree(pBSTTree, a, len);

	/* 寻找二叉查找树中的最大值 */
	cout << "最小值的节点为:" << findMinNode(pBSTTree)->key << endl;
	cout << "最大值的节点为:" << findMaxNode(pBSTTree)->key << endl;

	/* 寻找某个节点的前驱节点 */
	pPreNode = findPredecessor(pBSTTree->left->right);
	if (NULL != pPreNode)
	{
		cout << "该节点的前驱节点为:" << pPreNode->key << endl;
	}
	else
	{
		cout << "该节点无前驱节点" << endl;
	}

	/* 寻找某个节点的后继节点 */
	pSuccessor = findSuccessor(pBSTTree->left->left->left);
	if (NULL != pPreNode)
	{
		cout << "该节点的后继节点为:" << pSuccessor->key << endl;
	}
	else
	{
		cout << "该节点无后继节点" << endl;
	}

	/* 删除某个节点 */
	deleteNode(pBSTTree->right->right);
	deleteNode(pBSTTree->left->left);
	cout << "最小值的节点为:" << findMinNode(pBSTTree)->key << endl;
	pSuccessor = findSuccessor(pBSTTree->left->left);
	if (NULL != pPreNode)
	{
		cout << "该节点的后继节点为:" << pSuccessor->key << endl;
	}
	else
	{
		cout << "该节点无后继节点" << endl;
	}

	free(pBSTTree);
	pBSTTree = NULL;
	return 0;
}

/* 创建一个二叉查找树 */
void creatBinarySearchTree(pNode &pBSTTree, int *ptr, int len)
{
	for (int i = 0; i < len; i++)
	{
		insertNode(pBSTTree, *(ptr + i));
	}
}

/* 插入一个节点,复杂度为O(nlogn) */
void insertNode(pNode &pBSTTree, int value)
{
	pNode pInsertNode;

	/* 第一个节点,直接插入 */
	if (NULL == pBSTTree)
	{
		mallocInitNode(pInsertNode, value);
		pBSTTree = pInsertNode;
		return;
	}

	/* 如果键值已经存在的话,只需要times++ */
	if (value == pBSTTree->key)
	{
		pBSTTree->times++;
		return;
	}

	/* 如果小于本节点的值,且该节点无左孩子 */
	if ((NULL == pBSTTree->left) && (value < pBSTTree->key))
	{
		mallocInitNode(pInsertNode, value);
		pInsertNode->father = pBSTTree;
		pBSTTree->left = pInsertNode;
		return;
	}

	/* 如果大于本节点的值,且该节点无右孩子 */
	if ((NULL == pBSTTree->right) && (value > pBSTTree->key))
	{
		mallocInitNode(pInsertNode, value);
		pInsertNode->father = pBSTTree;
		pBSTTree->right = pInsertNode;
		return;
	}

	/* 如果小于本节点的值,但是该节点已经有左孩子,那么就继续递归 */
	if ((NULL != pBSTTree->left) && (value < pBSTTree->key))
	{
		insertNode(pBSTTree->left, value);
	}

	/* 如果大于本节点的值,但是该节点已经有右孩子,那么就继续递归 */
	if ((NULL != pBSTTree->right) && (value > pBSTTree->key))
	{
		insertNode(pBSTTree->right, value);
	}
}

/* 创建新节点并初始化 */
void mallocInitNode(pNode &pInsertNode, int value)
{
	pInsertNode = (pNode)malloc(sizeof(Node));
	pInsertNode->key = value;
	pInsertNode->father = NULL;
	pInsertNode->left = NULL;
	pInsertNode->right = NULL;
	pInsertNode->times = 1;
}

/* 寻找二叉树中最小的节点和最大的节点 */
pNode findMinNode(pNode &pTree)
{
	pNode pTemp = pTree;
	while (NULL != pTemp->left)
	{
		pTemp = pTemp->left;
	}
	return pTemp;

}

pNode findMaxNode(pNode &pTree)
{
	pNode pTemp = pTree;
	while (NULL != pTemp->right)
	{
		pTemp = pTemp->right;
	}
	return pTemp;
}

/* 找出前驱节点 */
pNode findPredecessor(pNode &pSearchNode)
{
	/* 如果左子树存在的话,则返回左子树中最大的节点,即为比它小的之中的最大的节点 */
	if (NULL != pSearchNode->left)
	{
		return findMaxNode(pSearchNode->left);
	}

	/* 如果左子树不存在的话,则需要往上找,直到找到目标节点是目标节点父亲节点的右孩子 */
	pNode pTemp = pSearchNode;
	while(pTemp != pTemp->father->right)
	{
		pTemp = pTemp->father;
	}
	return pTemp->father;
}

/* 找出后继节点 */
pNode findSuccessor(pNode &pSearchNode)
{
	/* 如果右子树存在的话,则返回右子树中最小的节点,即为比它大的之中的最小的节点 */
	if (NULL != pSearchNode->right)
	{
		return findMinNode(pSearchNode->right);
	}

	/* 如果左子树不存在的话,则需要往上找,直到找到目标节点是目标节点父亲节点的右孩子 */
	pNode pTemp = pSearchNode;
	while(pTemp != pTemp->father->left)
	{
		pTemp = pTemp->father;
	}
	return pTemp->father;
}

void deleteNode(pNode& pdeleteNode)
{
	/* 1.判断该节点的个数,如果节点的个数大于或等于1,则直接将该节点的个数-1 */
	if (1 < pdeleteNode->times)
	{
		pdeleteNode->times--;
		return;
	}
	/* 2.如果该节点只有一个,那么考虑删除 */
	/* 2.1 如果该节点没有孩子,则直接删除 */
	pNode pTemp = NULL;
	if ((NULL == pdeleteNode->left) && (NULL == pdeleteNode->right))
	{
		changeFatherChildNode(pdeleteNode, pTemp);
	}
	/* 2.2 如果该节点只有一个孩子,那么直接用该孩子代替该节点 */
	else if ((NULL == pdeleteNode->left) && (NULL != pdeleteNode->right))
	{
		changeFatherChildNode(pdeleteNode, pdeleteNode->right);
	}
	else if ((NULL == pdeleteNode->right) && (NULL != pdeleteNode->left))
	{
		changeFatherChildNode(pdeleteNode, pdeleteNode->left);
	}
	/* 2.3 如果该节点有两个孩子,那么考虑用该节点的前驱或者后继来代替该节点。在此,我们选择用前驱代替该节点 */
	else
	{
		pTemp = findPredecessor(pdeleteNode);
		pNode pRightChild = pdeleteNode->right;
		changeFatherChildNode(pdeleteNode, pTemp);
		pTemp->right = pRightChild;
	}
}

void changeFatherChildNode(pNode& pdeleteNode, pNode& pNewChildNode)
{
	if (pdeleteNode == pdeleteNode->father->right)
	{
		pdeleteNode->father->right = pNewChildNode;
	}
	else
	{
		pdeleteNode->father->left = pNewChildNode;
	}
}

代码运行结果:

时间: 2024-10-05 09:47:35

二叉搜索树建立、插入、删除、前继节点、后继节点之c++实现的相关文章

二叉搜索树的插入与删除图解

=================================================================== 一.二叉搜索树(BSTree)的概念 二叉搜索树又被称为二叉排序树,那么它本身也是一棵二叉树,那么满足以下性质的二叉树就是二叉搜索树: 1.若左子树不为空,则左子树上左右节点的值都小于根节点的值 2.若它的右子树不为空,则它的右子树上所有的节点的值都大于根节点的值 3.它的左右子树也要分别是二叉搜索树 ==============================

【算法导论】二叉搜索树的插入和删除

上一篇说了有关二叉树遍历的三种方式,文本将继续探讨如何实现二叉搜索树的插入和删除节点. 在继续之前,我们先来了解两个概念:前驱和后继. 一.后继和前驱 后继:如果所有的关键字互不相同,则一个节点x的后继是大于x.key的最小关键字的节点. 前驱:如果所有的关键字互不相同,则一个节点x的前驱是小于x.key的最大关键字的节点. 如果联系二叉搜索树的性质: 节点的key值总是大于它的左节点(如果存在)的key值并且小于它的右节点(如果存在)的key值.那么我们容易推知:如果一个节点有右子树,它后继即

C++实现二叉搜索树的插入,删除

二叉搜索树即左孩子的值小于根节点,右孩子的值大于根节点. 二叉搜索树的插入: 即检查要插入的数(key,下文都用它表示)是否存在,若存在返回false,不存在即插入,在插入时,若key大于根节点,则插入到右子树中,更新根节点,依次类推:若key小于根节点,则插入到左子树中,更新根节点,以此类推.下面用图表示 现在要插入13,由二叉树的性质可知,13要插到10的右子树上,即 代码实现: bool Insert(const k& key, const v& value) {  if (_roo

二叉搜索树的插入,删除,和中序遍历

构建一个值的类型为int的二叉搜索树,输入N和M,然后进行N次插入操作,每次插入之后进行一次遍历验证代码正确性.然后进行M次删除操作,每次删除之后进行一次遍历验证代码正确性. #include "bits/stdc++.h" using namespace std; typedef long long LL; const int INF = 0x3f3f3f3f; struct BST { int value; BST* lson; BST* rson; }* root; void r

二叉搜索树的插入

--------------------siwuxie095 二叉树的插入 程序: BST.h: #ifndef BST_H #define BST_H #include "stdlib.h" #include <queue> //二叉搜索树 template <typename Key, typename Value> class BST { private: struct Node { Key key; Value value; Node *left; No

Java对二叉搜索树进行插入、查找、遍历、最大值和最小值的操作

1.首先,需要一个节点对象的类.这些对象包含数据,数据代表存储的内容,而且还有指向节点的两个子节点的引用 class Node { public int iData; public double dData; public Node leftChild; public Node rightChild; public void displayNode() { System.out.print("{"); System.out.print(iData); System.out.print(

Java实现二叉搜索树节点的删除

前言: 之前写过一篇关于二叉搜索树的博客:Java对二叉搜索树进行插入.查找.遍历.最大值和最小值的操作  二叉查找树重要性质: (1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值: (2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值: (3)左.右子树也分别为二叉排序树: 如图: 这次我想分享的是二叉搜索树中节点是如何删除的,删除节点是二叉搜索树常用的一般操作中最复杂的,删除节点要从查找要删除的节点开始入手 ,找到节点后,这个要删除的节点可能会有三种情况需要考虑: 1

【算法导论】动态规划之“最优二叉搜索树”

之前两篇分别讲了动态规划的"钢管切割"和"矩阵链乘法",感觉到了这一篇,也可以算是收官之作了.其实根据前两篇,到这里,也可以进行一些总结的,我们可以找到一些规律性的东西. 所谓动态规划,其实就是解决递归调用中,可能出现重复计算子问题,从而导致耗费大量时间,去做重复劳动的问题.解决思路就是,将重复做过的子问题的结果,先存起来,等之后再需要用到的时候,直接拿过来用,而不需要再去计算. 但是这里还需要注意一些地方: ①要解决的问题,比如"钢管切割"中的

【数据结构05】红-黑树基础----二叉搜索树(Binary Search Tree)

目录 1.二分法引言 2.二叉搜索树定义 3.二叉搜索树的CRUD 4.二叉搜索树的两种极端情况 5.二叉搜索树总结 前言 在[算法04]树与二叉树中,已经介绍过了关于树的一些基本概念以及二叉树的前中后序遍历,而这篇文章将是在二叉树的基础上来展开讲解的二叉搜索树,也就是说二叉搜索树建立在树的基础之上.至于博主为何要花一整篇文章来讲这个二叉搜索树呢?原因很简单,红-黑树是基于二叉搜索树的,如果对二叉搜索树不了解,那还谈何红-黑树?红-黑树的重要性我想各位没吃过佩奇肉也肯定看过宜春跑....是的,j