PHP資料結構(二十六) ——基數排序實現36進位制數排序
PHP資料結構(二十六)——基數排序實現36進位制數排序
(原創內容,轉載請註明來源,謝謝)
一、概述
插入排序、選擇排序、快速排序等,都是通過關鍵字之間的比較和移動進行的。基數排序完全不同,其是藉助多個關鍵字排序的思想對單邏輯關鍵字進行排序的方法。
所謂多關鍵字,可以理解為帶權值的關鍵字。例如:
現有序列{a0,a1,a2,a3,b0,b1,b2,b3},假設a<b,數字按數字正常的大小。現要求對這個序列進行排序,但是要求數字的優先順序更高,即a0<b0<a1<b1。則這種排序可以認為是多關鍵字的排序。
1、序列對關鍵字有序的定義
假設序列{r1,r2…rn},每個ri均有d個關鍵字(k1,k2…kd)。當ri排在rj前面時,ri對應的任意ki都比rj對應的任意kj小(或大)。則成為序列按關鍵字有序。
2、排序的兩種方式
1)最高位優先法(MSD法)
先按最高位排好,再排次高位,直至最低位。按上面例子,先按照數字排好,再在排好的序列中去排字母的順序。
2)最低位優先法(LSD法)
先按最低位排好,再排次低位,直至最高為。按上面例子,先按字母排好,根據字母個數分成x組,再各組之間互相比較高級別的關鍵字。
3)比較
MSD法必將序列分割成若干個子序列,然後對子序列進行排序。
LSD法不用將內容進行分割,每次都是整個序列參加排序,但是對除了最底層以外的排序外,其他排序必須用穩定的排序。另外,也可以通過“分配”和“收集”的方式進行排序。
二、鏈式基數排序
鏈式基數排序,採用佇列+連結串列的方式,將整個序列用連結串列串起來,頭指標指向序列的第一個元素。接著採用LSD法,先遍歷最後一個元素,當元素有n種時,同時使用n個指標(例如對數字遍歷,則同時用10個指標,指向0-9),指向n1,n2…n為結尾的。接著再遍歷第二次,直至遍歷n次,串起來的即為排好序的內容。
1、演算法
1)根據輸入結果的位數,判斷輸入的元素有幾位數,對於不足最長位數的,開頭的地方進行補全,值設定為最小結果。(例如三位數字中,出現兩位數,則第一位補0)
2)分析輸入的數字,區分一共有幾種內容。用於後面設定指標,不需要設定最大指標,可以根據實際動態設定。(例如三位字母數字混合字串比較,只輸入了a01,b23,a56,則只需要分配指標給a、b、0、1、2、3、5、6,而不需要分配26+10=36個指標)
3)設定一個頭指標,指向序列的第一個元素,並且將第一個元素指向第二個元素,直至將元素串起來。
4)迴圈進行LSD,迴圈次數為元素的最大長度。迴圈做下列5、6兩項內容,直到完成迴圈次數。
5)首先判斷最低位,從頭指標一直往後讀取資料,將不同的最低位以佇列的方式進入表示不同權值的指標。
6)將指標按權值從低到高,按照佇列先進先出的方式,將所有資料再串成序列。
7)完成後,將序列返回,即為排好序的序列。
2、假設3位數進行排序,則共需要3輪,如下圖所示(圖片是資料結構書的內容)
3、程式概述
1)場景
現對序列array('a01','1g5', 'a8j', '13b', 'dx5', 'az', '10', '1cz', '0')進行基數排序,排序要求:從小到大,數字比字母小,0,1,2…9,a,b,c…z為從小到大的順序。另外,比較規則類似於十進位制的比較,如9<10,即雖然0<z,但是10>z。
因此,該程式實現36進位制數的比較。另外,程式的大小比較是通過例項化類時傳的引數進行的,因此,可以根據需要動態的改變比較規則。
2)實現過程
分幾步進行實現。
a.定義一個節點類Node,用做連結串列裡面的節點。
b.定義建構函式和__set()方法,用於設定比較方式。
c.定義函式用於通過使用者輸入的序列,獲取序列元素的最長值。
d.定義函式用於通過使用者輸入的序列,生成包含序列元素下標的陣列,每個下標有一個空陣列,用做指標,在比較期間存放資料。
e.定義函式,根據序列以及c步驟獲取的最大字串長度,生成連結串列。
f.進入迴圈,遍歷連結串列,首先看每個元素的末位,並根據末位的位置放置於d步驟生成的陣列的相應地方。接著將此陣列重組成連結串列。迴圈此步驟,從末位開始一直做到首位。則此時的連結串列已經是按照自定義規則比較的元素從小到大排序的連結串列了。
g.將連結串列轉回成陣列,由於一開始將不足的長度補全,故再次步驟需要將開頭位是最小值的去掉,但是如果全部都是最小值,則留下一個字元。(可以理解成十進位制的0078中的前兩個0去掉,留下78;但是如果是0000則只去掉3個0,留下0)。此陣列即為最終的按自定義規則從小到大比較排序的陣列。
4、程式執行結果
5、程式原始碼
<?php
class Node{ public$data; public$next;}
//基數排序實現類
class SortArrayMulty{
private$arr;//用於比較的陣列
private$type;//排序方式
publicfunction __construct(array $arr = array(), array $type = array()){
$this->arr= $arr;
//預設支援0-9以及小寫a-z,且數字優先順序比字母高
if(empty($type)){
$this->type= array( '0','1', '2', '3', '4', '5', '6', '7', '8', '9', 'a','b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k','l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u','v', 'w', 'x', 'y', 'z' );
}else{
$this->type= $type;
}
}
//設定排序方式
publicfunction __set($prop, $res){
if(!property_exists($this,$prop)){
returnfalse;
}
if(!is_array($res)){
returnfalse;
}
$this->$prop= $res;
}
//主要用於檢視排序方式
publicfunction __get($prop){
if(!property_exists($this,$prop)){
returnfalse;
}
return$this->$prop;
}
//基數排序主方法
publicfunction baseNumSort(array $arr = array()){
if(empty($arr)){
$arr= $this->arr;
}
if(1>= $arr){
return$arr;
}
//判斷位數
$strLength= $this->_getLongestStrLength($arr);
//判斷種類數,並根據type進行排序,返回排好序的陣列array('type1'=>array(),'type2'=>array()...)
$arrTypes= $this->_getStrTypes($arr);
//初始化連結串列,如果長度不足,從前面補最小字元 chainHead{data=null;next={data=val1;next={data=val2,next=....}}}
$chainHead= $this->_initChain($arr, $strLength);
//迴圈次數
$loopNum= 0;
while($loopNum< $strLength){
$findIndex= $strLength - 1 - $loopNum;//第0次查詢第strLength-1位,第1次查詢第strLength-2位,第loopNum次查詢第strLength-1-loopNum位
$curNode= $chainHead->next;
while(null!= $curNode){
$curData= $curNode->data; //獲取當前值
//獲取當前正在比較的字元的值,並根據下標將當前字元值放進相應的陣列
$curWorld= strval($curData[$findIndex]);
array_push($arrTypes[$curWorld],$curData);
$curNode= $curNode->next;
}
//根據生成的陣列指標,重新合成連結串列序列,需要安裝types來進行排序
$chainHead= $this->_rebuildChain($arrTypes);
//將陣列指標的內容情況,僅保留型別
$arrTypes= $this->_cleanSelf($arrTypes);
$loopNum++;
}
//將指標轉回成一維陣列
return$this->_convertChainToArray($chainHead);
}
//獲取序列中字串最長的字元數量
privatefunction _getLongestStrLength(array $arr){
$maxLength= 0;
//如果大於maxlength則賦值給maxlength
foreach($arras $str){
if(strlen($str)> $maxLength){
$maxLength= strlen($str);
}
}
return$maxLength;
}
//判斷序列有哪幾種字元,並根據type進行排序,返回排好序的陣列
privatefunction _getStrTypes(array $arr){
$arrTypes= array();
//遍歷序列
foreach($arras $str){
$curLength= strlen($str);
//遍歷每個字元,沒有則加上
for($i=0;$i<$curLength;$i++){
if(!in_array(strval($str[$i]),$arrTypes)){
$arrTypes[]= strval($str[$i]);
}
}
}
$type= $this->type;
//對$arrTypes按照type進行排序,返回有序內容
$arrRes= array();
foreach($typeas $item){
if(in_array($item,$arrTypes)){
$arrRes[$item]= array();
}
}
return$arrRes;
}
//將序列初始化成連結串列
privatefunction _initChain(array $arr, $strLength){
//定義連結串列頭
$head= new Node();
$tmpNode= $head;
$type= $this->type;
$minWord= $type[0];
foreach($arras $node){
//定義下一個節點
$tmpNode->next= new Node();
$tmpNode= $tmpNode->next;
//如果字元長度不足,則前面不齊最小值
if(strlen($node)< $strLength){
$repeatStr= str_repeat($minWord, $strLength-strlen($node));
$node= $repeatStr . $node;
}
$tmpNode->data= $node;
}
return$head;
}
//將陣列重組成連結串列
privatefunction _rebuildChain(array $arrTypes){
//定義表頭
$head= new Node();
$tmpNode= $head;
//遍歷arrtypes陣列生成連結串列
foreach($arrTypesas $items){
//如果節點為空則遍歷下一個
if(empty($items)){
continue;
}
//遍歷陣列中的元素,按順序拼接回連結串列
foreach($itemsas $item){
$tmpNode->next= new Node();
$tmpNode= $tmpNode->next;
$tmpNode->data= $item;
}
}
return$head;
}
//清空陣列內的元素,僅保留關鍵字
privatefunction _cleanSelf(array $arrTypes){
foreach($arrTypesas $type => $arr){
$arrTypes[$type]= array();
}
return $arrTypes;
}
//將指標轉回成一維陣列
privatefunction _convertChainToArray(Node $head){
$arrRes= array();
$curNode= $head->next;
//獲取最小值
$type= $this->type;
$minWorld= $type[0];
while(null!= $curNode){
//轉回一維陣列需要進行處理,開頭幾位如果有最小值(0)則應該去掉
$minWorldLength= 0;
$data= $curNode->data;
$len= strlen($data);
//如果全部是0,則保留一個0,故只遍歷到$len-2即可
while($minWorldLength<= $len - 2){
if($minWorld!= $data[$minWorldLength]){
break;
}
$minWorldLength++;
}
$data= substr($data, $minWorldLength);
array_push($arrRes,$data);
$curNode= $curNode->next;
}
return$arrRes;
}
}
$arrToSort = array('a01','1g5', 'a8j', '13b', 'dx5', 'az', '10', '1cz', '0');
$sortArrayMuti = newSortArrayMulty($arrToSort);
print_r($sortArrayMuti->baseNumSort());
——written by linhxx 2017.07.21
相關閱讀: