排序算法总结(三)——非比较排序

计数排序

计数排序的基本思想是对于给定的输入序列中的每一个元素x,确定该序列中值小于x的元素的个数(此处并非比较各元素的大小,而是通过对元素值的计数和计数值的累加来确定)。一旦有了这个信息,就可以将x直接存放到最终的输出序列的正确位置上。例如,如果输入序列中只有17个元素的值小于x的值,则x可以直接存放在输出序列的第18个位置上。当然,如果有多个元素具有相同的值时,我们不能将这些元素放在输出序列的同一个位置上,因此,上述方案还要作适当的修改。

步骤:

1.找到数据中的最大值max和最小值min

2.统计数组中每个值为min+i-1的元素出现的次数,存入数组arr的第i项,即a[0]记录的是min出现的次数;

比如一串数值{1,3,5,8,5,8,6,9,1}

max为9,min为1,所以构建n=max-min+1=9大小的数组arr[9],min(即1)出现2次,所以a[0]=2,min+2(即3)出现1次,所以a[2]=1,min+1出现0次,所以a[1]=0,以此类推。这样每一个数出现的次数都在数组中反应出来

3.统计完后,从arr[0]开始,依次将arr[i]对应的数值放入新数组,比如arr[0]=2,arr[1]=0,arr[2]=1,min=1,则先将arr[0]所对应min放入数组得{1,1},arr[1]没有对应元素,arr[2]=1对应一个min+2,继续放入数组得{1,1,3}。

c++实现如下:

void counting_sort(int a[], int n)//计数排序
{
	int min=INT32_MAX,max=-1;
	for (int i = 0; i < n; ++i) //找最大最小值
	{
		if (a[i] < min)
		{
			min = a[i];
		}
		if (a[i] > max) 
		{
			max = a[i];
		}
	}
	int len = max - min + 1;    //数组长度
	int* arr = new int[len]();
	for (int i = 0; i < n; ++i)
	{
		++arr[a[i] - min];    //记录数据
	}
	int j = 0;
	for (int i = 0; i < len; ++i) 
	{
		while (arr[i] > 0) 
		{
			a[j++] = i+min;    //赋值到新数组(这里重新对a赋值)
			--arr[i];    //每赋值一个arr[i]减1
		}	
	}
}

时间复杂度为O(n+k),空间复杂度为O(n),适合数据范围跨度较小的排序。

桶排序

假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排。

1:人为设置一个BucketSize,作为每个桶所能放置多少个不同数值(例如当BucketSize==5时,该桶可以存放{1,2,3,4,5}这几种数字,但是容量不限,即可以存放100个3);

2:遍历输入数据,并且把数据一个一个放到对应的桶里去

3:对每个不是空的桶进行排序,可以使用其它排序方法,也可以递归使用桶排序;

4:从不是空的桶里把排好序的数据拼接起来。 

 如下图(这里BucketSize为10)

 

c++实现如下:

int maxbit(int a[], int n) //辅助函数,求数据的最大位数
{
	int d = 1; //保存最大的位数
	int p = 10;
	for (int i = 0; i < n; ++i)
	{
		while (a[i] >= p)
		{
			p *= 10;
			++d;
		}
	}
	return d;
}

vector<int> bucket_Sort(vector<int>arr, int n,int d)    //返回桶排序后的数组
{
	if (d == 0||n==0)
	{
		return arr;
	}

	int s = pow(10, d - 1);    
	vector<int>b[10];    //创建10个桶
	vector<int>a;
	
	for (int i = 0; i < n; i++)
	{
		int k = arr[i] / s;    
		k = k % 10;    //先除再取余得到应放入的桶
		b[k].push_back(arr[i]);    //放入对应桶
	}
	for (int i = 0; i < 10; ++i)
	{
		int loc = 0;
		int size = b[i].size();
		b[i]=bucket_Sort(b[i], size,d-1);    //递归使用桶排序,也可直接使用其他比较排序
		for(int j=0;j<size;j++)
		{
			a.push_back(b[i][loc++]);
		}
	}
	return a;
}

void bucket_sort(int a[], int n)    //桶排序,这里block为10
{
	int d = maxbit(a, n);    //求最大位数
	vector<int>arr(a, a + n);    //为了避免链表的使用这里用了vecotr
	vector<int> b = bucket_Sort(arr, arr.size(),d);    //对arr使用桶排序
	memcpy(a, &b[0], b.size() * sizeof(int));    //vector转为普通数组
}

时间复杂度为O(n+k),空间复杂度为O(n+k),k为BucketSize大小,适合数据分布均匀的排序。

基数排序

基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。基数排序基于分别排序,分别收集,所以是稳定的。

基数排序的方式可以采用LSD(Least significant digital)或MSD(Most significant digital),LSD的排序方式由键值的最右边开始,而MSD则相反,由键值的最左边开始。

步骤1:取得数组中的最大数,并取得位数;

步骤2:a为原始数组,从最低位开始取每个位组成radix数组;

步骤3:对radix进行计数排序(利用计数排序适用于小范围数的特点);

第一步

以LSD为例,假设原来有一串数值如下所示:

73, 22, 93, 43, 55, 14, 28, 65, 39, 81

首先根据个位数的数值,在走访数值时将它们分配至编号0到9的桶子中:

0

1      81

2      22

3      73 93 43

4      14

5      55 65

6

7

8      28

9      39

第二步

接下来将这些桶子中的数值重新串接起来,成为以下的数列:

81, 22, 73, 93, 43, 14, 55, 65, 28, 39

接着再进行一次分配,这次是根据十位数来分配:

0

1 14

2 22 28

3 39

4 43

5 55

6 65

7 73

8 81

9 93

第三步

接下来将这些桶子中的数值重新串接起来,成为以下的数列:

14, 22, 28, 39, 43, 55, 65, 73, 81, 93

c++实现如下:

int maxbit(int a[], int n) //辅助函数,求数据的最大位数
{
	int d = 1; //保存最大的位数
	int p = 10;
	for (int i = 0; i < n; ++i)
	{
		while (a[i] >= p)
		{
			p *= 10;
			++d;
		}
	}
	return d;
}

void radix_sort(int a[], int n) //基数排序
{
	int d = maxbit(a, n);
	int *tmp = new int[n]; 
	int *count = new int[10]; //计数器
	int radix=1;
	for (int i = 1; i <= d; i++) 
	{
		for (int j = 0; j < 10; j++) 
		{
			count[j] = 0;
		}
		for (int j = 0; j < n; j++) 
		{
			int k = (a[j] / radix) % 10;
			count[k]++;    //计数
		}
		for (int j = 1; j < 10; j++) 
		{
			count[j] = count[j] + count[j - 1];    //tmp[j]为基数小与等于j的个数
		}
		for (int j = n - 1; j >= 0; --j)
		{
			int k = (a[j] / radix) % 10;
			tmp[count[k] - 1] = a[j];
			count[k]--;    //这里使用--可以使赋值完毕之后count都为0,而不需要清0
		}
		for (int j = 0; j < n; ++j) 
		{
			a[j] = tmp[j];
		}
		radix *= 10;
	}
	delete[]tmp;
	delete[]count;
} 

时间复杂度为O(n*k),空间复杂度为O(n+k),k为最大位数,适合位数较小的排序。

参考:百度百科

          十大经典排序算法总结

发布了33 篇原创文章 · 获赞 148 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_40692109/article/details/103264886