您的位置:首页 > 职场人生

面试常见基本题目总结及php实现(第一部分:排序算法)

2017-09-06 23:38 856 查看
稳定性是指假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变

第一部分:排序算法(下面的都是内部排序,只使用内存的排序算法)

1.插入排序(最坏时间复杂度O(n^2),平均时间复杂度O(n^2)),稳定的

就是把前面的排好,然后把后面的往前面中插入。代码如下所示

<?php
//从小到大
$array=array(12,1,4,2,1,4,6,34,12,2);
$arr=Insertsort($array);
for($i=0;$i<count($arr);$i++){
print $arr[$i];
print "\n";
}
function Insertsort($array){
for($i=1;$i<count($array);$i++){
$x=$array[$i];
$j=$i-1;
while($x<$array[$j]&&$j>=0){
$array[$j+1]=$array[$j];
$j--;
}
$array[$j+1]=$x;
}
return $array;
}
?>

希尔排序(最优的情况是O(n^(1.3))元素已经排好顺序,最差的情况O(n^2)元素是逆序的),不稳定

按照增量为[n/2,n/4,...1]来进行分组排序,即第0个数和第n/2个数进行插入排序,正常的插入排序从1开始往后进行插入排序,

这里从增量dk开始往后进行插入排序,比较的也是j-dk个数,j每次减dk循环。

每一步增量写一个函数

循环再写一个函数

<?php
//从小到大
$arr=array(1,3,2,5,1,32,14,3,5,6);
$arr=Shellsort($arr);
for($i=0;$i<count($arr);$i++){
print $arr[$i]."\n";
}
function Shellinsertsort(&$arr,$n,$dk){
if($dk>=1){
print $dk."a";
for($i=$dk;$i<$n;$i++){
if($arr[$i]<$arr[$i-$dk]){
$temp=$arr[$i];
//$a[$i]=$a[$i-$dk];
$j=$i-$dk;
while($temp<$arr[$j]&&$j>=0){
$arr[$j+$dk]=$arr[$j];
$j=$j-$dk;
}
$arr[$j+$dk]=$temp;
}
}
}
}
function Shellsort($arr){
$n=count($arr);
$dk=floor($n/2);
while($dk>=1){
Shellinsertsort($arr, $n, $dk);
$dk=floor($dk/2);
}
return $arr;
}
?>


2.选择排序(简单选择排序和堆排序)

简单选择排序(最坏时间复杂度O(n^2),平均时间复杂度O(n^2)),不稳定

在一趟选择,如果当前元素比一个元素小,而该小的元素又出现在一个和当前元素相等的元素后面,那么交换后稳定性就被破坏了。比较拗口,举个例子,序列5 8 5 2 9,我们知道第一遍选择第1个元素5会和2交换,那么原序列中2个5的相对前后顺序就被破坏了,所以选择排序不是一个稳定的排序算法。

每一步都找剩下的数中最小的(或者最大的)跟剩下的数的最前面互换。代码如下:

<?php
//从小到大
$array=array(12,1,4,2,1,4,6,34,12,2);
$arr=Selectsort($array);
for($i=0;$i<count($arr);$i++){
print $arr[$i];
print "\n";
}
function Selectsort($array){
for($i=0;$i<count($array);$i++){
$min=$i;
for($j=$i;$j<count($array);$j++){
if($array[$min]>$array[$j]){
$min=$j;
}
}
$temp=$array[$i];
$array[$i]=$array[$min];
$array[$min]=$temp;
}
return $array;
}
?>
堆排序(最坏时间复杂度O(n*log2n),平均时间复杂度O(n*log2n)),不稳定

就是说要把剩下的数排成大顶堆(根节点都比子节点大)或者小顶堆(根节点都比子节点小),然后把堆顶输出,输出之后将堆底元素(最后一个元素)放入堆顶,(小顶堆为例)然后与左右节点较小的元素交换,这样该节点所在的树就被破坏了,重复前面(为例之后),当该节点到达叶子节点时结束。

在数组中父节点和子节点的下标关系位两个子节点为父节点*2+1和父节点*2+2。

算法分两步:第一步建立堆,第二步输出顶元素;

<?php
//从大到小
$array=array(12,1,4,2,1,4,6,34,12,2);
$arr=Heapsort($array);
for($i=0;$i<count($arr);$i++){
print $arr[$i];
print "\n";
}
//$array为等待调整的数组
//$i为等待调整的数组元素的位置
//构造小顶堆
function HeapAdjust(&$array,$i,$length){

while(2*$i+1<$length){
$exchagechild=2*$i+1;
if($exchagechild+1<$length&&$array[$exchagechild]>$array[$exchagechild+1]){
$exchagechild++;
}
if($array[$i]>$array[$exchagechild]){
$temp=$array[$i];
$array[$i]=$array[$exchagechild];
$array[$exchagechild]=$temp;
}
else{
break;
}
$i=$exchagechild;
}
}
//堆排序
function
4000
Heapsort($array){
//从最后一个非叶结点开始进行建立堆结构
for($i=floor(count($array)/2)-1;$i>=0;$i--){
HeapAdjust($array,$i,count($array));
}
//建立好之后开始进行排序
for($i=count($array)-1;$i>0;$i--){
$temp=$array[$i];
$array[$i]=$array[0];
$array[0]=$temp;
HeapAdjust($array,0,$i);
}
return $array;
}
?>
3.交换排序(冒泡,快速)

冒泡排序法(最坏时间复杂度O(n^2),平均时间复杂度O(n^2)),稳定

这个是基本上最好理解的。把每个数和之后的数进行比较,两两交换,把最大或最小的放在最后。

<?php
//从大到小
$array=array(12,1,4,2,1,4,6,34,12,2);
$arr=Bubblesort($array);
for($i=0;$i<count($arr);$i++){
print $arr[$i];
print "\n";
}
//$array为等待调整的数组
//$i为等待调整的数组元素的位置
//构造小顶堆
function Bubblesort($array){
for($i=0;$i<count($array);$i++){
for($j=0;$j<count($array)-$i-1;$j++){
if($array[$j]<$array[$j+1]){
$temp=$array[$j];
$array[$j]=$array[$j+1];
$array[$j+1]=$temp;
}
}
}
return $array;
}
?>

快速排序法(最坏时间复杂度O(n^2),平均时间复杂度O(n*log2n)),不稳定

在中枢元素和a[j]交换的时候,很有可能把前面的元素的稳定性打乱,比如序列为5 3 3 4 3 8 9 10 11,现在中枢元素5和3(第5个元素,下标从1开始计)交换就会把元素3的稳定性打乱,所以快速排序是一个不稳定的排序算法,不稳定发生在中枢元素和a[j] 交换的时刻。

就是选择一个基准元素将数组分为大于他和小于他的两部分,然后对两部分做相同的操作,
递归的代码如下:

<?php
//从大到小
$array=array(12,1,4,2,1,4,6,34,12,2);
$arr=Quicksort($array);
for($i=0;$i<count($arr);$i++){
print $arr[$i];
print "\n";
}
//$array为等待调整的数组
//$i为等待调整的数组元素的位置
//构造小顶堆
function Quicksort($array){
if(count($array)==1){return $array;}
$flag=$array[0];
$left=array();
$right=array();
for($i=1;$i<count($array);$i++){
if($array[$i]>$flag){
$left[]=$array[$i];
}
else{
$right[]=$array[$i];
}
}
$x=Quicksort($left);
$y=Quicksort($right);
$arr=array_merge($x,array($flag),$y);
return $arr;
}
?>

注意array_merge()的使用,一定要是几个数组合并,不能是数字

去面试,人家面试官说这个又建了数组,浪费了空间,这个思想是对的,但是大家一般不这么写;

所以更新了算法:(怪不得人家说不用递归)

取最右为标准,从左往右,找出比标准小的,往数组的左边放,循环停止用i来记录左右的区分

<?php
$arr=array(3,14,5,7,23,54,12);
Quicksort($arr,0,count($arr)-1);
for($i=0;$i<count($arr);$i++){
print $arr[$i].'a';
}
function Quicksort(&$arr,$left,$right){
if($left<$right){
$i=$left-1;
$key=$arr[$right];
for($j=$left;$j<$right;$j++){
if($arr[$j]<$key){
$i++;
$temp=$arr[$j];
$arr[$j]=$arr[$i];
$arr[$i]=$temp;

}
}
$temp=$arr[$i+1];
$arr[$i+1]=$arr[$right];
$arr[$right]=$temp;
Quicksort($arr,$left,$i);
Quicksort($arr,$i+2,$right);
}
}
?>


快排非递归(面试大神和我说不用卧槽)

4归并排序(最坏时间复杂度O(n*log2n),平均时间复杂度O(n*log2n)),稳定

这个是二路归并排序

就是把两个有序的数组合成一个有序数组,或者把一个待排数组分成好几个数组排序,再合成一个数组。

<?php
//从大到小
$array=array(12,1,2,4,6,5,4,2,3,4);
Mergesort($array,0,count($array)-1);
for($i=0;$i<count($array);$i++){
print $array[$i];
print "\n";
}
function Merge(&$arr,$startA,$endA,$startB,$endB){
//print $startA.$endA.$startB.$endB."\n";
$i=$startA;$j=$endB;$m=$startA;
$arr1=array();
while($startA<=$endA&&$startB<=$endB){
if($arr[$startA]>$arr[$startB]){
$arr1[]=$arr[$startA];
$startA++;
}
if($arr[$startA]<=$arr[$startB]){
$arr1[]=$arr[$startB];
$startB++;
}
}
if($startA>$endA){
//$arr=array_merge($arr1,array_slice($arr, $startB,$endB));
while($startB<=$endB){
$arr1[]=$arr[$startB];
$startB++;
}
}
if($startB>$endB){
while($startA<=$endA){
$arr1[]=$arr[$startA];
$startA++;
}
}

for(;$i<=$j;$i++){

$arr[$i]=$arr1[$i-$m];
//print $arr[$i]."a";
}
}
function Mergesort(&$array,$start,$end){
//print $start.$end."\n";
if($start<$end){
$mid=floor(($start+$end)/2);
Mergesort($array,$start,$mid);
Mergesort($array,$mid+1,$end);
Merge($array,$start,$mid,$mid+1,$end);
}
}
?>


注:外部排序算法是,将大数据量的数据,分为多个存储在外部,然后按照内部排序方法对每一个进行排序,将排序结果存回去,然后多个文件采用归并排序,从而得到最后的有序的大文件,这里其实还是二路归并。

多路归并排序是就是k个(不是原来的两个)数组每次进行比较,选出最小的(每次进行k-1次比较),再继续排序。

如果使用“败者树”,可以使上面的k-1次比较变为log2k次比较。

败者树就是建立一个k个叶子节点的二叉树,然后进行比较,把败者记录在每个节点处,比较的时候是胜者比较,会记录下胜者不过不在树里。这样建立了败者树之后呢,就有最后的胜者,将胜者输出,胜者所在的那个数组向或者下一个值,然后和自己的父节点进行比较,确定败者,存入节点中,胜者继续向上,找到新的胜者和新的败者树。重复上面步骤。

5基排序(稳定的)和桶排序
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: