8-3变长数据项的排序

来源:互联网 发布:免费刷枪软件生死狙击 编辑:程序博客网 时间:2024/05/18 01:29
一、题目
a)给定一个整数数组,其中不同的整数中包含的数字个数可能不同,但是该数组中,所有整数中总的数字数为n。说明如何在O(n)时间内对该数组进行排序
b)给定一个字符串数组,其中不同的串包含的字符个数可能不同,但所有串中总的字符个数为n。说明如何在O(n)时间内对该数组进行排序(注意此处的顺序是指标准的字母顺序,例如,a < ab < b)

二、思路
a、完全用基数排序无法达到O(n)的时间要求,基数排序的总时间复杂度为θ(dn),d为n个整数的最大位数。对于该题,假设有m个数,m≤n,最坏情况下,一个数有n/2位,另外有n/2个数都是一位。那么,d=n/2,m=n/2+1,运行时间为θ(n^2).  假设所有数都是正数且最高位不为0,那么,位数多的数要大于位数少的数。首先用计数排序方法按照位数进行排列,然后再用基数排序对位数相同的数进行排序。
     下面说明为什么时间是O(n):假设mi是位数为i(1,2,...,n)的个数,由于总位数是n,所以

      计数排序所花时间为O(m+n)=O(n),m为数的个数,n为最大位数。

      对i位的数基数排序所花时间为θ(i*mi),基数排序总时间为=θ(n).所以计数排序 加基数排序总需时间为O(n).

b、题目要求排列成标准的字典序,即a<ab<b,
1>用计数排序按第一个字母的大小进行排序分组,将字母按照asc码对应于1-26个数字,其中空字母为0小于其他任何字母,
2>然后对每一个分组按照下一位字母递归调用第一步,直到比较完成。

分析:假设有m个字符串(m<=n),第i个字符串的长度为Li,这个字符串被计数排序存储的次数为Li+1,(+1是因为在某次计数中可能会存储空字符,例如ab和a在进行第二次计数排序时,a会计一个空字符),所以总花费时间为

=O(n+m)=O(n).

三、代码

/*思考题8-3.a * @$A array 输入数组 * @$p int  数组开始下标 * @$r int  数组结束下标 * @$n int  数组数字总位数 */function count_sort_num($A,$p=0,$r=0){if($p==0&&$r==0){$r=count($A)-1;}if($p<$r){$k=10;//$len = count($A);$C = $B = $D = array();for($j=$p;$j<=$r;$j++){$B[$j]=0;}$C[0]=$D[0]=0;$max_digit=0;for($i=$p;$i<=$r;$i++){$digit = strlen($A[$i]);//计算a元素的位数if($digit>$max_digit)$max_digit=$digit;//求出最大位数if(!isset($C[$digit]))$C[$digit]=0;$C[$digit] += 1;//$C[i]记录i位数的个数}for($i=1;$i<=$max_digit;$i++){//该循环执行完后$C[i]是位数<=i的数的个数if(!isset($C[$i])){$C[$i] = 0;}$C[$i] +=$C[$i-1];$D[$i] = $C[$i];}//print_r($C);for($i=$r;$i>=$p;$i--){$digit = strlen($A[$i]);$B[$C[$digit]+$p-1] = $A[$i];$C[$digit]--;}//输出的B是按位数从小到大排序的//print_r($B);for($i=1;$i<=$max_digit;$i++){//i是位数if($D[$i]!=$D[$i-1]){radix_sort($B, $p+$D[$i-1], $p+$D[$i]-1, $i);}}return $B;}}/* * 基数排序 * @$A array 需排序的数字数组 * @$p int 起始下标 * @$r int 终止下标 * @$d int 数字位数 */function radix_sort(&$A,$p,$r,$d){if($r>$p){$len = $r-$p+1;$k=9;for($i=1;$i<=$d;$i++){//对每位数计数排序$C = $B = array();for($j=0;$j<=$k;$j++){$C[$j]=0;}for($j=$p;$j<=$r;$j++){$dignum = substr($A[$j],-$i,1);//第i位数字$C[$dignum] +=1;}for($j=1;$j<=$k;$j++){$C[$j] +=$C[$j-1];}for($j=$r;$j>=$p;$j--){$dignum = substr($A[$j],-$i,1);$B[$C[$dignum]]=$A[$j];$C[$dignum]--;}for($j=$p;$j<=$r;$j++){//把排好序的B赋给A$A[$j] = $B[$j-$p+1];}}}}$a = array(12,1234,34,5657,3,67,456,237,66);print_r(count_sort_num($a));/*8-3.b * 对数组进行字典排序a<ab<b *  *//* * 获取字符串的第l位字母,并相应转换为数字1-26*/function get_letter($str,$l){$letter = strtolower(substr($str, $l,1));return $letter?ord($letter)-96:0;//96是小写a的asc码,把26个字母对应到1-26的数字}/* * 对字符串字典排序8-3.b *  */function count_sort_str(array &$A,$p=0,$r,$l=0){if($p < $r){$k = 27;//26个字母加空元素,所以是27$C = $B = $D = array();for($i=0;$i<$k;$i++){//数组C记录i字母的单词数量$C[$i] = $D[$i] = 0;}//$l = 0;//从第一位字母开始for($j=$p;$j<=$r;$j++){$let = get_letter($A[$j], $l);$C[$let] += 1;}$D[0] = $C[0];for($i=1;$i<$k;$i++){//该循环执行完后C[i]是小于字母i的单词数$C[$i] += $C[$i-1];$D[$i] = $C[$i];}for($j=$r;$j>=$p;$j--){//按第l位字母排序$let = get_letter($A[$j], $l);$B[$C[$let]] = $A[$j];$C[$let]--;}for($j=$p;$j<=$r;$j++){//把排好序的B赋给A$A[$j] = $B[$j-$p+1];} $l++;//$l自加,开始第二位字母for($i=1;$i<$k;$i++){if($D[$k-1]==$D[0])break;if($D[$i]!=$D[$i-1]){//echo $l;echo '</br>';count_sort_str($A, $D[$i-1]+$p, $D[$i]+$p-1, $l);}else continue; }}}//调用主函数// function user(&$A){// $r = count($A)-1;// count_sort_str($A,0,$r,0);// }// $a = array('fa','ac','abc','ba','a','b','mn','bc','aaykhukdf','Bbc');// user($a);//  print_r($a);


0 0
原创粉丝点击