1. 程式人生 > >淺談資料結構與演算法分析學習及如何進行演算法分析

淺談資料結構與演算法分析學習及如何進行演算法分析

一、前言

都說資料結構與演算法分析是程式設計師的內功,想要理解計算機世界就不能不懂點資料結構與演算法,然而這也備受爭議,因為大多數的業務需求都用不上資料結構與演算法,又或者說已經有封裝好的庫可以直接呼叫,例如Java中的ArrayList與LinkedList,直接呼叫add、remove等方法就已經可以完成插入刪除等基本操作,實現業務邏輯,而無需關注其內部實現。如果說一樣東西有沒有用是根據用不用得上來衡量的話,那麼只寫業務邏輯確實沒什麼卵用,但是個人覺得有沒有用是根據對自己有沒有幫助來判斷的,對自己有幫助==這個東西對我有用。

實話說,自己一開始是因為要準備企業面試才打算學習的資料結構與演算法(趁著噹噹網搞活動的時候買了本《資料結構與演算法分析Java語言描述》,打算在寒假的時候看),期間也看過不少文章,開復先生就曾經寫過一篇文章聊

演算法的重要性,當時就覺得懂演算法的人好厲害。但真正讓我開始重視資料結構與演算法的是Leetcode的第一道題目TwoSum.

給定一個數組和一個得數,返回陣列中兩個數字相加等於得數的這兩個數字的索引
example

Given nums = [2, 7, 11, 15], target = 9,

Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].

我的解法

public class Solution {
    public int[] twoSum(int[] nums, int target) {
        int
result[] = new int[2]; for(int x = 0;x<nums.length;x++){ for(int y = x+1;y<nums.length;y++){ if((nums[x]+nums[y])==target){ result[0] = x; result[1] = y; } } } return result; } }

別人的解法

public int[] twoSum(int[] numbers, int target) { 
    int[] result = new int[2]; 
    Map<Integer, Integer> map = new HashMap<Integer, Integer>(); 
    for (int i = 0; i < numbers.length; i++) { 
        if (map.containsKey(target - numbers[i])) { 
            result[1] = i + 1; 
            result[0] = map.get(target - numbers[i]); 
            return result; 
        } 
        map.put(numbers[i], i + 1); 
    } return result; 
}

雖然都可以執行,但是明顯別人的解法比我的優雅的太多太多。我的想法是用兩個迴圈窮舉,而別人是用了HashMap來儲存資料,並且在迴圈中同時進行插入和判斷,時間複雜度我的是O(n2),而別人的是O(n),如果資料規模大,執行時間則相差巨大。
可見同樣的問題,不同人採取的解決方式是可以體現出差別的,這就關係到資料結構與演算法

目前對資料結構與演算法的理解
資料結構:為了解決問題採用的更有效率的資料組織方式
演算法:為了解決問題採用的更加快速的程式設計思想

作為一個想在技術領域打拼十年的菜鳥,有必要學習資料結構與演算法。講真,這還是挺難學的,《資料結構與演算法Java語言描述》這本書看了有一半,後面的還看不太懂,前面的又忘了,覺得有必要總結一下。
網路上也有人非常詳細的總結了這本書前兩章的內容資料結構與演算法分析(一):基礎演算法分析,在這裡就不重複造輪子了,只總結一些想加深印象的內容。

二、如何進行演算法分析

(一)分析前提

計算模型:假設有無限記憶體的模型機做任一件簡單的工作都恰好花費一個時間單位,並且不存在矩陣求逆或排序之類的想象操作。

(二)分析重點

1.執行時間(時間複雜度)
影響因素:輸入大小n以及所使用的演算法
2.佔用記憶體(空間複雜度)
影響因素:演算法本身儲存佔用、輸入輸出資料佔用以及演算法執行過程臨時佔用

(三)時間複雜度計算——大O標記法(執行時間的上界,注意簡化常數項與低階項)

example:計算Ni=0i3的執行時間

public static int sum(int n) {
      int partialSum;
       partialSum = 0;
       for(int i = 1; i <= n; i ++)
       partialSum += i * i * i;
       return partialSum;
}

所有的宣告均不計時間
第3行和第6行各佔一個時間單元
第5行每執行一次佔用4個時間單元(兩次乘法、一次加法、一次賦值),執行N次共4N個時間單元
第4行初始化i佔用1個時間單元、測試i<=n為N+1個時間單元、i++自增運算為N個時間單元,共2N+2個時間單元
忽略呼叫方法和返回值的開銷,共6N+4個時間單元
故該方法是O(N)的

快速判斷法則:
1.for迴圈
一個for迴圈的執行時間至多是該for迴圈內部那些語句(包括測試)的總執行時間乘以迭代的次數
2.巢狀的for迴圈
從裡向外,一組巢狀迴圈內部的一條語句總的執行時間為該語句的執行時間乘以該組所有的for迴圈的大小的乘積
3.順序語句
各語句執行時間求和
4.if/else語句
一個if/else語的執行時間不超過 判斷的執行時間+if語句/else語句中較長的執行時間

分析的基本策略是從內部(或最深層的部分)向外展開工作的。有方法呼叫則首先分析方法呼叫;有遞迴過程,則具體情況具體分析

(四)空間複雜度計算

當算法佔用空間大小不隨資料量n變化而變化,其空間複雜度可記為O(1)
當算法佔用空間大小隨資料量n變化而線性變化,其空間複雜度可記為O(n)

在程式開發中,我們所指的複雜度不做特別說明的情況下,就是指時間複雜度。現在的硬體發展速度之快使得我們完全可以不用考慮演算法所佔的記憶體,通常都是用空間換取時間。加之演算法的空間複雜度比較難算,所以我們都側重於時間複雜度。

三、資料結構與演算法分析學習目標

學習,要以解決問題為導向,因此要:
1.瞭解各種資料結構的結構性質,以便遇到問題可以快速選取合適的資料結構
2.瞭解各大演算法的效能及應用場景,以便遇到問題可以快速選取合適的演算法