每日一道算法:兩數之和

題目:給定一個整數數組 nums 和一個目標值 target,請你在該數組中找出和爲目標值的那 兩個 整數,並返回他們的數組下標。

說明:你能夠假設每種輸入只會對應一個答案。可是,你不能重複利用這個數組中一樣的元素。php

示例:
給定 nums = [2, 7, 11, 15],target = 9
由於 nums[0] + nums[1] = 2 + 7 = 9
因此返回 [0, 1]git

每日一道算法:兩數之和

解法一:暴力法

思路分析:

暴力法很簡單,遍歷每一個元素 x,並查找是否存在一個值與 target−x 相等的目標元素

PHP代碼實現:

/**
 * @param Integer[] $nums
 * @param Integer $target
 * @return Integer[]
 */
function twoSum($nums, $target) {
    for($i=0;$i<count($nums);$i++){
        for($j=$i+1;$j<count($nums);$j++){
            if($nums[$i] + $nums[$j] == $target){
                return [$i,$j];
            }
        }
    }
}

複雜度分析:

時間複雜度:O(n^2)
對於每一個元素,咱們試圖經過遍歷數組的其他部分來尋找它所對應的目標元素,這將耗費 O(n) 的時間
空間複雜度:O(1)

解法二:兩遍哈希表

思路分析:

爲了對運行時間複雜度進行優化,咱們須要一種更有效的方法來檢查數組中是否存在目標元素。若是存在,咱們須要找出它的索引。保持數組中的每一個元素與其索引相互對應的最好方法是什麼?哈希表(hashTable)。

經過以空間換取速度的方式,咱們能夠將查找時間從 O(n) 下降到 O(1)。哈希表正是爲此目的而構建的,它支持以 近似 恆定的時間進行快速查找。我用「近似」來描述,是由於一旦出現衝突,查找用時可能會退化到 O(n)。但只要你仔細地挑選哈希函數,在哈希表中進行查找的用時應當被攤銷爲 O(1)。github

一個簡單的實現使用了兩次迭代。在第一次迭代中,咱們將每一個元素的值和它的索引添加到表中。而後,在第二次迭代中,咱們將檢查每一個元素所對應的目標元素(target - nums[i])是否存在於表中。注意,該目標元素不能是 nums[i] 自己!算法

採用數組函數 `array_keys()` 來解題, 返回包含數組中全部鍵名的一個新數組
`array_keys()` 是一個哈希函數

PHP代碼實現:

/**
 * @param Integer[] $nums
 * @param Integer $target
 * @return Integer[]
 */
function twoSum($nums, $target) {
    for($i=0;$i<count($nums);$i++){
        $difNum = $target - $nums[$i];
        $keys = array_keys($nums,$difNum);
        foreach($keys as $key){
            if($key && $key != $i){
                return [$i,$key];
            }
        }
    }
}

複雜度分析:

時間複雜度:O(n)
咱們把包含有 n 個元素的列表遍歷兩次。因爲哈希表將查找時間縮短到 O(1) ,因此時間複雜度爲 O(n)
空間複雜度:O(n)
所需的額外空間取決於哈希表中存儲的元素數量,該表中存儲了 n個元素

解法三:一遍哈希表

思路1:array_keys_exists()

思路分析:

事實證實,咱們能夠一次完成。在進行迭代並將元素插入到表中的同時,咱們還會回過頭來檢查表中是否已經存在當前元素所對應的目標元素。若是它存在,那咱們已經找到了對應解,並當即將其返回。
採用數組函數 `array_key_exists()` 來解題, 判斷數組是否存在此鍵名
`array_key_exists()` 是一個哈希函數

PHP代碼實現:

/**
 * @param Integer[] $nums
 * @param Integer $target
 * @return Integer[]
 */
function twoSum($nums, $target) {
    $find = [];
    for($i=0;$i<count($nums);$i++){
        $difNum = $target - $nums[$i];
        if(array_key_exists($difNum,$find)){
            return [$find[$difNum],$i];
        }
        $find[$nums[$i]] = $i;
    }
}

複雜度分析:

時間複雜度:O(n)
咱們只遍歷了包含有 n個元素的列表一次。在表中進行的每次查找只花費 O(1)的時間
空間複雜度:O(n)
所需的額外空間取決於哈希表中存儲的元素數量,該表最多須要存儲 n個元素

思路2:使用isset()代替array_key_exists()

思路分析:

`isset()`性能比`array_key_exists()`要好,由於`isset()`是語言結構,而`array_key_exists()`是函數,語言結構的解析運行要比函數快一點。

PHP代碼實現:

/**
 * @param Integer[] $nums
 * @param Integer $target
 * @return Integer[]
 */
function twoSum($nums, $target) {
    $find = [];
    for($i=0;$i<count($nums);$i++){
        $difNum = $target - $nums[$i];
        if(isset($find[$difNum])){
            return [$find[$difNum],$i];
        }
        $find[$nums[$i]] = $i;
    }
}

複雜度分析:

時間複雜度:O(n)
空間複雜度:O(n)
小貼士:
* isset()效率高於array_key_exists(), PHP7以後有30%左右的提高, php5.6有將近70%的提高
* isset()是語法結構, array_key_exists()是函數, 調用開銷要小
* isset()經過 Z_TYPE_P 獲取變量類型,而後再進行判斷實現的; array_key_exists()則是經過hash查找來實現的
* 對於數組,isset()的性能要高於array_key_exists() 因此,若是數組比較大,咱們應該用以下方法保證性能和準確性 isset()

解法四:二分查找法

思路分析:

用一個排序都能把複雜度降到 O(nlogn),經過排序,而後用兩個指針從先後掃描逼近真值。

注意這個思想,可讓 O(n^2) 的複雜度降爲 O(n),充分利用排序,由於必定會有一個值知足,而後經過值去原數組裏找對應的下標。數組

前提,該數組已是一個有序數組,必須先排序,再查找

PHP代碼實現:

/**
 * @param Integer[] $nums
 * @param Integer $target
 * @return Integer[]
 */
function twoSum($nums, $target) {
    $origin = $nums;
    sort($nums);
    $left = 0;
    $right = count($nums) - 1;
    while($left <= $right){
        if($nums[$left] + $nums[$right] == $target){
            $start = array_keys($origin,$nums[$left]);
            $end = array_keys($origin,$nums[$right]);
            return [reset($start),end($end)];
        }elseif($nums[$left] + $nums[$right] > $target){
            $right -= 1;
        }elseif($nums[$left] + $nums[$right] < $target){
            $left += 1;
        }
    }
}

複雜度分析:

時間複雜度:O(logn)
空間複雜度:O(1)

四種解法的性能對比

每日一道算法:兩數之和

時間複雜度:

Ο(1)<Ο(log2n)<Ο(n)<Ο(nlog2n)<O(n^2)<O(n^3)<…<Ο(2n)<Ο(n!)

結論:

通常算法是否最優,主要看時間複雜度,每每最優的算法須要犧牲空間複雜度
一遍哈希表 < 二分查找法 < 兩遍哈希表 < 暴力法

github

LeetCode_PHP:https://github.com/zhangdejian/LeetCode_PHP

後記

本身只能解出第一和第二種思路,原來還有其它思路解,soga,漲知識了。

本身不屬於那種最聰明的或最有天賦的人,只能後天靠努力來補啦哈,反正多刷刷算法題,準沒錯,可讓本身的思惟和深度無限延伸。函數

題目來源:力扣(LeetCode)

連接:https://leetcode-cn.com/probl...性能

相關文章
相關標籤/搜索