zoukankan      html  css  js  c++  java
  • 数组的遍历查找

    题目:

    前三题都可以用集合和来求。【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)

        

        

  • 相关阅读:
    Grafana 监控系统是否重启
    Grafana 短信报警
    Python 协程检测Kubernetes服务端口
    python过滤 Kubernetes api数据
    django 实现文件下载功能
    python tar.gz格式压缩、解压
    kafka查看消费数据
    SublimeText设置在浏览器打开 快捷键
    php如何优雅地把数组传递给前端js脚本?
    jQuery因mouseover,mouseout冒泡产生的闪烁问题
  • 原文地址:https://www.cnblogs.com/Lee-yl/p/10469654.html
Copyright © 2011-2022 走看看