<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Es Gilt Viele Mauern Abzubauen &#187; 算法</title>
	<atom:link href="http://boundary.cc/tag/algorithm/feed/" rel="self" type="application/rss+xml" />
	<link>http://boundary.cc</link>
	<description>Joker Lee&#039;s Blog</description>
	<lastBuildDate>Tue, 17 Jan 2012 03:25:44 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
<atom:link rel="hub" href="http://pubsubhubbub.appspot.com"/><atom:link rel="hub" href="http://superfeedr.com/hubbub"/>		<item>
		<title>[一道面试题]含有*的字符串匹配问题</title>
		<link>http://boundary.cc/200911/string-matching-with-wildcard/</link>
		<comments>http://boundary.cc/200911/string-matching-with-wildcard/#comments</comments>
		<pubDate>Wed, 25 Nov 2009 01:13:43 +0000</pubDate>
		<dc:creator>Joker</dc:creator>
				<category><![CDATA[编程]]></category>
		<category><![CDATA[算法]]></category>
		<category><![CDATA[面试题]]></category>

		<guid isPermaLink="false">http://jackaldire.com/?p=76</guid>
		<description><![CDATA[Question 字符串1：只含有英文字母 字符串2：含有英文字母和*，其中符号*表示匹配任意字符0或者多次，即正则表达式里面的含义。 现在给定这样的两个串，要求判断是否匹配？ bool isMatch ( const char *str1, const char *str2) 例如：str1 = “hello”, str2 = “he*o”，则二者匹配，返回true,str1 = “hello”, str2 = “he*l”，则不匹配，返回false。 Solution 关键是如何处理*，首先想到的就是回溯，在纸上画了一下得到如下算法 设输入是两个字符串 s, t, 其中t可能包含* 1.当*t不是*的时候, 就像普通的串匹配一样, 移动s和t 2.当*t是*的时候, 假设*t后面第一个不是*的字符是x, 若x是null, 直接匹配成功, 否则在s中找到当前位置后所有字符x的位置, 这时候问题转化成了t中x后的串和s中当前位置以后所有以x为开始的串的匹配问题, 递归调用即可, 其中任意一个匹配成功, 则原串匹配成功, 若都没有匹配成功则匹配失败. 3.当*s和*t其中一个是null时 跳出循环, 若此时 *t == &#8216;*&#8217;, 则++t 知道 t != &#8216;*&#8217;, 这时若<a href="http://boundary.cc/200911/string-matching-with-wildcard/"> <br /><br /> (Read More...)</a>]]></description>
			<content:encoded><![CDATA[<h2>Question</h2>
<p>字符串1：只含有英文字母<br />
字符串2：含有英文字母和*，其中符号*表示匹配任意字符0或者多次，即正则表达式里面的含义。</p>
<p>现在给定这样的两个串，要求判断是否匹配？<br />
bool isMatch ( const char *str1, const char *str2)</p>
<p>例如：str1 = “hello”, str2 = “he*o”，则二者匹配，返回true,str1 = “hello”, str2 = “he*l”，则不匹配，返回false。</p>
<p><span id="more-76"></span></p>
<h2>Solution</h2>
<p>关键是如何处理*，首先想到的就是回溯，在纸上画了一下得到如下算法</p>
<p>设输入是两个字符串 s, t, 其中t可能包含* <.p><br />
1.当*t不是*的时候, 就像普通的串匹配一样, 移动s和t <br />
2.当*t是*的时候,  假设*t后面第一个不是*的字符是x,  若x是null, 直接匹配成功,  否则在s中找到当前位置后所有字符x的位置, 这时候问题转化成了t中x后的串和s中当前位置以后所有以x为开始的串的匹配问题, 递归调用即可, 其中任意一个匹配成功, 则原串匹配成功, 若都没有匹配成功则匹配失败.</p>
<p>3.当*s和*t其中一个是null时 跳出循环, 若此时 *t  == &#8216;*&#8217;, 则++t 知道 t != &#8216;*&#8217;,  这时若 *t == 0 则代表匹配成功, 否则匹配失败。</p>
<p>代码如下:</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
</pre></td><td class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #339900;">#include &lt;cstring&gt;</span>
<span style="color: #339900;">#include &lt;iostream&gt;</span>
&nbsp;
<span style="color: #0000ff;">using</span> <span style="color: #0000ff;">namespace</span> std<span style="color: #008080;">;</span>
&nbsp;
<span style="color: #0000ff;">bool</span> is_match<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">const</span> <span style="color: #0000ff;">char</span> <span style="color: #000040;">*</span> s, <span style="color: #0000ff;">const</span> <span style="color: #0000ff;">char</span> <span style="color: #000040;">*</span> t<span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
    <span style="color: #0000ff;">while</span> <span style="color: #008000;">&#40;</span><span style="color: #000040;">*</span>s <span style="color: #000040;">&amp;&amp;</span> <span style="color: #000040;">*</span>t<span style="color: #008000;">&#41;</span>
    <span style="color: #008000;">&#123;</span>
        <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span><span style="color: #000040;">*</span>t <span style="color: #000040;">!</span><span style="color: #000080;">=</span> <span style="color: #FF0000;">'*'</span> <span style="color: #000040;">&amp;&amp;</span> <span style="color: #000040;">*</span>s <span style="color: #000080;">==</span> <span style="color: #000040;">*</span>t<span style="color: #008000;">&#41;</span>
            <span style="color: #000040;">++</span>s, <span style="color: #000040;">++</span>t<span style="color: #008080;">;</span>
        <span style="color: #0000ff;">else</span> <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span><span style="color: #000040;">*</span>t <span style="color: #000040;">!</span><span style="color: #000080;">=</span> <span style="color: #FF0000;">'*'</span> <span style="color: #000040;">&amp;&amp;</span> <span style="color: #000040;">*</span>s <span style="color: #000040;">!</span><span style="color: #000080;">=</span> <span style="color: #000040;">*</span>t<span style="color: #008000;">&#41;</span>
            <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">false</span><span style="color: #008080;">;</span>
        <span style="color: #0000ff;">else</span> <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span><span style="color: #000040;">*</span>t <span style="color: #000080;">==</span> <span style="color: #FF0000;">'*'</span><span style="color: #008000;">&#41;</span>
        <span style="color: #008000;">&#123;</span>
            <span style="color: #0000ff;">do</span> <span style="color: #000040;">++</span>t<span style="color: #008080;">;</span> <span style="color: #0000ff;">while</span> <span style="color: #008000;">&#40;</span><span style="color: #000040;">*</span>t <span style="color: #000080;">==</span> <span style="color: #FF0000;">'*'</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
            <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span><span style="color: #000040;">*</span>t <span style="color: #000080;">==</span> <span style="color: #0000dd;">0</span><span style="color: #008000;">&#41;</span> <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">true</span><span style="color: #008080;">;</span>
            <span style="color: #0000ff;">for</span> <span style="color: #008000;">&#40;</span> <span style="color: #008080;">;</span> <span style="color: #000040;">*</span>s<span style="color: #008080;">;</span> <span style="color: #000040;">++</span>s<span style="color: #008000;">&#41;</span>
                <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span><span style="color: #000040;">*</span>s <span style="color: #000080;">==</span> <span style="color: #000040;">*</span>t <span style="color: #000040;">&amp;&amp;</span> is_match<span style="color: #008000;">&#40;</span>s<span style="color: #000040;">+</span><span style="color: #0000dd;">1</span>, t<span style="color: #000040;">+</span><span style="color: #0000dd;">1</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span>
                    <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">true</span><span style="color: #008080;">;</span>
            <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">false</span><span style="color: #008080;">;</span>
        <span style="color: #008000;">&#125;</span>
    <span style="color: #008000;">&#125;</span>
    <span style="color: #0000ff;">while</span> <span style="color: #008000;">&#40;</span><span style="color: #000040;">*</span>t <span style="color: #000080;">==</span> <span style="color: #FF0000;">'*'</span><span style="color: #008000;">&#41;</span> <span style="color: #000040;">++</span>t<span style="color: #008080;">;</span>
    <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span><span style="color: #000040;">*</span>s <span style="color: #000080;">==</span> <span style="color: #000040;">*</span>t<span style="color: #008000;">&#41;</span> <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">true</span><span style="color: #008080;">;</span>
    <span style="color: #0000ff;">else</span> <span style="color: #0000ff;">return</span> <span style="color: #0000ff;">false</span><span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span>
&nbsp;
<span style="color: #0000ff;">int</span> main<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> argc, <span style="color: #0000ff;">char</span> <span style="color: #000040;">*</span> argv<span style="color: #008000;">&#91;</span><span style="color: #008000;">&#93;</span><span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
    <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>is_match<span style="color: #008000;">&#40;</span>argv<span style="color: #008000;">&#91;</span><span style="color: #0000dd;">1</span><span style="color: #008000;">&#93;</span>, argv<span style="color: #008000;">&#91;</span><span style="color: #0000dd;">2</span><span style="color: #008000;">&#93;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span>
        <span style="color: #0000dd;">cout</span> <span style="color: #000080;">&lt;&lt;</span> <span style="color: #FF0000;">&quot;match&quot;</span> <span style="color: #000080;">&lt;&lt;</span> endl<span style="color: #008080;">;</span>
    <span style="color: #0000ff;">else</span> <span style="color: #0000dd;">cout</span> <span style="color: #000080;">&lt;&lt;</span> <span style="color: #FF0000;">&quot;not match&quot;</span> <span style="color: #000080;">&lt;&lt;</span> endl<span style="color: #008080;">;</span>
    <span style="color: #0000ff;">return</span> <span style="color: #0000dd;">0</span><span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<h2>改进</h2>
<p>上面的暴力算法如果遇到“aaaaaaaaaaaaaaaa”，“a*a*a*a*a*a*a*a*a*a*a*a”这样的输入，会达到2^n的复杂度，是无法接受的。注意到，递归搜索是有很多重复的状态，自然就想到了记忆化搜索，时间空间复杂度均为O(n^2)，代码如下：</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
</pre></td><td class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #339900;">#include &lt;iostream&gt;</span>
<span style="color: #339900;">#include &lt;cstring&gt;</span>
<span style="color: #0000ff;">using</span> <span style="color: #0000ff;">namespace</span> std<span style="color: #008080;">;</span>
<span style="color: #0000ff;">const</span> <span style="color: #0000ff;">int</span> MAX_LEN <span style="color: #000080;">=</span> <span style="color: #0000dd;">1024</span><span style="color: #008080;">;</span>
<span style="color: #0000ff;">int</span> dp<span style="color: #008000;">&#91;</span>MAX_LEN<span style="color: #008000;">&#93;</span><span style="color: #008000;">&#91;</span>MAX_LEN<span style="color: #008000;">&#93;</span><span style="color: #008080;">;</span>
&nbsp;
<span style="color: #0000ff;">bool</span> is_match<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> p, <span style="color: #0000ff;">int</span> q, <span style="color: #0000ff;">const</span> <span style="color: #0000ff;">char</span> <span style="color: #000040;">*</span> s, <span style="color: #0000ff;">const</span> <span style="color: #0000ff;">char</span> <span style="color: #000040;">*</span> t<span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
    <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>dp<span style="color: #008000;">&#91;</span>p<span style="color: #008000;">&#93;</span><span style="color: #008000;">&#91;</span>q<span style="color: #008000;">&#93;</span> <span style="color: #000080;">&gt;=</span> <span style="color: #0000dd;">0</span><span style="color: #008000;">&#41;</span> <span style="color: #0000ff;">return</span> dp<span style="color: #008000;">&#91;</span>p<span style="color: #008000;">&#93;</span><span style="color: #008000;">&#91;</span>q<span style="color: #008000;">&#93;</span><span style="color: #008080;">;</span>
&nbsp;
    <span style="color: #0000ff;">int</span> <span style="color: #000040;">&amp;</span>ans <span style="color: #000080;">=</span> dp<span style="color: #008000;">&#91;</span>p<span style="color: #008000;">&#93;</span><span style="color: #008000;">&#91;</span>q<span style="color: #008000;">&#93;</span> <span style="color: #000080;">=</span> <span style="color: #0000dd;">0</span><span style="color: #008080;">;</span>
&nbsp;
    <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span><span style="color: #000040;">!</span>s<span style="color: #008000;">&#91;</span>p<span style="color: #008000;">&#93;</span> <span style="color: #000040;">&amp;&amp;</span> <span style="color: #000040;">!</span>t<span style="color: #008000;">&#91;</span>q<span style="color: #008000;">&#93;</span><span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span>
        ans <span style="color: #000080;">=</span> <span style="color: #0000ff;">true</span><span style="color: #008080;">;</span>
    <span style="color: #008000;">&#125;</span> <span style="color: #0000ff;">else</span> <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span><span style="color: #000040;">!</span>s<span style="color: #008000;">&#91;</span>p<span style="color: #008000;">&#93;</span> <span style="color: #000040;">||</span> <span style="color: #000040;">!</span>t<span style="color: #008000;">&#91;</span>q<span style="color: #008000;">&#93;</span><span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span>
        <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>t<span style="color: #008000;">&#91;</span>q<span style="color: #008000;">&#93;</span> <span style="color: #000080;">==</span> <span style="color: #FF0000;">'*'</span><span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span>
            ans <span style="color: #000080;">=</span> is_match<span style="color: #008000;">&#40;</span>p, q <span style="color: #000040;">+</span> <span style="color: #0000dd;">1</span>, s, t<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
        <span style="color: #008000;">&#125;</span>
    <span style="color: #008000;">&#125;</span> <span style="color: #0000ff;">else</span> <span style="color: #008000;">&#123;</span>
        <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>t<span style="color: #008000;">&#91;</span>q<span style="color: #008000;">&#93;</span> <span style="color: #000080;">==</span> <span style="color: #FF0000;">'*'</span><span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span>
            ans <span style="color: #000080;">=</span> is_match<span style="color: #008000;">&#40;</span>p <span style="color: #000040;">+</span> <span style="color: #0000dd;">1</span>, q, s, t<span style="color: #008000;">&#41;</span>
                <span style="color: #000040;">||</span> is_match<span style="color: #008000;">&#40;</span>p, q <span style="color: #000040;">+</span> <span style="color: #0000dd;">1</span>, s, t<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
        <span style="color: #008000;">&#125;</span> <span style="color: #0000ff;">else</span> <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>s<span style="color: #008000;">&#91;</span>p<span style="color: #008000;">&#93;</span> <span style="color: #000080;">==</span> t<span style="color: #008000;">&#91;</span>q<span style="color: #008000;">&#93;</span><span style="color: #008000;">&#41;</span> <span style="color: #008000;">&#123;</span>
            ans <span style="color: #000080;">=</span> is_match<span style="color: #008000;">&#40;</span>p <span style="color: #000040;">+</span> <span style="color: #0000dd;">1</span>, q <span style="color: #000040;">+</span> <span style="color: #0000dd;">1</span>, s, t<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
        <span style="color: #008000;">&#125;</span>
    <span style="color: #008000;">&#125;</span>
    <span style="color: #0000ff;">return</span> ans<span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span>
&nbsp;
<span style="color: #0000ff;">bool</span> is_match<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">const</span> <span style="color: #0000ff;">char</span> <span style="color: #000040;">*</span> s, <span style="color: #0000ff;">const</span> <span style="color: #0000ff;">char</span> <span style="color: #000040;">*</span> t<span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
    <span style="color: #0000dd;">memset</span><span style="color: #008000;">&#40;</span>dp, <span style="color: #000040;">-</span><span style="color: #0000dd;">1</span>, <span style="color: #0000dd;">sizeof</span><span style="color: #008000;">&#40;</span>dp<span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
    <span style="color: #0000ff;">return</span> is_match<span style="color: #008000;">&#40;</span><span style="color: #0000dd;">0</span>, <span style="color: #0000dd;">0</span>, s, t<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span>  
&nbsp;
<span style="color: #0000ff;">int</span> main<span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> argc, <span style="color: #0000ff;">char</span> <span style="color: #000040;">*</span> argv<span style="color: #008000;">&#91;</span><span style="color: #008000;">&#93;</span><span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
    <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>is_match<span style="color: #008000;">&#40;</span>argv<span style="color: #008000;">&#91;</span><span style="color: #0000dd;">1</span><span style="color: #008000;">&#93;</span>, argv<span style="color: #008000;">&#91;</span><span style="color: #0000dd;">2</span><span style="color: #008000;">&#93;</span><span style="color: #008000;">&#41;</span><span style="color: #008000;">&#41;</span>
        <span style="color: #0000dd;">cout</span> <span style="color: #000080;">&lt;&lt;</span> <span style="color: #FF0000;">&quot;match&quot;</span> <span style="color: #000080;">&lt;&lt;</span> endl<span style="color: #008080;">;</span>
    <span style="color: #0000ff;">else</span> <span style="color: #0000dd;">cout</span> <span style="color: #000080;">&lt;&lt;</span> <span style="color: #FF0000;">&quot;not match&quot;</span> <span style="color: #000080;">&lt;&lt;</span> endl<span style="color: #008080;">;</span>
    <span style="color: #0000ff;">return</span> <span style="color: #0000dd;">0</span><span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

]]></content:encoded>
			<wfw:commentRss>http://boundary.cc/200911/string-matching-with-wildcard/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>快速排序详细分析</title>
		<link>http://boundary.cc/200908/quick-sort-analysis/</link>
		<comments>http://boundary.cc/200908/quick-sort-analysis/#comments</comments>
		<pubDate>Thu, 27 Aug 2009 09:15:37 +0000</pubDate>
		<dc:creator>Joker</dc:creator>
				<category><![CDATA[编程]]></category>
		<category><![CDATA[算法]]></category>

		<guid isPermaLink="false">http://jackaldire.com/?p=73</guid>
		<description><![CDATA[快速排序详细分析 注：REF[n]为参考资料，列于文章结尾。 看了编程珠玑Programming Perls第11章关于快速排序的讨论，发现自己长年用库函数，已经忘了快排怎么写。于是整理下思路和资料，把至今所了解的快排的方方面面记录与此。 纲要 算法描述 时间复杂度分析 具体实现细节 划分 选取枢纽元 固定位置 随机选取 三数取中 分割 单向扫描 双向扫描 Hoare的双向扫描 改进的双向扫描 双向扫描的其他问题 分治 尾递归 参考文献 一、算法描述（Algorithm Description） 快速排序由C.A.R.Hoare于1962年提出，算法相当简单精炼，基本策略是随机分治。 首先选取一个枢纽元（pivot），然后将数据划分成左右两部分，左边的大于（或等于）枢纽元，右边的小于(或等于枢纽元)，最后递归处理左右两部分。 分治算法一般分成三个部分：分解、解决以及合并。快排是就地排序，所以就不需要合并了。只需要划分（partition）和解决（递归）两个步骤。因为划分的结果决定递归的位置，所以Partition是整个算法的核心。 对数组S排序的形式化的描述如下（REF[1]）: 如果S中的元素个数是0或1，则返回 取S中任意一元素v，称之为枢纽元 将S-{v}（S中其余元素），划分成两个不相交的集合：S1={x∈S-{v}&#124;x=v} 返回{quicksort(S1) , v , quicksort(S2)} 二、时间复杂度分析（Time Complexity） 快速排序最佳运行时间O(nlogn)，最坏运行时间Ｏ(N^2)，随机化以后期望运行时间O(nlogn)，关于这些任何一本算法数据结构书上都有证明，就不写在这了，一下两点很重要： 选取枢纽元的不同, 决定了快排算法时间复杂度的数量级； 划分方法的划分方法总是O(n), 所以其具体实现的不同只影响算法时间复杂度的系数。 所以诉时间复杂度的分析都是围绕枢纽元的位置展开讨论的。 三、具体实现细节（Details of Implementaion） 1、划分(Partirion) 为了方便讨论，将Partition从QuickSort函数里提出来，就像算法导论里一样。实际实现时我更倾向于合并在一起，就一个函数，减少了函数调用次数。（REF[2]） 1 2 3 4 5 6<a href="http://boundary.cc/200908/quick-sort-analysis/"> <br /><br /> (Read More...)</a>]]></description>
			<content:encoded><![CDATA[<p>快速排序详细分析</p>
<p>注：REF[n]为参考资料，列于文章结尾。</p>
<p>看了编程珠玑Programming Perls第11章关于快速排序的讨论，发现自己长年用库函数，已经忘了快排怎么写。于是整理下思路和资料，把至今所了解的快排的方方面面记录与此。</p>
<p><span id="more-73"></span></p>
<h2>纲要</h2>
<ol>
<li>算法描述</li>
<li>时间复杂度分析</li>
<li>具体实现细节</li>
<ol style="border:0px">
<li>划分</li>
<ol style="border:0px">
<li>选取枢纽元</li>
<ol style="border:0px">
<li>固定位置</li>
<li>随机选取</li>
<li>三数取中</li>
</ol>
<li>分割</li>
<ol style="border:0px">
<li>单向扫描</li>
<li>双向扫描</li>
<li>Hoare的双向扫描</li>
<li>改进的双向扫描</li>
<li>双向扫描的其他问题</li>
</ol>
</ol>
<li>分治</li>
<ol  style="border:0px">
<li>尾递归</li>
</ol>
</ol>
<li>参考文献</li>
</ol>
<h2>一、算法描述（Algorithm Description）</h2>
<p>快速排序由C.A.R.Hoare于1962年提出，算法相当简单精炼，基本策略是随机分治。<br />
首先选取一个枢纽元（pivot），然后将数据划分成左右两部分，左边的大于（或等于）枢纽元，右边的小于(或等于枢纽元)，最后递归处理左右两部分。<br />
分治算法一般分成三个部分：分解、解决以及合并。快排是就地排序，所以就不需要合并了。只需要划分（partition）和解决（递归）两个步骤。因为划分的结果决定递归的位置，所以Partition是整个算法的核心。</p>
<p>对数组S排序的形式化的描述如下（REF[1]）:</p>
<ol>
<li>如果S中的元素个数是0或1，则返回</li>
<li>取S中任意一元素v，称之为枢纽元</li>
<li>将S-{v}（S中其余元素），划分成两个不相交的集合：S1={x∈S-{v}|x<=v} 和 S2={x∈S-{v}|x>=v}</li>
<li>返回{quicksort(S1) , v , quicksort(S2)}</li>
</ol>
<h2>二、时间复杂度分析（Time Complexity）</h2>
<p>快速排序最佳运行时间O(nlogn)，最坏运行时间Ｏ(N^2)，随机化以后期望运行时间O(nlogn)，关于这些任何一本算法数据结构书上都有证明，就不写在这了，一下两点很重要：</p>
<ol>
<li>选取枢纽元的不同, 决定了快排算法时间复杂度的数量级；</li>
<li>划分方法的划分方法总是O(n), 所以其具体实现的不同只影响算法时间复杂度的系数。</li>
</ol>
<p>所以诉时间复杂度的分析都是围绕枢纽元的位置展开讨论的。</p>
<h2>三、具体实现细节（Details of Implementaion）</h2>
<h3>1、划分(Partirion)</h3>
<p>为了方便讨论，将Partition从QuickSort函数里提出来，就像算法导论里一样。实际实现时我更倾向于合并在一起，就一个函数，减少了函数调用次数。（REF[2]）</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
</pre></td><td class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">void</span> QuickSort<span style="color: #008000;">&#40;</span>T A<span style="color: #008000;">&#91;</span><span style="color: #008000;">&#93;</span>, <span style="color: #0000ff;">int</span> p, <span style="color: #0000ff;">int</span> q<span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
    <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>p <span style="color: #000080;">&lt;</span> q<span style="color: #008000;">&#41;</span>
    <span style="color: #008000;">&#123;</span>
        <span style="color: #0000ff;">int</span> q <span style="color: #000080;">=</span> Partition<span style="color: #008000;">&#40;</span>A, p, q<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
        QuickSort<span style="color: #008000;">&#40;</span>A, p, q<span style="color: #000040;">-</span><span style="color: #0000dd;">1</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
        QuickSort<span style="color: #008000;">&#40;</span>A, q<span style="color: #000040;">+</span><span style="color: #0000dd;">1</span>, r<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
    <span style="color: #008000;">&#125;</span>
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<p>划分又分成两个步骤：<b>选取枢纽元</b>和<b>按枢纽元将数组分成左右两部分</b></p>
<h4>a.选取枢纽元（Pivot Selection）</h4>
<h5>固定位置</h5>
<p>同样是为了方便，将选取枢纽元单独提出来成一个函数：select_pivot(T A[], int p, int q)，该函数从A[p...q]中选取一个枢纽元并返回，且枢纽元放置在左端（A[p]的位置）。</p>
<p>对于完全随机的数据，枢纽元的选取不是很重要，往往直接取左端的元素作为枢纽元。</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
</pre></td><td class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">int</span> select_pivot<span style="color: #008000;">&#40;</span>T A<span style="color: #008000;">&#91;</span><span style="color: #008000;">&#93;</span>, <span style="color: #0000ff;">int</span> p, <span style="color: #0000ff;">int</span> q<span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
    <span style="color: #0000ff;">return</span> A<span style="color: #008000;">&#91;</span>p<span style="color: #008000;">&#93;</span><span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<p>但是实际应用中，数据往往是部分有序的，如果仍用两端的元素最为枢纽元，则会产生很不好的划分，使算法退化成O(n^2)。所以要采用一些手段避免这种情况，我知道的有“随机选取法”和“三数取中法”。</p>
<h5>随机选取</h5>
<p>顾名思义就是从A[p...q]中随机选择一个枢纽元，这个用库函数可以很容易实现</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
</pre></td><td class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">int</span> select_pivot_random<span style="color: #008000;">&#40;</span>T A<span style="color: #008000;">&#91;</span><span style="color: #008000;">&#93;</span>, <span style="color: #0000ff;">int</span> p, <span style="color: #0000ff;">int</span> q<span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
    <span style="color: #0000ff;">int</span> i <span style="color: #000080;">=</span> randInt<span style="color: #008000;">&#40;</span>p, q<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
    swap<span style="color: #008000;">&#40;</span>A<span style="color: #008000;">&#91;</span>p<span style="color: #008000;">&#93;</span>, A<span style="color: #008000;">&#91;</span>i<span style="color: #008000;">&#93;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
    <span style="color: #0000ff;">return</span> A<span style="color: #008000;">&#91;</span>p<span style="color: #008000;">&#93;</span><span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<p>其中randInt(p, q)随机返回[p, q]中的一个数，C/C++里可由stdlib.h中的rand函数模拟。</p>
<h5>三数取中</h5>
<p>即取三个元素的中间数作为枢纽元，一般是取左端、右断和中间三个数，也可以随机选取。（REF[1]）</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
</pre></td><td class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">int</span> select_pivot_median3<span style="color: #008000;">&#40;</span>T A<span style="color: #008000;">&#91;</span><span style="color: #008000;">&#93;</span>, <span style="color: #0000ff;">int</span> p, <span style="color: #0000ff;">int</span> q<span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
    <span style="color: #0000ff;">int</span> m <span style="color: #000080;">=</span> <span style="color: #008000;">&#40;</span>p <span style="color: #000040;">+</span> q<span style="color: #008000;">&#41;</span><span style="color: #000040;">/</span><span style="color: #0000dd;">2</span><span style="color: #008080;">;</span>
    <span style="color: #ff0000; font-style: italic;">/* swap to ensure A[m] &lt;= A[p] &lt;= A[q] */</span>
    <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>A<span style="color: #008000;">&#91;</span>p<span style="color: #008000;">&#93;</span> <span style="color: #000080;">&lt;</span> A<span style="color: #008000;">&#91;</span>m<span style="color: #008000;">&#93;</span><span style="color: #008000;">&#41;</span> swap<span style="color: #008000;">&#40;</span>A<span style="color: #008000;">&#91;</span>p<span style="color: #008000;">&#93;</span>, A<span style="color: #008000;">&#91;</span>m<span style="color: #008000;">&#93;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
    <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>A<span style="color: #008000;">&#91;</span>q<span style="color: #008000;">&#93;</span> <span style="color: #000080;">&lt;</span> A<span style="color: #008000;">&#91;</span>m<span style="color: #008000;">&#93;</span><span style="color: #008000;">&#41;</span> swap<span style="color: #008000;">&#40;</span>A<span style="color: #008000;">&#91;</span>q<span style="color: #008000;">&#93;</span>, A<span style="color: #008000;">&#91;</span>m<span style="color: #008000;">&#93;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
    <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>A<span style="color: #008000;">&#91;</span>q<span style="color: #008000;">&#93;</span> <span style="color: #000080;">&lt;</span> A<span style="color: #008000;">&#91;</span>p<span style="color: #008000;">&#93;</span><span style="color: #008000;">&#41;</span> swap<span style="color: #008000;">&#40;</span>A<span style="color: #008000;">&#91;</span>q<span style="color: #008000;">&#93;</span>, A<span style="color: #008000;">&#91;</span>p<span style="color: #008000;">&#93;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
    <span style="color: #0000ff;">return</span> A<span style="color: #008000;">&#91;</span>p<span style="color: #008000;">&#93;</span><span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<h4>b.按枢纽元将数组分成左右两部分</h4>
<p>虽然说分割方法只影响算法时间复杂度的系数，但是一个好系数也是比较重要的。这也就是为什么实际应用中宁愿选择可能退化成O(n^2)的快速排序，也不用稳定的堆排序（堆排序交换次数太多，导致系数很大）。</p>
<p>常见的分割方法有三种：</p>
<h5>单向扫描</h5>
<p>单向扫描代码非常简单，只有短短的几行，思路也比较清晰。该算法由N.Lomuto提出，算法导论上也采用了这种算法。对于数组A[p...q]，该算法用一个循环扫描整个区间，并维护一个标志m，使得循环不变量（loop invariant）A[p＋1...m] < A[p] &#038;&#038; A[m+1, i-1] >= x[l]始终成立。（REF[2],REF[3]）</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
</pre></td><td class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">int</span> partition<span style="color: #008000;">&#40;</span>T A<span style="color: #008000;">&#91;</span><span style="color: #008000;">&#93;</span>, <span style="color: #0000ff;">int</span> p, <span style="color: #0000ff;">int</span> q<span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
    <span style="color: #0000ff;">int</span> x <span style="color: #000080;">=</span> select_pivot<span style="color: #008000;">&#40;</span>A, p, q<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
    <span style="color: #0000ff;">int</span> m <span style="color: #000080;">=</span> p, j<span style="color: #008080;">;</span>
    <span style="color: #0000ff;">for</span> <span style="color: #008000;">&#40;</span><span style="color: #0000ff;">int</span> j <span style="color: #000080;">=</span> p<span style="color: #000040;">+</span><span style="color: #0000dd;">1</span><span style="color: #008080;">;</span> j <span style="color: #000080;">&lt;=</span> q<span style="color: #008080;">;</span> <span style="color: #000040;">++</span>j<span style="color: #008000;">&#41;</span>
        <span style="color: #ff0000; font-style: italic;">/* invariant : A[p＋1...m] &lt; A[p] &amp;&amp; A[m+1, i-1] &gt;= x[q] */</span>
        <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>A<span style="color: #008000;">&#91;</span>j<span style="color: #008000;">&#93;</span> <span style="color: #000080;">&lt;=</span> x<span style="color: #008000;">&#41;</span>
            swap<span style="color: #008000;">&#40;</span>A<span style="color: #008000;">&#91;</span><span style="color: #000040;">++</span>m<span style="color: #008000;">&#93;</span>, A<span style="color: #008000;">&#91;</span>j<span style="color: #008000;">&#93;</span><span style="color: #008000;">&#41;</span> 
    swap<span style="color: #008000;">&#40;</span>A<span style="color: #008000;">&#91;</span>p<span style="color: #008000;">&#93;</span>, A<span style="color: #008000;">&#91;</span>m<span style="color: #008000;">&#93;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> 
    <span style="color: #ff0000; font-style: italic;">/* A[p...m-1] &lt; A[m] &lt;= A[m+1...u] */</span>
    <span style="color: #0000ff;">return</span> m<span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<p>顺便废话几句，在看国外的书的时候，发现老外在分析和测试算法尤其是循环时，非常重视不变量（invariant）的使用。确立一个不变量，在循环开始之前和结束之后检查这个不变量，是一个很好的保持算法正确性的手段。</p>
<p>事实上第一种算法需要的交换次数比较多，而且如果采用选取左端元素作为枢纽元的方法，该算法在输入数组中元素全部相同时退化成O(n^2)。第二种方法可以避免这个问题。</p>
<h5>双向扫描</h5>
<p>双向扫描用两个标志i、j，分别初始化成数组的两端。主循环里嵌套两个内循环：第一个内循环i从左向右移过小于枢纽元的元素，遇到大元素时停止；第二个循环j从右向左移过大于枢纽元的元素，遇到小元素时停止。然后主循环检查i、j是否相交并交换A[i]、A[j]。</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
</pre></td><td class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">int</span> partition<span style="color: #008000;">&#40;</span>T A<span style="color: #008000;">&#91;</span><span style="color: #008000;">&#93;</span>, <span style="color: #0000ff;">int</span> p, <span style="color: #0000ff;">int</span> q<span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
    <span style="color: #0000ff;">int</span> x <span style="color: #000080;">=</span> select_pivot<span style="color: #008000;">&#40;</span>A, p, q<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
    <span style="color: #0000ff;">int</span> i <span style="color: #000080;">=</span> p, j <span style="color: #000080;">=</span> q <span style="color: #000040;">+</span> <span style="color: #0000dd;">1</span><span style="color: #008080;">;</span>
    <span style="color: #0000ff;">for</span> <span style="color: #008000;">&#40;</span> <span style="color: #008080;">;</span> <span style="color: #008080;">;</span> <span style="color: #008000;">&#41;</span>
    <span style="color: #008000;">&#123;</span>
        <span style="color: #0000ff;">do</span> <span style="color: #000040;">++</span>i<span style="color: #008080;">;</span> <span style="color: #0000ff;">while</span> <span style="color: #008000;">&#40;</span>i <span style="color: #000080;">&lt;=</span> q <span style="color: #000040;">&amp;&amp;</span> A<span style="color: #008000;">&#91;</span>i<span style="color: #008000;">&#93;</span> <span style="color: #000080;">&lt;</span> x<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
        <span style="color: #0000ff;">do</span> <span style="color: #000040;">--</span>j<span style="color: #008080;">;</span> <span style="color: #0000ff;">while</span> <span style="color: #008000;">&#40;</span>A<span style="color: #008000;">&#91;</span>j<span style="color: #008000;">&#93;</span> <span style="color: #000080;">&gt;</span> x<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
        <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>i <span style="color: #000080;">&gt;</span> j<span style="color: #008000;">&#41;</span> <span style="color: #0000ff;">break</span><span style="color: #008080;">;</span>
        swap<span style="color: #008000;">&#40;</span>A<span style="color: #008000;">&#91;</span>i<span style="color: #008000;">&#93;</span>, A<span style="color: #008000;">&#91;</span>j<span style="color: #008000;">&#93;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
    <span style="color: #008000;">&#125;</span>
    swap<span style="color: #008000;">&#40;</span>A<span style="color: #008000;">&#91;</span>p<span style="color: #008000;">&#93;</span>, A<span style="color: #008000;">&#91;</span>j<span style="color: #008000;">&#93;</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
    <span style="color: #0000ff;">return</span> j<span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<p>双向扫描可以正常处理所有元素相同的情况，而且交换次数比单向扫描要少。</p>
<h5>Hoare的双向扫描</h5>
<p>这种方法是Hoare在62年最初提出快速排序采用的方法，与前面的双向扫描基本相同，但是更难理解，手算了几组数据才搞明白：（REF[2]）</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
</pre></td><td class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">int</span> partition<span style="color: #008000;">&#40;</span>T A<span style="color: #008000;">&#91;</span><span style="color: #008000;">&#93;</span>, <span style="color: #0000ff;">int</span> p, <span style="color: #0000ff;">int</span> q<span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
    <span style="color: #0000ff;">int</span> x <span style="color: #000080;">=</span> select_pivot<span style="color: #008000;">&#40;</span>A, p, q<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
    <span style="color: #0000ff;">int</span> i <span style="color: #000080;">=</span> p <span style="color: #000040;">-</span> <span style="color: #0000dd;">1</span>, j <span style="color: #000080;">=</span> q <span style="color: #000040;">+</span> <span style="color: #0000dd;">1</span><span style="color: #008080;">;</span>
    <span style="color: #0000ff;">for</span> <span style="color: #008000;">&#40;</span> <span style="color: #008080;">;</span> <span style="color: #008080;">;</span> <span style="color: #008000;">&#41;</span>
    <span style="color: #008000;">&#123;</span>
        <span style="color: #0000ff;">do</span> <span style="color: #000040;">--</span>j<span style="color: #008080;">;</span> <span style="color: #0000ff;">while</span> <span style="color: #008000;">&#40;</span>A<span style="color: #008000;">&#91;</span>j<span style="color: #008000;">&#93;</span> <span style="color: #000080;">&gt;</span> x<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
        <span style="color: #0000ff;">do</span> <span style="color: #000040;">++</span>i<span style="color: #008080;">;</span> <span style="color: #0000ff;">while</span> <span style="color: #008000;">&#40;</span>A<span style="color: #008000;">&#91;</span>i<span style="color: #008000;">&#93;</span> <span style="color: #000080;">&lt;</span> x<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
        <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>i <span style="color: #000080;">&lt;</span> j<span style="color: #008000;">&#41;</span> swap<span style="color: #008000;">&#40;</span>A<span style="color: #008000;">&#91;</span>i<span style="color: #008000;">&#93;</span>, A<span style="color: #008000;">&#91;</span>j<span style="color: #008000;">&#93;</span><span style="color: #008000;">&#41;</span>
        <span style="color: #0000ff;">else</span> <span style="color: #0000ff;">return</span> j<span style="color: #008080;">;</span>
    <span style="color: #008000;">&#125;</span>
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<p>需要注意的是，返回值j并不是枢纽元的位置，但是仍然保证了A[p..j] <= A[j+1...q]。这种方法在效率上于双向扫描差别甚微，只是代码相对更为紧凑，并且用A[p]做哨兵元素减少了内层循环的一个if测试。</p>
<p>http://www.see2say.com/channel/music/player.aspx?v_album_id=9804</p>
<h5>改进的双向扫描</h5>
<p>枢纽元保存在一个临时变量中，这样左端的位置可视为空闲。j从右向左扫描，直到A[j]小于等于枢纽元,检查i、j是否相交并将A[j]赋给空闲位置A[i],这时A[j]变成空闲位置；i从左向右扫描，直到A[i]大于等于枢纽元,检查i、j是否相交并将A[i]赋给空闲位置A[j]，然后A[i]变成空闲位置。重复上述过程，最后直到i、j相交跳出循环。最后把枢纽元放到空闲位置上。</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
12
13
14
</pre></td><td class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">int</span> partition<span style="color: #008000;">&#40;</span>T A<span style="color: #008000;">&#91;</span><span style="color: #008000;">&#93;</span>, <span style="color: #0000ff;">int</span> p, <span style="color: #0000ff;">int</span> q<span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
    <span style="color: #0000ff;">int</span> x <span style="color: #000080;">=</span> select_pivot<span style="color: #008000;">&#40;</span>A, p, q<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
    <span style="color: #0000ff;">int</span> i <span style="color: #000080;">=</span> p, j <span style="color: #000080;">=</span> q<span style="color: #008080;">;</span>
    <span style="color: #0000ff;">for</span> <span style="color: #008000;">&#40;</span> <span style="color: #008080;">;</span> <span style="color: #008080;">;</span> <span style="color: #008000;">&#41;</span>
    <span style="color: #008000;">&#123;</span>
        <span style="color: #0000ff;">while</span> <span style="color: #008000;">&#40;</span>i <span style="color: #000080;">&lt;</span> j <span style="color: #000040;">&amp;&amp;</span> A<span style="color: #008000;">&#91;</span>j<span style="color: #008000;">&#93;</span> <span style="color: #000080;">&gt;</span> x<span style="color: #008000;">&#41;</span> <span style="color: #000040;">--</span>j<span style="color: #008080;">;</span>
        A<span style="color: #008000;">&#91;</span>i<span style="color: #008000;">&#93;</span> <span style="color: #000080;">=</span> A<span style="color: #008000;">&#91;</span>j<span style="color: #008000;">&#93;</span><span style="color: #008080;">;</span>
        <span style="color: #0000ff;">while</span> <span style="color: #008000;">&#40;</span>i <span style="color: #000080;">&lt;</span> j <span style="color: #000040;">&amp;&amp;</span> A<span style="color: #008000;">&#91;</span>i<span style="color: #008000;">&#93;</span> <span style="color: #000080;">&lt;</span> x<span style="color: #008000;">&#41;</span> <span style="color: #000040;">++</span>i<span style="color: #008080;">;</span>
        A<span style="color: #008000;">&#91;</span>j<span style="color: #008000;">&#93;</span> <span style="color: #000080;">=</span> A<span style="color: #008000;">&#91;</span>i<span style="color: #008000;">&#93;</span><span style="color: #008080;">;</span>
    <span style="color: #008000;">&#125;</span>
    A<span style="color: #008000;">&#91;</span>i<span style="color: #008000;">&#93;</span> <span style="color: #000080;">=</span> x<span style="color: #008080;">;</span>  <span style="color: #666666;">// i == j</span>
    <span style="color: #0000ff;">return</span> i<span style="color: #008080;">;</span>
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<p>这种类似迭代的方法，每次只需一次赋值，减少了内存读写次数，而前面几种的方法一次交换需要三次赋值操作。由于没有哨兵元素，不得不在内层循环里判断i、j是否相交，实际上反而增加了很多内存读取操作。但是由于循环计数器往往被放在寄存器了，而如果待排数组很大，访问其元素会频繁的cache miss，所以用计数器的访问次数换取待排数组的访存是值得的。</p>
<h5>关于双向扫描的几个问题</h5>
<p>1.内层循环中的while测试是用“严格大于/小于”还是”大于等于/小于等于”。</p>
<p>一般的想法是用大于等于/小于等于，忽略与枢纽元相同的元素，这样可以减少不必要的交换，因为这些元素无论放在哪一边都是一样的。但是如果遇到所有元素都一样的情况，这种方法每次都会产生最坏的划分，也就是一边1个元素，令一边n－1个元素，使得时间复杂度变成O(N^2)。而如果用严格大于/小于，虽然两边指针每此只挪动1位，但是它们会在正中间相遇，产生一个最好的划分，时间复杂度为log(2,n)。</p>
<p>另一个因素是，如果将枢纽元放在数组两端，用严格大于/小于就可以将枢纽元作为一个哨兵元素，从而减少内层循环的一个测试。<br />
由以上两点，内层循环中的while测试一般用“严格大于/小于”。</p>
<p>2.对于小数组特殊处理</p>
<p>按照上面的方法，递归会持续到分区只有一个元素。而事实上，当分割到一定大小后，继续分割的效率比插入排序要差。由统计方法得到的数值是50左右(REF[3])，也有采用20的（REF[1]）, 这样原先的QuickSort就可以写成这样</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
10
11
</pre></td><td class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">void</span> QuickSort<span style="color: #008000;">&#40;</span>T A<span style="color: #008000;">&#91;</span><span style="color: #008000;">&#93;</span>, <span style="color: #0000ff;">int</span> p, <span style="color: #0000ff;">int</span> q<span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
    <span style="color: #0000ff;">if</span> <span style="color: #008000;">&#40;</span>q <span style="color: #000040;">-</span> p <span style="color: #000080;">&gt;</span> cutoff<span style="color: #008000;">&#41;</span> <span style="color: #666666;">//cutoff is constant </span>
    <span style="color: #008000;">&#123;</span>
        <span style="color: #0000ff;">int</span> q <span style="color: #000080;">=</span> Partition<span style="color: #008000;">&#40;</span>A, p, q<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
        QuickSort<span style="color: #008000;">&#40;</span>A, p, q<span style="color: #000040;">-</span><span style="color: #0000dd;">1</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
        QuickSort<span style="color: #008000;">&#40;</span>A, q<span style="color: #000040;">+</span><span style="color: #0000dd;">1</span>, r<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
    <span style="color: #008000;">&#125;</span>
    <span style="color: #0000ff;">else</span>
        InsertionSort<span style="color: #008000;">&#40;</span>T A<span style="color: #008000;">&#91;</span><span style="color: #008000;">&#93;</span>, p, q<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span> <span style="color: #666666;">//user insertion sort for small arrays</span>
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<h2>二、分治</h2>
<p>分治这里看起来没什么可说的，就是一枢纽元为中心，左右递归，实际上也有一些技巧。</p>
<h3>1.尾递归（Tail recursion）</h3>
<p>快排算法和大多数分治排序算法一样，都有两次递归调用。但是快排与归并排序不同，归并的递归则在函数一开始， 快排的递归在函数尾部，这就使得快排代码可以实施尾递归优化。第一次递归以后，变量p就没有用处了， 也就是说第二次递归可以用迭代控制结构代替。虽然这种优化一般是有编译器实施，但是也可以人为的模拟：</p>

<div class="wp_syntax"><table><tr><td class="line_numbers"><pre>1
2
3
4
5
6
7
8
9
</pre></td><td class="code"><pre class="cpp" style="font-family:monospace;"><span style="color: #0000ff;">void</span> QuickSort<span style="color: #008000;">&#40;</span>T A<span style="color: #008000;">&#91;</span><span style="color: #008000;">&#93;</span>, <span style="color: #0000ff;">int</span> p, <span style="color: #0000ff;">int</span> q<span style="color: #008000;">&#41;</span>
<span style="color: #008000;">&#123;</span>
    <span style="color: #0000ff;">while</span> <span style="color: #008000;">&#40;</span>p <span style="color: #000080;">&lt;</span> q<span style="color: #008000;">&#41;</span>
    <span style="color: #008000;">&#123;</span>
        <span style="color: #0000ff;">int</span> m <span style="color: #000080;">=</span> Partition<span style="color: #008000;">&#40;</span>A, p, q<span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
        QuickSort<span style="color: #008000;">&#40;</span>A, p, m<span style="color: #000040;">-</span><span style="color: #0000dd;">1</span><span style="color: #008000;">&#41;</span><span style="color: #008080;">;</span>
        p <span style="color: #000080;">=</span> m <span style="color: #000040;">+</span> <span style="color: #0000dd;">1</span><span style="color: #008080;">;</span>
    <span style="color: #008000;">&#125;</span>
<span style="color: #008000;">&#125;</span></pre></td></tr></table></div>

<p>采用这种方法可以缩减堆栈深度，由原来的O(n)缩减为O(logn)。</p>
<h2>三、参考文献：</h2>
<li>[1]Mark Allen Weiss. <em>Data Structures and Algorithms Analysis in C++</em>. Pearson Education, Third Edition, 2006.</li>
<li>[2]Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, Clifford Stein. <em>Introduction to Algorithms</em>. MIT Press, 2001, Second Edtion, 2001.</li>
<li>[3]Jon Bently. <em>Programming Pearls</em>. Addison Wesley, Second Edition, 2000.</li>
]]></content:encoded>
			<wfw:commentRss>http://boundary.cc/200908/quick-sort-analysis/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>[备忘]倒置字符串中的单词</title>
		<link>http://boundary.cc/200908/reverse-token/</link>
		<comments>http://boundary.cc/200908/reverse-token/#comments</comments>
		<pubDate>Tue, 18 Aug 2009 14:24:08 +0000</pubDate>
		<dc:creator>Joker</dc:creator>
				<category><![CDATA[编程]]></category>
		<category><![CDATA[备忘]]></category>
		<category><![CDATA[算法]]></category>

		<guid isPermaLink="false">http://jackaldire.com/?p=70</guid>
		<description><![CDATA[输入：一个字符串，单词用某个特定符号分割（比如空格） 输出：一个字符串，单词顺序和原串相反 看到倒置，一般的做法是用栈，要么自己建个数组、要么STL，或者递归用程序栈。 优雅的递归 void reverse_token&#40;&#41; &#123; char str&#91;MAX&#93; = &#123;0&#125;; if &#40;scanf&#40;&#34;%[^#]&#34;, str&#41; != EOF&#41; &#123; //利用scanf的正则式特性 getchar&#40;&#41;; reverse_token&#40;&#41;; printf&#40;&#34;%s &#34;, str&#41;; &#125; &#125; STL list void reverse_token&#40;&#41; &#123; char tmp&#91;MAX&#93;; list&#60;string&#62; stack; while &#40;cin.getline&#40;tmp, MAX, '#'&#41;&#41; stack.push_front&#40;string&#40;tmp&#41;&#41;; copy&#40;stack.begin&#40;&#41;, stack.end&#40;&#41;, ostream_iterator&#60;string&#62;&#40;cout,&#34; &#34;&#41;&#41;; &#125; 如果是处理字符串, 而不是stdin, 可以改用sscanf()或者STL的范型算法find, 更标准的做法是strtok()。想到了javascript里的String().split(&#8216;x&#8217;)，直接返回一个分割后的数组，相当的方便。]]></description>
			<content:encoded><![CDATA[<p>输入：一个字符串，单词用某个特定符号分割（比如空格）<br />
输出：一个字符串，单词顺序和原串相反</p>
<p>看到倒置，一般的做法是用栈，要么自己建个数组、要么STL，或者递归用程序栈。</p>
<h2>优雅的递归</h2>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">void</span> reverse_token<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
  <span style="color: #993333;">char</span> str<span style="color: #009900;">&#91;</span>MAX<span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #009900;">&#123;</span><span style="color: #0000dd;">0</span><span style="color: #009900;">&#125;</span><span style="color: #339933;">;</span>
  <span style="color: #b1b100;">if</span> <span style="color: #009900;">&#40;</span>scanf<span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;%[^#]&quot;</span><span style="color: #339933;">,</span> str<span style="color: #009900;">&#41;</span> <span style="color: #339933;">!=</span> EOF<span style="color: #009900;">&#41;</span>  <span style="color: #009900;">&#123;</span> <span style="color: #666666; font-style: italic;">//利用scanf的正则式特性</span>
      getchar<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
      reverse_token<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
      <span style="color: #000066;">printf</span><span style="color: #009900;">&#40;</span><span style="color: #ff0000;">&quot;%s &quot;</span><span style="color: #339933;">,</span> str<span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
  <span style="color: #009900;">&#125;</span> 
<span style="color: #009900;">&#125;</span></pre></div></div>

<p><span id="more-70"></span></p>
<h2>STL list</h2>

<div class="wp_syntax"><div class="code"><pre class="c" style="font-family:monospace;"><span style="color: #993333;">void</span> reverse_token<span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #993333;">char</span> tmp<span style="color: #009900;">&#91;</span>MAX<span style="color: #009900;">&#93;</span><span style="color: #339933;">;</span>
    list<span style="color: #339933;">&lt;</span>string<span style="color: #339933;">&gt;</span> stack<span style="color: #339933;">;</span>
    <span style="color: #b1b100;">while</span> <span style="color: #009900;">&#40;</span>cin.<span style="color: #202020;">getline</span><span style="color: #009900;">&#40;</span>tmp<span style="color: #339933;">,</span> MAX<span style="color: #339933;">,</span> <span style="color: #ff0000;">'#'</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> stack.<span style="color: #202020;">push_front</span><span style="color: #009900;">&#40;</span>string<span style="color: #009900;">&#40;</span>tmp<span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
    copy<span style="color: #009900;">&#40;</span>stack.<span style="color: #202020;">begin</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> stack.<span style="color: #202020;">end</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span> ostream_iterator<span style="color: #339933;">&lt;</span>string<span style="color: #339933;">&gt;</span><span style="color: #009900;">&#40;</span><span style="color: #000066;">cout</span><span style="color: #339933;">,</span><span style="color: #ff0000;">&quot; &quot;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>
如果是处理字符串, 而不是stdin, 可以改用sscanf()或者STL的范型算法find, 更标准的做法是strtok()。想到了javascript里的String().split(&#8216;x&#8217;)，直接返回一个分割后的数组，相当的方便。</p>
]]></content:encoded>
			<wfw:commentRss>http://boundary.cc/200908/reverse-token/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Fibonacci数的巧妙求法</title>
		<link>http://boundary.cc/200905/smart-way-to-solve-fibonacci/</link>
		<comments>http://boundary.cc/200905/smart-way-to-solve-fibonacci/#comments</comments>
		<pubDate>Wed, 06 May 2009 11:32:34 +0000</pubDate>
		<dc:creator>Joker</dc:creator>
				<category><![CDATA[编程]]></category>
		<category><![CDATA[Fibonacci]]></category>
		<category><![CDATA[算法]]></category>
		<category><![CDATA[算法导论]]></category>

		<guid isPermaLink="false">http://jackaldire.com/?p=25</guid>
		<description><![CDATA[看MIT算法导论视频真是收获不蜚，今天学会了求Fibonacci数最快的方法～ Fibonacci（斐波那契）数列小学生都知道的，求Fibonacci数也不是什么难事，可以用递推式一步一步推(线性)。有没有更快的方法呢，你可能马上就想到了通项公式: 这个公式要算一个数的n次方，由于一个数的n次方可以由一个分治算法在logn时间内得到： 所以这个算法时间复杂度是O(logn)的。推导没有错误，但是忽略了一个问题：精度。无理数只能近似表示，所以无理数的乘方是有误差的，而且误差越乘越大，所以需要足够多的有效位数才能保证最后的答案是正确的。由于是取最近的整数，只要误差超过0.5，结果就会出错。如果n很大，比如说1亿，这个算法就得几乎不可到正确结果。而下面的算法则避开了这个问题。 设Fibonacci数列的第n个数是Fn，则有： 好吧，一个2*2的matrix就解决问题，真是精妙的算法。这个公式可以轻松的用数学归纳法验证。由于计算过程中都是整数，所以不会出现精度问题。两个2＊2的矩阵相乘复杂度O(1)，矩阵的乘方也可用上面的分治算法求得，所以 T(n) ＝ T(n/2) + O(1)，由主定理解得 T(n) = O(logn)。 MIT的算法导论课程视频和讲义VeryCD上有：Click me.  主讲老师就是《算法导论》的主要作者之一：Charles E. Leiseson (中文版封底上有他的照片)，讲得非常好，通俗易懂，还可以顺带练习英语听力～～强烈推荐]]></description>
			<content:encoded><![CDATA[<p id="fp">看MIT算法导论视频真是收获不蜚，今天学会了求Fibonacci数最快的方法～</p>
<p style="text-indent:2em">Fibonacci（斐波那契）数列小学生都知道的，求Fibonacci数也不是什么难事，可以用递推式一步一步推(线性)。有没有更快的方法呢，你可能马上就想到了通项公式:</p>
<p style="text-indent: 2em; text-align: center;"><img class="aligncenter" title="Fibonacci" src="http://upload.wikimedia.org/math/f/f/4/ff4f4e93fa405071492981a63678442c.png" alt="" width="278" height="39" /><img src="file:///tmp/moz-screenshot-1.jpg" alt="" /><img src="file:///tmp/moz-screenshot.jpg" alt="" /></p>
<p style="text-indent:2em">这个公式要算一个数的n次方，由于一个数的n次方可以由一个分治算法在logn时间内得到：<img class="size-full wp-image-27 aligncenter" title="a" src="http://jackaldire.com/wordpress/wp-content/uploads/2009/05/a.png" alt="a" width="324" height="63" /></p>
<p style="text-indent:2em">
<p><span id="more-25"></span></p>
<p style="text-indent:2em">所以这个算法时间复杂度是O(logn)的。推导没有错误，但是忽略了一个问题：精度。无理数只能近似表示，所以无理数的乘方是有误差的，而且误差越乘越大，所以需要足够多的有效位数才能保证最后的答案是正确的。由于是取最近的整数，只要误差超过0.5，结果就会出错。如果n很大，比如说1亿，这个算法就得几乎不可到正确结果。而下面的算法则避开了这个问题。</p>
<p style="text-indent:2em">设Fibonacci数列的第n个数是Fn，则有：</p>
<p style="text-indent:2em"><img class="aligncenter size-full wp-image-29" title="21" src="http://jackaldire.com/wordpress/wp-content/uploads/2009/05/21.png" alt="21" width="217" height="77" />好吧，一个2*2的matrix就解决问题，真是精妙的算法。这个公式可以轻松的用数学归纳法验证。由于计算过程中都是整数，所以不会出现精度问题。两个2＊2的矩阵相乘复杂度O(1)，矩阵的乘方也可用上面的分治算法求得，所以 T(n) ＝ T(n/2) + O(1)，由主定理解得 T(n) = O(logn)。</p>
<p style="text-indent:2em">MIT的算法导论课程视频和讲义VeryCD上有：<a href="http://www.verycd.com/topics/87348/" target="_blank">Click me</a>.  主讲老师就是《算法导论》的主要作者之一：Charles E. Leiseson (中文版封底上有他的照片)，讲得非常好，通俗易懂，还可以顺带练习英语听力～～强烈推荐</p>
]]></content:encoded>
			<wfw:commentRss>http://boundary.cc/200905/smart-way-to-solve-fibonacci/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>

