8-3变长数据项的排序

一、题目
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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值