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

在编程中,我们往往使用已有的数据结构无法解决问题,这是不必要急着创建新的数据结构,而是在已有数据结构的基础上添加新的字段。本节在上一次笔记红黑树这一基础数据结构上进行扩展,得出两个重要的应用—动态顺序统计和区间树。

动态顺序统计

算法系列笔记2中我们在线性时间内完成了静态表的顺序统计,而这里我们在红黑树上进行扩展,在O(lgn)时间内完成该操作,主要包括返回第i
排名的元素os_select(i)和给定一个元素x,返回其排名(os_rank(x)).

思想:添加新项:在红黑树的结点上记录下该结点的子树个数。size[x] = size[left[x]] + size[right[x]] +1。 若结点为空,则为0。

此外当你对该扩展的数据结构进行插入和删除操作时,需随时更新子树的大小,与插入和删除操作同步进行,但是需要重新使其回到平衡。主要在于case2和case3这两种情况的旋转。<可以与算法系列笔记4>红黑树的插入代码进行对比,看修改情况。

代码:

返回第i 排名的元素os_select(i)

BSTNode* OSRBTree::os_select(BSTNode *p, const int &ith){
	if(p == NULL) return p;
	int k = 1;
	if(p->left != NULL){
		k = p->left->size + 1;           // 当前该结点所对应的rank

	}
	if(ith == k) return p;
	if(ith < k) return os_select(p->left, ith);
	else return os_select(p->right, ith - k);
}

给定一个元素x,返回其排名(os_rank(x))

//  return the rank of value
int OSRBTree::os_rank(BSTNode *p, const int &value){
	if(p == NULL) return 0;
	int k = 1;
	if(p->left != NULL)
		k = p->left->size + 1;
	if(p->val == value)
		return k;
	else if(p->val > value) return os_rank(p->left, value);
	else return os_rank(p->right, value)+k;
}

完整代码:

OSTree.h

#ifndef OSRBTREE
#define OSRBTREE
#include <iostream>
#include <string>
using namespace std;

class BSTNode{
public:
	BSTNode *left, *right;
	BSTNode *parent;
	int val;
	string color;
	int size;
};

class OSRBTree{
public:
	OSRBTree(const int &rootVal){
		root = new BSTNode();
		root->val = rootVal;
		root->left = NULL;
		root->right = NULL;
		root->color = "black";
		root->size = 1;
		root->parent = NULL;
	}
	BSTNode* insertBST(BSTNode *p, const int &value);
	void insertOSRBTree(BSTNode *root1, const int &value);
	void inorderOSRBTree(BSTNode *p);
	BSTNode* os_select(BSTNode *p, const int &ith);
	int os_rank(BSTNode *p, const int &value);
public:
	BSTNode *root;
};

#endif

OSTree.cpp

#include "OSRBTree.h"

// 二叉查找树的插入
BSTNode* OSRBTree::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;
	in->size = 1;
	while(p != NULL){
		y = p;
		p->size += 1;
		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;
	}
	return in;
}

// 插入红黑树
void OSRBTree::insertOSRBTree(BSTNode *root1, const int &value){
	BSTNode * in = insertBST(root1, value);
	in->color = "red";
	while(in != root1 && in->color == "red"){            // 对红黑特性进行调整
		if(in->parent->color == "black") return;   //  也就保证了必须
		if(in->parent == in->parent->parent->left){
			BSTNode *y = in->parent->parent->right;
			if(y != NULL && y->color == "red"){   //  case 1
				y->color = "black";
				y->parent->color = "red";
				in->parent->color ="black";
				in = in->parent->parent;
			}
			else{
				if(in == in->parent->right){      // case 2   in->parent 左旋
					BSTNode *pa = in->parent;
					in->size = pa->size;    //  修改该结点所包含子树结点个数
					in->parent = pa->parent;
					pa->parent->left = in;
					pa->parent = in;
					if(pa->left != NULL)
						pa->size = pa->left->size + 1;        // 修改结点子树结点大小
					else pa->size = 1;
					if(in->left != NULL){
						in->left->parent = pa;
						pa->size += in->left->size;
					}
					pa->right = in->left;
					in->left = pa;

					in = pa;
				}
				// case 3    in->parent->parent 右旋
				BSTNode *pa = in->parent;
				BSTNode *gpa = in->parent->parent;
				pa->size = gpa->size;
				if(gpa->parent != NULL){
					if(gpa == gpa->parent->left){
						gpa->parent->left = pa;
					}else
						gpa->parent->right = pa;
				}
				pa->parent = gpa->parent;
				if(gpa->right != NULL)gpa->size = gpa->right->size + 1;
				else gpa->size = 1;
				if(pa->right != NULL){
					gpa->size += pa->right->size;
					pa->right->parent = gpa;
				}
				gpa->left = pa->right;
				pa->right = gpa;
				gpa->parent = pa;
				pa->color = "black";
				gpa->color = "red";
			}
		}
		else{
			BSTNode *y = in->parent->parent->left;
			if(y != NULL && y->color == "red"){   //  case 1
				y->color = "black";
				y->parent->color = "red";
				in->parent->color ="black";
				in = in->parent->parent;
			}else{                               // do the same as A  but left与right对换
				if(in == in->parent->left){      // case 2   in->parent 右旋
					BSTNode *pa = in->parent;

					in->size = pa->size;    //  修改该结点所包含子树结点个数

					in->parent = pa->parent;
					pa->parent->right = in;
					pa->parent = in;
					if(pa->right != NULL)
						pa->size = pa->right->size + 1;
					else pa->size = 1;
					if(in->right != NULL){
						in->right->parent = pa;
						pa->size += in->right->size;
					}
					pa->left = in->right;
					in->right = pa;
					in = pa;
				}
				// case 3    in->parent->parent 左旋
				BSTNode *pa = in->parent;
				BSTNode *gpa = in->parent->parent;
				pa->size = gpa->size;
				if(gpa->parent != NULL){
					if(gpa == gpa->parent->left){
						gpa->parent->left = pa;
					}else
						gpa->parent->right = pa;
				}
				pa->parent = gpa->parent;
				if(gpa->left != NULL)gpa->size = gpa->left->size+1;
				else gpa->size = 1;
				if(pa->left != NULL){
					pa->left->parent = gpa;
					gpa->size += pa->left->size;
				}
				gpa->right = pa->left;
				pa->left = gpa;
				gpa->parent = pa;
				pa->color = "black";
				gpa->color = "red";
			}
		}
	}
	root1->color = "black";
}

// 中序遍历输出
void OSRBTree::inorderOSRBTree(BSTNode *p){
	if(p == NULL) return;
	if(p->left != NULL) inorderOSRBTree(p->left);
	cout << p->val << p->color << p->size << " ";
	if(p->right != NULL) inorderOSRBTree(p->right);
}

//  give ith smallest value
BSTNode* OSRBTree::os_select(BSTNode *p, const int &ith){
	if(p == NULL) return p;
	int k = 1;
	if(p->left != NULL){
		k = p->left->size + 1;           // 当前该结点所对应的rank

	}
	if(ith == k) return p;
	if(ith < k) return os_select(p->left, ith);
	else return os_select(p->right, ith - k);
}

//  return the rank of value
int OSRBTree::os_rank(BSTNode *p, const int &value){
	if(p == NULL) return 0;
	int k = 1;
	if(p->left != NULL)
		k = p->left->size + 1;
	if(p->val == value)
		return k;
	else if(p->val > value) return os_rank(p->left, value);
	else return os_rank(p->right, value)+k;
}

Main.cpp

int a[10] = {5,4,6, 7,2,4, 1, 8, 5, 10};
	OSRBTree osbrt(a[0]);
	for(int i = 1; i < 10; i++)
		osbrt.insertOSRBTree(osbrt.root, a[i]);
	cout << "中序遍历的结果: " << endl;
	osbrt.inorderOSRBTree(osbrt.root);
	cout << endl;

	int ith = 6;
	BSTNode *rank = osbrt.os_select(osbrt.root, ith);
	if(rank == NULL) cout << "排名" << ith << "不存在!!" << endl;
	cout << "排名" << ith << ": " << rank->val << endl;

	int x = 6;
	cout << x << "排名为: ";
	cout << osbrt.os_rank(osbrt.root, x) << endl;

Result:

它们的时间复杂度都为O(lgn),因为红黑树的高度为O(lgn)。

问题:为什么不直接使用这些结点排名作为新添加的项呢?原因在于当你此时对树进行修改时,维护这个树就变得很费劲。

方法论:如<OSTree—顺序统计树>

1:选择一个基础的数据结构(red-black tree)

2:在数据统计中维护一些附加信息(子树大小)

3:验证这个数据结构上的信息不会受修改操作的影响(insert, delete---rotations)

4:建立新的运算。假设新的数据已经存好了,然后开始使用这些信息(os_select, os_rank).

区间树(Interval Tree)

问题:保存一系列的区间,比如说时间区间。需要查询集合中的所有区间,与给定区间发生重叠的有哪些?

我们按照上面提到的方法论来进行:

1:选择红黑树作为基本的数据结构,并将区间的较低值(low)作为键值

2:将结点子树的最大值作为新添加的项(m[x] = max{high[int[x]],m[left[x]], m[right[x]]}).

3:是否受插入删除等操作的影响?受,但是在O(1)时间内就能调整过来,见代码。

4:新的操作,查询集合中与给定区间重叠的一个区间。

代码:

IntervalTree.h

#ifndef INTERVALTREE
#define INTERVALTREE
#include <iostream>
#include <string>
using namespace std;
struct dataNode{
	int low;
	int high;
};
class BSTNode{
public:
	BSTNode *left, *right;
	BSTNode *parent;
	int val;
	dataNode d;
	string color;
	int m;         // 最大值
};

class IntervalTree{
public:
	IntervalTree(const dataNode &d)
	{
		root = new BSTNode();
		root->d = d;
		root->color = "black";
		root->left = NULL;
		root->right = NULL;
		root->m = d.high;
		root->parent = NULL;
		root->val = d.low;
	}

	BSTNode* insertBST(BSTNode *p, const dataNode &d);
	void insertIntervalTree(BSTNode *root1, const dataNode &d);
	void inorderOSRBTree(BSTNode *p);
	BSTNode* intervalSearch(BSTNode *p, const dataNode &d);
public:
	BSTNode *root;
	void destroyBST(BSTNode *p);
};

#endif

IntervalTree.cpp

#include "IntervalTree.h"
using namespace std;

BSTNode* IntervalTree::insertBST(BSTNode *p, const dataNode &d){
	BSTNode *y = NULL;
	BSTNode *in = new BSTNode();
	in->left = NULL;
	in->right = NULL;
	in->val = d.low;
	in->parent = NULL;
	in->m = d.high;
	in->d = d;
	while(p != NULL){
		y = p;
		if(p->m < in->m) p->m = in->m;  // 为子树结点的最大值
		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;
	}
	return in;
}

void IntervalTree::insertIntervalTree(BSTNode *root1, const dataNode &d){
	BSTNode * in = insertBST(root1, d);
	in->color = "red";
	while(in != root1 && in->color == "red"){            // 对红黑特性进行调整
		if(in->parent->color == "black") return;   //  也就保证了必须
		if(in->parent == in->parent->parent->left){
			BSTNode *y = in->parent->parent->right;
			if(y != NULL && y->color == "red"){   //  case 1
				y->color = "black";
				y->parent->color = "red";
				in->parent->color ="black";
				in = in->parent->parent;
			}
			else{
				if(in == in->parent->right){      // case 2   in->parent 左旋
					BSTNode *pa = in->parent;
					in->m = pa->m;    //  修改该结点所包含子树结点个数
					in->parent = pa->parent;
					pa->parent->left = in;
					pa->parent = in;
					if(pa->left != NULL)
						pa->m = pa->left->m > pa->m ? pa->left->m : pa->m;

					if(in->left != NULL){
						in->left->parent = pa;
						pa->m = in->left->m > pa->m ? pa->left->m : pa->m;
					}
					pa->right = in->left;
					in->left = pa;

					in = pa;
				}
				// case 3    in->parent->parent 右旋
				BSTNode *pa = in->parent;
				BSTNode *gpa = in->parent->parent;
				pa->m = gpa->m;
				if(gpa->parent != NULL){
					if(gpa == gpa->parent->left){
						gpa->parent->left = pa;
					}else
						gpa->parent->right = pa;
				}
				pa->parent = gpa->parent;
				if(gpa->right != NULL)gpa->m = gpa->right->m > gpa->m ? gpa->right->m : gpa->m;
				if(pa->right != NULL){
					gpa->m = pa->right->m > gpa->m ? pa->right->m : gpa->m;
					pa->right->parent = gpa;
				}
				gpa->left = pa->right;
				pa->right = gpa;
				gpa->parent = pa;
				pa->color = "black";
				gpa->color = "red";
			}
		}
		else{
			BSTNode *y = in->parent->parent->left;
			if(y != NULL && y->color == "red"){   //  case 1
				y->color = "black";
				y->parent->color = "red";
				in->parent->color ="black";
				in = in->parent->parent;
			}else{                               // do the same as A  but left与right对换
				if(in == in->parent->left){      // case 2   in->parent 右旋
					BSTNode *pa = in->parent;

					in->m = pa->m;    //  修改该结点所包含子树结点个数

					in->parent = pa->parent;
					pa->parent->right = in;
					pa->parent = in;
					if(pa->right != NULL)
						pa->m = pa->right->m > pa->m ? pa->right->m : pa->m;
					if(in->right != NULL){
						in->right->parent = pa;
						pa->m = in->right->m > pa->m ? in->right->m : pa->m;
					}
					pa->left = in->right;
					in->right = pa;
					in = pa;
				}
				// case 3    in->parent->parent 左旋
				BSTNode *pa = in->parent;
				BSTNode *gpa = in->parent->parent;
				pa->m = gpa->m;
				if(gpa->parent != NULL){
					if(gpa == gpa->parent->left){
						gpa->parent->left = pa;
					}else
						gpa->parent->right = pa;
				}
				pa->parent = gpa->parent;
				if(gpa->left != NULL)gpa->m = gpa->left->m > gpa->m ? gpa->left->m : gpa->m;
				if(pa->left != NULL){
					pa->left->parent = gpa;
					gpa->m = pa->left->m > gpa->m ? pa->left->m : gpa->m;
				}
				gpa->right = pa->left;
				pa->left = gpa;
				gpa->parent = pa;
				pa->color = "black";
				gpa->color = "red";
			}
		}
	}
	root1->color = "black";
}

void IntervalTree::inorderOSRBTree(BSTNode *p){
	if(p == NULL) return;
	if(p->left != NULL) inorderOSRBTree(p->left);
	cout << p->val << p->color << p->m << " ";

	//cout << p->d.low << p->color << p->d.high << " ";
	if(p->right != NULL) inorderOSRBTree(p->right);
}

BSTNode* IntervalTree::intervalSearch(BSTNode *p, const dataNode &d){
	while(p != NULL && (d.low > p->d.high || d.high < p->d.low)){
		if(p->left != NULL && d.low < p->m)
			p = p->left;
		else p = p->right;
	}
	return p;
}

void IntervalTree::destroyBST(BSTNode *p){
	if(p == NULL) return;
	if(p->left != NULL){
		destroyBST(p->left);
	}
	if(p->right != NULL){
		destroyBST(p->right);
	}
	delete p;
}

Main.cpp

int a[6] = {17, 5, 21, 4, 15, 7};
	int b[6] = {19, 11, 23, 8, 18, 10};
	vector<dataNode> data;
	for(int i = 0; i < 6; i++)
	{
		dataNode d;
		d.low = a[i];
		d.high = b[i];
		data.push_back(d);
	}
	IntervalTree interval(data[0]);
	for(int i = 1; i < data.size(); i++){
		interval.insertIntervalTree(interval.root, data[i]);
	}
	cout << "中序遍历的结果: " << endl;
	interval.inorderOSRBTree(interval.root);
	cout << endl;

	dataNode sd;
	sd.low = 18;
	sd.high = 25;
	BSTNode * bst = interval.intervalSearch(interval.root, sd);
	cout << "[" << bst->d.low << "," << bst->d.high << "]" << endl;

Result:

时间复杂度都为O(lgn),因为红黑树的高度为O(lgn)。

时间: 2024-10-27 13:16:48

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

【算法导论】第十一课 扩充的数据结构、动态有序统计和区间树

本节课主要讲了如何构造自己想要的数据结构,或者扩充已有数据结构的功能,以实现想要的特定功能 比如设计一个动态结构,满足功能寻找第k大的数 其做法是维护每个结点的子结点个数来推导其秩,而不维护其秩,因为动态操作会使得其难以维护 红黑树的插入操作 1.树插入 2.rebalance 构造自己需要的扩充数据结构的基本流程 1.选择一个基本的数据结构 例如红黑树 2.决定要添加到结点的基本信息  例如实现查询第k大数功能,应添加的基本信息为所有子树结点之和,而非直接保存该结点键值的秩 3 维持 插入+旋

算法系列(八)数据结构之二叉查找树

在算法系列(七)数据结构之树的基本结构和二叉树的遍历 中介绍了基本的树结构,二叉树的实现和遍历. 这篇文章重点学习一下二叉查找树. 概述 二叉排序树(Binary Sort Tree)又称二叉查找树(Binary Search Tree)二叉搜索树. 二叉查找树(BST)是二叉树的一个重要的应用,它在二叉树的基础上加上了这样的一个性质:对于树中的每一个节点来说,如果有左儿子的话,它的左儿子的值一定小于它本身的值,如果有右儿子的话,它的右儿子的值一定大于它本身的值.二叉查找树的操作一般有插入.删除

算法系列笔记1(排序)

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

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

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

算法系列笔记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).与快排和随机化快速排序算法是做相同的比较,但是顺序不一样.可以证明随

数据结构—动态顺序表的实现

前面我们实现了顺序表,但是我们应该会考虑到一个问题,顺序表一次性创建那么大空间造成的浪费很多,所以在这里,我们需要一个可以动态增长的顺序表来满足我们的需求! 实现中需要注意的是:在这里我们要注意的是首先你应该给顺序表一个容量,当每次满了的时候,进行扩容! 另外,在这里我分别使用了三种的排序算法:插入排序,选择排序,冒泡排序. dynamic_seqlist.h #define _CRT_SECURE_NO_WARNINGS 1 #ifndef __DYNAMIC_SEQLIST_H__ #inc

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

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

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

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

算法系列(六)数据结构之表队列和栈

在http://blog.csdn.net/robertcpp/article/details/51559333一文中,我们讲了排序,这一章来介绍一下基本数据结构:表.队列.栈和它们的简单实现 一.表ADT 1.数组实现顺序表 通过对数组操作,来直接对表进行增删查改操作,这种线性表查找某个位置的元素花费的时间为O(1),但是插入删除元素花费的时间为O(n),如果对表的操作更多的是访问操作,那么选择这种实现更为合适. 下面是一个简单实现 package com.algorithm.list; im