数据结构与算法14—查找

查找

基本概念

查找就是在数据集中找出一个“特定元素”。

查找表是由同一类型的数据元素(或记录)构成的集合。

查找表是一种以集合为逻辑结构、以查找为核心的数据结构。

关键字

有时候我们需要指定某数据项的值来查找,这就用到了关键字。

关键字是数据元素中某个数据项的值,用以标识一个数据元素。

若此关键字可以识别唯一的一个记录,则称之谓“主关键字”;若此关键字能识别若干记录,则称之谓“次关键字”。

例:

对查找表经常进行的操作:

1)查询某个“特定的”数据元素是否在查找表中;

2)检索某个“特定的”数据元素的各种属性;

3)在查找表中插入一个数据元素;

4)从查找表中删去某个数据元素。

查找表可分为两类:

静态查找:仅作查询和检索操作。查找前后查找表未发生变化。

动态查找:在查询之后,将 “不在查找表中”的数据元素插入到查找表中;或者,从查找表中删除“在查找表中”的数据元素。 查找前后查找表发生了变化。

采用何种查找方法,取决于使用哪种数据结构来表示“查找表”。即表中记录是按何种方式组织的,根据不同的数据结构采用不同的查找方法。

平均查找长度:

查找算法中的基本运算是记录的关键字与给定值所进行的比较。其执行时间通常取决于关键字的比较次数,也称为平均查找长度ASL 。

ASL是衡量一个查找算法优劣的重要指标。

定义为:

n 是查找表中记录的个数

Pi 是查找第i个记录的概率

Ci 是找到第i个记录所需进行的比较次数。

静态查找

线性表查找属于静态查找,是将查找表视为一个线性表,将其顺序或链式存储,再进行查找,因此查找思想较为简单,效率不高。如果查找表中的数据元素有一定的规律(如按关键字有序),可以利用这些信息获得较好的查找效率。

顺序查找

即数据存储在顺序表中,然后逐项查找元素。

实现:

#define  MAXNUM  100    /*查找表的容量*/
typedef  int  KeyType;
typedef   struct{
    KeyType  key;     /*关键字字段*/
}DataType;
typedef struct{
    DataType  data[MAXNUM];   /*存储空间*/
    int n;  /*元素个数*/
}SeqList;
int Seq_Search_1 (SeqList list, KeyType kx)
{/*数据存放在list.data[1] 至list.data[n]中,在表list中查找关键字为kx的数据元素*/
/*若找到返回该元素在查找表中的位置,否则返回0*/
    int i=1;
    while(i<=list.n && list.data[i].key!= kx )
        i++;   /* 从表头端向后查找 */
    if (i>list.n)
        return 0;
    else
        return  i;
}

加监视哨后的顺序查找:

int Seq_Search_2(SeqList list, KeyType kx)
{ /*数据存放在list.data[1] 至list.data[n]中,在表list中查找关键字为kx的数据元素*/
/*若找到返回该元素在查找表中的位置,否则返回0 */
    int i;
    list.data[0].key=kx;
    i=list.n;
    while(list.data[i].key!= kx )
        i--;  /* 从表尾端向前查找 */
    return i;
}

比较次数减少了,效率提高。

顺序表上的顺序查找的性能分析

对于n个元素的查找表,若查找的是表中第i个记录时,需进行n-i+1次关键字比较,即ci=n-i+1

设查找每个元素的概率相等。 查找成功时,顺序查找的平均查找长度为:

查找不成功时,表中每个关键字都要比较一次,直到监视哨,因此关键字的比较次数总是n+1次,显然时间复杂度为O(n)。

顺序查找的特点

  • 顺序查找的优点是算法简单,对表中数据元素的存储方式、是否按关键字有序均无要求;
  • 缺点是平均查找长度较大,效率低,当n很大时,不宜采用顺序查找。

为了提高查找效率,查找表中的数据存放需依据查找概率越高,使其比较次数越少;查找概率越低,比较次数可相对较多的原则来存储数据元素。

有序表查找

  • 有序表是指查找表中的元素按关键字大小有序存储。
  • 如果查找表采用顺序结构存储且按关键字有序,那么查找时可采用效率较高的折半查找算法实现。

二分查找(折半查找)

折半查找的思想为:

在有序表中,取中间元素作为比较对象,若给定值与中间元素的关键字相等,则查找成功;若给定值小于中间元素的关键字,则在中间元素的左半区继续查找;若给定值大于中间元素的关键字,则在中间元素的右半区继续查找。不断重复上述查找过程,直到查找成功,或所查找的区域无数据元素,查找失败。

算法实现:

int Binary_Search(SeqList list, KeyType kx)
{ /*数据存放在list.data[1] 至list.data[n]中,在表list中查找关键字为kx的数据元素*/
/*若找到返回该元素在表中的位置,否则返回0  */
  int mid,low=1, high=list.n;   /*设置初始区间 */
  while(low<=high) {   /*当查找区间非空*/
      mid=(low+high)/2;  /*取区间中点 */
      if(kx==list.data[mid].key)
          return mid;  /* 查找成功,返回mid */
      else if (kx<list.data[mid].key)
          high=mid-1;   /* 调整到左半区 */
      else low=mid+1;     /* 调整到右半区 */
  }
  return  0;     /* 查找失败,返回0 */
}

折半查找的平均查找长度可用判定树来分析

先看一个具体的情况,假设:n=11

二分查找的效率(ASL)

1次比较就查找成功的元素有1个(20),即中间值;

2次比较就查找成功的元素有2个(21),即1/4处(或3/4)处;

3次比较就查找成功的元素有4个(22),即1/8处(或3/8)处…

4次比较就查找成功的元素有8个(23),即1/16处(或3/16)处…

……

则第h次比较时查找成功的元素会有(2h-1)个;

为方便起见,假设表中全部n个元素= 2h-1个,此时就不讨论第m次比较后还有剩余元素的情况了。

以含n个结点的满二叉树为例(n=2h-1),设查找概率相等,则二分查找的平均查找长度为:

最坏情况下,关键字比较次数为log2(n+1),期望时间复杂度为O(log2n)

折半查找的前提条件是需要有序表顺序存储,对于静态查找表,一次排序后不再发生变化,折半查找能得到不错的效率。但对于需要频繁执行插入或删除操作的数据集来说,维护有序的排序会带来不小的工作量,那就不建议使用。

顺序查找与二分查找的比较

顺序查找

优点:算法简单,对数据特性无要求;

缺点:ASL大,效率低。

二分查找

优点:查找速度快,效率高;

缺点:需在有序表上进行,且只限于顺序存储。

分块查找

基本思想:

(1)把表长为n的线性表分成m块,前m-1块记录个数为t=n/m,第m块的记录个数小于等于t;

(2)在每一块中,结点的存放不一定有序,但块与块之间必须是分块有序的;

(3)为实现分块检索,还需建立一个索引表。索引表的每个元素对应一个块,其中包括该块内最大关键字值和块中第一个记录位置的指针。

分块有序表的索引存储表示

索引顺序表=索引+顺序表

一般情况下,索引为有序表。 分块查找步骤:

1)由索引确定记录所在区间;

2)在顺序表的某个区间内进行顺序查找。

可见,索引顺序查找的过程也是一个“缩小区间”的查找过程。

分块查找性能分析

分块查找的ASL=查找“索引”的ASL+查找“顺序表”的ASL

设n个数据元素的查找表分为m个子表,且每个子表均为t个元素,即:m*t=n

设在索引表上的检索也采用顺序查找,这样,分块查找的平均查找长度为:

顺序表静态查找方法的比较

在上述3种查找方法中

二分查找具有最高的查找效率,但要求必须是顺序存储结构且元素有序排列,且若要进行插入、删除运算时,因需移动大量元素,运行效率将降低,所以二分查找只适用于有序表的静态查找;

顺序查找效率最低,但对线性表无任何要求——顺序存储或链式存储都可,是否有序都无影响;

分块查找是顺序查找和二分查找的综合。

原文地址:https://www.cnblogs.com/lisen10/p/10876221.html

时间: 2024-11-15 20:49:24

数据结构与算法14—查找的相关文章

数据结构和算法————二分查找

  二分查找 这些天深刻的体会到了巩固知识的重要性.对数据结构和算法的学习有一年的时间,然后搁置了一年,最后发现都忘记了. 不过还好不是失忆,看了之前做过的笔记,还是能回想起来的. 现在想在写一遍,算是对本子上的笔记做一个备份,更重要的是加深我的印象. 首先说一下二分查找的思想:假设数据是按升序排序的,对于给定值val,从序列的中间位置开始比较. 如果当前位置值等于val,则查找成功: 若val小于当前位置值,则在数列的前半段中查找 若val大于当前位置值,则在数列的后半段中继续查找. 重复以上

数据结构和算法之 查找

线性表查找算法 顺序查找 折半查找(线性表必须是已经排序好的) 1 /**折半查找 2 * @param obj 3 * @param value 4 */ 5 public static void halfSort(int[] obj,int value){ 6 int len=obj.length;//获取数组的长度 7 int min=0; 8 int max=len-2; 9 while (min<=max) { 10 int middle=(min+max)/2;//获取数组中间元素的

数据结构与算法(c++)——查找二叉树与中序遍历

查找树ADT--查找二叉树 定义:对于树中的每个节点X,它的左子树中的所有项的值小于X中的项,而它的右子树中所有项的值大于X中的项. 现在给出字段和方法定义(BinarySearchTree.h) #include <queue> class BinarySearchTree { private: struct Node { int value; Node* left; Node* right; }; Node* root; void insert(Node*, int); void trav

数据结构和算法14 之归并排序

本文为博主原创文章,转载请注明出处: http://blog.csdn.net/eson_15/article/details/51193139 归并算法的中心是归并两个已经有序的数组.归并两个有序数组A和B,就生成了第三个数组C,数组C包含数组A和B的所有数据项,并且使它们有序的排列在数组C中.首先我们来看看归并的过程,然后看它是如何在排序中使用的. 假设有两个有序数组,不要求有相同的大小.设数组A有4个数据项,数组B有6个数据项,它们要被归并到数组C中,开始时数组C有10个存储空间,归并过程

数据结构和算法--7查找算法

1.常用的查找算法 1) 顺序(线性)查找 2) 二分查找/折半查找 3) 插值查找 4) 斐波那契查找 2.查找 1)线性查找 A.题目: 有一个数列[1,43,22,-10,0],判断数列中是否包含此名称,如果找到了,就提示找到,并给出下标值. B,思路: 逐一查找 C.代码 package com.offcn.search; //线性查找 public class SeqSearch { public static void main(String[] args){ int[] arr =

研磨数据结构与算法-14红黑树

红黑树: public class RBTree { private final Node NIL = new Node(null,null,null,Color.BLACK,-1); private Node root; public RBTree() { root = NIL; } public RBTree(Node  root) { this.root = root; } //插入节点 public void rbInsert(Node node) { Node previous = N

[数据结构与算法] : 二分查找

1 #include <stdio.h> 2 3 #define NotFound -1; 4 typedef int ElementType; 5 6 int BinarySearch( const ElementType A[], ElementType X, int N ) 7 { 8 int Low, Mid, High; 9 10 Low = 0; High = N-1; 11 while( Low <= High ) // 注意终止条件 12 { 13 Mid = (Low

数据结构与算法-链表查找倒数第K个值

查找链表中倒数第k个结点题目:输入一个单向链表,输出该链表中倒数第k个结点.链表的倒数第0个结点为链表的尾指针.链表结点定义如下: struct ListNode { int m_nKey; ListNode* m_pNext; }; int FindCoundDownInList(pListNode head,int num) { pListNode p1,p2; p1=p2=head; while(num-->0 && p1!=NULL) p1=p1->m_pNext; i

java数据结构和算法------折半查找

1 package iYou.neugle.search; 2 3 public class Binary_search { 4 public static int BinarySearch(double[] array, double key) { 5 int left = 0; 6 int right = array.length - 1; 7 while (left <= right) { 8 int mid = (left + right) / 2; 9 if (array[mid] =