基数排序 (桶排序)(含负数)

本文详细介绍基数排序(radix sort)的基本概念,作为桶排序的扩展,它通过键值的各个位的值进行分配,达到排序效果。文章阐述了基数排序的稳定性、效率及其实现过程,特别指出其在处理大量数据时可能遇到的内存溢出问题,并提供了一个Java实现的示例代码。
  1. 基数排序(radixsort) 属于“分配式排序”(distributionsort) ,又称“桶子法”(bucket sort) 或binsort, 顾名思义,它是通过键值的各个位的值,将要排序的元素分配至某些“桶”中,达到排序的作用
    2)基数排序法是属于稳定 性的排序,基数排序法的是效率高的稳定性排序法
    3)基数排序(Radix Sort)是桶排序的扩展
    4)基数排序是 1887年赫尔曼●何乐礼发明的。它是这样实现的:将整数按位数切割成不同的数字,然后按每个位数分别比较。

  2. 基数排序是对传统桶排序的扩展,速度很快
    2)基数排序是经典的空间换时间的方式,占用内存很大,当对海量数据排序时,容易造成OutOfMemoryError。
    3)基数排序时稳定的。 [注:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些
    记录的相对次序保持不变,即在原序列中,r[j]=([j], 且[i]在r(i]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的
    有负数时处理思路:先判断有无负数 有则找到最小值 数组所有数据都减去该值 也就是数组最小值会变为0 接下来按正整数排序 最后数组所有数据加上原最小数 变为原有数据值

import java.util.Arrays;

public class RadixSort {
	//800W1s内
	public static void main (String[] args) {
		int a[] = {97,-80,-20,0,13,2};
		radixSort(a);
//		int a1[] = new int[8000000];
//		for(int i=0;i<8000000;i++) {
//			a1[i] = (int)(Math.random()*8000000);
//			
//		}
//		
//		System.out.println(System.currentTimeMillis()/1000);
//		radixSort(a1);
//
//		System.out.println(System.currentTimeMillis()/1000);

	

	}
	public static void radixSort(int[] arr) {
		int max = arr[0];//假定最大数为第一个
		int min = 0;
		for(int m = 1;m<arr.length;m++) {
			if(arr[m]>max) {
				max = arr[m];
			}
			if(arr[m]<min) {	
				min = arr[m];//找到负数最小值			
			}
		}
		if(min<0) {
			for(int mi = 0;mi<arr.length;mi++) {
				arr[mi] -= min;
				max -= min;
			}
		}
		System.out.println(Arrays.toString(arr)+"==");
		int maxLength = (max+"").length();//获取是几位数
		
		//定义一个二维数组,表示10个桶,每个桶就是一 个一维数组
		//说明
		//1.二维数组包含10个一维数组
		//2.为了防止在放入数的时候,数据溢出,则每个一维数组(桶),大小定为arr . length
		//3.名明确,基数排序是使用空间换时间的经典算法
		int[][] bucket = new int[10][arr.length];//以防所有数据个位数都是同一个数
		//为了记录每个桶中,实际存放了多少个数据,我们定义一一个-维数组来记录各个桶的每次放入的数据个数
		//可以这里理解
		//比如: bucketElementCounts[0] ,记录的就是bucket[0] 桶的放入数据个数
		int[] bucketElementCounts = new int[10];
		for (int n = 0, g=1; n < maxLength; n++,g*=10) {
			for (int i = 0; i < arr.length; i++) {
				int digitOfElement = arr[i]/g % 10;// 求各位数的数值
				bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[i];// [个位数值][该数存放的第几个]
				bucketElementCounts[digitOfElement]++;
			}
			// 存放完毕后再取回原数组
			int index = 0;
			for (int j = 0; j < bucketElementCounts.length; j++) {
				if (bucketElementCounts[j] != 0) {
					// 表示存储的有数据
					for (int k = 0; k < bucketElementCounts[j]; k++) {
						arr[index] = bucket[j][k];
						index++;
					}
				}
				bucketElementCounts[j] = 0;//为下一轮初始化
			}
			System.out.println(Arrays.toString(arr));

		}
		if(min<0) {
			for(int mi = 0;mi<arr.length;mi++) {
				arr[mi] += min;
			}
		}
		System.out.println(Arrays.toString(arr));

	}
}

基数排序在处理负数时会遇到一些问题,常见的问题及解决办法如下: ### 常见问题 - **空间损耗问题**:有人采用额外分配10个桶给负数的方法,但这种方式会损耗大量空间,在处理大规模数据时容易超时[^1]。 - **数值溢出问题**:部分方法通过给所有元素加一个正数使负数变为正数,但在处理存在极大数的情况时,加值操作可能会导致溢出,进而引发计算错误[^3]。 ### 解决办法 - **改进数位计算公式**:可以在计算数位时使用改进的公式,避免负数单独处理,不过引用中未详细给出该公式[^1]。 - **数组分区处理**:将数组分为负数和非负数两部分。首先将负数移动到数组前部,把除 `Integer.MIN_VALUE` 之外的负数乘以 -1 转换为正数,对这部分正数进行基数排序排序后再乘以 -1 变回负数,同时整体交换负数组元素顺序;最后对非负数部分进行基数排序。以下是示例代码: ```java public class RadixSortWithNegative { public void bucketSort(int[] arr) { int i = 0; int j = arr.length - 1; while (i < j) { while (i < j && arr[i] < 0) { i++; } while (i < j && arr[j] >= 0) { j--; } if (i < j) { swap(arr, i, j); } } i--; j = arr.length - 1; int index = 0; for (int k = 0; k <= i; k++) { if (arr[k] == Integer.MIN_VALUE) { swap(arr, index++, k); } } for (int k = index; k <= i; k++) { arr[k] *= -1; } bucketSort(arr, index, i); for (int k = index; k <= i; k++) { arr[k] *= -1; } int temp = 0; for (int k = index; k < (i - index + 1) / 2 + index; k++) { swap(arr, k, i - temp); temp++; } if (j > i) { bucketSort(arr, i + 1, j); } } public void bucketSort(int[] arr, int start, int end) { // 这里可以实现具体的基数排序逻辑 } public void swap(int[] arr, int i, int j) { int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } } ```
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值