﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>C++博客-编程自动化-随笔分类-算法</title><link>http://www.cppblog.com/AutomateProgram/category/8931.html</link><description>当音乐和传说在深夜中沉寂后，程序的每个字符还在跳动！</description><language>zh-cn</language><lastBuildDate>Mon, 13 Feb 2012 21:56:36 GMT</lastBuildDate><pubDate>Mon, 13 Feb 2012 21:56:36 GMT</pubDate><ttl>60</ttl><item><title>在windows中编译sphinx1.10beta--coreseek(类似)(翻译)</title><link>http://www.cppblog.com/AutomateProgram/archive/2012/02/13/165470.html</link><dc:creator>漂漂</dc:creator><author>漂漂</author><pubDate>Mon, 13 Feb 2012 03:50:00 GMT</pubDate><guid>http://www.cppblog.com/AutomateProgram/archive/2012/02/13/165470.html</guid><wfw:comment>http://www.cppblog.com/AutomateProgram/comments/165470.html</wfw:comment><comments>http://www.cppblog.com/AutomateProgram/archive/2012/02/13/165470.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/AutomateProgram/comments/commentRss/165470.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/AutomateProgram/services/trackbacks/165470.html</trackback:ping><description><![CDATA[<p>在windows中编译sphinx1.10beta <br /><br /></p>
<p>原文地址：<a href="http://blog.aulin.no/compiling-sphinx-110beta-on-windows">http://blog.aulin.no/compiling-sphinx-110beta-on-windows</a><br /><br />下面是引导大家如何在windows上编译sphinx 1.10beta</p>
<p>1. 下载sphinx源码(<a href="http://sphinxsearch.com/downloads/sphinx-1.10-beta.tar.gz">http://sphinxsearch.com/downloads/sphinx-1.10-beta.tar.gz</a>)<br />&nbsp;&nbsp;&nbsp; 注：最新版本在：<a href="http://sphinxsearch.com/downloads/archive/">http://sphinxsearch.com/downloads/archive/</a> 下<br />&nbsp;<br />2. 因为sphinx使用到MySQL, LibExpat and LibIConv,因此在编译之前需要配置这些库：<br />&nbsp;&nbsp; 下载MySQL的开发环境<a href="http://dev.mysql.com/get/Downloads/MySQL-5.1/mysql-5.1.52-win32.msi/from/http://mysql.borsen.dk/">http://dev.mysql.com/get/Downloads/MySQL-5.1/mysql-5.1.52-win32.msi/from/http://mysql.borsen.dk/</a>，安装开发组件<br />&nbsp;&nbsp; 下载LibExpat(<a href="http://garr.dl.sourceforge.net/project/expat/expat_win32/2.0.1/expat-win32bin-2.0.1.exe">http://garr.dl.sourceforge.net/project/expat/expat_win32/2.0.1/expat-win32bin-2.0.1.exe</a>)<br />&nbsp;&nbsp; 下载LibIConv (<a href="http://netcologne.dl.sourceforge.net/project/gnuwin32/libiconv/1.9.2-1/libiconv-1.9.2-1.exe">http://netcologne.dl.sourceforge.net/project/gnuwin32/libiconv/1.9.2-1/libiconv-1.9.2-1.exe</a>)<br />&nbsp;&nbsp; <br />3. 在shpinx.h中可以配置和移除sphinx需要的组件,如可以移除对PostgreSQL 的支持</p>
<p>4. 在visual studiao 08 中打开 Sphinx08.sln </p>
<p>5. 添加mysql 的include路径(C:\Program Files (x86)\MySQL\MySQL Server 5.1\include) to all projects (右击 - Properties - Configuration Properties - C/C++ - General - Additional Include Directories).</p>
<p>6. 添加mysql的lib路径(C:\Program Files (x86)\MySQL\MySQL Server 5.1\lib\opt) to all projects excluding "libsphinx" (右击 - Properties - Configuration Properties - Linker - General - Additional Library Directories)</p>
<p>7. 在除了libsphinx的所有工程中，添加LibExpat的路径(C:\Program Files (x86)\Expat 2.0.1\Bin)(右击 - Properties - Configuration Properties - Linker - General - Additional Library Directories)</p>
<p>8. 在除了libsphinx的所有工程中，添加LibIConv 的路径(C:\Program Files (x86)\GnuWin32\lib)(右击 - Properties - Configuration Properties - Linker - General - Additional Library Directories)</p>
<p>9. 编译Build! (F6)</p><img src ="http://www.cppblog.com/AutomateProgram/aggbug/165470.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/AutomateProgram/" target="_blank">漂漂</a> 2012-02-13 11:50 <a href="http://www.cppblog.com/AutomateProgram/archive/2012/02/13/165470.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>浅谈shuffle算法--播放器的另一种随机算法</title><link>http://www.cppblog.com/AutomateProgram/archive/2010/10/11/129458.html</link><dc:creator>漂漂</dc:creator><author>漂漂</author><pubDate>Mon, 11 Oct 2010 09:25:00 GMT</pubDate><guid>http://www.cppblog.com/AutomateProgram/archive/2010/10/11/129458.html</guid><wfw:comment>http://www.cppblog.com/AutomateProgram/comments/129458.html</wfw:comment><comments>http://www.cppblog.com/AutomateProgram/archive/2010/10/11/129458.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/AutomateProgram/comments/commentRss/129458.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/AutomateProgram/services/trackbacks/129458.html</trackback:ping><description><![CDATA[原文地址：<a href="http://www.cnblogs.com/huaping-audio/archive/2008/09/09/1287985.html">http://www.cnblogs.com/huaping-audio/archive/2008/09/09/1287985.html</a><br><br>
<p>shuffle算法，我把他叫做洗牌算法，它的目标正好与各种的sort算法相反，即把一个有序(或者无序)的一系列元素打乱，以满足需求。<br><br>举个两例子，大家都知道扑克牌，我们每次都需要在摸牌之前把牌洗掉，用来让每个人摸到每张牌的概率尽量相等，增加游戏的随机性和乐趣；还有音频播放器，有一些人不喜欢顺序播放，而喜欢使用随机播放(其实随机播放分为两种,random和shuffle，后文会介绍到)，比如iPod Shuffle的卖点之一就是&#8220;你永远不知道你将要听到的下一首歌曲是什么&#8221;。至少，如果要模拟扑克牌游戏，或者做音频播放器，都要使用shuffle算法，而二者的shuffle算法却有一些区别，一个是一次性的洗牌，另一个则是每次取一首歌。那么怎么实现他们呢？<br><br>扑克牌的shuffle算法：<br><br>下面为了方便和容易读懂，我都用扑克牌来作例子：桌上有n张牌，并且对桌子上的牌进行标号，从0直到n-1。我们的目的是洗这些牌。<br><br>一个比较容易想到的方法是，桌子上有n张扑克牌，我第i次从桌子上等概率随机取一张扑克牌，作为洗牌后牌堆的第i张扑克牌，那么这个算法实现起来应该是这样的：<br><br>伪代码：<br>for i &lt;- 0 to n - 1<br>do d &lt;- Random mod (n - i)<br>&nbsp;&nbsp; shuffle[i] &lt;- deck[d]<br>&nbsp;&nbsp; deck[d] &lt;- deck[n - i]<br><br>其中，deck是洗牌前的序列(0~n-1)，shuffle是洗牌后的序列(0~n-1)，第i次(从0开始数)在剩下的n-i张牌里等概率的取一张牌，把它放到shuffle里。而deck[d] = deck[n - i]这句达到的效果是删除取过的牌。<br><br>这个方法的时间复杂度是O(n)，已经可以接受了，但这个方法还不够好，因为我们需要两个长度为n数组。其实可以很容易得得到下面的方法，解决空间的问题：<br>伪代码：<br>for i &lt;- 0 to n - 1<br>do d &lt;- Random mod (n - i)<br>&nbsp;&nbsp; swap(deck[d], deck[n - i])<br><br>这样，这个算法的道理就有些像选择排序了，第i次(从0开始数)确定第n-i个元素的原位置，并且交换两个位置上的元素。它的复杂读仍然是O(n)，而只需要1个额外的空间来储存交换用的临时变量。<br>这个方法已经是一个比较好的解决方法了(自己认为)，如果你还能写出更好的shuffle算法，请告诉我。<br><br>我相信对洗牌这种东西有了解的人都不会用这样的方法来洗牌：另外对每张牌做一个标记，即是否抽过这张牌：然后第i次在n张牌里随机抽一个，如果这张牌曾经被抽过，那么把它放回去，重复抽取，直到抽到一张没被抽过的牌，将这张牌标记为抽取过的牌，然后在纸上的第i个地方记下这张牌。在计算机里这样实现：<br><br>伪代码：<br>for i &lt;- 0 to n - 1<br>do d &lt;- Random mod n<br>&nbsp;&nbsp; while did[d] = 1<br>&nbsp;&nbsp; do d = Random mod n<br>&nbsp;&nbsp; did[d] &lt;- 1<br>&nbsp;&nbsp; shuffle[i] &lt;- deck[d]<br><br><br>看了描述，你一定就会觉得这种方法实在是遭透了，不仅麻烦，而且会有一个陷阱，那就是在某次取牌的时候，也许会运气差永远也取不到没有被取过的那张牌，导致程序运行的不确定性。然而，在初学者当中，却有不少是用这种方法实现的shuffle的。个人认为，在设计算法的时候，越简单、越接近生活的模型，就越容易设计出好的算法，而且算法的描述也更接近实际生活。因此，设计算法的时候，如果能往平时生活的方面想， 总是事半功倍的。<br><br>附上我自己实现的一个类qsort的shuffle算法<br><br>// element_Size is the size of each element <br>&nbsp;<br>void swap(void const *element1, void const *element2, size_t element_Size) <br>{ <br>&nbsp;&nbsp;&nbsp; char *temp = new char, <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *elem1, *elem2; <br>&nbsp;&nbsp;&nbsp; elem1 = (char *)element1; <br>&nbsp;&nbsp;&nbsp; elem2 = (char *)element2; <br>&nbsp;&nbsp;&nbsp; for(int i = 0; i &lt; element_Size; i++, elem1++, elem2++){ <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *temp = *elem1; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *elem1 = *elem2; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; *elem2 = *temp; <br>&nbsp;&nbsp;&nbsp; } <br>&nbsp;&nbsp;&nbsp; delete temp; <br>} <br>&nbsp;<br>// array_Size is the size of array, <br>// element_Size is the size of each element in array <br>&nbsp;<br>void shuffle(void const *array, size_t array_Size, size_t element_Size) <br>{ <br>&nbsp;&nbsp;&nbsp; void *element1, *element2; <br>&nbsp;&nbsp;&nbsp; srand(time(0)); <br>&nbsp;&nbsp;&nbsp; for(int i = 0; i &lt; array_Size / element_Size; i++){ <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; element1 = (char *)array + i * element_Size; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; element2 = (char *)array + rand(i * element_Size, <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; array_Size - element_Size, element_Size); <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; swap(element1, element2, element_Size); <br>&nbsp;&nbsp;&nbsp; } <br>}</p>
<p>&nbsp;</p>
<p>播放器的shuffle算法： </p>
<p>前面说过播放器的随机播放有两种，一种叫Random,一种叫Shuffle(我自己理解的......)，下面解释这两种方法的不同。</p>
<p>学过概率的人都该知道有放回的抽取的概念。袋中有n个不同的小球，每次抽取一个小球，然后放回，每一次取的时候概率都是相同的。这正是播放器random算法的原理，这种算法实现起来很简单，一首歌结束以后，只需要随机选取下一首歌就行了。<br>但是这样做有一些缺点：1，有一定的概率使得连续选取的两首歌是同一首歌，我相信并不是所有人都希望在shuffle模式下连续听同一首歌吧，当然也有解决办法，那就是增加层循环判断，如果选上同一首歌，则重新选，而这样又会重蹈那个很烂的洗牌算法的覆辙。2，当听完一首歌的时候，觉得还想再听一遍，怎么办？按下&#8220;上一首&#8221;，你会发现这时听到的歌曲已经不是刚才那一首想听歌曲了，因为这种方法只知道当前的状态，而不知道过去的播放状态。怎么办？一种办法是增加一个队列叫做&#8220;刚才播放列表&#8221;，把播放过的歌曲按照顺序储存在列表里。3，有一定概率在很长的一段时间内，播放器不停的在重复播放两首歌曲A和B或者类似情况，就像这样：...-A-B-A-B-A-B-...。这种情况也是很讨厌的，可是如何避免呢？我能想到的办法是增加判断，看这首歌是不是在列表的最后几项里，如果在就不选这首......</p>
<p>但是这些概率都小的可怜，对于一个播放器的random函数来说，能够考虑到以上的几点，已经能够做到足够random和人性化了。只要能够合理的选择参数，考虑到一些特殊情况(比如极小的播放列表)，以及考虑用户的心理，就能做出一个比较好的random函数。</p>
<p>下面讲我设计的播放器shuffle算法，shuffle算法能够很大程度上避免random算法的缺陷，在空间时间上都很节约，而且能够达到比较理想的随机化效果。它的大体思路是这样的：</p>
<p>我们使用一个隐含的shuffle播放列表(一个循环队列)来储存歌曲的顺序，并用一个指针表示正在播放的歌曲(记作"^")，比如当前的播放列表是这样的：</p>
<p><font face="Courier New">ABCDEFGHIJKLMN<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ^</font></p>
<p>即现在有14首歌，将要播放位置1的歌曲(正在播放位置14的歌曲)，我们认为队列头和尾是相连的，即N后面的元素是A，那么这样够成了一个循环队列。<br>在播放之前，我们在前7(7=14*0.5，这个比例可以随便选，当然越大随机性越大，但能后退的次数越少)个位置中，随机取一个一首歌，把它和将要播放的那个位置的歌曲交换。假设我们选的是E，则队列变成这样：</p>
<p><font face="Courier New">EBCDAFGHIJKLMN<br>^</font></p>
<p>然后播放E。E播放完了以后(或者选择下一首时)，重复刚才的动作，即在BCDAFGH中随机选一个，交换，比如选到H，则队列变成：<br><font face="Courier New">EHCDAFGBIJKLMN<br>&nbsp;^</font></p>
<p>然后播放H。这样，一个shuffle算法初步完成了。</p>
<p>比如某一时刻播放器的状态是这样：<br><font face="Courier New">EHCDAFGBIJKLMN<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ^</font><br>则我们在LMNEHCD中选择一个，比如选择到H，那么交换并播放，成为：<br><font face="Courier New">ELCDAFGBIJKHMN<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ^</font><br>但是如果用户选择上一首怎么办呢?我们可以再记录一个指针指向最新shuffle选择出来的那首歌曲(记作"*")，没有选择过前一首的时候，它与播放指针指向同一个位置。当选择前一首的时候，仅移动指针^，而不移动*，比如上一个例子播放的时候按下前一首以后，成为：</p>
<p><font face="Courier New">ELCDAFGBIJKHMN<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ^*</font></p>
<p>这时候播放的K正好是刚才播放的那一首，当然这达到了我的目的，即可以选到刚才播放的曲目，当然如果再一次选择上一首，就会变成：</p>
<p><font face="Courier New">ELCDAFGBIJKHMN<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ^ *</font></p>
<p>这时候如果按下一首，应该判断^指向的是不是和*指向的相同，如果相同，就按照最早介绍的shuffle算法进行随机选取，不相同就简单的移动^，即成为：</p>
<p><font face="Courier New">ELCDAFGBIJKHMN<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ^*</font></p>
<p>伪代码：<br><font face="Courier New">function keypress(key)<br>&nbsp;&nbsp; if key = NEXT<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if p1 = p2<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; do p1 &lt;- p1 + 1<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; p2 &lt;- p2 + 1<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; k = Random mod (length / 2)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; swap(p1, (p1 + k) mod length)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; play(p2)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; do p2 &lt;- (p2 + 1) mod length<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; play(p2)<br>&nbsp;&nbsp; if key = PREV<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; do p2 &lt;- (p2 + length - 1) mod length<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; play(p2)</font></p>
<p>这个播放器的shuffle算法比较简单实用，而且节约内存开销(这对mp3 walkman之类的东西是十分重要的)，当然也有个小缺点，就是当^前移多次回到*以后，再按下一首，则会重新开始shuffle，但是歌曲数目很多的情况下，这个缺点并不是那么重要。<br>这个算法在刚开始听的时候，并不是很随机，可是随着听的次数的增多，队列会越来越乱，达到一个shuffle的效果。<br>当然，也可以在第一次对这个列表播放之前，使用扑克牌的shuffle算法(见本文第一部分)进行一次shuffle，这样，刚开始播放的时候列表就是随机的。<br>通过原理我们可以看到，对于刚听过的那首歌来说，不经过length / 2次，是不会再一次听到的，因此很大程度上避免了random算法的缺陷。这个length / 2的参数可以按照具体情况选择，可以是常数，也可以是随机数，也可以是和长度有关的一个数。&nbsp;</p>
<img src ="http://www.cppblog.com/AutomateProgram/aggbug/129458.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/AutomateProgram/" target="_blank">漂漂</a> 2010-10-11 17:25 <a href="http://www.cppblog.com/AutomateProgram/archive/2010/10/11/129458.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>协同推荐算法实践之Slope One的介绍（转）</title><link>http://www.cppblog.com/AutomateProgram/archive/2010/07/19/120790.html</link><dc:creator>漂漂</dc:creator><author>漂漂</author><pubDate>Mon, 19 Jul 2010 09:49:00 GMT</pubDate><guid>http://www.cppblog.com/AutomateProgram/archive/2010/07/19/120790.html</guid><wfw:comment>http://www.cppblog.com/AutomateProgram/comments/120790.html</wfw:comment><comments>http://www.cppblog.com/AutomateProgram/archive/2010/07/19/120790.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/AutomateProgram/comments/commentRss/120790.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/AutomateProgram/services/trackbacks/120790.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Slope&nbsp;One&nbsp;之一&nbsp;:&nbsp;简单高效的协同过滤算法(转)(&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;原文地址：http://blog.sina.c...&nbsp;&nbsp;<a href='http://www.cppblog.com/AutomateProgram/archive/2010/07/19/120790.html'>阅读全文</a><img src ="http://www.cppblog.com/AutomateProgram/aggbug/120790.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/AutomateProgram/" target="_blank">漂漂</a> 2010-07-19 17:49 <a href="http://www.cppblog.com/AutomateProgram/archive/2010/07/19/120790.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>傅里叶变换和拉普拉斯变换的意义</title><link>http://www.cppblog.com/AutomateProgram/archive/2010/06/13/117804.html</link><dc:creator>漂漂</dc:creator><author>漂漂</author><pubDate>Sun, 13 Jun 2010 10:19:00 GMT</pubDate><guid>http://www.cppblog.com/AutomateProgram/archive/2010/06/13/117804.html</guid><wfw:comment>http://www.cppblog.com/AutomateProgram/comments/117804.html</wfw:comment><comments>http://www.cppblog.com/AutomateProgram/archive/2010/06/13/117804.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/AutomateProgram/comments/commentRss/117804.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/AutomateProgram/services/trackbacks/117804.html</trackback:ping><description><![CDATA[原文地址：<a href="http://hi.baidu.com/zavierwong/blog/item/1af5c3d0cd34a4da572c849e.html">http://hi.baidu.com/zavierwong/blog/item/1af5c3d0cd34a4da572c849e.html</a><br><br>经常有人问我，傅里叶变换和拉普拉斯变换的意义。在这里我就自己的一些见解，以及结合别人的观点描述如下，希望大家对此有所了解。<br>&nbsp;&nbsp;&nbsp;&nbsp; 傅里叶变换（Transform&#233;e de Fourier）在物理学、数论、组合数学、信号处理、概率论、统计学、密码学、声学、光学、海洋学、结构动力学等领域都有着广泛的应用（例如在信号处理中，傅里叶变换的典型用途是将信号分解成幅值分量和频率分量）。<br>&nbsp;&nbsp;&nbsp;&nbsp; 傅里叶变换能将满足一定条件的某个函数表示成三角函数（正弦和/或余弦函数）或者它们的积分的线性组合。在不同的研究领域，傅里叶变换具有多种不同的变体形式，如连续傅里叶变换和离散傅里叶变换。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 傅里叶变换是一种解决问题的方法，一种工具，一种看待问题的角度。理解的关键是：一个连续的信号可以看作是一个个小信号的叠加，从时域叠加与从频域叠加都可以组成原来的信号，将信号这么分解后有助于处理。<br>　　我们原来对一个信号其实是从时间的角度去理解的，不知不觉中，其实是按照时间把信号进行分割，每一部分只是一个时间点对应一个信号值，一个信号是一组这样的分量的叠加。傅里叶变换后，其实还是个叠加问题，只不过是从频率的角度去叠加，只不过每个小信号是一个时间域上覆盖整个区间的信号，但他确有固定的周期，或者说，给了一个周期，我们就能画出一个整个区间上的分信号，那么给定一组周期值（或频率值），我们就可以画出其对应的曲线，就像给出时域上每一点的信号值一样，不过如果信号是周期的话 ，频域的更简单，只需要几个甚至一个就可以了，时域则需要整个时间轴上每一点都映射出一个函数值。<br>　　傅里叶变换就是将一个信号的时域表示形式映射到一个频域表示形式；逆傅里叶变换恰好相反。这都是一个信号的不同表示形式。它的公式会用就可以，当然把证明看懂了更好。<br>　　对一个信号做傅立叶变换，可以得到其频域特性，包括幅度和相位两个方面。幅度是表示这个频率分量的大小，那么相位呢，它有什么物理意义？频域的相位与时域的相位有关系吗？信号前一段的相位（频域）与后一段的相位的变化是否与信号的频率成正比关系。<br>　　傅立叶变换就是把一个信号，分解成无数的正弦波（或者余弦波）信号。也就是说，用无数的正弦波，可以合成任何你所需要的信号。<br>　　想一想这个问题：给你很多正弦信号，你怎样才能合成你需要的信号呢？答案是要两个条件，一个是每个正弦波的幅度，另一个就是每个正弦波之间的相位差。所以现在应该明白了吧，频域上的相位，就是每个正弦波之间的相位。　　<br>　　傅立叶变换用于信号的频率域分析，一般我们把电信号描述成时间域的数学模型，而数字信号处理对信号的频率特性更感兴趣，而通过傅立叶变换很容易得到信号的频率域特性　<br>　　傅里叶变换简单通俗理解就是把看似杂乱无章的信号考虑成由一定振幅、相位、频率的基本正弦（余弦）信号组合而成，傅里叶变换的目的就是找出这些基本正弦（余弦）信号中振幅较大（能量较高）信号对应的频率，从而找出杂乱无章的信号中的主要振动频率特点。如减速机故障时，通过傅里叶变换做频谱分析，根据各级齿轮转速、齿数与杂音频谱中振幅大的对比，可以快速判断哪级齿轮损伤。
<p><br>拉普拉斯变换（Laplace Transform)，是工程数学中常用的一种积分变换。<br>&nbsp;&nbsp;&nbsp; 它是为简化计算而建立的实变量函数和复变量函数间的一种函数变换。对一个实变量函数作拉普拉斯变换，并在复数域中作各种运算，再将运算结果作拉普拉斯反变换来求得实数域中的相应结果，往往比直接在实数域中求出同样的结果在计算上容易得多。拉普拉斯变换的这种运算步骤对于求解线性微分方程尤为有效，它可把微分方程化为容易求解的代数方程来处理，从而使计算简化。在经典控制理论中，对控制系统的分析和综合，都是建立在拉普拉斯变换的基础上的。<br>&nbsp;&nbsp;&nbsp; 引入拉普拉斯变换的一个主要优点，是可采用传递函数代替微分方程来描述系统的特性。这就为采用直观和简便的图解方法来确定控制系统的整个特性（见信号流程图、动态结构图）、分析控制系统的运动过程（见奈奎斯特稳定判据、根轨迹法），以及综合控制系统的校正装置（见控制系统校正方法）提供了可能性。<br>&nbsp;&nbsp;&nbsp; 拉普拉斯变换在工程学上的应用：应用拉普拉斯变换解常变量齐次微分方程，可以将微分方程化为代数方程，使问题得以解决。在工程学上，拉普拉斯变换的重大意义在于：将一个信号从时域上，转换为复频域（s域）上来表示；在线性系统，控制自动化上都有广泛的应用。</p>
<img src ="http://www.cppblog.com/AutomateProgram/aggbug/117804.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/AutomateProgram/" target="_blank">漂漂</a> 2010-06-13 18:19 <a href="http://www.cppblog.com/AutomateProgram/archive/2010/06/13/117804.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>A*寻路初探(转)</title><link>http://www.cppblog.com/AutomateProgram/archive/2008/12/01/68297.html</link><dc:creator>漂漂</dc:creator><author>漂漂</author><pubDate>Mon, 01 Dec 2008 08:39:00 GMT</pubDate><guid>http://www.cppblog.com/AutomateProgram/archive/2008/12/01/68297.html</guid><wfw:comment>http://www.cppblog.com/AutomateProgram/comments/68297.html</wfw:comment><comments>http://www.cppblog.com/AutomateProgram/archive/2008/12/01/68297.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/AutomateProgram/comments/commentRss/68297.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/AutomateProgram/services/trackbacks/68297.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 原文地址：http://www.vckbase.com/document/viewdoc/?id=1422原文出处:http://www.gamedev.net/reference/articles/article2003.aspA*寻路初探原文：Patrick Lester翻译：Panic 2005年3月18日&nbsp;译者序　　很久以前就知道了A*算法，但是从...&nbsp;&nbsp;<a href='http://www.cppblog.com/AutomateProgram/archive/2008/12/01/68297.html'>阅读全文</a><img src ="http://www.cppblog.com/AutomateProgram/aggbug/68297.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/AutomateProgram/" target="_blank">漂漂</a> 2008-12-01 16:39 <a href="http://www.cppblog.com/AutomateProgram/archive/2008/12/01/68297.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>红黑树（Red-Black Tree）(转)</title><link>http://www.cppblog.com/AutomateProgram/archive/2008/11/22/67581.html</link><dc:creator>漂漂</dc:creator><author>漂漂</author><pubDate>Sat, 22 Nov 2008 06:16:00 GMT</pubDate><guid>http://www.cppblog.com/AutomateProgram/archive/2008/11/22/67581.html</guid><wfw:comment>http://www.cppblog.com/AutomateProgram/comments/67581.html</wfw:comment><comments>http://www.cppblog.com/AutomateProgram/archive/2008/11/22/67581.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/AutomateProgram/comments/commentRss/67581.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/AutomateProgram/services/trackbacks/67581.html</trackback:ping><description><![CDATA[原文地址：<a href="http://imlazy.ycool.com/post.1104022.html">http://imlazy.ycool.com/post.1104022.html</a><br>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">（阅读本文之前请先了解二叉搜索树）<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: center" align=center><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">红黑树（<span lang=EN-US>Red-Black Tree</span>）<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 24pt; mso-char-indent-count: 2.0"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">红黑树（<span lang=EN-US>Red-Black Tree</span>）是二叉搜索树（<span lang=EN-US>Binary Search Tree</span>）的一种改进。我们知道二叉搜索树在最坏的情况下可能会<span style="COLOR: red">变成一个链表</span>（当所有节点按从小到大的顺序依次插入后）。而红黑树在每一次插入或删除节点之后都会花<span lang=EN-US>O</span>（<span lang=EN-US>log N</span>）的时间来对树的结构作修改，以<span style="COLOR: red">保持树的平衡</span>。也就是说，红黑树的查找方法与二叉搜索树完全一样；插入和删除节点的的方法前半部分节与二叉搜索树完全一样，而后半部分添加了一些修改树的结构的操作。<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 24pt; mso-char-indent-count: 2.0"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">红黑树的每个节点上的属性除了有一个<span lang=EN-US>key</span>、<span lang=EN-US>3</span>个指针：<span lang=EN-US>parent</span>、<span lang=EN-US>lchild</span>、<span lang=EN-US>rchild</span>以外，<span style="COLOR: red">还多了一个属性：<span lang=EN-US>color</span>。</span>它只能是两种颜色：红或黑。而红黑树除了具有二叉搜索树的所有性质之外，还具有以下<span lang=EN-US>4</span>点性质：<span style="COLOR: red">（为什么只要这些性质就能解决这个问题，其实还是一个问题）<span lang=EN-US><o:p></o:p></span></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">1. </span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">根节点是黑色的。<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">2. </span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">空节点是黑色的（红黑树中，根节点的<span lang=EN-US>parent</span>以及所有叶节点<span lang=EN-US>lchild</span>、<span lang=EN-US>rchild</span>都不指向<span lang=EN-US>NULL</span>，而是指向一个定义好的空节点）。<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">3. </span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">红色节点的父、左子、右子节点都是黑色。<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">4. </span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">在任何一棵子树中，每一条从根节点向下走到空节点的路径上包含的黑色节点数量都相同。<span lang=EN-US><br><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span></span>如下图就是一棵红黑树：<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: center" align=center><span lang=EN-US style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><v:shapetype id=_x0000_t75 coordsize="21600,21600" o:spt="75" o:preferrelative="t" path="m@4@5l@4@11@9@11@9@5xe" filled="f" stroked="f"><v:stroke joinstyle="miter"></v:stroke><v:formulas><v:f eqn="if lineDrawn pixelLineWidth 0"></v:f><v:f eqn="sum @0 1 0"></v:f><v:f eqn="sum 0 0 @1"></v:f><v:f eqn="prod @2 1 2"></v:f><v:f eqn="prod @3 21600 pixelWidth"></v:f><v:f eqn="prod @3 21600 pixelHeight"></v:f><v:f eqn="sum @0 0 1"></v:f><v:f eqn="prod @6 1 2"></v:f><v:f eqn="prod @7 21600 pixelWidth"></v:f><v:f eqn="sum @8 21600 0"></v:f><v:f eqn="prod @7 21600 pixelHeight"></v:f><v:f eqn="sum @10 21600 0"></v:f></v:formulas><v:path o:extrusionok="f" gradientshapeok="t" o:connecttype="rect"></v:path><o:lock v:ext="edit" aspectratio="t"></o:lock></v:shapetype><v:shape id=_x0000_i1025 style="WIDTH: 375pt; HEIGHT: 142.5pt" alt="" type="#_x0000_t75"><v:imagedata o:href="http://node2.foto.ycstatic.com/200602/16/8/22297832.jpg" src="file:///C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\msohtml1\01\clip_image001.jpg"></v:imagedata></v:shape><o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 24pt; mso-char-indent-count: 2.0"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">有了这几条规则，就可以保证整棵树的平衡，也就等于保证了搜索的时间为<span lang=EN-US>O</span>（<span lang=EN-US>log N</span>）。<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 24pt; mso-char-indent-count: 2.0"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">但是在插入、删除节点后，就有可能破坏了红黑树的性质。所以我们要做一些操作来把整棵树修补好。下面我就来介绍一下。<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 24pt; mso-char-indent-count: 2.0"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">首先有一个预备知识，那就是节点的<span lang=EN-US>Left-Rotate</span>和<span lang=EN-US>Right-Rotate</span>操作。所谓<span lang=EN-US>Left-Rotate(x)</span>就是把节点<span lang=EN-US>x</span>向左下方向移动一格，然后让<span lang=EN-US>x</span>原来的右子节点代替它的位置。而<span lang=EN-US>Right-Rotate</span>当然就是把<span lang=EN-US>Left-Rotate</span>左、右互反一下。如下图：<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 24pt; TEXT-ALIGN: center; mso-char-indent-count: 2.0" align=center><span lang=EN-US style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><v:shape id=_x0000_i1026 style="WIDTH: 225pt; HEIGHT: 68.25pt" alt="" type="#_x0000_t75"><v:imagedata o:href="http://node1.foto.ycstatic.com/200602/16/2/22282978.jpg" src="file:///C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\msohtml1\01\clip_image002.jpg"></v:imagedata></v:shape><o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 24pt; mso-char-indent-count: 2.0"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">注意，<span lang=EN-US>Left-Rotate(x)</span>后，<span lang=EN-US>x</span>的右子树变成了原来<span lang=EN-US>y</span>的左子树，<span lang=EN-US>Right-Rotate</span>反之。思考一下，这样一次变换后，仍然<span style="COLOR: red">满足二叉搜索树的性质</span>（<span style="COLOR: red">中序遍历并没有改变</span>）。在红黑树的插入、删除中，要用到很多<span lang=EN-US>Left-Rotate</span>和<span lang=EN-US>Right-Rotate</span>操作。<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red">//</span><span style="COLOR: red; FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">把一个节点向左下方移一格，并让他原来的右子节点代替它的位置。</span><span lang=EN-US style="COLOR: red"><br>&nbsp;&nbsp;&nbsp;void leftRotate(RBTNode* node)<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red"><span style="mso-spacerun: yes">&nbsp;</span>{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RBTNode* right = node-&gt;rchild;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; node-&gt;rchild = right-&gt;lchild;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; node-&gt;rcount = right-&gt;lcount;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; node-&gt;rchild-&gt;parent = node;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; right-&gt;parent = node-&gt;parent;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (right-&gt;parent == m_null) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_root = right;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else if (node == node-&gt;parent-&gt;lchild) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; node-&gt;parent-&gt;lchild = right;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; node-&gt;parent-&gt;rchild = right;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; right-&gt;lchild = node;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; right-&gt;lcount += node-&gt;lcount + 1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; node-&gt;parent = right;<br>&nbsp;&nbsp;&nbsp; }<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red">&nbsp;&nbsp;&nbsp; //</span><span style="COLOR: red; FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">把一个节点向右下方移一格，并让他原来的左子节点代替它的位置。</span><span lang=EN-US style="COLOR: red"><br>&nbsp;&nbsp;&nbsp; inline void rightRotate(RBTNode* node) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RBTNode* left = node-&gt;lchild;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; node-&gt;lchild = left-&gt;rchild;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; node-&gt;lcount = left-&gt;rcount;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; node-&gt;lchild-&gt;parent = node;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; left-&gt;parent = node-&gt;parent;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (left-&gt;parent == m_null) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_root = left;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else if (node == node-&gt;parent-&gt;lchild) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; node-&gt;parent-&gt;lchild = left;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; node-&gt;parent-&gt;rchild = left;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; left-&gt;rchild = node;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; left-&gt;rcount += node-&gt;rcount + 1;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; node-&gt;parent = left;<br>&nbsp;&nbsp;&nbsp; }<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 24pt; mso-char-indent-count: 2.0"><span lang=EN-US style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><o:p>&nbsp;</o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">一、 插入<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 24pt; mso-char-indent-count: 2.0"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">插入首先是按部就班二叉搜索树的插入步骤，把新节点<span lang=EN-US>z</span>插入到某一个叶节点的位置上。<span lang=EN-US><br><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span></span>接下来把<span lang=EN-US>z</span>的颜色设成<span style="COLOR: red">红色</span>。<span style="COLOR: red">为什么？</span>还记得红黑树的性质吗，从根节点向下到空节点的每一条路径上的黑色节点数要相同。如果新插入的是黑色节点，那么它所在的路径上就多<span style="COLOR: red">出了一个黑色的节点</span>了。所以新插入的节点一定要设成红色。但是这样可能又有一个矛盾，如果<span lang=EN-US style="COLOR: red">z</span><span style="COLOR: red">的父节点也是红色</span>，怎么办，前面说过红色节点的子节点必须是黑色。因此我们要执行下面一个迭代的过程，称为<span lang=EN-US>Insert-Fixup</span>，来修补这棵红黑树。<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 24pt; mso-char-indent-count: 2.0"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">在<span lang=EN-US>Insert-Fixup</span>中，每一次迭代的开始，指针<span lang=EN-US>z</span>一定都指向一个红色的节点。如果<span lang=EN-US>z-&gt;parent</span>是黑色，那我们就大功告成了；如果<span lang=EN-US>z-&gt;parent</span>是红色，显然这就违返了红黑的树性质，那么我们要想办法把<span lang=EN-US>z</span>或者<span lang=EN-US>z-&gt;parent</span>变成黑色，但这要建立在不破坏红黑树的其他性质的基础上。<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 24pt; mso-char-indent-count: 2.0"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">这里再引入两个指针：<span lang=EN-US style="COLOR: red">grandfather</span>，指向<span lang=EN-US>z-&gt;parent-&gt;parent</span>，也就是<span lang=EN-US>z</span>的爷爷<span lang=EN-US>(</span>显然由于<span lang=EN-US>z-&gt;parent</span>为红色，<span lang=EN-US>grandfather</span>一定是黑色<span lang=EN-US>)</span>；<span lang=EN-US>uncle</span>，指向<span lang=EN-US>grandfather</span>除了<span lang=EN-US>z-&gt;parent</span>之外的另一个子节点，也就是<span lang=EN-US>z</span>的父亲的兄弟，所以叫<span lang=EN-US>uncle</span>。<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 24pt; mso-char-indent-count: 2.0"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">（为了说话方便，我们这里都假设<span lang=EN-US>z-&gt;parent</span>是<span lang=EN-US>grandfather</span>的左子节点，而<span lang=EN-US>uncle</span>是<span lang=EN-US>grandfather</span>的右子节点。如果遇到的实际情况不是这样，那也只要把所有操作中的左、右互反就可以了。）<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 24pt; mso-char-indent-count: 2.0"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">在每一次迭代中，我们可能遇到以下三种情况。<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 24pt; mso-char-indent-count: 2.0"><span lang=EN-US style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">Case 1. uncle</span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">也是红色。这时只要把<span lang=EN-US>z-&gt;parent</span>和<span lang=EN-US>uncle</span>都设成黑色，并把<span lang=EN-US>grandfather</span>设成红色。这样仍然确保了每一条路径上的黑色节点数不变。然后把<span lang=EN-US>z</span>指向<span lang=EN-US>grandfather</span>，并开始新一轮的迭代。如下图：<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 24pt; mso-char-indent-count: 2.0"><span lang=EN-US style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><v:shape id=_x0000_i1027 style="WIDTH: 375pt; HEIGHT: 80.25pt" alt="" type="#_x0000_t75"><v:imagedata o:href="http://node3.foto.ycstatic.com/200602/16/d/22289341.jpg" src="file:///C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\msohtml1\01\clip_image003.jpg"></v:imagedata></v:shape><o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 24pt; mso-char-indent-count: 2.0"><span style="FONT-SIZE: 12pt; COLOR: red; FONT-FAMILY: 宋体">注<span lang=EN-US>1</span>：我们可以看出左边的图，各条路径包含黑颜色的数目是正确的，只是颜色不对而已，我们把它分成两边来看，即到节点<span lang=EN-US>D</span>应该包含<span lang=EN-US>N+1</span>个黑色节点，其中这个<span lang=EN-US>1</span>是<span lang=EN-US>C</span>，而<span lang=EN-US>N</span>是<span lang=EN-US>C</span>以上的黑色节点个数。同理<span lang=EN-US>A</span>也应该是<span lang=EN-US>N+1</span>，<span lang=EN-US>B</span>也是<span lang=EN-US>N+1</span>，调整以后，看看我们确实没有改变到<span lang=EN-US>A</span>、<span lang=EN-US>B</span>、<span lang=EN-US>D</span>的所包含的黑色节点数。下面的情况也可以同样的方法来分析。<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 24pt; mso-char-indent-count: 2.0"><span lang=EN-US style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">Case 2. uncle</span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">是黑色，并且<span lang=EN-US>z</span>是<span lang=EN-US>z-&gt;parent</span>的右子节点。这时我们只要把<span lang=EN-US>z</span>指向<span lang=EN-US>z-&gt;parent</span>，然后做一次<span lang=EN-US>Left-Rotate(z)</span>。就可以把情况转化成<span lang=EN-US>Case 3</span>。<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">Case 3. uncle</span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">是黑色，并且<span lang=EN-US>z</span>是<span lang=EN-US>z-&gt;parent</span>的左子节点。到了这一步，我们就剩最后一步了。只要把<span lang=EN-US>z-&gt;parent</span>设成黑色，把<span lang=EN-US>grandfather</span>设成红色，再做一次<span lang=EN-US>Right-Rotate(grandfather)</span>，整棵树就修补完毕了。可以思考一下，这样一次操作之后，确实满足了所有红黑树的性质。<span lang=EN-US>Case 2</span>和<span lang=EN-US>Case 3</span>如下图： <span lang=EN-US><br><br><span style="mso-spacerun: yes">&nbsp;&nbsp; </span></span>反复进行迭代，直到某一次迭代开始时<span lang=EN-US>z-&gt;parent</span>为黑色而告终，也就是当遇到<span lang=EN-US>Case 3</span>后，做完它而告终。<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><v:shape id=_x0000_i1028 style="WIDTH: 327pt; HEIGHT: 93pt" alt="" type="#_x0000_t75"><v:imagedata o:href="http://node0.foto.ycstatic.com/200602/16/8/22292680.jpg" src="file:///C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\msohtml1\01\clip_image004.jpg"></v:imagedata></v:shape><o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt">void insertFixup(RBTNode* insertNode) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RBTNode* p = insertNode;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; while (p-&gt;parent-&gt;color == RED) {<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 68.25pt; mso-char-indent-count: 6.5"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt">//z-&gt;parent</span><span style="COLOR: red; FONT-FAMILY: 宋体; mso-ascii-font-family: 'Courier New'; mso-bidi-font-size: 10.5pt; mso-bidi-font-family: 'Courier New'">是</span><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt">grandfather</span><span style="COLOR: red; FONT-FAMILY: 宋体; mso-ascii-font-family: 'Courier New'; mso-bidi-font-size: 10.5pt; mso-bidi-font-family: 'Courier New'">的左子节点</span><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt">,</span><span style="COLOR: red; FONT-FAMILY: 宋体; mso-ascii-font-family: 'Courier New'; mso-bidi-font-size: 10.5pt; mso-bidi-font-family: 'Courier New'">下面是三种情况</span><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (p-&gt;parent == p-&gt;parent-&gt;parent-&gt;lchild) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RBTNode* parentRight = p-&gt;parent-&gt;parent-&gt;rchild;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (parentRight-&gt;color == RED) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; p-&gt;parent-&gt;color = BLACK;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; parentRight-&gt;color = BLACK;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; p-&gt;parent-&gt;parent-&gt;color = RED;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; p = p-&gt;parent-&gt;parent;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (p == p-&gt;parent-&gt;rchild) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; p = p-&gt;parent;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; leftRotate(p);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; p-&gt;parent-&gt;color = BLACK;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; p-&gt;parent-&gt;parent-&gt;color = RED;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rightRotate(p-&gt;parent-&gt;parent);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; RBTNode* parentLeft = p-&gt;parent-&gt;parent-&gt;lchild;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (parentLeft-&gt;color == RED) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; p-&gt;parent-&gt;color = BLACK;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; parentLeft-&gt;color = BLACK;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; p-&gt;parent-&gt;parent-&gt;color = RED;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; p = p-&gt;parent-&gt;parent;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (p == p-&gt;parent-&gt;lchild) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; p = p-&gt;parent;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rightRotate(p);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; p-&gt;parent-&gt;color = BLACK;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; p-&gt;parent-&gt;parent-&gt;color = RED;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; leftRotate(p-&gt;parent-&gt;parent);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m_root-&gt;color = BLACK;<br>&nbsp;&nbsp;&nbsp; }<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">二、删除<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 24pt; mso-char-indent-count: 2.0"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">让我们来回顾一下二叉搜索树的删除节点<span lang=EN-US>z</span>的过程：如果<span lang=EN-US>z</span>没有子节点，那么直接删除即可；如果<span lang=EN-US>z</span>只有一个子节点，那么让这个子节点来代替<span lang=EN-US>z</span>的位置，然后把<span lang=EN-US>z</span>删除即可；如果<span lang=EN-US>z</span>有两个子节点，那么找到<span lang=EN-US>z</span>在中序遍历中的后继节点<span lang=EN-US>s</span>（也就是从<span lang=EN-US>z-&gt;rchild</span>开始向左下方一直走到底的那一个节点），把<span lang=EN-US>s</span>的<span lang=EN-US>key</span>赋值给<span lang=EN-US>z</span>的<span lang=EN-US>key</span>，然后删除<span lang=EN-US>s</span>。<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 24pt; mso-char-indent-count: 2.0"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">红黑树中删除一个节点<span lang=EN-US>z</span>的方法也是首先按部就班以上的过程。<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 24pt; mso-char-indent-count: 2.0"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">按照二叉搜索树的删除方法删除节点，如果删除节点是<span style="COLOR: red">红色的</span>，那并<span style="COLOR: red">不会改变</span>红黑树的性质。如果删除的节点是<span style="COLOR: red">黑色的</span>，那么显然它所在的路径上就少一个黑色节点，那么红黑树的<span style="COLOR: red">性质就被破坏了</span>。这时我们就要执行一个称为<span lang=EN-US>Delete-Fixup</span>的过程，来修补这棵树。下面我就来讲解一下。<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 30pt; mso-char-indent-count: 2.5"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">一个节点被删除之后，一定有一个它的子节点代替了它的位置（即使是叶节点被删除后，也会有一个空节点来代替它的位置。前面说过，在红黑树中，空节点是一个实际存在的节点。）。我们就设指针<span lang=EN-US>x</span>指向这个代替位置的节点。<span lang=EN-US><br></span>显然，如果<span lang=EN-US>x</span>是红色的，那么我们只要把它设成黑色，它所在的路径上就重新多出了一个黑色节点，那么红黑树的性质就满足了。<span lang=EN-US><br><span style="mso-spacerun: yes">&nbsp;&nbsp;&nbsp; </span></span>然而，如果<span lang=EN-US>x</span>是黑色的，那我们就要假想<span lang=EN-US>x</span>上背负了<span lang=EN-US>2</span>个单位的黑色。那么红黑树的性质也同样不破坏，但是我们要找到某一个红色的节点，把<span lang=EN-US>x</span>上<span lang=EN-US>&#8220;</span>超载<span lang=EN-US>&#8221;</span>的这<span lang=EN-US>1</span>个单位的黑色丢给它，这样才算完成。<span lang=EN-US>Delete-Fixup</span>做的就是这个工作。<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 24pt; mso-char-indent-count: 2.0"><span style="FONT-SIZE: 12pt; COLOR: red; FONT-FAMILY: 宋体">注：删除了一个黑色节点以后，遍历到节点一下的叶子节点比遍历其他分支的叶子节点的黑色节点数就少了一个，这就要是找到一个红色，把这个节点换成黑色来拟补这个删除的黑色节点，使得遍历到叶子节点经过黑色节点的数目一样。<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 30pt; mso-char-indent-count: 2.5"><span lang=EN-US style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">Delete-Fixup</span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">同样是一个循环迭代的过程。每一次迭代开始时，如果指针<span lang=EN-US>x</span>指向一个红色节点，那么大功告成，把它设成黑色即告终。相反如果<span lang=EN-US>x</span>黑色，那么我们就会面对<span style="COLOR: red">以下<span lang=EN-US>4</span>种情况</span>。<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 30pt; mso-char-indent-count: 2.5"><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">这里引入另一个指针<span lang=EN-US>w</span>，指向<span lang=EN-US>x</span>的兄弟。这里我们都默认<span lang=EN-US>x</span>是<span lang=EN-US>x-&gt;parent</span>的左子节点，则<span lang=EN-US>w</span>是<span lang=EN-US>x-&gt;parent</span>的右子节点。（如果实际遇到相反的情况，只要把所有操作中的左、右互反一下就可以了。）<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 24pt; mso-char-indent-count: 2.0"><span lang=EN-US style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">Case 1. w</span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">是红色。这时我们根据红黑树的性质可以肯定<span lang=EN-US>x-&gt;parent</span>是黑色、<span lang=EN-US>w-&gt;lchild</span>是黑色。我们把<span lang=EN-US>x-&gt;parent</span>与<span lang=EN-US>w</span>的颜色互换，然后做一次<span lang=EN-US>Left-Rotate(x-&gt;parent)</span>。做完之后<span lang=EN-US>x</span>就有了一个新的兄弟：原<span lang=EN-US>w-&gt;lchild</span>，前面说过它一定是黑色的。那么我们就在不破坏红黑树性质的前提下，把<span lang=EN-US>Case 1</span>转换成了<span lang=EN-US>Case2</span>、<span lang=EN-US>3</span>、<span lang=EN-US>4</span>中的一个，也就是<span lang=EN-US>w</span>是黑色的情况。思考一下，这样做不会改变每条路径上黑色节点的个数，如下图：<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 24pt; mso-char-indent-count: 2.0"><span lang=EN-US style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><v:shape id=_x0000_i1029 style="WIDTH: 316.5pt; HEIGHT: 75pt" alt="" type="#_x0000_t75"><v:imagedata o:href="http://node0.foto.ycstatic.com/200602/16/c/22276524.jpg" src="file:///C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\msohtml1\01\clip_image005.jpg"></v:imagedata></v:shape><o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 24pt; mso-char-indent-count: 2.0"><span style="FONT-SIZE: 12pt; COLOR: red; FONT-FAMILY: 宋体">注：可以看出这样变化以后就变成了<span lang=EN-US>Case2</span>了。<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 24pt; mso-char-indent-count: 2.0"><span lang=EN-US style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">Case 2. w</span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">是黑色，并且<span lang=EN-US>w</span>的两个子节点都是黑色。这时我们只要把<span lang=EN-US>w</span>设成红色。然后把<span lang=EN-US>x</span>移到<span lang=EN-US>x-&gt;parent</span>，开始下一轮迭代（注意，那<span lang=EN-US>&#8220;</span>超载<span lang=EN-US>&#8221;</span>的<span lang=EN-US>1</span>单位的黑色始终是跟着指针<span lang=EN-US>x</span>走的，直到<span lang=EN-US>x</span>走到了一个红色节点上才能把它<span lang=EN-US>&#8220;</span>卸下<span lang=EN-US>&#8221;</span>）。思考一下，这一次操作不会破坏红黑树的性质。如下图（图中节点<span lang=EN-US>B</span>不一定是红色，也可能是黑色）：<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 24pt; mso-char-indent-count: 2.0"><span lang=EN-US style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><v:shape id=_x0000_i1030 style="WIDTH: 342.75pt; HEIGHT: 75pt" alt="" type="#_x0000_t75"><v:imagedata o:href="http://node3.foto.ycstatic.com/200602/16/b/22290027.jpg" src="file:///C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\msohtml1\01\clip_image006.jpg"></v:imagedata></v:shape><o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 24pt; mso-char-indent-count: 2.0"><span style="FONT-SIZE: 12pt; COLOR: red; FONT-FAMILY: 宋体">注：这里只要把<span lang=EN-US>B</span>变成红色就大功告成了。<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 24pt; mso-char-indent-count: 2.0"><span lang=EN-US style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">Case 3. w</span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">是黑色，并且<span lang=EN-US>w</span>的两个子节点左红右黑。这时我们把<span lang=EN-US>w</span>与<span lang=EN-US>w-&gt;lchild</span>的颜色互换，然后做<span lang=EN-US>Right-Rotate(w)</span>。思考一下，这样做之后不会破坏红黑树的性质。这时<span lang=EN-US>x</span>的新的兄弟就是原<span lang=EN-US>w-&gt;lchild</span>。<span style="COLOR: red">而<span lang=EN-US>Case 3</span>被转化成了<span lang=EN-US>Case 4</span>。</span><span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 24pt; mso-char-indent-count: 2.0"><span lang=EN-US style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><v:shape id=_x0000_i1031 style="WIDTH: 5in; HEIGHT: 93pt" alt="" type="#_x0000_t75"><v:imagedata o:href="http://node3.foto.ycstatic.com/200602/16/a/22295354.jpg" src="file:///C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\msohtml1\01\clip_image007.jpg"></v:imagedata></v:shape><o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 24pt; mso-char-indent-count: 2.0"><span lang=EN-US style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">Case 4. w</span><span style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体">是黑色，并且<span lang=EN-US>w</span>的右子节点是红色。一但遇到<span lang=EN-US>Case 4</span>，就胜利在望了。我看下面一张图。先把<span lang=EN-US>w</span>与<span lang=EN-US>x-&gt;parent</span>的颜色互换，再做<span lang=EN-US>Left-Rotate(x-&gt;parent)</span>。这时图中节点<span lang=EN-US>E</span>（也就是原<span lang=EN-US>w-&gt;rchild</span>）所在的路径就肯定少了一个黑色，而<span lang=EN-US>x</span>所在的路径则多了一个黑色。那么我们就把<span lang=EN-US>x</span>上多余的<span lang=EN-US>1</span>个单位的黑色丢给<span lang=EN-US>E</span>就可以了。至此，<span lang=EN-US>Delete-Fixup</span>就顺利完成了。<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 24pt; mso-char-indent-count: 2.0"><span lang=EN-US style="FONT-SIZE: 12pt; FONT-FAMILY: 宋体"><v:shape id=_x0000_i1032 style="WIDTH: 316.5pt; HEIGHT: 75pt" alt="" type="#_x0000_t75"><v:imagedata o:href="http://node0.foto.ycstatic.com/200602/16/f/22285295.jpg" src="file:///C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\msohtml1\01\clip_image008.jpg"></v:imagedata></v:shape><o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt; TEXT-INDENT: 24pt; mso-char-indent-count: 2.0"><span style="FONT-SIZE: 12pt; COLOR: red; FONT-FAMILY: 宋体">注：通过注<span lang=EN-US>1</span>我们可以看出问题在<span lang=EN-US>Case4</span>后已经解决了。<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt">void delFixup(RBTNode* delNode) {<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp; </span>RBTNode* p = delNode;<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp; </span>while (p != m_root &amp;&amp; p-&gt;color == BLACK) {<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>if (p == p-&gt;parent-&gt;lchild) {//</span><span style="COLOR: red; FONT-FAMILY: 宋体; mso-ascii-font-family: 'Courier New'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-size: 10.5pt; mso-bidi-font-family: 'Courier New'">左边情况，以下是四种不同的</span><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt">Case<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 3">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>RBTNode* sibling = p-&gt;parent-&gt;rchild;<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 3">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>if (sibling-&gt;color == RED) {<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 4">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>sibling-&gt;color = BLACK;<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 4">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>p-&gt;parent-&gt;color = RED;<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 4">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>leftRotate(p-&gt;parent);<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 4">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>sibling = p-&gt;parent-&gt;rchild;<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 3">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 3">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>if (sibling-&gt;lchild-&gt;color == BLACK<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 4">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&amp;&amp; sibling-&gt;rchild-&gt;color == BLACK<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 4">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>) {<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 4">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>sibling-&gt;color = RED;<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 4">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>p = p-&gt;parent;<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 3">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 3">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>else {<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="mso-tab-count: 2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>if (sibling-&gt;rchild-&gt;color == BLACK) {<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 5">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>sibling-&gt;lchild-&gt;color = BLACK;<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 5">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>sibling-&gt;color = RED;<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 5">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>rightRotate(sibling);<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 5">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>sibling<span style="mso-spacerun: yes">&nbsp; </span>= sibling-&gt;parent;<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 4">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 4">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>sibling-&gt;color = sibling-&gt;parent-&gt;color;<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 4">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>sibling-&gt;parent-&gt;color = BLACK;<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 4">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>sibling-&gt;rchild-&gt;color = BLACK;<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 4">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>leftRotate(sibling-&gt;parent);<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 4">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>p = m_root;<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 3">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>else {//</span><span style="COLOR: red; FONT-FAMILY: 宋体; mso-ascii-font-family: 'Courier New'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-size: 10.5pt; mso-bidi-font-family: 'Courier New'">右边情况</span><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 3">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>RBTNode* sibling = p-&gt;parent-&gt;lchild;<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 3">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>if (sibling-&gt;color == RED) {<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 4">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>sibling-&gt;color = BLACK;<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 4">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>p-&gt;parent-&gt;color = RED;<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 4">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>rightRotate(p-&gt;parent);<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 4">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>sibling = p-&gt;parent-&gt;lchild;<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 3">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 3">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>if (sibling-&gt;lchild-&gt;color == BLACK<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 4">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>&amp;&amp; sibling-&gt;rchild-&gt;color == BLACK<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 4">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>) {<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 4">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>sibling-&gt;color = RED;<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 4">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>p = p-&gt;parent;<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 3">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 3">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>else {<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 4">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>if (sibling-&gt;lchild-&gt;color == BLACK) {<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 5">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>sibling-&gt;rchild-&gt;color = BLACK;<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 5">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>sibling-&gt;color = RED;<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 5">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>leftRotate(sibling);<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 5">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>sibling = sibling-&gt;parent;<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 4">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 4">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>sibling-&gt;color = sibling-&gt;parent-&gt;color;<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 4">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>sibling-&gt;parent-&gt;color = BLACK;<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 4">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>sibling-&gt;lchild-&gt;color = BLACK;<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 4">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>rightRotate(sibling-&gt;parent);<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 4">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>p = m_root;<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 3">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>}<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp; </span>}<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt"><span style="mso-tab-count: 1">&nbsp;&nbsp;&nbsp; </span>p-&gt;color = BLACK;<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="COLOR: red; FONT-FAMILY: 'Courier New'; mso-bidi-font-size: 10.5pt">}<o:p></o:p></span></p>
<img src ="http://www.cppblog.com/AutomateProgram/aggbug/67581.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/AutomateProgram/" target="_blank">漂漂</a> 2008-11-22 14:16 <a href="http://www.cppblog.com/AutomateProgram/archive/2008/11/22/67581.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>数学之美番外篇：快排为什么那样快(转）</title><link>http://www.cppblog.com/AutomateProgram/archive/2008/11/21/67512.html</link><dc:creator>漂漂</dc:creator><author>漂漂</author><pubDate>Fri, 21 Nov 2008 11:07:00 GMT</pubDate><guid>http://www.cppblog.com/AutomateProgram/archive/2008/11/21/67512.html</guid><wfw:comment>http://www.cppblog.com/AutomateProgram/comments/67512.html</wfw:comment><comments>http://www.cppblog.com/AutomateProgram/archive/2008/11/21/67512.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/AutomateProgram/comments/commentRss/67512.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/AutomateProgram/services/trackbacks/67512.html</trackback:ping><description><![CDATA[原文地址：<a href="http://blog.csdn.net/pongba/archive/2008/06/13/2544933.aspx">http://blog.csdn.net/pongba/archive/2008/06/13/2544933.aspx</a><br><br>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">By </span><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">刘未鹏<span lang=EN-US>(pongba)<o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">C++</span><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">的罗浮宫<span lang=EN-US>(<a href="http://blog.csdn.net/pongba"><u><font color=#800080>http://blog.csdn.net/pongba</font></u></a>)<o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">TopLanguage(<a title=http://groups.google.com/group/pongba href="http://groups.google.com/group/pongba"><font color=#0066ff>http://groups.google.com/group/pongba</font></a>)<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">目录<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">0. </span><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">前言<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">1. </span><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">猜数字<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">2. </span><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">称球<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">3. </span><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">排序<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 3.1 </span><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">为什么堆排比快排慢<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 3.2 </span><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">为什么快排其实也不是那么快<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 3.3 </span><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">基排又为什么那么快呢<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">4. </span><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">信息论！信息论？<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">5. </span><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">小结<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">0. </span><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">前言<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 知道这个理论是在<span lang=EN-US><a href="http://groups.google.com/group/pongba" target=_blank><font color=#0066ff>TopLanguage</font></a></span>上的一次讨论，先是<span lang=EN-US>g9<a href="http://groups.google.com/group/pongba/msg/f95aa12feb4dfd67"><font color=#0066ff><span lang=EN-US><span lang=EN-US>转了David MacKay</span></span><span lang=EN-US><span lang=EN-US>的一篇文章</span></span></font></a></span>，然后引发了牛人们的<span lang=EN-US><a href="http://groups.google.com/group/pongba/browse_frm/thread/28ac39e0222becf2"><span lang=EN-US><span lang=EN-US><font color=#0066ff>一场关于信息论的讨论</font></span></span></a></span>。<span lang=EN-US>Anyway</span>，正如<span lang=EN-US>g9</span>很久以前在<span lang=EN-US><a href="http://blog.csdn.net/g9yuayon"><font color=#0066ff>Blog</font></a></span>里面所<span lang=EN-US><a href="http://blog.csdn.net/g9yuayon/archive/2007/04/22/1574518.aspx" target=_blank><span lang=EN-US><span lang=EN-US><font color=#0066ff>说</font></span></span></a></span>的： <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">有时无知是福。俺看到一点新鲜的科普也能觉得造化神奇。刚才读<span lang=EN-US>Gerald Jay Sussman</span>（<span lang=EN-US><a href="http://mitpress.mit.edu/sicp/"><font color=#0066ff>SICP</font></a></span>作者）的文章，<span lang=EN-US><a href="http://swiss.csail.mit.edu/classes/symbolic/spring07/readings/robust-systems.pdf"><font color=#0066ff>Building Robust Systems &#8211; an essay</font></a></span>，竟然心如小鹿乱撞，手心湿润，仿佛第一次握住初恋情人温柔的手。<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 而看到<span lang=EN-US><a href="http://users.aims.ac.za/~mackay/" target=_blank><font color=#0066ff>MacKay</font></a></span>的这篇文章我也有这种感觉<span lang=EN-US>——</span>以前模糊的东西忽然有了深刻的解释，一切顿时变得明白无比。原来看问题的角度或层面能够带来这么大的变化。再一次印证了越是深刻的原理往往越是简单和强大。所以说，土鳖也有土鳖的幸福<span lang=EN-US>:P <o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">这篇文章相当于<span lang=EN-US>MacKay<a href="http://users.aims.ac.za/~mackay/sorting/sorting.html" target=_blank><span lang=EN-US><span lang=EN-US><font color=#0066ff>原文</font></span></span></a></span>的白话文版。<span lang=EN-US>MacKay</span>在原文中用到了信息论的知识，后者在我看来并不是必须的，尽管计算的时候方便，但与本质无关。所以我用大白话解释了一通。 <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp; <o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">1. </span><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">猜数字 <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 我们先来玩一个猜数字游戏：我心里默念一个<span lang=EN-US>1~64</span>之间的数，你来猜（你只能问答案是<span lang=EN-US>&#8220;</span>是<span lang=EN-US>&#8221;</span>或<span lang=EN-US>&#8220;</span>否<span lang=EN-US>&#8221;</span>的问题）。为了保证不论在什么情况下都能以尽量少的次数猜中，你应该采取什么策略呢？很显然，二分。先是猜是不是位于<span lang=EN-US>1~32</span>之间，排除掉一半可能性，然后对区间继续二分。这种策略能够保证无论数字怎么跟你捉迷藏，都能在<span lang=EN-US>log_2{n}</span>次以内猜中。用算法的术语来说就是它的下界是最好的。 <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 我们再来回顾一下这个游戏所蕴含的本质：为什么这种策略具有最优下界？答案也很简单，这个策略是平衡的。反之如果策略不是平衡的，比如问是不是在<span lang=EN-US>1~10</span>之间，那么一旦发现不是在<span lang=EN-US>1~10</span>之间的话就会剩下比<span lang=EN-US>N/2</span>更多的可能性需要去考察了。 <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; <a href="http://blog.youxu.info/" target=_blank><span lang=EN-US><span lang=EN-US><font color=#0066ff>徐宥</font></span></span></a></span><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">在讨论中提到，这种策略的本质可以概括成<span lang=EN-US>&#8220;</span>让未知世界无机可乘<span lang=EN-US>&#8221;</span>。它是没有<span lang=EN-US>&#8220;</span>弱点的<span lang=EN-US>&#8221;</span>，答案的任何一个分支都是等概率的。反之，一旦某个分支蕴含的可能性更多，当情况落到那个分支上的时候你就郁闷了。比如猜数字游戏最糟糕的策略就是一个一个的猜：是<span lang=EN-US>1</span>吗？是<span lang=EN-US>2</span>吗？<span lang=EN-US>... </span>因为这种猜法最差的情况下需要<span lang=EN-US>64</span>次才能猜对，下界非常糟糕。二分搜索为什么好，就是因为它每次都将可能性排除一半并且无论如何都能排除一半（它是最糟情况下表现最好的）。 <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp; <o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">2. </span><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">称球 <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 12</span><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">个小球，其中有一个是坏球。有一架天平。需要你用最少的称次数来确定哪个小球是坏的并且它到底是轻还是重。 <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 这个问题是一道流传已久的智力题。网络上也有很多讲解，还有泛化到<span lang=EN-US>N</span>个球的情况下的严格证明。也有零星的一些地方提到从信息论的角度来看待最优解法。本来我一直认为这道题目除了试错之外没有其它高妙的思路了，只能一个个方法试，并尽量从结果中寻找信息，然后看看哪种方案最少。 <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 然而，实际上它的确有其它的思路，一个更本质的思路，而且根本用不着信息论这么拗口的知识。 <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 我们先回顾一下猜数字游戏。为了保证任何情况下以最少次数猜中，我们的策略是每次都排除恰好一半的可能性。类比到称球问题上：坏球可能是<span lang=EN-US>12</span>个球中的任意一个，这就是<span lang=EN-US>12</span>种可能性；而其中每种可能性下坏球可能轻也可能重。于是<span lang=EN-US>&#8220;</span>坏球是哪个球，是轻是重<span lang=EN-US>&#8221;</span>这个问题的答案就有<span lang=EN-US>12&#215;2=24</span>种可能性。现在我们用天平来称球，就等同于对这<span lang=EN-US>24</span>种可能性发问，由于天平的输出结果有三种<span lang=EN-US>&#8220;</span>平衡、左倾、右倾<span lang=EN-US>&#8221;</span>，这就相当于我们的问题有三个答案，即可以将所有的可能性切成三份，根据猜数字游戏的启发，我们应当尽量让这三个分支概率均等，即平均切分所有的可能性为三等份。如此一来的话一次称量就可以将答案的可能性缩减为原来的<span lang=EN-US>1/3</span>，三次就能缩减为<span lang=EN-US>1/27</span>。而总共才有<span lang=EN-US>24</span>种可能性，所以理论上是完全可以<span lang=EN-US>3</span>次称出来的。 <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 如何称的指导原则有了，构造一个称的策略就不是什么太困难的事情了。首先不妨解释一下为什么最直观的称法不是最优的<span lang=EN-US>——6</span>、<span lang=EN-US>6</span>称：在<span lang=EN-US>6</span>、<span lang=EN-US>6</span>称的时候，天平平衡的可能性是<span lang=EN-US>0</span>。刚才说了，最优策略应该使得天平三种状态的概率均等，这样才能三等分答案的所有可能性。 <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp; 为了更清楚的看待这个问题，我们不妨假设有<span lang=EN-US>6</span>个球，来考虑一下<span lang=EN-US>3</span>、<span lang=EN-US>3</span>称和<span lang=EN-US>2</span>、<span lang=EN-US>2</span>称的区别： <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp; 在未称之前，一共有<span lang=EN-US>12</span>种可能性：<span lang=EN-US>1</span>轻、<span lang=EN-US>1</span>重、<span lang=EN-US>2</span>轻、<span lang=EN-US>2</span>重、<span lang=EN-US>...</span>、<span lang=EN-US>6</span>轻、<span lang=EN-US>6</span>重。现在将<span lang=EN-US>1</span>、<span lang=EN-US>2</span>、<span lang=EN-US>3</span>号放在左边，<span lang=EN-US>4</span>、<span lang=EN-US>5</span>、<span lang=EN-US>6</span>放在右边<span lang=EN-US>3</span>、<span lang=EN-US>3</span>称了之后，不失一般性假设天平左倾，那么小球的可能性就变成了原来的一半（<span lang=EN-US>6</span>种）：<span lang=EN-US>1</span>重、<span lang=EN-US>2</span>重、<span lang=EN-US>3</span>重、<span lang=EN-US>4</span>轻、<span lang=EN-US>5</span>轻、<span lang=EN-US>6</span>轻。即这种称法能排除一半可能性。 <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 现在再来看<span lang=EN-US>2</span>、<span lang=EN-US>2</span>称法，即<span lang=EN-US>1</span>、<span lang=EN-US>2</span>放左边，<span lang=EN-US>3</span>、<span lang=EN-US>4</span>放右边，剩下的<span lang=EN-US>5</span>、<span lang=EN-US>6</span>不称，放一边。假设结果是天平平衡，那么可能性剩下<span lang=EN-US>——4</span>种：<span lang=EN-US>5</span>重、<span lang=EN-US>5</span>轻、<span lang=EN-US>6</span>重、<span lang=EN-US>6</span>轻。假设天平左倾，可能性也剩下<span lang=EN-US>4</span>种：<span lang=EN-US>1</span>重、<span lang=EN-US>2</span>重、<span lang=EN-US>3</span>轻、<span lang=EN-US>4</span>轻。右倾和左倾的情况类似。总之，这种称法，不管天平结果如何，情况都被我们缩小到了原来的三分之一！我们充分利用了<span lang=EN-US>&#8220;</span>天平的结果状态可能有三种<span lang=EN-US>&#8221;</span>这个条件来三等分所有可能性，而不是二等分。 <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 说到这里，剩下的事情就实在很简单了：第二步称法，只要记着这样一个指导思想<span lang=EN-US>——</span>你选择的称法必须使得当天平平衡的时候答案剩下的可能性和天平左倾（右倾）的时候答案剩下的可能性一样多。实际上，这等同于你得选择一种称法，使得天平输出三种结果的概率是均等的，因为天平输出某个结果的概率就等同于所有支持这个结果（左倾、右倾、平衡）的答案可能性的和，并且答案的每个可能性都是等概率的。 <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">MacKay</span><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">在他的书《<span lang=EN-US>Information Theory: Inference and Learning Algorithms</span>》（<span lang=EN-US><a href="http://users.aims.ac.za/~mackay/itila/book.html" target=_blank><span lang=EN-US><span lang=EN-US><font color=#0066ff>作者开放免费电子书</font></span></span></a></span>）里面<span lang=EN-US>4.1</span>节专门讲了这个称球问题，还画了一张不错的图，我就照抄了： <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体"><v:shapetype id=_x0000_t75 coordsize="21600,21600" o:spt="75" o:preferrelative="t" path="m@4@5l@4@11@9@11@9@5xe" filled="f" stroked="f"><v:stroke joinstyle="miter"></v:stroke><v:formulas><v:f eqn="if lineDrawn pixelLineWidth 0"></v:f><v:f eqn="sum @0 1 0"></v:f><v:f eqn="sum 0 0 @1"></v:f><v:f eqn="prod @2 1 2"></v:f><v:f eqn="prod @3 21600 pixelWidth"></v:f><v:f eqn="prod @3 21600 pixelHeight"></v:f><v:f eqn="sum @0 0 1"></v:f><v:f eqn="prod @6 1 2"></v:f><v:f eqn="prod @7 21600 pixelWidth"></v:f><v:f eqn="sum @8 21600 0"></v:f><v:f eqn="prod @7 21600 pixelHeight"></v:f><v:f eqn="sum @10 21600 0"></v:f></v:formulas><v:path o:extrusionok="f" gradientshapeok="t" o:connecttype="rect"></v:path><o:lock v:ext="edit" aspectratio="t"></o:lock></v:shapetype><v:shape id=_x0000_i1025 style="WIDTH: 123.75pt; HEIGHT: 41.25pt" alt="" type="#_x0000_t75"><v:imagedata o:href="http://p.blog.csdn.net/images/p_blog_csdn_net/pongba/38175/o_weighing_problem.JPG" src="file:///C:\DOCUME~1\ADMINI~1\LOCALS~1\Temp\msohtml1\01\clip_image001.gif"></v:imagedata></v:shape><o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 图中<span lang=EN-US>&#8220;1+&#8221;</span>是指<span lang=EN-US>&#8220;1</span>号小球为重<span lang=EN-US>&#8221;</span>这一可能性。一开始一共有<span lang=EN-US>24</span>种可能性。<span lang=EN-US>4</span>、<span lang=EN-US>4</span>称了之后不管哪种情况（分支），剩下来的可能性总是<span lang=EN-US>4</span>种。这是一个完美的三分。然后对每个分支构造第二次称法，这里你只要稍加演算就可以发现，分支<span lang=EN-US>1</span>上的第二次称法，即<span lang=EN-US>&#8220;1</span>、<span lang=EN-US>2</span>、<span lang=EN-US>6</span>对<span lang=EN-US>3</span>、<span lang=EN-US>4</span>、<span lang=EN-US>5&#8221;</span>这种称法，天平输出三种结果的可能性是均等的（严格来说是几乎均等）。这就是为什么这个称法能够在最坏的情况下也能表现最好的原因，没有哪个分支是它的弱点，它必然能将情况缩小到原来的<span lang=EN-US>1/3</span>。 <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp; <o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">3. </span><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">排序 <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 用前面的看问题视角，排序的本质可以这样来表述：一组未排序的<span lang=EN-US>N</span>个数字，它们一共有<span lang=EN-US>N!</span>种重排，其中只有一种排列是满足题意的（譬如从大到小排列）。换句话说，排序问题的可能性一共有<span lang=EN-US>N!</span>种。任何基于比较的排序的基本操作单元都是<span lang=EN-US>&#8220;</span>比较<span lang=EN-US>a</span>和<span lang=EN-US>b&#8221;</span>，这就相当于猜数字游戏里面的一个问句，显然这个问句的答案只能是<span lang=EN-US>&#8220;</span>是<span lang=EN-US>&#8221;</span>或<span lang=EN-US>&#8220;</span>否<span lang=EN-US>&#8221;</span>，一个只有两种输出的问题最多只能将可能性空间切成两半，根据上面的思路，最佳切法就是切成<span lang=EN-US>1/2</span>和<span lang=EN-US>1/2</span>。也就是说，我们希望在比较了<span lang=EN-US>a</span>和<span lang=EN-US>b</span>的大小关系之后，如果发现<span lang=EN-US>a&lt;b</span>的话剩下的排列可能性就变成<span lang=EN-US>N!/2</span>，如果发现<span lang=EN-US>a&gt;b</span>也是剩下<span lang=EN-US>N!/2</span>种可能性。由于假设每种排列的概率是均等的，所以这也就意味着支持<span lang=EN-US>a&lt;b</span>的排列一共有<span lang=EN-US>N!/2</span>个，支持<span lang=EN-US>a&gt;b</span>的也是<span lang=EN-US>N!/2</span>个，换言之，<span lang=EN-US>a&lt;b</span>的概率等于<span lang=EN-US>a&gt;b</span>的概率。 <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 我们希望每次在比较<span lang=EN-US>a</span>和<span lang=EN-US>b</span>的时候，<span lang=EN-US>a&lt;b</span>和<span lang=EN-US>a&gt;b</span>的概率是均等的，这样我们就能保证无论如何都能将可能性缩小为原来的一半了！最优下界。 <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 一个直接的推论是，如果每次都像上面这样的完美比较，那么<span lang=EN-US>N</span>个元素的<span lang=EN-US>N!</span>种可能排列只需要<span lang=EN-US>log_2{N!}</span>就排查玩了，而<span lang=EN-US>log_2{N!}</span>近似于<span lang=EN-US>NlogN</span>。这正是快排的复杂度。 <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 3.1 </span><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">为什么堆排比快排慢 <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 回顾一下堆排的过程： <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 1. </span><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">建立最大堆（堆顶的元素大于其两个儿子，两个儿子又分别大于它们各自下属的两个儿子<span lang=EN-US>... </span>以此类推） <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 2. </span><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">将堆顶的元素和最后一个元素对调（相当于将堆顶元素（最大值）拿走，然后将堆底的那个元素补上它的空缺），然后让那最后一个元素从顶上往下滑到恰当的位置（重新使堆最大化）。 <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 3. </span><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">重复第<span lang=EN-US>2</span>步。 <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 这里的关键问题就在于第<span lang=EN-US>2</span>步，堆底的元素肯定很小，将它拿到堆顶和原本属于最大元素的两个子节点比较，它比它们大的可能性是微乎其微的。实际上它肯定小于其中的一个儿子。而大于另一个儿子的可能性非常小。于是，这一次比较的结果就是概率不均等的，根据前面的分析，概率不均等的比较是不明智的，因为它并不能保证在糟糕情况下也能将问题的可能性削减到原本的<span lang=EN-US>1/2</span>。可以想像一种极端情况，如果<span lang=EN-US>a</span>肯定小于<span lang=EN-US>b</span>，那么比较<span lang=EN-US>a</span>和<span lang=EN-US>b</span>就会什么信息也得不到<span lang=EN-US>——</span>原本剩下多少可能性还是剩下多少可能性。 <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 在堆排里面有大量这种近乎无效的比较，因为被拿到堆顶的那个元素几乎肯定是很小的，而靠近堆顶的元素又几乎肯定是很大的，将一个很小的数和一个很大的数比较，结果几乎肯定是<span lang=EN-US>&#8220;</span>小于<span lang=EN-US>&#8221;</span>的，这就意味着问题的可能性只被排除掉了很小一部分。 <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 这就是为什么堆排比较慢（堆排虽然和快排一样复杂度都是<span lang=EN-US>O(NlogN)</span>但堆排复杂度的常系数更大）。 <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; MacKay</span><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">也提供了一个修改版的堆排：每次不是将堆底的元素拿到上面去，而是直接比较堆顶（最大）元素的两个儿子，即选出次大的元素。由于这两个儿子之间的大小关系是很不确定的，两者都很大，说不好哪个更大哪个更小，所以这次比较的两个结果就是概率均等的了。具体参考<span lang=EN-US><a href="http://users.aims.ac.za/~mackay/sorting/sorting.html" target=_blank><span lang=EN-US><span lang=EN-US><font color=#0066ff>这里</font></span></span></a></span>。 <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">3.2 </span><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">为什么快排其实也不是那么快 <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 我们考虑快排的过程：随机选择一个元素做<span lang=EN-US>&#8220;</span>轴元素<span lang=EN-US>&#8221;</span>，将所有大于轴元素的移到左边，其余移到右边。根据这个过程，快排的第一次比较就是将一个元素和轴元素比较，这个时候显而易见的是，<span lang=EN-US>&#8220;</span>大于<span lang=EN-US>&#8221;</span>和<span lang=EN-US>&#8220;</span>小于<span lang=EN-US>&#8221;</span>的可能性各占一半。这是一次漂亮的比较。 <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 然而，快排的第二次比较就不那么高明了：我们不妨令轴元素为<span lang=EN-US>pivot</span>，第一次比较结果是<span lang=EN-US>a1&lt;pivot</span>，那么可以证明第二次比较<span lang=EN-US>a2</span>也小于<span lang=EN-US>pivot</span>的可能性是<span lang=EN-US>2/3</span>！这容易证明：如果<span lang=EN-US>a2&gt;pivot</span>的话，那么<span lang=EN-US>a1</span>，<span lang=EN-US>a2</span>，<span lang=EN-US>pivot</span>这三个元素之间的关系就完全确定了<span lang=EN-US>——a1&lt;pivot&lt;a2</span>，剩下来的元素排列的可能性我们不妨记为<span lang=EN-US>P</span>（不需要具体算出来）。而如果<span lang=EN-US>a2&lt;pivot</span>呢？那么<span lang=EN-US>a1</span>和<span lang=EN-US>a2</span>的关系就仍然是不确定的，也就是说，这个分支里面含有两种情况：<span lang=EN-US>a1&lt;a2&lt;pivot</span>，以及<span lang=EN-US>a2&lt;a1&lt;pivot</span>。对于其中任一种情况，剩下的元素排列的可能性都是<span lang=EN-US>P</span>，于是这个分支里面剩下的排列可能性就是<span lang=EN-US>2P</span>。所以当<span lang=EN-US>a2&lt;pivot</span>的时候，还剩下<span lang=EN-US>2/3</span>的&nbsp; 可能性需要排查。 <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 再进一步，如果第二步比较果真发现<span lang=EN-US>a2&lt;pivot</span>的话，第三步比较就更不妙了，模仿上面的推理，<span lang=EN-US>a3&lt;pivot</span>的概率将会是<span lang=EN-US>3/4</span>！ <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 这就是快排也不那么快的原因，因为它也没有做到每次比较都能将剩下的可能性砍掉一半。 <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 3.3 </span><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">鸡排为什么又那么快呢？ <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 传统的解释是：<span lang=EN-US><a href="http://en.wikipedia.org/wiki/Radix_sort" target=_blank><span lang=EN-US><span lang=EN-US><font color=#0066ff>基排</font></span></span></a></span>不是基于比较的，所以不具有后者的局限性。话是没错，但其实还可以将它和基于比较的排序做一个类比。 <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 基排的过程也许是源于我们理顺一副牌的过程：如果你有<span lang=EN-US>N</span>（<span lang=EN-US>N&lt;=13</span>）张牌，乱序，如何理顺呢？我们假象桌上有十三个位置，然后我们将手里的牌一张一张放出去，如果是<span lang=EN-US>3</span>，就放在位置<span lang=EN-US>3</span>上，如果是<span lang=EN-US>J</span>，就放在位置<span lang=EN-US>11</span>上，放完了之后从位置<span lang=EN-US>1</span>到位置<span lang=EN-US>13</span>收集所有的牌（没有牌的位置上不收集任何牌）。 <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 我们可以这样来理解基排高效的本质原因：假设前<span lang=EN-US>i</span>张牌都已经放到了它们对应的位置上，第<span lang=EN-US>i+1</span>张牌放出去的时候，实际上就相当于<span lang=EN-US>&#8220;</span>一下子<span lang=EN-US>&#8221;</span>就确立了它和前<span lang=EN-US>i</span>张牌的大小关系，用<span lang=EN-US>O(1)</span>的操作就将这张牌正确地插入到了前<span lang=EN-US>i</span>张牌中的正确位置上，这个效果就相当于插入排序的第<span lang=EN-US>i</span>轮原本需要比较<span lang=EN-US>O(i)</span>次的，现在只需要<span lang=EN-US>O(1)</span>了。 <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 但是，为什么基排能够达到这个效果呢？上面只是解释了过程，解释了过程不代表解释了本质。 <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 当<span lang=EN-US>i</span>张牌放到位之后，放置第<span lang=EN-US>i+1</span>张牌的时候有多少种可能性？大约<span lang=EN-US>i+1</span>种，因为前<span lang=EN-US>i</span>张牌将<span lang=EN-US>13</span>个位置分割成了<span lang=EN-US>i+1</span>个区间<span lang=EN-US>——</span>第<span lang=EN-US>i+1</span>张牌可以落在任意一个区间。所以放置第<span lang=EN-US>i+1</span>张牌就好比是询问这样一个问题：<span lang=EN-US>&#8220;</span>这张牌落在哪个区间呢？<span lang=EN-US>&#8221;</span>而这个问题的答案有<span lang=EN-US>i+1</span>种可能性？所以它就将剩下来的可能性均分成了<span lang=EN-US>i+1</span>份（换句话说，砍掉了<span lang=EN-US>i/i+1</span>的可能性！）。再看看基于比较的排序吧：由于每次比较只有两种结果，所以最多只能将剩下的可能性砍掉一半。 <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 这就是为什么基排要快得多。而所有基于比较的排序都逃脱不了<span lang=EN-US>NlogN</span>的宿命。 <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp; <o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 4. </span><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">信息论！信息论？ <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 本来呢，<span lang=EN-US>MacKay</span>写那篇文章是想用信息论来解释为什么堆排慢，以及为什么快排也慢的。<span lang=EN-US>MacKay</span>在他的文章中的解释是，只有提出每种答案的概率都均等的问题，才能获得最大信息量。然而，仔细一想，其实这里信息论并不是因，而是果。这里不需要用信息论就完全能够解释，而且更明白。信息论只是对这个解释的一个形式化。当然，信息论在其它地方还是有应用的。但这里其实用不着信息论这么重量级的东西（也许具体计算一些数据的时候是需要的），而是只需要一种看问题的本质视角：将排序问题看成和猜数字一样，是通过问问题来缩小<span lang=EN-US>/</span>排除（<span lang=EN-US>narrow down</span>）结果的可能性区间，这样一来，就会发现，<span lang=EN-US>&#8220;</span>最好的问题<span lang=EN-US>&#8221;</span>就是那些能够均分所有可能性的问题，因为那样的话不管问题的答案如何，都能排除掉<span lang=EN-US>k-1/k</span>（<span lang=EN-US>k</span>为问题的答案有多少种输出<span lang=EN-US>——</span>猜数字里面是<span lang=EN-US>2</span>，称球里面是<span lang=EN-US>3</span>）种可能性，而不均衡的问题总会有一个或一些答案分支排除掉的可能性要小于<span lang=EN-US>k-1/k</span>。于是策略的下界就被拖累了。 <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp; <o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 5. </span><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">小结 <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 这的确是<span lang=EN-US>&#8220;</span>小结<span lang=EN-US>&#8221;</span>，因为两点： <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 1. </span><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">这个问题可以有信息论的理论解释，而信息论则是一个相当大的领域了。<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 2. </span><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">文中提到的这种看问题的视角除了用于排序、称球，还能够运用到哪些问题上（比如搜索）。<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;<o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">Update(06/13/2008) : <a href="http://blog.youxu.info/" target=_blank><span lang=EN-US><span lang=EN-US><font color=#0066ff>徐宥</font></span></span></a></span><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">在讨论中<span lang=EN-US><a href="http://groups.google.com/group/pongba/msg/07493e329ed920ff" target=_blank><span lang=EN-US><span lang=EN-US><font color=#0066ff>继续提到</font></span></span></a></span>：<span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 另外，这几天我重新把<span lang=EN-US>TAOCP </span>第三卷<span lang=EN-US>(</span>第二版<span lang=EN-US>)</span>翻出来看了看<span lang=EN-US> Knuth </span>怎么说这个问题的<span lang=EN-US>, </span>发现真是牛大了： <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 先说性能： <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">pp148, section 5.2.3 </span><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">说： <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">When N = 1000, the approximate average runiing time on MIX are 160000u for heapsort 130000u for shellsort 80000u&nbsp; for quicksort <o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 这里<span lang=EN-US>,&nbsp; Knuth </span>同学发现一般情况下<span lang=EN-US> heapsort </span>表现很不好<span lang=EN-US>. </span>于是，在下文他就说，习题<span lang=EN-US>18 (pp156, </span>难度<span lang=EN-US>21) <o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span lang=EN-US style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">(R.W.Floyd) During the selection phase of heapsort, the key K tends to be quite small, so that nearly all the comparisons in step H6 find K&lt;K_j. Show how to modify the algorithm so that K is not compared with K_j in the main loop of the computation, thereby nearly cutting the average number of comparisons in half. <o:p></o:p></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 答案里面的方法和<span lang=EN-US>DMK</span>的方法是一样的。<span lang=EN-US>(</span>我觉得<span lang=EN-US>DMK</span>是看了这个论文或者<span lang=EN-US>TAoCP</span>的<span lang=EN-US>) </span>这里说<span lang=EN-US> by half</span>，就正好和快排差不多了。 <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 再说信息论分析： <span lang=EN-US><o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp;&nbsp; 在<span lang=EN-US>5.3.1 (pp181) </span>高爷爷就说<span lang=EN-US>, &#8220;</span>排序问题可以看成是一个树上的鸟儿排排站的问题<span lang=EN-US>. (</span>还特地画了一棵树<span lang=EN-US>), </span>下一段就说<span lang=EN-US>, </span>其实这个也有等价说法<span lang=EN-US>, </span>就是信息论<span lang=EN-US>, </span>我们从称球问题说起<span lang=EN-US>...&#8221; <o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp; 然后后面一直讲信息论和最小比较排序<span lang=EN-US>...<o:p></o:p></span></span></p>
<p class=MsoNormal style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 14pt; FONT-FAMILY: 宋体">&nbsp;&nbsp; 高爷爷真不愧是姓高的，囧<span lang=EN-US>rz..<o:p></o:p></span></span></p>
<img src ="http://www.cppblog.com/AutomateProgram/aggbug/67512.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/AutomateProgram/" target="_blank">漂漂</a> 2008-11-21 19:07 <a href="http://www.cppblog.com/AutomateProgram/archive/2008/11/21/67512.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>什么是算法，为什么需要学算法，以及算法学到什么程度（转载）</title><link>http://www.cppblog.com/AutomateProgram/archive/2008/11/15/67002.html</link><dc:creator>漂漂</dc:creator><author>漂漂</author><pubDate>Sat, 15 Nov 2008 08:37:00 GMT</pubDate><guid>http://www.cppblog.com/AutomateProgram/archive/2008/11/15/67002.html</guid><wfw:comment>http://www.cppblog.com/AutomateProgram/comments/67002.html</wfw:comment><comments>http://www.cppblog.com/AutomateProgram/archive/2008/11/15/67002.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cppblog.com/AutomateProgram/comments/commentRss/67002.html</wfw:commentRss><trackback:ping>http://www.cppblog.com/AutomateProgram/services/trackbacks/67002.html</trackback:ping><description><![CDATA[<p align=left>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 第一个问题我觉得我无法给出完美的答案，这里搞竞赛的牛人蛮多，不妨说说体会:D <o:p></o:p></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我个人觉得算法里面极大一部分内容是如何有效地进行搜索，这里的"有效"可以分为：避免不必要的计算（如A*寻路以及所有的启发式剪枝），缓存重复计算（如所有&shy;的动态规划）。当然，知道这些跟具体的设计出一个算法至少还有十万八千里，只能说有了这个大体的思路，就可以从这两个角度去审视手头的问题，往往是会有启发意义&shy;的罢了。如何避免不必要的计算？也有很多 rules of thumb 可以遵循，如启发式剪枝里面就要求去设计一个最优下界，而最一般的思路则是使劲瞅瞅问题里面有什么条件是没有利用的，这些条件组合起来可以得出什么性质，也许某&shy;个性质就能够被利用来减掉一大堆计算，至于如何从题目条件推出有价值的性质，有两个办法，一是试错（想到的结论都给写出来，陶哲轩在 Solving Mathematical Problems 里面就提到过这个办法。）；另一个方向则是脑袋里揣着想要实现的目的往反方向归约。如何缓存重复计算？简单的动态规划问题如fibonacci数列计算，其重复&shy;计算是非常明显的，计算的过程本身就指明了哪些计算是重复的（An 项的计算是重复的）——当然，正如早前邓同学发的一个题目&lt;<a href="https://groups.google.com/group/pongba/browse_frm/thread/2ca1f2bda0c8" target=_blank>https://groups.google.com/group/pongba/browse_frm/thread/2ca1f2bda0c8...</a>&gt;里面说的，其实fibonacci数列计算里面的线性变换本身也是有重复计算的——后者便是更隐蔽的重复计算了，一个 non-trivial 的动态规划问题往往涉及到非常隐蔽的重复计算，或者更难的是，你遍历组合空间的方式决定了你所能够缓存的重复计算到底有多少，也许某个遍历方式之下就没有办法去&shy;缓存计算。当然，算法的范畴其实是很大的，算法是一个AI-Complete 的问题，所有的 Problem-Solving 过程都可以叫做算法。只是有很多实际当中的算法会掉入以上两类而已。&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp; 第二个问题我举一个例子：不像很多牛人在高中和本科就竞赛奖牌一堆，我直到大四的时候还不知道什么是动态规划，因为本科四年我一直只对底层技术感兴趣，最喜欢看 比如 Petzold 的《编码的奥秘》和 Richter 的《.NET 框架程序设计》（事实上这是我看的第一本英文原版书）这类书。研一的时候由于方向是自然语言处理，看的第一篇 paper 是 Rabiner 的 &nbsp;A Tutorial on Hidden Markov Models and Selected Applications in Speech <br>Recognition 。Paper 的内容倒是完全能够理解，但是理解其实只是第一步，我发现理解了之后很快就忘掉了，这就说明理解得不够深刻。比如里面的 Viterbi 算法，花了时间去理解，但是一转头很快又忘掉了。一年后因为机缘巧合，对算法发生了一段短暂的兴趣，并学习了一些基础的算法，尤其是算法的思想，因为思想是有穷&shy;的，但算法是无穷的，尤其是题目是做不完的。之后一段时间，碰巧又需要翻一翻马可夫模型，搜出吴军的数学之美以及那篇 Paper ，发现 Viterbi 算法其实就是最简单的一类动态规划，由于对于动态规划的理解深刻了很多，所以对于 Viterbi 算法，在脑袋里面记住的不再是什么 Forward Variable/Backward Variable <br>之类的技术细节，而是它的本质，于是便不再容易忘掉，而即便忘掉，就如庞加莱所说，也可以非常迅速的将算法的细节自行构建出来。 <o:p></o:p></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 其实我相信这样的例子是数不胜数的，所以我这个只是算一个 Yet Another Example ，由于对我来说比较特殊，所以印象较为深刻。 <o:p></o:p></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这个例子是关于"理解"的。有时候算法也会非常有用，如有一次写程序时需要用到 LCS 和 Edit-Distance （这样的机会很少，但遇到了时如果不知道有多项式复杂度的算法就很悲惨了），而做机器学习和数据挖掘的更是少不了一坨坨的算法，如果光是理解别人的做法然后实现&shy;出来，那么对算法的思想的把握有助于理解和记忆；如果需要自己设计算法，那就需要算法基础知识的辅助才行了。绝大多数人应该属于前者。 <o:p></o:p></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 学习到什么程度？我觉得视人群而定。如果做底层开发、应用开发、系统开发，只要知道一个大概就可以了，知道经典的数据结构和算法没有任何困难，而且反正经典算法&shy;都有现成的库可用。对于有兴趣做一点 research 沾边的事情的人，则需要了解这些算法背后的一般性思路是什么，否则来一个特定的算法你就特定的理解记忆一下，肯定不牢靠，而且浪费大脑资源。对于搞 real deal 的 original research 的那就需要广泛的知识积累了，光知道一般性思路都不够。 <o:p></o:p></p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 另一方面，我觉得学完了经典算法，深刻理解了算法背后的一般性思路之后，如果再进一步去玩题目，做题库。效益却不是很大的，因为刀磨了是要用的，玩题目做题库就&shy;是进一步磨刀而不用（不去解决实际问题，能够产生影响力的，或生产力的问题）。实际上做了一些题目之后就完全没必要进一步做题目了，因为做来做去，拼的基本也就&shy;是谁的知识积累多（套路多），谁的耐心大（肯使劲去磨一道题目）；实际上谁也不比谁笨，到最后区别就基本上显露在知识积累和耐心上了。所以接着做，刀也不会磨得&shy;更锋利，更何况大好的时光应该去做点有意义的事情（如果是为了 fun 而做题的，那么有意义的事情同样也可以是 extremely fun），比如我觉得最吸引人也最根本的问题就是人工智能问题（想想看，人脑是世界上迄今为止所知最为复杂的结构，这个结构具备了认识自然界"规律"的能力，具&shy;备了认识"自我"的能力，具备了归纳和演绎推理的能力，类比的能力，具备了难以置信的启发式搜索能力，具备完美的模式识别能力，而根据进化论的观点，这样的结构&shy;居然仅仅是通过变异——筛选得来的，如果真有上帝，那么利用上帝赋予我们的大脑去破解上帝这个顶级牛逼程序员写的程序——人脑的秘密，还有比这更带劲儿的事情吗&shy;？），所以我觉得有那么好的基础的牛人，不去直面真正 fundamental 的 problems ，就可惜了，须知题目是永远做不完的，一个公理系统的定理也是永远推导不完的，永远可以设计出题目来给你做，但是真正的问题其实只有一个。如果穷举不了世界上所&shy;有的问题，至少可以举出那些有趣、有意义的问题:) <o:p></o:p></p>
<p>-- <br>刘未鹏(pongba) <br>Blog|C++的罗浮宫 <br><a href="http://blog.csdn.net/pongba" target=_blank>http://blog.csdn.net/pongba</a> <o:p></o:p></p>
<p align=left><o:p>&nbsp;</o:p></p>
<p>&nbsp;</p>
<img src ="http://www.cppblog.com/AutomateProgram/aggbug/67002.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cppblog.com/AutomateProgram/" target="_blank">漂漂</a> 2008-11-15 16:37 <a href="http://www.cppblog.com/AutomateProgram/archive/2008/11/15/67002.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>