一、题目
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,所以
1>用计数排序按第一个字母的大小进行排序分组,将字母按照asc码对应于1-26个数字,其中空字母为0小于其他任何字母,
2>然后对每一个分组按照下一位字母递归调用第一步,直到比较完成。
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);