《算法笔记》读书记录DAY_35

CHAPTER_9  提高篇(3)——数据结构(2)

9.4.1二叉查找树的定义

二叉查找树(BST)是一种特殊的二叉树,又称为二叉排序树、二叉搜索树。二叉查找树的定义如下:

(1)要么二叉查找树是一颗空树。

(2)要么二叉查找树由根节点、左子树、右子树构成。其中左子树和右子树都是二叉查找树,且           左子树上所有节点的数据域均小于等于根节点的数据域,右子树上所有节点的数据域均大于           根节点的数据域。

上图给出了一个BST的例子。从图中可以看出,二叉查找树实际上是一棵数据域有序的二叉树。如果对BST进行中序遍历,就可以得到一个递增的序列。

9.4.2二叉查找树的基本操作

二叉查找树的基本操作有查找、插入、建树、删除。这些操作过程是基于一般二叉树改造而来的。

查找操作

二叉查找树的性质使得二叉树可以高效的查找元素,查找过程将会是从根节点到查找节点的一条路径。最坏复杂度为O(h),h为树的高度。查找数据x的基本思路如下:

(1)如果当前根节点为空,查找失败,返回。

(2)如果x等于当前根节点的数据域,说明查找成功,访问之。

(3)如果x小于当前根节点的数据域,说明x可能在左子树,向左子树递归。

(4)如果x大于当前根节点的数据域,说明x可能在右子树,向右子树递归。

代码如下: 

void search(node* root,int x) {
	if(root==NULL) {
		cout<<"failed"<<endl;
		return;
	}
	if(x==root->data) {
		cout<<root->data;
	}
	else if(x<root->data) {
		search(root->lchild,x);
	}
	else {
		search(root->rchild,x);
	}
}

插入操作

与二叉树的插入类似,二叉查找树的插入同样是个查找的过程。当某个值在BST中查找成功,说明节点已经存在,如果查找失败,说明查找失败的地方一定是节点需要插入的地方。因此插入代码只需在查找的基础上稍作修改。

代码如下:

void insert(node* &root,int x) {   //注意root参数一定要引用 
	if(root==NULL) {               //查找失败,新建节点进行插入 
		root=newNode(x);
		return;
	}
	if(x==root->data) {            //节点已存在 
		return;
	}
	else if(x<root->data) {        //插入位置在左子树 
		insert(root->lchild,x);
	}
	else(x>root->data) {           //插入位置在右子树 
		insert(root->rchild,x);
	}
}

二叉查找树的建立

建树的过程就是节点一个个的插入的过程,和一般二叉树的建立是一样的。

代码如下:

void create(int data[],int n) {
	node* root=NULL;
	for(int i=0;i<n;i++) {
		insert(root,data[i]);
	}
	return;
}

二叉查找树的删除

二叉查找树的删除操作稍显复杂。如下图,我们要删除节点3,我们必须保证删除后仍是一棵二叉查找树。

在讲解删除操作之前,我们先要理解节点的前驱和后继概念。把二叉查找树中比节点权值小的最大节点称作该节点的前驱,而比节点权值小的最大节点称作该节点的后继。例如上图中,节点3的前驱为2,后继为4。可以看出,节点的前驱是该节点的左子树中的最右节点,而后继是该节点的右子树中的最左节点。

因从我们可以用下面两个函数来寻找某BST的最大节点和最小节点,依次来辅助寻找某节点的前驱和后继:

//寻找某BST中的最大权值节点
node* finMax(node* root) {
	while(root->rchild) {
		root=root->rchild;     //不断向右,找到最右的节点 
	}
	return root;
}

//寻找某BST中的最小权值节点 
node* finMin(node* root) {
	while(root->lchild) {
		root=root->lchild;    //不断向左,找到最左的节点    
	}
}

接下来可以我们来看如何在BST中删除一个节点。假设我们要在BST中删除节点N。我们可以用节点N的后继P来替换节点N,于是就把问题转换为在N的右子树中删除P,就可以递归下去了,直到递归到一个叶子节点,就可以把它删除了。同样地,我们也可以用后继节点来代替N。例如上图中,我们用后继4来代替节点3。

删除节点x操作的基本思路如下:

(1)如果当前节点root为空,说明不存在权值为x的节点,返回。

(2)如果当前节点权值为x,说明找到元素,此时进入删除处理。

        (a)如果当前节点root不存在左右孩子,说明是叶子节点,直接删除。

        (b)如果当前节点存在左孩子,则进入左子树中寻找前驱pre,然后让pre的数据覆盖root,                   接着在左子树中删除节点pre。

        (c)如果当前节点root存在右孩子,则进入右子树寻找后继next,然后让next的数据覆盖                       root,接着在右子树中删除next。

(3)如果当前节点root的权值大于给定权值x,则在左子树中递归删除权值为x的节点。

(4)如果当前节点root的权值小于给定权值x,则在右子树中递归删除权值为x的节点。

代码如下:

void deleteNode(node* &root,int x) {
	if(root==NULL)
		return;
	if(root->data==x) {
		if(root->lchild==NULL&&root->rchild==NULL) {
			delete root;
			root==NULL;
		}
		else if(root->rchild) {
			node *next=findMin(root->rchild);
			root->data=next->data;
			deleteNode(root->rchild,next->data);
		}
		else(root->lchild) {
			node *pre=findMax(root->lchild);
			root->data=pre->data;
			deleteNode(root->lchild,pre->data);
		}
	}
	else if(root->data>x) {
		deleteNode(root->lchild,x);
	}
	else {
		deleteNode(root->rchild,x);
	}
}

猜你喜欢

转载自blog.csdn.net/jgsecurity/article/details/120907791