基礎班:第一節
一. 演算法的衡量標準
1.1 時間複雜度
在電腦科學中,演算法的時間複雜度是一個函式,它定性描述了該演算法的執行時間。
這是一個關於代表演算法輸入值的字串的長度的函式。
時間複雜度常用大O符號表述,不包括這個函式的低階項和首項係數。
1.2 空間複雜度
空間複雜度(Space Complexity)是對一個演算法在執行過程中臨時佔用儲存空間大小的量度,記做S(n)=O(f(n))。
比如直接插入排序的時間複雜度是O(n^2),空間複雜度是O(1) 。
而一般的遞迴演算法就要有O(n)的空間複雜度了,因為每次遞迴都要儲存返回資訊
1.3 舉例理解時間複雜度
一個有序陣列A,另一個無序陣列B,請列印B中的所有不在A中的數。A陣列長度為N,B陣列長度為M。
三種流程,三種時間複雜度的表示式
演算法流程1:對於陣列B中的每一個數,都在A中通過遍歷的方式找一下。
O(M*N)
演算法流程2:對於陣列B中的每一個數,都在A中通過二分的方法找一下。
O(M*log2N)
演算法流程3:先把陣列B排序,然後用類似外排的方式列印所有不在A中出現的數。
O(M*log2M)+O(N+M)
分析:排除演算法1。
如果A陣列個數比較少,相對來說陣列B個數比較多,演算法2較好。
如果A陣列個數比較多,相對來說陣列B個數比較少,演算法3較好。
二. 對數器
- 有一個你想要測的方法A
- 實現一個絕對正確但是複雜度不好的方法B
- 實現一個隨機樣本產生器
- 實現比對的方法
- 把方法A和方法B比對很多次來驗證方法A是否正確
- 如果有一個樣本使得比對出錯,列印樣本分析是哪個方法出錯
- 當樣本數量很多時比對測試依然正確,可以確定方法A已經正確
三. 氣泡排序
時間複雜度O(N2),額外空間複雜度O(1)
// 氣泡排序 先減後加是冒泡、冒泡加中有if
protected function bubbleSort(&$data)
{
$dataNum = count($data);
if ($dataNum < 2) {
return $data;
}
for ($j=$dataNum-1; $j > 0 ; $j--) {
for ($i=0; $i < $j; $i++) {
if ($data[$i] > $data[$i+1]) {
$this->swap($data, $i, $i+1);
}
}
}
}
四. 選擇排序
時間複雜度O(N2),額外空間複雜度O(1)
// 選擇排序 選擇排序有加加、三元變數和if
protected function selectionSort(Array $data)
{
$dataNum = count($data);
for ($j=0; $j < $dataNum - 1; $j++) {
$minIndex = $j;
for ($i=$j+1; $i < $dataNum; $i++) {
$minIndex = $data[$i] < $data[$minIndex] ? $i : $minIndex ;
}
if ($minIndex != $j) {
$tmp = $data[$minIndex];
$data[$minIndex] = $data[$j];
$data[$j] = $tmp;
}
}
return $data;
}
五. 插入排序
時間複雜度O(N2),額外空間複雜度O(1)
// 插入排序 先加後減是插入、插入減中有&
protected function insertionSort(&$data)
{
$dataNum = count($data);
for ($j=1; $j < $dataNum; $j++) {
for ($i=$j ; $i > 0 && $data[$i] < $data[$i-1]; $i--) {
$tmp = $data[$i];
$data[$i] = $data[$i-1];
$data[$i-1] = $tmp;
}
}
}
六. 遞迴行為及其時間複雜度的估算
遞迴函式就是系統幫你壓棧,系統會按照程式碼的執行數序,把現場資訊儲存到程序控制塊中。
估計遞迴時間複雜度的通式
master公式的使用:原始樣本量為N的時間複雜度 = 子問題的樣本量 + 除去子問題的樣本量。
T(N) = a*T(N/b) + O(Nd)
條件 | 時間複雜度 |
---|---|
logba > d | O(Nlog~b~a) |
logba = d | O(Nd * logN) |
logba < d | O(Nd) |
七. 歸併排序
時間複雜度O(N*logN),額外空間複雜度O(N)
// 歸併排序
protected function mergeSort(&$data)
{
$start = 0;
$end = count($data) - 1;
$this->mSort($data, $start, $end);
}
protected function mSort(Array &$data, $start, $end)
{
if ($start < $end) {
//$mid = $start + ($end - $start) >> 1;
$mid = floor(($start + $end) / 2);
$this->mSort($data, $start, $mid);
$this->mSort($data, $mid + 1, $end);
$this->merge($data, $start, $mid, $end);
}
}
protected function merge(&$data, $start, $mid, $end)
{
$p = $start;
$j = $mid + 1;
$i = $start;
$tmpData = [];
while ($p != $mid + 1 && $j != $end + 1) {
$tmpData[$i++] = $data[$p] <= $data[$j] ? $data[$p++] : $data[$j++];
}
while ($p != $mid + 1) {
$tmpData[$i++] = $data[$p++];
}
while ($j != $end + 1) {
$tmpData[$i++] = $data[$j++];
}
for ($i=$start; $i <= $end; $i++) {
$data[$i] = $tmpData[$i];
}
}
八. 小和問題和逆序對問題
8.1 小和問題
在一個數組中,每一個數左邊比當前數小的數累加起來,叫做這個陣列的小和。求一個數組的小和。
例子:
[1, 3, 4, 2, 5]
1左邊比1小的數,沒有
3左邊比3小的數,1
4左邊比4小的數, 1、3
2左邊比2小的數,1
5左邊比5小的數,1、3、4、2
所有小和為1+1+3+1+1+3+4+2=16
8.2逆序對問題
<?php
namespace App\Services;
// 小和和逆序對問題
class SmallSumService
{
public function mergeSortSum(&$data){
if(count($data) < 2){
return 0;
}
// 排序並返回最小和
return $this->mSort($data, 0, count($data) - 1);
}
public function mSort(&$data, $start, $end)
{
if ($start == $end) {
return 0;
}
$mid = (int)floor(($start + $end) / 2);
// 左邊的最小和+右邊的最小和+最後排序好的最小和就是最後的結果
return $this->mSort($data, $start, $mid) + $this->mSort($data, $mid + 1, $end) + $this->merge($data, $start, $mid, $end);
}
public function merge(&$data, $start, $mid, $end)
{
$l = $start;
$r = $mid + 1;
$i = $start;
$result = 0;
$tmpData = [];
while ($l != $mid + 1 && $r != $end + 1) {
// 如果左邊小於右邊,那就有($r - $l + 1)個$data[$l]元素的和是最小和
// 如果大於右邊,返回0
$result += $data[$l] < $data[$r] ? $l * ($end - $r + 1) : 0;
// sum += $data[$l] > $data[$r] ? ($mid - $l + 1) : 0; //求逆序對
$tmpData[$i++] = $data[$l] < $data[$r] ? $data[$l++] : $data[$r++];
}
while ($l != $mid + 1) {
$tmpData[$i++] = $data[$l++];
}
while ($r != $end+ 1) {
$tmpData[$i++] = $data[$r++];
}
for ($i = $start; $i < $end; $i++) {
$data[$i] = $tmpData[$i];
}
return $result;
}
}