算法68------數組和矩陣問題

目錄:

  1. 轉圈打印矩陣
  2. 將正方形矩陣順時針轉動90度
  3. 「之」字形打印矩陣
  4. 找到無序數組中最小的k個數
  5. 須要排序的最短子數組長度【找到比當前最小值左邊大的數和比最大值右邊大的數的索引】
  6. 在數組中找到出現次數大於N/K的數【刪除不一樣的數】
  7. 在行列都排好序的矩陣中找數
  8. 最長的可整合子數組的長度
  9. 不重複打印排序數組中相加和爲給定值的全部二元組和三元組【左右變量遍歷】
  10. 不重複打印排序數組中相加和爲給定值的N元祖【遞歸】
  11. 未排序正整數數組中累加和等於給定值的最長子數組長度【左右變量遍歷】
  12. 未排序數組中累加和爲給定值的最長子數組系列問題【arr[i……j】=s[j]-s[i],採用字典存儲和s
  13. 未排序數組中累加和小於或等於給定值的最長子數組長度【列表存s,二分法查找列表】
  14. 計算數組的小和
  15. 天然數數組的排序
  16. 奇數下標都是奇數或偶數下標都是偶數【even、odd變量變量】
  17. 奇數在前,偶數在後,且本來位置不變,即穩定的。【插入】
  18. 子數組的最大累加和問題【設置當前和變量,小於0置0】
  19. 子矩陣的最大累加和問題
  20. 在數組中找到一個局部最小的位置
  21. 數組中三個數的最大累乘積【幾個變量】
  22. 數組中子數組的最大累乘積 【動態規劃】
  23. 打印N個數組總體最大的Top K【堆排序】
  24. 邊界都是1的最大正方形大小【遍歷+動態規劃】
  25. 不包含本位置值的累乘數組【左右額外空間遍歷】
  26. 數組的partition調整【快排思想】
  27. 求最短通路值【寬度優先】
  28. 數組中未出現的最小正整數【左右變量遍歷】
  29. 數組排序以後相鄰數的最大差值【桶排序】
  30. 查找知足條件的二維數組最大值和次大值之和。【前面最大變量 + 後面最大變量】(RMQ)
  31. 數組中的逆序對【樹狀數組】
  32. RMQ算法【樹狀數組】

 

 

一、轉圈打印矩陣

思路:

一、設置左上角的座標(lu1,lu2)和右下角的座標(rd1,rd2),4個變量。按照題目打印python

(第一次:lu1=0,lu2=0---rd1=3,rd2=3)ios

二、打印完一次,左上角座標(lu1+=1,lu2+=1),右下角的座標(rd1 -= 1,rd2 -= 1),直到左上角座標在右下角座標的右下方。算法

(第二次:lu1=1,lu2=1---rd1=2,rd2=2)數組

代碼:

def printmatrix(lu1,lu2,rd1,rd2,res): if lu1 == rd1: for i in range(lu2,rd2+1): res.append(matrix[lu1][i]) elif lu2 == rd2: for i in range(lu1,rd1+1): res.append(matrix[i][lu2]) else: curl1 , curl2 = lu1 , lu2 while curl2 != rd2: res.append(matrix[lu1][curl2]) curl2 += 1
        while curl1 != rd1: res.append(matrix[curl1][rd2]) curl1 += 1
        while curl2 != lu2: res.append(matrix[rd1][curl2]) curl2 -= 1
        while curl1 != lu1: res.append(matrix[curl1][lu2]) curl1 -= 1
        
def OrderPrint(matrix): lu1 , lu2 = 0 , 0 rd1 , rd2 = len(matrix)-1 , len(matrix[0])-1 res = [] while lu1 <= rd1 and lu2 <= rd2: printmatrix(lu1,lu2,rd1,rd2,res) lu1 += 1 lu2 += 1 rd1 -= 1 rd2 -= 1 matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]] OrderPrint(matrix)

 二、將正方形矩陣順時針轉動90度

思路:

 和上一題同樣app

一、設置左上角的座標(lu1,lu2)和右下角的座標(rd1,rd2),4個變量。旋轉着改變矩陣curl

二、打印完一次,左上角座標(lu1+=1,lu2+=1),右下角的座標(rd1 -= 1,rd2 -= 1),直到左上角座標在右下角座標的右下方。函數

代碼:

def rotate(matrix,lu1,lu2,rd1,rd2): times = rd1 - lu1 temp = 0 for i in range(times): temp = matrix[lu1][lu2+i] matrix[lu1][lu2+i] = matrix[rd1-i][lu2] matrix[rd1-i][lu2] = matrix[rd1][rd2-i] matrix[rd1][rd2-i] = matrix[lu1+i][rd2] matrix[lu1+i][rd2] = temp def rorateEdge(matrix): if not matrix or len(matrix) == 0 or len(matrix[0]) == 0: return matrix lu1 , lu2 , rd1 , rd2 = 0 , 0 , len(matrix)-1 , len(matrix[0])-1
    while lu1 <= rd1 and lu2 <= rd2: rotate(matrix,lu1,lu2,rd1,rd2) lu1 += 1 lu2 += 1 rd1 -= 1 rd2 -= 1
    return matrix matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]] rorateEdge(matrix)

 三、「之」字形打印矩陣

思路:

 代碼:

def printZMatrix(matrix): if len(matrix) == 0 or len(matrix[0])==0: return matrix ld1 , ld2 = 0 , 0 ru1 , ru2 = 0 , 0 res = [] end1 , end2 = len(matrix)-1 , len(matrix[0])-1 sign = False while ru1 <= end1: printlr(matrix,ld1,ld2,ru1,ru2,res,sign) ru1 += 1 if ru2 == end2 else 0 ru2 += 1 if ru2 != end2 else 0 ld2 += 1 if ld1 == end1 else 0 ld1 += 1 if ld1 != end1 else 0 sign = ~sign return res def printlr(matrix,ld1,ld2,ru1,ru2,res,sign): # sign = True if ru2 % 2 == 0 else False
    
    if sign: while ru1 <= ld1: res.append(matrix[ru1][ru2]) ru1 += 1 ru2 -= 1
    else: while ld1 >= ru1: res.append(matrix[ld1][ld2]) ld1 -= 1 ld2 += 1 matrix = [[1,2,3,4],[5,6,7,8],[9,10,11,12]] printZMatrix(matrix)

 四、找到無序數組中最小的k個數

要求:要求時間複雜度爲O(Nlogk)和O(N)ui

若是先排序再找到最小的K個數,時間複雜度爲O(NlogN)url

【一個採用棧的思路:時間最壞爲O(NK)】

採用棧來存儲當前K個最小的值,棧中數值升序排序。spa

若棧的大小<K 且當前元素比棧頂元素大,則往棧中加元素。

不然,若當前元素比棧頂元素小,則將當前元素加入棧中,調整棧中元素的大小順序並保證棧的大小爲K。

代碼:

def sortarr(arr,k): if not arr or k >= len(arr): return arr stack ,temp = [] , [] for i in range(len(arr)): if not stack: stack.append(arr[i]) else: if len(stack) < k and arr[i] >= stack[-1]: stack.append(arr[i]) while stack and arr[i] < stack[-1]: temp.append(stack.pop()) if temp: stack.append(arr[i]) stack.extend(temp[1:][::-1]) temp = [] return stack arr = [1,3,7,9,4,6,3,1,2,5,0] k = 4 sortarr(arr,k)

 

O(Nlogk)的思路:創建K個元素最大堆【空間O(K)】

一直維護一個大小爲k的大根堆,這個堆表示目前選出的k個最小的數。

接下來遍歷整個數組,

遍歷過程當中看當前數是否比堆頂元素小,若是是的話,將堆頂元素替換成當前的數,而後從堆頂向下調整堆;

不然,不進行任何操做。遍歷結束後,堆中的k個數就是答案。

 代碼:【python的heapq只能創建一個最小堆,因此要創建最大堆,須要將數字取反放入堆中,取出時再取反】

import heapq def KNumHeap(arr,k): if k <= 0 or k >= len(arr) or len(arr) == 0: return arr max_heap = [] for i in range(len(arr)): cur = -arr[i] if len(max_heap) < k: heapq.heappush(max_heap,cur) else:
#heappushpop函數能夠將一個cur元素插入最大堆中,保持堆的大小不變,即若cur > 堆頂元素,則cur不插入,堆不變。不然,刪除堆頂元素,cur插入堆中正確位置 heapq.heappushpop(max_heap,cur)
return list(map(lambda x:-x,max_heap)) arr = [1, 9, 2, 4, 7, 6, 3] k = 3 KNumHeap(arr,k)

 

 五、須要排序的最短子數組長度

給定一個無序數組arr,求出須要排序的最短子數組長度。

例如:arr = 【1,5,3,4,2,6,7】返回4,由於只有【5,3,4,2】須要排序。

思路:時間O(N),空間O(1)

從右往左遍歷,找最小值,若是最小值左邊有比其大的數,索引就到此處。

從左往右遍歷, 找最大值,若是最大值右邊有比其小的數,索引就到此處。

如如下,紅圈2爲從右往左遍歷到的最小值,左邊比其大的數爲其左邊綠框的5,3,8,4。【爲什麼不是1,由於1,2已在最左邊放好】,nominindex = 2

紅圈9爲從左往右遍歷獲得的最大值,右邊比其小的數爲4。maxindex = 10

結果巍峨10-2+1=9

代碼:

def getMinLen(arr): if not arr or len(arr) < 2: return 0 minnum = arr[len(arr)-1] nominindex = -1
    for i in range(len(arr)-2,-1,-1): if arr[i] > minnum: nominindex = i else: minnum = min(minnum,arr[i]) if nominindex == -1: return 0 maxnum = arr[0] nomaxindex = -1
    for i in range(1,len(arr)): if arr[i]<maxnum: nomaxindex = i else: maxnum = max(maxnum,arr[i]) return nomaxindex - nominindex + 1 arr = [1,2,5,3,8,4,2,6,7,9,4] getMinLen(arr)

 

六、在數組中找到出現次數大於N/K的數

思路:找到出現最多的那個數且次數超過一半。方法是遍歷過程當中成對刪除不一樣的數,一個數若是出現次數超過一半,剩下的數必是該數,不然多是也可能不是。如:

【4,3,7,4,4,4,5】,設置一個cand和time變量。開始,

開始,cand = 4,time= 1,

遇到下一個數3,二者不一樣,time - 1 = 0

遇到下一個數7,由於time=0,因此cand重置爲=7,time=1

遇到下一個數4,cand和4不一樣,因此time - 1 =0

遇到下一個數4,time = 0,cand重置爲4,time= 1

遇到下一個數4,cand和4相同,time + 1 = 2

遇到下一個數5,cand和5不一樣,time - 1 = 1

故:剩下的數cand爲4。

而後從頭開始遍歷,判斷4是否出現超過一半【由於若是沒超過一半也可能cand爲4,如 [ 4,3,4,5]】

代碼:

def printHalfMajor(arr): if arr == None or len(arr) == 0: print("No such number!") return False cand = 0 time = 0 for i in range(len(arr)): if time == 0: cand = arr[i] time += 1
        elif cand == arr[i]: time += 1
        else: time -= 1 time = 0 for i in range(len(arr)): if arr[i] == cand: time += 1
    if time > len(arr)//2: return True else: return False arr=[4,3,4,3,4,6,5,4] printHalfMajor(arr)

進階問題思路:一次刪掉K個不一樣的數,屢次刪除,直到剩下的數種類不足K。若一個數出現次數超過N/K,該數最後必定會剩下。

一個map記錄K個不一樣的數,一旦map大小==k-1,且下一個arr[i]不在map中,就能夠開始刪除map中K個不一樣值。

代碼:

def printHalfMajor(arr,k): if arr == None or len(arr) <= k: print("No such number!") return False #重點:每次刪除K種不一樣的值【採用字典存儲K種不一樣的值】
    map = {} for i in range(len(arr)): if arr[i] in map: map[arr[i]] += 1
        else: if len(map) == k-1: for key in list(map): map[key] -= 1
                    if map[key] == 0: del map[key] else: map[arr[i]] = 1
    #查詢map剩餘的數在原數組中的數量
    time = 0 flag = False for key in map: for i in range(len(arr)): if arr[i] == key: time += 1
        if time > len(arr)//k: flag = True print(key) time = 0 return flag arr=[4,3,4,3,4,6,5,4] K = 3 printHalfMajor(arr,K)

 七、在行列都排好序的矩陣中找數

思路:

八、最長的可整合子數組的長度

 

思路:時間O(n3),空間O(n)

代碼:

def longestSubArr(arr): if not arr: return 0 res = 0 for i in range(len(arr)): for j in range(i,len(arr)): if isValid(arr[i:j+1]): res = max(res,j - i + 1) return res def isValid(arr): sortArr = sorted(arr) for i in range(len(sortArr)-1): if sortArr[i] != sortArr[i+1] - 1: return False return True arr = [5,5,3,2,6,4,3] longestSubArr(arr) 

 思路:時間O(n2),時間是在驗證階段縮短的,採起的驗證方法是無重複數且總個數=最大值-最小值+1。

 

代碼:採用一個集合來判斷是否有重複數。

 九、不重複打印排序數組中相加和爲給定值的全部二元組和三元組

 二元組的思路:時間O(N)

 

 代碼:

def two(arr,k): if not arr or len(arr)<2: return None res = [] left = 0 right = len(arr) - 1
    while left < right: if arr[left] + arr[right] == k: if left == 0 or left > 0 and arr[left] != arr[left-1]: print(arr[left],arr[right]) left += 1 right -= 1
            
        elif arr[left] + arr[right] < k: left += 1
        elif arr[left] + arr[right] > k: right -= 1 arr=[-8,-4,-3,0,1,2,4,5,8,9] k=10 two(arr,k)

 

 三元組思路:時間O(N2)

 代碼:

def two(arr,f,l,r,k): while l < r: if arr[l] + arr[r] < k: l += 1
        elif arr[l] + arr[r] > k: r -= 1
        else: if l == f+1 or arr[l-1] != arr[l]: print(arr[f], ',' , arr[l] , ',' , arr[r]) l += 1 r -= 1
def three(arr,k): if not arr or len(arr) < 3: return False for i in range(len(arr)): if i == 0 or arr[i] != arr[i-1]: two(arr,i,i+1,len(arr)-1,k-arr[i]) arr = [-8,-4,-3,0,1,2,4,5,8,9] k = 10 three(arr,k)

十、找出數組中和爲K的一組解【遞歸+動態規劃】

考慮N個數的數組A中是否存在和爲K的解,能夠分爲考慮(N-1個數中是否有和爲K的解)或者(N-1個數中是否有和爲K-A[N-1]的解(解包含A[N-1]的狀況))。

也就是說,動態規劃方程是:P[n][k]=P[n-1][k] || P[n-1][ k-arr[n-1] ];

另外一種解法:https://www.tuicool.com/articles/eEfe2mf【採用棧】

 
 

#include<vector>
#include<iostream>
using namespace std;
vector<int> ivec;
 
 
void findSum(int arr[],int n,int sum)
{
  if(sum<=0||n<=0)return; 
  if(arr[n] == sum){
    vector<int>::iterator it;
    for(it=ivec.begin();it!=ivec.end();++it)
      cout<<*it<<"+";
    cout<<arr[n]<<endl;
  }
  ivec.push_back(arr[n]);
  findSum(arr,n-1,sum-arr[n]);
  ivec.pop_back();
  findSum(arr,n-1,sum);
}
int main()
{
  int arr[]={1,2,4,5,6};
  findSum(arr,4,12);
  return 0;
}
 

 十一、未排序正整數數組中累加和等於給定值的最長子數組長度

給定一個數組arr,該數組無序,但每一個值爲正數,再給定一個正數k。求arr的全部子數組中全部元素相加和爲k的最長子數組長度。

例如,arr=【1,2,1,1,1】,k=3。

累加和爲3的最長子數組爲【1,1,1】,因此結果爲3

思路:時間O(N),空間O(1)

設置兩個指針變量left,right。遍歷數組,直到 right < len(arr)。不用擔憂left,由於只要left>right,下一步right就會+1.初始狀態,二者都爲0。

變量lrsum記錄,left到right的和。變量res記錄結果,即=k的子數組最長長度。

  • 若 lrsum == k:【left右移】

res = max(res,right - left +1)

lrsum -= arr[left]

left += 1

  • 若 lrsum < k:【right右移】

right += 1

若right==len(arr):break

lrsum += arr【right】

  • 若  lrsum > k:【left右移】

lrsum -= arr[left]

left += 1

代碼:

def getMaxlen(arr,k): if not arr or len(arr)<1: return 0 left , right = 0 , 0 res , lrsum = 0 , arr[0] while right < len(arr): if lrsum == k: res = max(res,right - left + 1) lrsum -= arr[left] left += 1
            
        elif lrsum < k: right += 1
            if right == len(arr): break lrsum += arr[right] else: lrsum -= arr[left] left += 1 arr = [2,2,9,2] k = 4 getMaxlen(arr,k)

十一、未排序數組中累加和爲給定值的最長子數組系列問題

題目:

給定一個無序數組arr,其中元素可正、可負、可0,給定一個整數k,求arr全部的子數組中累加和爲k的最長子數組長度。

補充題目:

給定一個無序數組arr,其中元素可正、可負、可0。求arr全部的子數組中正數與負數個數相等的最長子數組長度。

補充題目:

給定一個無序數組arr,其中元素只是1或0,求arr全部的子數組中0和1個數相等的最長子數組長度。

原問題思路:時間O(N)、空間O(N)

s【i】表明子數組arr【0……i】全部元素的累加和。

arr【j……i】= s【i】- s【j】

題目要求的是若arr【j……i】==k,求最長的j-i+1

好比:arr = 【5,1,2,3,3】,k=6。

s[3] = 11,s[0] =5,sum(arr 【1:3】)= s[3] - s[0] = 6,長度爲3

s[4] = 14,s[2] = 8, sum(arr 【3:4】)= s[4] - s[2] = 6,長度爲2

結果爲3

採用字典存儲s

代碼:

 

def maxLen(arr,k): if not arr or len(arr) == 0: return 0 dic = {} dic[0] = -1 res , nsum = 0 , 0 for i in range(len(arr)): nsum += arr[i] if (nsum - k) in dic: res = max(i - dic[nsum - k],res) if nsum not in dic: dic[nsum] = i return res arr = [5,1,2,3,3] k = 6 maxLen(arr,k)

 補充問題思路:

 十二、未排序數組中累加和小於或等於給定值的最長子數組長度

 給定一個無序數組arr,其中元素可正、可負、可0,給定一個整數k。求arr全部的子數組中累加和小於或等於k的最長子數組長度。

例如:arr = [3,-2,-4,0,6],k = -2,相加和小於或等於-2的最長子數組爲{3,-2,-4,0},因此結果返回4。

思路:

題目要求的是:若arr【i……j】<= k,求最長的j-i+1。

arr【i……j】 = s[j] - s[i] ,(s[i] 表示 arr[0……i] 的值)

故判斷 s[j] - s[i] <= k,即 判斷 s[j] - k <= s[i]。

對於s[j] - k 找到第一個s[i] >= s[j] - k,就是最長的j-i+1。故建立一個輔助的列表helper,存儲s[i] 當前出現的最大值。例如:arr = [1,2,-1,5,-2],k= -2 ,則 s[i] 列表爲 [ 0,1,3,2,7,5],則helper = 【0,1,3,3,7,7】

好比當 j = 3時,【查找用二分法,時間複雜度爲O(logN)】要找到 比 arr[3] - k = 5 大的第一個 s[i] 所在的位置i,所求的結果就爲 j - i+ 1。藉助helper輔助列表可知,第一個比5大的數爲7,在 i = 4的位置,此時 表示遍歷到 j = 3但只有在i=4時才能找到比5大的數,故j-i+1=0表示 arr【0……3】沒有比 k小於或等於的數。

總結步驟:

一、建立一個輔助列表。

二、遍歷數組,計算s[i] ,採用二分法查找輔助列表,找到第一個比s[i] - k大的數。

代碼:

def maxLen(arr,k): if not arr or len(arr) <= 0: return 0 helper = [0] * (len(arr)+1) nsum = 0 for i in range(len(arr)): nsum += arr[i] helper[i+1] = max(nsum,helper[i]) nsum = 0 res = 0 pre = 0 nlen = 0 for i in range(len(arr)): nsum += arr[i] tmp = getcurMax(helper,nsum - k) nlen = 0 if tmp == -1 else (i-tmp + 1) res = max(res,nlen) return res def getcurMax(arr,nsum): left = 0 right = len(arr) - 1 res = -1
    while left <= right: mid = (left + right) //2
        if arr[mid] >= nsum: right = mid - 1 res = mid else: left = mid + 1
    return res arr = [-1,-2,-1,-5,-2] k = -2 maxLen(arr,k)

 1三、計算數組的小和

思路:時間複雜度O(NlogN),空間O(N)

 

 

1五、奇數下標都是奇數或偶數下標都是偶數

給定一個長度不小於2的數組arr,實現一個函數調整arr,要麼讓全部的偶數下標都是偶數,要麼讓全部的奇數下標都是奇數。

思路:時間O(N),空間O(1)

設置三個變量,even下標、odd下標、end下標

只要arr【end】 是偶數,就將arr【end】和arr【even】交換,even+=2.

一樣,arr【end】 是奇數,就將arr【end】和arr【odd】交換,odd+=2.

代碼:

def test(arr): if not arr or len(arr) <= 1: return arr even = 0 odd = 1 end = len(arr) - 1
    while even < len(arr) and odd < len(arr): if arr[end] % 2 == 0: arr[end] , arr[even] = arr[even] , arr[end] even += 2
        elif arr[end] % 2 == 1: arr[end] , arr[odd] = arr[odd] , arr[end] odd += 2
    return arr arr = [1,8,3,2,4,6] test(arr)

 

1六、子數組的最大累加和問題

給定一個數組arr,返回子數組的最大累加和。

例如,arr= 【1,-2,3,5,-2,6,-1】,全部的子數組中,【3,5,-2,6】能夠累加出最大的和12,因此返回12.

時間O(N),空間O(1):設置一個cur和變量,一旦cur小於0,cur從0開始加當前位置的值。

import sys def test(arr): if not arr or len(arr) == 0: return 0 res = -sys.maxsize cur = 0 for i in range(len(arr)): cur += arr[i] res = max(res,cur) cur = 0 if cur<0 else cur return res arr = [1,-2,3,5,-2,6,-1] test(arr)

1七、子矩陣的最大累加和問題

給定一個矩陣matrix,其中的值有正、有負、有0,返回子矩陣的最大累加和。

例如,矩陣matrix爲:

思路:時間O(N3),空間O(N)

對於每一行都有如下操做:for i in range(len(arr))

  for  j in range(i ,len(arr)):

  • 有一行時,子數組最大和和上題同樣。
  • 有兩行時,兩行矩陣最大值爲行對應的列相加:如:

【64,-40,64】

【-81,-7,66】

變成 s =【-17,-47,130】,此時該子數組最大和爲130【for k in range(len(arr[0]))】

  • 有三行時,與兩行的處理方式同樣
  • ……

 

代碼:

import sys def maxSum(arr): if not arr or len(arr) == 0 or len(arr[0]) == 0: return 0 res = - sys.maxsize cur = 0 for i in range(len(arr)): s = [0] * len(arr[0]) for j in range(i , len(arr)): cur = 0 for k in range(len(arr[0])): s[k] += arr[j][k] cur += s[k] res = max(res,cur) cur = 0 if cur < 0 else cur return res arr = [[-90,48,78],[64,-40,64],[-81,-7,66]] maxSum(arr)

 1九、數組中子數組的最大累乘積

思路:時間複雜度O(N)、空間複雜度O(1).

大體思路是,遍歷一遍數組,求出以每個元素結尾的子數組的最大累乘積。如何快速的求出以 i 位置結尾的子數組的最大累積呢?假設以arr[i-1]結尾的最小乘積是min,最大乘積是max。那麼,以arr[i]結尾的最大累乘積只可能來自如下三種狀況:
  • 多是max * arr[i]
  • 多是min * arr[i],由於數組中可能包含負數,負負得正
  • 多是arr[i],由於以arr[i-1]結尾的最大乘積可能小於1
這三種可能的值中最大的那個就做爲以 i 位置結尾的最大累乘積,最小的做爲最小累乘積,繼續遍歷下一個位置。

代碼:

def maxMul(arr): if not arr or len(arr) == 0: return 0 maxMul = arr[0] minMul = arr[0] res = 0 for i in range(1,len(arr)): maxMul = maxMul * arr[i] minMul = minMul * arr[i] maxMul = max(maxMul , minMul ,arr[i] ) minMul = min(maxMul , minMul , arr[i] ) res = max(res , maxMul) return res

 20、數組中三個數的最大累乘積

思路:時間複雜度O(N)、空間複雜度O(1).

  找到三個最大的數max1,max2,max3 和兩個最小的數 min1 ,min2 ,min3 ,返回 max( max1*max2*max3 , max1 * min1 * min2)

 

 2一、邊界都是1的最大正方形大小

給定一個N*M的矩陣matrix, 在這個矩陣中, 只有0和1兩種值, 返回邊框全是1的最大正方
形的邊長長度。
例如:
0 1 1 1 1
0 1 0 0 1
0 1 0 0 1
0 1 1 1 1
0 1 0 1 1
其中, 邊框全是1的最大正方形的大小爲4*4, 因此返回4

思路1:時間O(N4),枚舉全部正方形,判斷邊框是否都爲1

1.矩陣中一共有N*N個位置。O(N2)

2.對每個位置均可以成爲邊長爲N~1的正方形左上角。好比,對於(0,0)位置,依次檢查是不是邊長爲5的正方形的左上角,而後檢查邊長爲四、3等。O(N)

3.如何檢查一個位置是否能夠成爲邊長爲N的正方形的左上角?遍歷這個邊長爲N的正方形邊界看是否只由1組成,也就是走過四個邊的長度(4N)。O(N)

總的時間複雜度:O(N2)*O(N)*O(N)=O(N4)

思路2:時間複雜度爲O(N3),以空間換時間的作法。

採用預處理矩陣的方法,一樣也是枚舉全部的正方形,可是判斷該正方形是否符合規則是,是O(1)的時間複雜度,因此當M=N時,這是O(N^3)時間複雜度。 

用與原矩陣一樣大小的兩個矩陣,一個爲right,一個爲down,

right[i][j]的值表示從位置(i,j)向右出發有多少個連續的1。

down[i][j]的值表示從位置(i,j)向下出發有多少個連續的1。

right和down的計算過程:從右到左,從下到上依次填好兩個矩陣。

  • 從矩陣的右下角(n-1,n-1)位置開始計算,若是matrix[n-1][n-1]=1,那麼,right[n-1][n-1]=1,down[n-1][n-1]=1,不然都等於0。
  • 從右下角向上計算,即在matrix最後一列上計算,位置就表示爲(i,n-1)。對right來講,最後一列的右邊沒有內容,因此,若是matrix[i][n-1]=1,             right[i][n-1]=1而且down[i][n-1]=down[i+1][n-1]+1,不然right[i][n-1]=0而且down[i][n-1]=0。
  • 從右下角向左計算,即在matrix最後一行上計算,位置就表示爲(n-1,j)。對down來講,最後一行的下邊沒有內容,因此,若是matrix[n-1][j]=1,           down[n-1][j]=1而且right[n-1][j]=down[n-1][j+1]+1,不然right[n-1][j]=0而且down[n-1][j]=0。
  • 剩下的位置都是既有右,又有下,假設位置(i,j):
    • if matrix[i][j]=1, then right[i][j+1]=right[i][j]+1,down[i][j]=down[i+1][j]+1.
    • if matrix[i][j]=0,then right[i][j]=0,down[i][j]=0.

 

2二、不包含本位置值的累乘數組

  給定一個整型數組arr,返回不包含本位置值的累乘數組。
  例如,arr = [2, 3, 4, 1],返回[12, 8, 24, 6],即除本身之外,其餘位置上的累乘。

【要求】

    1. 時間複雜度O(N)
    2. 除須要返回的結果數組外,額外空間複雜度O(1)。

不用除法思路:

分別使用輔助兩個數組left和right,其中left表示數組從左到右的累乘結果(即left[i] = arr[0…i]的累乘);相反,right表示數組從右到左的累乘結果。那麼對於結果數組res,res[i] = left[i-1] * right[i+1]。
  實際上,並不須要額外聲明兩個輔助數組。能夠複用結果數組res,即先將res當輔助數組用,再把res調整爲結果數組便可。具體實現見以下代碼:

def product2(arr): if arr == None or len(arr) < 2: return res = [0 for i in range(len(arr))] res[0] = arr[0] for i in range(1, len(res)): res[i] = res[i-1] * arr[i] tmp = 1
    for i in range(len(arr)-1, 0, -1): res[i] = res[i-1] * tmp tmp *= arr[i] res[0] = tmp return res arr = [2,3,1,4] product2(arr)

 

 2三、數組的partition調整

給定一個有序數組arr,調整arr使得這個數組的左半部分沒有重複部分且升序,而不用保證右部分是否有序。

例如:arr=[1,2,2,2,3,3,4,5,6,6,7,7,8,8,9,9],調整以後arr=[1,2,3,4,5,6,7,8,9…]。

 

要求:

 

時間複雜度O(N),額外空間複雜度O(1)

思路:兩個變量

變量u表示:arr[0……u]上是無重複且升序的,初始u=0

變量i表示:arr[u+1……i]不保證無重複但升序的區域,i是遍歷到的結果,初始時i=1.

i向右移動,若arr【i】!=arr【u】,則將arr【i】加入arr【0……u】區域內,即arr【u+1】和arr【i】交換。

直到遍歷結束

代碼:

 

def patition(arr): if not arr or len(arr)==1: return arr u , i = 0 , 1
    while i < len(arr): if arr[i] != arr[u]: arr[i] , arr[u+1] = arr[u+1] , arr[i] u += 1 i += 1
    return arr arr = [1,2,2,2,3,3,4,5,5,6,6,7,8,9,9] patition(arr)

 

 

 

 補充題目:

給定一個數組arr,其中只可能含有0、一、2三個值,請實現arr的排序。

另一種問法:有一個數組,其中只有紅球、籃球和黃球,請實現紅球全放在數組的左邊,籃球放在中間,黃球放在右邊。

另一種問法:有一個數組,再給定一個值K,請實現比K小的數都放在數組的左邊,等於K的值都放在數組的中間,比K大的數都放在數組的右邊。

 思路:

left變量:表示左區arr【0……left】都是0,left初始爲-1

index變量:表示中區arr【left+1……index】都是1,index初始爲0

right變量:表示右區arr【right……N-1】都是2,right初始爲N-1.

index表示從左往右遍歷的位置:

arr【index】== 1,留在中區,index+=1

arr【index】==0,加入左區,即arr【left+1】和arr【index】交換,left+=1,index+=1

arr【index】== 2,加入右區,即arr【right-1】和arr【index】交換,right-=1。

當index == right,中止遍歷,由於中區和右區對接了。

代碼:

def patition(arr): if not arr or len(arr)==1: return arr left , index , right = -1 , 0 , len(arr) while index != right: if arr[index] == 0: arr[left+1] , arr[index] = arr[index] , arr[left+1] left += 1 index += 1
        elif arr[index] == 1: index += 1
        elif arr[index] == 2: arr[right - 1] , arr[index] = arr[index] , arr[right-1] right -= 1
    return arr arr = [1,0,2,2,1,1,0,2,1,2] patition(arr) 

 

 2五、數組中未出現的最小正整數

給定一個無序整型數組arr,找到數組中未出現的最小正整數。

舉例:

arr = 【-1,2,3,4】。返回1

arr = 【1,2,3,4】。返回5.

思路:時間O(N),空間O(1)

分析:

假如arr有序,則:

(1)  arr爲整數1,2,3…N,N+2,N+3……的一個隨機排列,那個未出現的最小正整數就是N+1。

【設置left變量表示已經有序的N個數 arr [0:N-1] 】

(2)  arr中有小於1或者大於N或者重複的數出現(咱們稱之爲「不合法」的數),則未出現的最小正整數必定在1到N中間(由於數組一共只有N個數,若是出現不合法的數,則出現的1到N之間的數的個數必定小於N,故必定有沒有出現的數)。

【設置right變量,初始N+2, 表示還能夠有序的arr [ N:N+2 ]】

【好比:發現 arr[N](即N+2)合法但位置不理想,將N+2換到它理想的位置,而後將N+3換到該位置 arr[ N ],繼續比較N+3是否是合法且理想,發現並不合法,合法區間right-1變成 arr[ N:N+1 ] 】

作法:

(1)  先設置兩個變量L,R。初始值:L=0,R=len(arr)

  • L表示已經從1到L已經出現(左邊界),L的初值爲0。
  • 若是一個數字過大(不合法),就會被扔掉,用R表示這個右邊界,即大於R的數會被扔掉。R的初值爲N,表示從1到R的元素都不會被扔掉,大於R的就會被扔掉。可是這個R的值是變化的,若是L+1到R中有一個元素不合法,那麼R--,由於最多隻能放下R-1個合法數。

也就是說,1到L上的數已經出現,[L+1,R]區間上的數未出現但可能會出現

(2)遍歷數組:

1》當 L 的位置值等於L+1,表示獲得想要的,故L ++

2》三種不合法狀況:都需R--

  • 小於左邊的數:當 L 的位置值< L 時,表示 L 位置的值已經存在,因此這個數組不會存在到 R 的值了 so R--
  • 大於右邊的數:當 L 的位置值> R 時,表示 L 的位置已經超過 R ,因此數組不會存在到 R 的值了, so R--
  • 左邊區域重複的數:當 L 的位置值和 (L 值的位置-1)的的位置值相等時,全部數組不會存在到 R 的值了,so R--    【即L位置的值已經存在左區域已經出現的數中了,兩個同樣的值,好比 [1,2,3,2,4] ,第二個2已經重複了[1,2,3]中的2了 】

3》合法但位置不理想:else,將 arr【L】 交換到理想位置,理想位置的值換到 L 位置來,繼續判斷 L 位置的值。

代碼

def missNum(arr): if not arr: return 1 left , right = 0 , len(arr) while left < right: if arr[left] == left + 1: left += 1
        elif arr[left] <= left or arr[left] > right or arr[left] == arr[arr[left]-1]: right -= 1 arr[left] = arr[right]  #right-=1後合法區域不包括arr【right】了,將arr[right]換到left位置繼續判斷
        else: #arr[left]理想位置爲arr[left]-1,將arr[arr[left]-1]換到left位置繼續判斷
            arr[left] , arr[arr[left] - 1] = arr[arr[left]-1] , arr[left] return left + 1 arr = [1,3,-2,4,5] missNum(arr)

 

 2六、題目:查找知足條件的二維數組最大值和次大值之和(RMQ算法)

在一個二維座標數組中,第一列x爲座標值,第二列y爲金額,求知足|x1-x2| >= d,求(y1 + y2)和最大的值。【兩個變量知足座標值大於等於d條件,且其金額之和最大】,返回金額和的值。

如:d = 3 , 輸入 [[1,1],[3,5],[4,8],[6,4],[10,3],[11,2]]。輸出:11,由於 [4,8] 和[10,3],|4 - 10| >= d, 8 + 3 =11金額最大。

思路:

  先將座標排序,而後將金額從後面開始,從大到小放入棧中。

  接着,從前日後遍歷,採用一個prefix存儲前面的最大值,同時一旦遇到知足d條件的棧中的第一個數,更新和最大值。

代碼

def solve(arr,d): #將arr按座標值排序
 arr.sort(); #suffix是將arr按金額從大到小存入棧中,如arr中金額爲[1,5,8,3,4,2] ,則suffix爲[8,8,8,4,4,2]
    suffix = [0] * len(arr) suffix[len(arr) - 1] = arr[len(arr) - 1][1] for i in range(len(arr) - 2,-1,-1): suffix[i] = suffix[i + 1] if suffix[i + 1] > arr[i][1] else arr[i][1] #prefix存前面最大的數
    prefix = 0 # pos爲當前第一個知足arr[pos] > arr[i]的數
    pos = 0 ret = 0 for i in range(len(arr)): prefix = prefix if prefix > arr[i][1] else arr[i][1] while pos < len(arr) and arr[pos][0] - arr[i][0] < d: pos += 1
        if pos < len(arr): ret = ret if ret > prefix +suffix[pos] else prefix + suffix[pos] return ret arr = [[1,1],[3,5],[4,8],[6,4],[10,3],[11,12]] d = 3 solve(arr,d)

 

 2七、題目:奇數在前,偶數在後,且本來位置不變,即穩定的,奇偶數

輸入一個整數數組,實現一個函數來調整該數組中數字的順序,使得全部的奇數位於數組的前半部分,全部的偶數位於數組的後半部分,並保證奇數和奇數,偶數和偶數之間的相對位置不變。

 

 思路:

1.從前日後遍歷,第一個元素若是是偶數,記住這個 偶數的角標位。若是不是偶數,啥也不幹。
2.當遇到偶數的時候,判斷這是否是第一個偶數,若是是 記住角標位。
3.當遇到奇數的時候,判斷其前面有沒有偶數,若是有,則將前面的連續的多個偶數,順序日後移動一位,而後將奇數插入第一個偶數位置。
具體步驟以下例子:
2,1,8,3,4,9   //index=0 
             index>=0       奇數1 和2 交換位置  而後修改index=1
1,2,8,3,4,9   //index=1
1,3,2,8,4,9   //index=2
              index>=0       奇數3存入臨時變量,而後元素2 和8 依次後移,以後將3存入2位置。
1,3,9,2,8,4   //index=3
               index>=0       奇數9存入臨時變量,而後元素2 ,8和4 依次後移,以後將9存入2位置。

 代碼:

def reOrderArray(self, array): # write code here
        if len(array) <= 1: return array flag = -1
        for i in range(len(array)): if (array[i] % 2 == 0) and flag == -1: flag = i elif (array[i] % 2 == 1) and flag != -1: array.insert(flag,array[i]) flag += 1
                del array[i+1] return array

 2八、數組中的逆序對

在數組中的兩個數字,若是前面一個數字大於後面的數字,則這兩個數字組成一個逆序對。輸入一個數組,求出這個數組中的逆序對的總數P。並將P對1000000007取模的結果輸出。 即輸出P%1000000007

輸入:1,2,3,4,5,6,7,0

輸出:7

一、暴力求解思路:

  遍歷兩遍,針對每個數都找後面比其小的數。

 二、歸併排序思路:

逆序對的總數=左邊數組中的逆序對的數量+右邊數組中逆序對的數量+左右結合成新的順序數組時中出現的逆序對的數量

代碼:

class Solution: def __init__(self): self.P = 0 def InversePairs(self, data): # write code here
        if not data: return 0 def merge(arr1,arr2): res = [] i , j = 0 , 0 while i < len(arr1) and j < len(arr2): if arr1[i] <arr2[j]: res.append(arr1[i]) i += 1
                else: res.append(arr2[j]) j += 1 self.P += (len(arr1)-i) res.extend(arr1[i:]) res.extend(arr2[j:]) return res def fen(data): if len(data)<=1: return data mid = len(data)//2 left = fen(data[:mid]) right = fen(data[mid:]) return merge(left,right) fen(data) return self.P % 1000000007

三、樹狀數組思路:

思路1:離散化 + 樹狀數組

先利用離散化,一次性讀入全部數據,而後記錄讀入數據的下標和值,按照值的大小對值和下標進行排序。獲得新的數據後,創建以新的值(排序的序號)爲下標的樹狀數組,樹狀數組的值爲當前狀態下該位值出現的次數。在每次向樹狀數組插入元素時,都利用樹狀數組查詢比當前插入元素的值更大的元素出現的個數。
 

離散化:

樹狀數組:

能夠把數一個個插入到樹狀數組中, 每插入一個數, 統計比他小的數的個數,對應的逆序爲 i- getsum( data[i] ),其中 i 爲當前已經插入的數的個數, getsum( data[i] )爲比 data[i] 小的數的個數,i- getsum( data[i] ) 即比 data[i] 大的個數, 即逆序的個數。最後須要把全部逆序數求和,就是在插入的過程當中邊插入邊求和。

代碼:

#include <iostream> #include <stdio.h> #include <stdlib.h> #include <algorithm> #include <string.h>
using namespace std; const int maxn=500005; int n;int aa[maxn]; //離散化後的數組
int c[maxn]; //樹狀數組 
struct Node{ int v; int order; } in[maxn]; int lowbit(int x){ return x&(-x); } void update(int t,int value){ int i; for(i=t;i<=n;i+=lowbit(i)) { c[i]+=value; } } int getsum(int x){ int i; int temp=0; for(i=x;i>=1;i-=lowbit(i)) { temp+=c[i]; } return temp; } bool cmp(Node a ,Node b){ return a.v<b.v; } int main(){ int i,j; while(scanf("%d",&n)==1 && n) { //離散化 
        for(i=1;i<=n;i++) { scanf("%d",&in[i].v); in[i].order=i; } sort(in+1,in+n+1,cmp); for(i=1;i<=n;i++) aa[in[i].order]=i; //樹狀數組求逆序 
        memset(c,0,sizeof(c)); long long ans=0; for(i=1;i<=n;i++) { update(aa[i],1); ans+=i-getsum(aa[i]); } cout<<ans<<endl; } return 0; } 

 

另外一種思路:

  

這樣直到把最小元素放完,累加每次放元素是該元素前邊已放元素的個數,這樣就算出總的逆序數來了
在統計和計算每次放某個元素時,該元素前邊已放元素的個數時若是一個一個地數,那麼一趟複雜度爲O(n),總共操做n趟,複雜度爲O(n^2),和第一種方法的複雜度同樣了,那咱們爲何還用這麼複雜的方法
固然,在每次統計的過程當中用樹狀數組能夠把每一趟計數個數的複雜度降爲O(logn),這樣整個複雜度就變爲O(nlogn)

將序列中的每一個數按照從大到小的順序插入到樹狀數組中,給當前插入節點及其父節點的個數加1,而後統計該節點下邊及右邊放置元素的個數

 

 2九、題目:RMQ算法:

題意:一維線性的直線上,排列着n個兵營,初始每一個兵營有固定的人數,有兩個操做:一個是添加,把某個兵營增長人數d;二是詢問,求某兩個兵營之間全部兵營的總人數之和。

代碼:

相關文章
相關標籤/搜索