每日一題6:位元影像向量排序,位元影像向量排序
位元影像向量排序有三大優點:1)速度快,複雜度為O (n);2)記憶體需要少;3)排序時間與輸入元素資料無關,但它也有三個很嚴苛的限制:1)整數;2)元素值在一定範圍之內(與機器記憶體有關)3)沒有重複。它的本質是使用位元組的下標來記錄元素值,而數組的值表示該元素下標是否存在,因此達到節省記憶體、加快速度的目標(節省記憶體是針對元素比較密集的情況,過於稀疏的情況未必就節省記憶體)。
比如說要表示包含1-5範圍內的整數的一個集合,只需要一個位元組(8位),而不是20個位元組。但是需要把這八個位看作是一個具有八個元素的數組,如果某個元素為1,那麼表示這個元素的下標在整數集裡,如果為0則表示不存在下標表示的整數。因此1-5的整數可以表示為:
0 1 1 1 1 1 0 0
表示完成後,就可以掃描數組,輸出排序結果。
但是當前的程式語言(除了一些庫)中並沒有bit資料類型,所以需要找到一種方法,進行bit位與選用的資料類型的轉換,為了簡單,可以使用unsigned char類型來表示bit位,每個unsigned char元素c有八位,要判斷第a位是否為1,可以使用該元素與一個只在第a位上為1而在其他位上都為0的數b(八位表示的數)相與,結果為0,表示a位為0,非0,則a位為1。要設定某一位的值,只需將a與b相或即可(原理嘛,用筆在紙上寫寫就知道了)。數b如何求呢,第一位為1其他位為0的數是128,其它位為1的數就可以通過對128進行右移位元運算得到。準備工作完成:
#include "stdafx.h"#include <iostream>using namespace std;//為了使演算法對負數也有效,所以將最小值和最大值也作為輸入void sort(int *array,int value_count,int min_value,int max_value){ if(min_value >= max_value) return; //bit向量的大小,並不是由輸入數組的大小決定,而是由輸入數組 //的元素範圍決定,所以先max_value - min_value + 1計算出 //表示這個範圍需要多少個bit位,但是為了保證分配到的記憶體足夠 //且最少,所以再加7,然後用得到的和除以8,從而計算出表示位元影像 //向量數組的大小 int bit_map_count = (max_value - min_value + 1 + 7) / 8; unsigned char* bit_map = new unsigned char[bit_map_count]; //必須將位元影像向量先初始化為0 memset(bit_map,0,bit_map_count*sizeof(unsigned char)); int* p = array; for (int i = 0; i < value_count; ++i,++p) { //保證value非負,因為數組下標是非負的 int value = *p - min_value; //元素值除以8,得到表示該元素的位元影像向量所在位元組的下標 int index = value >> 3; //求摸則得到元素在位元組中所處的位置 int pos_in_byte = value % 8; //設定相應的位為1 bit_map[index] = bit_map[index] | (128>>pos_in_byte); } p = array; for (int i = 0; i < bit_map_count; ++i) { unsigned char c = bit_map[i]; for (int j = 0; j < 8; ++j) { //判斷相應位是否為1 if( c & (128>>j)) { //恢複輸入元素的原值,並將其添加到輸出數組中 *p = (i<<3) + j + min_value; ++p; } } }}int _tmain(int argc, _TCHAR* argv[]){ int n = 10; int array[] = {0,-1,-2,-3,-4,-5,-6,-7,-8,-9}; for (int i = 0; i < n; ++i) { cout<<array[i]<<' '; } cout<<endl; sort(array,n,-9,98); for (int i = 0; i < n; ++i) { cout<<array[i]<<' '; } cout<<endl;}
程式正確運行,sort函數還有可以改進的地方:將b的值先計算出來,然後就可以把與b相關迴圈展開。