【算法實戰】生成窗口最大值數組

【算法實戰】生成窗口最大值數組

作算法題了,題的難度咱們分爲「士,尉,校,將」四個等級。這個算法題的模塊是篇幅比較小的那種模塊。首先是給出一道題的描述,以後我會用個人想法來作這道題,今天算是算法題的第一道題,先來試試水。算法

問題描述(等級:尉)

有一個整型數組arr和一個大小爲w的窗口從數組的最左邊滑到最右邊,窗口每次向右邊滑一個位置。
例如,數組爲[4,3,1,5,4,3,7,5],窗口大小爲5時:
[4 3 1 5 4] 3 7 5  max = 5
4 [3 1 5 4 3] 7 5  max = 5
4 3 [1 5 4 3 7] 5  max = 7
4 3 1 [5 4 3 7 5]   max = 7
即窗口最大值數組爲 result = {5, 5,7,7}

解答:

對於一道題,我通常會第一時間想到用暴力的方法來作,以後再來慢慢優化。數組

顯然,對於這道題用暴力法來作仍是挺簡單了,窗口每次向右移動一位時,咱們每次遍歷窗口內的w個元素,而後求出此時窗口的最大值就能夠了,用這種方法的時間複雜度是 O(wn)。代碼以下:微信

//暴力法求解
   public static int[] getMaxWindow(int[] arr, int w) {
       if (w < 1 || arr == null || arr.length < w) {
           return null;
       }
       int[] result = new int[arr.length - w + 1];
       int index = 0;
       //暴力求解直接從第 w-1個元素開始遍歷
       for (int i = w - 1; i < arr.length; i++) {
           int max = arr[i];
           //找出最大值
           for (int k = i; k > i - w; k--) {
               if (max < arr[k]) {
                   max = arr[k];
               }
           }
           result[index++] = max;
       }
       return result;
   }

注:能夠左右拉動

你們想一個問題,例如對於剛纔例題中的數組:ide

第一次遍歷的時候,max = 5學習

【算法實戰】生成窗口最大值數組

第二次遍歷的時候,max = 5優化

【算法實戰】生成窗口最大值數組

咱們剛纔用暴力法的時候,不管是第一次仍是第二次,咱們都是把窗口內的全部元素都給遍歷了一次,以此來尋找最大值,但是,真的須要這樣嗎?3d

第一次遍歷的時候,咱們找出了max = 5, 那麼在第二次遍歷的時候,在窗口範圍內,max = 5 左邊的兩個數1, 3 還有多是最大值嗎?也就是說,max=5 左邊的窗口元素還要必要遍歷嗎?code

顯然,max=5左邊的窗口其實是沒必要再遍歷的了,也就是它不可能會是窗口的最大值。blog

而 max = 5 右邊的 4 有可能會是窗口的最大值嗎?因爲窗口還會一直向右移動,因此 max = 5 右邊的窗口元素仍是有多是某一個窗口的最大值的。隊列

所以,咱們能夠用一個雙向的隊列,來記錄有可能成爲窗口最大值的下標,注意,這裏指的是有可能。

像剛纔的 max = 5 前面的 1,3 就不可能成爲窗口的最大值了,而右邊的4仍是有可能成爲窗口的最大值的。而且這個隊列是有序的,隊首存放的老是隊列中的最大值,

我以這道題來演示一下,咱們用result[] 數組來存放窗口最大值。

一、result[0] = 5

【算法實戰】生成窗口最大值數組

二、result[1] = 5;

【算法實戰】生成窗口最大值數組

三、result[2] = 7

【算法實戰】生成窗口最大值數組

其餘的所有都要出隊,由於7前面的5,4,3是不可能成爲窗口最大值的了。

四、result[3] = 7

【算法實戰】生成窗口最大值數組

遍歷完畢。這種方法的話時間複雜度是 O(n)。

我這裏只是提供了思路與大體的作法,具體的代碼實現仍是有不少細節須要注意的。下面給出實現代碼,代碼會有詳細的解釋。

//優化
   public static int[] getMaxWindow2(int[] arr, int w) {
       if (w < 1 || arr == null || arr.length < w) {
           return null;
       }
       //用來保存成爲最大窗口的元素
       int[] result = new int[arr.length - w + 1];
       int index = 0;
       //用鏈表從當雙向隊列。
       LinkedList<Integer> temp = new LinkedList<>();
       //剛纔演示的時候,我i直接從i = w-1那裏開始演示了。
       for (int i = 0; i < arr.length; i++) {
           //若是隊列不爲空,而且存放在隊尾的元素小於等於當前元素,那麼
           //隊列的這個元素就能夠彈出了,由於他不可能會是窗口最大值。
           //【當前元素】指的是窗口向右移動的時候新加入的元素。
           while (!temp.isEmpty() && arr[temp.peekLast()] <= arr[i]) {
               temp.pollLast();//把隊尾元素彈出
           }
           //把【當前元素】的下邊加入到隊尾
           temp.addLast(i);
           //若是隊首的元素不在窗口範圍內,則彈出
           if (temp.peekFirst() == i - w) {
               temp.pollFirst();//
           }
           if (i >= w - 1) {
               //因爲隊首存放的是最大值,因此隊首老是對應窗口的最大值元素
               result[index++] = arr[temp.peekFirst()];
           }
       }
       return result;
   }

說實話,微信看代碼確實有點難受,若是是在電腦瀏覽的話還好點,我在考慮要不要用截圖的方式,不過若是是截圖的話,有些人想要複製代碼的話會複製不了,那我以後考慮把代碼打包,大家後臺回覆獲取。

  • End -
    推薦閱讀:
    按部就班帶你學習時間複雜度和空間複雜度。
    談談NAT:什麼?全球IP和私有IP是什麼鬼?

【算法實戰】生成窗口最大值數組

相關文章
相關標籤/搜索