array专题8
670 Maximum Swap
思路:先把整數(shù)分解成一個(gè)一個(gè)的數(shù),從0-n放著從最低位到最高位的數(shù)字。例如2376變成數(shù)組[6,7,3,2]。假設(shè)要替換的是最高位n-1,從0到n-2中查找是否有比nums[n-1]大的元素;如果有則替換,否則繼續(xù)考慮替換n-2位。比較的時(shí)候一定要從0位開(kāi)始,因?yàn)槿绻?第1,2下標(biāo)元素相同,把第1位的元素?fù)Q到高位數(shù)字更大。樣例數(shù)據(jù)1993,替換以后是9913。
學(xué)習(xí):首先,記錄0-9這十個(gè)數(shù)字在num中的最低位數(shù)。其次,遍歷每個(gè)位置,找一找從9到digits[i]+1這幾個(gè)數(shù)字中是不是存在可以替換的數(shù)值。
代碼
289 Game of Life
思路:這個(gè)題目的難點(diǎn)是既要根據(jù)數(shù)組情況修改board[i][j]的新?tīng)顟B(tài),還要能作為鄰居找到board[i][j]原來(lái)的狀態(tài)。可以使用如下策略:
live->die 2
live->live 1
die->die 0
die->live 3
最后再遍歷一次,把所有值%2。
代碼
792 Number of Matching Subsequences
思路:暴力方法不再敘述。
學(xué)習(xí):把words的第一個(gè)字符以<character,string><character,string><script type="math/tex" id="MathJax-Element-1"></script>的數(shù)據(jù)結(jié)構(gòu)存儲(chǔ),接著遍歷S中的每一個(gè)字符,找到在該字符下的words,刪除words的字符,更新word。當(dāng)word的長(zhǎng)度為1時(shí),表示匹配成功,計(jì)數(shù)。這里使用雙向列表是一個(gè)技巧之處。(參考)
學(xué)習(xí)2:第二種思路是:s的長(zhǎng)度為n,計(jì)算每個(gè)位置i之后 [a-z]首次出現(xiàn)的位置。這樣,當(dāng)檢查每一個(gè)word的時(shí)候,只要查看位置就可以。
例如 s=”abcde”。
指針在位置0(也就是a前面),那么a、b、c、d、e首次出現(xiàn)的位置分別為1,2,3,4,5。
在遇到’a’以后,a、b、c、d、e首次出現(xiàn)的位置分別為-1,2,3,4,5,如此循環(huán)下去。
在匹配單詞bb的時(shí)候,首先指針在0,b的位置是2。指針在2,b的位置是-1.所以找不到。
代碼
11 Container With Most Water
思路:根據(jù)題意,我們知道取兩條邊中最小的值*底長(zhǎng)得到面積,找到最大面積。用暴力枚舉是可以算得的,但會(huì)超時(shí)。
學(xué)習(xí):減少循環(huán)的核心思路是省去沒(méi)有必要的遍歷,并且確保所需的答案一定能被遍歷到。
算法描述是:假設(shè)現(xiàn)在有一個(gè)容器,則容器的盛水量是由容器的底和高共同決定的。則我們可以從最大的底入手。設(shè)兩個(gè)指針一個(gè)在最右邊,一個(gè)在最左邊。接著移動(dòng)較小值的指針,左指針右移,右指針左移。直到兩指針相遇。遍歷結(jié)束。計(jì)算出最大值。
算法證明:首先解釋為什么移動(dòng)較小值的指著。假設(shè)left值小于right值。如果移動(dòng)right,底肯定會(huì)變小,如果移動(dòng)之后:
1 新的right<<<script type="math/tex" id="MathJax-Element-2"><</script>left,那么高也變小,容量變小;
2 新的right>=>=left,那么高不變,底變小,容量變小。
所以移動(dòng)值比較大的指針沒(méi)有意義。(ps:移動(dòng)值小的指針,可以認(rèn)為我們是在枚舉高度)。
其次證明這種移動(dòng)方式肯定會(huì)經(jīng)過(guò)容量最大的點(diǎn)。假設(shè)容量最大的時(shí)候指針?lè)謩e為p_left和p_right。根據(jù)題設(shè),遍歷的時(shí)候左指針先到達(dá)p_left,右指針還沒(méi)有到達(dá)p_right,假設(shè)右指針在p_right+n。這個(gè)時(shí)候會(huì)移動(dòng)的條件是p_left<(p_right+n)p_left<(p_right+n)。如果是這樣最大面積就不是p_left,p_right兩指針。因?yàn)?span id="ozvdkddzhkzd" class="MathJax_Preview" style="color: inherit; display: none;">p_left?([p_right+n]?p_left)的底>p_left?(p_right?p_left)的底p_left?([p_right+n]?p_left)的底>p_left?(p_right?p_left)的底。這與假設(shè)矛盾。所以一定會(huì)經(jīng)過(guò)容量最大的點(diǎn)。
學(xué)習(xí)2:不但要能想到算法描述,更需要證明算法正確。
參考
代碼
713 Subarray Product Less Than K
思路:首先肯定想到暴力枚舉,超時(shí)。接著考慮有哪些計(jì)算是重復(fù)的。以nums=[10, 5, 2,6]為例。
計(jì)算i=0,遍歷j=0,1,2,3;計(jì)算i=1,遍歷j=1,2,3;如果[0,2]符合要求,那么[1,2]一定是符合要求的,進(jìn)一步考慮[0],[0,1],[0,2],[1,2]都是符合要求的。當(dāng)然我思考到這里就進(jìn)行不下去了。
學(xué)習(xí):slide window的思想。
10:count+1
10*5<100:count+2,因?yàn)閇5],[10,5]
10*5*2=100:count+2 因?yàn)?#xff1a;[2],[5,2]
5*2*6<100:count+3 因?yàn)閇6][6,2][6,2,5]
需要保持最大的乘積<<<script type="math/tex" id="MathJax-Element-6"><</script>k的窗口;
每次向右移動(dòng)一個(gè)數(shù)值nums[j];如果右移后乘積>=>=k,就增加i,減少左邊的數(shù)值,直到乘積<k;
每一步都會(huì)新增(j-i+1)個(gè)子數(shù)組。
這種滑動(dòng)窗口,或者說(shuō)每一步增加一個(gè)元素,考慮是否改變數(shù)組起止位置的思想與795 Number of Subarrays with Bounded Maximum很類(lèi)似。如果能從一步計(jì)算中推出多個(gè)結(jié)論,就可以減少計(jì)算次數(shù)。降低復(fù)雜度。
代碼
33 Search in Rotated Sorted Array
思路:這是要在一個(gè)有旋轉(zhuǎn)的排序數(shù)組中查找。
學(xué)習(xí):一種思路是找到旋轉(zhuǎn)點(diǎn)的位置每次找mid,找到的是(mid+ratedIdx)%n的位置
學(xué)習(xí)2:使用最大值、最小值使得數(shù)組有序。我理解的不太好。查看網(wǎng)頁(yè)。文中提到的If nums[mid] and target are “on the same side” of nums[0], we just take nums[mid].這句話(huà)意思是:nums[0]<nums[mid]?&&?nums[0]<targetnums[0]<nums[mid]?&&?nums[0]<target 或者 nums[0]>nums[mid]?&&?nums[0]>targetnums[0]>nums[mid]?&&?nums[0]>target
代碼
學(xué)習(xí)3:因?yàn)閞otate的緣故,當(dāng)我們切取一半的時(shí)候可能會(huì)出現(xiàn)誤區(qū),所以我們要做進(jìn)一步的判斷。具體來(lái)說(shuō),假設(shè)數(shù)組是A,每次左邊緣為l,右邊緣為r,還有中間位置是m。在每次迭代中,分三種情況:
(1)如果target==A[m]target==A[m],那么m就是我們要的結(jié)果,直接返回;
(2)如果A[m]<A[r]A[m]<A[r],那么說(shuō)明從l到m一定是有序的,同樣只需要判斷target是否在這個(gè)范圍內(nèi),相應(yīng)的移動(dòng)邊緣即可。
根據(jù)以上方法,每次我們都可以切掉一半的數(shù)據(jù),所以算法的時(shí)間復(fù)雜度是O(logn),空間復(fù)雜度是O(1)。(原文鏈接)
81 Search in Rotated Sorted Array II
學(xué)習(xí):本題和上一題唯一的區(qū)別是這道題目中元素會(huì)有重復(fù)的情況出現(xiàn)。不過(guò)正是因?yàn)檫@個(gè)條件的出現(xiàn),出現(xiàn)了比較復(fù)雜的case,甚至影響到了算法的時(shí)間復(fù)雜度。原來(lái)我們是依靠中間和邊緣元素的大小關(guān)系,來(lái)判斷哪一半是不受rotate影響,仍然有序的。而現(xiàn)在因?yàn)橹貜?fù)的出現(xiàn),如果我們遇到中間和邊緣相等的情況,我們就丟失了哪邊有序的信息,因?yàn)槟倪叾加锌赡苁怯行虻慕Y(jié)果。假設(shè)原數(shù)組是{1,2,3,3,3,3,3},那么旋轉(zhuǎn)之后有可能是{3,3,3,3,3,1,2},或者{3,1,2,3,3,3,3},這樣的我們判斷左邊緣和中心的時(shí)候都是3,如果我們要尋找1或者2,我們并不知道應(yīng)該跳向哪一半。解決的辦法只能是對(duì)邊緣移動(dòng)一步,直到邊緣和中間不在相等或者相遇,這就導(dǎo)致了會(huì)有不能切去一半的可能。所以最壞情況(比如全部都是一個(gè)元素,或者只有一個(gè)元素不同于其他元素,而他就在最后一個(gè))就會(huì)出現(xiàn)每次移動(dòng)一步,總共是n步,算法的時(shí)間復(fù)雜度變成O(n)。(來(lái)自網(wǎng)頁(yè))
代碼
209 Minimum Size Subarray Sum
思路:這道題目與713很類(lèi)似。一個(gè)是求子數(shù)組的和,一個(gè)是求子數(shù)組的乘積。都可以使用滑動(dòng)窗口的思想。這里要求得到的是子數(shù)組和>=s>=s的最小長(zhǎng)度。兩指針,一個(gè)指向窗口的開(kāi)始位置,一個(gè)指向窗口的結(jié)束位置。(參考網(wǎng)頁(yè))
學(xué)習(xí):二分查找。輸入的數(shù)組沒(méi)有順序,而且也不能排序,那怎么用二分查找呢?sum[i]表示從下標(biāo)0到i-1區(qū)間元素的和。因?yàn)檩斎氲脑厥钦麛?shù),所以sum[i]是一個(gè)遞增函數(shù)。最短的數(shù)組長(zhǎng)度,也就是找sum[i,j]的和。sum[i,j]=sum[0,j]?sum[0,i?1]>=ssum[i,j]=sum[0,j]?sum[0,i?1]>=s。對(duì)于每個(gè)sum[0,i-1]找到>sum[0,i?1]+s>sum[0,i?1]+s的最小的值,更新數(shù)組長(zhǎng)度。
代碼
228 Summary Ranges
思路:這道題目簡(jiǎn)單。只要判斷前后元素差距不等于1,就斷開(kāi)。需要注意的是[2147483648,-2147483647,2147483647]這樣的數(shù)組。
代碼
56 Merge Intervals
思路:合并。用每一個(gè)Interval和其他所有Interval去比較合并。
學(xué)習(xí):改進(jìn)思路是按照Interval的start升序排列。這樣就不需要和每個(gè)Interval比較。
代碼
16 3Sum Closest
思路:找三個(gè)數(shù)的和最近接一個(gè)目標(biāo)值。首先想到三個(gè)數(shù)的和可能大于,也可能小于目標(biāo)值。其次,枚舉其中某一個(gè)值,然后使用兩指針從頭、尾開(kāi)始相加。當(dāng)然數(shù)組要求是排序了的。
代碼
55 Jump Game
思路:初看是一道非常簡(jiǎn)單的題目。但是沒(méi)有正確理解題意。第一點(diǎn):每一個(gè)nums[i]表示最多能跳多少個(gè)元素,而不是必須跳那么多個(gè)元素;第二點(diǎn):如果能夠到達(dá)最后一個(gè)元素位置就返回true。有時(shí)候按照最遠(yuǎn)的跳,可能超過(guò)最后一個(gè)元素了,也是可以的。當(dāng)然第一點(diǎn)理解正確了,第二點(diǎn)也就正確的。第三:如果輸入[0],那么初始化的時(shí)候就已經(jīng)到了最后一個(gè)元素的位置了。我按照遞歸的代碼實(shí)現(xiàn)了一版,超時(shí)。
public boolean canJump(int[] nums) {return jump(nums,nums.length-1);}private boolean jump(int[] nums,int idx){if(idx<0){return false;}if(idx==0){return nums[idx]>0 || nums.length==1;}for(int i=idx-1;i>=0;i--){if(nums[i]>=idx-i){if(jump(nums,i)){return true;}}}return false;}思路2:用數(shù)組表示是否能夠到達(dá)i的結(jié)果保存起來(lái),用dp[i]表示。發(fā)生棧溢出。遞歸改為迭代?失敗,沒(méi)有完成。
學(xué)習(xí)1:基本事項(xiàng)是:在每一步,記錄下當(dāng)前能夠達(dá)到的最遠(yuǎn)的下標(biāo)。我們循環(huán)遍歷每一個(gè)下標(biāo),如果發(fā)現(xiàn)當(dāng)前下標(biāo)是不可到達(dá)的,也就是說(shuō)(當(dāng)前idx>reachableIdx),那就退出循環(huán),返回false。
學(xué)習(xí)2:參考網(wǎng)頁(yè)
169 Majority Element
思路:最開(kāi)始的思路,一定是用個(gè)map記錄每個(gè)元素出現(xiàn)次數(shù)。最后返回次數(shù)最多的那個(gè)元素。
學(xué)習(xí):因?yàn)橹髟氐某霈F(xiàn)次數(shù)比其他所有元素出現(xiàn)次數(shù)的總和還要多1,或者相同,那么就用元素相同次數(shù)加1,元素不同次數(shù)減1,來(lái)過(guò)濾掉最后剩下的元素。Moore voting algorithm摩爾投票算法。
學(xué)習(xí)2:排序后,直接返回nums[nums.length/2]位置的元素。
學(xué)習(xí)3:位運(yùn)算。一個(gè)元素的二進(jìn)制表示是相同的。所有元素,計(jì)算二進(jìn)制位置每個(gè)位置出現(xiàn)次數(shù)。
代碼
229 Majority Element II
學(xué)習(xí):229是169的升級(jí)版,摩爾投票算法的一般情況。
代碼
總結(jié)
- 上一篇: 理解transformer
- 下一篇: 【讨论】新一轮互联网的泡沫即将破灭,大量