??xml version="1.0" encoding="utf-8" standalone="yes"?>
单源最短\径问题,卛_图中求出l定点到其它Q一点的最短\径。在弄清楚如何求单源最短\径问题之前,必须弄清楚最短\径的最优子l构性质?/p>
一.最短\径的最优子l构性质
该性质描述为:如果P(i,j)={Vi....Vk..Vs...Vj}是从点i到j的最短\径,k和s是这条\径上的一个中间顶点,那么P(k,s)必定是从k到s的最短\径。下面证明该性质的正性?/p>
假设P(i,j)={Vi....Vk..Vs...Vj}是从点i到j的最短\径,则有P(i,j)=P(i,k)+P(k,s)+P(s,j)。而P(k,s)不是从k到s的最短距,那么必定存在另一条从k到s的最短\径P'(k,s)Q那么P'(i,j)=P(i,k)+P'(k,s)+P(s,j)<P(i,j)。则与P(i,j)是从i到j的最短\径相矛盾。因此该性质得证?/p>
?Dijkstra法
׃q性质可知Q如果存在一条从i到j的最短\?Vi.....Vk,Vj)QVk是Vj前面的一点。那?Vi...Vk)也必定是从i到k的最短\径。ؓ了求出最短\径,Dijkstra提Z以最短\径长度递增Q逐次生成最短\径的法。譬如对于源点V0Q首先选择其直接相ȝ点中长度最短的点ViQ那么当前已知可得从V0到达Vj点的最短距dist[j]=min{dist[j],dist[i]+matrix[i][j]}。根据这U思\Q?/p>
假设存在G=<V,E>Q源点为V0QU={V0},dist[i]记录V0到i的最短距,path[i]记录从V0到i路径上的i前面的一个顶炏V?/p>
1.从V-U中选择使dist[i]值最的点iQ将i加入到U中;
2.更新与i直接盔R点的dist倹{?dist[j]=min{dist[j],dist[i]+matrix[i][j]})
3.知道U=VQ停止?/p>
代码实现:
所谓逆序敎ͼ是指一个序列S[i]Q统计处于序列的每个数的比这个数大ƈ且排在它前面的数的数目,然后对于所有数Q把q个数目加v来求和就是了?br />比如 4 3 1 2
4W一个,所以数目ؓ0
3的前面是4Q大?的数目ؓ1
1的前面是4 3 Q大?的数目ؓ2
2的前面是4 3 1Q大?的数目ؓ2
所以逆序Cؓ1+2+2 = 5
求逆序数的两种Ҏ
常规Ҏ是按照逆序数的规则做,l果复杂度是O(n*n)Q一般来_有两U快速的求逆序数的Ҏ
分别是归q排序和树状数组?/p>
2. 归ƈ排序
归ƈ排序是源于分而治之思想Q详l的q程可以查阅其他资料QM思想是划分一半,各自排好序后两个有序序列合qv来?/p>
如何修改归ƈ排序求逆序?
首先我们假设两个有序序列 a[i]和b[i]Q当合ƈӞ
׃a[i]已是有序Q所以对于a[i]的各个元素来_排在它前面且比它大的数目都是0
当b[i]中含有比a[i]的元素Ӟ我们必然b[i]元素插到前面Q那么就是说Q在b[i]原先位置到该插的位置中,所有数都比b[i]大且排在它前?br />所以这是b[i]的数目ؓ新插入位|newPos - 原来位置oldPos
那么对于一半的序列又怎么做呢Q我们知道,归ƈ排序会l向下递归Q而递归完成q回后将是两l有序的序列Qƈ且拿到局部的逆序敎ͼ
所以在Merge函数中添加这一计数操作卛_
代码CZ如下Q?br />
3. 树状数组
求逆序数的另外一U方法是使用树状数组
对于数据,可以直接插入树状数组Q对于大数据Q则需要离散化Q所谓离散化Q就是将
100 200 300 400 500 ---> 1 2 3 4 5
q里主要利用树状数组解决计数问题?/p>
首先按顺序把序列a[i]每个数插入到树状数组中,插入的内Ҏ1Q表C放了一个数到树状数l中?br />然后使用sum操作获取当前比a[i]的敎ͼ那么当前i - sum则表C当前比a[i]大的敎ͼ如此反复直到所有数都统计完Q?br />比如
4 3 1 2
i = 1 : 插入 4 : update(4,1)Qsum(4)q回1Q那么当前比4大的?i - 1 = 0;
i = 2 : 插入 3 : update(3,1)Qsum(3)q回1Q那么当前比3大的?i - 1 = 1;
i = 3 : 插入 1 : update(1,1)Qsum(1)q回1Q那么当前比1大的?i - 1 = 2;
i = 4 : 插入 2 : update(2,1)Qsum(2)q回2Q那么当前比2大的?i - 2 = 2;
q程很明了,所以逆序Cؓ1+2+2=5
代码CZ如下Q?/p>
q种题也是一道经典的面试题,主要考察q制转换l想QCoding质量{?/p>
当我们把十进制{成二q制的时候,我们通过辗{盔RQ取余,逆置余数序列的过E得到新的进制的数。因此我们可以借助q种思想把Mq制转成Nq制的数?/p>
如下是C的详l的实现Ҏ
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 |
void m2n( int m, char * mNum, int n, char * nNum) { int i = 0; char c, *p = nNum; //q是一个考察地方Q是否能用最乘法次数? while (*mNum != '\0' ) i = i*m + *mNum++ - '0' ; //辗{取余 while (i) { *p++ = i % n + '0' ; i /= n; } *p-- = '\0' ; //逆置余数序列 while (p > nNum) { c = *p; *p-- = *nNum; *nNum++ = c; } } |
观察上面的代码,存在着众多的不뀂例如,要对输入参数做检查,数值的大小收到int值最大值的限制{。不q好在一点,该算法的旉复杂度是O(n)的?/p>
我们霚w无敌的n大叔又提供了一U用Java实现的通用的进制{换方法,即Windows的计器也{不了的大敎ͼq个法也可以{。算和上面的法相比Q他的基本思想不变Q还是辗转除Q但是用了字W串做大数相除,很不错的创新点,赞一个。代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69 |
package test; /** * 功能Q将一个数从Mq制转换成Nq制 * MValueQMq制数的字符串表C方? * ShangQ保存中间运结? * MQMq制 * NQNq制 */ public class M2N { // 在这里对输入赋? public static String MValue = "1231412423534674574757" ; public static String Shang = null ; public static int M = 10 ; public static int N = 8 ; public static void main(String[] args) { String nValue = "" ; Shang = MValue; while (Shang.length() > 0 ) { nValue = qiuyu(Shang) + nValue; } System.out.println(nValue); } /** * 功能Q对l定的Mq制字符串对n求余? * * @param MTempValue * @param m * @param n * @return */ public static String qiuyu(String MTempValue) { Shang = "" ; int temp = 0 ; while (MTempValue.length() > 0 ) { int t = getIntFromStr(MTempValue.substring( 0 , 1 )); MTempValue = MTempValue.substring( 1 ); temp = temp * M + t; Shang += getStrFromInt(temp / N); temp = temp % N; } while (Shang.length() > 0 && Shang.charAt( 0 ) == '0' ){ Shang = Shang.substring( 1 ); } return getStrFromInt(temp); } public static int getIntFromStr(String str){ return str.charAt( 0 ) <= '9' && str.charAt( 0 ) >= '0' ? str.charAt( 0 ) - '0' : str.charAt( 0 ) - 'a' + 10 ; } public static String getStrFromInt( int value){ String result = null ; if (value>= 0 && value<= 9 ) result = String.valueOf(( char )( '0' + value)); else if (vlaue > 9 && value < 36 ) { result = String.valueOf(( char )( 'a' + value - 10 )); } else { result = "-1" ; // 出错误了 } return result; } } |
赵大叔的法好了不少Q除了参数检查,大小写之外都很好。值得我们借鉴?nbsp;
囄存储l构Q邻接矩c?font face="Calibri">Arcs
工作l构Q结Ҏ mystack;
状态保存结构:
Q?Q?nbsp;VertexStatus[]={0,0,0,1,1,…}。当l点未进栈或者已l出栈,则其对应的状态ؓ0Q否则状态ؓ1Q?/font>
Q?Q?nbsp;ArcStatus[][]={0,0,1,0,1…..}当且仅当边的两个l点都在栈外Ӟ边的状态才?/font>0Q否则ؓ1?/font>
注意我们只所以设计如上结炏V边两个状态存储结构,是依据?font face="Calibri">path的定义,l点不重复,边不重复。具有边状态存储结构,也是我的法与其他算法根本上的不同?/font>
不失一般性,我们假设原点的编hؓ0,目标点的~号最?/font>N。我们的问题转换成了Q求最编L节点与最大编L节点之间的所有\径?br />
Paths={}//路径集合
VertexStatus[]={0};//全部|?/font>0
ArcStatus[][]={0};////全部|?/font>0
mystack.push(0);
VertexStatus[0]=1;
While(!mystack.empty()){
Int elem= mystack.top();//获得栈顶元素
if(elem==N){//扑ֈ了一条\?/font>
path=Traverse(mystack);
Paths.add(path);
VertexStatus[elem]=0;
UpdateArcStatus();//更新ArcStatus[][]Q得所有两个端炚w不在栈内的边的状态ؓ0
mystack.pop();//U除栈顶元素
}else{
i=0;
For(;i<N;i++){
if(VertexStatus[i]=0&&ArcStatus[elem][i]=0&&Arcs.contain(elem,i)){
VertexStatus[i]=1;
ArcStatus[elem][i]=1;
Mystack.push(i);//入栈
break;
}
}
if(i=N){//该节Ҏ有符合要求的后箋节点
VertexStatus[elem]=0;
UpdateArcStaus();////更新ArcStatus[][]Q得所有两个端炚w不在栈内的边的状?/font>0
Mystack.pop();//出栈
}
}
}
已知一个函数rand7()能够生成1-7的随机数Q请l出一个函敎ͼ该函数能够生?-10的随机数?/p>
思\Q?/strong>
假如已知一个函数能够生?-49的随机数Q那么如何以此生?-10的随机数呢?
解法Q?/strong>
该解法基于一U叫做拒l采LҎ。主要思想是只要生一个目标范围内的随机数Q则直接q回。如果生的随机C在目标范围内Q则丢弃该|重新取样。由于目标范围内的数字被选中的概率相{,q样一个均匀的分布生成了?/p>
昄rand7臛_需要执?ơ,否则产生不了1-10的数字。通过q行rand7两次Q可以生?-49的整敎ͼ
1 2 3 4 5 6 7 1 1 2 3 4 5 6 7 2 8 9 10 1 2 3 4 3 5 6 7 8 9 10 1 4 2 3 4 5 6 7 8 5 9 10 1 2 3 4 5 6 6 7 8 9 10 * * 7 * * * * * * *׃49不是10的倍数Q所以我们需要丢弃一些|我们惌的数字范围ؓ1-40Q不在此范围则丢弃ƈ重新取样?/strong>
代码Q?/strong>
E(# calls to rand7) = 2 * (40/49) + 4 * (9/49) * (40/49) + 6 * (9/49)2 * (40/49) + ... ∞ = ∑ 2k * (9/49)k-1 * (40/49) k=1 = (80/49) / (1 - 9/49)2 = 2.45优化Q?/strong>
上面的方法大概需?.45ơ调用rand7函数才能得到1?-10范围的数Q下面可以进行再度优化?/p>
对于大于40的数Q我们不必马上丢弃,可以?1-49的数减去40可得?-9的随机数Q而rand7可生?-7的随机数Q这样可以生?-63的随机数。对?-60我们可以直接q回Q?1-63则丢弃,q样需要丢弃的数只?个,相比前面?个,效率有所提高。而对?1-63的数Q减?0后ؓ1-3Qrand7产生1-7Q这样可以再度利用?-21的数Q对?-20我们则直接返回,对于21则丢弃。这Ӟ丢弃的数只?个了Q优化又q一步。当然这里面对rand7的调用次C是增加了的。代码如下:
E(# calls to rand7) = 2 * (40/49) + 3 * (9/49) * (60/63) + 4 * (9/49) * (3/63) * (20/21) + (9/49) * (3/63) * (1/21) * [ 6 * (40/49) + 7 * (9/49) * (60/63) + 8 * (9/49) * (3/63) * (20/21) ] + ((9/49) * (3/63) * (1/21))2 * [ 10 * (40/49) + 11 * (9/49) * (60/63) + 12 * (9/49) * (3/63) * (20/21) ] + ... = 2.2123q里期望ơ数?.21Q比h优化?.45ơ减了大概10%?/span>
最镉K增子序列问题:在一列数中寻找一些数Q这些数满QQ意两个数a[i]和a[j]Q若i<jQ必有a[i]<a[j]Q这h长的子序列称为最镉K增子序列?/span>
设dp[i]表示以i为结最镉K增子序列的长度Q则状态{ULEؓQ?/span>
dp[i] = max{dp[j]+1}, 1<=j<i,a[j]<a[i].
q样单的复杂度ؓO(n^2)Q其实还有更好的Ҏ?/span>
考虑两个数a[x]和a[y]Qx<y且a[x]<a[y],且dp[x]=dp[y]Q当a[t]要选择Ӟ到底取哪一个构成最优的呢?昄选取a[x]更有潜力Q因为可能存在a[x]<a[z]<a[y]Q这样a[t]可以获得更优的倹{在q里l我们一个启C,当dp[t]一hQ尽量选择更小的a[x].
按dp[t]=k来分c,只需保留dp[t]=k的所有a[t]中的最|设d[k]记录q个|d[k]=min{a[t],dp[t]=k}?/span>
q时注意到d的两个特点(重要Q:
1. d[k]在计过E中单调不升Q?nbsp;
2. d数组是有序的Qd[1]<d[2]<..d[n]?/span>
利用q两个性质Q可以很方便的求解:
1. 讑ֽ前已求出的最长上升子序列的长度ؓlenQ初始时?Q,每次d一个新元素xQ?/span>
2. 若x>d[len]Q则直接加入到d的末,且len++Q(利用性质2Q?/span>
否则Q在d中二分查找,扑ֈW一个比x的数d[k]Qƈd[k+1]=xQ在q里x<=d[k+1]一定成立(性质1,2Q?/span>
初期:
一.基本法:
(1)枚D. (poj1753,poj2965)
(2)贪心(poj1328,poj2109,poj2586)
(3)递归和分L.
(4)递推.
(5)构造法.(poj3295)
(6)模拟?(poj1068,poj2632,poj1573,poj2993,poj2996)
?囄?
(1)囄深度优先遍历和广度优先遍?
(2)最短\径算?dijkstra,bellman-ford,floyd,heap+dijkstra)
(poj1860,poj3259,poj1062,poj2253,poj1125,poj2240)
(3)最生成树法(prim,kruskal)
(poj1789,poj2485,poj1258,poj3026)
(4)拓扑排序 (poj1094)
(5)二分囄最大匹?(匈牙利算? (poj3041,poj3020)
(6)最大流的增q\法(KM法). (poj1459,poj3436)
?数据l构.
(1)?(poj1035,poj3080,poj1936)
(2)排序(快排、归q排(与逆序数有?、堆? (poj2388,poj2299)
(3)单ƈ查集的应?
(4)哈希表和二分查找{高效查找法(数的Hash,串的Hash)
(poj3349,poj3274,POJ2151,poj1840,poj2002,poj2503)
(5)哈夫曼树(poj3253)
(6)?nbsp;
(7)trie?静态徏树、动态徏? (poj2513)
?单搜?nbsp;
(1)深度优先搜烦 (poj2488,poj3083,poj3009,poj1321,poj2251)
(2)q度优先搜烦(poj3278,poj1426,poj3126,poj3087.poj3414)
(3)单搜索技巧和剪枝(poj2531,poj1416,poj2676,1129)
?动态规?nbsp;
(1)背包问题. (poj1837,poj1276)
(2)型如下表的简单DP(可参考lrj的书 page149):
1.E[j]=opt{D+w(i,j)} (poj3267,poj1836,poj1260,poj2533)
2.E[i,j]=opt{D[i-1,j]+xi,D[i,j-1]+yj,D[i-1][j-1]+zij} (最长公共子序列)
(poj3176,poj1080,poj1159)
3.C[i,j]=w[i,j]+opt{C[i,k-1]+C[k,j]}.(最优二分检索树问题)
?数学
(1)l合数学:
1.加法原理和乘法原?
2.排列l合.
3.递推关系.
(POJ3252,poj1850,poj1019,poj1942)
(2)数论.
1.素数与整除问?nbsp;
2.q制?
3.同余模运?
(poj2635, poj3292,poj1845,poj2115)
(3)计算Ҏ.
1.二分法求解单调函数相关知?(poj3273,poj3258,poj1905,poj3122)
?计算几何?
(1)几何公式.
(2)叉积和点U的q用(如线D늛交的判定,点到U段的距ȝ). (poj2031,poj1039)
(3)多边型的单算?求面U?和相兛_?点在多边型内,多边型是否相?
(poj1408,poj1584)
(4)凸包. (poj2187,poj1113)
中:
一.基本法:
(1)C++的标准模版库的应? (poj3096,poj3007)
(2)较ؓ复杂的模拟题的训l?poj3393,poj1472,poj3371,poj1027,poj2706)
?囄?
(1)差分U束pȝ的徏立和求解. (poj1201,poj2983)
(2)最费用最大流(poj2516,poj2195)
(3)双连通分?poj2942)
(4)通分支及其羃?(poj2186)
(5)囄割边和割?poj3352)
(6)最割模型、网l流规约(poj3308, )
?数据l构.
(1)U段? (poj2528,poj2828,poj2777,poj2886,poj2750)
(2)静态二叉检索树. (poj2482,poj2352)
(3)树状树组(poj1195,poj3321)
(4)RMQ. (poj3264,poj3368)
(5)q查集的高应用. (poj1703,2492)
(6)KMP法. (poj1961,poj2406)
?搜烦
(1)最优化剪枝和可行性剪?nbsp;
(2)搜烦的技巧和优化 (poj3411,poj1724)
(3)记忆化搜?poj3373,poj1691)
?动态规?nbsp;
(1)较ؓ复杂的动态规?如动态规划解特别的施行商问题{?
(poj1191,poj1054,poj3280,poj2029,poj2948,poj1925,poj3034)
(2)记录状态的动态规? (POJ3254,poj2411,poj1185)
(3)树型动态规?poj2057,poj1947,poj2486,poj3140)
?数学
(1)l合数学:
1.Ҏ原理.
2.抽屉原理.
3.|换与Polya定理(poj1286,poj2409,poj3270,poj1026).
4.递推关系和母函数.
(2)数学.
1.高斯消元?poj2947,poj1487, poj2065,poj1166,poj1222)
2.概率问题. (poj3071,poj3440)
3.GCD、扩展的Ƨ几里d(中国剩余定理) (poj3101)
(3)计算Ҏ.
1.0/1分数规划. (poj2976)
2.三分法求解单?单谷)的极?
3.矩阵?poj3150,poj3422,poj3070)
4.q代D(poj3301)
(4)随机化算?poj3318,poj2454)
(5)杂题.
(poj1870,poj3296,poj3286,poj1095)
?计算几何?
(1)坐标L?
(2)扫描U算?例如求矩形的面积和周长ƈ,常和U段树或堆一起?.
(poj1765,poj1177,poj1151,poj3277,poj2280,poj3004)
(3)多边形的内核(半^面交)(poj3130,poj3335)
(4)几何工具的综合应?(poj1819,poj1066,poj2043,poj3227,poj2165,poj3429
)
高:
一.基本法要求:
(1)代码快速写?_但不失风?nbsp;
(poj2525,poj1684,poj1421,poj1048,poj2050,poj3306)
(2)保证正确性和高效? poj3434
?囄?
(1)度限制最生成树和第K最短\. (poj1639)
(2)最短\,最生成树,二分?最大流问题的相关理?主要是模型徏立和求解)
(poj3155, poj2112,poj1966,poj3281,poj1087,poj2289,poj3216,poj2446
(3)最优比率生成树. (poj2728)
(4)最树形图(poj3164)
(5)ơ小生成?
(6)无向图、有向图的最环
?数据l构.
(1)trie囄建立和应? (poj2778)
(2)LCA和RMQ问题(LCA(最q公q先问? 有离U算?q查?dfs) ?在线法
(RMQ+dfs)).(poj1330)
(3)双端队列和它的应?l护一个单调的队列,常常在动态规划中起到优化状态{U?br />?nbsp;
目的). (poj2823)
(4)左偏?可合q堆).
(5)后缀?非常有用的数据结?也是赛区考题的热?.
(poj3415,poj3294)
?搜烦
(1)较麻烦的搜烦题目训练(poj1069,poj3322,poj1475,poj1924,poj2049,poj3426)
(2)q搜的状态优?利用Mq制数存储状态、{化ؓ串用hash表判重、按位压~存?br />状态、双向广搜、A*法. (poj1768,poj1184,poj1872,poj1324,poj2046,poj1482)
(3)深搜的优?量用位q算、一定要加剪枝、函数参数尽可能、层C易过?br />、可以考虑双向搜烦或者是轮换搜烦、IDA*法. (poj3131,poj2870,poj2286)
?动态规?nbsp;
(1)需要用数据l构优化的动态规?
(poj2754,poj3378,poj3017)
(2)四边形不{式理论.
(3)较难的状态DP(poj3133)
?数学
(1)l合数学.
1.MoBius反演(poj2888,poj2154)
2.偏序关系理论.
(2)博奕?
1.极大极小q程(poj3317,poj1085)
2.Nim问题.
?计算几何?
(1)半^面求?poj3384,poj2540)
(2)可视囄建立(poj2966)
(3)炚w最圆覆盖.
(4)对踵?poj2079)
?l合?
(poj3109,poj1478,poj1462,poj2729,poj2048,poj3336,poj3315,poj2148,poj1263
)
53.字符串的排列?br />题目Q输入一个字W串Q打印出该字W串中字W的所有排列?br />例如输入字符串abcQ则输出由字Wa、b、c 所能排列出来的所有字W串
abc、acb、bac、bca、cab 和cba?/p>
分析Q此题最初整理于d的微软面?00题中W?3题,W二ơ整理于微Y、Google{公叔R常好的面试题及解{[W?1-70题] W?7题。无独有Ӟq个问题今年又出C今年?011.10.09癑ֺW试题中。okQ接下来Q咱们先好好分析q个问题?br />
U黑树首先是一二叉查找树Q它每个l点都被标上了颜ԌU色或黑ԌQ红黑树满以下5个性质Q?/span>
1?nbsp;每个l点的颜色只能是U色或黑艌Ӏ?/span>
2?nbsp;根结Ҏ黑色的?/span>
3?nbsp;每个叶子l点都带有两个空的黑色结点(被称为黑哨兵Q,如果一个结?/span>n的只有一个左孩子Q那?/span>n的右孩子是一个黑哨兵Q如果结?/span>n只有一个右孩子Q那?/span>n的左孩子是一个黑哨兵?/span>
4?nbsp;如果一个结ҎU的Q则它的两个儿子都是黑的。也是说在一条\径上不能出现盔R的两个红色结炏V?/span>
5?nbsp;对于每个l点来说Q从该结点到其子孙叶l点的所有\径上包含相同数目的黑l点?/span>
U黑树的q?/span>5个性质中,W?/span>3Ҏ比较隄解的Q但它却非常有必要。我们看?/span>1中的左边q张图,如果不用黑哨兵Q它完全满U黑树性质Q结?/span>50C个叶l点8和叶l点82路径上的黑色l点数都?/span>2个。但如果加入黑哨兵后Q如?/span>1叛_中的黑圆点Q,叶结点的个数变ؓ8个黑哨兵Q根l点50到这8个叶l点路径上的黑高度就不一样了Q所以它q不是一늺黑树?/span>
要看真正的红黑树请在以上动画中添加几个结点,看看是否满以上性质?/span>
U黑树的旋{操作?/span>AVL树一P分ؓLL?/span>RR?/span>LR?/span>RL四种旋{cdQ差别在于旋转完成后改变的是l点的颜Ԍ而不是^衡因子。旋转动LC参?/span>AVLq篇文章中的Flash动画Q?/span>
http://www.cnblogs.com/abatei/archive/2008/11/17/1335031.html
在讨论红黑树的插入操作之前必要明白QQ何一个即插入的新结点的初始颜色都ؓU色。这一点很Ҏ理解Q因为插入黑点会增加某条路径上黑l点的数目,从而导致整|黑高度的不^衡。但如果新结点父l点为红色时Q如?/span>2所C)Q将会违q红黑树性质Q一条\径上不能出现盔R的两个红色结炏V这时就需要通过一pd操作来ɾU黑树保持^衡?/span>
Z清楚地表C插入操作以下在l点中?#8220;?#8221;字表CZ个新插入的结点;使用“?#8221;字表C新插入点的父结点;使用“?#8221;字表C?#8220;?#8221;l点的兄弟结点;使用“?#8221;字表C?#8220;?#8221;l点的父l点。插入操作分Z下几U情况:
1?/span>黑父
如图3所C,如果新点的父l点为黑色结点,那么插入一个红点将不会影响U黑树的qQ此时插入操作完成。红黑树?/span>AVL树优U的地方之一在于黑父的情冉|较常见,从而ɾU黑树需要旋转的几率相对AVL树来说会一些?/span>
2Q红?/span>
如果新点的父l点为红Ԍq时需要进行一pd操作以保证整|U黑性质。如?/span>3所C,׃父结点ؓU色Q此时可以判定,父l点必定为黑艌Ӏ这旉要根据叔父结点的颜色来决定做什么样的操作。青色结点表C颜色未知。由于有可能需要根l点到新点的路径上进行多ơ旋转操作,而每ơ进行不q判断的v始点Q我们可其视ؓ新点Q都不一栗所以我们在此用一个蓝色箭头指向这个v始点QƈUC为判定点?/span>
2.1 U叔
当叔父结点ؓU色Ӟ如图4所C,无需q行旋{操作Q只要将父和叔结点变为黑Ԍ祖父结点变为红色即可。但׃父l点的父l点有可能ؓU色Q从而违反红黑树性质。此时必d父l点作ؓ新的判定点l向上进行^衡操作?/span>
需要注意,无论“?#8221;?#8220;?#8221;的左边还是右边,无论“?#8221;?#8220;?#8221;的左孩子q是叛_子,它们的操作都完全一栗?/span>
2.2 黑叔
当叔父结点ؓ黑色Ӟ需要进行旋转,以下囄了所有的旋{可能
情Ş1Q?/span>
情Ş2Q?/span>
情Ş3Q?/span>
情Ş4Q?/span>
可以观察刎ͼ当旋转完成后Q新的旋转根全部为黑Ԍ此时不需要再向上回溯q行q操作Q插入操作完成。需要注意,上面四张囄“?#8221;?#8220;1”?#8220;2”?#8220;3”l点有可能ؓ黑哨늻炏V?/span>
其实U黑树的插入操作不是很难Q甚xAVL树的插入操作q更单些。但删除操作pq比AVL树复杂得多,下面׃l红黑树的删除操作?/span>
U黑树本w是一二叉查找树Q它的删除和二叉查找树的删除cM。首先要扑ֈ真正的删除点Q当被删除结?/span>n存在左右孩子Ӟ真正的删除点应该?/span>n的中序遍在前驱,关于q一点请复习二叉查找树的删除。如?/span>9所C,当删除结?/span>20Ӟ实际被删除的l点应该?/span>18Q结?/span>20的数据变?/span>18?/span>
所以可以推断出Q在q行删除操作Ӟ真正的删除点必定是只有一个孩子或没有孩子的结炏V而根据红黑树的性质可以得出以下两个l论Q?/span>
1?nbsp;删除操作中真正被删除的必定是只有一个红色孩子或没有孩子的结?/span>?/span>
2?nbsp;如果真正的删除点是一个红色结点,那么它必定是一个叶子结?/span>?/span>
理解q两炚w帔R要,如图10所C,除了情况(a)外,其他MU况l点N都无法满红黑树性质?/span>
在以下讨ZQ我们用蓝色箭头表C真正的删除点,它也是旋转操作过E中的第一个判定点Q真正的删除点?#8220;?#8221;标注Q旧Ҏ在位|将被它的的孩子l点所取代Q最多只有一个孩子)Q我们?#8220;?#8221;表示旧点的孩子结炏V删除操作可分ؓ以下几种情ŞQ?/span>
1、旧点ؓU色l点
若旧点ؓU色l点Q则它必定是叶子l点Q直接删除即可。如?/span>11所C?/span>
2、一U一?/span>
当旧点ؓ黑色l点Q新点ؓU色l点Ӟ新点取代旧点位|后Q将新点染成黑色卛_Q如?/span>12所C)。这里需要注意:旧点为红Ԍ新点为黑色的情况不可能存在?/span>
3、双?/span>
当旧点和新点都ؓ黑色Ӟ新点为空l点Ӟ亦属于这U情况)Q情冉|较复杂,需要根据旧点兄弟结点的颜色来决定进行什么样的操作。我们?#8220;?#8221;来表C旧点的兄弟l点。这里可分ؓU兄和黑兄两U情况:
3.1 U兄
׃兄弟l点为红Ԍ所以父l点必定为黑Ԍ而旧点被删除后,新点取代了它的位|。下图演CZ两种可能的情况:
U兄的情况需要进?/span>RR?/span>LL型旋转,然后父l点染成U色Q兄l点染成黑色。然后重C新点为判定点q行q操作。我们可以观察到Q旋转操作完成后Q判定点没有向上回溯Q而是降低了一层,此时变成了黑兄的情况?/span>
3.2 黑兄
黑兄的情冉|为复杂,需要根据黑兄孩子结点(q里?#8220;?#8221;表示Q和父亲l点的颜色来军_做什么样的操作?/span>
3.2.1 黑兄二黑侄红?/span>
如图14所C,q种情况比较单,只需父l点变ؓ黑色Q兄l点变ؓ黑色Q新l点变ؓ黑色卛_Q删除操作到此结束?/span>
3.2.2 黑兄二黑侄黑?/span>
如图15所C,此时父l点染成新结点的颜色Q新l点染成黑色Q兄l点染成U色卛_。当新结点ؓU色Ӟ父结点被染成U色Q此旉要以父结点ؓ判定点l向上进行^衡操作?/span>
3.2.3 黑兄U侄
黑兄U侄有以下四U情形,下面分别q行囄Q?/span>
情Ş1Q?/span>
情Ş2Q?/span>
情Ş3Q?/span>
情Ş4Q?/span>
׃上图例所C,看完以上四张囄兄弟有可能会有一个疑问,如果情Ş1和情?/span>2中的两个侄子l点都ؓU色Ӟ是该q行LL旋{q是q行LR旋{呢?{案是进?/span>LL旋{。情?/span>3和情?/span>4则是优先q行RR旋{的判定?br />U黑树的代码实现