題目:給定一個包含非負數的數組和一個目標整數 k,編寫一個函數來判斷該數組是否含有連續的子數組,其大小至少爲 2,總和爲 k 的倍數,即總和爲 n*k,其中 n 也是一個整數。
輸入: [23,2,4,6,7], k = 6
輸出: True
解釋: [2,4] 是一個大小爲 2 的子數組,並且和爲 6。
輸入: [23,2,6,4,7], k = 6
輸出: True
解釋: [23,2,6,4,7]是大小爲 5 的子數組,並且和爲 42。
說明:
- 數組的長度不會超過10,000。
- 你可以認爲所有數字總和在 32 位有符號整數範圍內。
思路:以sum[i] 表示從下標0 到 下標 i 子數組的和,則下標 i 到 j的子數組,記爲 [i,j]的和爲 sum[j] - sum[i-1], 子數組 [i,j] 如果是 k 的倍數,則有 (sum[j] - sum[i-1])%k 等於 0 ,即 sum[j]%k == sum[i-1]%k。 我們可以用一個 map結構存放 (sum[i]%k,index) 的值,index爲下標,再遍歷 map 結構, 1、當key==0時, * 只需要數組中有兩個連續的 0 即可 2、當key!=0時 1)如果對應的鏈表只有一個元素,即只有一個下標時,看是否該下標 i 大於1(因爲要求數組大小最少爲2),是的話,則證明sum[i]%k == 0,子數組[0,i]之和爲k的倍數 2)如果對應的鏈表有兩個及以上個元素,則說明一定滿足有個子數組長度大於2 ,且和爲k 的倍數的條件 注意: 子數組有兩個連續的0時,也算做滿足條件,因爲 (0 + 0) /k = 0
public boolean checkSubarraySum(int[] nums, int k) { boolean flag = false; if(nums.length<2){ return false; } Map<Integer,List<Integer>> map = new HashMap<>(); //key = sum[i]%k ,value = [index1,index2,...] if(k!=0) { int[] sum = new int[nums.length]; //存放數組從下標0開始,到當前下標i的和sum[i] for(int i=0;i<nums.length;i++){ if(i==0){ sum[i] = nums[i]; }else { if(nums[i] ==0 && nums[i-1] == 0){ // 子數組有兩個連續的0時,也算做滿足條件,因爲 (0 + 0) /k = 0 flag = true; break; } sum[i] = sum[i-1] + nums[i]; } } //填充 map 結構,key = sum[i]%k ,value = [index1,index2,...] for(int i=0;i<sum.length;i++){ int key = sum[i]%k; if(!map.containsKey(key)){ List<Integer> list = new ArrayList<>(); list.add(i); map.put(key,list); }else { map.get(key).add(i); } } for(Map.Entry<Integer,List<Integer>> entry:map.entrySet()){ if(entry.getKey()==0){ if(map.get(0).size()>=2){ flag = true; break; }else if(map.get(0).get(0)>=1){ flag = true; break; } } List<Integer> list = entry.getValue(); for (int i= list.size()-1;i>0;i--){ int index = list.get(i); if(nums[index]!=0&&index-list.get(0)>1){ //排除類似於 [2,0] k = 2 這種情況 flag = true; break; } if(flag){ break; } } if(flag){ break; } } }else { for (int i = 1; i < nums.length; i++) { if(nums[i-1] == 0 && nums[i] ==0){ //當 k等於 0的時候,只需要數組中有兩個連續的 0 即可 flag = true; break; } } } return flag; }