查找
遍历比对查找每一项,找到匹配的哪一个数据元素(记录)。
一、概念术语
查找表
把它理解为是一个名词,不要理解为动词哦,其实就是一个表,同一类型数据元素(或记录)的集合。
关键字
指的是数据元素中的某一个值,可以用它来唯一表示这个数据元素。(分为主关键字和次关键字)
查找
根据一个值,确定这个值在查找表中位置。
动态查找表与静态查找表
如果在查找表的时候,如果表中没有这个目标值,我们就插入关键字等于给定值,这样就称为是动态查找表,而否则就是静态查找表。
平均查找长度
ASL = Σ(Pi)(Ci) Pi为查找表中第i个记录的概率。Ci表示找到关键字的时候,和给定值比较的关键字的个数。(比较的次数)
二、线性表的查找
2.1 顺序查找
顺序查找适合与线性表的顺序存储结构以及链式存储结构
- 线性表顺序存储结构
#include <stdio.h>
#include <stdlib.h>
#define LIST_INT_SIZE 100 // 线性表的初始空间
#define LIST_INCREAMENT 10 // 增量
typedef int KeyType ; // 关键字
typedef int infoType ; // 其他信息
#define OK 1
#define ERROR 2
#define TRUE 1
#define FALSE 0
#define LIST_INCREAMENT 10 // 增量
#define OVERFLOW -1
typedef struct {
KeyType key; // 关键字
infoType otherInfo; // 其他信息
}ElementData;
typedef struct {
ElementData *el;
int length; // 当前数据的量
int listsize; // 当前空间的最大容量
}SStable;
// 初始化
int create(SStable &q) {
q.el = (ElementData *)malloc(sizeof(ElementData)*LIST_INT_SIZE);
if(!q.el) {
exit(OVERFLOW);
}
q.listsize = LIST_INT_SIZE;
q.length = 0;
return OK;
}
// 插入
int Listinsert(SStable &L, int i, KeyType e) // 插入
{
// 判断i值是否正确
if(i<1 || i>L.listsize+1) {
return ERROR;
}
// 如果空间已满
if(L.length == L.listsize) {
ElementData *newList = (ElementData*)realloc(L.el,(L.listsize+LIST_INCREAMENT)*sizeof(ElementData));
if(!newList) {
exit(OVERFLOW);
}
L.el = newList;
L.listsize += LIST_INCREAMENT;
}
// 插入
ElementData *q = &L.el[i-1];
q->key = e;
L.length++;
return OK;
}
void ListTraverse_Sq(SStable &L) // 遍历
{
for(int i = 0; i < L.length; i++)
{
printf("第%d个:\t%d\n",i+1,*(L.el + i));
}
}
// 执行查找
int search_List(SStable &L, KeyType e) {
int i ;
for(i = L.length; i>=1; i--){
if(L.el[i].key == e) {
return i;
}
}
return 0;
}
// 优化之后的算法
int search_List_New(SStable &L, KeyType e) {
L.el[0].key = e;
int i;
for(i = L.length; L.el[i].key != e; i--);
return i;
}
int main() {
SStable p;
create(p);
for(int i=1;i<=10;i++)
{
Listinsert(p,i, i+1);//插值
}
ListTraverse_Sq(p);
// 开始查找喽
int index = search_List_New(p, 4);
printf("位置索引为%d",index);
return 0;
}
#include <iostream>
#include <stdlib.h>
using namespace std;
#define LIST_INT_SIZE 100 // 线性表的初始空间
#define LIST_INCREAMENT 10 // 增量
#define OK 1
#define ERROR 2
#define TRUE 1
#define FALSE 0
#define LIST_INCREAMENT 10 // 增量
#define OVERFLOW -1
typedef int Status; // 返回结果
typedef int Bool;
typedef int ElemType; // 数据类型
typedef struct SQLIST{
ElemType *elm;
int length; // 长度
int listsize; // 当前分配的空间
}Sqlist;
/**
* 【0】初始化
* 参数: 传入一个结构体的地址
* 使用: Sqlist L; // 声明一个结构体 initList(L);
**/
Status initList(Sqlist &L)
{
// 创建一串空间,将首地址赋值给elm
L.elm = (ElemType *)malloc(sizeof(ElemType)*LIST_INT_SIZE);
if(!L.elm) {
exit(OVERFLOW);
}
L.length = 0;
L.listsize = LIST_INT_SIZE;
return OK;
}
/**
* 【1】销毁
* 参数: 传入一个结构体的地址
* 使用: Sqlist L; // 声明一个结构体 destroyList(L);
**/
Status destroyList(Sqlist &L) // 销毁
{
L.length = 0;
L.listsize = 0;
free(L.elm);
L.elm = NULL;
return OK;
}
/**
* 【2】插值
* 参数: (结构体的地址, 第i个位置插入, 插入的元素 e)
* 使用: Listinsert(L,i, i+1);
**/
Status Listinsert(Sqlist &L, int i, ElemType e) // 插入
{
// 判断i值是否正确
if(i<1 || i>L.length+1) {
return ERROR;
}
// 如果空间已满
if(L.length == L.listsize) {
ElemType *newList = (ElemType*)realloc(L.elm,(L.listsize+LIST_INCREAMENT)*sizeof(ElemType));
if(!newList) {
exit(OVERFLOW);
}
L.elm = newList;
L.listsize += LIST_INCREAMENT;
}
// 插入
ElemType *q = &L.elm[i-1];
*q = e;
L.length++;
return OK;
}
/**
* 【3】遍历
* 参数: 结构体地址
*
**/
void ListTraverse_Sq(Sqlist &L) // 遍历
{
cout<<"---------------遍历开始----------------------\n";
for(int i = 0; i < L.length; i++)
{
cout<<"第"<< i +1 <<"个:\t"<<*(L.elm + i)<<"\n";
}
cout<<"-----------------遍历结束--------------------\n";
}
/**
* 【4】取值
* 参数: (结构体的地址, 第i个位置, 取出的元素 e)
*/
void getEleList(Sqlist &L, int i, ElemType &e)
{
if(i<1 || i>L.length) {
return ;
}
e = L.elm[i-1];
}
/**
* 【5】删除
* 参数: (结构体的地址, 第i个位置, 删除的元素 e)
*
**/
Status ListDelete(Sqlist &L, int i, ElemType &e)
{
ElemType *q = &L.elm[i-1];// 取得要删除的元素的地址
e = *q;
q = q+1;
for(;q<=&L.elm[L.length-1]; ++q) {
*(q-1) = *q;
}
L.length-=1;
return OK;
}
// 查找某一项,返回其索引
Status search_List(Sqlist &L, ElemType e) {
int i;
for(i = L.length; i>=1; i--) {
if(L.elm[i] == e) {
return i;
}
}
return 0;
}
int main() {
Sqlist L;
initList(L);
//destroyList(L);
for(int i=1;i<=10;i++)
{
Listinsert(L,i, i+1);//插值
}
ListTraverse_Sq(L); // 遍历
Status index = search_List(L, 3);
cout<<index<<endl;
// int e1;
// getEleList(L, 1, e1);// 取第一个值
// cout<<e1<<endl;
// int e2;
// ListDelete(L, 2, e2);
// ListTraverse_Sq(L); // 遍历
}
在程序中初始化创建查找表时,由于是顺序存储,所以将所有的数据元素存储在数组中,但是把第一个位置留给了用户用于查找的关键字。例如,在顺序表{1,2,3,4,5,6}中查找数据元素值为 7 的元素,则添加后的顺序表为:
- 链式结构的顺序查找
// 查找某一给值的位置
int search(Link head, ElemType e) {
int count = 0;
Link p;
p = head->next;
if(p == NULL) {
cout<<"链表为空"<<endl;
return 0;
}
while(p!=NULL) {
count++;
if(p->data == e) {
return count;
}
p = p->next;
}
}
- 性能分析
Pi 为第 i 个数据元素被查找的概率,所有元素被查找的概率的和为 1;Ci 表示在查找到第 i 个数据元素之前已进行过比较的次数。若表中有 n 个数据元素,查找第一个元素时需要比较 n 次;查找最后一个元素时需要比较 1 次,所以有 Ci = n – i + 1。
优点: 算法简单、对结构无任何要求,即使用与顺序存储结构,也适用于链式存储结构。
缺点: 平均查找长度有点长,耗时、当n值比较大的时候,查找速度会降低。
2.2 折半查找(二分查找)
限制条件: 必须采用顺序存储结构,并且数据必须是有序排列,递增或者递减。
例如,在{5,21,13,19,37,75,56,64,88 ,80,92}
这个查找表使用折半查找算法查找数据之前,需要首先对该表中的数据按照所查的关键字进行排序:{5,13,19,21,37,56,64,75,80,88,92}
。
int search(SStable &L, KeyType key) {
int low = 1;
int high = L.length;
int middle;
while(low <= high) {
middle = (low + high) / 2;
if(L.el[middle-1].key == key) {
return middle;
} else if(L.el[middle-1].key < key) {
low = middle + 1;
} else {
high = middle -1;
}
}
return 0;
}
折半查找的运行过程可以用二叉树来描述,这棵树通常称为“判定树”。例如图 1 中的静态查找表中做折半查找的过程,
在判定树中可以看到,如果想在查找表中查找 21
的位置,只需要进行 3 次比较,依次和 56、19、21
进行比较,而比较的次数恰好是该关键字所在判定树中的层次(关键字 21
在判定树中的第 3 层)。
对于具有 n 个结点(查找表中含有 n 个关键字)的判定树,它的层次数至多为:log2n + 1
(如果结果不是整数,则做取整操作,例如: log211 +1 = 3 + 1 = 4
)。
同时,在查找表中各个关键字被查找概率相同的情况下,折半查找的平均查找长度为:ASL = log2(n+1) – 1。
性能分析
优点:
查找次数少、查找效率高。
缺点:
对结构要求高,查找前需要排序,限制因素多。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。