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

本文详细介绍了多种排序算法,包括插入排序、希尔排序、选择排序、堆排序、冒泡排序、快速排序及归并排序等,涵盖了它们的时间复杂度、稳定性特点,并提供了PHP实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

稳定性是指假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变

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

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 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] 交换的时刻。

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

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

所以更新了算法:

取最右为标准,从左往右,找出比标准小的,往数组的左边放,循环停止用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基排序(稳定的)和桶排序

 

 


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值