1 查找的基本概念
- 列表:由同一类型的数据元素构成的集合,可利用任意数据结构实现。
- 关键字:数据元素的某个数据项的值,用它可以标识列表中的一个或一组数据元素。
- 查找:根据给定的关键字值,在特定的列表中确定一个其关键字与给定值相同的数据元素,并返回该数据元素在列表中的位置。
- 平均查找长度:为确定数据元素在列表中的位置,需和给定值进行比较的关键字个数的期望值。对于长度为n的列表,查找成功的平均查找长度为:
A
S
L
=
P
1
C
1
+
P
2
C
2
+
…
+
P
n
C
n
ASL=P_1C_1+P_2C_2+{\dots}+P_nC_n
ASL=P1C1+P2C2+…+PnCn,其中
P
i
P_i
Pi是查找列表中第
i
i
i个元素的概率,
C
i
C_i
Ci为找到列表中第
i
i
i个数据元素时,已经进行过的关键字比较次数。
注:查找的基本方法可以分为两大类,即比较式查找法和计算式查找法。其中比较式查找法根据数据元素的组织结构可以分为基于线性表的查找法和基于树的查找法,而计算式查找法称为哈希查找法。
2 基于线性表的查找法
2.1 顺序查找法
原理:根据所给关键字与线性表中各元素的关键字逐个比较,直到成功或失败。
#include "stdio.h"
#include "stdlib.h"
const int MaxLength = 50; // 定义全局数组的最大长度
int arr[MaxLength]; // 定义全局数组
// 顺序查找法:为避免麻烦,直接将数组长度作为参数传入
int SeqSearch(int arr[], int key, int length) {
for (int i = 0; i < length; i++) {
if (arr[i] == key)
return i;
}
return -1;
}
int main() {
int n, key;
printf("请输入元素的个数n,其中n必须大于0,且小于或等于%d:\n", MaxLength);
scanf_s("%d", &n);
printf("请依次向数组中输入元素:\n");
for (int i = 0; i < n; i++) {
scanf_s("%d", &arr[i]);
}
printf("请输入要查找的值: ");
scanf_s("%d", &key);
int res = SeqSearch(arr, key, n);
if (res != -1)
printf("该值在数组中的下标为%d\n", res);
else
printf("该值不存在!\n");
system("pause");
return 0;
}
注:
1、上述代码并未设置监视哨。
2、时间复杂度为
O
(
n
)
O(n)
O(n)。
3、查找成功的平均复杂度为
A
S
L
s
u
c
c
=
1
n
∑
C
i
=
(
n
+
1
)
2
ASL_{succ}=\frac{1}{n}{\sum}C_i=\frac{(n+1)}{2}
ASLsucc=n1∑Ci=2(n+1)。
2.2 折半查找法
原理:将表中间位置记录的关键字与查找关键字比较,如果两者相等,则查找成功;否则利用中间位置记录将表分成前、后两个子表,如果中间位置记录的关键字大于查找关键字,则进一步查找前一子表,否则进一步查找后一子表。
#include "stdio.h"
#include "stdlib.h"
const int MaxLength = 100; // 定义全局数组的最大长度
int arr[MaxLength]; // 定义全局数组
// 折半查找法:为避免麻烦,直接将数组长度作为参数传入
int BinSearch(int arr[], int key, int n) {
int low = 0, high = n - 1;
while (low <= high) {
int mid = (low + high) / 2;
if (arr[mid] == key)
return mid;
else if (arr[mid] > key)
high = mid - 1;
else low = mid + 1;
}
return -1;
}
int main() {
int n, key;
printf("请输入元素的个数n,其中n必须大于0,且小于或等于%d:\n", MaxLength);
scanf_s("%d", &n);
printf("请依次向数组中输入元素,注意要从小到大排序:\n");
for (int i = 0; i < n; i++) {
scanf_s("%d", &arr[i]);
}
printf("请输入要查找的值: ");
scanf_s("%d", &key);
int res = BinSearch(arr, key, n);
if (res != -1)
printf("该值在数组中的下标为%d\n", res);
else
printf("该值不存在!\n");
system("pause");
return 0;
}
注:
1、必须采用顺序存储结构,必须按关键字大小有序排列。
2、折半查找过程可以用二叉判定树来描述,其查找成功的平均查找长度与判定树的深度有关。
2.3 分块查找法
原理:①首先将列表分为若干块(子表)。块的长度均匀,最后一块可以不满。每块中元素任意排列,即块内无序,但快与块之间有序。②构造一个索引表。其中每个索引项对应一个块病记录每块的起始位置,以及每块中的最大关键字(或最小关键字)。
3 基于树的查找法
3.1 二叉查找树
定义:二叉查找树或者是一棵空树,或者是具有如下性质的二叉树。①若它的左子树非空,则左子树上所有结点的值均小于根结点的值。②若它的右子树非空,则右子树上所有结点的值均大于根结点的值。③它的左右子树也分别是二叉查找树。
#include "stdio.h"
#include "stdlib.h"
const int ENDKEY = 666666;
typedef int KeyType;
typedef struct node {
KeyType key;
struct node *lchild, *rchild;
}BSTNode, *BSTree;
// 插入一个元素的时间复杂度为O(log2n)
void InsertBST(BSTree *bst, KeyType key) {
if (*bst == NULL) {
BSTree s = (BSTree)malloc(sizeof(BSTNode));
s -> key = key;
s->lchild = NULL;
s->rchild = NULL;
*bst = s;
}
else if (key < (*bst)->key) {
InsertBST(&((*bst)->lchild), key);
}
else if (key > (*bst)->key) {
InsertBST(&((*bst)->rchild), key);
}
}
// 插入n个元素的时间复杂度为O(nlog2n)
void CreateBST(BSTree *bst) {
*bst = NULL;
KeyType key;
scanf_s("%d", &key);
while (key != ENDKEY) {
InsertBST(&(*bst), key);
scanf_s("%d", &key);
}
}
// 平均查找长度O(log2n)
BSTree SearchBST(BSTree *bst, KeyType key) {
if (bst == NULL)
return NULL;
else if (key == (*bst)->key)
return *bst;
else if (key < (*bst)->key)
return SearchBST(&((*bst)->lchild), key);
else if(key > (*bst)->key)
return SearchBST(&((*bst)->rchild), key);
}
// 时间复杂度O(log2n)
BSTree DelBST(BSTree *bst, KeyType key) {
BSTNode *p, *f, *s, *q;
p = *bst;
f = NULL;
while (p) {
if (p->key == key)
break;
f = p;
if (key < p->key)
p = p->lchild;
else
p = p->rchild;
}
if (p == NULL)
return *bst;
if (p->lchild == NULL) {
if (f == NULL)
*bst = p->rchild;
else if (f->lchild == p)
f->lchild = p->rchild;
else
f->rchild = p->rchild;
free(p);
}
else {
q = p;
s = p->lchild;
while (s->rchild) {
q = s;
s = s->rchild;
}
if (q == p)
q->lchild = s->lchild;
else
q->rchild = s->lchild;
p->key = s->key;
free(s);
}
return *bst;
}
void InOrder(BSTree bst) {
if (bst != NULL) {
InOrder(bst->lchild);
printf("%d ", bst->key);
InOrder(bst->rchild);
}
}
int main() {
BSTree bst;
CreateBST(&bst);
printf("================\n");
InOrder(bst);
printf("\n================\n");
int key = 10;
BSTree q = SearchBST(&bst, key);
if (q != NULL)
printf("%d存在二叉排序树中!\n", q->key);
else
printf("值%d不存在!\n", key);
printf("================\n");
BSTree t = DelBST(&bst, 5);
printf("删除根结点5中序遍历如下:\n");
InOrder(t);
system("pause");
return 0;
}
4 计算式查找法–哈希法
哈希方法基本思想:首先在元素的关键字
k
k
k和元素的存储位置
p
p
p之间建立一个对应关系
H
H
H,使得
p
=
H
(
k
)
p=H(k)
p=H(k),
H
H
H称为哈希函数。
哈希法主要解决两方面的问题:如何构造哈希函数以及如何处理冲突。
4.1 哈希函数的构造方法
1、数字分析法
2、平方取中法
3、分段叠加法
4、除留余数法
5、伪随机数法
4.2 处理冲突的方法
1、开放定址法
2、再哈希法
3、链地址法
4、建立公共溢出区
4.3 手工计算等概率情况下查找成功的平均查找长度
A
S
L
s
u
c
c
=
1
表
中
置
入
元
素
个
数
n
∑
C
i
ASL_{succ}=\frac{1}{表中置入元素个数n}{\sum}C_i
ASLsucc=表中置入元素个数n1∑Ci
其中,
C
i
C_i
Ci为查找第
i
i
i个元素时所需的比较次数(即置入第
i
i
i个元素时所需的比较次数)。