数据结构——二叉树:二叉搜索树(C++)

内容概要:

  • 二叉搜索树的相关概念
  • 实现二叉搜索树时注意事项
  • code:Node类,BST类,测试函数

一、相关概念

  • 二叉搜索树(Binary Search Tree,简称BST):也称二叉查找树,二叉排序树等。特点:对于其中的任一节点,设其值为k,该节点左子树任一节点的值都小于k;该节点右子树任一节点的值都大于或等于k(也有版本认为BST中不存在键值相等的节点)。
  • 节点的前驱:是该节点的左子树中的最大节点。
    节点的后继:是该节点的右子树中的最小节点。

       数据结构——二叉树:二叉搜索树(C++)_第1张图片

二、注意事项

  • BST的插入与搜索都能很快完成,但BST也有可能处于严重不平衡状态(甚至退化成链表),因此又有很多在BST基础上改进的树,如伸展树(splay tree),AVL树。
  • BST中的基本操作有清空、插入、删除、查找、打印,其中较难实现的是插入与删除,由于树的特性,经常使用递归思想来实现相关操作。
  • 插入:先找到它应该放在的地方,即某个叶节点或一个待插入方向上没有子节点的分支节点,本文中的代码允许插入key相同的节点,但必须规定其只存在于左右子树中的一个(本文中为右子树,即将与某个节点有相同值的节点插入到其右子树上),否则会给删除操作带来很大的麻烦。下图是在BST中插入46的过程:

       数据结构——二叉树:二叉搜索树(C++)_第2张图片

  • 删除:需要删除的节点有四种情况
  1. 不存在
  2. 无子节点:删除后使其父节点指向NULL,如下图中删除24数据结构——二叉树:二叉搜索树(C++)_第3张图片
  3. 有一个子节点 :删除后使其父节点指向子节点,如下图中删除42数据结构——二叉树:二叉搜索树(C++)_第4张图片
  4. 有两个子节点:使其前驱或后继的值代替需要删除的节点的值,然后删除前驱或后继,实现删除的目的,考虑到相同的值可能存在于右子树上,所以用后继代替,如下图删除50数据结构——二叉树:二叉搜索树(C++)_第5张图片

三、code(测试环境VS2017)

//Node.h 一个简单的节点类,这里没有设置父节点,用处不大
#pragma once
template
class Node
{
public:
	Key key;
	E e;
	Node*lc;
	Node*rc;

	Node(const Key&k, const E&it, Node*l = NULL, Node*r = NULL) { key = k; e = it; lc = l; rc = r; }
};
//BST.h 二叉搜索树的模板类
#pragma once
#include"Node.h"

template
class BST
{
private:
	Node*root;
	int nodecount;

	//基本操作的辅助函数:清除,插入,删除,查找,打印
	void clearhelp(Node*);
	Node* inserthelp(Node*, const Key&, const E&);
	Node* deletemin(Node*);//删除后驱,辅助删除操作
	Node* getmin(Node*);//获取后驱,辅助删除操作
	Node* removehelp(Node*,const Key&);
	E findhelp(Node*, const Key&)const;
	void printhelp(Node*, int)const;

public:
	BST() { root = NULL; nodecount = 0; }
	~BST() { clearhelp(root); }

	void clear() { clearhelp(root); root = NULL; nodecount = 0; }

	void insert(const Key&k, const E&e) { root=inserthelp(root, k, e); nodecount++; }

	E remove(const Key&k)
	{
		E temp = findhelp(root, k);
		if (temp != NULL)
		{
			root = removehelp(root, k);
			nodecount--;
		}
		return temp;
	}

	E find(const Key&k) { return findhelp(root, k); }

	void print()const
	{
		if (root == NULL)
			cout << "The BST is empty" << endl;
		else
			printhelp(root, 0);
	}

	int size() const { return nodecount; }
};

template
void BST::clearhelp(Node*root)
{
	//本质上就是个后序遍历
	if (root == NULL)
		return;
	clearhelp(root->lc);
	clearhelp(root->rc);
	delete root;
}

template
Node* BST::inserthelp(Node*root, const Key&k, const E&it)
{
	//递归实现插入
	if (root == NULL)
		return new Node(k, it);
	if (k < root->key)
		root->lc = inserthelp(root->lc, k, it);//可以看到,这里的赋值操作是很多的,但是代码简便了很多,下同
	else//这里把相同的值插入右子树中,那么下面删除的操作也要考虑重复的值,如果不想要重复的值,可另作处理
		root->rc = inserthelp(root->rc, k, it);
	return root;
}

template
Node* BST::deletemin(Node*rt)
{
	if (rt->lc == NULL)
		return rt->rc;
	else
	{
		rt->lc = deletemin(rt->lc);
		return rt;
	}
}

template
Node* BST::getmin(Node*rt)
{
	if (rt->lc == NULL)
		return rt;
	else
		return getmin(rt->lc);
}


template
Node* BST::removehelp(Node*rt, const Key&k)
{
	//删除的节点可能有0、1、2个子节点,删除节点后对应的其父节点分别指向NULL、子节点、前驱(后继),由于可能存在重复值(在右子树),2个子节点的删除后父节点应指向后继,保证与节点的值相同的子节点在节点的右子树
	if (rt == NULL)//树中没有为k的节点
		return NULL;
	else if (k < rt->key)
		rt->lc = removehelp(rt->lc, k);
	else if (k > rt->key)
		rt->rc = removehelp(rt->rc, k);
	else//找到为k的节点
	{
		Node*temp = rt;
		if (rt->lc == NULL)//只有右子树
		{
			rt = rt->rc;
			delete temp;
		}
			
		else if (rt->rc == NULL)//只有左子树
		{
			rt = rt->lc;
			delete temp;
		}
			
		else//左右子树都存在
		{
			Node*temp0 = getmin(rt->rc);
			
			rt->e = temp0->e;
			rt->key = temp0->key;
			rt->rc = deletemin(rt->rc);
			delete temp0;
		}	
	}
	return rt;
}

template
E BST::findhelp(Node*root, const Key&k)const
{
	if (root == NULL)
		return NULL;
	if (k < root->key)
		findhelp(root->lc, k);
	else if (k > root->key)
		findhelp(root->rc, k);
	else
		return root->e;
}

template
void BST::printhelp(Node*root,int level)const
{
	//,从大到小可视化打印
	if (root == NULL)
		return;
	printhelp(root->rc, level + 1);
	for (int i = 0; i < level; i++)
		cout << "	";//每一行的缩进都显示了树中相应节点的深度
	cout << root->key << endl;
	printhelp(root->lc, level + 1);
}
//测试函数

#include
#include"BST.h"
using namespace std;

int main()
{
	BSTtest;
	int a[10] = { 37,24,42,42,7,32,30,2,40,120 };
	for (int i = 0; i < 10; i++)
		test.insert(a[i], a[i]*a[i]);
	test.print();

	test.remove(37);
	test.print();

	cout << test.find(40) << endl;

	cout<<"size"<

  上述测试函数的结果:

  数据结构——二叉树:二叉搜索树(C++)_第6张图片

另外附一个模拟二叉搜索树的网站:https://www.cs.usfca.edu/~galles/visualization/BST.html

你可能感兴趣的:(数据结构2018)