排序与查找

这篇博客详细介绍了多种排序算法的实现,包括选择排序、冒泡排序、快速排序和插入排序,并提供了每种排序算法的C语言代码实现。此外,还涉及到折半查找法和哈希查找法,以及哈希表的创建、插入和查找操作。文章通过实例展示了如何应用这些算法,并提供了相应的主函数示例。
摘要由CSDN通过智能技术生成
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

#define 	swap(a,b)    	 (c = a; a = b; b = c)
#define 	swap1(a,b) 		 ( (a) = (a) + (b); (b) =  (a) - (b); (a) = (a) - (b) )	//可能a+b 会溢出
#define 	swap2(a,b) 		 ( (a) = (a) ^ (b); (b) =  (a) ^ (b); (a) = (a) ^ (b) ) //不会溢出

#define 	get_ave(a,b) 	(a + (b-a) / 2)    // ( (a+b) / 2 ) 第二种 溢出概率会小点


//选择排序:每次循环获取的是  当前循环内的最小值的位置下标
//			每次得到下标以后 就和当前循环基准 比较 是否进行交换 
//			得到的最小值 存于放于基准位置 
//			之后将基准的下一位置 作为基准继续重复以上
int select_sort(int a[],int len)
{
	int i,k;
	int j;
	int temp;
	for(i = 0;i < len -1;i++)//共需要 len-1 个返回
	{
		k = i;//记录每次 循环下 存放最小值的下标

		for(j = i+1;j < len ;j++)//获取每个循环内最小数 的下标
		{
			if(a[k] > a[j])//从0 ~ len-1 开始比 获得更小值的下标存于k中
			{
				k = j;
			}
		}
		if(k != i)//当最小数 不是 循环的基准位置  交换两个位置 接着寻找第二个更小的数
		{	
			temp = a[i];
			a[i] = a[k];
			a[k] = temp;
		}
	}
	for(i = 0; i < len;i++)
	{
		printf("%2d ",a[i]);

	}
	putchar(10);
	return 0;
}

//选择原理:  每次从头开始  依次比较 得到最大的数 存于末尾
//			  
int maopao(int a[],int len)
{
	int i;int j;int temp;

	for(i = 0;i < len-1;i++)//一共需要比较 len -1个来回
	{
		for(j = 0;j < len-1-i; j++)//每一个循环中第一个数都得一次跟后面的数进行比较
			//随着循环的增加  要比较的数开始减少
		{
			if(a[j] > a[j+1])//前大于后
			{
				temp = a[j];
				a[j] = a[j+1];
				a[j+1] = temp;
			}
		}
	}
	return 0;
}

//快速排序:①   快排:设置准点 将大于基准点的数放于基准点左边 小于基准点的数,放于基准点右边
//		   ②   对基准点左边的数进行递归快排    当只剩一个数的时候  此时left > right 递归结束
//		   ③   对基准点右边的数进行递归快排    当只剩一个数的时候  此时left > right 递归结束

//一次快排
int get_mid(int a[],int left,int right)//快排   填坑法
{
	int base = a[left];//设置基准点
	while(left < right)
	{
		//这里等于 一定要加上  当你没加这个等于的时候  当这一轮结束时
		//下一轮开始时如果 出现的基准点作为全场最大的数 此时while 的两个条件都不满足 left和right 都不会变化
		// 使得最外层while陷入一个死循环的状态

		while( (a[right] >= base) && (left < right) ) right--;  //出现大于等于基准点的数时  右边 开始左移
		a[left] = a[right];//当出现比基准点小的数时,放入左边基准点空位 
		//此时 右边出现空位
		while( (a[left] <= base) && (left < right) ) left++;//出现小于等于 基准点的数 时  左边下标开始右移
		a[right] = a[left];		//当出现 比基准点大的数的时候 放入右边空位 
		//此时左边出现空位
	}//不断循环  直至所有的数 被基准点 左右完全分割完毕后  跳出循坏

	a[left] = base;//归位
	return left;
}

// 快排 
void quick_sort(int a[],int left,int right)
{

	if(left < right)//或 if(left > right ) return ;
	{
		
      int mid =get_mid(a,left,right);//快排的基准点分割				
		quick_sort(a,left,mid-1);//当快排只剩一个数时  返回的要么是mid= left 或mid=right   此时快排条件left>right的左边快排不满足   就会结束 进入右边快排
		quick_sort(a,mid+1,right);//每进行一次递归 右边快排 都会进入左边快排 进行mid判断  当右边快排只剩一个数时,mid= left 或mid=right 此时快排条件left>right的右边快排不满足  就会结束 再次进入左边快排
		
		//知道左右两边快排 都结束以后那么排序完成
	}
}
//插入排序
//把数据分成两部分 一边是有序 一边是无序 (正常把第一个数作为有序的开始)
//从第二个数开始  也就是无序的第一个数 和 它前一个数比较(有序的第一个数) 比它小的话 就有序部分后移一个数 将比较的数 插入空出的有序部分的位置  此时有序部分变为两个数  
//					当比它大的话  继续比较下一个无序部分的数  
//这样通过一次循环  多次比较  解决排序问题 常用于数据量较少的排序  和 冒泡差不多

void insert_sort1(int a[],int len)
{
	for(int i = 1;i<len;i++)//默认第一个数有序 从第二个开始排
	{
		int value = a[i];//value 表示 要排序的数

		int end = i-1;//被比较的 有序部分 数的下标 当i = 1;跟 a[0]比较  i=2,要跟a[1] a[0]比较
		//用于记录 要插入的位置 

		while(end >= 0 && a[end] > value)  //end用于记录 每个数的 比较次数 同时用于比较 要插入的数的值
			//当end = -1表示 比较结束 
			//当有序部分末尾的数 比 要插入的数大时	往前 继续比较
		{			
			a[end+1] = a[end];				//后面的数 往后挪一个位置
			end--;
		}
		a[end + 1] = value;
	}
}
//插入排序2 优化 
//
//
void insert_sort2(int a[],int len)
{
	for(int i = 1;i<len;i++)// a[1] ~ a[len-1]是需要排序的  无序部分的数
		//a[0] 初始 作为 有序部分
	{
		int temp = a[i];//记录 要排序的值	
		//要排序的数值 的前一个  就是有序部分的最大值
		int j = i;

		for(; j>0 && temp < a [j-1] ;j--)  //    排序的值 与有序部分(从后往前)依次比较 当前一个值小(也就是比有序部分最大值小) 
			//就进入循环  将前一个值 后移一位	  不要怕覆盖   因为它覆盖的是要排序的值
			//	  而这个值已经在前面保存了
		{
			a[j] = a[j-1];//后移 留出空位
		}
		a[j] = temp;
	}
}
///希尔排序: 插入排序的改进  数据量越大 差距越明显
// 一:它通过增量d 来对 原序列进行分组设定  得到 它有几组 也就是几个部分  增量初值为元素总数的一半 每次循环减少自身一半 直到0
// 二:每次分组完  都对每个组内元素  进行直接插入排序
//重复一二  继续减小增量   直到增量为d = d/2 =0   排序结束
//
void shell_sort(int a[],int len)//
{

	for(int d = len/2; d>0; d/=2) //增量控制 
	{
		for(int i = d;i < len;i++)//组内元素 的插入排序    这里获取的是 后半部分下标遍历 
		{  
			int temp = a[i];

			int j = i;
			for(;j-d >= 0 && temp <a[j-d];j-=d)//后比前大  前往后移
			{
				a[j] = a[j-d];
			}
			a[j] = temp;
		}
	}
}


//折半查找法  又叫二分查找
//1. 指针有序序列进行的查找 通过定值 k 确认查找的范围 每次查找 将搜索空间减半

int binarySearch1(int a[],int len,int data)
{
	int low = 0,high = len-1; //查找区间  每次查找 区间减半 当找到 时 就返回  或直到区间为0 查找结束

	while(low <= high)
	{
		//int k = (low + high) >>1; //获取k值 可能会溢出

		int k = low + (high -low) /2;
		if(data < a[k]) 
		{
			high = k-1;//设定范围

		}
		else if(data > a[k])
		{	
			low = k + 1;
		}
		else //相等 时 说明找到了 直接返回位置
		{
			return k;
		}
	}
	return -1;//跳出循环 说明没炸到
}

// 折半查找 利用递归
int binarySearch(int a[],int len ,int low,int high,int data)
{

	if( low <= high)
	{
		int k = (low + high) / 2;

		if(data < a[k]) 
		{
			binarySearch(a,len,low,k-1,data);
		}
		else if(data > a[k])
		{	
			binarySearch(a,len,k+1,high,data);
		}
		else //相等 时 说明找到了 直接返回位置
		{
			return k;
		}
	}
	else

		return -1;
}

// 哈希查找  通过建立 记录的关键字 与 存放地址 间的联系 
//				1.哈希函数选择: 	尽可能将记录点均匀化 减少冲突
//								  主要有 直接地址法  数字分析法 除留取余法  平方取中法 叠加法 随机函数法	
//				2.冲突的处理方法:  开放地址法: 增量的设定:线性探查法 二次探查法			
//									链地址法 :将冲突通过链表 挂载到 冲突位置 中

//这里 使用除留取余法 确定地址  采用开放地址法 解决冲突

//hash表创建  返回值为申请 的 表长
int hash_init(int **hash,int m)
{
	int n = m/0.75;
	*hash = (int *)malloc(sizeof(n * sizeof(int)));// n/0.75 算出填充因子
	memset(*hash,'\0',n*4);

	if(hash == NULL)
	{
		perror("malloc");
		exit(-1);
	}
	return n;
}

//hash表插入数据  *hash    
void insert_hash(int hash[],int len,int data)
{
	int addr = data % len;//哈希地址

	while(hash[addr] != 0)
	{
		addr = (addr+1)%len;//开放地址法存入
	}
	hash[addr] = data;//放入数据

}
//哈希表 查找 
int find_hash(int hash[],int len,int data)
{
	int addr = data % len;
	int *p = &hash[addr];//查找终点

	while(hash[addr] != data)
	{
		addr = (addr+1)%len;
		if(p == &hash[addr])
		{
			return -1;
		}

	}

	return addr;

}



int main()
{
	///哈希查找
	int *hash = NULL;
	int m;
	printf("请设置hash表的大小: ");	
	scanf("%d",&m);
	int len = hash_init(&hash,m);
	int data;
	for(int i =0;i< m;i++)
	{
		printf("inset data: ");
		scanf("%d",&data);
		insert_hash(hash,len,data);
	}
	printf("find data: ");
	scanf("%d",&data);
	printf("find pos %d\n",find_hash(hash,len,data));
	//
	/*

	   int a[21] = {7,4,16,1,9,6,3,5,0,8,12,14,18,2,13,19,17,11,15,10,14};
	   int len =  sizeof(a)/sizeof(a[0]);
	   int i;
	   for(i = 0; i < len;i++)
	   {
	   printf("%2d ",a[i]);
	   }
	   putchar(10);
	   maopao(a,len);
	   for(i = 0; i < len;i++)
	   {
	   printf("%2d ",a[i]);
	   }
	   putchar(10);
	//sort(a,len);
	//quick_sort(a,0,len-1);
	//insert_sort2(a,len);
	//shell_sort(a,len);
	int data;
	printf("查找的数: ");
	scanf("%d",&data);
	printf("find data pos : %d\n",binarySearch1(a,len,data));
	printf("find data pos : %d\n",binarySearch(a,len,0,len-1,data));
	*/	
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值