0. Thanks
1. 概述
有這樣的一道題目,給出一定範圍的1億個資料(N=<資料<=M),要求給他從小到大排序
顯然這個涉及到超大資料的排序。一般有兩個套路:一個用堆排序,一個是用點陣圖排序。這裡說
一下點陣圖排序。
2. 原理
點陣圖排序其實是用資料的下標作對映到對應的資料。假如現在有一個待排序的資料:
int[] a = {4,7,2,5,3};
我們需要先知道這些資料的取值範圍,我們看到資料是<8,那麼我們初始化8個bit位的陣列:
並把他們初始化為零。每一個bit位的取值是0,或者1。
然後把每一個的待排序的數字取出來,根據數字的大小把bit陣列的對應下標的bit置為1.
到最後會變成這樣:
然後,我們從第0未bit開始列印非0位的下標,也就是:23457
,也就排好序了。
3. Java來實現一下
基本的資料型別是沒有bit,最小是byte,所以我們先實現一個bit陣列這樣的一個資料結構:
/**
* 這裡,先實現一個位陣列的資料結構
*/
public static class BitArr {
private int bitLength = 0;
private byte[] bytes;
public byte[] getBytes() {
return bytes;
}
/**
* 構建多少位的位陣列
* @param bitLength 位長
*/
public BitArr(int bitLength) {
this.bitLength = bitLength;
bytes = new byte[(int) Math.ceil((double) bitLength/7)];
}
/**
* 標記某一個位
* 設定為1
* @param position 位
*/
public void mark(int position) {
if (position>bitLength)
return;
int arrIndex = position/7;
int bitIndex = position%7;
bytes[arrIndex] |= (1 << (6-bitIndex));
}
public void cleanMark(int position) {
if (position>bitLength)
return;
int arrIndex = position/7;
int bitIndex = position%7;
bytes[arrIndex] &= ~(1 << (6-bitIndex));
}
public void printAllBit() {
for (byte aByte : bytes) {
System.out.print(BitArr.Byte2String(aByte));
}
System.out.println();
}
/**
* 列印除符號位的bit
* @param nByte
* @return
*/
private static String Byte2String(byte nByte){
StringBuilder nStr=new StringBuilder();
for(int i=6;i>=0;i--) {
int j=(int)nByte & (int)(Math.pow(2, (double)i));
if(j>0){
nStr.append("1");
}else {
nStr.append("0");
}
}
return nStr.toString();
}
}
複製程式碼
再基於此實現演算法:
public static int[] bitmapSort(int[] arr, int theMax) {
if (arr==null || arr.length==0)
return null;
BitArr bitArr = new BitArr(theMax+1);
for (int anArr : arr) {
bitArr.mark(anArr);
}
int[] result = new int[arr.length];
byte[] bytes = bitArr.getBytes();
int index = 0;
for (int i = 0; i < bytes.length; i++) {
for (int j = 0; j < 7; j++) {
byte temp = (byte) (1<<6-j);
byte b = (byte) (bytes[i] & temp);
if ( b == temp) {
result[index++] = i*7 + j;
}
}
}
return result;
}
複製程式碼
來個驗證:
public static void main(String[] args) {
int[] a = {4,7,2,5,14,3,8,12};
int[] end = bitmapSort(a, 14);
for (int x : end) {
System.out.print(x+",");
}
}
//輸出
2,3,4,5,7,8,12,14,
複製程式碼
有幾個地方需要注意:
- java裡面沒有無符號的型別,所以我們只能用byte的前7位
上面寫的Java實現,其實還有幾個問題:
- 如果我們一開始並不知道,這堆待排序資料的取值範圍怎麼辦?也許可以採取動態擴充陣列
- 如果待排序的資料有小於0的資料呢?
4.總結
點陣圖演算法,其需要一次遍歷整個資料,假如有N個資料,就只是需要遍歷N次,所以時間複雜度
是 O(N)
。但是,其需要額外地開闢記憶體空間,有N個資料,就需要多開闢N bit位的資料,
額外需要:N/8/1024/1024 MB
的空間。假如是一億個資料,那麼大概要:11.92MB
。