查找与排序算法
一、实验目的
1.掌握顺序查找、二分查找和冒泡排序的必要特点;
2.重点掌握顺序查找、二分查找和冒泡排序的过程 。
3.熟悉哈希查找的方法,能选择合适散列函数,实现不同冲突处理方法的散列表的查找、建立。
4.了解各种常用的查找算法和排序算法。
二、实验内容
基础性任务:
(1)给出在一个无序表中采用顺序查找算法查找值为x的元素的算法.
(2)给出在一个将无序表通过冒泡排序变为递增有序表的算法。
(3)给出在一个递增有序表中采用二分查找算法查找算法查找值为x的元素的算法。
即:编写程序实现顺序查找、冒泡排序和二分查找,其中,二分查找和冒泡排序的程序流程图如图1所示。
程序流程:main() 输入数据 seqsearch() bubblesort () binsearch ()
提高性任务(从下述任务中任选一个完成):
(1) 实现二叉排序树查找(整数);
随机产生一组关键字,利用二叉排序树的插入算法建立二叉排序树,然后删除某一指定关键字元素。
(2)实现哈希查找(一组整数),处理冲突的方法采用线性探测再散列法。
有一组关键字{19,01,23,14,55,20,84,27,68,11,10,77},采用哈希函数:H(key)=key % 13 ,采用开放地址法的线性探测方法解决冲突,试在 0~18 的散列地址空间中对该关键字序列构造哈希表。
三、实验要求
(1) 给出程序设计的基本思想、原理描述和算法实现。
(2) 对源程序给出注释。
四、实现提示
(1)查找表需要在运行时输入,查找关键字也是在运行时指定。
(2)顺序查找可采用顺序存储结构和链式存储结构,如果采用顺序存储结构,可以定义一个数组存放关键字。
(3)注意理解折半查找的适用条件(链表能否实现折半查找?冒泡排序呢?)。
五、实验步骤提示
基础性任务:
(1)理解相关查找算法和排序算法对存储结构及是否有序的要求及其算法思想;
(2)仔细分析实验内容,给出所用查找方法和冒泡排序方法的算法思想和算法流程图;
(3)用C或C++语言实现所用查找算法(已给出部分源代码,请将程序补充完整);
(4)给出测试数据,并分析其结果;在实验报告册上写出实验过程。
提高性任务(二选一):
(1)用C或C++语言实现二叉排序树查找算法;
(2)用C或C++语言实现采用线性探测再散列法的哈希查找算法。

六、源程序
6.1基础任务
c
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#define DATA_SIZE 10
// 函数声明
void seqsearch(int*, int); // 顺序查找
void binsearch(int*, int); // 二分查找
void bubblesort(int*, int); // 冒泡排序
void menu(); // 显示菜单
void printArray(int*, int); // 打印数组
int main() {
int i, j = 1;
int ch;
int data[DATA_SIZE];
printf("请输入10个整型数据: ");
for (i = 0; i < DATA_SIZE; i++) {
scanf("%d", &data[i]);
}
menu(); // 显示菜单
while (j) {
printf("\n请选择操作: ");
scanf("%d", &ch);
switch (ch) {
case 1:
seqsearch(data, DATA_SIZE);
break;
case 2:
printf("执行二分查找前需要对数组排序...\n");
bubblesort(data, DATA_SIZE);
printf("排序后的数组: ");
printArray(data, DATA_SIZE);
binsearch(data, DATA_SIZE);
break;
case 3:
bubblesort(data, DATA_SIZE);
printf("排序后的数组: ");
printArray(data, DATA_SIZE);
break;
case 4:
j = 0;
break;
default:
printf("无效的选择!\n");
break;
}
if (j) {
printf("\n当前数组状态: ");
printArray(data, DATA_SIZE);
menu();
}
}
return 0;
}
// 显示菜单
void menu() {
printf("\n==== 菜单 ====\n");
printf("1. 顺序查找\n");
printf("2. 二分查找\n");
printf("3. 冒泡排序\n");
printf("4. 退出\n");
}
// 打印数组
void printArray(int* arr, int n) {
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
// 顺序查找
void seqsearch(int* arr, int n) {
int x, i;
printf("请输入要查找的值: ");
scanf("%d", &x);
for (i = 0; i < n; i++) {
if (arr[i] == x) {
printf("找到元素 %d 在数组的第 %d 个位置(下标为%d)\n", x, i + 1, i);
return;
}
}
printf("未找到元素 %d\n", x);
}
// 冒泡排序
void bubblesort(int* arr, int n) {
int i, j, temp;
for (i = 0; i < n - 1; i++) {
for (j = 0; j < n - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
// 交换元素
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
// 二分查找
void binsearch(int* arr, int n) {
int x, left = 0, right = n - 1, mid;
printf("请输入要查找的值: ");
scanf("%d", &x);
while (left <= right) {
mid = left + (right - left) / 2;
if (arr[mid] == x) {
printf("找到元素 %d 在数组的第 %d 个位置(下标为%d)\n", x, mid + 1, mid);
return;
}
else if (arr[mid] < x) {
left = mid + 1;
}
else {
right = mid - 1;
}
}
printf("未找到元素 %d\n", x);
}
6.2提高任务
(1)随机产生一组关键字,利用二叉排序树的插入算法建立二叉排序树,然后删除某一指定关键字元素。
(2)有一组关键字{19,01,23,14,55,20,84,27,68,11,10,77},采用哈希函数:H(key)=key % 13 ,采用开放地址法的线性探测方法解决冲突,试在 0~18 的散列地址空间中对该关键字序列构造哈希表。
c
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#define HASH_SIZE 19 // 0-18的散列空间
// 哈希表初始化
void initHashTable(int* hashTable) {
for(int i = 0; i < HASH_SIZE; i++) {
hashTable[i] = -1; // -1表示空位置
}
}
// 哈希函数
int hashFunction(int key) {
return key % 13;
}
// 插入元素到哈希表
void insertHash(int* hashTable, int key) {
int index = hashFunction(key);
int originalIndex = index;
while(hashTable[index] != -1) { // 线性探测解决冲突
index = (index + 1) % HASH_SIZE;
if(index == originalIndex) { // 表满
printf("哈希表已满,无法插入 %d\n", key);
return;
}
}
hashTable[index] = key;
printf("插入 %d 到位置 %d\n", key, index);
}
// 在哈希表中查找元素
void searchHash(int* hashTable, int key) {
int index = hashFunction(key);
int originalIndex = index;
while(hashTable[index] != key) {
if(hashTable[index] == -1) { // 遇到空位置,说明不存在
printf("未找到 %d\n", key);
return;
}
index = (index + 1) % HASH_SIZE;
if(index == originalIndex) { // 绕了一圈没找到
printf("未找到 %d\n", key);
return;
}
}
printf("找到 %d 在位置 %d\n", key, index);
}
// 打印哈希表
void printHashTable(int* hashTable) {
printf("\n哈希表内容:\n");
printf("位置\t值\n");
for(int i = 0; i < HASH_SIZE; i++) {
printf("%d\t%d\n", i, hashTable[i]);
}
}
// 哈希查找测试
void testHashSearch() {
int hashTable[HASH_SIZE];
initHashTable(hashTable);
int keys[] = {19, 1, 23, 14, 55, 20, 84, 27, 68, 11, 10, 77};
int n = sizeof(keys) / sizeof(keys[0]);
// 插入关键字
for(int i = 0; i < n; i++) {
insertHash(hashTable, keys[i]);
}
printHashTable(hashTable);
// 测试查找
printf("\n测试查找:\n");
searchHash(hashTable, 55);
searchHash(hashTable, 20);
searchHash(hashTable, 99); // 不存在的值
}
int main() {
testHashSearch();
return 0;
}
七、实验结果
算法思想
1.顺序查找(seqsearch):
从数组的第一个元素开始,逐个比较直到找到目标值或遍历完整个数组
时间复杂度:O(n)
2.冒泡排序(bubblesort):
通过多次遍历数组,每次比较相邻元素并交换顺序不正确的元素
每轮遍历后,最大的元素会"冒泡"到数组末尾
时间复杂度:O(n^2)
3.二分查找(binsearch):
适用于有序数组
每次与中间元素比较,缩小搜索范围
时间复杂度:O(log n)
4.菜单系统:
提供一个简单的命令行界面让用户选择操作
在执行二分查找前会自动进行排序

算法思想
1.二叉排序树实现:
使用递归方法实现了插入、查找和删除操作
删除操作考虑了三种情况:无子节点、有一个子节点和有两个子节点
提供了生成随机数创建二叉排序树的功能
2.哈希查找实现:
使用线性探测法解决冲突
实现了插入、查找、删除和显示功能
提供了示例测试函数,可以直接测试实验要求的关键字集合
使用两个特殊标记:EMPTY表示空位置,DELETED表示已删除位置
八、讨论和心得
通过这次实验,在之前学习C语言的基础上,我对几种排序查找算法有了进一步的认识。
1.查找算法:
顺序查找简单但效率低,适用于小数据量或无序数据
二分查找高效,但要求数据必须有序
哈希查找平均时间复杂度为O(1),但需要额外空间并处理冲突
2.排序算法:
冒泡排序实现简单,但效率低,适合教学演示
在实际应用中,如数据量大应考虑更高效的排序算法(如快速排序、归并排序等)
3.哈希表:
哈希查找性能优异,但需要设计好的哈希函数
线性探测法容易实现但可能导致聚集现象
装填因子(已用空间/总空间)影响哈希表性能
4.总结:
不同算法各有优缺点,应根据具体场景选择,理解算法思想比记住代码更重要,通过实现加深了对数据结构和算法的认识。