算法与数据结构 如何分析时间复杂度和空间复杂度

时间复杂度:
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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

mr.杰瑞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值