1. 程式人生 > >非比較排序-----計數排序,基數排序。

非比較排序-----計數排序,基數排序。

排序總歸來說可分為兩大類,比較排序與非比較排序。比較排序就是我們常用到的氣泡排序,插入排序,希爾排序,選擇排序,堆排序,快速排序,歸併排序。非比較排序不常用,但是在對一些特殊的情況進行處理時,它的速度反而更快。

1、計數排序  

排序原理:利用雜湊的方法,將每個資料出現的次數都統計下來。雜湊表是順序的,所以我們統計完後直接遍歷雜湊表,將資料再重寫回原資料空間就可以完成排序。

注意事項

①因為要建立雜湊表,計數排序只適用於對資料範圍比較集中的資料集合進行排序。範圍分散情況太浪費空間。如我有100個數,其中前99個都小於100,最後一個數位是10000,我們總不能開闢10000個數據大小的雜湊表來進行排序吧!這樣空間就太浪費了。 此時,就得考慮其他的演算法了。當然,這個例子也可能不恰當,這裡只是想讓你們直觀的理解。
②除了上面那種情況,還有一個問題,例如我有一千個數,1001~2000,此時我的雜湊表該怎麼開闢呢? 開0~2000個?那前面1000個空間就浪費了!直接從1001開始開闢?你想多了!所以這種情況我們就需要遍歷一遍資料,找出最大值與最小值,求出資料範圍。範圍 = 最大值 - 最小值+1。 例如,2000-1001+1 = 1000,這樣我們就開闢0~1000個空間,用1代表1001,1000代表2000。節省了大量的空間。

③肯定有同學想到用點陣圖(BitMap)來做,但是點陣圖(BitMap)有侷限性,它要求每個資料只能出現一次。演算法有些複雜,但是可以嘗試。

時間複雜度分析:綜上所述,我們總需要兩遍遍歷,第一遍統計字資料出現次數,遍歷原資料,複雜度為O(N),第二遍遍歷雜湊表,向原空間寫資料,遍歷了範圍次(range),時間複雜度為O(range),所以總的時間複雜度為O(N+range)。

空間複雜度分析:開闢了範圍(range)大小的輔助雜湊表,所以空間複雜度為O(range)。

下面我對一組資料進行排序做出圖解:


c++程式碼實現:

void CountSort(int *a, int n)
{
	assert(a);

	int max = a[0];
	int min = a[0];
	//選出最大數與最小數,確定雜湊表的大小
	for (int i = 0; i < n; ++i)
	{
		if (a[i] > max)
		{
			max = a[i];
		}
		if (a[i] < min)
		{
			min = a[i];
		}
	}

	int range = max - min + 1;
	int *count = new int[range];
	memset(count, 0, sizeof(int)*range);//當初始化成0或-1的時候可以用memset,其他情況均用for迴圈
	/*for (int i = 0; i < range; ++i)
	{
		count[i] = 0;
	}*/
	for (int i = 0; i < n; ++i)
	{
		count[a[i] - min]++;
	}
	//將資料重新寫回陣列
	int j = 0;
	for (int i = 0; i < range; ++i)
	{
		
		while ((count[i]--) > 0)
		{
			a[j] = i + min;
			j++;
		}
	}
}


2、基數排序

基數排序是基於資料位數的一種排序演算法,什麼叫排序位數呢?個,十,百,千,萬...明白了吧!。

它有兩種演算法

①LSD--Least Significant Digit first  從低位(個位)向高位排。

②MSD-- Most Significant Digit first 從高位向低位(個位)排。

排序原理:

因為是按位進行排序,資料可能不統一,有的最大位為十位,有的最大位為百位。我們需要找到最大的位數來進行決定迴圈的次數。

LSD和MSD的兩種演算法思想一致,下面以一組資料用LSD進行排序:


在上圖中最大的位數為4位,所以我們要對個,十,百,千,四位各進行一次迴圈排序。下面以個位為例進行示範。

①我們知道無論個十百千萬哪個位,都只有10種情況,0-9,所以我們先建立一個大小為10 的雜湊表,統計出每個位各有多少資料。例如上圖資料中個位為0的出現1次,個位為1的出現2次,個位為2的出現1次,個位為3的出現2次,個位為9的出現1次。所以雜湊表建立後得:


②再得出次數之後,我們需要開闢一個輔助空間tmp,並按0~9的順序將其重新寫入輔助空間,但是怎麼寫呢?這裡我們就需要用到矩陣轉置的思想了,再開闢一個大小為10 的陣列,用來記錄每個位(0~9)上的資料重新寫入輔助空間時的起始下標。

例如:

0位有1個數據,但是0位之前再其他位,所以0位的起始下標就為0。

1位有2個數據,1位之前有個0位,並且0位有一個數據,所以1位的起始下標為1。

2位有1個數據,2位之前有兩個位(0和1),所以2位之前一共有3個數據,所以2位的起始下標就為3。

由上面刻得出,每個位的起始下標就等於每個位之前的總資料個數之和。

所以得出起始下標陣列為:


③現在起始下標得到了,我們就可以遍歷原陣列往輔助空間裡寫資料了,但是有一點要注意,每寫入一個數據,起始下標陣列相應位的起始下標就要加1。最後得:


到這裡,我們的資料還在輔助空間tmp內,我們需要將其重新寫回我們原資料空間。這樣我們一趟排序就完成了,其他十位,百位,千位的排序只要在低位排序的基礎上進行上面相似的排序即可。

時間複雜度分析:排序最大位數次,每次都要對陣列進行一次排序,所以時間複雜度為O(N*最大位數)。

空間複雜度分析:建立與原陣列同樣大小的輔助空間,再無其他空間開銷,所以空間複雜度為O(N)。

實現程式碼:

//基數排序
//LSD  先以低位排,再以高位排
//MSD  先以高位排,再以低位排
void LSDSort(int *a, int n)
{
	assert(a);
	//求出其最大位數  個 十 百 千 萬....
	int digit = 0;
	int bash = a[0];
	for (int i = 0; i < n; ++i)
	{
		while (a[i] > (pow(10,digit)))
		{
			digit++;
		}
	}
	int flag = 1;
	for (int j = 1; j <= digit; ++j)
	{
		//建立陣列統計每個位出現數據次數
		int Digit[10] = { 0 };
		for (int i = 0; i < n; ++i)
		{
			Digit[(a[i] / flag)%10]++;
		}
		 //建立陣列統計起始下標
		int BeginIndex[10] = { 0 };
		for (int i = 1; i < 10; ++i)
		{
			BeginIndex[i] = BeginIndex[i - 1] + Digit[i - 1];
		}
		//建立輔助空間
		int *tmp = new int[n];
		memset(tmp, 0, sizeof(int)*n);//初始化
		//將資料寫入輔助空間
		for (int i = 0; i < n; ++i)
		{
			int index = (a[i] / flag)%10;
			tmp[BeginIndex[index]++] = a[i];
		}
		//將資料重新寫回原空間
		for (int i = 0; i < n; ++i)
		{
			a[i] = tmp[i];
		}
		flag = flag * 10;
		delete[] tmp;
	}
}


相關推薦

比較排序-----計數排序基數排序

排序總歸來說可分為兩大類,比較排序與非比較排序。比較排序就是我們常用到的氣泡排序,插入排序,希爾排序,選擇排序,堆排序,快速排序,歸併排序。非比較排序不常用,但是在對一些特殊的情況進行處理時,它的速度反而更快。 1、計數排序   排序原理:利用雜湊的方法,將每個資料出現的

線性排序演算法 --- 計數排序基數排序排序

計數排序應用: J - Jeronimo's List  Gym - 101466J  http://codeforces.com/gym/101466/problem/J 線性排序演算法計數排序應該挺好理解的,每次把數字出現的次數記錄下來,然後做成字首,字首就是

排序演算法(計數排序基數排序排序

       1. 計數排序             思想:對於有限個一定範圍內的整數,我們可以採用遍歷的方式得出每一個數的個數,放入相應的值所對應的下標陣列的位置中,再通過將原陣列從後往前遍歷,並對照與 計數陣列之間的關係,將原陣列資料排好序放入新的陣列中。一般情況下k&

【演算法導論】8.線性時間排序計數排序基數排序排序

三種線性時間複雜度的排序演算法:計數排序,基數排序,桶排序。 8.1排序演算法的下界 給定兩個元素ai和aj,如果使用比較排序,可以有ai<aj,ai<=aj,ai=aj,ai>aj,ai>=aj五種操作來確定其相對次序。本節假設所有的比較採用ai<=aj形

九種經典排序演算法詳解(氣泡排序插入排序選擇排序快速排序歸併排序排序計數排序排序基數排序

綜述 最近複習了各種排序演算法,記錄了一下學習總結和心得,希望對大家能有所幫助。本文介紹了氣泡排序、插入排序、選擇排序、快速排序、歸併排序、堆排序、計數排序、桶排序、基數排序9種經典的排序演算法。針對每種排序演算法分析了演算法的主要思路,每個演算法都附上了虛擬

計數排序排序基數排序

計數排序->桶排序->基數排序,三者的排序思想是相通的,是逐漸複雜,使用性更廣的.(個人理解,歡迎指正,wiki上說基數排序是桶排序的變形,兩者的方法相似,都是分配和收集.百度百科說基數排序又叫bucket sort,基數在這裡個人理解就是基座,如果當前數的要分

排序計數排序基數排序

桶排序: 要排序的資料有n個,均勻地劃分到m個桶內,每個桶裡就有k=n/m個元素。每個桶內部使用快速排序,時間複雜度為O(k*logk)。m個桶排序的時間複雜度是O(m*k*logk),因為k=n/m,所以整個桶排序的時間複雜度就是O(n*log(n/m))。當桶個數m接近

面試題22——編碼實現堆排歸併排序基數排序

堆排(大根堆): void adjust(int*arr,int start,int end) { int tmp=arr[start]; for(int i=2*start+1;i<=end;i=2*i+1) { if(i<end&&arr[i]&

順序表儲存實現氣泡排序選擇排序插入排序希爾排序基數排序

#include<iostream> using namespace std; const int MAXSIZE = 100; typedef int ElemType; struct Data { ElemType key; // int shu

Comparator比較器的使用Map排序

專案開發過程中,總會遇到各種沒有遇見過的需求,今天遇到了一個map排序問題。 Map<String,Object> testMap = new HashMap<String,Object>(); KEY 值的規則是這樣的: xx

排序(冒泡插入希爾歸併快排選擇排序排序基數排序雞尾酒排序

穩定的排序 時間複雜度 空間複雜度 氣泡排序 最差、平均O(n^2),最好O(N) 1 雞尾酒排序(雙向氣泡排序) 最差、平均O(n^2),最好O(N) 1 插入排序 最差、平均O(n^2),最好O(N) 1 歸

氣泡排序插入排序基數排序互動排序演算法

一、實驗內容 1、輸入2-10個不為零的正整數,遇到0,代表輸入結束。 2、數字選擇排序方法,1-氣泡排序、2-插入排序、3-基數排序。 3、使用所選排序方法的排序,結果輸出所用方法以及結果,每個數之間用“,”隔開,中間不要有空格。 //===========

八大排序算法之基數排序

輸出 bsp 交換 and del 當前 print [] radixsort 設計思想   它是根據關鍵字中各位的值,通過對排序的N個元素進行若幹趟“分配”與“收集”來實現排序的。它不要比較關鍵字的大小。   假設:R {50, 123, 543, 187, 49, 30

排序算法之基數排序

java 算法 排序 根據維基百科,基數排序的定義為: 基數排序(英語:Radix sort)是一種非比較型整數排序算法,其原理是將整數按位數切割成不同的數字,然後按每個位數分別比較。由於整數也可以表達字符串(比如名字或日期)和特定格式的浮點數,所以基數排序也不是只能使用於整數。 基數排序的思路是

排序演算法10——圖解基數排序(次位優先法LSD和主位優先法MSD)

排序演算法1——圖解氣泡排序及其實現(三種方法,基於模板及函式指標) 排序演算法2——圖解簡單選擇排序及其實現 排序演算法3——圖解直接插入排序以及折半(二分)插入排序及其實現 排序演算法4——圖解希爾排序及其實現 排序演算法5——圖解堆排序及其實現 排序演算法6——圖解歸併排序及其遞迴與非

基數排序與鏈式基數排序

折騰資料結構實驗的時候遇到這一個演算法。之前對此不甚瞭解,總算也找到一篇不錯的文章。轉載如下 基數排序 (Radix Sort) 基數排序是採用“分配”與“收集”的辦法,用對多關鍵碼進行排序的思想實現對單關鍵碼進行排序的方法。 多關鍵碼排序: 1.以撲克牌排序為例。每張

內部排序演算法5(基數排序

基數排序 多排序碼排序的概念 如果每個元素的排序碼都是由多個數據項組成的組項,則依據它進行排序時就需要利用多排序碼排序。實現多排序碼排序有兩種常用的方法,最高位優先(Most Significant Digit (MSD) First)和最低位優先(Le

比較排序計數排序基數排序

非比較排序 與插入、希爾、快速、歸併、堆排序等等排序方式不同的是 以上這些排序演算法都涉及到待排序列中元素值的比較。 然而也有不需要比較的排序演算法。 計數排序 計數排序主要思想: 給定一組要排序的序列,找出這組序列中的最大值,然後開闢一個最

比較排序計數排序、桶排序基數排序

一:什麼是非比較排序 眾所周知,排序有很多種辦法,其中無非就是比較陣列中的值,另一種就是一種分配的思想,類似於一個蘿蔔一個坑,這也就是非比較排序。二:有哪些非比較排序呢? 可以分為三大類 計數排序 、 基數排序 、 桶排序 。這是比較常見的三種排

排序演算法(六)比較排序----計數排序基數排序

前邊的幾篇文章介紹的幾種排序演算法都是比較排序,接下來的文章將會介紹兩種非比較排序。 計數排序: 計數排序通過雜湊的方法將一組資料對映到一個數組裡,最後將陣列中的數依次讀取,並寫進原來的陣列,讀出的資