大话数据结构 - 查找算法总结

1. 顺序表查找(Sequential Search)

1> 算法思想:顺序表查找应该是查找算法中最简单的了。顺序表中所有的记录都是无序的,因此在查找时,没有对查找对象范围的可能线索,唯一的方法就是沿着一个方向一直比较,直到和查找对象相等。完成查找的过程。这里一个优化点是设置一个哨兵,放在顺序表的开始或者结束,每次在搜索顺序表的时候就不必考虑索引是否越界,减少了比较的过程,性能得到提高。

2> 时间复杂度:最好的情况是第一次比较就找到了,时间复杂度是O(1),最坏情况是到最后一个记录才找到,或者根本没找到,时间复杂度是O(n)。因此平均的查找次数是(n+1)/2,时间复杂度是O(n)

3> 优缺点:顺序表的优点就是简单,插入只需要在表的末尾增加一个记录,删除也只需用末尾的记录替换待删除的记录,后将末尾记录删除即可。缺点就是查找的效率比较低,尤其是当n很大时,因此顺序表对于一些小型数据的查找是有用的。

2. 有序表查找

顺序表中的记录之间没有大小关系,造成查找时效率低,如果我们的表是有序的呢?

2.1 折半查找(Binary Search)

1> 算法思想:对有序表来说,可以每次取表的中间进行比较,如果查找记录和中间的记录不同,就可以通过比较大小知道它落在有序表中的哪一半,这样就可以过滤掉另一半的记录。这样有序表的结果实际上就是一个二叉树,效率得到提高。

2> 时间复杂度:折半查找的总次数就是二叉树的深度,对于n个结点的二叉树深度为log2n+1,因此时间复杂度为O(logn)。

3> 优缺点:折半查到的时间复杂度是O(logn),好于顺序查找的O(n),但它的缺点就在于每次都是从表的中间开始,如果对于一个很大的表,查找表的开始或结束附近的记录就需要经过很多次查找。这个缺点在下面的插值查找中得到的解决。

2.2 插值查找(Interpolation Search)

1> 算法思想:插值查找和折半查找类似,可以通过比较表中的某项记录,确定查找记录的范围。唯一不同的是,对每次查找的中间点进行了变换,使之更加接近待查找的记录,减少了查找的次数。折半查找的中间点mid = low + (high - low) / 2,插值查找对1/2进行了修正,mid = low + (key - a[low])/(a[high] - a[low]) * (high - low)

2> 时间复杂度:和折半查找相同,插值查找的时间复杂度也是O(logn)

3> 优缺点:对于分布均匀的有序表来说,插值查找减少了查找次数,因为(key - a[low])/(a[high] - a[low])更加接近于查找记录在表中的位置。但对于分布不均匀的表来说,插值查找就未必是合适的选择。

2.3 斐波那契查找(Fibonacci Search)

1> 算法思想:依然是对查找点的优化,采用Fibonacci数组,找到对于当前长度的有序表的黄金分割点,作为每次的中间值。

2> 时间复杂度:时间复杂度和其他两种有序表查找相同,都是O(logn)

3> 优缺点:对于平均性能,斐波那契查找要优于折半查找,但如果是最坏情况,查找效率低于折半查找。

小结:有序表查找是一种针对查找优化的表结构,查找的时间复杂度是O(logn)。但有序表的插入和删除性能是比较差的,插入和删除不能破坏有序表的特性。

3. 线形索引查找

之前的查找都是基于记录所在的位置,如果我们的查找对象是key-value对呢?

3.1 稠密索引

1> 算法思想:稠密索引是关键码有序的,因此可以对索引使用折半、插值、斐波那契等有序表查找算法,大大提高了效率。

2> 时间复杂度:因为对于索引的查找使用的也是有序表的查找算法,时间复杂度是O(logn)

3> 优缺点:和有序表类似的是,稠密索引必须要维护索引的有序性。另外如果数据量很大,也要同时维护一个同样规模的索引,可能就需要反复访问磁盘,降低了查找性能。

3.2 分块索引

1> 算法思想:如果对索引进行一次分级呢?对于一级索引下,可能会有多个记录,称之为一个块,块内的记录再获得一个二级的索引。这些块有一个条件,就是块内无序,块间有序。块内无序就是在一级索引内部的记录可以是无序的,只要落在索引的范围内就可以;块间有序就是下一个块所有的关键字都要大于上一个块的最大关键字。因此对于一个块结构来讲,它具有最大关键码,块中的记录个数和指向块首数据的指针。

2> 时间复杂度:分块索引在查找时,先找到查找记录所在的块,在查找在块内的为孩子。设n个记录,平均分成m个块,每个块有t个记录,这样平均查找次数就是(m+1)/2 + (t+1)/2 = (n/t + t)/2 + 1 >= logn + 1。所以分块索引的时间复杂度介于O(n)和O(logn)之间。

3> 分块索引兼顾了有序和无序的需求,平衡了插入,删除和查找的性能,普遍用于数据库查找技术等。

3.3 倒排索引

1> 算法思想:倒排索引主要应用于搜索引擎。基本思想就是将得到的key-value关系进行一个反映射,得到某个value有多少个key指向它。比如查找某个单词出现在哪些文章中,可以先访问文章中的所有单词,建立一个单词的索引,将出现该单词的文章记录到索引中。这样在搜索时直接输入单词,就能得到文章列表。

2> 优缺点:倒排索引的优点是速度快,缺点就是记录不等长,维护比较困难,插入和删除都要做相应的处理。比如删除某个文章,就可能要对所有的单词都进行考察。

4. 二叉排序树

1> 算法思想:有序表的问题就是如果插入一个较小的记录,就要把比它大的记录依次移动,腾出插入的位置。如果用二叉树来实现呢,只需要让这个较小的记录成为某个结点的左孩子就可以了。为什么是左孩子呢,和二叉排序数的定义有关,简单来说,二叉排序树的中序遍历就是一个有序表。这样插入任何一个记录都不需要改变已经建好的树。

1) 查找:查找某个记录时,从根结点开始,如果查找记录大于该结点的值,就走右子树;如果小于该结点的值,就走左子树。不断向下查找,直到找到该记录,或者到叶子结点的值和查找记录不同,未找到该记录。

2) 插入:插入和查找类似,向下找到最接近它的结点,然后把该记录作为它的左孩子或者右孩子。

3) 删除:删除相对查找和插入来讲复杂一点,主要复杂在如果处理它的子树。删除分为几种情况,如果删除结点是叶子结点,直接把它和二叉排序树断开即可,不影响树上的其他结点。如果删除结点带左子树或者右子树(不同时),那就将它左子树或者右子树的根结点代替它,连接到树上。如果删除结点同时带有左子树和右子树呢?想要对原排序树的破坏最小,最好的办法是找到该结点的前驱或者后继结点,这可以很容易的找到。假设我们这里使用删除结点的前驱结点,要先将前驱结点的值赋给删除结点,如果前驱结点就是删除结点的左孩子(删除结点及其子孙只有左孩子,斜树),就将前驱结点的左子树接到删除结点的左子树上;如果前驱结点是某个结点的右孩子(删除结点及其子孙不只有左孩子),还要将它的左子树接到它父母的右子树上。

2> 时间复杂度:如果二叉排序树是平衡的,那么查找的时间复杂度是O(logn);如果是不平衡,比如最极端的斜树,那么时间复杂度是O(n)。

3> 优缺点:二叉排序树保留了有序表查找高效的特点,最理想的情况能达到O(logn)的时间复杂度,并且解决了插入和删除记录的问题,能够保证树的整体结构不受影响。缺点就是可能在插入的过程中,二叉排序树不能保持平衡,出现了某一边的树远远大于另一边,降低了查找的效率。后面提到的平衡二叉树解决了这个问题。

5. 平衡二叉树(AVL)

1> 算法思想:平衡二叉树的出现是为了解决二叉排序树可能出现的不平衡问题。平衡二叉树的概念是树中任何结点的平衡因子只能是-1,0,1,也就左子树和右子树的深度相差最多是1。为了实现这个目的,每次插入记录后,都会检查二叉树是否处在平衡状态,如果不是的话,会进行相应的旋转操作使之平衡。平衡的过程就是从新插入的结点向上查找,如果某个结点的BF=2,就顺时针旋转。最简单的旋转就是对于斜树,直接将BF=2结点的孩子作为新的子树根结点,将BF=2连接到它的右孩子。稍复杂的旋转就是BF=2和它的左孩子有相同的旋转方向,这样将它的左孩子作为新的子数根结点,BF=2连接在新根结点的右子树上,再将新的根结点原来的右孩子连接到BF=2的左子树上。最复杂的旋转就是BF=2和它的左孩子是相反的旋转方向,就要将它的左孩子先进行一次右旋转,再对BF=2作左旋转。同样,如果找到某个结点的BF=-2,做逆时针旋转,旋转的方法和顺时针旋转类似。

2> 时间复杂度:由于平衡二叉树的特性,它的时间复杂度一直是O(logn)。

3> 优缺点:平衡二叉树的优点就在于将不平衡消灭在最初的阶段,保持了很好的查找特性。缺点?比较复杂?

6. 2-3树,2-3-4树,B树

1> 算法思想:结点的孩子可以多于2个,可以是3个,4个。

// TODO: add B-tree later

7. 散列表查找

1> 算法思想:对key-value对进行储存。散列就是把关键字和存储位置之间建立一个确定的对应关系,f(key) = location. 因此散列表的查找就是通过散列函数计算出关键字存储记录所在的位置。存储就是通过散列函数计算出该记录的关键字代表的应该存储的位置。散列表的构造方法有很多种,包括直接定址法(f(key)= a * key +b),平方取中法(对关键字算平方后取出中间的几位),折叠法(把关键字分割几次后叠加求和),除留余数法(f(key) = key mode p (p <= m),通常p为小于或等于表厂的最小质数或者不包含小于20质因子的合数),随机数法( f(key) = random(key))。如果两个不同的key计算出相同的地址,那么就出现了冲突,如何处理这种冲突呢?开放定址法(寻找下一个散列地址,fi(key) = (f(key) + di) MOD m),再散列函数法(准备多个散列函数),链地址法(每个记录是个单链表),公共溢出区法(冲突放在溢出表)

时间: 2024-10-04 05:40:53

大话数据结构 - 查找算法总结的相关文章

大话数据结构——KMP算法(还存在问题)

http://www.ruanyifeng.com/blog/2013/05/Knuth%E2%80%93Morris%E2%80%93Pratt_algorithm.html /*#include<iostream> #include <string> using namespace std; int count_same_char(string T,int j); void get_next(string T,int *next) { int j=1; next[1]=0; f

Python 常用查找数据结构及算法

一.基本概念 二.无序表查找 三.有序表查找 3.1 二分查找(Binary Search) 3.2 插值查找 3.3 斐波那契查找 四.线性索引查找 4.1 稠密索引 4.2 分块索引 4.3 倒排索引 五.二叉排序树 六. 平衡二叉树 七.多路查找树(B树) 7.1 2-3树 7.2 2-3-4树 7.3 B树 7.4 B+树 八.散列表(哈希表) 8.1 散列函数的构造方法 8.2 处理散列冲突 8.3 散列表查找实现 8.4 散列表查找性能分析 参考书目<大话数据结构> 一.基本概念

《数据结构、算法与应用》8.(顺序查找数组中第一个出现指定元素的位置)

最近在读<数据结构.算法与应用>这本书,把书上的习题总结一下,用自己的方法来实现了这些题,可能在效率,编码等方面存在着很多的问题,也可能是错误的实现,如果大家在看这本书的时候有更优更好的方法来实现,还请大家多多留言交流多多指正,谢谢 8. 从左至右检查数组a[0:n-1]中的元素,以查找雨x相等的那些元素.如果找到一个元素与x相等,则函数返回x第一次出现所在的位置.如果在数组中没有找到这样的元素,函数则返回-1. // // main.cpp // Test_08 // // Created

我的软考之路(七)——数据结构与算法(5)之查找

上篇博文我重点介绍了八大内部排序,这篇博文(数据结构与算法的最后一课)重点介绍查找,我们依旧沿用上篇博文的风格,先简单介绍,再以例子重点讲解. 下面我们开始今天的旅行,首先祝你旅行愉快,呵呵. 静态查找 若查找目的是为了查询某个特定的数据是否在表中或检索某个特定数据的各种属性,则此类查找表为静态查找表. 1.顺序查找 基本原理:从表一端开始逐个和关键字进行比较,若找到一个记录和给定值相等,则查找成功,反之失败.再简单点就是,一个一个的比大小,看看是否相等. 例子: 顺序查找更适合于顺序存储结构和

数据结构和算法 &ndash; 2.基础查找算法

1.顺序查找 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace 数据结构和算法 { class _2 { public readonly static int[] arrayList = new int[] { 312, 564, 1254, 145, 212, 884, 145, 1212, 4

图的基础算法(大话数据结构笔记)

概述 线性表的每个元素有线性关系,每个数据元素只有一个直接前去和一个直接后继.树的数据元素之间有着明细那的层次关系,并且每层上的数据元素可能和下一层中多个元素相关,但只能和上一层中一个元素相关.这和一对父母可以有很多孩子,但每个孩子却只能有一对父母是一个道理.可现实中,人与人之间关系复杂,不是简单一对一,一对多的关系.这种复杂关系更适合用图来表示.在图结构中,节点之间的关系可以是任意的,图中任意两个数据元素之间都可能相关.如下图所示:无向边:Edge (vi,vj)有向边:也叫弧,Arc. <v

Java数据结构 遍历 排序 查找 算法实现

1. 遍历算法(遍历二叉树6种方法) 1.1. 概述 遍历算法针对二叉树而言的,主要有先序.中序.后序三种遍历顺序,三种顺序又分别有递归和常规算法,二叉树遍历的主要思想是:遍历左子树,遍历右子树,访问根节点,由这三者的遍历顺序来确定是先序.中序还是后序.下面只要求掌握递归遍历算法,常规遍历算法见附录一. 1.2. 先序遍历算法 遍历顺序:访问根节点,遍历左子树,遍历右子树.代码如下: void preOrder(BinaryTreeNode bt) { if (bt == null)// 如果当

【数据结构与算法】顺序查找

基本思想 顺序查找是最简单的查找方法,从线性表的一端开始,依次将每个记录的关键字与给定值进行比较. 代码实现 /** * 源码名称:SeqSearch.java * 日期:2014-08-13 * 程序功能:顺序查找 * 版权:[email protected] * 作者:A2BGeek */ public class SeqSearch { public static int seqSearch(int[] in, int key) { int length = in.length; int

python数据结构与算法 29-1 哈希查找

前面的章节中,我们利用数据集中元素的相对位置信息来提高查找算法的性能. 比方知道列表是有序的,能够使用二分查找.本节我们走得更远一些,创建一个数据结构,使得查找性能提高到O(1).称为哈希查找. 要做到这种性能,我们要知道元素的可能位置.假设每一个元素就在他应该在的位置上,那么要查找的时候仅仅须要一次比較得到有没有的答案,但以下将会看到.不是这么回事. 哈希表是这样一种数据集合,元素的保存的时候就存在easy找到位置上.哈希表表中每个位置,一般称为槽位,每个槽位都能保存一个数据元素并以一个整数命