﻿<?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>IT博客-菜鸟天堂-随笔分类-算法与数据结构</title><link>http://www.cnitblog.com/tonywearme/category/3674.html</link><description>海纳百川有容乃大，壁立千仞无欲则刚</description><language>zh-cn</language><lastBuildDate>Sat, 01 Oct 2011 09:35:22 GMT</lastBuildDate><pubDate>Sat, 01 Oct 2011 09:35:22 GMT</pubDate><ttl>60</ttl><item><title>折半插入排序的实现</title><link>http://www.cnitblog.com/tonywearme/archive/2006/09/01/16259.html</link><dc:creator>樱木</dc:creator><author>樱木</author><pubDate>Fri, 01 Sep 2006 08:22:00 GMT</pubDate><guid>http://www.cnitblog.com/tonywearme/archive/2006/09/01/16259.html</guid><wfw:comment>http://www.cnitblog.com/tonywearme/comments/16259.html</wfw:comment><comments>http://www.cnitblog.com/tonywearme/archive/2006/09/01/16259.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/tonywearme/comments/commentRss/16259.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/tonywearme/services/trackbacks/16259.html</trackback:ping><description><![CDATA[
		<p>template&lt;typename T&gt;<br />void insertion_sort( T * t, const int &amp; size )<br />{<br /> int key,<br />  i, j,<br />  low, high, mid;<br />  <br /> for( i=1; i&lt;size; i++ )<br /> {<br />  if( t[i] &lt; t[i-1] )<br />  {<br />   low = 0;<br />   high = i-1;<br />   key = t[i];<br />   while( low &lt;= high )<br />   {<br />    mid = (low+high)/2;<br />    <br />    if( key &lt; t[mid] )<br />     high = mid - 1;<br />    else<br />     low = mid + 1;    <br />   }   <br />   for( j=i; j&gt;high+1; j-- )<br />    t[j] = t[j-1];<br />   t[high+1] = key;<br />  }<br /> }<br />}</p>
		<p>// 保持稳定性要求折半查找返回最后相等的关键字的位置或其右边<br />// high+1: 插入的位置<br /><br />分析:<br />由于折半插入排序和直接插入排序相比, 仅减少了关键字比较的次数, 而记录移动次数不变. 其时间复杂度仍为O(n^2).  而且它查找和移动不是在同一个迭代中完成的. 所以只有当n较大时其性能才略优于直接插入排序.</p>
<img src ="http://www.cnitblog.com/tonywearme/aggbug/16259.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/tonywearme/" target="_blank">樱木</a> 2006-09-01 16:22 <a href="http://www.cnitblog.com/tonywearme/archive/2006/09/01/16259.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>直接插入排序的链表实现</title><link>http://www.cnitblog.com/tonywearme/archive/2006/09/01/16258.html</link><dc:creator>樱木</dc:creator><author>樱木</author><pubDate>Fri, 01 Sep 2006 07:00:00 GMT</pubDate><guid>http://www.cnitblog.com/tonywearme/archive/2006/09/01/16258.html</guid><wfw:comment>http://www.cnitblog.com/tonywearme/comments/16258.html</wfw:comment><comments>http://www.cnitblog.com/tonywearme/archive/2006/09/01/16258.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/tonywearme/comments/commentRss/16258.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/tonywearme/services/trackbacks/16258.html</trackback:ping><description><![CDATA[template&lt;class T&gt;<br />void insertion_sort( node&lt;T&gt; * &amp; head )//<br />{<br /> node&lt;T&gt; * first_unsorted,<br />      * last_sorted,<br />   * insert,<br />   * insertPre;<br /> if( head )<br /> {<br />  last_sorted = head;  <br />  while( last_sorted-&gt;next )<br />  {<br />   first_unsorted = last_sorted-&gt;next;<br />   if( first_unsorted-&gt;key &lt; last_sorted-&gt;key )<br />   {<br />    insert = head;   <br />    while( first_unsorted-&gt;key&gt;=insert-&gt;key ) //从前到后, 保持稳定<br />    {<br />     insertPre = insert;<br />     insert = insert-&gt;next;<br />    }    <br />    if( insert == head )<br />    {<br />     last_sorted-&gt;next = first_unsorted-&gt;next;//<br />     first_unsorted-&gt;next = head;<br />     head = first_unsorted;       <br />    }<br />    else<br />    {     <br />     last_sorted-&gt;next = first_unsorted-&gt;next;<br />     first_unsorted-&gt;next = insert;<br />     insertPre-&gt;next = first_unsorted;<br />    }    <br />   }<br />      else<br />    last_sorted = first_unsorted;//   <br />  } <br /> }<br />}<br />说明:<br />1) 插入之前不需要移动元素, 所以不需要从后往前查找. <br />2) 保持算法的稳定性.<br />3) 函数参数必须是指针的引用!<br /><img src ="http://www.cnitblog.com/tonywearme/aggbug/16258.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/tonywearme/" target="_blank">樱木</a> 2006-09-01 15:00 <a href="http://www.cnitblog.com/tonywearme/archive/2006/09/01/16258.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>请编写一个正确的二分搜索算法？！</title><link>http://www.cnitblog.com/tonywearme/archive/2006/08/30/16180.html</link><dc:creator>樱木</dc:creator><author>樱木</author><pubDate>Wed, 30 Aug 2006 15:24:00 GMT</pubDate><guid>http://www.cnitblog.com/tonywearme/archive/2006/08/30/16180.html</guid><wfw:comment>http://www.cnitblog.com/tonywearme/comments/16180.html</wfw:comment><comments>http://www.cnitblog.com/tonywearme/archive/2006/08/30/16180.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.cnitblog.com/tonywearme/comments/commentRss/16180.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/tonywearme/services/trackbacks/16180.html</trackback:ping><description><![CDATA[
		<p>本文是《数据结构与程序设计（c++语言描述）》中关于二分搜索的学习笔记，二分搜索的思想很简单，但是要实现一个正确最优的算法确不是那么容易。第一个二分搜索算法于1946年就出现，然而第一个完全正确的二分搜索算法直到1962年才出现，所以，认真点＋留神点～ ^_^<br /><br />首先，介绍一下二分搜索的思想：对于一个有序表而言，要查找某个关键字，首先比较表中央的那个节点，如果该节点的关键字与目标关键字相同，则找到。若比目标小，则目标关键字肯定出现在该点的右半段内。反之则出现在左半段。这样，每次比较后就能丢去一半的节点，对于一般的有序线性表而言，其搜索效率大大高于顺序查找。<br /><br /><font color="#ff0000">第一个版本的二分搜索：</font><br />根据算法，我们设置三个位置指示器：low, high, mid，分别指向当前搜索范围的最低位置、最高位置和中间位置，中间位置由mid = (low+high)/2决定。首先将mid处的关键字与key比较，相同则返回mid；若key较大，令low＝mid＋1继续搜索右半段；若key较小，令high＝mid－1继续搜索左半段。这样，搜索范围不断减小，high－low也越来越小。当high-low&lt;0时算法由于没有找到key而终止。<br /><br />///////////////////////////////////程序实现///////////////////////////////////////////<br />//第一个版本的递归实现（binary_search_v1_recursion）<br />template&lt;class Record, class Key&gt;<br />int binary_search( Record * r, const int &amp; low, const int &amp; high, const Key &amp; k )<br />{<br /> int mid = ( low + high ) / 2;<br /> if( low &lt;= high )<br /> {<br />  if( r[mid] == k )<br />   return mid;<br />  else if( r[mid] &lt; k )<br />   return binary_search( r, mid+1, high, k );<br />  else<br />   return binary_search( r, low, mid-1, k );<br /> }<br /> else<br />  return -1;<br />}<br /><br />//第一个版本的迭代实现（binary_search_v1_iteration）<br />template&lt;class Record, class Key&gt;<br />int binary_search( Record * r, const int &amp; size, const Key &amp; k )<br />{<br /> int low=0, high=size-1, mid;<br /> while( low &lt;= high )<br /> {<br />  mid = (low + high)/2;<br />  if( r[mid] == k )<br />   return mid;<br />  else if( r[mid] &lt; k )<br />   low = mid + 1;<br />  else<br />   high = mid -1;<br /> }<br /> return -1;<br />}<br /><br />在这个版本中，由于&gt;，&lt;符号的使用，搜索的范围始终控制在[low, high]之间，在0～low－1之间的一定小于关键字，在high＋1～size－1之间的一定大于关键字。当找到一个关键字时退出循环算法结束（成功）。考虑一下如果一张有序表中有多个重复关键字怎么办呢？一般的习惯是返回第一个的序号，然而此版本的算法只是返回随机的一个位置。这是一个问题。<br /><br />///////////////////////////////////复杂度分析///////////////////////////////////////////<br />接下来分析一下和目标关键字k的比较次数。在分析用关键字比较次数来作为算法时间复杂性度量的这一类查找算法时，比较树（comparison tree）是很有用的一种方法。在此介绍两种类型的比较树（当然是和算法有关），它们都符合2－树的定义。所谓2－树就是树中的节点要么是叶子节点要么有两个儿子节点（注意：2－树不是二叉树，前者的条件更强些）。二叉树相信大家已经非常熟悉了，所以这里直接拿出它的两个引理而不加证明：<br />1）第i层上最多有2^i个节点（0&lt;=i&lt;&lt;h=树深度）。<br />2）高度为h的一棵二叉树最多有（2^h）-1个节点，节点最多时为满二叉树。<br /><br />易验证这里的比较树都是2－树，而2－树是条件更强的二叉树，自然满足1）、2）。<br />此算法的的比较树如下（size＝10）：<br /><img src="D:\Documents and Settings\zj\桌面\searchTree1.jpg" /><br />在这棵查找树中，内部节点（i）表示一次查找成功，外部（叶子F）节点表示一次查找失败，形象一点的话可以说查找成功的情况分布在树冠（树的上部）查找失败的情况树的底层或次底层（可用数学归纳法证明，从略啦）。在证明过程中需要特别注意的是若是查找成功，成功节点的祖先（在比较路径上从root（第0层）到成功节点的父亲）各比较了两次！而成功节点自身则比较了一次。失败的话自然路径上的每个内部节点都比较了两次。另外注意内部节点有n个，外部节点有n＋1个（n表示有序表大小）。<br /><br />查找失败：<br />查找失败时的比较次数与树的深度密切相关。假设深度h，则F节点分布在h层或h和h－1层。由于h－1层上非F节点都会在h层产生两个F节点，因此：2^(h-1) &lt; n+1 &lt;= 2^h 。所以树深度h= <span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">┌</span></span>lg(n+1)<span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">┐（以2为底）。所以平均情况和最坏情况下查找失败的比较次数是2×<span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">┌</span></span><font size="3">lg(n+1)</font><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">┐。<br /></span></span><br />查找成功：<br />首先引入2－树的引理：对于任何一个2－树而言（二叉树就没有这个定理！），外部路径总长度E＝内部路径总长度I＋内部节点个数q×2。可以用数学归纳法证明，不是很难，从略。E<span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">≈(n+1)<span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">┌</span></span><font size="3">lg(n+1)</font><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">┐ <span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">∴I＝<span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">(n+1)<span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">┌</span></span><font size="3">lg(n+1)</font><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">┐－2n 。平均内部路径长度＋1表示平均比较的节点数，故:<br /></span></span></span></span></span>（I/n＋1）×2－1＝2（n＋1）<span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">┌</span></span><font face="宋体"><font size="3">lg(n+1)</font><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">┐/n－3就是成功查找的平均查找长度。其中×2表示每个节点都比较2次（除了最后一个节点），最后的减1也就是减去那个多算的1次比较。<br /></span></font>另外一种求法是从上到下计算每一层的比较次数然后相加，这是国内很多教材选用的方法，比较直接且麻烦，偶不喜欢～。<br />查找成功时的最坏情况当然是树的深度减1：<span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">┌</span></span><font face="宋体"><font size="3">lg(n+1)</font><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">┐－1 。<br /></span></font><br /><font color="#ff0000">第一个版本的二分搜索：<br /></font>前面一个版本二分搜索特点是当找到一个成功情况时就退出，这样对于具有多个相同关键字的有序表来说就不太适合了，最好返回这些相同关键字的第一个的位置而不是随便一个。由此引出了下面的改进算法：当mid处的关键字等于k时，算法并不马上结束，因为mid之前可能还有相同的关键字，也就是说第一个关键字的位置&lt;=mid，所以令high=mid。那么这个算法结束的条件是什么呢？这个是关键。当low或high中的任何一个先指向第一个k关键字时，在接下来的迭代过程中该指针将保持不变，另一个指针会不断向它靠近直到high＝low。也就是说high＝low是查找成功的必要条件，可见：（high＝low）&amp;&amp;（mid处关键字＝k）就是查找成功的充分条件。查找失败有两种可能：<br />1）（high＝low））&amp;&amp;（mid处关键字<span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'">≠</span>k）<br />2）low &gt; high<br /><br />///////////////////////////////////程序实现///////////////////////////////////////////<br />//第二个版本的递归实现（binary_search_v2_recursion）<br />template&lt;class Record, class Key&gt;<br />int binary_search( Record * r, const int &amp; low, const int &amp; high, const Key &amp; k )<br />{<br /> int mid = (low + high)/2;<br /> if( low &lt; high )<br /> {<br />  if( k &lt;= r[mid] )<br />   binary_search( r, low, mid, k );  <br />  else<br />   binary_search( r, mid+1, high, k );<br /> }<br /> else if( low == high )<br /> {<br />  if( k == r[mid] )<br />   return low;<br />  else<br />   return -1;<br /> }<br /> else<br />  return -1;<br />}<br /><br />//第二个版本的迭代实现（binary_search_v2_iteration）<br />template&lt;typename Record, typename Key&gt;<br />int binary_search( Record * r, const int &amp; size, const Key &amp; k )<br />{<br /> int low=0, high=size-1, mid;<br /> while( low &lt; high )<br /> {<br />  mid = (low + high) / 2;<br />  if( k &gt; r[mid] )<br />   low = mid + 1;<br />  else<br />   high = mid;<br /> }<br /> if( low &gt; high )<br />  return -1;<br /> else<br /> {<br />  if( k == r[low] )<br />   return low;<br />  else<br />   return -1;<br /> }<br />}<br /><br />///////////////////////////////////复杂度分析///////////////////////////////////////////<br />由于成功和失败的判断一定要走到最后一步才能确定，所以成功和失败的情况都是在2－树的叶子（外部）节点反之亦然。而这些外部节点总数为2n（n为有序表大小），所以比较树的深度h＝<span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">┌</span></span><font face="宋体"><font size="3">lg(2n）</font><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">┐＝<span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">┌</span></span><font size="3">lgn</font><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">┐＋1 。由于2－树叶子节点层数最多相差1，所以查找成功/查找失败时的平均比较次数和最坏比较次数均<span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">≈<span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA"><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">┌</span></span><font size="3">lgn</font><span style="FONT-SIZE: 10.5pt; FONT-FAMILY: 宋体; mso-bidi-font-size: 12.0pt; mso-bidi-font-family: 'Times New Roman'; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">┐＋1 。算法2的比较树如下（n＝10）：<br /></span></span></span></span></font><img src="D:\Documents and Settings\zj\桌面\searchTree2.JPG" /><br />由此得出结论，虽然算法1和算法2的时间复杂度是一个级别，但是当n较大时，算法2要比算法1快1倍！虽然从表面看算法1在某些情况下（树顶的那些成功节点）的确比算法2先结束，但是它只是对处于搜索树上部的一些为数不多的节点来说是早结束，而付出的代价却是每个节点比较2次。n较小的时候可能算法1稍微快点，但是此时的n非常小（比较对数曲线）以至于采用顺序查找会比二分算法1更好。由此我们得出结论如下：<br />对于较大的有序表，采用二分搜索算法2最快。<br />对于较小的有序表，采用顺序搜索就可以啦。<br /><br />当然了，实际上由于算法1的两次比较都是相同两个数之间的比较，所以对于某些优化的编译器来说，可能实际的比较时间比两次独立的比较所需的时间要少（比如重复访问的数据直接从cache而不是从ram中读。。。etc）。<br /><br />呵呵，写到这里终于结束啦。各位各位，如果将来面试的时候考官叫你写个二分搜索算法，你会写哪个呀？要想起我哦，呵呵～ <br /><br /></p>
<img src ="http://www.cnitblog.com/tonywearme/aggbug/16180.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/tonywearme/" target="_blank">樱木</a> 2006-08-30 23:24 <a href="http://www.cnitblog.com/tonywearme/archive/2006/08/30/16180.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>一个简单的顺序查找</title><link>http://www.cnitblog.com/tonywearme/archive/2006/08/30/16166.html</link><dc:creator>樱木</dc:creator><author>樱木</author><pubDate>Wed, 30 Aug 2006 08:03:00 GMT</pubDate><guid>http://www.cnitblog.com/tonywearme/archive/2006/08/30/16166.html</guid><wfw:comment>http://www.cnitblog.com/tonywearme/comments/16166.html</wfw:comment><comments>http://www.cnitblog.com/tonywearme/archive/2006/08/30/16166.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/tonywearme/comments/commentRss/16166.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/tonywearme/services/trackbacks/16166.html</trackback:ping><description><![CDATA[
		<p>实现一个最简单的顺序查找算法，如下所示：<br />template&lt;class Record, class Key&gt;<br />int sequential_search( Record *r, const int &amp; size, const Key &amp; k )<br />{<br /> for(int i=0; i&lt;size; i++)<br />  if( r[i] == k )<br />   return i;<br /> return -1;<br />}<br />有几点需要注意的：<br />1）如果在数组的最低或最高单元加上一个哨兵元素（sentinel），则可以在每次迭代中少一个判断（i&lt;size），因为总会遇到相同的节点。<br />2）在仅考虑搜索成功以及等概率搜索的前提下，顺序查找的的关键字比较次数为n*(n+1)/2。<br />3）常见搜索算法的时间复杂度是以关键字比较的次数来进行度量的。<br />4）与二分搜索不同，顺序搜索可以针对无序表进行。<br />5）顺序查找的搜索树是一颗单链树，有n＋1个叶子节点，其中n个成功节点，1个失败节点。<br /></p>
<img src ="http://www.cnitblog.com/tonywearme/aggbug/16166.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/tonywearme/" target="_blank">樱木</a> 2006-08-30 16:03 <a href="http://www.cnitblog.com/tonywearme/archive/2006/08/30/16166.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>通过优化数据结构改进n皇后问题</title><link>http://www.cnitblog.com/tonywearme/archive/2006/08/28/16088.html</link><dc:creator>樱木</dc:creator><author>樱木</author><pubDate>Mon, 28 Aug 2006 10:45:00 GMT</pubDate><guid>http://www.cnitblog.com/tonywearme/archive/2006/08/28/16088.html</guid><wfw:comment>http://www.cnitblog.com/tonywearme/comments/16088.html</wfw:comment><comments>http://www.cnitblog.com/tonywearme/archive/2006/08/28/16088.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/tonywearme/comments/commentRss/16088.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/tonywearme/services/trackbacks/16088.html</trackback:ping><description><![CDATA[
		<p>n皇后问题的时间代价除了递归和回溯需要的外，在每一步判断加入的皇后是否与当前棋盘格局冲突也需要不少时间，guarded函数需要扫描所在列以及两条对角线。然而我们可以通过改进数据结构来使后者的时间代价降低为O(1)。</p>
		<p>对于n皇后问题来说，每一行每一列每条对角线上最多有一个皇后，那么对于这样的每个元素（行、列或对角线）我们可以用一个bool值来表示是否已经有皇后在该元素上，这样判断是否冲突就变得十分简单，只需要读取一个bool值。另外需要记录棋盘的格局，考虑到一行只有一个皇后，可以建议一个一维数组保存每一行的皇后占用的列号。</p>
		<p>这样基本就OK了，只是对于行而言，我们采用了回溯法（剪枝函数）保证了每行只有一个皇后，这样行的bool数组就可以省略了。</p>
		<p>整个程序的实现如下：<br />//queens.h<br />static const int MAX_SIZE = 20;</p>
		<p>class queens<br />{<br />public:<br /> queens(const int &amp; size);<br /> void add(const int &amp; col);<br /> void remove(const int &amp; col);<br /> bool guarded(const int &amp; col);<br /> bool finished();<br /> void print();<br /> int size;<br />private:<br /> int count;<br /> int array[MAX_SIZE];<br /> bool downward[MAX_SIZE*2-1];<br /> bool upward[MAX_SIZE*2-1];<br /> bool cols[MAX_SIZE];<br />};</p>
		<p>//queens.cpp<br />#include "queens.h"<br />#include &lt;iostream&gt;<br />using namespace std;</p>
		<p>queens::queens(const int &amp; size)<br />{<br /> this-&gt;size = size;<br /> count = 0;<br /> for(int i=0; i&lt;MAX_SIZE; i++)<br />  cols[i] = false;<br /> for(i=0; i&lt;MAX_SIZE*2-1; i++)<br />  upward[i] = downward[i] = false;<br /> for(i=0; i&lt;MAX_SIZE; i++)<br />  array[i] = -1;<br />}</p>
		<p>void queens::add(const int &amp; col)<br />{<br /> array[count] = col;<br /> cols[col] = downward[size-1-(count-col)] = upward[count+col] = true;<br /> ++count;<br />}</p>
		<p>void queens::remove(const int &amp; col)<br />{<br /> array[--count] = -1;<br /> cols[col] = downward[size-1-(count-col)] = upward[count+col] = false; <br />}</p>
		<p>bool queens::guarded(const int &amp; col)<br />{<br /> if( cols[col] || downward[size-1-(count-col)] || upward[count+col] )<br />  return true;<br /> return false;<br />}</p>
		<p>bool queens::finished()<br />{<br /> return count==size ? true : false;<br />}</p>
		<p>void queens::print()<br />{<br /> static int num = 0;<br /> ++num;<br /> cout&lt;&lt;"solution "&lt;&lt;num&lt;&lt;endl;<br /> for(int i=0; i&lt;size; i++)<br /> {<br />  for(int j=0; j&lt;size; j++)<br />   if( j == array[i] )<br />    cout&lt;&lt;"1  ";<br />   else<br />    cout&lt;&lt;"0  ";<br />  cout&lt;&lt;endl;<br /> }<br /> cout&lt;&lt;endl&lt;&lt;endl&lt;&lt;endl;  <br />}</p>
		<p>//test.cpp<br />#include &lt;iostream&gt;<br />#include "queens.h"</p>
		<p>using namespace std;</p>
		<p>void chess_board( queens config )<br />{<br /> if( config.finished() )<br />  config.print();<br /> else<br />  for(int i=0; i&lt;config.size; i++)<br />   if( !config.guarded(i) )<br />   {<br />    config.add(i);<br />    chess_board(config);<br />    config.remove(i);<br />   }  <br />}</p>
		<p>int main()<br />{<br /> queens configuration(8);<br /> chess_board(configuration);<br />}<br /></p>
<img src ="http://www.cnitblog.com/tonywearme/aggbug/16088.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/tonywearme/" target="_blank">樱木</a> 2006-08-28 18:45 <a href="http://www.cnitblog.com/tonywearme/archive/2006/08/28/16088.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>n皇后问题实现</title><link>http://www.cnitblog.com/tonywearme/archive/2006/08/28/16087.html</link><dc:creator>樱木</dc:creator><author>樱木</author><pubDate>Mon, 28 Aug 2006 10:44:00 GMT</pubDate><guid>http://www.cnitblog.com/tonywearme/archive/2006/08/28/16087.html</guid><wfw:comment>http://www.cnitblog.com/tonywearme/comments/16087.html</wfw:comment><comments>http://www.cnitblog.com/tonywearme/archive/2006/08/28/16087.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/tonywearme/comments/commentRss/16087.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/tonywearme/services/trackbacks/16087.html</trackback:ping><description><![CDATA[
		<p>n皇后问题是递归和回溯法的一个典型应用。问题描述如下：对于一个n×n的棋盘，要求在其中放入互不攻击的n个皇后。皇后的攻击范围包括：<br />1) 同一行<br />2) 同一列<br />3) 同一对角线（包括两个方向）</p>
		<p>分析后可见，皇后之间互不攻击当且仅当满足下列条件：<br />1) 每一行只能有一个皇后<br />2) 每一列只能有一个皇后<br />3) 任意条对角线上面只能有一个皇后</p>
		<p>若按照从左到右从上到下的顺序手动摆放皇后，会发现算法遵循的是回溯法的思想。于是我们遵循top-down design的顺序来设计整个算法和程序。</p>
		<p>采用OOP的思想，先假设存在一个·表示棋盘格局的类queens，则定义回溯函数solve_from(queens configuration)，configuration表示当前棋盘格局，算法不断扩展棋盘的当前格局（找到下一个非冲突位置），当找到一个解决方案时打印该方案。该递归函数采用回溯法求出所有解。main函数调用solve_from时传递的实参是一个空棋盘。</p>
		<p>对于模拟棋盘的queens类，我们可以定义三个数据成员：<br />1) size：棋盘的边长，即大小<br />2) count：已放置的互不冲突的皇后数<br />3) array[][]：布尔矩阵，true表示当前格有皇后</p>
		<p>这里需要稍加思考以便稍后可以简化程序：因为每行只能放一个皇后，从上到下，从左到右放，那么count个皇后占用的行为0——count－1。所以 count还表示下一个皇后应该添加在哪一行。这样，add和remove操作的入口参数就只需要提供列号就行了，降低了耦合度 ：）</p>
		<p>为了模拟棋盘操作，应该至少提供以下方法：<br />1) 初始化一个size大小的空棋盘queens(int size)<br />2) 插入一个非冲突的皇后add( int col )<br />3) 删除最新插入的皇后remove( int col )<br />4) 判断要插入的皇后是否与已存在的棋盘格局相冲突bool guarded( int col )<br />5) 判断是否棋盘已满bool finished( )<br />6) 若棋盘已满则打印棋盘格局print()</p>
		<p>于是solve_from定义如下：<br />void solve_from( queens &amp;configuration )<br />{<br />      if( configuration.finished() )<br />            configuration.print();<br />      else<br />            for( int i=0; i&lt;size; i++ )<br />                  if( !configuration.guarded( i ) )<br />                  {<br />                        configuration.add( i );<br />                        solve_from(configuration);<br />                        configuration.remove( i );<br />                  }<br />}</p>
		<p>queens的方法实现如下：<br />static const int MAX_SIZE = 20<br />queens::queens( const int &amp; size )<br />{<br />      for( int i=0; i&lt;MAX_SIZE; i++ )<br />           for( int j=0; j&lt;MAX_SIZE; j++ )<br />                  array[ i ][ j ] = false;<br />      this-&gt;size = size;<br />      count = 0;       <br />}</p>
		<p>void queens::add( const int &amp; col )<br />{<br />      array[ count++ ][ col ] = true;<br />}</p>
		<p>void queens::remove( const int &amp; col )<br />{<br />      array[ --count ][ col ] = false;<br />}</p>
		<p>关于guarded还有一点说明：<br />由于回溯法中的剪枝函数configuration.remove保证了每行只有一个皇后，我们是需要判断列冲突与对角线冲突的情况。而且由递归的顺序是从上到下从左到右，所以我们也不需要判断下方的列和对角线，也就是说只要判断三个方向：<br />current——top<br />current——upper-left<br />current——upper-right<br />工作量就减少了一半以上。</p>
		<p>bool queens::guarded( const int &amp; col )<br />{<br />      bool gu = false;<br />      // current——top<br />      for( int i=0; !gu&amp;&amp;i&lt;count; i++ )<br />           gu = array[ i ][ col ];<br />      // current——up-left<br />      for( i=1; !gu&amp;&amp;(count-i&gt;=0)&amp;&amp;(col-i&gt;=0); i++ )<br />           gu = array[ count-i ][ col-i ];<br />      // current——up-right<br />      for( i=1; !gu&amp;&amp;(count-i&gt;=0)&amp;&amp;(col+i&lt;=size; i++ )<br />           gu = array[ count-i ][ col+i ];<br />      return gu;<br />}</p>
		<p>bool queens::finished()<br />{<br />      return count&lt;size ? false : true;<br />}</p>
		<p>bool queens::print()<br />{<br />      static int num = 0;<br />      ++num;<br />      cout&lt;&lt;"solution "&lt;&lt;num&lt;&lt;endl;<br />      for( int i=0; i&lt;size; i++ )<br />      {<br />            for( int j=0; j&lt;size; j++ )<br />                cout&lt;&lt;array[ i ][ j ]&lt;&lt;"   ";<br />           cout&lt;&lt;endl;<br />      }<br />      cout&lt;&lt;endl&lt;&lt;endl&lt;&lt;endl;               <br />}<br /></p>
<img src ="http://www.cnitblog.com/tonywearme/aggbug/16087.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/tonywearme/" target="_blank">樱木</a> 2006-08-28 18:44 <a href="http://www.cnitblog.com/tonywearme/archive/2006/08/28/16087.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>递归函数引用参数对递归工作栈大小的影响分析</title><link>http://www.cnitblog.com/tonywearme/archive/2006/08/28/16086.html</link><dc:creator>樱木</dc:creator><author>樱木</author><pubDate>Mon, 28 Aug 2006 10:41:00 GMT</pubDate><guid>http://www.cnitblog.com/tonywearme/archive/2006/08/28/16086.html</guid><wfw:comment>http://www.cnitblog.com/tonywearme/comments/16086.html</wfw:comment><comments>http://www.cnitblog.com/tonywearme/archive/2006/08/28/16086.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.cnitblog.com/tonywearme/comments/commentRss/16086.html</wfw:commentRss><trackback:ping>http://www.cnitblog.com/tonywearme/services/trackbacks/16086.html</trackback:ping><description><![CDATA[
		<p>本文从递归函数参数的角度来分析，递归函数调用时递归工作栈中工作记录减肥的可能性。</p>
		<p>用最简单的求阶层函数来举例：<br />n! = n*(n-1)*(n-2)***2*1</p>
		<p>常见的递归实现如下：<br />int fun(int n)<br />{<br />      if( n&lt;=1 ) return 1;<br />      return n*fun(n-1);<br />}<br />不用多说，这个实现下每次递归调用都会在当前层的工作记录中分配一个4字节（IBM兼容机）的空间来存放n的值。若栈的深度为DEPTH，那么一共需要DEPTH×4个字节存放参数。</p>
		<p>那么能不能避免这些存储空间呢，我们来看看引用的情况：<br />int fun(int &amp;n)<br />{<br />      if( n&lt;=1 ) return 1;<br />      return n*fun(n-1);<br />}<br />这段代码编译不能通过，原因是n-1只是一个右值。这里简单介绍一下c＋＋中的无名对象（变量）。c＋＋中的无名对象与const常量有些相似，他们都只能做右值而不能做左值，区别是const常量是有地址的可以取地址，而无名对象一般是不可以的。这里n－1有点无名对象的味道，因为fun要求一个变量引用。由于n－1是右值，所以把参数类型改成int fun(const int &amp; n)就可以了。</p>
		<p>int fun(const int &amp;n)<br />{<br />      if( n&lt;=1 ) return 1;<br />      return n*fun(n-1);<br />}<br />现在运行通过了，但是栈空间需求仍然没有减少，因为引用的是无名对象，无名对象只是寄存器中的一个值，是没有RAM地址的。所以每递归调用一次，就会在工作记录中分配空间来存放n－1，实际效果相当于const int n = n-1;</p>
		<p>那么换个思路。以上程序之所以如此是因为传给递归函数的不是n本身，现设计如下：<br />int fun(const int &amp; n)<br />{<br />      cout&lt;&lt;(long)&amp;n&lt;&lt;endl;<br />      if( n&lt;=1 ) return 1;<br />      --n;<br />      return (n+1)*fun(n);<br />}<br />这个程序运行正确而且所有的n确实是最外面的那个n的引用（打印的地址均一致）。有些人会怀疑这个程序不能正确的求出n!。想想也对，当递归不断深入到最里层的时候n＝1，递归回退的时候n仍然还是1，那结果不就成了1×2×2×2××××2了么？？您可以试试看，结果可能出乎您的预料：计算出的确实是 n! 原因如下：<br />每调用一层时先计算n+1，n＋1的结果保存在register中，进入下一层递归前编译器会把适当的register的值压栈以便日后恢复，这里某个寄存器就存放着运算的中间结果n+1。日后返回该层时虽然此时n确实是1，但是n＋1的结果已经计算过，所以只是从栈顶取出n+1而非重新计算。作为思考，您可以试试看将(n+1)*fun(n)改成fun(n)*(n+1)，它将得出错误的结果。</p>
		<p>看来，递归调用函数使用引用参数和普通使用引用参数的非递归调用函数是有区别的：并不是使用了引用参数后就一定能够节省栈的空间，这取决于具体的递归函数（复杂。。。^_^）</p>
		<p>当然，在递归函数中使用引用参数还有一种目的，当然是为了改变实参的数值罗。对于非内部类型的对象而言，使用引用往往可以既节省空间又同时可以修改实参的值，这里就不再举例了。<br /></p>
<img src ="http://www.cnitblog.com/tonywearme/aggbug/16086.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.cnitblog.com/tonywearme/" target="_blank">樱木</a> 2006-08-28 18:41 <a href="http://www.cnitblog.com/tonywearme/archive/2006/08/28/16086.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>