一、排序算法概述

  1. 读过数据结构与算法这本书的应该知道,排序算法有几种最常用最基本的算法,包括:冒泡排序、选择排序、插入排序、希尔排序
  2. 下面使用php来分别讨论下这些排序算法的实现

二、冒泡排序

  1. 冒泡排序:为了依次确定数组每个位置上的正确顺序的元素,通过比较当前位置与循环遍历的元素的大小,然后两个数字进行互换位置,确定每个位置的正确顺序的元素,实现升序或者降序。
  2. 如果是升序,在两个元素互换位置的时候看起来就像较大的那个数字不断的冒泡排到后边。
  3. 此算法中通常有两层循环,外层循环的元素与内层循环的元素进行比较并且互换位置,除了最后一个元素不需要比较。
  4. 内层循环的元素从外层元素的下一个元素开始遍历,将内层的元素分别和外层的元素进行比较,互换位置,直到外层的元素都确定。
  5. 外层循环,每循环一次,就确定一个位置的元素。
  6. 代码:

    $arr = [23,3,43,233,2,1,34,0,21234,344];
    $len = count($arr);
    
    for($i=0; $i<$len-1; $i++){
        for($j=$i+1; $j<$len; $j++){
            if($arr[$j] < $arr[$i]){
                $tmp = $arr[$i];
                $arr[$i] = $arr[$j];
                $arr[$j] = $tmp;
            }
        }
    }
    
    echo '<pre>';
    print_r($arr);

三、选择排序

  1. 选择排序:选择一个数当作最小或者最大值,然后和数组剩下的元素进行比较,互换位置,不断循环选出最小或者最大的值。
  2. 选择排序,重点在于选出一个最小的或者最大的值,外层循环的元素提前当作是最小的值,然后和内层循环的数据进行比较。
  3. 代码:

    $arr = [23,3,43,233,2,1,34,0,21234,344];
    $len = count($arr);
    
    for($i=0; $i<$len; $i++){
        $min = $i;
        for($j=$i+1; $j<$len; $j++){
            if($arr[$j] < $arr[$min]){
                $min = $j;
            }
        }
        $tmp = $arr[$i];
        $arr[$i] = $arr[$min];
        $arr[$min] = $tmp;
    }
    
    echo '<pre>';
    print_r($arr);

四、插入排序

  1. 插入排序:将数组看成左右两边两组数据,其中左边是默认已经排序好的数据,右边就是原来的数据,从右边取出一个数,和左边数据倒着开始依次比较,找到合适的位置,将取出的这个右边的数据插入到左边。
  2. 左右两边数据比较的时候,如果还没找到合适的位置的时候要将左边的进行比较的那个数右移一位,直到找到合适的位置插入。
  3. 代码:

    $arr = [23,3,43,233,2,1,34,0,21234,344];
    $len = count($arr);
    
    for($i=1; $i<$len; $i++){
        $value = $arr[$i];//右边取出的那个数据
        for($j=$i-1; $j>=0; $j--){
            if($value < $arr[$j]){
                $arr[$j+1] = $arr[$j];//左边的数右移一位
            }else{
                break;//找到合适位置直接退出循环
            }
        }
        $arr[$j+1] = $value;
    }
    

五、希尔排序

  1. 希尔排序实际上是优化后的插入排序。
  2. 希尔排序就是设定一个增量gap,每隔gap个元素组成一个组,数组最后可以分成gap个组,然后每个组都进行插入排序,然后再将gap减小,循环进行分组排序减小gap,直到gap为1。
  3. 设置gap->分组->插入排序->减小gap->分组->插入排序->减小gap->...->gap减小到1->分组(按照增量1分组,实际上就是整个数组)->插入排序
  4. 代码:

    $arr = [23,3,43,233,2,1,34,0,21234,344,12];
    $len = count($arr);
    
    for($gap=floor($len/2); $gap>=1; $gap=floor($gap/=2)){//每次gap是上次的一半
        for($i=0; $i<$gap; $i++){//每次循环都将数组分成了gap组
            for($k=$gap; $k<$len; $k+=$gap){//从第gap个开始(插入排序的右边)
                $value = $arr[$k];
                for($j=$k-$gap; $j>=0; $j-=$gap){//插入排序的左边
                    if($value < $arr[$j]){
                        $arr[$j+$gap] = $arr[$j];//移动位置
                    }else{
                        break;
                    }
                }
                $arr[$j+$gap] = $value;
            }
        }
    }

六、快速排序

  1. 快速排序:找一个基准值,将数组每个元素和这个基准值进行比较,分成两组,比基准值大的分到右边,比基准值小的分到左边。
  2. 递归对左右两边的数组再进行快速排序,获取基准值分组,直到无法再分的时候。
  3. 代码:

    $arr = [23,3,43,233,2,1,34,0,21234,344,12];
    $arr = quickSort($arr);
    echo '<pre>';
    print_r($arr);
    
    function quickSort($arr){
        $len = count($arr);
    
        if($len <= 1){
            return $arr;
        }
    
        $left = $right = array();
        $middle = $arr[0];//基准值
    
        for($i=1; $i<$len; $i++){
            if($arr[$i] > $middle){
                $right[] = $arr[$i];
            }else{
                $left[] = $arr[$i];
            }
        }
    
        $left = quickSort($left);
        $right = quickSort($right);
        return array_merge($left,array($middle),$right);
    
    }
    

七、归并排序

  1. 归并排序:实际上是先切分后合并,递归切分与递归合并的算法。
  2. 将数组元素等份分成两份,然后两份分别再等份分成各自的两份,不断的切分,直到不能再分,然后将分开的元素合并起来进行比较,新创建一个和源数组等大的空数组,将较小的元素不断的填充到新的数组中。
  3. 归并排序图解:11.png
  4. 代码:

    $arr = [23,3,43,233,2,1,34,0,21234,344,12];
    $len = count($arr);
    
    Msort($arr,0,$len-1);//归并排序
    echo '<pre>';
    print_r($arr);
    
    function Msort(&$arr,$start,$end){
        $len = count($arr);
        if($start < $end){
            $mid = floor(($start+$end)/2);
            Msort($arr,$start,$mid);
            Msort($arr,$mid+1,$end);//递归切分
            Merge($arr,$start,$mid,$end);//合并小数组为大数组
        }
    }
    
    function Merge(&$arr,$start,$mid,$end){
        $newArray = array();
        $p1 = $start;//左边小数组指向开始的指针
        $p2 = $mid + 1;//右边指向开始的指针,从mid+1开始
        $p = $start;//将新的数组的指针指向start开始
        while ($p1 <= $mid && $p2 <= $end) {
            if($arr[$p1] < $arr[$p2]) {
                $newArray[$p++] = $arr[$p1++];//较小的放到新数组中,并移动p1和p的指针
            }else{
                $newArray[$p++] = $arr[$p2++];//较大的放到新数组中,并移动p2和p的指针
            }
        }
    
        //左边有剩余的元素
        while ($p1 <= $mid) {
            $newArray[$p++] = $arr[$p1++];
        }
    
        //右边有剩余的元素
        while ($p2 <= $end) {
            $newArray[$p++] = $arr[$p2++];
        }
    
        for($i=$start; $i<=$end;$i++){
            $arr[$i] = $newArray[$i];
        }
    }
    

八、堆排序

  1. 堆排序:实际上是利用堆数据结构进行排序。
  2. 将原来的数据构建成堆数据结构,利用完全二叉树的特性,父节点都大于子节点元素。
  3. 构建一次堆结构,那么最大的那个数就在第一个元素上,将第一个与最后一个互换位置,相当于确定了最大的那个数的位置,然后排除最后一个位置的剩下的元素重新构建堆结构,再将第一个元素换到最后,这样不断的重复,直到不能互换为止。
  4. 代码:

    $arr = [23,3,43,233,2,1,34,0,21234,344,12];
    $len = count($arr);
    for($i=0; $i<$len; $i++){
        //每次建大顶堆排除一个元素
        buildBigHeap($arr,$len-$i);
    
        //交换元素
        $tmp = $arr[0];
        $arr[0] = $arr[$len-1-$i];
        $arr[$len-1-$i] = $tmp;
    }
    
    echo '<pre>';
    print_r($arr);
    
    function buildBigHeap(&$arr,$size){
        for($i=$size-1; $i>=0; $i--){
            buildHeap($arr,$i,$size);//从下往上开始循环互换父子节点,形成大顶堆
        }
    
    }
    
    //建立堆,满足父节点大于子节点
    function buildHeap(&$arr,$currentNode,$size){
        if($currentNode < $size){
            $left = 2*$currentNode + 1;
            $right = 2*$currentNode + 2;
            $max = $currentNode;//将当前的节点认为是最大值
    
            if($left<$size && $arr[$left] > $arr[$max]){
                $max = $left;
            }
            if($right < $size && $arr[$right] > $arr[$max]){
                $max = $right;
            }
    
            //将父节点和最大的那个节点互换,保证父节点是最大的数
            if($max != $currentNode){
                $tmp = $arr[$currentNode];
                $arr[$currentNode] = $arr[$max];
                $arr[$max] = $tmp;
    
                //如果最大值的那个节点还有子节点,则继续比较,互换父子位置
                buildHeap($arr,$max,$size);
            }
    
        }
    
    }

九、基数排序

  1. 基数排序:就是获取数组中每个元素的位数上的数字,设置10个0-9序号的桶,将各位数值等于桶序号的元素装入桶中,组成新的序列,然后再获取下一个位数的值分别根据同样的规则放到桶中。
  2. 先获取最大元素的位数,然后循环每个位数,将数据放到桶中组成新的序列即可。
  3. 代码:

    $arr = [23,3,43,233,2,1,34,0,21234,344,12];
    
    //1.获取最大的位数
    $maxW = getMaxW($arr);
    
    //2.循环将每个位数放到是个桶中组成新的序列
    $arr = sortW($arr,$maxW);
    echo '<pre>';
    print_r($arr);
    
    function sortW($arr,$maxW){
        for($i=0; $i<$maxW; $i++){
            foreach ($arr as $value) {
                //取出当前位的值
                $currW = getCurrW($value,$i);
                //将当前的值放到对应的桶
                $bucket[$currW][] = $value;
            }
    
            //桶中的数据组合成新的列表
            $arr = readDataFromBucket($bucket);
            //桶数据清空
            unset($bucket);
        }
        return $arr;//返回新的序列
    }
    
    //获取最大的位数
    function getMaxW($arr){
        $maxW = 0;
        foreach ($arr as $key => $value) {
            $currW = strlen($value);
            if($currW > $maxW){
                $maxW = $currW;
            }
        }
        return $maxW;
    
    }
    
    //获取元素当前位的数字
    function getCurrW($value,$i){
        $tmp = pow(10,$i);
        $v = $value/$tmp;
        $currW = $v%10;
        return $currW;
    }
    
    //读取桶中数据组合新的序列
    function readDataFromBucket($bucket){
        $arr = [];
        for($i=0; $i<10; $i++){
            if(!empty($bucket[$i])){
                foreach($bucket[$i] as $v){
                    $arr[] = $v;
                }
            }
    
        }
        return $arr;
    }

十、算法比较及参考链接

  1. 各算法的比较:各算法比较
  2. 各算法参考链接

繁星落眼眶
626 声望54 粉丝