計數排序和基數排序的實現

計數排序

計數排序的原理

    設被排序的數組爲A,排序後存儲到B,C爲臨時數組。所謂計數,首先是通過一個數組C[i]計算大小等於i的元素個數,此過程只需要一次循環遍歷就可以;在此基礎上,計算小於或者等於i的元素個數,也是一重循環就完成。下一步是關鍵:逆序循環,從length[A]到1,將A[i]放到B中第C[A[i]]個位置上。原理是:C[A[i]]表示小於等於a[i]的元素個數,正好是A[i]排序後應該在的位置。而且從length[A]到1逆序循環,可以保證相同元素間的相對順序不變,這也是計數排序穩定性的體現。在數組A有附件屬性的時候,穩定性是非常重要的。

計數排序的前提及適用範圍

    A中的元素不能大於k,而且元素要作爲數組的下標,所以元素應該爲非負整數。而且如果A中有很大的元素,不能夠分配足夠大的空間。所以計數排序有很大侷限性,其主要適用於元素個數多,但是普遍不太大而且總小於k的情況,這種情況下使用計數排序可以獲得很高的效率。由於用來計數的數組C的長度取決於待排序數組中數據的範圍(等於待排序數組的最大值與最小值的差加上1),這使得計數排序對於數據範圍很大的數組,需要大量時間和內存。例如:計數排序是用來排序0到100之間的數字的最好的算法,但是它不適合按字母順序排序人名。但是,計數排序可以用在基數排序中的算法來排序數據範圍很大的數組。

     當輸入的元素是 n 個 0 到 k 之間的整數時,它的運行時間是 Θ(n + k)。計數排序不是比較排序,排序的速度快於任何比較排序算法。

計數排序算法的步驟:

1.找出待排序的數組中最大和最小的元素

2.統計數組中每個值爲i的元素出現的次數,存入數組C的第i項

3.對所有的計數累加(從C中的第一個元素開始,每一項和前一項相加)

4.反向填充目標數組:將每個元素i放在新數組的第C(i)項,每放一個元素就將C(i)減去1

實現代碼:

void CountSort(int *a, int size)
{
	int min = a[0], max = a[0];
	int i = 0;
	for ( i = 0; i < size; i++)
	{
		if (min > a[i])
		{
			min = a[i];//找出數組中最小的數
		}
		if (max < a[i])
		{
			max = a[i];//找出數組中的最大數
		}
	}
	int range = max - min + 1;
	int *count = new int[range];
	//初始化數組
	//memset(count, 0, sizeof(int)*range);
	for (i = 0; i < range; i++)
	{
		count[i] = 0;
	}
	//把數組a中數變成數組count中的0,1,2....
	for (i = 0; i < size; i++)
	{
		//把數組count中對應位置製成數字,代表這個位置有幾個相同的數
		//列如製成1,代表這個位置有一個數
		//列如製成2,代表這個位置有兩個相同的數
		count[a[i] - min]++;
	}
	int j = 0;
	//把count中的數還原回數組a中,它就排好序了
	for (i = 0; i < range; i++)
	{
		//重複了n次,就拿回去n次
		while (count[i]>0)
		{
			a[j++] = i + min;
			count[i]--;
		}
	}
	delete[] count;
}

基數排序

算法思想:

待排序數組[62,14,59,88,16]簡單點五個數字

分配10個桶,桶編號爲0-9,以個位數數字爲桶編號依次入桶,變成下邊這樣

|  0  |  0  | 62 |  0  | 14 |  0  | 16 |  0  |  88 | 59 |

|  0  |  1  |  2  |  3  |  4 |  5  |  6  |  7  |  8  |  9  |桶編號

將桶裏的數字順序取出來,

輸出結果:[62,14,16,88,59]

再次入桶,不過這次以十位數的數字爲準,進入相應的桶,變成下邊這樣:

由於前邊做了個位數的排序,所以當十位數相等時,個位數字是由小到大的順序入桶的,就是說,入完桶還是有序

|  0  | 14,16 |  0  |  0  |  0  | 59 | 62  | 0  | 88  |  0  |

|  0  |  1      |  2  |  3  |  4  |  5  |  6  |  7  |  8  |  9  |桶編號


因爲沒有大過100的數字,沒有百位數,所以到這排序完畢,順序取出即可

最後輸出結果:[14,16,59,62,88]

實現代碼:

//獲取最大位數
static int GetMaxRadix(int *a, int size)
{
	int radix = 10;
	int count = 1;
	for (int i = 0; i < size; i++)
	{
		//注意這裏必須是">=",假如你的最大數是100,如果
		//沒有“=”的話,你獲取最大位就是兩位
		while (a[i]>=radix)
		{
			radix *= 10;
			count++;
		}
	}
	return count;
}
static void _RadixSort(int* a, int size, int divisor, int* tmp)
{
	int count[10] = { 0 };
	int start[10] = { 0 };
	//如果你處理的是個位,count代表就是數據個位在
	//count對應位置出現的個數。十位,百位類似。
	for (int i = 0; i < size; i++)
	{
		int num1 = a[i] / divisor;
		count[num1 % 10]++;
	}
	//個位,十位,百位等出現的起始位置
	for (int j = 1; j < 10; j++)
	{
		start[j] = start[j - 1] + count[j - 1];
	}
	//根據start,將a中的數據放在tmp中,已排好序
	for (int k = 0; k < size; k++)
	{
		int num2 = a[k] / divisor;
		tmp[start[num2 % 10]++] = a[k];
	}
	//把排好序的數據放回a中
	for (int n = 0; n < size; n++)
	{
		a[n] = tmp[n];
	}
}
void RadixSort(int *arr, int size)
{
	assert(arr);
	int *tmp = new int[size];
	int n = GetMaxRadix(arr, size);
	for (int i = 1; i <= n; i++)
	{
		int divisor = 1;
		int k = i;
		while (--k)
		{
			divisor *= 10;
		}
		_RadixSort(arr, size, divisor, tmp);
	}
	delete[] tmp;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章