1. 程式人生 > >基礎班:第一節

基礎班:第一節

一. 演算法的衡量標準

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較好。

二. 對數器

  1. 有一個你想要測的方法A
  2. 實現一個絕對正確但是複雜度不好的方法B
  3. 實現一個隨機樣本產生器
  4. 實現比對的方法
  5. 把方法A和方法B比對很多次來驗證方法A是否正確
  6. 如果有一個樣本使得比對出錯,列印樣本分析是哪個方法出錯
  7. 當樣本數量很多時比對測試依然正確,可以確定方法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; 
    }
}