數組的遍歷查找


題目:

前三題都可以用集合和來求。【sum(set(nums))*N-sum(nums)】

1、數組中別的數都出現2次,只有一個數出現1次,找出該數。

2、數組中別的數都出現3次,只有一個數出現1次,找出該數。

3、數組中別的數都出現N次,只有一個數出現1次,找出該數。

4、數組中別的數都出現2次,只有兩個數只出現1次,找出這兩個數。

5.1、數組中1到1000遍歷(順序打亂),有兩個數缺失,求出這兩個數。

  5.2、數組中未出現的最小正整數【左右變量遍歷】

6、數組中別的數都出現1次,只有一個數出現2次,找出該數。

  【n個正整數,每個數值不超過n-1,有一個重復的數,找出那個重復的數】

7、n個[0,n)的數,求每個數出現次數(不能開辟額外空間)【排序】

8、數組去重

9、在數組中找到出現次數大於N/K的數【刪除不同的數】

10、在一個有序數組中查找一個數,怎么最快【二分查找】

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

12、奇數下標都是奇數或偶數下標都是偶數【even、odd變量】

13、在數組中找到一個局部最小的位置

14、邊界都是1的最大正方形大小【遍歷+動態規划】

15、不包含本位置值的累乘數組【左右額外空間遍歷】

16、求最短通路值【寬度優先】

17、二維數組的轉置(每行數組的長度一樣和不一樣)

18.股票最大利潤

 

 

解題:

1、(3)第1題相當於第3題N為偶數。解法思路:數組為nums,則數組中所有數異或,最后的值為所求。

復制代碼
def findNum(nums):
  if not nums:
    return -1
i=1 res=nums[0]
   while i<len(nums): res^=nums[i]
     i += 1 return res
復制代碼

2、(3)第2題相當於第3題N為奇數。解法思路:所有數的二進制位相加 mod 3 ,所有模3值不為0的位匯總的數即為所求。

復制代碼
def findNum(nums):
    if not nums:
        return -1
    i=0
    res=0
    bitSum=[0]*4  #這里是4是因為若設32位,但無論怎么算都只需4位
    while i<4:
        j=0
        while j<len(nums):
            bitSum[i]+=(nums[j]>>i)&1
       j+=1 res|=(bitSum[i]%3)<<i i+=1 return res
復制代碼

更簡單的解法:一個數用來表示出現一次的位,也就是說這個數的為1的位就表示這個位上面的數出現過了一次,比如0x10001,就表示bit[0]和bit[4]就是出現過了一次的位。然后再用一個數表示出現過了兩次的位,再用一個數表示出現過了3次的位。只要這個位出現過了3次,我就把這個位拿清除掉,這樣剩余的最后出現過一次的位的這個數就是我們要找的數了。

復制代碼
def findNum(nums):
    ones=twos=threes=0
    for i in range(len(nums)):
            twos |= ones & A[i]
            ones ^= A[i]
            threes = ones & twos
            ones &= ~threes
            twos &= ~threes
    return ones
復制代碼

 

第3題:所有數的二進制位相加 mod n ,所有模n值不為0的位匯總的數即為所求。

def findNum(nums,n):
    if not nums:
        return -1
    arr = [0] * 32
    for i in range(len(nums)):
        binnum = nums[i]
        j = 0
        #將十進制轉成二進制
        while binnum:
            binnum = binnum >> j
            arr[j] += binnum & 1
            j += 1
    res = 0
    #記錄%n有余數的數
    for i in range(len(arr)):
        tmp = (arr[i] % n)&1
        res += tmp << i
    return int(res)

nums = [1000,1000,1000,2,3,4,4,4,3,3]
n = 3
print(findNum(nums,n))

 

import math
def findNum(nums,n):
    if not nums:
        return -1
    arr = [0] * 32
    for i in range(len(nums)):
        binnum = nums[i]
        j = 0
        while binnum:
            binnum , remainder = divmod(binnum,2) #divmod將商和余數結合起來
            if remainder == 1:
                arr[j] += 1
            j += 1
    res = 0
    for i in range(len(arr)):
        if arr[i] % n == 1:
            res += math.pow(2,i)
    return int(res)

nums = [5,5,5,2,3,4,4,4,3,3]
n = 3
print(findNum(nums,n))

 

 

第4題:先將數組所有數字異或,最后結果就是兩個出現一次的數字相互異或的結果,再將這兩個數分別分在兩個數組中進行異或。如鏈接:

https://blog.csdn.net/morewindows/article/details/8214003

    設題目中這兩個只出現1次的數字分別為A和B,如果能將A,B分開到二個數組中,那顯然符合“異或”解法的關鍵點了。因此這個題目的關鍵點就是將A,B分開到二個數組中。由於A,B肯定是不相等的,因此在二進制上必定有一位是不同的。根據這一位是0還是1可以將A,B分開到A組和B組。而這個數組中其它數字要么就屬於A組,要么就屬於B組。再對A組和B組分別執行“異或”解法就可以得到A,B了。而要判斷A,B在哪一位上不相同,只要根據A異或B的結果就可以知道了,這個結果在二進制上為1的位都說明A,B在這一位上是不相同的。

      比如int a[] = {1, 1, 3, 5, 2, 2}

      整個數組異或的結果為3^5即 0x0011 ^ 0x0101 = 0x0110

      對0x0110,第1位(由低向高,從0開始)就是1。因此整個數組根據第1位是0還是1分成兩組。

      a[0] =1  0x0001  第一組

      a[1] =1  0x0001  第一組

      a[2] =3  0x0011  第二組

      a[3] =5  0x0101  第一組

      a[4] =2  0x0010  第二組

      a[5] =2  0x0010  第二組

      第一組有{1, 1, 5},第二組有{3, 2, 3},明顯對這二組分別執行“異或”解法就可以得到5和3了。

復制代碼
def findNum(nums):
    if not nums:
        return -1
    temp=0
    ##先所有數異或得到A^B的值,A和B為只出現一次的數
    for item in nums:
        temp^=item
    #找到A和B不同的第一位,A^B的第一個1位。該位設為j位
    j=0
    while j<32:
        if (temp>>j)&1==1:
            break
        j+=1
    A=[]
    B=[]
    #將A、B分到按第j位不同分到不同組A和B
    for num in nums:
        if (num>>j)&1==1:
            A.append(num)
        else:
            B.append(num)
    ##分到的兩個組全部都異或
    resA=0
    resB=0
    for itemA in A:
        resA^=itemA
    for itemB in B:
        resB^=itemB
    return resA,resB
nums=[1,1,3,5,2,2]
print(findNum(nums))
復制代碼

5.1題:數組中1到1000遍歷(順序打亂),有兩個數缺失,求出這兩個數。

  • 思路1:list(set(range(1,1001))-set(nums)),即兩個集合相減
  • 思路2:排序,然后比較【時間O(nlogn),空間O(1)】
  • 思路3:采用字典,將數組的元素存到key中,從1-n查詢時若有哪兩個數缺失則返回。【空間換時間】

5.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)

 

第6題:數組中別的數都出現1次,只有一個數出現2次,找出該數。

  • 思路1:【空間換時間】,采用字典,將數組的元素存到key中,value對應數量,若value=2則返回該key值。
  • 思路2:【累加求和法,可能溢出】,sum(nums) - sum([1……n])即所求
  • 思路3:【異或法】nums數與 [1……n] n個數異或,異或結果為所求
  • 思路4:【數據映射法】全部變為負數,最后一個為正數的即所求。【如arr = [2,3,4,3,1],令arr[0] = -2,指向下標為2,即arr[2] = -4, arr[4] = -1, arr[1] = -3,arr[3] = -3,arr[3] = 3變為正數,即為所求。】如果元素不合理時,存在數組越界問題
  • 思路5:【環形相遇法】將數組連成鏈表,如果有重復值則會形成環形鏈表,只需要求入環點即可。
  • 思路6:排序,找相鄰的數。

     

9、在數組中找到出現次數大於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剩余的數在原數組中的數量,【如[1,1,3,3,4,4,4,4,2,2]中最后map會剩下{4:2,2:2}】
    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)
復制代碼

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

思路:

11、奇數下標都是奇數或偶數下標都是偶數

給定一個長度不小於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)
復制代碼

 

12、邊界都是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的正方形的左上角,然后檢查邊長為4、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.

 

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

  給定一個整型數組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)

 

 17、二維數組的轉置

題目:

代碼:

    

def merge(arr,n):
    if not arr:
        return arr
    res = []  
    num = [0] * len(arr)
    count = len(arr)
    while count:
        for i in range(len(arr)):
            m = len(arr[i])
            if num[i] + n <= m:
                res.extend(arr[i][num[i]:num[i]+n])
                num[i] += n
            elif num[i]+n > m and arr[i][num[i]:]:
                res.extend(arr[i][num[i]:])
                num[i] += n
                count -= 1
    return res
arr = [[2,5,6,7,9,5,7],[1,7,4,3,4]]
n = 3
merge(arr,n)

  長度一樣的轉置:【采用解壓*】list(zip(*matrix))

 18. 股票最大利潤

 思路1:用后一天減去前天得到隔天的利潤,然后將該題目轉化為求最大子序列和的問題。

思路2:當天的價格減去今天以前的股票最小值

 

 代碼:

arr = [7,1,4,3,1]
min_P = 2**32
max_P = -min_P
if not arr:
    print('0')
else:
    for i in range(len(arr)):
        min_P = min(min_P,arr[i])
        max_P = max(max_P,arr[i]-min_P)
    print(max_P)

 

 

 

 

 

 

 

    

    


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM