Sphinx(狮身人面像) 想必大家都比较了解,就不作介绍了,不了解的童鞋可以自己Google

原生的Sphinx只支持中文
所以这里重点介绍支持中文分词的 Coreseek。

注意:Coreseek 3.2 后,只有安装 Coreseek 就可以了,它对LibMMSeg和sphinx做了整合,不用再安装原生Sphinx。(3.2前是要安装原生Sphinx,还要装补丁,非常繁琐)

安装coreseek

下面以coreseek-3.2.14为例,它基于Sphinx 0.99(不用安装Sphinx 0.99)

详细官方手册:http://www.coreseek.cn/products-install/install_on_bsd_linux/

ubuntu-10.04 安装 coreseek安装需要预装的软件
sudo apt-get install make gcc g++ automake libtool mysql-client libmysqlclient15-dev   libxml2-dev libexpat1-dev


#下载 coreseek-3.2.14,里面已经包含 mmsegcd /tmpwget http://www.coreseek.cn/uploads/csft/3.2/coreseek-3.2.14.tar.gztar xzvf coreseek-3.2.14.tar.gzcd coreseek-3.2.14# 先安装mmsegcd mmseg-3.2.14./bootstrap    #输出的warning信息可以忽略,如果出现error则需要解决./configure --prefix=/opt/mmseg  [color=red]#下面安装coreseek 需要此路径[/color]sudo make && sudo make install#安装coreseekcd ..cd csft-3.2.14sh buildconf.sh    #输出的warning信息可以忽略,如果出现error则需要解决./configure --prefix=/opt/coreseek --without-unixodbc --with-mmseg --with-mmseg-includes=/opt/mmseg/include/mmseg/ --with-mmseg-libs=/opt/mmseg/lib/ --with-mysql    ##如果提示mysql问题,可以查看MySQL数据源安装说明sudo make && sudo make installcd ..#然后建立命令快捷方式,方便使用sudo ln -s /opt/coreseek/bin/indexer   /usr/local/bin/indexersudo ln -s /opt/coreseek/bin/indextool  /usr/local/bin/indextoolsudo ln -s /opt/coreseek/bin/search  /usr/local/bin/searchsudo ln -s /opt/coreseek/bin/searchd  /usr/local/bin/searchdsudo ln -s /opt/coreseek/bin/spelldump /usr/local/bin/spelldump


集成到rails项目

这里用的是gem thinking-sphinx

如果没有新项目自己创建Rails项目,这里不做说明。
可以参考Railscasts: http://railscasts.com/episodes/120-thinking-sphinx?autoplay=true

安装 thinking-sphinx
官方手册:http://freelancing-god.github.com/ts/en/installing_thinking_sphinx.html

这里只说明下 rails 3.0

在Gemfile中加入
gem 'thinking-sphinx'


然后
bundle install



可以参考快速实现:http://freelancing-god.github.com/ts/en/quickstart.html

在model中定义索引
class Post < ActiveRecord::Basedefine_index doindexes :title, :body#声明使用实时索引set_property :delta => true  #注意这句一定要加,否则添加了记录不会自动索引endend


记得添加实时索引字段 delta
ruby script/generate migration add_delta_to_posts delta:boolean


class AddDeltaToPosts < ActiveRecord::Migrationdef self.upadd_column :posts, :delta,:boolean, :default => true, :null => falseenddef self.downremove_column :posts, :deltaendend

rake db:migrate


在controller中加search代码,controler大家自己建啦。
class FullTextSearchController < ApplicationControllerdef searchper_page = params[:limit].to_ipage = (params[:start].to_i / per_page) + 1total_count = ThinkingSphinx.count(params[:query], :classes => [Post], :page => page, :per_page => per_page)@results = ThinkingSphinx.search(params[:query], :classes => [Post], :page => page, :per_page => per_page)respond_to do |format|format.json { render(:json => {:total => total_count, :success => true,:items => @results.map{ |i| {:id  => i.id, :title => i.title, :body => i.body} unless i.blank? },}.to_json)}endendend


#创建rails项目全文搜索数据目录cd 你的rails目录mkdir fulltext_search_data#从安装包中复制字典到Rails项目cp /tmp/coreseek-3.2.14/mmseg-3.2.14/data/*.* 你的rails目录/fulltext_search_data#新建配置文件:vi config/sphinx.yml#内容如下:test:bin_path: /opt/coreseek/binmem_limit: 128Mconfig_file: config/test.sphinx.confcharset_type: zh_cn.utf-8charset_dictpath: <%=::Rails.root.to_s + "/fulltext_search_data "%>pid_file: "/tmp/searchd.test.pid"ngram_len: 0development:bin_path: /opt/coreseek/binmem_limit: 128Mconfig_file: config/development.sphinx.confcharset_type: zh_cn.utf-8charset_dictpath: <%=::Rails.root.to_s + "/fulltext_search_data "%>pid_file: "/tmp/searchd.development.pid"ngram_len: 0production:bin_path: /opt/coreseek/binmem_limit: 128Mconfig_file: config/production.sphinx.confcharset_type: zh_cn.utf-8charset_dictpath: <%=::Rails.root.to_s + "/fulltext_search_data "%>pid_file: "/tmp/searchd.production.pid"ngram_len: 0#建立配置文件rake thinking_sphinx:configure#建立索引rake thinking_sphinx:index#开启服务rake thinking_sphinx:start#现在可以先加些数据,在浏览器中访问你的controller测试搜索#也可以用如下命令来测试全文搜索引擎search '关键字' -c 你的Rsils项目/config/development.sphinx.conf


最后再补几点注意事项
1.定义索引时 "set_property :delta => true", 没有这句新加的记录不会索引。
2.coreseek 安装包中的字典文件一定要复制到Rails项目数据目录,否则中文无法支持。


参考资料

详见:https://github.com/freelancing-god/thinking-sphinx
官方手册:http://freelancing-god.github.com/ts/en/installing_thinking_sphinx.html
快速实现:http://freelancing-god.github.com/ts/en/quickstart.html
posted @ 2011-10-17 08:41 佘亮 阅读(112) 评论(0) 编辑
Solr 是基于lucene的检索服务器。能够很快的搭建检索服务,并且提供的很多实用的组件。例如 高亮(highlight)、拼写检查(spellCheck)和匹配相位(moreLikeThis)。下面我将在我工作中接触到的一些实践与大家分 享。(我当前使用的solr 版本是 3.4,使用tomcat 7.0.21)

(如果你也使用的是 tomcat 服务器,而且查询请求包含中文的话,还需要 修改 TOMCAT_HOME/conf/server.xml 的 <Connector ... URIEncoding="UTF-8"/> 使用 UTF-8 编码,详见 URI_Charset_Confighttp)

高亮(highlight)
我们经常使用搜索引擎,比如在google 搜索 java ,会出现如下结果,结果中与关键字匹配的地方是红色显示与其他内容区别开来。




solr 默认已经配置了highlight 组件(详见 SOLR_HOME/conf/sorlconfig.xml)。通常我出只需要这样请求 http://localhost:8080/solr/select?q=name:王麻子&start=0&rows=10&hl=true&hl.fl=name ,可以看到与比一般的请求多了两个参数 "hl=true" 和 "hl.fl=name" 。"hl=true" 则是开启高亮,"hl.fl=name" 则告诉solr 对 name 字段进行高亮(如果你想对多个字段进行高亮,可以继续添加字段,字段间用逗号隔开,如 "hl.fl=name,name2,name3")。
查询结果如下:
Xml代码  收藏代码
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <response>  
  3.   <lst name="responseHeader">  
  4.     <int name="status">0</int>  
  5.     <int name="QTime">15</int>  
  6.     <lst name="params">  
  7.       <str name="hl">true</str>  
  8.       <str name="hl.fl">name</str>  
  9.       <str name="q">name:王麻子</str>  
  10.       <str name="start">0</str>  
  11.       <str name="rows">10</str>  
  12.     </lst>  
  13.   </lst>  
  14.   <!--此处是一般的返回结果-->  
  15.   <result name="response" numFound="1" start="0">  
  16.     <doc>  
  17.       <str name="id">4</str>  
  18.       <str name="name">王麻子勤劳朴实</str>  
  19.     </doc>  
  20.   </result>  
  21.   <!--此处是高亮的返回结果-->  
  22.   <lst name="highlighting">  
  23.     <!--id=4-->  
  24.     <lst name="4">   
  25.       <!--字段name 的高亮内容-->  
  26.       <arr name="name">   
  27.         <!--下面是经过xml转义,其实内容是 "<em>王麻子</em>勤劳朴实"-->  
  28.         <str>&lt;em&gt;王麻子&lt;/em&gt;勤劳朴实</str>  
  29.       </arr>  
  30.     </lst>  
  31.   </lst>  
  32. </response>  


高亮内容与关键匹配的地方,默认将会被 "<em>" 和 "</em>" 包围。如果用户想自定义高亮地方的前后标签,可以在请求中再加两个参数 "hl.simple.pre" 和 "hl.simple.post" 来分别指定前后标签,如 http://localhost:8080 /solr/select?q=name:王麻子&start=0&rows=10&hl=true& hl.fl=name&hl.simple.pre=<b>&hl.simple.post=</b>。或者修改 solrconfig.xml 配置文件中的 highligh searchComponent 来实现。
(highlight 更多请求参数可以参考HighlightingParameters)


拼写检查(spellCheck)

首页 配置 solrconfig.xml,文件可能已经有这两个元素(如果没有添加即可),需要根据我们自己的系统环境做些适当的修改。

Xml代码  收藏代码
  1. <searchComponent name="spellcheck" class="solr.SpellCheckComponent">  
  2.   <lst name="spellchecker">  
  3.     <str name="name">default</str>  
  4.     <!--这里指明需要根据哪个字段的索引为依据进行拼写检查。现配置 名为 name 的字段-->  
  5.     <str name="field">name</str>  
  6.     <!--拼写检查索引的目录-->  
  7.     <str name="spellcheckIndexDir">spellchecker</str>  
  8.     <!--当commit的时候,对拼写检查索引进行构建。(只有构建后,拼写检查才有效果)-->  
  9.     <!--当然,也可以选择在optimize的时候,进行构建。那么只需要将"buildOnCommint"换为 "buildOnOptimize"-->  
  10.     <str name="buildOnCommit">true</str>  
  11.   </lst>  
  12. </searchComponent>  
  13.   
  14. <requestHandler name="/spell" class="solr.SearchHandler" startup="lazy">  
  15.   <!--默认参数-->  
  16.   <lst name="defaults">  
  17.     <str name="spellcheck.onlyMorePopular">false</str>  
  18.     <str name="spellcheck.extendedResults">false</str>  
  19.     <!--配置拼写检查提示结果的个数(可以根据需要适当加大)-->  
  20.     <str name="spellcheck.count">1</str>  
  21.   </lst>  
  22.   <arr name="last-components">  
  23.     <str>spellcheck</str>  
  24.   </arr>  
  25. </requestHandler>  


配置完之后,需要重新建遍索引才能失效。然后我们这以请求 http://localhost:8080/solr/spell?q=name:王麻字&spellcheck=true
查询如果如下:
Xml代码  收藏代码
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2.   <response>  
  3.     <lst name="responseHeader">  
  4.       <int name="status">0</int>  
  5.       <int name="QTime">0</int>  
  6.     </lst>  
  7.     <result name="response" numFound="0" start="0"/>  
  8.     <lst name="spellcheck">  
  9.       <lst name="suggestions">  
  10.         <lst name="王麻字">  
  11.           <int name="numFound">1</int>  
  12.           <int name="startOffset">0</int>  
  13.           <int name="endOffset">3</int>  
  14.           <arr name="suggestion">  
  15.             <str>王麻子</str>  
  16.           </arr>  
  17.         </lst>  
  18.       </lst>  
  19.     </lst>  
  20.   </response>  


有时候我们需要以多个字段为依据进行拼写检查,但上面的配置只能设一个字段。为了达到同样的效果,我能只能另行其道了。需要用到 coptyField 技术。比如我们在 schema.xml 中定义了
Xml代码  收藏代码
  1. <field name="a" .../>   
  2. <field name="b" .../>  
想对 字段 a 和 b 同时为依据进行拼写检查,我们可能再加一个 field
Xml代码  收藏代码
  1. <field name="ab" multiValued="true" .../>  
  然后再加两个 copyField
Xml代码  收藏代码
  1. <copyField source="a" dest="ab" />   
  2. <copyField source="b" dest="ab" />  

完整的配置如下:
Xml代码  收藏代码
  1. <field name="a" .../>   
  2. <field name="b" .../>  
  3. <field name="ab" multiValued="true" .../>  
  4. <copyField source="a" dest="ab" />   
  5. <copyField source="b" dest="ab" />  
(更详细的内容可以参考 SpellCheckComponent)


匹配相似(moreLikeThis)
他的作用是查找相似的document。

首先在 solrconfig.xml 中配置 MoreLikeThisHandler
Xml代码  收藏代码
  1. <requestHandler name="/mlt" class="solr.MoreLikeThisHandler">  
  2. </requestHandler>  

然后我就可以请求 http://localhost:8080/solr/mlt?q=id:7&mlt.true&mlt.fl=name&mlt.mintf=1&mlt.mindf=1
上面请求的意思 查找 id 为 7 的 document ,然后返回与此 document 在 name 字段上相似的其他 document。需要注意的是 mlt.fl 中的 field 的 termVector=true 才有效果
Xml代码  收藏代码
  1. <field name="name" termVector="true" .../>  

当然 mlt.fl 也可以添加多个field ,用逗号隔开就行了
(详细说明可参考 MoreLikeThis MoreLikeThisHandler)
posted @ 2011-10-17 08:31 佘亮 阅读(244) 评论(0) 编辑

堆排序快速排序希尔排序一样都是时间复杂度为O(N*logN)的几种常见排序方法。学习堆排序前,先讲解下什么是数据结构中的二叉堆。

二叉堆的定义

二叉堆是完全二叉树或者是近似完全二叉树。

二叉堆满足二个特性:

1.父结点的键值总是大于或等于(小于或等于)任何一个子节点的键值。

2.每个结点的左子树和右子树都是一个二叉堆(都是最大堆或最小堆)。

当父结点的键值总是大于或等于任何一个子节点的键值时为最大堆。当父结点的键值总是小于或等于任何一个子节点的键值时为最小堆。下图展示一个最小堆:

由于其它几种堆(二项式堆,斐波纳契堆等)用的较少,一般将二叉堆就简称为堆。

 

堆的存储

一般都用数组来表示堆,i结点的父结点下标就为(i – 1) / 2。它的左右子结点下标分别为2 * i + 12 * i + 2。如第0个结点左右子结点下标分别为12

 

堆的操作——插入删除

下面先给出《数据结构C++语言描述》中最小堆的建立插入删除的图解,再给出本人的实现代码,最好是先看明白图后再去看代码。

堆的插入

每次插入都是将新数据放在数组最后。可以发现从这个新数据的父结点到根结点必然为一个有序的数列,现在的任务是将这个新数据插入到这个有序数据中——这就类似于直接插入排序中将一个数据并入到有序区间中,对照《白话经典算法系列之二 直接插入排序的三种实现》不难写出插入一个新数据时堆的调整代码:

// 新加入i结点 其父结点为(i - 1) / 2

void MinHeapFixup(int a[], int i)

{

    int j, temp;

      

       temp = a[i];

       j = (i - 1) / 2;      //父结点

       while (j >= 0)

       {

              if (a[j] <= temp)

                     break;

             

              a[i] = a[j];     //把较大的子结点往下移动,替换它的子结点

              i = j;

              j = (i - 1) / 2;

       }

       a[i] = temp;

}

更简短的表达为:

void MinHeapFixup(int a[], int i)

{

       for (int j = (i - 1) / 2; j >= 0 && a[i] > a[j]; i = j, j = (i - 1) / 2)

              Swap(a[i], a[j]);

}

插入时:

//在最小堆中加入新的数据nNum

void MinHeapAddNumber(int a[], int n, int nNum)

{

       a[n] = nNum;

       MinHeapFixup(a, n);

}

堆的删除

按定义,堆中每次都只能删除第0个数据。为了便于重建堆,实际的操作是将最后一个数据的值赋给根结点,然后再从根结点开始进行一次从上向下的调整。调整时先在左右儿子结点中找最小的,如果父结点比这个最小的子结点还小说明不需要调整了,反之将父结点和它交换后再考虑后面的结点。相当于从根结点将一个数据的“下沉”过程。下面给出代码:

// i节点开始调整,n为节点总数 0开始计算 i节点的子节点为 2*i+1, 2*i+2

void MinHeapFixdown(int a[], int i, int n)

{

    int j, temp;

 

       temp = a[i];

       j = 2 * i + 1;

       while (j < n)

       {

              if (j + 1 < n && a[j + 1] < a[j]) //在左右孩子中找最小的

                     j++;

 

              if (a[j] >= temp)

                     break;

 

              a[i] = a[j];     //把较小的子结点往上移动,替换它的父结点

              i = j;

              j = 2 * i + 1;

       }

       a[i] = temp;

}

//在最小堆中删除数

void MinHeapDeleteNumber(int a[], int n)

{

       Swap(a[0], a[n - 1]);

       MinHeapFixdown(a, 0, n - 1);

}

堆化数组

有了堆的插入和删除后,再考虑下如何对一个数据进行堆化操作。要一个一个的从数组中取出数据来建立堆吧,不用!先看一个数组,如下图:

很明显,对叶子结点来说,可以认为它已经是一个合法的堆了即2060 65 4 49都分别是一个合法的堆。只要从A[4]=50开始向下调整就可以了。然后再取A[3]=30A[2] = 17A[1] = 12A[0] = 9分别作一次向下调整操作就可以了。下图展示了这些步骤:

写出堆化数组的代码:

//建立最小堆

void MakeMinHeap(int a[], int n)

{

       for (int i = n / 2 - 1; i >= 0; i--)

              MinHeapFixdown(a, i, n);

}

至此,堆的操作就全部完成了(1),再来看下如何用堆这种数据结构来进行排序。

 

堆排序

首先可以看到堆建好之后堆中第0个数据是堆中最小的数据。取出这个数据再执行下堆的删除操作。这样堆中第0个数据又是堆中最小的数据,重复上述步骤直至堆中只有一个数据时就直接取出这个数据。

由于堆也是用数组模拟的,故堆化数组后,第一次将A[0]A[n - 1]交换,再对A[0…n-2]重新恢复堆。第二次将A[0]A[n – 2]交换,再对A[0…n - 3]重新恢复堆,重复这样的操作直到A[0]A[1]交换。由于每次都是将最小的数据并入到后面的有序区间,故操作完成后整个数组就有序了。有点类似于直接选择排序

void MinheapsortTodescendarray(int a[], int n)

{

       for (int i = n - 1; i >= 1; i--)

       {

              Swap(a[i], a[0]);

              MinHeapFixdown(a, 0, i);

       }

}

注意使用最小堆排序后是递减数组,要得到递增数组,可以使用最大堆。

由于建立堆的时间复杂度为O(logN),共N - 1次建堆操作,故堆排序的时间复杂度为O(N * logN)

 

 

 

1 作为一个数据结构,最好用类将其数据和方法封装起来,这样即便于操作,也便于理解。此外,除了堆排序要使用堆,另外还有很多场合可以使用堆来方便和高效的处理数据,以后会一一介绍。
posted @ 2011-08-22 16:34 佘亮 阅读(31) 评论(0) 编辑
Trie,又称字典树、单词查找树,是一种树形结构,用于保存大量的字符串。它的优点是:利用字符串的公共前缀来节约存储空间。
相对来说,Trie树是一种比较简单的数据结构.理解起来比较简单,正所谓简单的东西也得付出代价.故Trie树也有它的缺点,Trie树的内存消耗非常大.当然,或许用左儿子右兄弟的方法建树的话,可能会好点.

其基本性质可以归纳为:
1. 根节点不包含字符,除根节点外每一个节点都只包含一个字符。
2. 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串。
3. 每个节点的所有子节点包含的字符都不相同。

其基本操作有:查找 插入和删除,当然删除操作比较少见.我在这里只是实现了对整个树的删除操作,至于单个word的删除操作也很简单.

搜索字典项目的方法为:

(1) 从根结点开始一次搜索;

(2) 取得要查找关键词的第一个字母,并根据该字母选择对应的子树并转到该子树继续进行检索;

(3) 在相应的子树上,取得要查找关键词的第二个字母,并进一步选择对应的子树进行检索。
(4) 迭代过程……
(5) 在某个结点处,关键词的所有字母已被取出,则读取附在该结点上的信息,即完成查找。
其他操作类似处理.

 

/*
Name: Trie树的基本实现 
Author: MaiK 
Description: Trie树的基本实现 ,包括查找 插入和删除操作
*/

#include
<algorithm>
#include
<iostream>
using namespace std;

const int sonnum=26,base='a';
struct Trie
{
    
int num;//to remember how many word can reach here,that is to say,prefix
    bool terminal;//If terminal==true ,the current point has no following point
    struct Trie *son[sonnum];//the following point
}
;
Trie 
*NewTrie()// create a new node
{
    Trie 
*temp=new Trie;
    temp
->num=1;temp->terminal=false;
    
for(int i=0;i<sonnum;++i)temp->son[i]=NULL;
    
return temp;
}

void Insert(Trie *pnt,char *s,int len)// insert a new word to Trie tree
{
    Trie 
*temp=pnt;
    
for(int i=0;i<len;++i)
    
{
        
if(temp->son[s[i]-base]==NULL)temp->son[s[i]-base]=NewTrie();
        
else temp->son[s[i]-base]->num++;
        temp
=temp->son[s[i]-base];
    }

    temp
->terminal=true;
}

void Delete(Trie *pnt)// delete the whole tree
{
    
if(pnt!=NULL)
    
{
        
for(int i=0;i<sonnum;++i)if(pnt->son[i]!=NULL)Delete(pnt->son[i]);
        delete pnt; 
        pnt
=NULL;
    }

}

Trie
* Find(Trie *pnt,char *s,int len)//trie to find the current word
{
    Trie 
*temp=pnt;
    
for(int i=0;i<len;++i)
        
if(temp->son[s[i]-base]!=NULL)temp=temp->son[s[i]-base];
        
else return NULL;
    
return temp;
}

posted @ 2011-08-19 10:08 佘亮 阅读(52) 评论(0) 编辑
摘要: 快速排序由于排序效率在同为O(N*logN)的几种排序方法中效率较高,因此经常被采用,再加上快速排序思想----分治法也确实实用,因此很多软件公司的笔试面试,包括像腾讯,微软等知名IT公司都喜欢考这个,还有大大小的程序方面的考试如软考,考研中也常常出现快速排序的身影。 总的说来,要直接默写出快速排序还是有一定难度的,因为本人就自己的理解对快速排序作了下白话解释,希望对大家理解有帮助,达到快速排序,快速搞定。 快速排序是C.R.A.Hoare于1962年提出的一种划分交换排序。它采用了一种分治的策略,通常称其为分治法(Divide-and-ConquerMethod)。 该方法的基本思想是: 1阅读全文
posted @ 2011-08-15 11:35 佘亮 阅读(68) 评论(2) 编辑

归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。

首先考虑下如何将将二个有序数列合并。这个非常简单,只要从比较二个数列的第一个数,谁小就先取谁,取了后就在对应数列中删除这个数。然后再进行比较,如果有数列为空,那直接将另一个数列的数据依次取出即可。

//将有序数组a[]b[]合并到c[]

void MemeryArray(int a[], int n, int b[], int m, int c[])

{

       int i, j, k;

 

       i = j = k = 0;

       while (i < n && j < m)

       {

              if (a[i] < b[j])

                     c[k++] = a[i++];

              else

                     c[k++] = b[j++];

       }

 

       while (i < n)

              c[k++] = a[i++];

 

       while (j < m)

              c[k++] = b[j++];

}

可以看出合并有序数列的效率是比较高的,可以达到O(n)

解决了上面的合并有序数列问题,再来看归并排序,其的基本思路就是将数组分成二组AB,如果这二组组内的数据都是有序的,那么就可以很方便的将这二组数据进行排序。如何让这二组组内数据有序了?

可以将AB组各自再分成二组。依次类推,当分出来的小组只有一个数据时,可以认为这个小组组内已经达到了有序,然后再合并相邻的二个小组就可以了.

这样通过先递的分解数列,再合数列就完成了归并排序。

下面给出了代码。

bool MergeSort(int a[], int n)

{

       int *pTempArray = new int[n];

       if (p == NULL)

              return false;

       mergesort(a, 0, n - 1, pTempArray);

       return true;

}

void mergesort(int a[], int first, int last, int temp[])

{

       if (first < last)

       {

              int mid = (first + last) / 2;

              mergesort(a, first, mid, temp);    //左边有序

              mergesort(a, mid + 1, last, temp); //右边有序

              mergearray(a, first, mid, last, temp); //再将二个有序数列合并

       }

}

//将有二个有序数列a[first...mid]a[mid...last]合并。

void mergearray(int a[], int first, int mid, int last, int temp[])

{

       int i = first, j = mid + 1;

       int m = mid,   n = last;

       int k = 0;

      

       while (i <= m && j <= n)

       {

              if (a[i] < a[j])

                     temp[k++] = a[i++];

              else

                     temp[k++] = a[j++];

       }

      

       while (i <= m)

              temp[k++] = a[i++];

      

       while (j <= n)

              temp[k++] = a[j++];

      

       for (i = 0; i < k; i++)

              a[first + i] = temp[i];

}

归并排序的效率是比较高的,设数列长为N,将数列分开成小数列一共要logN步,每步都是一个合并有序数列的过程,时间复杂度可以记为O(N),故一共为O(N*logN)

因为归并排序每次都是在相邻的数据中进行操作,所以归并排序在O(N*logN)的几种排序方法(快速排序,归并排序,希尔排序,堆排序)也是效率比较高的。

 

在本人电脑上对冒泡排序直接插入排序,归并排序及直接使用系统的qsort()进行比较(均在Release版本下)

20000个随机数据进行测试:

50000个随机数据进行测试:

再对200000个随机数据进行测试:

 

注:有的书上是在mergearray()合并有序数列时分配临时数组,但是过多的new操作会非常费时。因此作了下小小的变化。只在MergeSort()new一个临时数组。后面的操作都共用这一个临时数组
posted @ 2011-08-11 13:45 佘亮 阅读(46) 评论(0) 编辑
摘要: Lucene支持对搜索条件的排序,一个条件或者多个条件,以及是升序还是降序,部分代码如下: stringINDEX_STORE_PATH=Server.MapPath("index");//INDEX_STORE_PATH为索引存储目录 stringkeyword=TextBox2.Text;//搜索内容 Hitsmyhit=null; IndexSearchermysea=newIndexSearcher(INDEX_STORE_PATH); QueryParserq=newQueryParser("indexcontent",newStandardA阅读全文
posted @ 2011-08-10 10:42 佘亮 阅读(68) 评论(0) 编辑
摘要: 直接选择排序和直接插入排序类似,都将数据分为有序区和无序区,所不同的是直接播放排序是将无序区的第一个元素直接插入到有序区以形成一个更大的有序区,而直接选择排序是从无序区选一个最小的元素直接放到有序区的最后。 设数组为a[0…n-1]。 1. 初始时,数组全为无序区为a[0..n-1]。令i=0 2. 在无序区a[i…n-1]中选取一个最小的元素,将其与a[i]交换。交换之后a[0…i]就形成了一个有序区。 3. i++并重复第二步直到i==n-1。排序完成。 直接选择排序无疑是最容易实现的,下面给出代码: void Selectsort(int a[], int n) { int i, j, 阅读全文
posted @ 2011-08-09 15:54 佘亮 阅读(20) 评论(0) 编辑
摘要: 冒泡排序是非常容易理解和实现,以从小到大排序举例: 设数组长度为N。 1.比较相邻的前后二个数据,如果前面数据大于后面的数据,就将二个数据交换。 2.这样对数组的第0个数据到N-1个数据进行一次遍历后,最大的一个数据就“沉”到数组第N-1个位置。 3.N=N-1,如果N不为0就重复前面二步,否则排序完成。 按照定义很容易写出代码: //冒泡排序1 void BubbleSort1(int a[], int n) { int i, j; for (i = 0; i < n; i++) for (j = 1; j < n - i; j++) if (a[j - 1] > a[j]阅读全文
posted @ 2011-08-09 15:42 佘亮 阅读(42) 评论(0) 编辑
摘要: 直接插入排序(Insertion Sort)的基本思想是:每次将一个待排序的记录,按其关键字大小插入到前面已经排好序的子序列中的适当位置,直到全部记录插入完成为止。 设数组为a[0…n-1]。 1. 初始时,a[0]自成1个有序区,无序区为a[1..n-1]。令i=1 2. 将a[i]并入当前的有序区a[0…i-1]中形成a[0…i]的有序区间。 3. i++并重复第二步直到i==n-1。排序完成。 下面给出严格按照定义书写的代码(由小到大排序): void Insertsort1(int a[], int n) { int i, j, k; for (i = 1; i < n; i++阅读全文
posted @ 2011-08-09 15:41 佘亮 阅读(21) 评论(0) 编辑