时间复杂度:
1.不计入计算的是打印 和初始变量
2遍历
for($i=0;$i<$num-1;$i++)
{
$p = $i;
for($j=$i+1;$j<$num;$j++)
{
$p = $arr[$j]<$arr[$p] ? $j : $p;
//主要耗时点在这里 (n-1)+(n-2)+...+1 等差数列和 n(n-1)/2
//还有些常数时间T
}
}
空间复杂度:
空间复杂度指的是算法用到的额外空间
将数组复制,到别的数组
算法不稳定:
两个数据相等,当排序时,这两个相等的数据位置变化了。
如何验证算法正确-对数器
1.肉眼观察
2.产生足够多随机样本
3.用确定正确的算法计算样本结果
4.对比被验证算法的结果
function getRandArray($num)
{
for($i=0;$i<$num;$i++)
{
$arr[$i] = mt_rand(0,$num);
}
return $arr;
}
function check()
{
$arr = getRandArray(10000);
$arrCopy = $arr;
$num = count($arrCopy);
//系统的算法
array_sort($arr);
//自己写的算法
my_sort($arrCopy);
for($i=0;$i<$num;$i++)
{
if($arr[$i] != $arrCopy[$i]) $same = true;
}
return $same==true ? "right" : "wrong";
}
冒泡排序
一个数与第二个数比较,大的数往后移,与后面的数比较。直到末尾没有数可比为止。
$arr = array(5,1,3,2,6);
function mySort($arr)
{
$num = count($arr);
//for($i=0;$i<$num-1;$i++)
//{
// for($j=0;$j<$num-$i-1;$j++)
// {
// if($arr[$j]>$arr[$j+1])
// {
// $temp = $arr[$j+1];
// $arr[$j+1] = $arr[$j];
// $arr[$j] = $temp;
// }
// }
//}
for($i=$num-1;$i>0;$i--)
{
for($j=0;$j<$i;$j++)
{
$flag = false;
if($arr[$j]>$arr[$j+1])
{
$temp = $arr[$j+1];
$arr[$j+1] = $arr[$j];
$arr[$j] = $temp;
$flag = true;
}
if($flag == false)
{
return $arr;
}
}
}
return $arr;
}
时间复杂度
评价复杂度
外层n 内层 n-1 n-2 n-3 … 3 2 1 也是 n(n-1)/2 O(n的平方)
因为是两两比较 不是跳着比较 是稳定的
最好的时间复杂度
是n。直接在程序中加个标志位。
插入排序
对于基本有序的数组最好用 且 稳定
思想:把基本有序的值插入到指定的位置就叫做插排,第一个数和第二个数比较,小的数挪到前面。第三个数和第二个比较如果小就换位。
function insertSort($arr)
{
$num = count($arr);
//for($i=1;$i<$num;$i++)
//{
// for($j=0;$j<$i;$j++)
// {
// if($arr[$j]>$arr[$j+1])
// {
// $temp = $arr[$j+1];
// $arr[$j+1] = $arr[$j];
// $arr[$j]= $temp;
// }
// }
//}
for($i=1;$i<$num;$i++)
{
for($j=$i;$j>0 && $arr[$j]< $arr[$j-1];$j--)
{
$temp = $arr[$j-1];
$arr[$j-1] = $arr[$j];
$arr[$j]= $temp;
}
}
return $arr;
}
空间复杂度:因为没有用到额外的空间 O(1)
时间复杂度: 外 1~n-1 内层 1,2,3…n-1 O(n的平方) 但是插入排序比冒泡排序快,它减少了一些比较。也比选择排序快
最好的时间复杂度 O(n)
优化
用临时变量记录标记值 想到再更新
冒泡 O(n)
基本不用,太慢
选择
基本不用,不稳
插入 最好O(n)
样本小且基本有序的时候效率比较高
希尔排序
改进的插入排序
思想:首先有一个间隔,然后根据这个间隔找到对应的数,用插入排序将值排好。然后将第一个数往后推一位,排完缩小间隔再排一次,直到间隔为1,排一遍
改进点在间隔上。一个小的数能很快的挪到数组的前半部分
间隔大的情况下移动的次数小
间隔小的情况下移动的距离短
function insertSort($arr)
{
$num = count($num);
$h = 1;
while($h<=intval($num/3))
{
$h = $h*3 +1;
}
/**
*Knuth序列
h=1
h=3h+1
*/
for($gap=$h;$gap>0;$gap=intval(($gap-1)/3))
{
for($i=$gap;$i<$num;$i++)
{
for($j=$i;$j>$gap-1;$j-=$gap)
{
if($arr[$j]<$arr[$j-$gap])
{
$temp = $arr[$j-$gap];
$arr[$j-$gap] = $arr[$j];
$arr[$j] = $temp;
}
}
}
}
return $arr;
}
希尔排序
空间复杂度O(1)
时间复杂度 O(n的1.3次方)
归并排序
如果有两个有序的数组,怎么排序,首先分配空间能装下两个数组。
首先拿a数组第一位和b数组第一位比较,如果a小,a移动到新空间,那用a的第二个位置与b数组的第一位比较。
两个数组,从第一个值开始比较 哪个小放哪个数组的值
先写一段伪代码
首先假设 数组为 1 3 8 9 5 7 10
找到中间值 7/2 取整 = 3
mid =3
第一个数组 1 3 8 9(开始的键为0 i=0) 第二个数组 5 7 10 (开始的键为 j = mid + 1)
临时数组键 k=0
当第一个数组的键小于等于中间值,并且第二个数组小于数组的长度时
如果arr[0]<arr[4]
temp[0] = 1 i=1 k=1
arr[1]<arr[4]
temp[1] = 3 i=2 k=2
arr[2]>arr[4]
temp[2] = 5 j=5 k=3
arr[2]>arr[5]
temp[3] = 7; j=6 k=4
arr[2] < arr[7]
temp[4] = 8 i=3 k =5
arr[3] < arr[7]
temp[5] = 9 i = 4 k=6 4大于3跳出循环
还剩第二个数组的 10
现在会剩下的可能是,要么是第一个数组有剩余,要么是第二个数组有剩余
现在不管是第一个数组还是第二个数组有剩余,剩下的都是大数,并且有顺序
如果满足i<=mid temp[k++] = arr[i++]
如果满足j<数组长度 temp[k++] = arr[j++]
按照伪代码来写
function merge($arr)
{
$num = count($arr);
$mid = intval($num/2);
$i=0;
$j=$mid+1;
$k=0;
while($i <= $mid && $j < $num)
{
$temp[$k++] = $arr[$i]<=$arr[$j] ? $arr[$i++] : $arr[$j++];
//if($arr[i]<=$arr[$j])
//{
//$temp[$k] = $arr[$i];
//$i++;
//$k++;
// $temp[$k++] = $arr[$i++];
//}
//else
//{
// //$temp[$k] = $arr[$j];
//$j++;
//$k++;
// $temp[$k++] = $arr[$j++];
//}
}
while($i <= $mid) $temp[$k++] = $arr[$i++];
while($j<$num) $temp[$k++] = $arr[$j++];
return $temp;
}
function merge($arr,$left,$right,$rightBound)
{
$mid = $right-1;
$i=$left;
$j=$right;
$k=0;
while($i <= $mid && $j <= $rightBound)
{
$temp[$k++] = $arr[$i]<=$arr[$j] ? $arr[$i++] : $arr[$j++];
//if($arr[i]<=$arr[$j])
//{
//$temp[$k] = $arr[$i];
//$i++;
//$k++;
// $temp[$k++] = $arr[$i++];
//}
//else
//{
// //$temp[$k] = $arr[$j];
//$j++;
//$k++;
// $temp[$k++] = $arr[$j++];
//}
}
while($i <= $mid) $temp[$k++] = $arr[$i++];
while($j<=$rightBound) $temp[$k++] = $arr[$j++];
return $temp;
}
function smallSort($arr,$left,$right)
{
if($left == $right) return;
$mid = ($left+$right)/2;
smallSort($arr,$left,$mid);
smallSort($arr,$mid,$right);
merge($arr,$left,$mid+1,$right);
}
时间复杂度 n×log以2为底n的对数
n 一直对半分 n/2 ,n/4 ,n/8, n/16 …2 分了 log以2为底n的对数
空间复杂度 O(n)
php 底层sort 的实现原理
zend_sort
当数据量小于等于16,会使用插入排序,因为此时插入排序性能更好;否则会使用快速排序
function stable_uasort(&$array, $cmp_function) {
if(count($array) < 2) {
return;
}
$halfway = count($array) / 2;
$array1 = array_slice($array, 0, $halfway, TRUE);//来取0到$halfway的数组 带key或者不带key
$array2 = array_slice($array, $halfway, NULL, TRUE);
stable_uasort($array1, $cmp_function);
stable_uasort($array2, $cmp_function);
if(call_user_func($cmp_function, end($array1), reset($array2)) < 1) {
$array = $array1 + $array2;
return;
}
$array = array();
reset($array1);
reset($array2);
while(current($array1) && current($array2)) {
if(call_user_func($cmp_function, current($array1), current($array2)) < 1) {
$array[key($array1)] = current($array1);
next($array1);
} else {
$array[key($array2)] = current($array2);
next($array2);
}
}
while(current($array1)) {
$array[key($array1)] = current($array1);
next($array1);
}
while(current($array2)) {
$array[key($array2)] = current($array2);
next($array2);
}
return;
}
function cmp($a, $b) {
if($a['n'] == $b['n']) {
return 0;
}
return ($a['n'] > $b['n']) ? -1 : 1;
}
$a = $b = array(
'a' => array("l" => "A", "n" => 1),
'b' => array("l" => "B", "n" => 2),
'c' => array("l" => "C", "n" => 1),
'd' => array("l" => "D", "n" => 2),
'e' => array("l" => "E", "n" => 2),
);
uasort($a, 'cmp');
print_r($a);
stable_uasort($b, 'cmp');
print_r($b);
打印结果
Array
(
[b] => Array
(
[l] => B
[n] => 2
)
[d] => Array
(
[l] => D
[n] => 2
)
[e] => Array
(
[l] => E
[n] => 2
)
[a] => Array
(
[l] => A
[n] => 1
)
[c] => Array
(
[l] => C
[n] => 1
)
)
Array
(
[b] => Array
(
[l] => B
[n] => 2
)
[d] => Array
(
[l] => D
[n] => 2
)
[e] => Array
(
[l] => E
[n] => 2
)
[a] => Array
(
[l] => A
[n] => 1
)
[c] => Array
(
[l] => C
[n] => 1
)
)
递归
10开始执行
【10空间】-》【9空间】-》【8空间】…【1空间】
执行完消失 1空间先消失-》10空间
实际不是同一个方法
一定要有base case 边界
快排
思想:首先找到一个基准,把比这个基准小的排左边,大的排右边。
然后再选一个轴继续排,分到只剩一个
$arr = array(1,3,9,4,8);
var_dump(quickSort($arr));
function quickSort($arr)
{
$num = count($arr);
if($num<=1)
{
return $arr;
}
$mid = $arr[0];
$left = array();
$right = array();
for($i=1;$i<$num;$i++)
{
if($arr[$i]>$mid)
{
$right[] = $arr[$i];
}
else
{
$left[] = $arr[$i];
}
}
$left = quickSort($left);
$right = quickSort($right);
return array_merge($left,array($mid),$right);
}
//这样写是以一个值为标准 所有值都和改值进行比较
//还有种思路是 选一个值为轴 左边的数从开头来依次取数和轴比较,取出最大值,右边的数从末端开始,从右往左与轴比较,取最小的值,然后左边最大的值和右边最小的值交换位置
function quickSort($arr,$leftBound,$rightBound)
{
if($leftBound >= $rightBound) reture;
$mid = partition($arr,$leftBound,$rightBound);
quickSort($arr,$leftBound,$mid-1);
quickSort($arr,$mid+1,$rightBound);
}
function partition($arr,$leftBound,$rightBound)
{
$pivot = $arr[$rightBound];
$left = $leftBound;
$right = $rightBound-1;
while($left<=$right)
{
while($left<=$right && $arr[$left] <= $privot) $left++;
while($left<=$right && $arr[$right] > $privot) $right++;
if($left < $right) swap($arr,$left,$right);
}
swap($arr,$left,$rightBound);
return $left;
}
function swap($arr,$i,$j)
{
$temp = $arr[$i];
$arr[$i] = $arr[$j];
$arr[$j] = $temp;
return $arr;
}
空间复杂度为 O(logn) 有个递归
每分一层就要存一次临时变量
单轴快排
双轴快排 分三个区域
时间复杂度 O(nlogn)
把数据一次分两份,然后继续分一成两份
最差情况是最大数在末尾 时间复杂度 n平方
1.读 2.中间 3 减少功能
计数排序
非比较排序
桶思想的一种
时间复杂度 O(n+k)
空间复杂度 n+k
思想:
量大但是范围小 1年龄排序 2 高考名次
在范围可控的条件下,算每个数出现的次数,需要生成一个额外的空间n+k,把数值当做该空间的键,出现的次数当做这个键的值。
$arr=array(1,3,2,10,6);
function countSort($arr)
{
$num = count($arr);
if($num<=1)
{
return $arr;
}
$temp = $result =array();
for($i=0;$i<$num;$i++)
{
$temp[$arr[$i]]++;
}
for($i=0,$j=0;$i<count($temp);$i++)
{
while($temp[$i]-- >0) $result[$j++]=$i;
}
return $result;
}
var_dump(countSort($arr));
//巧妙的地方在运用数组的
while($temp[$i]-- >0) $result[$j++]=$i; 数组先从第一个格子 也就是值为1的地方开始减
但是计数排序不稳定 怎么优化
function countSort($arr)
{
$num = count($arr);
if($num<=1)
{
return $arr;
}
$temp = $result =array();
for($i=0;$i<$num;$i++)
{
$temp[$i] = $temp[$i] + $temp[$i-1];
}
for($i=$num-1;$i>=0;$i--)
{
$result[--$temp[$arr[$i]]] = $arr[$i];
}
return $result;
}
计数排序非比较排序
适用于特定的问题,对源数据有要求 量大,出现的数字不多
时间复杂度 2(n+k) 忽略常数项 n+k
空间复杂度 n+k
原数组 2遍
count数组 2遍
累加数组
基数排序
非比较排序
桶思想
多关键字排序
先比较个位,再比较十位,再比较百位
时间复杂度 O(n*k) 一直需要复制
空间复杂度 O(n)
百位的数先/100 然后取余 通过计数排序进行一遍排序
十位数/10取余 ...依次类推
多关键字排序
分治思想
桶排序
首先分桶 是一个范围,把符合条件的数放到各个通里,通里的数再进行排序
时间
3n+k+n/k*log(n/k)k = 3n+k+nlog(n/k)
大约等于n+k
最坏放一个桶没有顺序 n的平方
最好
空间 n+k