简单算法--冒泡排序、快速排序、选择排序及二分法

冒泡排序 是一种简单的交换排序方法;从数列头部(或尾部)开始,进行两两比较,根据大小交换位置,直到最后将最大(小)的数据元素交换到了数列的尾部,然后重复这个过程,直到所有数据元素都排好序。

冒泡排序算法的运作如下:(从后往前)

  1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
  2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
  3. 针对所有的元素重复以上的步骤,除了最后一个。
  4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

实现代码如下:

// 冒泡排序
function bubble_sort(&$arr){
    $len = count($arr);
    for ($i=0, $i < $len; $i++) {
        for ($j=1; $j < $len-$i; $j++) {
            if ($arr[$j-1] > $arr[$j]) {
                $temp = $arr[$j-1];
                $arr[$j-1] = $arr[$j];
                $arr[$j] = $temp;
            }
        }
    }
}

function bubbleSort($arr) {
    $cnt = count($arr);
    for ($i = 0; $i < $cnt; $i++) {
        for ($j = 0; $j < $cnt-$i-1; $j++) {
            if ($arr[$j] > $arr[$j+1]) {
                $temp = $arr[$j];
                $arr[$j] = $arr[$j+1];
                $arr[$j+1] = $temp;
            }
        }
    }

    return $arr;
}

function maoPaoSort($arr){
    
    if(!is_array($arr)) return false;
    
    $len = count($arr);

    if($len <= 1) return $arr;

    for($i=0;$i<$len;$i++){
        for($j=$len-1;$j>$i;$j--){
            if($arr[$j]<$arr[$j-1]){
                $temp = $arr[$j-1];
                $arr[$j-1] = $arr[$j];
                $arr[$j] = $temp;
            }
        }
    }

    return $arr;
}
 
// 测试
$arr = array(10,2,36,14,10,25,23,85,99,45); 
$newArr = bubbleSort($arr);
print_r($newArr);
bubble_sort($arr);
print_r($arr);

知识点:php参数前加 & 是什么意思?

加上&的意思是值引用,也就是引用传递,在引用过程中修改参数的数值,能在函数外产生效果。

快速排序 是对冒泡排序的一种改进;它的基本思想是:通过一趟快速排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

算法是通过分治递归来实现的,其效率很大程度上取决于参考元素的选择,可以选择数组的中间元素,也可以随机得到三个元素,然后选择中间的那个元素(三数中值法)。
另外还有一点,就是当我们在分割时,如果分割出来的子序列的长度很小的话(小于5到20),通常递归的排序的效率就没有诸如插入排序或希尔排序那么快了。因此可以会去判断数组的长度,如果小于10的话,直接用插入排序,而不再递归调用这个快速排序。

冒泡排序算法的运作如下:

    设要排序的数组是A[0]……A[N-1],首先任意选取一个数据(通常选用数组的第一个数)作为关键数据,然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面,这个过程称为一趟快速排序。值得注意的是,快速排序不是一种稳定的排序算法,也就是说,多个相同的值的相对位置也许会在算法结束时产生变动。
 一趟快速排序的算法是:
    1)设置两个变量i、j,排序开始的时候:i=0,j=N-1;
    2)以第一个数组元素作为关键数据,赋值给key,即key=A[0];
    3)从j开始向前搜索,即由后开始向前搜索(j--),找到第一个小于key的值A[j],将A[j]和A[i]互换;
    4)从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的A[i],将A[i]和A[j]互换;

    5)重复第3、4步,直到i=j; (3,4步中,没找到符合条件的值,即3中A[j]不小于key,4中A[i]不大于key的时候改变j、i的值,使得j=j-1,i=i+1,直至找到为止。找到符合条件的值,进行交换的时候i, j指针位置不变。另外,i==j这一过程一定正好是i+或j-完成的时候,此时令循环结束)。

<?php
function quickSort($arr){
    if(count($arr)>1){
        $k=$arr[0];
        $x=array();
        $y=array();
        $_size=count($arr);
        for($i=1;$i<$_size;$i++){
            if($arr[$i]<=$k){
                $x[]=$arr[$i];
            }elseif($arr[$i]>$k){
                $y[]=$arr[$i];
            }
        }
        $x=quickSort($x);
        $y=quickSort($y);
        return array_merge($x,array($k),$y);
    }else{
        return$arr;
    }
}

function kuaiSuSort($arr){
    if(!is_array($arr)) return false;

    if(count($arr)<=1) return $arr;

    $pivot = $arr[0];
    $big_arr  = array();
    $small_arr = array();

    for ($i=1; $i < count($arr) ; $i++) { 
        if($arr[$i] >= $pivot){
            $big_arr[] = $arr[$i];
        }else{
            $small_arr[] = $arr[$i];
        }
    }
    
    $small_arr = kuaiSuSort($small_arr);
    $big_arr = kuaiSuSort($big_arr); 

    return array_merge($small_arr,array($pivot),$big_arr);
}

// 测试
$arr = array(10,2,36,14,10,25,23,85,99,45);
$newArr = quickSort($arr);
print_r($newArr);
?>


选择排序法 是对冒泡排序法的一种改进。选择排序的基本思想是:每一趟在n-i+1(i=1,2,…n-1)个元素中选取最小的元素作为有序序列中第i个元素。基于此思想的算法主要有简单选择排序、树型选择排序和堆排序。

简单选择排序的基本思想:第1趟,在待排序元素[1]~[n]中选出最小的元素,将它与[1]交换;第2趟,在待排序元素[2]~[n]中选出最小的元素,将它与[2]交换;以此类推,第i趟在待排序元素[i]~[n]中选出最小的元素,将它与[i]交换,直到全部排序完毕。

<?php
//选择排序
function SelectSort($arr){  
    $length = count($arr);  
    if($length<=1){  
        return $arr;  
    }  
    for($i=0;$i<$length;$i++){  
        $min = $i;  
        for($j=$i+1;$j<$length;$j++){  
        	//如果当前最小的还比当前的元素大
            if($arr[$min] > $arr[$j]){  
                $min = $j; //赋值新的最小的
            }  
        }  
        // 将当前内循环的最小元素放在$i位置上
        if($min != $i){  
            $tmp = $arr[$i];  
            $arr[$i] = $arr[$min];  
            $arr[$min] = $tmp;  
        }  
    }  
    return $arr;  
}  
?>


二分查找(又叫折半查找)适用于有序数列(要求数组已经排好顺序)

<?php
/**
 * @param $array 数组
 * @param int $low  数组的最小下标
 * @param int $high 数组的最大下标 
 * @return $k 要查询的元素
 */
function bin_search_recursive($array,$k,$low=0,$high=0){
    // 判断是否为第一次调用,获取数组元素的数量
    if(count($array)!=0 and $high==0){      
        $high = count($array);
    }

    if($low <= $high){      
        $mid = intval(($low+$high)/2); //取$low 与$high的中间值
        if($array[$mid] == $k){
            return $mid;    //如果找到则返回
        }elseif($k < $array[$mid]){
            return bin_search_recursive($array,$k,$low,$mid-1);
        }else{
            return bin_search_recursive($array,$k,$mid+1,$high);
        }
    }

    return -1;
}
/*
*  //while循环实现二分法查找
*/
function bin_search_while($array,$k){
	$low = 0;
    $high = count($array)-1;// 使用下标,注意减去1
	
	while($low <= $high){
	    
	    $mid = intval(($low+$high)/2);

	    if($array[$mid] == $k){
	        return $mid;
	    }elseif($array[$mid] < $k){
	        $low = $mid+1;
	    }else{
	        $high = $mid-1;
	    }
	}

    return -1;// 找不到的时候返回-1  
}

$arr = array(9,15,34,76,25,5,47,55);
sort($arr);
print_r($arr);
echo "<br />";
// 测试:顺序查找
echo seq_sch($arr,47);
echo "<br />";
//测试:二分查找
echo bin_search_recursive($arr,47);
echo "<br />";
echo bin_search_while($arr,47);
?>

猴子选大王(约瑟夫环问题)

<?php
/**
 * @param int $n 开始时的猴子数量
 * @param int $m 指定数组,(报到这个数的猴子被淘汰,然后下一个猴子重新从①开始报数) 
 * @return int 猴子的初始编号 
 */
function select_king($n,$m)
{
 	//猴子的初始数量不能小于2
 	if ($n<2) { return false; }
 
 	//定义一个数组, 数组的值对应猴子的初始编号
 	$mokey_arr = range(1,$n);
 
 	//定义一个变量,记录猴子的报数
 	$unsetNum=0;
  
 	for ($i = 2; $i <=$n*$m ; $i++) //总的循环次数不知道怎么计算,
 	{
 		//不过因为循环中设置了return,所以$m*$len效率还可以
 		foreach ($mokey_arr as $k => $v) {
  			$unsetNum++; //每到一个猴子, 猴子报数+1
  
		 	//当猴子的报数等于淘汰的数字时:淘汰猴子(删除数组元素)
		 	//报数归0(下一个猴子从1开始数)
		  	if ($unsetNum == $m) {
				//  echo "<pre>";//打开注释,可以看到具体的淘汰过程
				//  print_r($mokey_arr);
		  		unset($mokey_arr[$k]); //淘汰猴子  
		  		$unsetNum=0; //报数归零
  				if (count($mokey_arr)==1) {
  					//判断数组的长度, 如果只剩一个猴子, 返回它的值
				   	return reset($mokey_arr);
				}
  			}
 		}
 	}
}

// 方案一,使用php来模拟这个过程
function getKing($n,$m){
    $arr=range(1,$n);         
    $i= 0;
    while(count($arr)!=1){
         
        if(($i+1)%$m==0){
            unset($arr[$i]);
        }else{ 
            $arr[]=$arr[$i];  //array_push()也是调用这个
            unset($arr[$i]);
        }

        $i++;
    } 
    return $arr[$i];
}

// 方案二
function selectKing($n,$m){

    $mokey = range(1, $n);//创建数组,有$n只猴子
    $i = 0;

    while (count($mokey) >1) {
        $i += 1;
        $head = array_shift($mokey);//一个个出列最前面的猴子
        if ($i % $m !=0) {
            //如果不是m的倍数,则把猴子返回尾部,否则就抛掉,也就是出列
            array_push($mokey,$head);
        }
    }

    return $mokey[0];//剩下的最后一个就是大王了
}

//方案二,使用数学方法解决
function SelectKingByMath($n,$m) { 
  $r = 0; 
  for($i=2; $i<=$n; $i++) {
 
      $r = ($r+$m)%$i; 
  }
  return $r+1; 
} 

// 测试
print_r(select_king(10,7));
echo '<br/>';
print_r(SelectKing(10,7));
echo '<br/>';
print_r(SelectKingByMath(10,7));
?>

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值