查找算法系列之复杂算法:二叉排序树BST

前面总结了顺序查找,二分查找,分块查找算法,此篇博文将详解介绍二叉排序算法(Binary Sort Tree)。

在介绍二叉排序算法之前,首先介绍什么事二叉排序树(BST)。

首先从二叉树讲起:


1、二叉树的概念

二叉树是每个结点最多有两个子树的有序树。通常子树的根被称作“左子树”(leftsubtree)和“右子树”(rightsubtree)。二叉树常被用作二叉查找树和二叉堆或是二叉排序树。二叉树的每个结点至多只有二棵子树(不存在度大于2的结点),二叉树的子树有左右之分,次序不能颠倒。二叉树的第i层至多有2的 i -1次方个结点;深度为k的二叉树至多有2^(k) -1个结点;对任何一棵二叉树T,如果其终端结点数(即叶子结点数)为n0,度为2的结点数为n2,则n0 = n2 +1。

二叉树的链式存储结构是一类重要的数据结构,其形式定义如下:

//二叉树结点  
typedef struct BiTNode{  
    //数据  
    char data;  
    //左右孩子指针  
    struct BiTNode *lchild,*rchild;  
}BiTNode,*BiTree;  

2、二叉树的建立

通过读入一个字符串,建立二叉树的算法如下:

//按先序序列创建二叉树  
int CreateBiTree(BiTree &T){  
    char data;  
    //按先序次序输入二叉树中结点的值(一个字符),‘#’表示空树  
    scanf("%c",&data);  
    if(data == '#'){  
        T = NULL;  
    }  
    else{  
        T = (BiTree)malloc(sizeof(BiTNode));  
        //生成根结点  
        T->data = data;  
        //构造左子树  
        CreateBiTree(T->lchild);  
        //构造右子树  
        CreateBiTree(T->rchild);  
    }  
    return 0;  
}  


3、二叉树的遍历

遍历是对树的一种最基本的运算,所谓遍历二叉树,就是按一定的规则和顺序走遍二叉树的所有结点,使每一个结点都被访问一次,而且只被访问一次。由于二叉树是非线性结构,因此,树的遍历实质上是将二叉树的各个结点转换成为一个线性序列来表示。

                   先序遍历:访问根节点,访问左子节点,访问右子节点

                   中序遍历:访问左子结点,访问根节点,访问右子节点

                   后序遍历:访问左子节点,访问右子节点,访问根节点

                   层次遍历:从顶层到底层,按从顶向下,从左至右的顺序来逐层访问每个节点,层次遍历的过程中需要用队列。

事实上,知道任意两种方式,并不能唯一地确定树的结构,但是,只要知道中序遍历和另外任意一种遍历方式,就一定可以唯一地确定一棵树。


// 先序遍历
void preOrderTraverse(BST T)
{
	if(T)
	{
		cout << T->key <<" ";
		preOrderTraverse(T->lChild);
		preOrderTraverse(T->rChild);
	}
}

// 中序遍历
void inOrderTraverse(BST T)
{
	if(T)
	{
		inOrderTraverse(T->lChild);
		cout << T->key <<" ";
		inOrderTraverse(T->rChild);
	}
}

//后序遍历
void postOrderTraverse(BST T)
{
	if(T)
	{
		postOrderTraverse(T->lChild);
		postOrderTraverse(T->rChild);
		cout << T->key <<" ";
	}
}
//层次遍历
void LevelOrder(BST T)
{
	BST p = T;
	//队列
	queue<BST> queue;
	queue.push(p);
	while(!queue.empty())
	{
		p = queue.front();
		cout<< p->key<<" ";
		queue.pop();
		if(p->lChild != NULL)
			queue.push(p->lChild);
		if(p->rChild != NULL)
			queue.push(p->rChild);
	}
}



4.BST树的概念

 它或者是一棵空树;或者是具有下列性质的二叉树:

  (1)若左子树不空,则左子树上所有结点的值均小于左子树所在树的根结点的值;

  (2)若右子树不空,则右子树上所有结点的值均大于右子树所在树的根结点的值;

  (3)左、右子树也分别为二叉排序树;

         最大关键字及最小关键字元素:顾名思义,肯定是二叉搜索树的最大最小值,以最大关键字为例,一直查询树的右孩子,直到该节点无右孩子为止,该节点就是最大关键字, 当然,最小关键字同理;

          后继与前驱:对一个节点来说,最大的小于该节点值的即是前驱,最小的大于该节点        值的即是后继。以后继为例,如果该节点的右子树不为空,那么后继就是右子树中最小关键字元素;若是该节点右孩子不存在,这时,只需由该节点往上寻找,直到这个节点是其   父节点的左孩子即可。当然,前驱也是类似情况;


5、BST树的查找:

                   时间复杂度与树的深度的有关,O(log n)。

  步骤:若根结点的关键字值等于查找的关键字,成功。

  否则:若小于根结点的关键字值,递归查左子树。

  若大于根结点的关键字值,递归查右子树。

  若子树为空,查找不成功。


6、BST树的插入

                   首先执行查找算法,找出被插结点的父亲结点。

  判断被插结点是其父亲结点的左儿子还是右儿子。将被插结点作为叶子结点插入。

  若二叉树为空。则首先单独生成根结点。

  注意:新插入的结点总是叶子结点,所以算法复杂度是O(h)。

                   BST树的建立过程就是依次插入的过程。

 

7、BST树的删除

  如果删除的结点没有孩子,则删除后算法结束;

  如果删除的结点只有一个孩子,则删除后该孩子取代被删除结点的位置;

  如果删除的结点有两个孩子,则选择该结点的后继结点(该结点右孩子为根的树中的左   子树中的值最小的点)作为新的根,同时在该后继结点开始,递归执行删除算法,直到叶    子节点,删除算法结束。

 

将BST的实现写到BST.h文件中

#include <iostream>
#include <queue>
using namespace std;

// BST的结点  
typedef int ElemType;
typedef struct node  
{  
	ElemType key;  
    struct node *lChild, *rChild;  
}Node, *BST; 

//在BST树中插入
bool BSTInsert(BST & root, ElemType x)
{
	if(root == NULL)
	{
		root = new Node;
		root->key = x;
		root->lChild = NULL;
		root->rChild = NULL;
		return true;
	}
	if(root->key == x)
		return false;
	if(x < root->key)
		return BSTInsert(root->lChild , x);
	
	return BSTInsert(root->rChild,x);
}

//创建BST树
void createBST(BST &root, ElemType *x, int N)
{
	root = NULL;
	for(int i = 0; i < N; i++)
	{
		BSTInsert(root,x[i]);
	}
}

// 先序遍历
void preOrderTraverse(BST T)
{
	if(T)
	{
		cout << T->key <<" ";
		preOrderTraverse(T->lChild);
		preOrderTraverse(T->rChild);
	}
}

// 中序遍历
void inOrderTraverse(BST T)
{
	if(T)
	{
		inOrderTraverse(T->lChild);
		cout << T->key <<" ";
		inOrderTraverse(T->rChild);
	}
}

//后序遍历
void postOrderTraverse(BST T)
{
	if(T)
	{
		postOrderTraverse(T->lChild);
		postOrderTraverse(T->rChild);
		cout << T->key <<" ";
	}
}
//层次遍历
void LevelOrder(BST T)
{
	BST p = T;
	//队列
	queue<BST> queue;
	queue.push(p);
	while(!queue.empty())
	{
		p = queue.front();
		cout<< p->key<<" ";
		queue.pop();
		if(p->lChild != NULL)
			queue.push(p->lChild);
		if(p->rChild != NULL)
			queue.push(p->rChild);
	}
}


//查找元素,递归算法,找到返回关键字的结点指针,没找到返回NULL
BST BSTSearch(BST root, ElemType key)//递归算法
{
	if(root == NULL)
		return NULL;
	if(root->key == key)
		return root;
	if(key > root->key )
		return BSTSearch(root->rChild , key);
	else
		return BSTSearch(root->lChild , key);
}

//查找元素,非递归算法
BST BSTSearch2(BST root, ElemType key)
{
	if(root == NULL)
		return NULL;
	BST p = root;
	while (p->key != key)
	{
		if(key < p->key)
			p = p->lChild;
		else
			p = p->rChild;
		if(p == NULL)
			return NULL;
	}
	return p;
}

//查找最小关键字,空树时返回NULL  
BST SearchMin(BST root)  
{  
    if(root == NULL)  
        return root;  
  
	while(root->lChild != NULL)  
		root = root->lChild;  
    return root;  
}  

//查找最大关键字,空树时返回NULL  
BST SearchMax(BST root)  
{  
    if(root == NULL)  
        return root;  
  
	while(root->rChild != NULL) //迭代  
		root = root->rChild;  
    return root;  
} 


//查找前驱,即左子树中比当前数小的最大的数,若不存在左子树,复杂,暂时没有实现
BST SearchPredecessor(BST root)
{
	if(root == NULL)
		return root;
	//若存在左孩子,前驱是其左子树中最大的 
	if(root->lChild != NULL)
		return SearchMax(root->lChild);

	//不存在左孩子,需要回溯找到它的parent,因此需要在sruct中加入一个parent
	 
 //   BST y = root->parent;  
	//while(y != NULL && root == y->lChild)  
 //   {  
 //       root = y;  
 //       y = root->parent;  
 //   }  
 //   return y;  
}

//查找后继  
BST SearchSuccessor(BST root)  
{  
    if(root == NULL)  //空  
        return root;  
  
    //若存在右孩子,后继是其右子树中最小的  
	if(root->rChild != NULL)  
		return SearchMin(root->rChild);  

 //   BST y = root->parent;  
	//while(y != NULL && root == y->rChild)  
 //   {  
 //       root = y;  
 //       y = root->parent;  
 //   }  
 //   return y;  
}
BST BSTDelete(BST root, ElemType key)
{
	BST p,q;
	if(root==NULL)
		return root;
	if(root->key == key){
		if(root->lChild == NULL && root->rChild == NULL){
			free(root);
			return NULL;
		}
		else if(root->lChild == NULL){
			p = root->rChild;
			free(root);
			return p;
		}
		else if(root->rChild == NULL){
			p = root->lChild;
			free(root);
			return p;
		}else{
			p = q = root->rChild;
			while(p->lChild!=NULL)
				p = p->lChild;
			p->lChild = root->lChild;
			free(root);
			return(q);
		}
	}	
	if(root->key > key && root->lChild !=NULL)
		root->lChild = BSTDelete(root->lChild,key);

	if(root->key < key && root->rChild !=NULL)
		root->rChild = BSTDelete(root->rChild,key);

	return root;
}



文章参考了_Luffy的文章:http://blog.csdn.net/xjm199/article/details/20003045

以及另一位的文章:http://www.open-open.com/bbs/view/1410494404789


完整代码下载点击:

查找算法代码C++——包括顺序、二分、BST、哈希

http://download.csdn.net/detail/u010025211/8841123

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值