第一節課
第一題:題意與leetcode354的問題相同
算法原型 最長遞增子序列問題

/* * 題意:求出給定序列的最長遞增子序列的長度,給定序列不是有序的,子序列不是子數組,元素在原數組中不必是連續的 * */ /* * solutions1: 時間復雜度O(n^2),空間復雜度O(n) * 新建一個輔助數組h,h[i]表示以nums[i]結尾的最長遞增子序列的長度 * h[i]值的確定,需要nums[i]與nums[j](0<=j<i)做比較,h[i] = h[j] + 1 (when nums[j] < nums[i]),找出這些值中的最大值即最終的nums[i] * 如果nums[i]最小的話,則h[i] = 1; * 因為h[i]值的確定需要nums[i]進行最多i-1次比較,所以時間復雜度為O(n^2) */ /* * solution2: 時間復雜度O(n*log(n)),空間復雜度O(n) * 新建一個輔助數組h,h[i]表示遍歷到當前nums[j]位置時長度為i+1的遞增子序列的最小末尾 * h填入值的部分稱作有效區,還未填入值的部分稱作無效區 * h[i]值的確定,h[0] = nums[0],在遍歷nums 的同時,在h[i]中找到第一個大於等於nums[j]的數,並將其替換為為nums[j],如果沒找到則將h有效區后一個元素置為nums[j] * h[i]會一直保持有序狀態,所以找第一個大於等於nums[j]的數可以用二分法,最后返回h有效區的長度即可 * 由於在遍歷的同時采用了時間復雜度為log(n)的二分查找,故時間復雜度為O(n*log(n)) */ /* * 實現solution2 */ public class LongestSubSequence { public int getLonggestLen(int[] nums) { int maxLen = 0; if (nums.length == 0) return maxLen; int[] h = new int[nums.length]; h[0] = nums[0]; maxLen = 1; for (int i = 1; i < nums.length; i++) { int pos = getPos(h, 0, maxLen - 1,nums[i]); if (pos == -1) { h[maxLen++] = nums[i]; } else h[pos] = nums[i]; } return maxLen; } public int getPos(int[] h, int left, int right, int num) { if (left <= right) { int mid = left + (right - left) / 2; if (h[mid] >= num) { int pos = getPos(h, left, mid - 1, num); if (pos == -1) return mid; return pos; } else { return getPos(h, mid + 1, right, num); } } return -1; } public static void main(String[] args){ LongestSubSequence l = new LongestSubSequence(); int[] nums = {1,3,6,7,9,4,10,5,6}; l.getLonggestLen(nums); } }
回歸到本題的解法

import java.util.Arrays; import java.util.Comparator; /* * 題意:CackingCodingInterview上的疊羅漢問題,leetcode上的沙皇問題 * 將最長遞增子序列擴展到了二維 */ /* * solutions : 時間復雜度O(n+n*log(n)+n*log(n)) 也就是n*log(n) * 目標是將二維數組抽象成一維數組,再利用解最長遞增子序列的解法來解決這個問題 * 首先我們需要將二維數組排序,排序策略是先按照array[i][0]排序,在array[i][0]相同的情況下,按照array[i][1]倒序排序 * 為了排序我們可將array抽象成一個類A,二元組的兩個元素分別是這個類A的兩個成員變量 * 將array轉化為A的數組,給A自實現一個排序函數(按照上面的排序策略) * 接下來就轉化為了最長遞增子序列問題,將類A數組的h成員變量看作之前的一維數組就好了 */ public class Leetcode354 { public int maxEnvelopes(int[][] envelopes) { if (envelopes.length < 2) return envelopes.length; A[] a = new A[envelopes.length]; for (int i = 0; i < envelopes.length; i++) { a[i] = new A(envelopes[i][0],envelopes[i][1]); } Arrays.sort(a,new Comparator<A>(){ @Override public int compare(A a, A b) { if (a.w > b.w) { return 1; } else if (a.w < b.w) { return -1; } else { if (b.h > a.h) { return 1; } else if (b.h < a.h) { return -1; } else return 0; } } }); int maxLen = 0; int[] h = new int[envelopes.length]; h[0] = a[0].h; for (int i = 1; i < envelopes.length; i++) { int pos = getPos(h, 0, maxLen - 1,a[i].h); if (pos == -1) { h[maxLen++] = a[i].h; } else h[pos] = a[i].h; } return maxLen; } public int getPos(int[] h, int left, int right, int num) { if (left <= right) { int mid = left + (right - left) / 2; if (h[mid] >= num) { int pos = getPos(h, left, mid - 1, num); if (pos == -1) return mid; return pos; } else { return getPos(h, mid + 1, right, num); } } return -1; } public static void main(String[] args){ Leetcode354 l = new Leetcode354(); int [][] envelopes = {{5,1},{6,4},{6,7},{2,3}}; l.maxEnvelopes(envelopes); } } class A { int w = 0; int h = 0; public A(int w, int h) { this.w = w; this.h = h; } }
第二題

/* * 題意:詳見2016 《牛課堂第一節課課件.pdf》第2題,leetcode42原題 */ /* * solution1:時間復雜度 O(n) 空間復雜度O(n) * 首先簡化一下題意:如果能求得當前位置格子上的水量,那么總水量就是每個位置水量之和 * 當前格子上所能存儲的水量 = 當前格子左邊最大值與右邊最大值之間的較小值 - 當前格子高度 * 所以要先求出當前格子左邊的最大值與右邊最大值,對於右邊最大值用數組r來輔助存儲,從右往左遍歷一下原數組即可得到r * 對於左邊的最大值在遍歷原數組的同時用一個全局變量記錄下來就行,此時時間復雜度為O(n) 空間復雜度O(n),還沒達到最優解 * * solution2:時間復雜度 O(n) 空間復雜度O(1) * 為了不使用輔助數組,我們采用雙指針的方法 * 雙指針left和right分別指向數組的第二個元素和倒數第二個元素,由題意可知第一個元素和最后一個元素的儲水量都是零 * 當前元素的遍歷則從第二個和倒數第二個元素開始,用leftMax和rightMax分別記錄左邊最大值和右邊最大值,用一個全局變量totalWater 更新總的儲水量 * 若leftMax < rightMax則結算左指針height[left],leftMax > height[left]時,totalWater += leftMax - height[left++],反之更新左邊最大值leftMax = height[left++],左指針向右移一位; * 反之,結算右邊的當前元素,過程與左邊類似 */ public class Question1 { public int getWater(int[] height){ if (height == null || height.length < 3) return 0; int totalWater = 0; int left = 1, right = height.length - 2; int leftMax = height[0], rightMax = height[height.length - 1]; while (left <= right) { if (leftMax < rightMax) { if (leftMax > height[left]) { totalWater += leftMax - height[left++] ; } else { leftMax = height[left++]; } } else { if (rightMax > height[right]) { totalWater += rightMax - height[right--] ; } else { rightMax = height[right--]; } } } return totalWater; } public static void main(String[] args){ Question1 q = new Question1(); int[] height = {0,1,0,2,1,0,1,3,2,1,2,1}; q.getWater(height); } }
第三題

/* * 題意:詳見2016 《牛課堂第一節課課件.pdf》第3題,leetcode11原題,找兩個邊界來裝最多的水 */ /* * solution : 時間復雜度 O(n) 空間復雜度O(1) * 還是利用雙指針left和right分別指向第一個元素和最后一個元素 * 每次移動雙指針中較小者,並更新最大裝水量 */ public class Question2 { public int maxArea(int[] height) { if (height == null || height.length < 2) return 0; int left = 0, right = height.length - 1; int max = 0; while (left <= right) { //用這句比較慢 // max = Math.max(max, (right-left)*Math.min(height[left], height[right])); if (height[left] < height[right]) { int cur = height[left] * (right - left); max = max > cur ? max : cur; left++; } else { int cur = height[right] * (right - left); max = max > cur ? max : cur; right--; } } return max; } }