array专题6
78. Subsets
思路1:深度優先搜索:每個位置都有選,和 不選兩種選擇。退出條件是下標出界。
public List<List<Integer>> subsetsV3(int[] nums) {List<List<Integer>> result = new ArrayList<List<Integer>>();List<Integer> list = new ArrayList<Integer>();dfs(result, list, nums, 0);return result;}private void dfs(List<List<Integer>> result, List<Integer> list, int[] nums, int idx) {if(idx==nums.length){result.add(new ArrayList<Integer>(list));return;}dfs(result, list, nums, idx+1);list.add(nums[idx]);dfs(result, list, nums, idx+1);list.remove(list.size()-1);}思路2:使用位操作。有一個二進制數組,對應數組中每個元素是不是選擇。例如數組[1,2,3],有一個二進制數組[0,0,0]表示一個元素都不選擇;[0,0,1]選擇nums[0]元素;[0,1,0]選擇nums[1]元素;依次….
public List<List<Integer>> subsetsV2(int[] nums) {List<List<Integer>> result = new ArrayList<List<Integer>>();int n = nums.length;int total = (1 << n) - 1;// 2^n-1for (int i = 0; i <= total; i++) {List<Integer> list = new ArrayList<Integer>();for(int j=0;j<n;j++){if(((i>>j) &1)==1){list.add(nums[j]);}}result.add(list);}return result;}代碼
90. Subsets II
思路:與78的深度優先搜索思路相同,只是要自己試試看哪些情況下出現重復的情況,在深度優先搜索過程中去掉這些情況。
代碼
729 My Calendar I
思路:挨個比較
學習:放在map中,floorKey(key):比key小的最大值,ceilingKey(key):比key大的最小的值
代碼
611 Valid Triangle Number
思路:這道題目首先會想到用深度優先搜索。每個位置的元素可以選擇或者不選擇。這樣肯定能解決,問題是會超時。這時候肯定是需要觀察,找規律。很遺憾,我沒找到規律。
學習:條件是:三角形任意兩邊之和>第三邊。如果兩個小的邊的和>最長的邊,那一定符合要求。先確定最大的邊:數組排序,下標最大的就是最大的邊。
例如數組[2,2,3,4]。如果i=3,接著使用兩個指針l=0,r=2;如果nums[0]+nums[2]>nums[3],那么,保持r不變,不斷增加l,直到l=r,那這個過程中所有的nums[l]+nums[r]>nums[i]。所以這中間的組合有r-l個。具體來說就是:因為nums[0]+nums[2]>nums[3]符合要求,那么nums[1]+nums[2]>nums[3]肯定符合要求,所以組合數有r-l=2-0=2個。
代碼
526 Beautiful Arrangement
思路:深度優先搜索,對于第i個位置,枚舉可能使用的數值。可能使用的數值需要滿足兩個條件: 1 還未被使用;2 能夠使得 數值%i=0或者 i%數值=0
學習:思路相同,但是迭代次數更少。
技巧一: backtracing從后面開始,這樣每個搜索在失敗之前都不會太深,因為較小的數字有更大的機會在他們自己之間被分割。解釋是這樣的。如果start從小到大,假如 start=1,nums[1]=4 ,那么4%1=0成立;start=4,nums[4]=3,那么4%3=0 或者 3%3=0都不成立;如果start從大到小,假如start=4, nums[4]=3,那么4%3=0 或者 3%3=0都不成立;則就不可能再去判斷 nums[4]=3情況下: start=3,2,1的情況了,這樣就會減少調用次數。
技巧二:Also I don’t use “visited” boolean array but use swap of an array of 1~N to avoid duplication.第start位能夠選擇的數字在nums[1~start]這些位置上的數字選擇,每當選擇一個,就把這個數字放在nums[start]位置上。這樣別的位置就不能再選擇這個數字了。聰明。
代碼
667 Beautiful Arrangement II
思路:深度優先搜索,為每個位置上取值,再判斷是否符合條件。超時。繼續觀察。想到差值最小是1,最大是n-1.如果 k<n?1k<n?1怎么處理?我想到的是隨機取。然后就思考不下去了。
學習:如果 k<n?1k<n?1,那就先取k-1個差值:n-1,n-2….,最后的一個差值取1。
1,n,2,n-1,3,n-2,4… ==> Diff: n-1, n-2, n-3, n-4, n-5… k個數有k-1個不同的差值。
例如n=9,k=5,則取數據:1 9 2 8 3 4 5 6 7
例如n=9,k=6,則取數據:1 9 2 8 3 7 6 5 4
代碼
238 Product of Array Except Self
思路:這道題目如果允許除法,很簡單;如果是O(n^2)也很簡單。所有數相乘,只是不乘以對應下標中nums[i]這個數。但是當要求不用除法,O(n)時間復雜度,就沒有思路了。
給定數組nums={2,3,4,5}。
結果應該是:
| 結果 | 1*3*4*5 | 2*1*4*5 | 2*3*1*5 | 2*3*4*1 |
我思考到這里,就不知道該怎么做了。
學習:學習了一個思路,太鬼才了。在上面的表格中按1分隔,分為從左邊乘和從右邊乘。
考慮下標i=2,結果應該是2*3*1*5。這個結果可以看做是左邊的2*3,和右邊的5相乘得到的結果。
| 左邊 | 2 | 2*3 | 2*3*4 | |
| 右邊 | 5*4*3 | 5*4 | 5 |
把空的地方填上1
| 左邊 | 1 | 2 | 2*3 | 2*3*4 |
| 右邊 | 5*4*3 | 5*4 | 5 | 1 |
通過兩次循環實現。
如果能看到2*3*4=4*3*2也許能想到從右向左。如果能看到以1分隔,分別是nums[i]左邊的數字和右邊的數字相乘,也許能想到解決方法把。
代碼
565 Array Nesting
思路:像環的檢測, Floyd環之前做過。最簡單:把每個i遍歷一次,比較長度 。做完之后打印一下環,發現一個環內的元素無論從哪個元素開始,得到的長度都相同。例如nums=[5,4,0,3,1,6,2]。對于{5, 6, 2, 0}這樣一個環,從下標0開始,長度是4;從下標2開始,長度還是4。所以需要去重,訪問過的就不再訪問了。提交后發現超時。
學習:這個時候我又在想我的思路錯了?是不是還有更好的思路。看了討論,發現是在去重那里優化。不需要用額外的空間存儲是否訪問過;訪問過的可以標記為-1,因為初始每個元素是在[0,N-1]之間的。
改進的時候有兩種:第一計算方式需要改進,這個時候需要重新觀察數據。例如238。第二種是去重方式可以優化。例如本題。
學習2:本題還有用并查集解決的。
nums=[5,4,0,3,1,6,2]
| nums | 5 | 4 | 0 | 3 | 1 | 6 | 2 |
| parent | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
| rank | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
union(0,5):5的父節點是0:parent[5]=0;
| nums | 5 | 4 | 0 | 3 | 1 | 6 | 2 |
| parent | 0 | 1 | 2 | 3 | 4 | 0 | 6 |
| rank | 1 | 0 | 0 | 0 | 0 | 0 | 0 |
union(1,4):4的父節點是1:parent[4]=1;
| nums | 5 | 4 | 0 | 3 | 1 | 6 | 2 |
| parent | 0 | 1 | 2 | 3 | 1 | 0 | 6 |
| rank | 1 | 1 | 0 | 0 | 0 | 0 | 0 |
union(2,0):0的父節點是2:rootP=2,rootQ=0,rank[rootQ] > rank[rootP]=>parent[rootP] = rootQ;
| nums | 5 | 4 | 0 | 3 | 1 | 6 | 2 |
| parent | 0 | 1 | 0 | 3 | 1 | 0 | 6 |
| rank | 1 | 1 | 0 | 0 | 0 | 0 | 0 |
union(3,3):3的父節點是3:parent[3]=3;
| nums | 5 | 4 | 0 | 3 | 1 | 6 | 2 |
| parent | 0 | 1 | 0 | 3 | 1 | 0 | 6 |
| rank | 1 | 1 | 0 | 0 | 0 | 0 | 0 |
union(4,1):1的父節點是4:parent[1]=4,parent[4]=1 return;
| nums | 5 | 4 | 0 | 3 | 1 | 6 | 2 |
| parent | 0 | 1 | 0 | 3 | 1 | 0 | 6 |
| rank | 1 | 1 | 0 | 0 | 0 | 0 | 0 |
union(5,6):6的父節點是5:parent[6]=6,parent[5]=0 rank[rootQ] < rank[rootP] =>parent[6]=0
| nums | 5 | 4 | 0 | 3 | 1 | 6 | 2 |
| parent | 0 | 1 | 0 | 3 | 1 | 0 | 0 |
| rank | 1 | 1 | 0 | 0 | 0 | 0 | 0 |
union(6,2): rootP=0,rootQ=0
| nums | 5 | 4 | 0 | 3 | 1 | 6 | 2 |
| parent | 0 | 1 | 0 | 3 | 1 | 0 | 0 |
| rank | 1 | 1 | 0 | 0 | 0 | 0 | 0 |
最終有4處的parent都是0,所以長度為4,是最長的環。
[代碼]
(https://github.com/Emma123456/leetcodepractice/blob/master/src/array/ArrayNesting565.java)
769 Max Chunks To Make Sorted
思路:刷leetcode,簡直就是在找虐。我想到了,題目要求返回最多的分區數量。那如果每個都可以單獨分開是最好的。但是不可以。例如nums=[1,0,2,3,4],0和1分開的話,排序就不對了,所以0和1必須在一起。那就是說,如果在當前位置i,如果arr[i+1]以及之后的數字都大于0到i位置的元素,那么就能分隔。這樣的話,需要O(n^2)的復雜度。應該可以簡化。
學習1:因為所有左邊的元素 < 右邊元素的時候可以形成新的分區,也就是說如果對于i下標:左邊元素的最大值 <<<script type="math/tex" id="MathJax-Element-8"><</script> (i+1)到(n-1)位置的最小元素 =>可以形成新的分區。學習到的思路就是:用maxOfLeft數組,記錄當當前位置最大值;而不是拿一個變量找全局最大值。
學習2:用一個max數組保存到當前位置最大的元素,每個位置的最大元素和已經排序好的數組中的元素比較,如果max[i] = sorted[i],那在這個位置就可以分割。
| max: | 1 | 1 | 2 | 3 | 4 |
| 排序好的數組: | 0 | 1 | 2 | 3 | 4 |
| 下標: | 0 | 1 | 2 | 3 | 4 |
在題目中說明arr[i] 在 [0,arr.length - 1]所以排序后的數組一定是0,1,2,….n-1。就可以進一步省略max數組,只需要max變量。這里就是重點理解 max==i。max是到目前位置的最大值;i是排序好以后i位置元素的值。
public int maxChunksToSortedV2(int[] arr) {int count = 0;int max = -1;for (int i = 0; i < arr.length; i++) {max = Math.max(max, arr[i]);if (max == i) {count++;}}return count;}代碼
總結
- 上一篇: 力克三维软装饰设计
- 下一篇: zabbix监控 openstack 的