Linux C 算法——查找

      所谓“查找”记为在一个含有众多的数据元素(或记录)的查找表中找出某个“特定的”数据,即在给定信息集上寻找特定信息元素的过程。

    为了便于讨论,必须给出这个“特定的”词的确切含义。首先,引入一个“关键字”的概念;

    关键字(Key) 是数据元素(或记录)中某个数据项的值,用它可以标识(识别)一个数据元素(或记录);

     查找(Serching)根据给定的某个值,在查找表中确定一个其关键字等于给定值的记录或数据元素。若表中存在这样的一个记录,则称查找成功,此时查找的结果为给出整个记录的信息,或只是该记录在查找表中的位置;若表中不存在关键字等于给定值的记录,则称查找不成功,此时查找的结果可给出一个“空”记录或“空”指针。

     查找方法有顺序查找、折半查找、分块查找、Hash表查找等。查找算法的优劣将影响到计算机的使用效率,应根据应用场合选择相应的查找算法。评价一个算法的好坏,一是时间复杂度(T(n)),n 为问题的体积,此时为表长;二是空间复杂度(D(n));三是算法的结构等其他特性;

     对查找算法,主要分析其T(n)。查找过程是key的比较过程,时间主要耗费在各记录的key与给定k值的比较上,比较次数越多,算法效率越差(即T(n)量级越高),故用“比较次数”刻画算法的T(n)。另外,不能以查找某个记录的时间来作为T(n),一般以“平均查找长度”来衡量 T(n)。

一、顺序表的查找

     所谓顺序表(Sequential Table),是将表中记录(R1 、R2。。。Rn)按其序号存储于一维数组空间,其特点是相邻记录的物理位置也是相邻的。

记录Ri的类型描述如下:

[cpp] view plaincopy
  1. typedef int key_t;  
  2.   
  3. typedef struct {  
  4.     key_t   key; //记录key值  
  5.     /* value ... */  
  6. } record_t;  
  7. <span style="font-family:Arial;BACKGROUND-COLOR: #ffffff"></span>  

顺序表类型描述如下:

[cpp] view plaincopy
  1. #define N 1024     /* max length of list = n */  
  2.   
  3. /* {R0, R1, R2, R3 ... Rn-1} are stored in from data[1] to data[n],data[0] is reserved*/  
  4. typedef struct {  
  5.     record_t    data[N + 1]; //顺序表空间;  
  6.     int len;    //当前表长,标空时len = 0;  
  7. } sqlist;  

若说明: sqlist r,则(r.data[1],.......r.data[r.len])为记录表(R1、R2 ...... Rn),Ri.key为r.data[i].key;

算法思路

   设给定值为k,在表(R1、R2。。。Rn)中,从Rn开始,查找key = k的记录。若存在一个记录Ri (1<i<n)的key为k,则查找成功,返回记录序号i;否则,查找失败,返回0;

算法描述:

第一种:

[cpp] view plaincopy
  1. int sqsearch_1(sqlist r, key_t k)  
  2. {  
  3.     int i;  
  4.       
  5.     i = r.len;  
  6.     while (i >= 1 && r.data[i].key != k)   
  7.         i--;  
  8.       
  9.     return i;  
  10. }  

我们可以看出其比较次数最大是2 * len.

第二种:

[cpp] view plaincopy
  1. int sqsearch_2(sqlist r, key_t k)  
  2. {  
  3.     int i;  
  4.       
  5.     r.data[0].key = k; //k存入监视哨  
  6.       
  7.     i = r.len;  
  8.     while (r.data[i].key != k)//顺序前往查找  
  9.         i--;  
  10.           
  11.     return i;  
  12. }  

这里比较次数最大为len.

第二种算法中引用了“监视哨”这个概念。它是放在data[0]位置的,这就我们前面讲a[0]置空的原因;监视哨在这里无需判断是否越界,这样比较次数就会少一半,这里现将k存入监视哨,若对某个i 有r.data[i].key = k,则查找成功,返回i ;若i 从n 递减到1 都无记录的key为k,i 再减1 为 0 时,必有r.data[0].key = k ,说明查找失败,返回i = 0;

 对于查找算法,ASL = O(n),则效率是很低的,意味着查找某记录几乎要扫描整个表,若表长len很大时,会令人无法忍受。

二、折半查找算法

 折半查找算法即二分法,二分查找算法的前提是表必须是有序的

算法思路:

对给定值k,组不确定待查记录所在区间,每次将搜索空间减少一半(折半),知道查找成功或失败为止。

设两个指针(或游标)low 、high ,分别指向当前待查找表的上界(表头)和下界(表尾)。对于表(R1、R2。。。Rn),初始时 low = 1、high = n,令:

mid = [ (low + high) / 2 ]

 指向当前待查找表中间的那个记录,下面举例说明折半查找的过程:

算法描述:

[cpp] view plaincopy
  1. int Binserch(sqlist r,key_t k)  
  2. {  
  3.     int low,high,mid;  
  4.     low = 1;  
  5.     high = r.len; //上下界初值  
  6.     while(low <= high) //表空间存在时  
  7.     {  
  8.         mid = (low + high)/2; //求当前的mid  
  9.         if(k == r.data[mid].key)  
  10.             return mid; //查找成功,返回mid  
  11.         if(k < r.data[mid].key)  
  12.             high = mid -1; //调整上界,向左部查找  
  13.         else  
  14.             low = mid + 1; //调整下界,向右部查找  
  15.     }  
  16.     return 0; //low > high 查找失败  
  17. }  

该例子中记录表的查找过程可以用二叉树来表示:

 

我们可以看到:找到第6个元素仅需比较一次,找到第3和第9个元素需比较2次;找到第1、4、7和10个元素许比较3次;找到第2、5、8、11个元素需比较4次;

在这个二叉树中。树中每个节点表示表中的一个记录,节点中的值为该记录在表中的位置,通常称这个描述查找过程的二叉树为判定书,从判定树上可见,查找20的过程恰好是走了一条从根到节点4的路径,和给定值进行比较的关键字个数为该路径上的节点数或节点4在判定树上的层次数。类似地,找到有序表中任意记录的过程就是走了一条从根节点到该记录相应的节点的路径,和给定值进行比较的关键字个数恰为该节点在判定树上的层次数。因此,折半查找法在查找成功时进行比较的关键字个数最多不超过树的深度,而具有n个节点的判定书的深度为[log 2 n] + 1,所以,折半查找法在查找成功时和给定值进行比较的关键字个数之多为[log2 n] + 1。n为无穷时,大大优于O(n);

 

三、HASH查找

       HASH表,又称散列表。在前面的讨论的顺序查找、折半查找中,其时间复杂度都在O(n)~O(log2 n)之间,不论ASL在哪个量级,都与记录长度n有关。随着n的扩大,算法的效率会越来越低。ASL 与n 有关是因为记录在存储器中的存放是随机的,或者说记录的key与记录的存放地址无关,因而查找只能建立在key的“比较”基础上。

       理想的查找方法是:对给定的k,不经任何比较便能获取所需的记录,其查找的时间复杂度为常数级O(C)。这就要求在建立记录表时,确定记录的key与其存储地址之间的关系f,即使key与记录的存放地址H相对应。

1、哈希查找(定义)

     哈希查找是给定键值key,通过公式f (key) 直接计算数据元素的存储地址(哈希值)进行数据存储(即建立哈希表)和进行数据查找的一种方法,该公式 f 称为哈希函数。哈希查找的本质是怎么构造的表就怎么在表中查找,具有O(1)的查找效率。记为 H(key) = f (key);

     不同的key 可能得到同一个Hash地址,即当 key1 != key2时,可能有 H(key1) = H(key2),此时称key1和key2为同义词。这种现象称为“冲突”“碰撞”,因为一个单位只可存放一条记录。一般,选取Hash函数只能做到使冲突尽可能少,却不能完全避免。这就要求在出现冲突之后,寻求恰当的方法来解决冲突记录的存放问题。

 

2、哈希查找(步骤)

建立哈希表操作步骤

1)step1取数据元素的关键字key,计算其哈希函数值(地址)。若该地址对应的存储空间还没有被占用,则该元素存入;否则执行step2解决冲突;

2)step2根据选择的冲突处理方法,计算关键字key的下一个存储地址。若下一个存储地址仍被占用,则继续执行step2,知道找到能用的存储地址为止。

哈希查找操作步骤:

1)step1对给定key值,计算哈希地址H(key);若该地址上记录不存在,则查找失败,若记录存在且key值匹配则查找成功;否则说明发生冲突,执行step2解决冲突。

2)step2 根据选择的冲突处理方法,重复计算下一个存储地址直到找到非空记录并且key值相等才认为查找成功并结束,否则查找失败。

 

3、哈希函数构造方法

这里主要介绍两种构造方法:1)直接地址法 2)保留除数法

4、解决冲突的方法

这里主要介绍两种解决冲突方法:1)开放地址法 2)链地址法

 5、下面介绍Hash查找的使用

1、性探查法解决冲突时Hash表的查找及插入

[cpp] view plaincopy
  1. #define m 64 //设定表长m  
  2. typedef struct  
  3. {  
  4.     keytype key; //记录关键字  
  5.     .....  
  6. }Hretype;  
  7.   
  8. Hretype HT[m]; //Hash表存储空间  
  9.   
  10. int Lhashserch(Hretype HT[m],keytype k) //线性探查法解决冲突时的查找  
  11. {  
  12.     int j,d,i = 0;  
  13.     j = d = H(k); //求Hash地址并赋给j和d  
  14.     while((i < m)&&(HT[j].key != NULL)&&(HT[j].key != k)  
  15.     {  
  16.         i ++;   
  17.         j = (d + i) % m; //冲突时形成下一地址  
  18.     }  
  19.     if(i == m)  
  20.         return -1; //表溢出时返回-1  
  21.     else  
  22.         return(j);  
  23. //HT[j.key == k,查找成功;H[j].key == NULL,查找失败  
  24.   
  25. void LHinsert(Hretype HT[m],Hretype R) //记录R插入Hash表的算法  
  26. {  
  27.     int j = LHashserch(HT.R.key); //查找R,确定其位置  
  28.     if((j == -1) || (HT[j].key == R.key)) //表溢出或记录已存  
  29.         ERROR();  
  30.     else  
  31.         HT[j] = R; //插入HT[j]单元  
  32. }  

2、链地址法解决冲突时Hash表的查找及插入

[cpp] view plaincopy
  1. typedef struct node //记录对应节点  
  2. {  
  3.     keytype key;  
  4.     .....  
  5.     struct node *next;  
  6. }Renode;  
  7.   
  8. Renode *LinkHserch(Renode *HT[m],keytype k) //链地址法解决冲突时的查找  
  9. {  
  10.     Renode *p;  
  11.     int d = H(k); //求Hash地址d  
  12.     p = HT[d]; //取链表头结点指针  
  13.     while(p && (p->key != k))  
  14.         p = p->next; //冲突时取下一同义词节点  
  15.       
  16.     return p;  
  17. //查找成功时p->key == k,否则p = ^   
  18.   
  19. void LHinsert(Renode HT[m],Renode *S) //将指针S所指记录插入表HT的算法  
  20. {  
  21.     int d;  
  22.     Renode *p;  
  23.     p = LinkHserch(HT,S->key); //查找S节点  
  24.     if(p)  
  25.         ERROR(); //记录已存在  
  26.     else  
  27.     {  
  28.         d =H(S->key);  
  29.         S->next = HT[d];  
  30.         HT[d] = S;  
  31.     }  
  32. }  

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/402392.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SharePoint项目中新建类库的错误处理及项目建设中遇到的问题总结

第一次SP项目总监遇到各种问题&#xff0c;以下是总结&#xff1a;问题1.创建SP项目的时候“场解决方案”跟“沙盒解决方案”是有区别的&#xff0c;具体可以看MSDN官方文档&#xff0c;这里简单摘抄如下&#xff1a;1&#xff09;场解决方案&#xff1a;承载与W3WP.exe中&…

ECharts学习(1)--简单图表的绘制

1.获取ECharts 官网 下载&#xff1a;http://echarts.baidu.com/download.html 2.在html页面中引入ECharts文件 <!DOCTYPE html> <html><head><meta charset"UTF-8"><title>ECharts练习</title><script type"text/javas…

Linux C 算法——排序

排序(Sort)是将无序的记录序列&#xff08;或称文件&#xff09;调整成有序的序列。 为了方便讨论&#xff0c;在此首先要对排序下一个确切的定义&#xff1a; 假设含有n个记录的序列为 { R1、R2、&#xff0c;。。。Rn } 其相应的关键字序列为 {K1、K2&#xff0c;。。。。Kn}…

JSON.parse 解析json字符串时,遇换行符报错

Json字符串转换成Json对象时候&#xff0c;有两种方式&#xff1a; 假设d是json字符串&#xff1a; 1&#xff0c;eval(( d ))。 2&#xff0c;JSON.parse(d)&#xff1b; 但是以上方式有隐患&#xff0c;如果Json字符串有换行的话&#xff0c;这样转换就会报错。 假如有…

文件I/O和标准I/O的区别

一、先来了解下什么是文件I/O和标准I/O&#xff1a; 文件I/O&#xff1a;文件I/O称之为不带缓存的IO&#xff08;unbuffered I/O)。不带缓存指的是每个read&#xff0c;write都调用内核中的一个系统调用。也就是一般所说的低级I/O——操作系统提供的基本IO服务&#xff0c;与os…

程序集、应用程序配置及App.config和YourSoft.exe.config .

转自&#xff1a;http://www.cnblogs.com/luminji/archive/2010/10/21/1857339.html 什么是程序集 程序集标识属性 强名称的程序集 强名称工作原理配置文件使用 DEVPATH 查找程序集指定要使用的运行库版本Appconfig和YourSoftexeconfig本章概要&#xff1a; 1&#xff1a;什么是…

[Android]在Dagger 2中使用RxJava来进行异步注入(翻译)

以下内容为原创&#xff0c;欢迎转载&#xff0c;转载请注明 来自天天博客&#xff1a;http://www.cnblogs.com/tiantianbyconan/p/6236646.html 在Dagger 2中使用RxJava来进行异步注入 原文&#xff1a;http://frogermcs.github.io/async-injection-in-dagger-2-with-rxjava 几…

关于Go语言在服务端做Restful接口和socket通信

请到我的个人博客看golang rest相关文章 http://xiaorui.cc关于Go语言在服务端做Restful接口和socket通信已经转到: http://xiaorui.cc/2014/10/25/%E5%85%B3%E4%BA%8Ego%E8%AF%AD%E8%A8%80%E5%9C%A8%E6%9C%8D%E5%8A%A1%E7%AB%AF%E5%81%9Arestful%E6%8E%A5%E5%8F%A3%E5%92%8C…

UVa 11136 - Hoax or what

题目大意&#xff1a;超市进行促销活动&#xff0c;顾客可以把账单放到一个箱子里&#xff0c;每天超市会从箱子中抽出最高消费者和最低消费者&#xff0c;最高消费者可以得到&#xff08;最高消费-最低消费&#xff09;的金钱。询问超市在n天的促销活动结束后应支付多少钱。 找…

Winfrom实现圆角设计

主要代码 public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void Form1_Paint(object sender, PaintEventArgs e) { Type(this, 25, 0.1); } private void…

Linux 系统应用编程——进程基础

一、Linux下多任务机制的介绍 Linux有一特性是多任务&#xff0c;多任务处理是指用户可以在同一时间内运行多个应用程序&#xff0c;每个正在执行的应用程序被称为一个任务。 多任务操作系统使用某种调度(shedule)策略&#xff08;由内核来执行&#xff09;支持多个任务并发执行…

【Python文件处理】递归批处理文件夹子目录内所有txt数据

因为有个需求&#xff0c;需要处理文件夹内所有txt文件&#xff0c;将txt里面的数据筛选&#xff0c;重新存储。 虽然手工可以做&#xff0c;但想到了python一直主张的是自动化测试&#xff0c;就想试着写一个自动化处理数据的程序。 一.分析数据格式 需要处理的数据是txt格式存…

Windows Azure 之服务总线中继服务

Windows Azure的服务总线允许在Web服务内部与外部之间做成一个公共的连接点&#xff0c;在无需更改企业防火墙或者其他安全配置的情况下连接内部和外部的服务 而使用Azure云服务的时候由于缩放的原因通过IP来制定连接也是不科学的&#xff0c;而中继服务则可以充当很好的公共连…

【qt】QT 的信号与槽机制

QT 是一个跨平台的 C GUI 应用构架&#xff0c;它提供了丰富的窗口部件集&#xff0c;具有面向对象、易于扩展、真正的组件编程等特点&#xff0c;更为引人注目的是目前 Linux 上最为流行的 KDE 桌面环境就是建立在 QT 库的基础之上。 QT 支持下列平台&#xff1a;MS/WINDOWS-9…

Linux 系统应用编程——进程间通信(上)

现在再Linux应用较多的进程间通信方式主要有以下几种&#xff1a; 1&#xff09;无名管道&#xff08;pipe&#xff09;及有名管道&#xff08;fifo&#xff09;&#xff1a;无名管道可用于具有亲缘关系进程间的通信&#xff1b;有名管道除具有管道相似的功能外&#xff0c;它还…

通过JDBK操作数据库

一、配置程序——让我们程序能找到数据库的驱动jar包1.把.jar文件复制到项目中去,整合的时候方便。2.在eclipse项目右击“构建路径”--“配置构建路径”--“库”--“添加外部jar”--找到数据库的驱动jar包--点击确定。会在左侧包资源管理器中出现“引用的库”&#xff0c;在里面…

Linux 系统应用编程——网络编程(常用命令解析)

1、telnet Telnet协议是TCP/IP协议族中的一员&#xff0c;是Internet远程登陆服务的标准协议和主要方式。它为用户提供了在本地计算机上完成远程主机工作的能力。在终端使用者的电脑上使用telnet程序&#xff0c;用它连接到服务器。终端使用者可以在telnet程序中输入命令&#…

灾难 BZOJ 2815

灾难 【样例输入】 5 0 1 0 1 0 2 3 0 2 0 【样例输出】 4 1 0 0 0 题解&#xff1a; 先跑出拓扑序 我们按拓扑序建立一棵“灭绝树” 灭绝树含义是当一个点灭绝时&#xff0c;它的子树将会全部灭绝 所以答案就是点在灭绝树中的子树大小 一个点如果灭绝&#xff0c;那么需要所有…

centos关于”running yum-complete-transaction first...

2019独角兽企业重金招聘Python工程师标准>>> 今天在用yum安装软件出错几次户&#xff0c;总是有提示信息&#xff1a; There are unfinished transactions remaining. You might consider running yum-complete-transaction first to finish them. The program yum…