常用排序算法记录

一、前言

    简单记录一下常用的算法排序,以便复习

二、快速排序

  主要思想:

  (1)在数据集之中,选择一个元素作为"基准"(pivot)。

  (2)所有小于"基准"的元素,都移到"基准"的左边;所有大于"基准"的元素,都移到"基准"的右边。

  (3)对"基准"左边和右边的两个子集,不断重复第一步和第二步,直到所有子集只剩下一个元素为止。

       代码实现:

function quickSort($arr){
    $length = count($arr);
    $arr_left = array();
    $arr_right = array();
    if($length <= 1){
        return $arr;
    }
    $pivot = $arr[0];
    for($i = 1 ;$i < $length; $i ++){
        if($arr[$i] <= $pivot){
            $arr_left[] = $arr[$i];
        }else{
            $arr_right[] = $arr[$i];
        }
    }
    $arr_left = quick_sort($arr_left);
    $arr_right = quick_sort($arr_right);
    $arr = array_merge($arr_left,array($pivot),$arr_right);
    return $arr;
}

$arr = array(2,1,4,5,3);
var_dump(quickSort($arr));

三、归并排序

  主要思想:

主要是利用 分治法 ;

首先考虑下如何将二个有序数列合并。这个非常简单,只要从比较二个数列的第一个数,谁小就先取谁,取了后就在对应数列中删除这个数。然后再进行比较,如果有数列为空,那直接将另一个数列的数据依次取出即可。

归并排序,就是 将数组分成二组A,B,然后分别将A,B再分成两组,依次类推,当分出来的小组只有一个数据时,可以认为这个小组组内已经达到了有序,然后再合并相邻的二个小组就可以了。这样通过先递归的分解数列,再合并数列就完成了归并排序。

    步骤:

  1. 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列

  2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置

  3. 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置

  4. 重复步骤3直到某一指针达到序列尾

  5. 将另一序列剩下的所有元素直接复制到合并序列尾

  代码实现: 

/**
* mergeSort 归并排序
* 是开始递归函数的一个驱动函数
* @param &$arr array 待排序的数组
*/
function mergeSort(&$arr) {
    $len = count($arr);//求得数组长度
 
    mSort($arr, 0, $len-1);
}
/**
* 实际实现归并排序的程序
* @param &$arr array 需要排序的数组
* @param $left int 子序列的左下标值
* @param $right int 子序列的右下标值
*/
function mSort(&$arr, $left, $right) {
 
    if($left < $right) {
        //说明子序列内存在多余1个的元素,那么需要拆分,分别排序,合并
        //计算拆分的位置,长度/2 去整
        $center = floor(($left+$right) / 2);
        //递归调用对左边进行再次排序:
        mSort($arr, $left, $center);
        //递归调用对右边进行再次排序
        mSort($arr, $center+1, $right);
        //合并排序结果
        mergeArray($arr, $left, $center, $right);
    }
}
 
/**
* 将两个有序数组合并成一个有序数组
* @param &$arr, 待排序的所有元素
* @param $left, 排序子数组A的开始下标
* @param $center, 排序子数组A与排序子数组B的中间下标,也就是数组A的结束下标
* @param $right, 排序子数组B的结束下标(开始为$center+1)
*/
function mergeArray(&$arr, $left, $center, $right) {
    //设置两个起始位置标记
    $a_i = $left;
    $b_i = $center+1;
    while($a_i<=$center && $b_i<=$right) {
        //当数组A和数组B都没有越界时
        if($arr[$a_i] < $arr[$b_i]) {
            $temp[] = $arr[$a_i++];
        } else {
            $temp[] = $arr[$b_i++];
        }
    }
    //判断 数组A内的元素是否都用完了,没有的话将其全部插入到C数组内:
    while($a_i <= $center) {
        $temp[] = $arr[$a_i++];
    }
    //判断 数组B内的元素是否都用完了,没有的话将其全部插入到C数组内:
    while($b_i <= $right) {
        $temp[] = $arr[$b_i++];
    }
 
    //将$arrC内排序好的部分,写入到$arr内:
    for($i=0, $len=count($temp); $i<$len; $i++) {
        $arr[$left+$i] = $temp[$i];
    }
 
}
 
 
//do some test:
$arr = array(4, 7, 6, 3, 9, 5, 8);
mergeSort($arr);
print_r($arr);

  参考资料:http://blog.phpha.com/archives/1683.html

四、堆排序

  主要思想:

  堆积排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆是一个近似完全二叉树的结构,并同时满足堆性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

     (堆可以视为一棵完全的二叉树,完全二叉树的一个“优秀”的性质是,除了最底层之外,每一层都是满的,这使得堆可以利用数组来表示,每一个结点对应数组中的一个元素 )

节点与数组索引关系

对于给定的某个结点的下标i,可以很容易的计算出这个结点的父结点、孩子结点的下标,而且计算公式很漂亮很简约

   代码实现:

/**
 * 使用异或交换2个值,原理:一个值经过同一个值的2次异或后,原值不变
 * @param int $a
 * @param int $b
 */
function swap(&$a,&$b){
    $a = $a^$b;
    $b = $a^$b;
    $a = $a^$b;
}
 
/**
 * 整理当前树节点($n),临界点$last之后为已排序好的元素
 * @param int $n
 * @param int $last
 * @param array $arr
 * 
 */
function adjustNode($n,$last,&$arr){
    $l = $n<<1;   // 左孩子
    if( !isset($arr[$l])||$l>$last ){
        return ;
    }
    $r = $l+1;  // 右孩子
    // 如果右孩子比左孩子大,则让父节点与右孩子比
    if( $r<=$last&&$arr[$r]>$arr[$l] ){
        $l = $r;
    }
    // 如果其中子节点$l比父节点$n大,则与父节点$n交换
    if( $arr[$l]>$arr[$n] ){
        swap($arr[$l],$arr[$n]);
        // 交换之后,父节点($n)的值可能还小于原子节点($l)的子节点的值,所以还需对原子节点($l)的子节点进行调整,用递归实现
        adjustNode($l, $last, $arr);
    }
}
 
/**
 * 堆排序(最大堆)
 * @param array $arr
 */
function heapSort(&$arr){
    // 最后一个算数位
    $last = count($arr);
    // 堆排序中常忽略$arr[0]
    array_unshift($arr, 0);
    // 最后一个非叶子节点
    $i = $last>>1;
    // 整理成最大堆,最大的数放到最顶,并将最大数和堆尾交换,并在之后的计算中,忽略数组最后端的最大数(last),直到堆顶(last=堆顶)
    while(true){
        adjustNode($i, $last, $arr);
        if( $i>1 ){
            // 移动节点指针,遍历所有节点
            $i--;
        }
        else{
            // 临界点$last=1,即所有排序完成
            if( $last==1 ){
                break;
            }
            swap($arr[$last],$arr[1]);
            $last--;
        }
    }
    // 弹出第一个元素
    array_shift($arr);
}

  参考资料:http://www.cnblogs.com/iampeter/p/3223487.html

五、选择排序

  主要思想:

  1. 首先在未排序序列中找到最小元素,存放到排序序列的起始位置

  2. 然后,再从剩余未排序元素中继续寻找最小元素,然后放到排序序列末尾

  3. 以此类推,直到所有元素均排序完毕

  代码实现:

function selectSort( Array $arr){
    $len = count($arr);
    for( $i = 0; $i < $len ; $i ++){
        for($j = $i; $j < $len ; $j ++){
            if( $arr[$i] > $arr[$j]){
                $temp = $arr[$i];
                $arr[$i] = $arr[$j];
                $arr[$j] = $temp;
            }
        }
    }
    return $arr;
}

$arr = array(23,1,45,12,7,19);
var_dump(selectSort($arr));

六、冒泡排序

  主要思想:

  1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个

  2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对;这样,最后的元素应该会是最大的数

  3. 以此类推,针对所有的元素重复以上的步骤,除了最后一个。

  代码实现:

function bubbleSort( Array $arr){
    $len = count($arr);
    for($i = 0; $i < $len - 1; $i ++){
        for($j = 0; $j < $len - 1 - $i ; $j ++){
            if( $arr[$j] > $arr[$j+1]){
                $temp = $arr[$j];
                $arr[$j] = $arr[$j+1];
                $arr[$j+1] = $temp;
            }
        }
    }
    return $arr;
}
$arr = array(23,1,45,12,7,19);
var_dump(bubbleSort($arr));

七、插入排序

   主要思想:

  1. 从第一个元素开始,该元素可以认为已经被排序

  2. 取出下一个元素,在已经排序的元素序列中从后向前扫描

  3. 如果该元素(已排序)大于新元素,将该元素移到下一位置

  4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置

  5. 将新元素插入到该位置中

  6. 重复步骤2

  代码实现:

function insertSort(&$arr){
    $len = count($arr);
    // 默认下标为0的是已排好序的
    for($i = 1; $i < $len ; $i ++){
        $insertValue = $arr[$i]; // 待插入数据
        $insertIndex = $i - 1;
        while($insertIndex >= 0 && $insertValue < $arr[$insertIndex]){
            // 数组后移
            $arr[$insertIndex + 1] = $arr[$insertIndex];
            $insertIndex -- ;
        }
        $arr[$insertIndex + 1] = $insertValue;
    }
}
$arr = array(23,1,45,12,7,19);
insertSort($arr);
var_dump($arr);

八、希尔排序

  主要思想:

  希尔排序,也称递减增量排序算法,是插入排序的一种高速而稳定的改进版本。

  希尔排序的大体思路是:将一个未排序的序列分成多个组,然后在组内使用插入排序对组内序列排序。我们的分组方式是将每隔固定位置(增量)的元素分成一组。之后去调整这个间隔大小,重新分组,组内重新排序。直到分组的间隔为1,也就是所有的元素分成一组,再进行一次插入排序,这样就可以完成整个序列的排序过程。

  分组序列称之为希尔排序的增量序列,增量序列有一个非常流行的选择称之为希尔增量(希尔这个人建议的增量序列):假设序列为ht...hk hk-1 .. h1 ,排序序列的长度为N,那么ht = N除以2的商, 而hk-1 = hk 除以2的商,直到h1 = 1;
例如如果待排序数组的长度为 10,那么序列为 5, 2, 1; 

  代码实现:

function shellSort(&$arr) {
    $len = count($arr);
    //确定增量序列
    //php内没有整除 采用floor向下去整
    for($inc=floor($len/2); $inc>=1; $inc=floor($inc/2)) {
        //内部实现与插入排序类似
        //不过比较的元素取决于增量
        for($i=$inc; $i<$len; ++$i) {
            $tmp = $arr[$i];
            for($j=$i-$inc; $j>=0 && $tmp<$arr[$j]; $j-=$inc) {
                $arr[$j+$inc] = $arr[$j];
            }
            $arr[$j+$inc] = $tmp;
        }
    }
}
$arr = array(23,1,45,12,7,19);
shellSort($arr);
var_dump($arr);

  参考资料:http://bbs.itcast.cn/thread-7315-1-1.html

原文地址:https://www.cnblogs.com/fanfan259/p/4517428.html