算法练习--归并排序排列题

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法练习--归并排序排列题相关的知识,希望对你有一定的参考价值。

背景:

  这几天玩的有点多了,代码敲少,今天补一发练习,顺便把前两天做的一个题也贴上。

正题:

  1、归并排序

  概念(来源百度百科):归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。

  我的理解,很简单,就是把一个无序数组进行分组,两两组作比较,组别的元素数目为 2^i(i=0,1,2,...,n)的增长。直到最后只有一个完全有序组。

  代码(感觉写的十分屎):

<?php
function merging_pass($list , $i)
{
    //设置第二个组开始位置
    $j = $i;

    //设置第一个组开始位置
    $k = 0;

    //设置新数组下标
    $start = $k;

    //当发现某一小组没有与之匹配的小组时退出
    while($list[$j] != null)
    {    
        //设置一趟归并中每两组的结束
        $j_end = $j+$i;                            
        $k_end = $k+$i;

        while($j<$j_end && $k<$k_end)
        {
            if($list[$j] === null || $list[$i] === null)
            {
                break;
            }
            if($list[$j] < $list[$k])
            {
                $new_list[$start++] = $list[$j++];
            }
            else
            {
                $new_list[$start++] = $list[$k++];
            }
        }

        while($j<$j_end && $list[$j] != null)
        {
            $new_list[$start++] = $list[$j++];
        }
        while($k<$k_end && $list[$k] != null)
        {
            $new_list[$start++] = $list[$k++];
        }
        $k = $j;
        $j += $i;
    }

    //如果存在单独的小组,直接复制到新数组
    while($list[$k] != null)
    {
        $new_list[$start++] = $list[$k++];
    }
    print_r($new_list);echo "<br>";
    return $new_list;
}


function merging_sort(&$list)
{
    $count = count($list);
    $i = 1;
    while($i < $count)
    {
        $list = merging_pass($list , $i);
        $i *= 2;
    }
}

//test
$list = array(34,39,31,20,50,10,14,28,17);
print_r($list);echo "<br>";
merging_sort($list);

/*
Array ( [0] => 34 [1] => 39 [2] => 31 [3] => 20 [4] => 50 [5] => 10 [6] => 14 [7] => 28 [8] => 17 ) 
Array ( [0] => 34 [1] => 39 [2] => 20 [3] => 31 [4] => 10 [5] => 50 [6] => 14 [7] => 28 [8] => 17 ) 
Array ( [0] => 20 [1] => 31 [2] => 34 [3] => 39 [4] => 10 [5] => 14 [6] => 28 [7] => 50 [8] => 17 ) 
Array ( [0] => 10 [1] => 14 [2] => 20 [3] => 28 [4] => 31 [5] => 34 [6] => 39 [7] => 50 [8] => 17 ) 
Array ( [0] => 10 [1] => 14 [2] => 17 [3] => 20 [4] => 28 [5] => 31 [6] => 34 [7] => 39 [8] => 50 ) 
*/
?>

   归并排序是一个稳定的的排序,但是相对其他排序,空间复杂度高一些,需要定义一些开始结束标志,和新数组。但是时间复杂度和堆排序是一样的O(nlog2n)。相对而言,归并排序的思想也较好理解。所以自我感觉归并排序是一种很不错的排序方式,不过究竟如何很好的排序,还是要看序列的特征来说啦。(大小排序问题,就告一段落了)

 

  2、字符排序

            技术分享

    这是在网上看到的题目,既然看到了,那就练习一下。

    思路:大家都知道的,最小序列是abcdefghijkl , 最大序列:lkjihgfedcba ,一共有多少种排发?高中生都知道。。用到排列组合的知识,12!(!表示阶乘)。看看样例 2 ,开始的字母不是a,而是h,那么说明至少这个序列已经排在a,b,c,d,e,f,g开头的序列后边了,那么由第一位我们可以得到 7*11!(计算后将h从总字母表中剔除),第二位是g 那再想想,说明这个序列至少排在以a,b,c,d,e,f,作为第二个位置的序列后边了,此时排名就是 7*11!+6*10!(计算后将g从总字母表中剔除)。 以此类推,最后序列:hgebkflacdji 的排名是7*11!+6*10!+4*9!+1*8!+6*7!+3*6!+5*5!+0*4!+0*3!+1*2!+0*1。

    注意:每次单个字母计算后,需从字母表中剔除,因为该字母已选择,序列后面无法再选择该字母。

    代码:

<?php
/*
    获得序列排行。
    @param array 所给序列条数和需要排名的序列
    @param string 基本序列参考

    return array 序列排名
*/
function getRankNum($input , $example)
{
    $time = strlen($example);
    $rank = array_fill(1 , 3 , 1);
    for($j = 1; $j<=$input[0]; $j++)
    {
        
        $tmp = $example;
        $start = 0;
        for($i = $time-1; $i>=1; $i--)
        {
            $word = substr($input[$j] , $start++ , 1);
            $r = strpos($tmp , $word);

            $rank[$j] += factorial($i)*($r);
            $tmp = str_replace("$word" , ‘‘ , $tmp);
        }
    }
    return $rank;
}

/*
    计算阶乘。。
    @param int 需要计算的数字

    return int 阶乘结果
*/
function factorial($i)
{
    $result = 1;
    for(;$i>1;$i--)
    {
        $result *= $i;
    }
    return $result;
}

//test
$example = ‘abcdefghijkl‘;
$input = array(4,‘abcdefghijkl‘,‘hgebkflacdji‘,‘gfkedhjblcia‘,‘abcdefghijlk‘);

print_r(getRankNum($input , $example));

//Array ( [1] => 1 [2] => 302715242 [3] => 260726926 [4] => 2 )
?>

 

以上是关于算法练习--归并排序排列题的主要内容,如果未能解决你的问题,请参考以下文章

证明归并排序输出输入的排列

排序算法-快排归并堆排序-高频题-912. 排序数组

《算法零基础100讲》(第36讲) 排序进阶 - 归并排序

算法练习——归并排序

算法笔记——归并排序及其基础面试题

排序算法-归并排序