嵌入式学习DAY23 --- 排序、查找,哈希表,用哈希表编写一个 <学生信息管理系统>

嵌入式入门学习笔记,遇到的问题以及心得体会!
DAY23

概述:

一.排序
1.插入排序
2.快速排序
二.查找
1.顺序查找
2.折半查找
3.分块查找
三.哈希函数(哈希表)
1.概念
2.用法注意点
四.运用哈希表来编写实现一个<学生信息管理系统>

笔记:

一.排序

1.插入排序:

先构建一个有序的序列,遍历无序的序列,将无序的序列中的每一个元素和有序序列中的元素从后向前进行比较,如果比有序序列中的小,就继续比较,直到找到合适的位置,然后插入

void InsertSort (ElemType A[],int n) 
{
	int i,j;
	for (i=2;i<=n;i++)//依次将A[2]~A[n]插入到前面已排序序列
	if(A[i]<A[i-1])//若A[i]关键码小于其前驱,将A[i]插入有序表
	{
		A[0]=A[i] ;//复制为哨兵,A[0]不存放元素
		for(j=i-1;A[0]<A[j];--j)//从后往前查找待插入位置
		A[j+1]=A[j]; // 向后挪位
		A[j+1]=A[0];//复制到插入位置
	}
}

在这里插入图片描述

代码演示:(插入排序)

#include <stdio.h>

void display(int Arr[],int size)
{
	int i;
	for(i=0;i<size;i++)
	{
		printf("%d ",Arr[i]);
	}
	printf("\n");
}
void InsertSort(int Arr[],int size)
{
	int i;
	for(i = 1;i<size;i++)	//从第二个数开始遍历
	{
		int tmp = Arr[i];	//取出i的数,和i-1的数比较
		int j = i-1;	//因i-1的数之前都是有序的,所以只要当前的数比较
		while(j>=0 && Arr[j]>tmp)
		{
			Arr[j+1] = Arr[j];//大于tmp(Arr[i])的数后移
			j--;		//向前比较
		}
		Arr[j+1] = tmp;
	}
}
78,23,54,3,34,99,427823  54  3  34  99  42   tmp  23   j = 0   ==78 78  54  3  32  99  42  ==》【23 7854  3  32  99  42 
===》【23  54  783  32  99  42

int main()
{
	int Arr[] = {78,23,54,3,34,99,42};
	int size = sizeof(Arr)/sizeof(int);

	printf("%d \n",size);	
	InsertSort(Arr,size);
	display(Arr,size);
	return 0;
}

2.快速排序

找到一个基准,实现比基准小的值移到基准的左边,比基准大的值移动到基准的右边,接下来继续对基准左右两边的值按照同样的方法进行排序,所以需要递归
快速排序的基本思想是基于分治法的:在待排序表L1…n]中任取- -个元素 pivot作为枢轴(或基准,通常取首元素),通过一趟排序将待排序表划分为独立的两部分L…k-1]和L[k…n],使得L1…k-1]中的所有元素小于pivot, L[k…n]中的所有元素大于等于pivot,则pivot放在了其最终位置L/(k).上,)这个过程称为一趟快速排序(或一次划分)。然后分别递归地对两个子表重复上述过程,直至每部分内只有一一个元素或空为止,即所有元素放在了其最终位置上。 在这里插入图片描述
在这里插入图片描述

代码演示:(快速排序)

#include<stdio.h>
void quickSort(int *arr, int left, int right)
{
	if(left < right)
	{
		int i = left;
		int j = right;

		int key = arr[i];
		while(i < j)
		{
			while(key < arr[j] && i < j)
			{
				j--;
			}
			if(i < j)
			{
				arr[i++] = arr[j];
			}

			while(key > arr[i] && i < j)
			{
				i++;
			}
			if(i < j)
			{
				arr[j--] = arr[i];
			}
		}
		arr[i] = key;
		quickSort(arr, left, i-1);
		quickSort(arr, i+1, right);
	}
}

int main()
{
	int arr[8] = {50, 36, 66, 76, 36, 12, 25, 95};
	quickSort(arr, 0, 7);

	int i;
	for(i = 0; i < 8; i++)
	{
		printf("%d  ", arr[i]);
	}
	printf("\n");
}

二、查找

Key:能够标志一个或者一组记录
主key:能够唯一的标志一个记录

顺序查找:ASL=0(n)

一般线性表的顺序查找:
作为一种最直观的查找方法,其基本思想是从线性表的一端开始, 逐个检查关键字是否满足给定的条件。若查找到某个元素的关键字满足给定条件,则查找成功,返回该元素在线性表中的位置;若已经查找到表的另一端, 但还没有查找到符合给定条件的元素,则返回查找失败的信息。
下面给出其算法,主要是为了说明其中引入的“哨兵”的作用。

typedef struct//查找表的数据结构
{
	ElemType *elem;O / /元素存储空间基址,建表时按实际长度分配,0号单元留空
	int TableLen;//表的长度
}SSTable;

int Search_ Seq (SSTable ST, ElemType key) 
{
	ST( elem[0]-key;//“哨兵’
	for (i=ST.TableLen;ST.elem[i]!=key;--i);//从后往前找
	return i; //若表中不存在关键字为key的元素,将查找到i为0时退出for循环
}

折半查找: ASL–》对数级

折半查找又称二分查找,它仅适用于有序的顺序表。
折半查找的基本思想:首先将给定值key与表中中间位置的元素比较,若相等,则查找成功,返回该元素的存储位置;若不等,则所需查找的元素只能在中间元素以外的前半部分或后半部分(例如,在查找表升序排列时,若给定值key大于中间元素,则所查找的元素只可能在后半部分)。
然后在缩小的范围内继续进行同样的查找,如此重复,直到找到为止,或确定表中没有所需要查找的元素,则查找不成功,返回查找失败的信息。算法如下:

int Binary_ Search (SeqList L, ElemType key)
{
	int low=0,high=L. TableLen-1 , mid;
	while (low<=high) 
	{
		mid= (low+high) /2;//取中间位置
		if (L.elem[mid]==key)
		return mid;//查找成功则返回所在位置
		else if (L.elem [mid]>key)
		high=mid-1;//从前半部分继续查找
		else
		1ow=mid+1;/ /丛后半部分继续查找
	}
		return -1;(//查找失败,返回-1
}

分块查找:

分块查找又称索引顺序查找,它吸取了顺序查找和折半查找各自的优点,既有动态结构,又适于快速查找。
分块查找的基本思想将查找表分为若干子块。块内的元素可以无序,但块之间是有序的,即第一个块中的最大关键字小于第二个块中的所有记录的关键字,第二个块中的最大关键字小于第三个块中的所有记录的关键字,以此类推。再建立一个索引表, 索引表中的每个元素含有各块的最大关键字和各块中的第一个元素的地址,索引表按关键字有序排列。分块查找的过程分为两步:第一步是在索引表中确定待查记录所在的块,可以顺序查找或折半查找索引表;第二步是在块内顺序查找。

分块查找示意图
分块查找示意图


三.哈希函数(哈希表)

前面三种查找算法都与n有关,n越大,查找的时间就会越长

能不能有一种方法,查找的时间与n无关,时间复杂度是常数级
查找需要满足的条件:
1、一批数据
2、每一个记录都有主key值
3、数据连续存储在数组里

如何快速找到我想找的那组记录呢?我如果知道我要找的这组记录的下标
但是如何知道下标呢?
会在存数据之前,将主key值和下标之间需要有一个一一对应的关系

1.哈希表基本原理:

散列表(Hash table, 也叫哈希表),是根据关键码 - 值(Key - value)而直接进行访问的数据结构。 也就是说, 它通过把关键码 - 值映射到表中一个位置来访问记录, 以加快查找的速度。这个映射的函数叫做散列函数,存放记录的数组叫做散列表。

如下所示就是一个典型的哈希表
在这里插入图片描述

哈希表是数组和链表的结合体,上图的哈希表左边是一个数组,右边是链表,即数组中的每个元素都是一个链表。一个新的结点具体添加到哪个链表中是由映射关系来决定的。同样,我们如果想要查找某个结点,只需要通对结点的关键码进行映射关系运算,计算出在数组的第几个元素(即哪个链表),然后遍历链表比对关键码即可得出结果。

从典型的哈希表结构中,我们可以得出这样的结论:本质上哈希表是一个元素为链表的数组。

在这里插入图片描述
以上是理想的情况,实际的情况是根据哈希函数算出的index有可能有重复的,怎么办?
调整哈希函数,但是哈希函数只能减少冲突,不能完全解决冲突

所以:只能先选择合适的哈希函数,然后再想办法解决冲突

哈希函数一般的选择:
直接地址法
平方取中法
叠加法
保留除数法
随机函数法

解决冲突的办法:链地址法
在这里插入图片描述


以下我用一个简单的学生管理系统展示下如何创建哈希表并且如何使用

四.<学生信息管理系统>

1.hash.h ----------------------------------------------------- 如下

#ifndef _HASH_H_
#define _HASH_H_


//哈希函数
//假设学号是一个十位的字符串,比如“2102101”
//H(key) = (2102+101)-2002;
//这里采用的是叠加法,学号是设定的7位数字,前面四位与后面三位叠加
//例如2102+101
//后面式子减去的2002是定义假如本校所来的第一位学生的学号为2001001
//那么第一位学生的学号运用叠加法就是  2001+001=2002
//所以在创建的哈希表中2002将放在表首


#define SIZE 2000

typedef struct Stu
{
	char id[10];//学号
	char name[10];//姓名
	int age;//年龄
	float score;//成绩
} Stu;

typedef Stu data_t;

//节点
typedef struct HashNode
{
	data_t data;
	struct HashNode *pNext;
} HashNode;

//哈希表
typedef struct Hash
{
	HashNode *hash[SIZE];
}Hash;

enum HASH_OP
{
	HASH_ERR = -1,
	HASH_OK,
	NOT_FOUND
};

Hash *createHash();
int insertStu(Hash *pHash, data_t tData);
int deleteStu(Hash *pHash, data_t *pData);
int searchStu(Hash *pHash, data_t *pData);
void showHash(Hash *pHash);
void destroyHash(Hash **ppHash);
#endif

在这里插入图片描述

2.hash.c ----------------------------------------------------- 如下

#include<stdio.h>
#include "hash.h"
#include <stdlib.h>
#include <string.h>

Hash *createHash()
{
	Hash *pHash = (Hash *)malloc(sizeof(Hash));
	if(NULL == pHash)
	{
		return NULL;
	}
	memset(pHash, 0, sizeof(Hash));
	return pHash;
}
int insertStu(Hash *pHash, data_t tData)
{
	if(NULL == pHash)
	{
		return HASH_ERR;
	}
	//创建节点
	HashNode *pNode = (HashNode*)malloc(sizeof(HashNode));
	if(NULL == pNode)
	{
		return HASH_ERR;
	}

	memset(pNode, 0, sizeof(HashNode));
	pNode->data = tData;

	//根据key计算要插入的位置
	int index;
	int id = atoi(pNode->data.id);
	index = id / 1000 + id % 1000 - 2002;//为了运用叠加法来求出存储位置
    //因为学号是7位数字,没有例外所以前四位用id/1000 后三位用id%1000来分别求出
    
	//插入
	pNode->pNext = pHash->hash[index];
	pHash->hash[index] = pNode;

	return HASH_OK;
}
int deleteStu(Hash *pHash, data_t *pData)
{
	if(NULL == pHash || NULL == pData)
	{
		return HASH_ERR;
	}

	//计算index
	int index;
	int id = atoi(pData->id);
	index = id / 1000 + id % 1000 - 2002;

	HashNode *pTmp = pHash->hash[index];
	if(NULL == pTmp)
	{
		return NOT_FOUND;
	}

	//如果要删除的节点刚好是链表的第一个节点,需要单独处理
	if(0 == strcmp(pData->id, pTmp->data.id))
	{
		pHash->hash[index] = pTmp->pNext;
		strcpy(pData->name, pTmp->data.name);
		pData->age = pTmp->data.age;
		pData->score = pTmp->data.score;
		free(pTmp);
		pTmp = NULL;
		return HASH_OK;
	}
	//删除操作时,如果是第一个节点那么和删除后面的节点是有所区别的

	//删除的不是第一个节点
	HashNode *pDel = pTmp->pNext;
	while(pDel != NULL)
	{
		if(0 == strcmp(pData->id, pDel->data.id))
		{
			//删除pDel
			pTmp->pNext = pDel->pNext;
			strcpy(pData->name, pDel->data.name);
			pData->age = pDel->data.age;
			pData->score = pDel->data.score;
			free(pDel);
			pDel = NULL;
			return HASH_OK;
		}
		pTmp = pTmp->pNext;
		pDel = pTmp->pNext;
	}
	return NOT_FOUND;
}

//pDel参数:保存找到的节点的地址,给删除节点的时候用
//一般情况下可以给pDel传NULL
int searchStu(Hash *pHash, data_t *pData)
{
	if(NULL == pHash || NULL == pData)
	{
		return HASH_ERR;
	}

	//计算index
	int index;
	int id = atoi(pData->id);
	index = id / 1000 + id % 1000 - 2002;

	//遍历index位置处的链表
	HashNode *pTmp = pHash->hash[index];
	if(NULL == pHash->hash[index])
	{
		return NOT_FOUND;
	}
	else
	{
		while(pTmp != NULL)
		{
			//将链表中的每一个节点和传进来的id进行比较
			if(0 == strcmp(pTmp->data.id, pData->id))
			{
				strcpy(pData->name, pTmp->data.name);
				pData->age = pTmp->data.age;
				pData->score = pTmp->data.score;
				return HASH_OK;
			}
			pTmp = pTmp->pNext;
		}
	}
	return NOT_FOUND;
}

void showHash(Hash *pHash)
{
	if(NULL == pHash)
	{
		return;
	}

	int i;
	for(i = 0; i < SIZE; i++)
	{
		//如果是NULL,进行下一个
		if(NULL == pHash->hash[i])
		{
			continue;
		}
		HashNode *pTmp = pHash->hash[i];
		while(NULL != pTmp)
		{
			printf("id:%s\nname:%s\nage=%d\nscore:%f\n", pTmp->data.id, pTmp->data.name, pTmp->data.age, pTmp->data.score);
			pTmp = pTmp->pNext;
		}
		printf("*************\n");
	}
}
void destroyHash(Hash **ppHash)
{
	if(NULL == ppHash || NULL == *ppHash)
	{
		return;
	}

	//释放所有的链表
	int i;
	for(i = 0; i < SIZE; i++)
	{
		if(NULL == (*ppHash)->hash[i])
		{
			continue;
		}
		HashNode *pDel = (*ppHash)->hash[i];
		while(pDel != NULL)
		{
			(*ppHash)->hash[i] = pDel->pNext;
			free(pDel);
			pDel = (*ppHash)->hash[i];
		}
	}

	//释放哈希表
	free(*ppHash);
	*ppHash = NULL;
}

3.main.c ----------------------------------------------------- 如下

#include<stdio.h>
#include "hash.h"
#include <string.h>


int main()
{
	Hash *pHash = createHash();
	if(NULL == pHash)
	{
		printf("创建失败!\n");
		return -1;
	}

	int cmd;
	data_t stu = {0};
	while(1)
	{
		printf("0 退出 1 插入 2 删除  3 查找  4  显示\n");
        scanf("%d", &cmd);

		memset(&stu, 0, sizeof(stu));
		if(0 == cmd)
		{
			break;
		}
		else if(1 == cmd)
		{
			printf("请输入学生的学号:");
			scanf("%s", stu.id);
			printf("请输入学生的姓名:");
			scanf("%s", stu.name);
			printf("请输入学生的年龄:");
			scanf("%d", &stu.age);
			printf("请输入学生的成绩:");
			scanf("%f", &stu.score);

			insertStu(pHash, stu);
		}
		else if(2 == cmd)
		{
			printf("请输入要删除的学生的学号:");
			scanf("%s", stu.id);
			deleteStu(pHash, &stu);
			printf("id:%s,name:%s,age:%d,score:%f已被删除\n", stu.id,stu.name,stu.age,stu.score);
		}
		else if(3 == cmd)
		{
			printf("请输入要查找的学生的学号:");
			scanf("%s", stu.id);
			if(NOT_FOUND == searchStu(pHash,&stu))
			{
				printf("查无此人! \n");
			}
			else
			{
				printf("id:%s,name:%s,age:%d,score:%f已找到\n", stu.id,stu.name,stu.age,stu.score);
			}
		}
		else if(4 == cmd)
		{
			showHash(pHash);
		}
		else
		{
			printf("请重新输入!\n");
		}
	}


	destroyHash(&pHash);
	return 0;
}
  • 25
    点赞
  • 70
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值