二叉查找树查找、插入、删除

二叉查找树的插入,寻找前驱结点、寻找后继结点、搜索、删除操作

二叉查找树的性质:对于树中的每个结点X,它的左子树中的所有项的值小于X中的项,而它的右子树中所有项的值大于X中的项。如下图中的两棵树,左边的为二叉查找树,而右边的树则不是。


二叉查找树的具体操作代码实现:

创建的树形结构如下图:


1.首先创建一个二叉树结点的类

template <class T>
class BSTNode{
public:
	T data;
	BSTNode *left;
	BSTNode *right;
	BSTNode *parent;

	//BSTNode 的构造函数
	BSTNode(T value,BSTNode *p,BSTNode *l,BSTNode * r):data(value),parent(),left(l),right(r){}
};
2.创建一个二叉查找树的类

template <class T>
class BinarySearchTree{
private:
	BSTNode<T> *mRoot;
public:
	BinarySearchTree();
	~BinarySearchTree();

	//将结点T插入到二叉树中
	void insert(T data);
	// 前序遍历"二叉树"
    void preOrder();
	// 中序遍历"二叉树"
    void inOrder();
	//删除结点T
	void remove(T data);
	//查找结点T
	BSTNode<T>* search(T data);
	//查找某一键值的前驱结点
	BSTNode<T>* searchPreNode(T data);
	//查找某一键值的后继结点
	BSTNode<T>* searchSucceedNode(T data);
private:
	void insert(BSTNode<T>* &tree,BSTNode<T>* T);
	void recursionInsert(BSTNode<T>* &tree,BSTNode<T>* data,BSTNode<T>* parent);
	int remove(BSTNode<T>* T);
	void remove(BSTNode<T>* TNode,T data);
	BSTNode<T>* search(BSTNode<T>* &tree,T data);
	BSTNode<T>* searchPreNode(BSTNode<T>* &tree,BSTNode<T>* node);
	BSTNode<T>* searchSucceedNode(BSTNode<T>* &tree,BSTNode<T>* node);
	void preOrder(BSTNode<T>* tree) const;
	void inOrder(BSTNode<T>* tree) const;
};
3.二叉查找树的插入操作:

//递归插入
template <class T>
void BinarySearchTree<T>::recursionInsert(BSTNode<T>* &tree,BSTNode<T>* t,BSTNode<T>* parent){
	if(tree == NULL){
		tree = t;
		tree->parent = parent;
	}else if(tree->data < t->data){
		recursionInsert(tree->right,t,tree);
	}else if(tree->data > t->data){
		recursionInsert(tree->left,t,tree);
	}
}
//插入结点的接口
template <class T>
void BinarySearchTree<T>::insert(T data){
	BSTNode<T> *node = NULL;
	node = new BSTNode<T>(data,NULL,NULL,NULL);
	//如果新建结点失败,则返回
	if(node == NULL){
		return;
	}
	recursionInsert(mRoot,node,NULL);
}
4.二叉查找树的搜索操作

//递归实现查找结点
template <class T>
BSTNode<T>* BinarySearchTree<T>::search(BSTNode<T>* &tree,T data){
	if(tree == NULL || tree->data == data){
		return tree;
	}else if(tree->data > data){
		search(tree->left,data);
	}else if(tree->data < data){
		search(tree->right,data);
	}
}

//查找结点的接口
template <class T>
BSTNode<T>* BinarySearchTree<T>::search(T data){
	return search(mRoot,data);
}
5.寻找某一键值的前驱结点和后继结点(按照结点的**中序遍历**顺序设定)

前驱结点和和后继结点定义: 

前驱结点:结点val值小于该节点val值,并且值最大的结点; 

后继结点:结点val值大于该节点val值,并且值最小的结点;

寻找规则:

寻找前驱结点:

1.若一个结点有左子树,那么该节点的前驱结点是其左子树中val值最大的结点(也就是左子树中所谓的rightMostNode)

2.若一个结点没有左子树,那么判断该节点和其父节点的关系:

2.1.若该结点是其父节点的右边孩子,那么该结点的前驱结点即为其父节点;

2.2.若该节点是其父节点的左边孩子,那么需要沿着其父亲结点一直向树的顶端寻找,直到找到一个结点P,P结点是其父节点Q的右边孩子,那么Q就是该结点的前驱结点。

寻找后继结点:

1.若一个结点有右子树,那么该节点的后继结点是其右子树中val值最小的结点

2.若一个结点没有右子树,那么判断该结点与其父节点的关系:

2.1.若该结点是其父节点的左边孩子,那么该结点的后继结点即为其父节点

2.2.若该节点是其父节点的右边孩子,那么需要沿着其父节点一直向树的顶端一直寻找,知道找到一个结点P,P结点是其父节点Q的左边孩子,那么Q就是该节点的后继结点

//查找结点的前驱结点
template <class T>
BSTNode<T>* BinarySearchTree<T>::searchPreNode(BSTNode<T>* &tree,BSTNode<T>* node){
	BSTNode<T>* leftTreeNode = node->left;
	
	if(tree == NULL){
		return tree;
	}
	//当该节点有左子树时,则该节点的前驱为左子树中val值最大的结点
	if(leftTreeNode != NULL){
		//findedNode = searchPreNode(mRoot,node->right);
		while(leftTreeNode->right != NULL){
			leftTreeNode = leftTreeNode->right;
		}
		return leftTreeNode;
	}else{
		//当该节点没有左子树,则判断该节点是起父节点的右边孩子,那么该节点的前驱结点即为其父节点
		//若该节点昰其父节点的左边孩子,那么需要沿着其父亲结点一直向树的顶端寻找,直到找到节点P,P节点是其父节点的Q的右边孩子
		while((node->parent != NULL)&&(node->parent->right != node)){
			node = node->parent;
		}
		return node->parent;
	}	
}

//查找前驱结点的接口
template <class T>
BSTNode<T>* BinarySearchTree<T>::searchPreNode(T data){	
	BSTNode<T>* node = search(data);
	return searchPreNode(mRoot,node);
}
//查找后继结点
template<class T>
BSTNode<T>* BinarySearchTree<T>::searchSucceedNode(BSTNode<T>* &tree,BSTNode<T>* node){
	//查找结点右子树的根结点
	BSTNode<T>* rightTreeNode = node->right;
	if(rightTreeNode != NULL){
		while(rightTreeNode->left != NULL){
			rightTreeNode = rightTreeNode->left;
		}
		return rightTreeNode;
	}else{
		while((node->parent != NULL)&&(node->parent->left != node)){
			node = node->parent;
		}
		return node->parent;
	}
}
//查找后继结点的接口
template<class T>
BSTNode<T>* BinarySearchTree<T>::searchSucceedNode(T data){
	BSTNode<T>* node = search(data);
	return searchSucceedNode(mRoot,node);
}



6.二叉查找树的删除操作

//删除结点
/*在二叉查找树中删除一个给定的结点p有三种情况
(1)结点p无左右子树,则直接删除该结点,修改父节点相应指针
(2)结点p有左子树(右子树),则把p的左子树(右子树)接到p的父节点上
(3)左右子树同时存在,则有三种处理方式
a.找到结点p的中序直接前驱结点s,把结点s的数据转移到结点p,然后删除结点s,由于结点s为p的左子树中最右的结点,因而s无右子树,删除结点s可以归结到情况(2)。严蔚敏数据结构P230-231就是该处理方式。
b.找到结点p的中序直接后继结点s,把结点s的数据转移到结点p,然后删除结点s,由于结点s为p的右子树总最左的结点,因而s无左子树,删除结点s可以归结到情况(2)。算法导论第2版P156-157该是该处理方式。
c.找到p的中序直接前驱s,将p的左子树接到父节点上,将p的右子树接到s的右子树上,然后删除结点p。*/
template <class T>
int BinarySearchTree<T>::remove(BSTNode<T>* node){
	BSTNode<T> *p = NULL;
	BSTNode<T>* parent = node->parent;//需要删除节点的父节点
	BSTNode<T>* s;
	if((node->left == NULL)&&(node->right == NULL)){
		p = node;
		node = NULL;
		if(p == parent->left){
			parent->left = NULL;
		}else{
			parent->right = NULL;
		}
	}else if((node->left != NULL)&&(node->right == NULL)){
		p = node;
		//将节点的左子树上移
		node = node->left;
		//将左子树上移后,需要将删除结点的左(右)孩子结点连接到左子树的结点,此处代码很重要,若不添加,则会导致再次中序遍历此树的时候出错
		if(p == parent->left){
			parent->left = p->left;
		}else{
			parent->right = p->left;
		}
		delete(p);
		//delete(p);
	}else if((node->left == NULL)&&(node->right != NULL)){
		//node = node->right;
		//node->parent = p->parent;
		//node->parent->right = node->right;
		p = node;
		//将节点的右子树上移
		node = node->right;
		//将右子树上移后,需要将删除结点的左(右)孩子结点连接到左子树的结点,此处代码很重要,若不添加,则会导致再次中序遍历此树的时候出错
		if(p == parent->left){
			parent->left = p->right;
		}else{
			parent->right = p->right;
		}
		delete(p);
	}else{
		//找到删除结点node的直接前驱s,把节点s的数据转移到node,然后删除结点s
		//采用第三种方法:找到p的中序直接前驱s,将p的左子树接到父节点上,将p的右子树接到s的右子树上,然后删除结点p。		
		p = node;
		//s为要删除的node结点的直接前驱结点
		s = searchPreNode(node->data);
		//将P的左子树借到父亲节点上,则判断P是其父节点的左孩子还是右孩子
		//如果删除的node是父节点的左孩子
		node->data = s->data;
		remove(s);
	}
	return true;
}
//删除结点的接口
template <class T>
void BinarySearchTree<T>::remove(T data){

	BSTNode<T>* node = search(data);
	//BSTNode<T>* deleteNode;
	if(node != NULL){
		remove(node);
	}	
}

6.二叉查找树的前序遍历

//二叉查找树的前序遍历(递归实现)
template <class T>
void BinarySearchTree<T>::preOrder(BSTNode<T>* tree) const{
	if(tree == NULL){
		return;
	}
	//printf("%d",tree->data);
	cout << tree->data << " ";
	preOrder(tree->left);
	preOrder(tree->right);
}
//二叉查找树的前序遍历接口
template <class T>
void BinarySearchTree<T>::preOrder() 
{
    preOrder(mRoot);
}
7.二叉查找树的中序遍历
//二叉查找树的中序遍历(递归实现)
template <class T>
void BinarySearchTree<T>::inOrder(BSTNode<T>* tree) const{
	if(tree == NULL){
		return;
	}
	//printf("%d",tree->data);	
	inOrder(tree->left);
	cout << tree->data << " ";
	inOrder(tree->right);
}
//二叉查找树的中序遍历接口
template <class T>
void BinarySearchTree<T>::inOrder() 
{
    inOrder(mRoot);
}
8.测试代码

int _tmain(int argc, _TCHAR* argv[])
{
	//static int arr[]= {1,5,4,3,2,6};
	//static int arr[]= {15, 6, 18, 3, 7, 17, 20, 2, 4, 13, 9};62,88,58,47,35,73,51,99,37,93
	//static int arr[]= {10,18,3,6,12,2,4};
	static int arr[]= {62,88,58,47,35,73,51,99,37,93,29,36,49,48,50,56};
	int i,ilen;
	ilen = sizeof(arr)/sizeof(arr[0]);
	BinarySearchTree<int>* tree = new BinarySearchTree<int>();
	for(i = 0;i < ilen;i++){
		cout << arr[i] << " ";
		tree->insert(arr[i]);
	}
	cout << "\n== 前序遍历: ";
    tree->preOrder();
	cout << "\n== 中序遍历: ";
	tree->inOrder();
	BSTNode<int>* node = new BSTNode<int>(5,NULL,NULL,NULL);
	node = tree->searchPreNode(51);
	if(node == NULL){
		cout << "\n== 该结点51无前驱! ";
	}else{
		cout << "\n== 该结点51的前驱为: ";
		cout << node->data << " ";
	}
	node = tree->searchSucceedNode(37);
	if(node == NULL){
		cout << "\n== 该结点37无后继! ";
	}else{
		cout << "\n== 该结点37的后继为: ";
		cout << node->data << " ";
	}
	//删除节点
	cout << "\n== 删除的结点为47: ";
	tree->remove(47);
	cout << "\n== 删除后中序遍历: ";
	tree->inOrder();
	system("pause");
	return 0;
}
9.测试结果:














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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值