二维数组求最小值_05-最大子矩形-最大值减去最小值小于或等于num的子数组数量...
年輕即出發...
簡書:https://www.jianshu.com/u/7110a2ba6f9e
知乎:https://www.zhihu.com/people/zqtao23/posts
GitHub源碼:https://github.com/zqtao2332
個人網站:http://www.zqtaotao.cn/ (停止維護更新內容)
QQ交流群:606939954
咆哮怪獸一枚...嗷嗷嗷...趁你現在還有時間,盡你自己最大的努力。努力做成你最想做的那件事,成為你最想成為的那種人,過著你最想過的那種生活。也許我們始終都只是一個小人物,但這并不妨礙我們選擇用什么樣的方式活下去,這個世界永遠比你想的要更精彩。
最后:喜歡編程,對生活充滿激情
本節內容預告
實例1:最大子矩形
實例2:最大值減去最小值小于或等于num的子數組數量
實例1:最大子矩形
給定一個整型矩陣map,其中的值只有0和1兩種,求其中全是1的所有矩形區域中,最大的矩形區域為1的數量。
例如:1110其中,最大的矩形區域有3個1,所以返回3。
再如:
1 0 1 1
1 1 1 1
1 1 1 0
其中,最大的矩形區域有6個1,所以返回6。
思路:
本題算法涉及到了兩個知識點
1、數組壓縮:將二維矩陣問題壓縮為一維數組問題
2、單調棧
先理解一下這兩個概念
數組壓縮
通過合并矩陣中的數組的方式,將二維矩陣問題壓縮為一維數組問題。
單調棧
棧的入棧順序是遞增或者遞減的;兩種狀態可以很好的讓我們求得一些特殊數據。
注意:傳統單調棧針對的數據必須是不同的,所以當遇到相同數據時,使用單調棧需要進行簡單處理。
單調增:快速得到任意元素左邊最近比它小的數,右邊最近比它小的數
如:3 4 5 2 1 6 可以快速得到 5左邊最近比它小的數是4,右邊最近比它小的數是2
單調減:同單調增相反。
本題解題策略
每次以第 i 行作為底,求當前構成的矩陣能得到的最大矩形。
1 0 1 1
1 1 1 1
1 1 1 0
第一次以0行為底
當前構成的矩陣 1 0 1 1最大矩形 1 1第二次以1行為底
當前構成的矩陣 1 0 1 1 1 1 1 1最大矩形 1 1 1 1第三次以2行為底
當前構成的矩陣 1 0 1 1 1 1 1 11 1 1 0最大矩形 1 1 1 1 1 1這里為了直觀的理解最大矩形,可以將每一個1都看做是一個小矩形,例如1 0 1 1構成的直方圖
進行數組壓縮時,遇到1 則在原直方圖對應位置增加一個小矩形,遇到0則消除原直方圖對應位置的所有矩形。例如第三次以2為底時進行壓縮構成的直方圖。
現在的問題就是給你一個數組,它代表一個直方圖,求它的最大矩形面積。
對于任意的一個直方圖,求解它的最大矩形,其實就是求解它的面積。
使用遞增單調棧來找到兩邊比當前位置小的數,就可以直接來進行計算。
import java.util.Stack;/*** @description: 最大矩形時間復雜度 O(N*M)* @version: 1.0*/ public class Code_13_MaximalRectangle {public static int maxRecSize(int[][] map) {if (map == null || map.length == 0 || map[0].length == 0) {return 0;}int maxArea = 0; // 結果int[] height = new int[map[0].length]; // 數組壓縮時的輔助數組,理解為矩形高度for (int i = 0; i < map.length; i++) {for (int j = 0; j < map[0].length; j++) { // 矩陣壓縮height[j] = map[i][j] == 0 ? 0 : height[j] + 1; // 遇0,拆解當前位置所有的小矩形}// 求解當前行為底構成的直方圖面積,即最大矩形maxArea = maxRecFromBottom(height);}return maxArea;}//public static int maxRecFromBottom(int[] height) {if (height == null || height.length == 0) {return 0;}int maxArea = 0;Stack<Integer> toBiggerStack = new Stack<>(); // 遞增單調棧, 存儲的是下標for (int i = 0; i < height.length; i++) {while (!toBiggerStack.isEmpty() && height[i] <= height[toBiggerStack.peek()]) {int index = toBiggerStack.pop();// 左邊最近小于彈出的數的下標int leftSmallNumIndex = toBiggerStack.isEmpty() ? -1 : toBiggerStack.peek();int curArea = (i - leftSmallNumIndex - 1) * height[index]; // 關鍵i - leftSNI - 1maxArea = Math.max(maxArea, curArea);}toBiggerStack.push(i);}while (!toBiggerStack.isEmpty()) { // 單調棧中依然有元素未彈出int index = toBiggerStack.pop();int leftSmallNumIndex = toBiggerStack.isEmpty() ? -1 : toBiggerStack.peek();// 注意,此時可以肯定index后的元素一定都是大于等于height[index] 的// 所以本身加上后面的長度是height.length - leftSNI - 1// 如直方圖全是遞增數 4 5 6 7 ,單調棧剩余 4 5 ,彈出5時 ,左邊最近比5 小的下標是0// 4 - 0 -1 = 3 就是 5 6 7 三列的寬度;int curArea = (height.length - leftSmallNumIndex - 1) * height[index];maxArea = Math.max(maxArea, curArea);}return maxArea;}public static void main(String[] args) {int[][] map = {{1, 0, 1, 1}, {1, 1, 1, 1}, {1, 1, 1, 0},};System.out.println(maxRecSize(map));} }實例2:最大值減去最小值小于或等于num的子數組數量
給定數組arr和整數num,共返回有多少個子數組滿足如下情況:
max (arr[i.. j])-min (arr[i..j]) <= num
max(arr [i..j]) 表示子數組arr[i..j]中的最大值,
min (arr[i..j]) 表示子數組arr[i..j]中的最小值。
此題最優時間復雜度O(N)
滑動窗口
L、R更新結構,R+1 進數,L+1,出數
雙端隊列
實質就是雙鏈表。維護兩個雙端隊列來實時更新滑動窗口的最大值和最小值。
如果全局最大和全局最小都滿足,那么任意滑動窗口中的子數組也是滿足的。
計算子數組時,每次只計算以滑動窗口的左邊界為啟點的子數組數量有多少,實際就是滑動窗口的長度。
import java.util.LinkedList;/*** @description: 最大值減去最小值小于或等于num的子數組數量* 本題需要滑動窗口和兩個雙端隊列配合* 維護兩個雙端隊列來 實時 更新滑動窗口的最大值和最小值*/ public class Code_14_AllLessNumSubArray {public static int getNum(int[] arr, int num) {if (arr == null || arr.length == 0) {return 0;}// 維護兩個雙端隊列LinkedList<Integer> qmin = new LinkedList<>(); // 頭小尾大LinkedList<Integer> qmax = new LinkedList<>(); // 頭大尾小// 滑動窗口int L = 0;int R = 0;int res = 0;while (L < arr.length) {while (R < arr.length) {// 小值隊列更新維護while (!qmin.isEmpty() && arr[qmin.peekLast()] >= arr[R]) {qmin.pollLast(); // 小值雙端隊列更新,遇大則彈出隊列中的元素,直到合適}qmin.push(R);// 大值隊列更新維護while (!qmax.isEmpty() && arr[qmax.peekLast()] <= arr[R]){qmax.pollLast();}qmax.push(R);if (arr[qmax.getFirst()] - arr[qmin.getFirst()] > num){break; // 如果子數組的最大值-最小值都不滿足條件,那么無論窗口怎樣滑動都不可能滿足題目要求,跳出}R++;}// 排除掉無效的下標,上一個循環 L 向右移動可能導致雙端隊列中的最大值和最小值失效if (qmin.peekFirst() == L) {qmin.pollFirst();}if (qmax.peekFirst() == L) {qmax.pollFirst();}// 滑動窗口如果是滿足的 那么以 L為開頭的子數組有 R-L個res += R - L;L++;}return res;}// for testpublic static int[] getRandomArray(int len) {if (len < 0) {return null;}int[] arr = new int[len];for (int i = 0; i < len; i++) {arr[i] = (int) (Math.random() * 10);}return arr;}// for testpublic static void printArray(int[] arr) {if (arr != null) {for (int i = 0; i < arr.length; i++) {System.out.print(arr[i] + " ");}System.out.println();}}public static void main(String[] args) {int[] arr = getRandomArray(30);int num = 5;printArray(arr);System.out.println(getNum(arr, num));} }總結
以上是生活随笔為你收集整理的二维数组求最小值_05-最大子矩形-最大值减去最小值小于或等于num的子数组数量...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 5 华为兼容性 双指缩放_华为EMUI1
- 下一篇: alert点击确定后跳转_公众号/h5