一.浙大机试:stack,动态维护一个栈中的中位数。
时间限制:10ms,命令行cmd的条数N<=100000,由此分析每条命令执行时间为O(n)必定会超时。
引入red-black Tree,由于red-black Tree的insert,delete,search操作时间均为O(lgn),从而可由此下手开始解决。
代码如下:繁琐的地方在于怎么利用两颗红黑树动态维护前(n+1)/2个数和后(n+1)/2个数
// 1057. Stack.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <cstdio>
#include <cstring>
#include <set>
using namespace std;
const int N = 100003;
int arr[N], tArr[N];
//设置两棵红黑树,cset1维护前(n+1)/2个数,cset2维护后(n+1)/2个数
//算法主要在于:随着栈的插入和弹出,怎么动态去维护这两棵红黑树
multiset<int> cset1, cset2;
int main()
{
int n, cnt=0;
scanf("%d", &n);
cset1.clear();
cset2.clear();
while(n--){
char cmd[13];
scanf("%s", cmd);
if(strcmp("Push", cmd) == 0){
int data;
scanf("%d", &data);
arr[cnt++] = data;
//insert data into the multiset
//往cset1红黑树中插入min{data,cset2红黑树中最小值*cset2.begin()}
//cset2红黑树中只能插入max{data,cset2红黑树中最小值*cset2.begin()}
if(cset1.size() < (cnt + 1) / 2){
if(cset2.size() != 0){
int tmp = *cset2.begin();
if(data < tmp){
cset1.insert(data);
}
else{
cset2.erase(cset2.begin());
cset1.insert(tmp);
cset2.insert(data);
}
}
else{
cset1.insert(data);
}
}
else{//cset1无需新的数,但是需要考虑:“data可能会小于cset1中的最大值”这种情况
int curMedian = *(-- cset1.end());
if(data < curMedian){
int tmp = *(-- cset1.end());
cset1.erase(-- cset1.end());
cset1.insert(data);
cset2.insert(tmp);
}
else{
cset2.insert(data);
}
}
}
else if(strcmp("Pop", cmd)==0){
if(0 == cnt)
printf("Invalid\n");
else{
printf("%d\n", arr[-- cnt]);
//delete arr[cnt] from multiset
typedef multiset<int>::iterator MIT;
MIT ite1 = cset1.find(arr[cnt]), ite2 = cset2.find(arr[cnt]);
if(ite1 == cset1.end()){//所删除的数在cset2中
cset2.erase(ite2);
if(!(cnt & 0x01)){//栈的大小为偶数,需将cset1中最大数往cset2移
int tmp = *(-- cset1.end());
cset1.erase(-- cset1.end());
cset2.insert(tmp);
}
}
else{ //所删除的数在cset1中
cset1.erase(ite1);
if(cnt & 0x01){//栈的大小为奇数,由于现在被删除了一个数,所以缺一个数,需将cset2中最小数往cset1中移
int tmp = *(cset2.begin());
cset2.erase(cset2.begin());
cset1.insert(tmp);
}
}
}
}
else{
if(0 == cnt)
printf("Invalid\n");
else{
printf("%d\n", *(-- cset1.end()));
}
}
}
return 0;
}
二.从浙大机试开始学习red-black Tree:
1.red-black Tree的性质:(假设树的节点为n个)
红黑树是每个节点都带有颜色属性的二叉查找树,颜色为红色或黑色。在二叉查找树强制一般要求以外,对于任何有效的红黑树我们增加了如下的额外要求:
性质1. 节点是红色或黑色。
性质2. 根是黑色。
性质3. 所有叶子都是黑色(叶子是NIL节点)。
性质4. 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
性质5. 从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点。[1]
主要思想:由于red-black Tree的性质,其树的最大高度h<=2lg(n+1)(可以将red-black Tree转换为2-3-4树,由此入手证明)。在进行inset和delete操作时,首先插入或者删除特定值,由此会破坏红黑树的第四个性质。由此需要相关算法保持红黑树的性质。
2.insert算法:RB-Insert(T,x)
[2]
[2]
[2]
[2]
[2]
[2]
注:case1只要进行recoloring,算法复杂度为O(1),每次操作都会使得节点向上移动两步,由于red_black Tree的最大高度为2lg(n+1),所以case1最大循环次数为lg(n+1),case1总的算法复杂度为O(lgn);
case2,case3要进行rotation,有趣的是该过程所做的旋转从不超过两次,因为只要执行了case2或case3之后就red-black的性质就满足,即insert算法停止。
综上所述:insert算法总的复杂度为O(lgn)
3.delete算法:RB-delete(T,x)
学习待续中ing.......
所以red-black Tree的插入,删除和查询特定值的算法复杂度均为O(lgn),so cool,因此可以利用red-black Tree动态维护利用multiset(red-black Tree)动态维护中位数。
[1]引自维基百科:http://zh.wikipedia.org/wiki/%E7%BA%A2%E9%BB%91%E6%A0%91
[2]引自MIT“the introduction to algorithm”Lecture