查找与十大算法

本文详细介绍了数据结构中的算法概念,包括其五大特征和如何评价算法的时间复杂度。接着,讲解了几种常见的排序算法,如冒泡排序、选择排序、插入排序、希尔排序、快速排序、归并排序和堆排序,以及它们的时间复杂度和稳定性。此外,还讨论了非比较排序算法,如计数排序、桶排序和基数排序,强调了它们在特定场景下的优势和局限性。
摘要由CSDN通过智能技术生成
 

算法:

数据结构中的算法,指的是数据结构所具备的功能

解决特定问题的方法,它是前辈们的一些优秀的经验总结

算法有五大特征:可行性、有穷性、确定性、输入、输出

如何评价一个算法:

时间复杂度:

由于计算机性能的不同,无法准确地统计出算法执行所需要的时间

因此采用算法的执行次数来代表算法的时间复杂度,一般采用 O(公式) 忽略常数

常见的时间复杂度:

// O(1)

printf("%d",i);

// O(logn) //使用n或者N

for(int i=n; i>=0; i/=2)

{

printf("%d",i);

}

// O(n)

for(int i=0; i<n; i++)

{

printf("%d",i);

}

// O(nlogn)

for(int i=0; i<n; i++)

{

for(int j=n; j>=0; j/=2)

{

printf("%d",i);

}

}

// O(n^2)

for(int i=0; i<n; i++)

{

for(int j=0; j<n; j++)

{

printf("%d",i);

}

}

空间复杂度:

执行一个程序所需要的额外的内存空间大小,是对一个算法在运行过程中额外临时占用的空间大小的一个度量

通常来说,只要算法不涉及动态分配的空间以及递归,基本上空间复杂度O(1)

分治:分而治之

把一个大而复杂的问题,分解成若干个小而简单的问题,利用计算机强大的计算能力来解决所有的小问题,是一种算法思想

实现分治思想的方法:循环、递归

查找:

顺序查找:

对待查找的数据没有要求,从头到尾逐一比较,在小规模的数据查找中比较常见,相比较而言效率较低

时间复杂度:O(n)

int order_find(int* arr,int len,int key)
{
	for(int i=0; i<len; i++)
	{
		if(arr[i] == key) return i;	
	}
	return -1;
}

二分查找:折半查找

待查找的数据必须有序,从中间位置数据开始比较查找,如果中间值比key小,则从左部分进行二分查找,反之比key大,则从右部分进行二分查找,如果相同则查找成功,当查找的范围缩小为0,则查找失败

时间复杂度:O(logn)

//	循环二分
int binary_for_find(int* arr,int len,int key)
{
	int l = 0,r = len-1;	
	while(l <= r)
	{
		int m = (l+r)/2;
		if(arr[m] == key)	return m;
		if(arr[m] > key) r = m-1;
		else			 l = m+1;
	}
	return -1;
}

int _binary_find(int* arr,int l,int r,int key)
{
	if(l > r) return -1;

	int m = (l+r)/2;
	if(arr[m] == key)	return m;
	if(arr[m] > key)	return _binary_find(arr,l,m-1,key);
	else				return _binary_find(arr,m+1,r,key);
}

//	递归二分
int binary_recusion_find(int* arr,int len,int key)
{
	return _binary_find(arr,0,len-1,key);	
}

块查找:

是一种数据查找处理的思想,不是特定的算法,当数据量过多时,可以把数据进行特定的分块处理,然后再进行查找,例如单词字典

哈希查找:Hash

数据先经过哈希函数计算出该数据在哈希表中的位置,然后标记该位置的数据,之后查找数据直接从哈希表中查找,它的查找时间复杂度最高能到 O(1)

但是该算法有很大的局限性,不适合浮点型、有符号负数数据,还需要额外的内存空间,空间复杂度很高,还不好确定额外的空间大小,是一种典型的空间换时间的算法

设计哈希函数的方法:

1、直接定址法:把数据当做哈希表的下标来标记哈希表

2、数据分析法:

先分析数据的特点然后设计哈希函数,常用方法是先找出最大、最小值,哈希表长度:最大-最小+1,哈希函数:数据-最小值 作为哈希表下标进行标记

平方折中法、折叠法、随机数法等方法设计哈希函数,如果出现相同数据时,哈希表会出现所谓的哈希冲突,一般使用链表解决

hash函数应用:MD5、SHA-1都属于Hash算法

//	哈希查找
bool hash_find(int* arr,int len,int key)
{
	/*
	//	1、直接定址法
	//	创建哈希表
	int hash[100000] = {};
	//	标记
	for(int i=0; i<len; i++)
	{
		hash[arr[i]]++;	
	}
	//	查找
	return hash[key];
	*/
	// 2、数据分析法
	int min = arr[0], max = arr[0];
	for(int i=0; i<len; i++)
	{
		if(arr[i] > max) max = arr[i];
		if(arr[i] < min) min = arr[i];
	}
	//	创建哈希表
	int hash[max-min+1];
	memset(hash,0,4*(max-min+1));
	//	标记
	for(int i=0; i<len; i++)
	{
		hash[arr[i]-min]++;	
	}
	if(key<min || key>max) return false;
	//	查找
	return hash[key-min];
	
}

全部程序:

#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>

#define LEN 10
#define show_arr(arr,len) {\
	for(int i=0;i<len;printf("%d ",arr[i++]));printf("\n");}
#define swap(a,b) {typeof(a) t=(a);(a)=(b);(b)=t;}

//	顺序查找
int order_find(int* arr,int len,int key)
{
	for(int i=0; i<len; i++)
	{
		if(arr[i] == key) return i;	
	}
	return -1;
}

//	排序
void sort(int* arr,int len)
{
	for(int i=0; i<len-1; i++)
	{
		for(int j=i+1; j<len; j++)
		{
			if(arr[i] > arr[j])
			{
				swap(arr[i],arr[j]);	
			}
		}
	}
}

//	循环二分
int binary_for_find(int* arr,int len,int key)
{
	int l = 0,r = len-1;	
	while(l <= r)
	{
		int m = (l+r)/2;
		if(arr[m] == key)	return m;
		if(arr[m] > key) r = m-1;
		else			 l = m+1;
	}
	return -1;
}

int _binary_find(int* arr,int l,int r,int key)
{
	if(l > r) return -1;

	int m = (l+r)/2;
	if(arr[m] == key)	return m;
	if(arr[m] > key)	return _binary_find(arr,l,m-1,key);
	else				return _binary_find(arr,m+1,r,key);
}

//	递归二分
int binary_recusion_find(int* arr,int len,int key)
{
	return _binary_find(arr,0,len-1,key);	
}

//	哈希查找
bool hash_find(int* arr,int len,int key)
{
	/*
	//	1、直接定址法
	//	创建哈希表
	int hash[100000] = {};
	//	标记
	for(int i=0; i<len; i++)
	{
		hash[arr[i]]++;	
	}
	//	查找
	return hash[key];
	*/
	// 2、数据分析法
	int min = arr[0], max = arr[0];
	for(int i=0; i<len; i++)
	{
		if(arr[i] > max) max = arr[i];
		if(arr[i] < min) min = arr[i];
	}
	//	创建哈希表
	int hash[max-min+1];
	memset(hash,0,4*(max-min+1));
	//	标记
	for(int i=0; i<len; i++)
	{
		hash[arr[i]-min]++;	
	}
	if(key<min || key>max) return false;
	//	查找
	return hash[key-min];
	
}

int main(int argc,const char* argv[])
{
	int arr[LEN] = {};
	for(int i=0; i<LEN; i++)
	{
		arr[i] = rand()%100;	
	}
	sort(arr,LEN);
	show_arr(arr,LEN);
	printf("order_find:%d\n",order_find(arr,LEN,49));
	printf("binary_for_find:%d\n",binary_for_find(arr,LEN,4));
	printf("binary_recusion_find:%d\n",binary_recusion_find(arr,LEN,86));
	printf("hash_find:%d\n",hash_find(arr,LEN,1));
}

排序算法:

排序算法的稳定性:

待排序数据中,如果存在值相同的数据,如果在排序的过程中不会改变它们的前后顺序,则认为该排序算法是稳定的

冒泡排序:

数据左右相比,把大的数据交换到右边,然后继续下两个数据左右相比,直到最大的数据交换到末尾,重复以上步骤。

注意:在冒泡排序中一旦发现数据已经有序,立即停止排序,称为有序性敏感

冒泡排序适合数据基本有序的情况,冒泡的效率是非常高

时间复杂度:最好 O(n) 平均 O(n^2)

稳定性: 稳定的

//	冒泡
void bubble_sort(TYPE* arr,size_t len)
{
	bool flag = true;	//解决有序性敏感
	for(int i=len-1; i>0 && flag; i--)
	{
		flag = false;
		for(int j=0; j<i; j++)
		{
			if(arr[j] > arr[j+1])
			{
				swap(arr[j],arr[j+1]);	
				flag = true;
			}
		}
	}
	show_arr(arr,len);
	printf(" %s\n",__func__);
}

选择排序:

假定最开始的位置是最小值并记录下标min,然后与后面的数据进行比较,如果有比min为下标的数据更小的值,则更新min,如果比较结束后,min的位置发生了改变,则交换最开始的位置与min位置的值

虽然时间复杂度没有变化,但是数据的交换次数比较少,因此实际的运行速度并不慢(数据交换要比赋值、数据比较要耗时)

选择排序相当于冒泡排序的一种变种,但是不存在有序性敏感,比较适合数据比较混乱的情况会比冒泡快

时间复杂度:O(n^2)

稳定性:不稳定

10 10 1

a b

//	选择
void select_sort(TYPE* arr,size_t len)
{
	for(int i=0; i<len-1; i++)
	{
		int min = i;
		for(int j=i+1; j<len; j++)
		{
			if(arr[j] < arr[min]) min = j;	
		}
		if(min != i) swap(arr[min],arr[i]);
	}
	show_arr(arr,len);
	printf(" %s\n",__func__);
}

插入排序:

把数据看成两部分,一部分是有序的,后一部分的数据与有序部分的数据逐个比较,直到找到合适的位置后,插入到该位置

适合对已排序的数据新增数据后进行排序

时间复杂度:O(n^2)

稳定的

//	插入
void insert_sort(TYPE* arr,size_t len)
{
	//	i是无序部分第一个位置
	for(int i=1,j=0; i<len; i++)
	{
		//	j是要插入的位置
		int val = arr[i];
		for(j=i; j>0 && arr[j-1]>val; j--)
		{
			arr[j] = arr[j-1];	
		}
		if(j!=i) arr[j] = val;
	}
	show_arr(arr,len);
	printf(" %s\n",__func__);
}

希尔排序:缩小增量排序

是插入排序的一个增强版,由于插入排序数据移动的速度比较慢,如果待排序数据很混乱时效率较低,因此引入增量的概念,数据间隔增量个数进行插入排序,然后不停地缩小一倍增量,直到增量为1,此时数据从很混乱变为接近有序,以此提高排序的效率

时间复杂度:O(nlogn)

不稳定的

//	希尔排序
//   5 4 3 2 1 比插入快
void shell_sort(TYPE* arr,size_t len)
{
	for(int k=len/2; k>0; k/=2)
	{
		for(int i=k,j=0; i<len; i++)
		{
			int val = arr[i];
			for(j=i; j-k>=0 && arr[j-k] > val; j-=k)
			{
				arr[j] = arr[j-k];	
			}
			if(j!=i) arr[j] = val;
		}
	}
	show_arr(arr,len);
	printf(" %s\n",__func__);
}

快速排序:

在中间位置确定为标杆p,备份标杆的数据为val,从左边找比val大的数据,找到后位置为l,然后把该数据赋值给p位置,更新p为l,然后从右边找比val小的数据,找到后位置为r,然后把该数据赋值给p位置,更新p为r,重复以上过程,直到l和r相遇时,结束此次排序,最后把val还原赋值给p位置,最终p左边都比val小,右边都比val大,局部有序

继续对左部分快排,右部分快排,直到全部有序为止

注意:

快排的综合性能是最高的,因此称为快速排序,也是笔试最常考的排序算法

时间复杂度:O(nlogn)

不稳定的

void _quick_sort(TYPE* arr,int left,int right)
{
	if(left >= right) return;
	//	记录左右标杆
	int l = left, r = right;
	//	找到中间位置标杆
	int p = (l+r)/2;
	//	备份标杆的值
	int val = arr[p];

	while(l < r)
	{
		//	从p左边找比val大的值
		while(l < p && arr[l] <= val) l++;
		//	如果找到了
		if(l < p)
		{
			arr[p] = arr[l];
			p = l;
		}

		//	从p的右边找比val小的值
		while(r > p && arr[r] >= val) r--;
		//	如果找到了
		if(r > p)
		{
			arr[p] = arr[r];
			p = r;
		}
	}
	//	还原标杆的值
	arr[p] = val;
	_quick_sort(arr,left,p-1);
	_quick_sort(arr,p+1,right);
}

//	快排
void quick_sort(TYPE* arr,size_t len)
{
	_quick_sort(arr,0,len-1);	
	show_arr(arr,len);
	printf(" %s\n",__func__);
}

归并排序:

先将数据拆分成单独的个体,然后再按照从小到大的顺序把两组数据合并到临时空间中,合并结束后,再重新拷贝回原空间的原位置中,得到数量翻倍的一组组有序的数据,继续两组两组合并,直到整体有序为止

归并排序是利用了额外的内存空间,避免了数据交换的耗时,是一种典型的以空间换时间的算法

时间复杂度:O(nlogn)

稳定的

void _merge_sort(TYPE* arr,TYPE* temp,int l,int r)
{
	if(l >= r) return;
	int m = (l+r)/2;
	_merge_sort(arr,temp,l,m);
	_merge_sort(arr,temp,m+1,r);

	//	最后合并两部分
	//	前提:左右两部分各自有序
	//	左:最小l 最大m		右:最小m+1  最大r
	if(arr[m] < arr[m+1]) return;

	int k = l, i = l, j = m+1;
	while(i<=m && j<=r)
	{
		if(arr[i] <= arr[j])
			temp[k++] = arr[i++];
		else
			temp[k++] = arr[j++];
	}
	
	//	把剩余没比完的直接放入temp末尾
	while(i<=m) temp[k++] = arr[i++];
	while(j<=r) temp[k++] = arr[j++];

	//	把数据从temp还原回arr中
	memcpy(arr+l,temp+l,sizeof(TYPE)*(r-l+1));
}

//	归并排序
void merge_sort(TYPE* arr,size_t len)
{
	//	申请临时空间
	TYPE* temp = malloc(sizeof(TYPE)*len);
	_merge_sort(arr,temp,0,len-1);
	//	释放临时空间
	free(temp);
	show_arr(arr,len);
	printf(" %s\n",__func__);
}

堆排序:

先把数据看成一棵完全二叉树,然后先调整成大顶堆或小顶堆,然后把根节点与末尾交换,然后有效个数-1,重新调整成堆结构,继续以上步骤,直到有效个数为1,此时整个数组就有序了,既可以循环也可以递归实现

大顶堆:堆排序后 从小到大

小顶堆:堆排序后 从大到小

时间复杂度:O(nlogn)

不稳定 例如: 1 10 10

参考前面的文章《堆和平衡二叉树》里的代码。

计数排序:

在数据中找出最大值和最小值,通过 最大值-最小值 方式创建哈希表,然后根据 数据-最小值 的值作为哈希表的下标访问并标记(累加)对应的哈希表,然后标记所有数据

排序时,遍历整个哈希表,如果表中数据非0时,把该位置 下标+最小值 依次存回排序数组中,遍历结束数组有序

理论上计数排序非常快,因为它不是基于比较的排序,在一定范围内整数排序时快于任何一种比较排序算法,但是有很大的局限性,适合无符号整型、正整型数据,并且数据的差值不宜过大,否则会非常浪费内存,因此,如果数据越平均,重复数据多的情况该排序性价比越高

时间复杂度:Ο(n+k)(其中k是待排序的整数的范围)

注意:如果k范围过大,效率可能会低于O(nlogn)

稳定的

//	计数排序
void count_sort(TYPE* arr,size_t len)
{
	int min = arr[0], max = arr[0];
	for(int i=0; i<len; i++)
	{
		if(min > arr[i]) min = arr[i];
		if(max < arr[i]) max = arr[i];
	}
	//	创建哈希表
	int* hash = calloc(4,max-min+1);
	//	标记哈希表
	for(int i=0; i<len; i++)
	{
		hash[arr[i]-min]++;		//	必须累加	
	}

	//	遍历哈希表,还原排序后的数据
	for(int i=0,j=0; i<max-min+1; i++)
	{
		while(hash[i]--)
		{
			arr[j++] = i+min;	
		}
	}

	//	销毁哈希表
	free(hash);
	show_arr(arr,len);
	printf(" %s\n",__func__);
}

桶排序:

根据数据的值,存储到不同的桶中,然后调用其他的排序算法,对桶中的数据进行排序,然后再全部按桶的顺序拷贝回数组中,以此降低排序的规模来提高排序速度,以空间换时间的排序算法

缺点:如何分桶、桶有几个、桶定义多大都不一定,需要对排序数据有一定的了解和分析后才能确定

时间复杂度:Ο(n)

稳定的

//	cnt桶数 range桶中数据的范围
void _bucket_sort(TYPE* arr,size_t len,int cnt,int range)
{
	//	申请桶内存 bucket桶首地址  bucket_end桶中最后一个元素的地址
	TYPE* bucket[cnt],*bucket_end[cnt];
	for(int i=0; i<cnt; i++)
	{
		//	最坏情况 所有数据在一个桶中
		bucket[i] = malloc(sizeof(TYPE)*len);	
		//	开始时,起始、末尾指针都指向开头
		bucket_end[i] = bucket[i];
	}

	//	把所有数据,按照桶的范围值放入对应的桶中
	for(int i=0; i<len; i++)
	{
		for(int j=0; j<cnt; j++)
		{
			if(range*j <= arr[i] && arr[i] < range*(j+1))
			{
				*(bucket_end[j]) = arr[i];
				bucket_end[j]++;	//末尾指针往后移动一个元素
				break;
			}
		}
	}

	//	调用其他的排序算法,对每个桶中的数据排序
	for(int i=0; i<cnt; i++)
	{
		//	计算桶中的元素个数
		int size = bucket_end[i] - bucket[i];
		//	调用其他的排序算法排序
		if(1 < size) count_sort(bucket[i],size);
		//	把排序好的数据依次还原回数组中
		memcpy(arr,bucket[i],size*sizeof(TYPE));
		arr += size;
		free(bucket[i]);
	}
}

//	桶排序
void bucket_sort(TYPE* arr,size_t len)
{
	_bucket_sort(arr,len,4,25);
	show_arr(arr,len);
	printf(" %s\n",__func__);
}

基数排序:

是桶排序思想的其中一种具体实现,首先创建10个队列(桶),然后依次按照数据的个、十、百位...的顺序,入队到对应的桶中,然后依次每个出队回原来的数组中,当最高位都处理完且返回数组后,数组就有序了

缺点:只适合排序正整数数据,需要准备大量的临时空间

时间复杂度:Ο(n)

稳定的

//	基数排序
void base_sort(TYPE* arr,size_t len)
{
	//创建队列
	ListQueue* queue[10] = {};
	for(int i=0; i<10; i++)
	{
		queue[i] = create_list_queue();	
	}

	//	循环次数由最大值的位数决定
	int max = arr[0];
	for(int i=1; i<len; i++)
	{
		if(arr[i] > max) max = arr[i];	
	}

	//	i 1个位 2十位 ....
	for(int i=1,k=1; max/k>0; k*=10,i++)
	{
		//int mod = pow(10,i);
		//int div = mod/10;
		for(int j=0; j<len; j++)
		{
			//	获取每个数的每个位的值
			//int index = arr[j]%mod/div;
			int index = arr[j]/k%10;
			push_list_queue(queue[index],arr[j]);
		}
		
		//	依次从队列重新返回到数组
		int index = 0;
		for(int j=0; j<10; j++)
		{
			while(!empty_list_queue(queue[j]))	
			{
				arr[index++] = head_list_queue(queue[j]);	
				pop_list_queue(queue[j]);
			}
		}
	}

	for(int i=0; i<10; i++)
	{
		destroy_list_queue(queue[i]);	
	}
	show_arr(arr,len);
	printf(" %s\n",__func__);

}	

全部代码

#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
#include "list_queue.h"

#define LEN 15
#define TYPE int

#define show_arr(arr,len) {\
	for(int i=0;i<len;printf("%d ",arr[i++]));}
#define swap(a,b) {typeof(a) t=(a);(a)=(b);(b)=t;}

typedef void (*SortFP)(TYPE* arr,size_t len);

//	冒泡
void bubble_sort(TYPE* arr,size_t len)
{
	bool flag = true;	//解决有序性敏感
	for(int i=len-1; i>0 && flag; i--)
	{
		flag = false;
		for(int j=0; j<i; j++)
		{
			if(arr[j] > arr[j+1])
			{
				swap(arr[j],arr[j+1]);	
				flag = true;
			}
		}
	}
	show_arr(arr,len);
	printf(" %s\n",__func__);
}

//	选择
void select_sort(TYPE* arr,size_t len)
{
	for(int i=0; i<len-1; i++)
	{
		int min = i;
		for(int j=i+1; j<len; j++)
		{
			if(arr[j] < arr[min]) min = j;	
		}
		if(min != i) swap(arr[min],arr[i]);
	}
	show_arr(arr,len);
	printf(" %s\n",__func__);
}

//	插入
void insert_sort(TYPE* arr,size_t len)
{
	//	i是无序部分第一个位置
	for(int i=1,j=0; i<len; i++)
	{
		//	j是要插入的位置
		int val = arr[i];
		for(j=i; j>0 && arr[j-1]>val; j--)
		{
			arr[j] = arr[j-1];	
		}
		if(j!=i) arr[j] = val;
	}
	show_arr(arr,len);
	printf(" %s\n",__func__);
}

//	希尔排序
//   5 4 3 2 1 比插入快
void shell_sort(TYPE* arr,size_t len)
{
	for(int k=len/2; k>0; k/=2)
	{
		for(int i=k,j=0; i<len; i++)
		{
			int val = arr[i];
			for(j=i; j-k>=0 && arr[j-k] > val; j-=k)
			{
				arr[j] = arr[j-k];	
			}
			if(j!=i) arr[j] = val;
		}
	}
	show_arr(arr,len);
	printf(" %s\n",__func__);
}

void _quick_sort(TYPE* arr,int left,int right)
{
	if(left >= right) return;
	//	记录左右标杆
	int l = left, r = right;
	//	找到中间位置标杆
	int p = (l+r)/2;
	//	备份标杆的值
	int val = arr[p];

	while(l < r)
	{
		//	从p左边找比val大的值
		while(l < p && arr[l] <= val) l++;
		//	如果找到了
		if(l < p)
		{
			arr[p] = arr[l];
			p = l;
		}

		//	从p的右边找比val小的值
		while(r > p && arr[r] >= val) r--;
		//	如果找到了
		if(r > p)
		{
			arr[p] = arr[r];
			p = r;
		}
	}
	//	还原标杆的值
	arr[p] = val;
	_quick_sort(arr,left,p-1);
	_quick_sort(arr,p+1,right);
}

//	快排
void quick_sort(TYPE* arr,size_t len)
{
	_quick_sort(arr,0,len-1);	
	show_arr(arr,len);
	printf(" %s\n",__func__);
}

void _merge_sort(TYPE* arr,TYPE* temp,int l,int r)
{
	if(l >= r) return;
	int m = (l+r)/2;
	_merge_sort(arr,temp,l,m);
	_merge_sort(arr,temp,m+1,r);

	//	最后合并两部分
	//	前提:左右两部分各自有序
	//	左:最小l 最大m		右:最小m+1  最大r
	if(arr[m] < arr[m+1]) return;

	int k = l, i = l, j = m+1;
	while(i<=m && j<=r)
	{
		if(arr[i] <= arr[j])
			temp[k++] = arr[i++];
		else
			temp[k++] = arr[j++];
	}
	
	//	把剩余没比完的直接放入temp末尾
	while(i<=m) temp[k++] = arr[i++];
	while(j<=r) temp[k++] = arr[j++];

	//	把数据从temp还原回arr中
	memcpy(arr+l,temp+l,sizeof(TYPE)*(r-l+1));
}

//	归并排序
void merge_sort(TYPE* arr,size_t len)
{
	//	申请临时空间
	TYPE* temp = malloc(sizeof(TYPE)*len);
	_merge_sort(arr,temp,0,len-1);
	//	释放临时空间
	free(temp);
	show_arr(arr,len);
	printf(" %s\n",__func__);
}

//	计数排序
void count_sort(TYPE* arr,size_t len)
{
	int min = arr[0], max = arr[0];
	for(int i=0; i<len; i++)
	{
		if(min > arr[i]) min = arr[i];
		if(max < arr[i]) max = arr[i];
	}
	//	创建哈希表
	int* hash = calloc(4,max-min+1);
	//	标记哈希表
	for(int i=0; i<len; i++)
	{
		hash[arr[i]-min]++;		//	必须累加	
	}

	//	遍历哈希表,还原排序后的数据
	for(int i=0,j=0; i<max-min+1; i++)
	{
		while(hash[i]--)
		{
			arr[j++] = i+min;	
		}
	}

	//	销毁哈希表
	free(hash);
	show_arr(arr,len);
	printf(" %s\n",__func__);
}

//	cnt桶数 range桶中数据的范围
void _bucket_sort(TYPE* arr,size_t len,int cnt,int range)
{
	//	申请桶内存 bucket桶首地址  bucket_end桶中最后一个元素的地址
	TYPE* bucket[cnt],*bucket_end[cnt];
	for(int i=0; i<cnt; i++)
	{
		//	最坏情况 所有数据在一个桶中
		bucket[i] = malloc(sizeof(TYPE)*len);	
		//	开始时,起始、末尾指针都指向开头
		bucket_end[i] = bucket[i];
	}

	//	把所有数据,按照桶的范围值放入对应的桶中
	for(int i=0; i<len; i++)
	{
		for(int j=0; j<cnt; j++)
		{
			if(range*j <= arr[i] && arr[i] < range*(j+1))
			{
				*(bucket_end[j]) = arr[i];
				bucket_end[j]++;	//末尾指针往后移动一个元素
				break;
			}
		}
	}

	//	调用其他的排序算法,对每个桶中的数据排序
	for(int i=0; i<cnt; i++)
	{
		//	计算桶中的元素个数
		int size = bucket_end[i] - bucket[i];
		//	调用其他的排序算法排序
		if(1 < size) count_sort(bucket[i],size);
		//	把排序好的数据依次还原回数组中
		memcpy(arr,bucket[i],size*sizeof(TYPE));
		arr += size;
		free(bucket[i]);
	}
}

//	桶排序
void bucket_sort(TYPE* arr,size_t len)
{
	_bucket_sort(arr,len,4,25);
	show_arr(arr,len);
	printf(" %s\n",__func__);
}

//	基数排序
void base_sort(TYPE* arr,size_t len)
{
	//创建队列
	ListQueue* queue[10] = {};
	for(int i=0; i<10; i++)
	{
		queue[i] = create_list_queue();	
	}

	//	循环次数由最大值的位数决定
	int max = arr[0];
	for(int i=1; i<len; i++)
	{
		if(arr[i] > max) max = arr[i];	
	}

	//	i 1个位 2十位 ....
	for(int i=1,k=1; max/k>0; k*=10,i++)
	{
		//int mod = pow(10,i);
		//int div = mod/10;
		for(int j=0; j<len; j++)
		{
			//	获取每个数的每个位的值
			//int index = arr[j]%mod/div;
			int index = arr[j]/k%10;
			push_list_queue(queue[index],arr[j]);
		}
		
		//	依次从队列重新返回到数组
		int index = 0;
		for(int j=0; j<10; j++)
		{
			while(!empty_list_queue(queue[j]))	
			{
				arr[index++] = head_list_queue(queue[j]);	
				pop_list_queue(queue[j]);
			}
		}
	}

	for(int i=0; i<10; i++)
	{
		destroy_list_queue(queue[i]);	
	}
	show_arr(arr,len);
	printf(" %s\n",__func__);

}	

int main(int argc,const char* argv[])
{
	SortFP sort[] = {bubble_sort,select_sort,insert_sort,shell_sort,quick_sort,merge_sort,count_sort,bucket_sort,base_sort};
	TYPE arr[LEN] = {};
	for(int i=0; i<sizeof(sort)/sizeof(sort[0]); i++)
	{
		for(int j=0; j<LEN; j++)
		{
			arr[j] = rand()	%90+10;
		}
		show_arr(arr,LEN);
		printf("\n");
		sort[i](arr,LEN);
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值