算法学习笔记(6)-基于数值划分的高效排序算法

##时间复杂度O(n)

目录

##时间复杂度O(n)

##桶排序

##原理

##图例

##代码实现

##计数排序

##原理

##图例

##代码实现

 ##计数排序进阶版本

##原理

##图例

##代码实现

##基数排序

##原理

##图例

##代码实现


##桶排序

##原理

桶排序的核心思想就是,将一组无序数字,通过一定的方式散列到不同的桶中,且桶之间有大小顺序(例:后面一个桶中的最小数字都比前面桶中的最大数字大),对每个桶进行排序(选择排序等)然后将每个桶中的元素依次取出,即可实现桶排序

考虑一个长度为 𝑛 的数组,其元素是范围 [0,1) 内的浮点数。

  1. 初始化 𝑘 个桶,将 𝑛 个元素分配到 𝑘 个桶中。
  2. 对每个桶分别执行排序(这里采用编程语言的内置排序函数)。
  3. 按照桶从小到大的顺序合并结果。

##图例

##代码实现

//python代码示例
##桶排序
def bucket_sort(a):
    """桶排序"""
    #初始化k = n / 2 个桶,预期向每个桶中分配2个元素
    k = len(a) // 2
    buckets = [[] for _ in range(k)]

    #将元素分配到各个列表桶中
    for num in a :
        #输入数据的范围为[0,1),使用num * k 映射到索引范围[0,k-1]
        i = int(num * k)
        buckets[i].append(num)
    print(buckets)
    for bucket in buckets :
        bucket.sort()
    print(buckets)

    i = 0

    for bucket in buckets :
        for num in bucket :
            a[i] = num
            i += 1
    return a

a = [0.49,0.96,0.82,0.09,0.57,0.43,0.91,0.75,0.15,0.37]
print(bucket_sort(a))

//c++代码示例
void bucketSort(vector<float> &a)
{
	int k = a.size() / 2 ;
	
	vector<vector<float>> buckets(k) ;
	
	for (float num : a)
	{
		int i = num * k ;
		buckets[i].push_back(num) ;
	} 
	
	for (vector<float> &bucket: buckets)
	{
		sort(bucket.begin(),bucket.end()) ;
	}
	
	int i = 0 ;
	for (vector<float> &bucket : buckets)
	{
		for (float num : bucket)
		{
			a[i++] = num ;
		}
	}
} 
//python代码示例
def Selection_Sort(a) :
    n = len(a)
    for i in range(0,n) :
        min = i
        for j in range(i+1,n) :
            if a[j] < a[min] :
                min = j
        a[min] , a[i] = a[i] ,a[min]

#选取足够大的桶数M,如果数据分布还足够均匀,复杂度可以逼近O(n)
def Bucket_sort(a) :
    a_min = min(a)
    a_max = max(a)


    bucketCount = 3
    bucket = [[], [], []]
    #关键点
    perBucket = (a_max - a_min + bucketCount) // bucketCount #计算出来每个桶的区间范围

    for num in a :
        buckerIndex = (num - a_min) // perBucket
        bucket[buckerIndex].append(num)

    for i in range(bucketCount) :
        Selection_Sort(bucket[i])

    idx = 0
    for i in range(bucketCount) :
        for num in bucket[i] :
            a[idx] = num
            idx += 1

##计数排序

##原理

最简单的实现方式:(不考虑数据为数据对象)

给定一个长度为 𝑛 的数组 nums ,其中的元素都是“非负整数”,

  1. 遍历数组,找出其中的最大数字,记为 𝑚 ,然后创建一个长度为 𝑚+1 的辅助数组 counter 。
  2. 借助 counter 统计 nums 中各数字的出现次数,其中 counter[num] 对应数字 num 的出现次数。统计方法很简单,只需遍历 nums(设当前数字为 num),每轮将 counter[num] 增加 1 即可。
  3. 由于 counter 的各个索引天然有序,因此相当于所有数字已经排序好了。接下来,我们遍历 counter ,根据各数字出现次数从小到大的顺序填入 nums 即可。

##图例

##代码实现

//python代码示例

#计数排序-借用了哈希思想
def CountingSort(a) :
    n = len(a)
    cntLen = max(a) + 1
    cnt = [0] * cntLen
    for val in a :
        cnt[val] += 1
    print(cnt)
    n = 0
    for val in range(cntLen) :
        while cnt[val] > 0 :
            cnt[val] -= 1
            a[n] = val
            n += 1
a = [2,3,1,3,2,1,4,2,4,6,2]
CountingSort(a)
print(a)

//c++代码示例
void conutSortNaive(vector<int> &a)
{
	int m = 0 ;
	for (int num : a)
	{
		m = max(m,num) ;
	}
	
	vector<int> counter(m+1,0) ;
	for (int num : a)
	{
		couuter[num]++ ;
	}
	
	int i = 0 ;
	for (int num = 0 ; num < m + 1 ; num++)
	{
		for (int j = 0 ; j < counter[num] ; j++,i++)
		{
			a[i] = num ;	
		}	
	} 
}

 ##计数排序进阶版本

##原理

(若输入数据为对象时,最简单的基数排序就失效了)

假设输入数据是商品对象,我们想按照商品价格(类的成员变量)对商品进行排序,而上述算法只能给出价格的排序结果。

##图例

##代码实现

#python代码示例
def counting_sort(a) :
    """计数排序"""
    #可排序对象,并且是稳定排序
    #1.统计数组最大元素m
    m = max(a)
    #2.统计各数字出现的次数
    #counter[num]代表num出现的次数
    counter = [0] * (m + 1)
    for num in a :
        counter[num] += 1
    print(counter)
    #3.求counter的前缀和,将出现次数转化成尾索引
    #即counter[num] - 1 是num在res中最后一次出现的索引
    for i in range(m) :
        counter[i + 1] += counter[i]
    #倒叙遍历a,将各个元素填入到结果数组res
    print(counter)

    n = len(a)
    res = [0] * n
    for i in range(n-1,-1,-1) :
        num = a[i]
        res[counter[num] - 1] = num
        counter[num] -= 1
    print(res)
    for i in range(n) :
        a[i] = res[i]

a = [2,3,1,3,2,1,4,2,4,6,2]
counting_sort(a)
print(a)

//c++代码示例
void countingSort(vector<int> &a)
{
	int m = 0 ;
	for (int num : a)
	{
		m = max(m,num) ;
	}
	
	vector<int> counter(m+1,0) ;
	
	for (int num : a)
	{
		counter[num]++ ;
	}
	
	for (int i = 0 ; i < m ; i++)
	{
		counter[i+1] += counter[i] ;
	}
	
	int n = a.size() ;
	vector<int> res(n,0) ;
	for (int i = n-1 ; i >= 0 ; i--)
	{
		int num = a[i] ;
		res[counter[num] - 1] num ;
		counter[num] -= 1 ;
	}
	
//	for (int i = 0 ; i < n ; i++)
//	{
//		a[i] = res[i] ;
//	}
	a = res ;		
} 

##基数排序

##原理

通过迭代,利用数位来实现排序,具体的实现分为以下及部分:

1.定义是个数位桶0-9分别用来存储每一次迭代的结果

2.利用base来实现数位的移动(base*=10)

3.利用整除和取模操作来得到具体的数位【num // base % 10】

4.迭代

##图例

##代码实现

#python代码示例

def RadixSort(a) :
    base = 1
    maxv = max(a)
    while base <= maxv :
        
        bucket = []
        for _ in range(10) :
            bucket.append([])
        
        for num in a :
            idx = num // base % 10
            bucket[idx].append(num)
        
        l = 0
        for idx in range(10) :
            for v in bucket[idx] :
                a[l] = v
                l+=1
        print(a)
        base = base * 10

#python代码示例
def digit(num,exp) :
    """获取元素 num 的第 k 位,其中 exp = 10^(k-1)"""
    # 传入 exp 而非 k 可以避免在此重复执行昂贵的次方计算
    return (num // exp) %10

def counting_sort_digit(a,exp) :
    """计数排序(根据 nums 第 k 位排序)"""
    # 十进制的位范围为 0~9 ,因此需要长度为 10 的桶数组
    counter = [0] *10
    n = len(a)
    for i in range(n) :
        d = digit(a[i],exp)
        counter[d] += 1
    # 求前缀和,将“出现个数”转换为“数组索引”
    for i in range(1,10) :
        counter[i] += counter[i-1]

    res = [0] * n
    for i in range(n-1,-1,-1) :
        d = digit(a[i],exp)
        j = counter[d] - 1
        res[j] = a[i]
        counter[d] -= 1
    for i in range(n) :
        a[i] = res[i]

def radix_sort(a) :
    m = max(a)
    exp = 1
    while exp <= m :
        counting_sort_digit(a,exp)
        exp = exp * 10
//c++代码示例
int digit(int num , int exp)
{
	return (num / exp) % 10 ;
}

void countingSortDigit(vector<int> &nums ,int exp)
{
	vector<int> counter(10,0) ;
	int n = nums.size() ;
	
	for (int i = 0 ; i < n ; i++)
	{
		int d = digit(nums[i],exp) ;
		counter[d]++ ; 
	}
	
	for (int i = 1 ; i < 10 ; i++)
	{
		counter[i] += counter[i-1] ;
	}
	
	vector<int> res)(n,0) ;
	for (int i = n-1 ; i >= 0 ; i--)
	{
		int d = digit(nums[i],exp) ;
		int j = counter[d] - 1 ;
		res[j] = nums[i] ;
		counter[d]-- ;
	}
	
	for (int i = 0 ; i < n ; i++)
	{
		nums[i] = res[i] ;
	}
}

void radixSort(vector<int> &nums)
{
	int m = *max_element(nums.begin(),nums.end()) ;
	
	for (int exp = 1 ; exp <= m ; exp *= 10)
	{
		countingSortDigit(nums,exp) ;
	}
}
  • 8
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值