1. 程式人生 > >程式設計師如何巧妙學習演算法技巧?(文末有彩蛋)

程式設計師如何巧妙學習演算法技巧?(文末有彩蛋)

今天和大家講講,在做演算法題時常用的一些技巧。對於平時沒用過這些技巧的人,或許你可以考慮試著去看看在實踐中能否用的上這些技巧來優化問題的解。

 

                                                                                        巧用陣列下標

 

陣列的下標是一個隱含的很有用的陣列,特別是在統計一些數字,或者判斷一些整型數是否出現過的時候。

例如,給你一串字母,讓你判斷這些字母出現的次數時,我們就可以把這些字母作為下標,在遍歷的時候,如果字母a遍歷到,則arr[a]就可以加1了,即arr[a]++。

通過這種巧用下標的方法,我們不需要逐個字母去判斷。

我再舉個例子:

問題:給你n個無序的int整型陣列arr,並且這些整數的取值範圍都在0-20之間,要你在 O(n) 的時間複雜度中把這n個數按照從小到大的順序打印出來。

對於這道題,如果你是先把這n個數先排序,再列印,是不可能O(n)的時間打印出來的。

但是數值範圍在0-20。我們就可以巧用陣列下標了。把對應的數值作為陣列下標,如果這個數出現過,則對應的陣列加1。

程式碼如下:

 

public void f(int arr[]) {

        int[] temp = new int[21];
        for (int i = 0; i < arr.length; i++) {
            temp[arr[i]]++;
        }
        //順序列印
        for (int i = 0; i < 21; i++) {
            for (int j = 0; j < temp[i]; j++) {
                System.out.println(i);
            }
        }
    }

利用陣列下標的應用還有很多,大家以後在遇到某些題的時候可以考慮是否可以巧用陣列下標來優化。

 

                                                                                            巧用取餘

 

有時候我們在遍歷陣列的時候,會進行越界判斷,如果下標差不多要越界了,我們就把它置為0重新遍歷。特別是在一些環形的陣列中,例如用陣列實現的佇列。往往會寫出這樣的程式碼:

 

for (int i = 0; i < N; i++) {
        if (pos < N) {
         //沒有越界
         // 使用陣列arr[pos]
        else {
          pos = 0;//置為0再使用陣列
           //使用arr[pos]
         }
        pos++;
   }

實際上我們可以通過取餘的方法來簡化程式碼。

 

for (int i = 0; i < N; i++) {
   //使用陣列arr[pos]   (我們假設剛開始的時候pos < N)
   pos = (pos + 1) % N;
}

 

 

                                                                                          巧用雙指標

 

對於雙指標,在做關於單鏈表的題是特別有用,比如“判斷單鏈表是否有環”、“如何一次遍歷就找到連結串列中間位置節點”、“單鏈表中倒數第k個節點”等問題。

對於這種問題,我們就可以使用雙指標了,會方便很多。我順便說下這三個問題怎麼用雙指標解決吧。

例如對於第一個問題,我們就可以設定一個慢指標和一個快指標來遍歷這個連結串列。

慢指標一次移動一個節點,而快指標一次移動兩個節點,如果該連結串列沒有環,則快指標會先遍歷完這個表,如果有環,則快指標會在第二次遍歷時和慢指標相遇。

對於第二個問題,一樣是設定一個快指標和慢指標。慢的一次移動一個節點,而快的兩個。

在遍歷連結串列的時候,當快指標遍歷完成時,慢指標剛好達到中點。

對於第三個問題,設定兩個指標,其中一個指標先移動k個節點。之後兩個指標以相同速度移動。

當那個先移動的指標遍歷完成的時候,第二個指標正好處於倒數第k個節點。

你看,採用雙指標方便多了吧。所以以後在處理與連結串列相關的一些問題的時候,可以考慮雙指標哦。

 

                                                                                        巧用移位運算

 

有時候我們在進行除數或乘數運算的時候,例如n / 2,n / 4, n / 8這些運算的時候,我們就可以用移位的方法來運算了,這樣會快很多。

例如:

  • n / 2等價於n >> 1;

  • n / 4等價於n >> 2;

  • n / 8等價於n >> 3。

這樣通過移位的運算在執行速度上是會比較快的,也可以顯得你很厲害的樣子,哈哈。

還有一些&(與)、|(或)的運算,也可以加快運算的速度。例如判斷一個數是否是奇數,你可能會這樣做:

 

if(n % 2 == 1){
  dosomething();
}

不過我們用與或運算的話會快很多。例如判斷是否是奇數,我們就可以把n和1相與了,如果結果為1,則是奇數,否則就不會。即:

 

if(n & 1 == 1){
  dosomething();
)

具體的一些運算技巧,還得需要你們多在實踐中嘗試著去使用,這樣用久後就會比較熟練了。

                                                                                          設定哨兵位

 

在連結串列的相關問題中,我們經常會設定一個頭指標,而且這個頭指標是不存任何有效資料的,只是為了操作方便,這個頭指標我們就可以稱之為哨兵位了。

例如我們要刪除頭第一個節點是時候,如果沒有設定一個哨兵位,那麼在操作上,它會與刪除第二個節點的操作有所不同。

但是我們設定了哨兵,那麼刪除第一個節點和刪除第二個節點那麼在操作上就一樣了,不用做額外的判斷。當然,插入節點的時候也一樣。

有時候我們在運算元組的時候,也是可以設定一個哨兵的,把arr[0]作為哨兵。

例如,要判斷兩個相鄰的元素是否相等時,設定了哨兵就不怕越界等問題了,可以直接arr[i] == arr[i-1]?了。不用怕i = 0時出現越界。

當然我這只是舉一個例子,具體的應用還有很多,例如插入排序,環形連結串列等。

 

                                                                                  與遞迴有關的一些優化

 

  • 對於可以遞迴的問題考慮狀態儲存

當我們使用遞迴來解決一個問題的時候,容易產生重複去算同一個子問題,這個時候我們要考慮狀態儲存以防止重複計算。例如我隨便舉一個之前舉過的問題

問題:一隻青蛙一次可以跳上1級臺階,也可以跳上2級。求該青蛙跳上一個n級的臺階總共有多少種跳法?

這個問題用遞迴很好解決。假設f(n)表示n級臺階的總跳數法,則有f(n) = f(n-1)+f(n - 2)。

遞迴的結束條件是當0 <= n <= 2時, f(n) = n。因此我們可以很容易寫出遞迴的程式碼:

 

    public int f(int n) {
        if (n <= 2) {
            return n;
        } else {
            return f(n - 1) + f(n - 2);
        }
    }

不過對於可以使用遞迴解決的問題,我們一定要考慮是否有很多重複計算。顯然對於f(n) = f(n-1) + f(n-2)的遞迴,是有很多重複計算的。如:

就有很多重複計算了。這個時候我們要考慮狀態儲存。例如用hashMap來進行儲存,當然用一個數組也是可以的,這個時候就像我們上面說的巧用陣列下標了。

可以當arr[n] = 0時,表示n還沒計算過,當arr[n] != 0時,表示f(n)已經計算過,這時就可以把計算過的值直接返回回去了。因此我們考慮用狀態儲存的做法程式碼如下:

 

//陣列的大小根據具體情況來,由於int陣列元素的的預設值是0
    //因此我們不用初始化
    int[] arr = new int[1000];
    public int f(int n) {
        if (n <= 2) {
            return n;
        } else {
            if (arr[n] != 0) {
                return arr[n];//已經計算過,直接返回
            } else {
                arr[n] = f(n-1) + f(n-2);
                return arr[n];
            }
        }
    }

這樣,可以極大提高演算法的效率。也有人把這種狀態儲存稱之為備忘錄法。

  • 考慮自底向上

對於遞迴的問題,我們一般都是從上往下遞迴的,直到遞迴到最底,再一層一層著把值返回。

不過,有時候當n比較大的時候,例如當n = 10000時,那麼必須要往下遞迴10000層直到n <=2才將結果慢慢返回,如果n太大的話,可能棧空間會不夠用。

對於這種情況,其實我們是可以考慮自底向上的做法的。例如我知道:

  • f(1)=1;

  • f(2)=2。

那麼我們就可以推出f(3)= f(2)+f(1)=3。從而可以推出f(4),f(5)等直到f(n)。

因此,我們可以考慮使用自底向上的方法來做。

程式碼如下:

 

public int f(int n) {
        if(n <= 2)
            return n;

        int f1 = 1;
        int f2 = 2;
        int sum = 0;

        for (int i = 3; i <= n; i++) {
            sum = f1 + f2;
            f1 = f2;
            f2 = sum;
        }
        return sum;
    }

我們也把這種自底向上的做法稱之為遞推。

 

                                                                                            總結一下

 

當你在使用遞迴解決問題的時候,要考慮以下兩個問題。

(1)是否有狀態重複計算的,可不可以使用備忘錄法來優化。

(2)是否可以採取遞推的方法來自底向上做,減少一味遞迴的開銷。

今天就先講到這裡,之後有時間再來多謝一些其他的。如果覺得不錯,不妨點個贊。

且慢, 說到這裡還沒完, 我們不僅有知識技能的見解, 小編更是歷經辛苦幫大家收集到一套關於演算法一系列全套完整的視訊講解課程, 廢話不多說,直接上乾貨 至於視訊的含金量大家自己看了自然會知道, 部分資源截圖如下:

 

 

 

 

(僅展示部分資源截圖)

佔用空間23G, 考慮到如果是壓縮檔案,大家還得需要下載下來才能觀看, 所以

直接就是視訊資源啦,大家儲存到網盤後就可以直接觀看了,就是這麼任性!!! 有知識點的剖析, 更有配套視訊資源講解,大家還怕學不會嗎? 儲存到網盤後你們就可以反覆觀看了,總之關注小編公眾號是絕對不會讓大家失望的, 後續更多精彩視訊文章陸續更新中ing...,贈人玫瑰,手留餘香,希望大家分享給身邊有需要的人,感謝大家的支援!大資料視訊教程也在整理中, 後續會陸續和大家見面, 感謝大家的支援!

關注微信公眾號 [ java大資料修煉之道 ]

在微信公眾號後臺回覆關鍵字: 演算法 獲取上述全套視訊講解資源

 

注:本公眾號純屬個人公眾號!不存在任何培訓機構招生資訊!更不存在任何收費專案!純屬公益分享!為的就是不讓大家走彎路能有一個明確的學習方向!請大家分享給有需要的人!贈人玫瑰,手有餘香!

推薦往期作品

  ●  淺談Java工程師的職業規劃

  ●  Java學不進,沒頭緒?是不是沒摸清階段順序?

  ●  面試系列String,StringBuffer,StringBuilder三者區別

  ●  Java日誌效能那些事

  ●  學習Java需吃透這些基本概念

  ●  Java中的String為什麼是不可變的?以及原始碼分析

  ●  BAT 大企內部面試題洩密

  ●  SpringBoot入門到高階視訊資源全套,確定不領取下?

  ● 某平臺3980元大資料/機器學習課程免費下載,僅此1次

  ●  java學習路線全攻略(資料、視訊、原始碼、專案實戰)

                                                                                          ·end·

                                                             —寫文不易,你的轉發就是對我最大的支援—


 

                                                                           推薦程式設計師必備微訊號 

                                                                                         ▼

 

                                                                                  Java大資料修煉之道

                                                                            微訊號: gh_9119f24d3793

 

推薦理由:

在這裡,我們分享程式設計師相關技術,職場生活,行業熱點資訊。不定期還會分享IT趣文和趣圖、各種java大資料學習視訊。這裡屬於我們程式設計師自己的生活,工作和娛樂空間。

 

                                                                          ▼長按下方↓↓↓二維碼識別關注