【数据结构】快速排序,基数排序/桶排序

基数排列


桶排序:低位优先,所有数据从(个位)开始
依次放入10个桶内(入队,再从桶里取出,直到完全有序)。
基数(桶)排序:低位优先,所有数据从低(个)位开始,依次放到对应的桶内(入队),再接着从桶内取出(出队),直到完全有序 时间复杂度O(dn) 空间复杂度O(n) 稳定吗

如下图:
循环的次数和最大值的位数有关
请添加图片描述
请添加图片描述
请添加图片描述

《二维数组实现桶排序》

Get_figure(arr, len); :获取最大值的位数,来作为循环遍历位数的条件
Radix(arr, len, i);:进行将所有数据,按照位数放入桶中,放完之后,再取出来放到arr数组中
Get_Num(arr[i], fin):获取当前位数所在桶的序号。index获取

static int  Get_figure(int* arr, int len)
{
	//assert
	//获取最大值是多少
	int tmp = 0;
	for (int i = 0; i < len; i++)
	{
		
		if (tmp < arr[i])
		{
			tmp = arr[i];
		}
	}
	//获取tmp的位数
	int count = 0;
		while (tmp != 0)
		{
			count++;
			tmp = tmp/10; //每次丢掉一个位,直到商变为0
		}
		//while结束,得到的是最大值的位数count
		return count;
}

以fin的规则取n对应的值

static int Get_Num(int arr, int fin)
{
	for (int i = 0; i < fin; i++)
	{
		arr /= 10;//扔掉最低位,获得当前数歌舞ie,示威,百位的数字,
		           //入249 个位9,十位4,百位2
	}
	return arr % 10;   //得到当前述的最低位
}

以fin位排序一次,放到桶内,再从桶内取出。

static void Radix(int* arr, int len, int fin)
{
	int brr[10][20] = { 0 };//十个桶,每个桶初始容量20
	int num[10] = { 0 }; //保存每个桶的有效值个数
	for(int i = 0;i<len;i++)
	{
		int index = Get_Num(arr[i], fin);//index就是几号桶
		brr[index][num[index]]= arr[i];
			//num就相当于要插入的下标
		num[index] ++;//对应的桶内数据个数++
	}

此时for结束,代表数据全部放入桶内
接着,从0号桶一次取出数据

	int k = 0;
	for(int  i = 0; i <= 9;i++)//i保存了桶号	
    {
		//int k = 0;如果写在着,会导致第二个桶开始所有的数据,每次都收覆盖前一个桶
	   //保存向arr中存放的空间下标
		for (int j = 0; j < num[i]; j++)//从桶内0号下标开始取值,直到取完
		{
			arr[k++] = brr[i][j];
		}
     }
}
void RadixSort(int arr[], int len)
{

循环次数,最大值的位数来控制

	int count = Get_figure(arr, len);
	for (int i = 0; i < count; i++)
	{
		Radix(arr, len, i);

	}


}

《链式队列实现桶排序》
请添加图片描述
核心代码:

static void LRadix(int* arr, int len, int fin)
{
	Head Arr_queue[10];
		for(int i = 0;i<10;i++)
		{
			Init_Iqueue(&Arr_queue[i]);
		}
		//十个链式队列初始化完成。
		for (int i = 0; i < len; i++)
		{
			int index = Get_num(arr[i], fin);
				push(&Arr_queue[index], arr[i]);
		}
		int k = 0;
		for (int i = 0; i < 10; i++)
		{
			while (!IsEmpty(&Arr_queue[i]))
			{
				int tmp = 0;
				pop(&Arr_queue[i], &tmp);
				arr[k++] = tmp;
				//可以合并
			}
		}
}

链式队列实现基数排序和二维数组实现基数排序的区别?
请添加图片描述

快速排序

快速排序(递归):(越乱越有序)从右向左找比基准值小的,向左放,再从左向右找比基准值大的,向右放,重复此过程 直到完全有序 时间复杂度O(nlogn) 空间复杂度O(logn) 不稳定

优化

1.如果right-left特别小,直接用冒泡
2.三位取中法,取第一个值,中间值,和最后一个值判断一下,找一不大不小的值作为基准值
   不论不大不小的值是哪一个,都将该值和最左边的值交换,再进行下面的操作
3.第三个优化:主要针对快排已经有序,越有序,越慢。
    已经完全有序,在执行就会变成选择排序,为了防止这种情况发生,可以自己去打乱一下。

划分函数

请添加图片描述
请添加图片描述

int Partion(int* arr, int left, int right)
{
	//将左边第一个数当作基准点
	int temp = arr[left];
	while (left < right)
	{   //基准点在左边,所以从右边开始
		while (left < right && arr[right] > temp)//不能等于
		{  //从右往左,找比tmp大的,如果小于tmp就把当前值放到左边
			--right;
		}
		if (left < right) arr[left] = arr[right];
		while (left < right && arr[left] <= temp)
		{    //从左往右找比temp小的,如果比tmp大,就放到右边
			++left;
		}
		if (left < right) arr[right] = arr[left];
		
	}
	arr[left] = temp; //arr[right] 也对,因为此时 right=left
	return left;//将基准值返回。
}
void SortPass(int* arr, int left, int right)
{
	//优化:
	//1.如果right-left特别小,直接用冒泡
	//2.三位取中法,取第一个值,中间值,和最后一个值判断一下,找一不大不小的值作为基准值
     // 不论不大不小的值是哪一个,都将该值和最左边的值交换,再进行下面的操作
     //第三个优化:主要针对快排已经有序,越有序,越慢。
	//已经完全有序,在执行就会变成选择排序,为了防止这种情况发生,可以自己去打乱一下。


	int par = Partion(arr, left, right); //得到第一次划分的基准值下标
	if(left < right)
	{
		if (left < par - 1)  //按照基准数分成两边,par是当前左边的下边界
			                     //是右边的始边界
			//保证基准值左右两边至少有两个数据
		{
			SortPass(arr, left, par - 1);
		}
		if (right > par + 1)
		{
			SortPass(arr, par + 1, right);
		}
	}
}
void QuickSort(int *arr,int len)
{
	for (int i = 0; i < len - 1; i++)
	{
		SortPass(arr, 0, len - 1);
	}
}