算法系列笔记3(二叉查找树)

(1)二叉查找树的性质:设x为二叉查找树的一个结点。如果y是x左子树中的一个结点,则key[y]≤key[x]。如果y是x的右子树中的一个结点。则key[x]≤key[y].

(2)二叉查找树的结点中除了key域和卫星数据外,还包括left、right和p分别指向结点的左儿子、右儿子和父节点。

(3)构造一棵二叉查找树最好情况下时间复杂度为O(nlgn),最坏情况为O(n^2)。随机化构造一棵二叉查找树的期望时间O(nlgn)。与快排和随机化快速排序算法是做相同的比较,但是顺序不一样。可以证明随机化构造一棵二叉查找树的期望高度为O(lgn).这样我们就可以在O(lgn)时间内完成动态集合的操作了,包括minimum、maximum、search、predecessor(前驱)、successor(后继)、insert及delete。

(4)二叉查找树的中序遍历就是其排列顺序,中序遍历的时间复杂度为O(n)

下面我们将对二叉查找树的创建、中序遍历及动态集合的操作minimum、maximum、search、predecessor(前驱)、successor(后继)、insert及delete分别做介绍。

1:创建与插入

这里创建一棵二叉查找树采用了两种方法,一种是普通的递归,另外一种采用的是插入(非递归)。

代码如下:

// 创建BST
void BSTree::createBST(BSTNode *p,const int &value){
	if(p == NULL)return;
	if(p->val > value){
		if(p->left == NULL){
			p->left = new BSTNode();
			p->left->val = value;
			p->left->left = NULL;
			p->left->right = NULL;
			p->left->parent = p;
		}else{
			createBST(p->left, value);
		}
	}
	else{
		if(p->right == NULL){
			p->right = new BSTNode();
			p->right->val = value;
			p->right->left = NULL;
			p->right->right = NULL;
			p->right->parent = p;
		}else{
			createBST(p->right, value);
		}
	}
}
// 插入值
void BSTree::insertBST(BSTNode *p, const int &value){
	BSTNode *y = NULL;
	BSTNode *in = new BSTNode();
	in->left = NULL;
	in->right = NULL;
	in->val = value;
	in->parent = NULL;

	while(p != NULL){
		y = p;
		if(p->val > in->val) p = p->left;
		else p = p->right;
	}

	if(y == NULL)
		p = in;
	else{
		in->parent = y;
		if(y->val > in->val) y->left = in;
		else y->right = in;
	}
}<strong>
</strong>

2:中序遍历

采用递归左子树、输出结点值和递归右子树就可以了。

代码如下:

//  中序遍历BST
void BSTree::inorderBST(BSTNode *p){
	if(p == NULL) return;
	if(p->left != NULL) inorderBST(p->left);
	cout << p->val << " ";
	if(p->right != NULL) inorderBST(p->right);
}

3:minimum和maximum

最小值为最左边的结点,最大值为最右边的结点。

代码如下:

// 最大值和最小值
BSTNode* BSTree::minBST(BSTNode *p){
	while(p->left != NULL)
		p = p->left;
	return p;
}

BSTNode* BSTree::maxBST(BSTNode *p){
	while(p->right != NULL)
		p = p->right;
	return p;
}

4:查找

这里给出递归和迭代两种。

代码如下:

// 查找BST
BSTNode* BSTree::searchBST(BSTNode *p, const int &value){
	if(p == NULL || p->val == value) return p;
	if(value < p->val) return searchBST(p->left, value);
	else return searchBST(p->right, value);
}

// 迭代
BSTNode* BSTree::iterSearchBST(BSTNode *p, const int &value){
	while(p != NULL && p->val != value){
		if(value < p->val) p = p->left;
		else p = p->right;
	}
	return p;
}

5:predecessor(前驱)和successor(后继)

前驱:如果p的左子树不为空,则p的左子树的最大值即为p的前驱;否则令y为p的父节点,如果y不为空,设y为其前驱,y是p的最低祖先结点,y的右孩子也是p的祖先。

后继:如果p的右子树不为空,则p的右子树的最小值即为p的后继;否则令y为p的父节点,如果y不为空,设y为其后继,y是p的最低祖先结点,y的左孩子也是p的祖先。

代码如下:

// 后继与前驱
BSTNode* BSTree::successor(BSTNode *p){
	if(p->right != NULL) return minBST(p->right);   //  如果它的右子树不为空
	BSTNode *y = p->parent;
	while(y != NULL && y->right == p){       // 如果不为空 设y为p后继, y是p的最低祖先节点,y的左孩子也是p的祖先
		p = y;
		y = p->parent;
	}
	return y;
}

BSTNode* BSTree::predecessor(BSTNode *p){
	if(p->left != NULL) return maxBST(p->left);
	BSTNode* y = p->parent;
	while(y != NULL && y->left == p){   // 如果不为空 设y为其前驱, y是p的最低祖先节点,y的右孩子也是p的祖先   如果y->right == p 则y就是前驱
		p = y;
		y = p->left;
	}
	return y;
}

6:delete

待续….

7:销毁二叉树

代码如下:

// 销毁BST
void BSTree::destroyBST(BSTNode *p){
	if(p == NULL) return;
	if(p->left != NULL){
		destroyBST(p->left);
	}
	if(p->right != NULL){
		destroyBST(p->right);
	}
	delete p;
}

完整代码:

BSTree.h

#ifndef BSTREE
#define BSTREE
#include <iostream>
using namespace std;
class BSTNode{
public:
	BSTNode *left, *right;
	BSTNode *parent;
	int val;
	//friend class BSTree;
};

class BSTree{
public:
	// 初始化根节点
	BSTree(int rootVal){
		root = new BSTNode();
		root->val = rootVal;
		root->left = NULL;
		root->right = NULL;
		root->parent = NULL;
	}
	// 创建二叉查找树
	void createBST(BSTNode *p, const int &value);

	// 中序遍历 数组排序
	void inorderBST(BSTNode *p);
	void destroyBST(BSTNode *p);

	// 查找
	BSTNode* searchBST(BSTNode *p, const int &value);
	BSTNode* iterSearchBST(BSTNode *p, const int &value);

	// 最小值和最大值
	BSTNode* minBST(BSTNode *p);
	BSTNode* maxBST(BSTNode *p);

	//  后继与前驱
	BSTNode *successor(BSTNode *p);
	BSTNode *predecessor(BSTNode *p);

	// 插入值
	void insertBST(BSTNode *p, const int &value);

	BSTNode *root;
};
#endif

BSTree.cpp

#include "BSTree.h"
#include <iostream>
using namespace std;

// 创建BST
void BSTree::createBST(BSTNode *p,const int &value){
	if(p == NULL)return;
	if(p->val > value){
		if(p->left == NULL){
			p->left = new BSTNode();
			p->left->val = value;
			p->left->left = NULL;
			p->left->right = NULL;
			p->left->parent = p;
		}else{
			createBST(p->left, value);
		}
	}
	else{
		if(p->right == NULL){
			p->right = new BSTNode();
			p->right->val = value;
			p->right->left = NULL;
			p->right->right = NULL;
			p->right->parent = p;
		}else{
			createBST(p->right, value);
		}
	}
}

//  中序遍历BST
void BSTree::inorderBST(BSTNode *p){
	if(p == NULL) return;
	if(p->left != NULL) inorderBST(p->left);
	cout << p->val << " ";
	if(p->right != NULL) inorderBST(p->right);
}

// 销毁BST
void BSTree::destroyBST(BSTNode *p){
	if(p == NULL) return;
	if(p->left != NULL){
		destroyBST(p->left);
	}
	if(p->right != NULL){
		destroyBST(p->right);
	}
	delete p;
}

// 查找BST
BSTNode* BSTree::searchBST(BSTNode *p, const int &value){
	if(p == NULL || p->val == value) return p;
	if(value < p->val) return searchBST(p->left, value);
	else return searchBST(p->right, value);
}

// 迭代
BSTNode* BSTree::iterSearchBST(BSTNode *p, const int &value){
	while(p != NULL && p->val != value){
		if(value < p->val) p = p->left;
		else p = p->right;
	}
	return p;
}

// 最大值和最小值
BSTNode* BSTree::minBST(BSTNode *p){
	while(p->left != NULL)
		p = p->left;
	return p;
}

BSTNode* BSTree::maxBST(BSTNode *p){
	while(p->right != NULL)
		p = p->right;
	return p;
}

// 后继与前驱
BSTNode* BSTree::successor(BSTNode *p){
	if(p->right != NULL) return minBST(p->right);   //  如果它的右子树不为空
	BSTNode *y = p->parent;
	while(y != NULL && y->right == p){       // 如果不为空 设y为p后继, y是p的最低祖先节点,y的左孩子也是p的祖先
		p = y;
		y = p->parent;
	}
	return y;
}

BSTNode* BSTree::predecessor(BSTNode *p){
	if(p->left != NULL) return maxBST(p->left);
	BSTNode* y = p->parent;
	while(y != NULL && y->left == p){   // 如果不为空 设y为其前驱, y是p的最低祖先节点,y的右孩子也是p的祖先   如果y->right == p 则y就是前驱
		p = y;
		y = p->left;
	}
	return y;
}

// 插入值
void BSTree::insertBST(BSTNode *p, const int &value){
	BSTNode *y = NULL;
	BSTNode *in = new BSTNode();
	in->left = NULL;
	in->right = NULL;
	in->val = value;
	in->parent = NULL;

	while(p != NULL){
		y = p;
		if(p->val > in->val) p = p->left;
		else p = p->right;
	}

	if(y == NULL)
		p = in;
	else{
		in->parent = y;
		if(y->val > in->val) y->left = in;
		else y->right = in;
	}
}

Main.cpp

// 二叉查找树
	int a[10] = {5,4,6, 7,2,4, 1, 8, 5, 10};
	BSTree bst(a[0]);
	for(int i = 1; i < 10; i++)
		bst.insertBST(bst.root, a[i]);  // 通过插入值来构建一棵二叉查找树
		//bst.createBST(bst.root, a[i]);  // 通过递归操作来创建一棵二叉查找树

	// 中序遍历输出
	bst.inorderBST(bst.root);
	cout << endl;

	BSTNode *s = bst.iterSearchBST(bst.root, 5);
	cout << s->val << " 左孩子: " << s->left->val << " 右孩子: " << s->right->val <<endl;

		//  查找最小值
	BSTNode *min = bst.minBST(bst.root);
	cout << "最小值为: " << min->val << endl;

	// 查找最大值
	BSTNode *max = bst.maxBST(bst.root);
	cout << "最大值为: " << max->val << endl;

	// 后继
	BSTNode *suc = bst.successor(s);
	cout << "后继: " << suc->val << endl;

	// 前驱
	BSTNode *pre = bst.predecessor(s);
	cout << "前驱: " << pre->val << endl;

	// 插入6
	bst.insertBST(bst.root, 6);
	cout << "插入元素后中序遍历: " << endl;
	bst.inorderBST(bst.root);
	cout << endl;

	bst.destroyBST(bst.root);
	return 0;

Result:

时间: 2024-12-21 00:13:31

算法系列笔记3(二叉查找树)的相关文章

算法系列笔记5(扩展数据结构-动态顺序统计和区间树)

在编程中,我们往往使用已有的数据结构无法解决问题,这是不必要急着创建新的数据结构,而是在已有数据结构的基础上添加新的字段.本节在上一次笔记红黑树这一基础数据结构上进行扩展,得出两个重要的应用-动态顺序统计和区间树. 动态顺序统计 在算法系列笔记2中我们在线性时间内完成了静态表的顺序统计,而这里我们在红黑树上进行扩展,在O(lgn)时间内完成该操作,主要包括返回第i 排名的元素os_select(i)和给定一个元素x,返回其排名(os_rank(x)). 思想:添加新项:在红黑树的结点上记录下该结

算法系列笔记1(排序)

本次主要记录一些经典的排序算法,其中包括冒泡排序.直接选择排序.插入排序.归并排序.快速排序.堆排序.希尔排序.桶排序以及计数排序和基数排序.首先会给出这些排序算法的基本思想,然后给出实现的代码,最后会给出其时间复杂度. 1:冒泡排序 思想: (1):比较相邻的前后两个元素,如果后面的数据小于前面的数据,则交换这两个数据的位置.这样经过一次遍历,最小的元素将在第0个位置,属于"冒泡". (2):重复第一步,依次将第二小-的元素排列到数组的顶端. // 交换数据的三种方法 void sw

算法系列笔记6(有关图的算法一—搜索,拓扑排序和强连通分支)

简单概念:对于图G(V,E),通常有两种存储的数据结构,一种是邻接矩阵,此时所需要的存储空间为O(V^2):第二种是邻接表,所需要的存储空间为O(V+E).邻接表表示法存在很强的适应性,但是也有潜在的不足,当要快速的确定图中边(u,v)是否存在,只能在顶点u的邻接表中搜索v,没有更快的方法,此时就可以使用邻接矩阵,但要以占用更多的存储空间作为代价:此外当图不是加权的,采用邻接矩阵存储还有一个优势:在存储邻接矩阵的每个元素时,可以只用一个二进位,而不必用一个字的空间. 图的搜索算法 搜索一个图示有

算法系列笔记4(红黑树)

随机构建的二叉查找树的高度期望值为O(lgn),并不代表所有的二叉查找树的高度都为O(lgn).但是对于有些二叉查找树的变形来说,动态集合各基本操作的性能却总是很好的,如红黑树.B树.平衡二叉树(AVL树).跳跃表(确切的说不是树,或多或少有树的结构).treaps(树堆)等.这里我们讲解红黑树. 平衡的意思就是完成动态数据集的操作(minimum.maximum.search.predecessor(前驱).successor(后继).insert及delete)在O(lgn)时间内完成. 定

算法系列笔记2(静态表顺序统计-随机选择算法)

问题:当给定存在静态表(如数组)中的n个元素,如何快速找到其中位数.最小值.最大值.第i小的数? 首先想到的方法是先对数组元素进行排序,然后找到第i小的元素.这样是可行的,但比较排序最快也需要O(nlgn),能否在线性时间内解决呢.这就是随机的分治法-随机选择. 思想:利用随机划分(在快速排序中介绍过)找到主元r,这样就将小于等于r的元素放在了其左边,大于r的元素放在了其右边.这是可以计算出r的rank为k,如果正好等于i,则就返回该元素:如果k大于i,则在左边中寻找第i小的元素,否则在右边中寻

算法系列笔记8(有关图的算法二—最短路径问题)

图的最短路径问题主要分为两类,单源最短路径问题和全对最短路径问题.单源最短路径问题指给点单个源点,求其到所有其它顶点之间的最短距离.而全对最短路径问题指所有顶点之间的最短路劲问题.此外对于单对最短路径问题,从渐进意义上来看,目前还没有比最好的单元算法更快的算法来解决这一问题. 一:单源最短路径问题 单源最短路劲问题根据其权重分为四类,当图G=(V,E)为无权图,直接使用广度优先遍历(这里不做介绍):当权值为非负值,则使用Dijkstra算法:存在负权值及负权环,可以使用Bellman-Ford算

算法系列笔记9(字符串匹配)

字符串匹配指有一个文本串S和一个模式串P,现在要查找P在S中的位置. 主要有以下算法: 其中朴素算法和KMP算法我们在这边bloghttp://blog.csdn.net/lu597203933/article/details/41124815中已经讲解过.RP算法时间复杂度较高,我也没看,想看可以看算法导论.这里主要讲解有限自动机的字符串匹配算法. 有限自动机的定义: 有限自动机字符串匹配主要是构建一个状态转移函数.&(q,a)表示状态q<其中状态q表示已经匹配成功q个字符了>接收字

算法系列笔记10(有关图的算法三—最大流与二分图)

本次主要记录流网络以及最大流的简单概念(以后可能会将最大流的实现算法补充),重点讲解用匈牙利算法来求二分图的最大匹配. 1:流网络 流网络是G(V, E)是一个有限的有向图,它的每条边(u, v)∈E都有一个非负值实数的容量c(u, v)≥0.如果(u, v)不属于E,我们假设c(u, v) = 0.我们区别两个顶点: 一个源点s和一个汇点t..并假定每个顶点均处于从源点到汇点的某条路径上. 形式化的定义:一道网络流是一个对于所有结点u和v都有以下特性的实数函数::满足下面两条性质: 容量限制:

算法系列笔记6(动态规划—最长公共子序列/串lcs)

子序列要求元素顺序一致就可以了,而字串必须是连续的.如ABCBDAB与BDCABA两个字符串,最长公共子序列有BCBA.BDAB和BCAB, 而最长公共字串只有AB和BD<连续>.当然这里的求解只求一个,但通常是这样直接说求最长公共子串,子序列,准确的应该是之一. 最长公共子序列 法一:穷举法 检查字符串x所有字序列,共有2^m个,检查它是否在y字符串中出现,每个需要O(n),时间复杂度为指数级的. 法二:动态规划(DP) 将两个字符串x[1-m]和y[1-n]放在x轴和y轴方向上便得到一个二