积跬步至千里——算法强化训练(6)利用基数排序解决O(n)问题

本文介绍了基数排序的基本原理及步骤,并通过实例演示了排序过程。此外,还提供了两个算法题的解决方案,包括如何在O(1)空间和O(n)时间内找出数组中的重复元素,以及如何找到数组中缺失的第一个正整数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

基数排序:

基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或bin sort,顾名思义,它是透过键值的部份资讯,将要排序的元素分配至某些“桶”中,藉以达到排序的作用,基数排序法是属于稳定性的排序,其时间复杂度为O (nlog(r)m),其中r为所采取的基数,而m为堆数——百度百科

举个简单的例子:

第一步
以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
这时候整个数列已经排序完毕;如果排序的对象有三位数以上,则持续进行以上的动作直至最高位数为止


具体的算法题:

1、一个大小为n的数组,里面的数都属于范围[0, n-1],有不确定的重复元素,找到至少一个重复元素,要求O(1)空间和O(n)时间。

思路:n个数,都在0——n-1之间,所以假设n个桶,把数字扔到对应桶里面,如果桶里面数字和要扔进去的一样就找到了重复的。比如 2 4 5 1 2 0 六个数字,a[0] = 2,则和2号桶交换 5 4 2 1 2 0,那么2是就位了,a[0]=5,和5号桶交换,编程0 4 2 1 2 5,a[0]=0了,0、2、5三个桶都是对的了,a[1]=4,和4号桶交换,变成0 2 2 1 4 5,a[1]=2,要和2号桶交换,2号里面有个2,咿,找到重复了。

int FindTheRepeat(int data[],int n)
{
	for (int i =0; i < n; ++i)
	{
		while (i!=data[i])
		{
			if(data[i]==data[data[i]]) return data[i];
			swap(data[i],data[data[i]]);
		}
	}
	return -1;
}

上面的写法需要不断的交换,挺费时间的,现在主要是想区分一个元素是不是又被访问了,如果是就是重复了,可以考虑给每个数加上n 还是2 4 5 1 2 0,a[0]=2 把a[2]+6,变成2 4 11 1 2 0,a[1]=4,a[4]+6,变成2 4 11 1 8 0,a[2]=11>6,则a[a[2]-6]+6,变成2 4 11 1 8 6,a[3]=1,则a[1]+6,变成8 4 11 1 8 6 ,a[4]=8>6,a[a[4]-6]=11>6,说明a[2]之前已被人加过了,那么a[4]-6就是重复的。

int FindTheRepeat(int data[],int n)
{
	for (int i= 0;i<n;++i)
	{
		int index = data[i]>n?data[i]-n:data[i];
		if(data[index]>=n) return index;
		data[index] += n;
	}
}


2、给定一个无序的整数数组,怎么找到第一个大于0,并且不在此数组的整数。比如[1,2,0]返回3,[3,4,-1,1]返回    2,[1, 5, 3, 4, 2]返回6,[100, 3, 2, 1, 6,8, 5]返回4。要求使用O(1)空间和O(n)时间
思路:从前到后,如果一个数在0—n之间,就放到对应的坑里面,如果不在这个范围,或者坑里的元素和要放进去的相等,就不用放,全部放完了,从前到后,找到第一个不连续的就好了。
100 3 2 1 6 8 5为例,a[0]=100 不在范围,不动,a[1]=3,交换a[3] 100 1 2 3 6 8 5,a[1]=1,继续往后走,a[2]=2,继续后走,a[3]=3,继续,a[4]=6,交换,100 1 2 3 5 8 6,a[4]=5,继续交换,100 1 2 3 8 5 6,a[4]=8,超过范围,不动,继续走a[5]=5 a[6]=6,走完一遍,第二遍,从前到后,1 2 3都有,4没有,返回4

int FindFirstNumberNotExistenceInArray(int a[], int n)  
{  
	int i;  
	// 类似基数排序,当a[i]>0且a[i]<N时保证a[i] == i + 1  
	// a[i] != a[a[i] - 1]判断是为了防止1 1 1这种死循环
	for (i = 0; i < n; i++)  
		while (a[i] > 0 && a[i] <= n && a[i] != i + 1 && a[i] != a[a[i] - 1])  
			Swap(a[i], a[a[i] - 1]);  
	// 查看缺少哪个数  
	for (i = 0; i < n; i++)  
		if (a[i] != i + 1)  
			break;  
	return i + 1;  
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值