剑指offer || 数组

剑指offer || 数组

剑指offer中有关数组的题目汇总


面试题3:数组中重复的数字

题目一:找出数组中重复的数字

​ 在一个长度为n的数组里的所有数字都在0~n-1的范围内。数组中的数字是重复的
但不知道有几个数字重复了,也不知道每个数字重复几次。请找出数组中任意一个重复的数字。

输入:(2,3,1,0,2,5,3)
输出:2或3

思路

  • 对数组进行排序

  • 比较 i n u m [ i ] ,令 m = n u m [ i ] ,

    如果 i = m , 继续扫描

    i f i <> m ,则比较 m = n u m [ m ]

    i f m = n u m [ m ] ,则找到重复值

    i f m n <> n u m [ m ] , 令 n u m [ i ] , n u m [ m ] = n u m [ m ] , n u m [ i ]

​ 继续扫描

代码

#################面试题3#######################
#题目一:找出数组中重复的数字
def getDupliction(num):
    i = 0
    while i < len(num):
        m = num[i]
        if m >= len(num):
            print('there are at least one number bigger than length of array!')
            return
        if i != m:
            if m != num[m]:
                num[i], num[m] = num[m], num[i]
            else:
                print(num[i])
                i += 1
        else:
            i += 1

测试

#长度为n的数组里包含一个或多个重复的数字
num = [2,3,7,2,5,3,7]
getDupliction(num)

#数组中不包含重复的数字
num = [2,4,6,1,5,3,0]
getDupliction(num)
#返回None

#无效输入
getDupliction(None)
getDupliction([1,2])

分析

时间复杂度 O ( n )

空间复杂度 O ( 1 )

改变数组

题目二:不修改数组找出重复数字

​ 在一个长度为 n + 1 的数组里的所有数字都在 0   n 的范围内。所以数组中至少有一个数字是重复的。请找出数组中任意一个重复的数字,但不能修改输入的数组。

输入:(2,3,5,4,3,2,6,7)
输出:2或3

思路

假如没有重复数字,那么在 1 n 的范围里只有 n 个数字,如果数组里超过n个数字,则一定包含了重复数字。(key point: 某个范围里特定的数字个数)

  • 1 n 的数字从中间的数字 m 分为两部分
  • 分别计算两区间的数字个数,在包含重复数字的区间继续进行二分查找

代码

#################面试题3#######################
#题目二:不修改数组找出重复数字
def countRange(num,start,end):
    count = 0
    for i in num:
        if start <= i <= end:
            count += 1
    return count

def getDupliction(num):
    if not num:
      return None
    if len(num) == 0:
        return -1
    L = len(num)
    start = 1
    end = L-1
    while end >= start:
        middle = start + (end - start) // 2
        count = countRange(num,start,middle)
        if start == middle:
            if count > 1:
                return start
            else:
                break
        if count > middle - start +1 :
            end = middle
        else:
            start = middle + 1
    return -1

测试

#长度为n的数组里包含一个或多个重复的数字
num = [2,3,5,4,3,2,6,7]
getDupliction(num)

#数组中不包含重复的数字
num = [2,4,6,1,5,3,0]
getDupliction(num)
#返回-1

#无效输入
getDupliction(None) #返回None
getDupliction([1,3]) #返回 -1

分析

时间复杂度 O ( n l o g n )

空间复杂度 O ( 1 )

不改变数组,以时间换空间


面试题4:二维数组中的查找

题目:二维数组中的查找

​ 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

输入:数组:[[1,2,8,9],[2,4,9,12],[4,7,10,13],[6,8,11,15]] 数字: 5

[[1,2,8,9],

[2,4,9,12],

[4,7,10,13],

[6,8,11,15]]

输出:False

思路

​ 从数组的一个角上选取数字来和要查找的数字进行比较。

​ ( 这里选取右上角)

  • 如例子,选择9 与 5比较,5<9,则第4列均删除
  • 问题变成在前3列数组中查找5,以此类推

代码

#################面试题#######################
#二维数组中的查找
class Solution:
    # array 二维列表
    def Find(self, target, array):
        found = 0
        rows = len(array)
        if len(array) == 0:
            return found
        row,col = 0,len(array[0])-1
        while col >= 0  and row < rows:
            if target == array[row][col]:
                found = 1
                break
            elif target < array[row][col]:
                col -= 1
            else:
                row += 1
        return found

测试

#二维数组中包含查找的数字
array = [[1,2,8,9],[2,4,9,12],[4,7,10,13],[6,8,11,15]]
s = Solution()
s.Find(1,array)
s.Find(15,array)
s.Find(10,array)

#二维数组中没有查找的数字
array = [[1,2,8,9],[2,4,9,12],[4,7,10,13],[6,8,11,15]]
s = Solution()
s.Find(14,array)
#返回None

#无效输入
s = Solution()
s.Find(14,Nonez)

面试题11:旋转数组的最小数字

题目:旋转数组的最小数字

​ 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。输入一个递增序列的数组的旋转,输出旋转数组的最小元素。

输入: 旋转数组:[3,4,5,1,2] 原数字: [1,2,3,4,5]

输出:1

思路

​ 旋转数组实际上可以分为两个排序的子数组。前面元素都大于或的等于后面子数组的元素。

​ 最小元素刚好是这两个子数组的分界线。

​ (用二分法的思路来查找最小元素)

  • 两个指针分别指向数组的第一个元素和最后一个元素
  • 找数组中间元素,该元素位于前面的递增子数组,那么它大于或等于第一个指针指向的元素。
  • 否则,位于后面数组

特例 [1,0,1,1,1,1]

当两个指针指向的数字以及它们中间的数字三者相同时候,无法判断中间数字属于前子数组还是后子数组。此时,采用顺序查找的方法。

代码

#################面试题11#######################
#旋转数组的最小数字
class Solution:
    def minNumberInRotateArray(self, rotateArray):
        # write code here
        if rotateArray is None or len(rotateArray) == 0 :
            return 
        L = len(rotateArray)
        if L == 0:
            return 0
        elif L ==1:
            return rotateArray[0]
        p1 = 0
        p2 = L-1
        Mid = p1
        while rotateArray[p1] >= rotateArray[p2]:
            Mid = (p2 + p1) // 2
            if rotateArray[p1] == rotateArray[Mid] == rotateArray[p2]:
                minnum = rotateArray[p1]
                for i in range(p1+1,p2):
                    if minnum >= rotateArray[i]:
                        minnum = rotateArray[i]
                return minnum
            if p1 - p2 == 1:
                return rotateArray[p2]
            if rotateArray[p1] < rotateArray[Mid]:
                p1 = Mid + 1
            if rotateArray[p2] > rotateArray[Mid ]:
                p2 = Mid
        return rotateArray[Mid]

测试

#功能测试
#输入的数组是升序排序数组的一个旋转,数组中有重复数字或者没有重复数字
s = Solution()
s.minNumberInRotateArray([5,6,7,8,1,2,3,4])
s.minNumberInRotateArray([1,0,1,1,1,1])

#边界值测试
#输入数组是一个升序排序的数组,只包含一个数字的数组
s = Solution()
s.minNumberInRotateArray([1,2,3,4,5,6,7,8])
s.minNumberInRotateArray([8])

#无效输入
s = Solution()
s.minNumberInRotateArray(None)

#牛客上解法
class Solution:
    def minNumberInRotateArray(self, rotateArray):
        length = len(rotateArray)
        if length == 0:
            return 0
        if length == 1:
            return rotateArray[0]
        p1, p2 = 0, length - 1
        while p1<= p2:
            mid = (p1+ p2) >> 1
            if rotateArray[p1] < rotateArray[p2]:
                return rotateArray[p1]
            if rotateArray[mid] > rotateArray[p2]:
                p1 = mid+ 1
            elif rotateArray[mid] < rotateArray[p2]:
                p2 = mid
            else:
                p2 -= 1
            if p1 >= p2:
                break
        return rotateArray[p1]

面试题21:调整数组顺序使奇数位于偶数前面

题目:调整数组顺序使奇数位于偶数前面

​ 输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分。

拓展:调整数组顺序使奇数位于偶数前面,且需要保证相对位置不变

输入: 数组:[3,4,5,1,2]

输出:[2,4,5,1,3]

思路

基本解法:

​ 发现偶数在奇数面前,交换它们的顺序

​ 运用两个指针,分别指向数组的第一个和数组的最后一个

拓展解法:

​ 交换之前先调整位置

代码

#################面试题21#######################
#调整数组顺序使奇数位于偶数前面
#基本解法
def ReorderOddEven(lst):
    i = 0
    j = len(lst)-1
    while i <= j:
        if lst[i] & 1 == 0 and lst[j] & 1 == 1:
            lst[i] , lst[j] = lst[j],lst[i]
            i += 1
            j -= 1
        elif lst[i] & 1 == 0 and lst[j] & 1 == 0:
            j -= 1
        elif lst[i] & 1 == 1 and lst[j] & 1 == 0:
            i += 1
            j -= 1
        else:
            i += 1
    return lst

#拓展解法
def ReorderOddEven(lst):
    if not lst:
        return
    right = 1
    left = 0
    while right < len(lst):
        if lst[right] & 1 == 1 and lst[right-1] & 1 == 0:
            odd = lst[right]
            for i in range(right,left,-1):
                lst[i] = lst[i - 1]
            lst[left] = odd
            right += 1
            left += 1
        elif lst[right] & 1 == 1 and lst[right-1] & 1 == 1:
            right += 1
        elif lst[right] & 1 == 0 and lst[right - 1] & 1 == 1:
            left = right
            right += 1
        elif lst[right] & 1 == 0 and lst[right - 1] & 1 == 0:
            right += 1
    return lst

测试

#功能测试
#输入的数组中奇数、偶数交替出现
lst = [1,2,11,4,1,6,1,1,3,3,3,3]
ReorderOddEven(lst)

#特殊输入
ReorderOddEven(None)
ReorderOddEven([1])

面试题29:顺时针打印矩阵

题目:顺时针打印矩阵

​ 输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字

输入: 数组:

1 2 3 4

5 6 7 8

9 10 11 12

13 14 15 16

输出:1,2,3,4,8,12,16,15,14,13,9,5,6,7,11,10

思路

按照圆圈来打印,注意边界条件

代码

#################面试题21#######################
#顺时针打印矩阵

class Solution:
    def PrintMatrixInCircle(startcol,startrow,cols, rows,numbers):
        if cols == 0 or rows == 0:
            return
        if cols == 1:
            for i in range(startrow, startrow + rows):
                print(numbers[i][startcol])
            return
        elif rows == 1:
            for j in range(startcol, startcol + cols):
                print(numbers[startrow][j])
            return
        else:
            # 从左往右打印
            for i in range(startcol, startcol + cols):
                print(numbers[startrow][i])
            # 从上到下打印
            for i in range(startrow + 1, startrow + rows):
                print(numbers[i][startrow + rows - 1])
            # 从右往左打印
            for i in range(startcol + cols - 2, startcol - 1, -1):
                print(numbers[startrow + rows - 1][i])
            # 从下往上打印
            for i in range(startrow + rows - 2, startrow, -1):
                print(numbers[i][startcol])

    def PrintMatrixClockwisely(self,numbers,cols,rows):
        minOfColsAndRows = min(cols,rows)
        if minOfColsAndRows == 1:
            Solution.PrintMatrixInCircle(0, 0, cols, rows, numbers)
            return
        if minOfColsAndRows & 1 == 1:
            rounds = minOfColsAndRows//2
        else:
            rounds = minOfColsAndRows // 2 - 1
        for i in range(rounds):
            Solution.PrintMatrixInCircle(i,i,cols,rows,numbers)
            cols -= 2
            rows -= 2


class Solution:
    # matrix类型为二维列表,需要返回列表
    def printMatrix(self, matrix):
        # write code here
        def PrintMatrixInCircle(startcol , startrow, cols, rows, numbers):
            numLst = []
            if cols == 0 or rows == 0:
                return False
            if cols == 1:
                for i in range(startrow, startrow + rows):
                    numLst.append(numbers[i][startcol])
                return numLst
            elif rows == 1:
                for j in range(startcol, startcol + cols):
                    numLst.append(numbers[startrow][j])
                return numLst
            else:
                # 从左往右打印
                for i in range(startcol, startcol + cols):
                    numLst.append(numbers[startrow][i])
                # 从上到下打印
                for i in range(startrow + 1, startrow + rows):
                    numLst.append(numbers[i][startcol+cols-1])
                # 从右往左打印
                for i in range(startcol + cols - 2, startcol - 1, -1):
                    numLst.append(numbers[startrow + rows - 1][i])
                # 从下往上打印
                for i in range(startrow + rows - 2, startrow, -1):
                    numLst.append(numbers[i][startcol])
            return numLst
        cols = len(matrix[0])
        rows = len(matrix)
        numbers = matrix
        minOfColsAndRows = min(cols, rows)
        numLst = []
        if minOfColsAndRows == 1:
            numLst.extend(PrintMatrixInCircle(0, 0, cols, rows, numbers))
            return numLst
        if minOfColsAndRows & 1 == 1:
            rounds = minOfColsAndRows // 2 + 1
        else:
            rounds = minOfColsAndRows // 2
        for i in range(rounds):
            numLst.extend(PrintMatrixInCircle(i, i, cols, rows, numbers))
            cols -= 2
            rows -= 2
        return numLst

测试

#功能测试
#数组中有多行多列
numbers= [[1,2],[3,4],[5,6],[7,8],[9,10]]
s = Solution()
s.printMatrix(numbers)
#数组中有一行
numbers= [[1,2]]
s = Solution()
s.printMatrix(numbers)
#数组中有一列
numbers= [[1],[2]]
s = Solution()
s.printMatrix(numbers)
#数组中有一行一列
numbers= [[1]]
s = Solution()
s.printMatrix(numbers)

面试题39:数组中出现次数超过一半的数字

题目:数组中出现次数超过一半的数字

​ 数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。

输入: 数组:[1,2,3,2,2,2,6,4,2]

输出:2

思路

解法一:基于Partition函数的时间复杂度为O(n)的算法

超过一半的数字是排序后数组的中位数,采用快速排序的思想

解法二:根据数组特点找出时间复杂度为O(n)的算法

思路:数组中有一个数字出现次数超过数组长度的一半,也就是说,它出现的次数比其他所有数字出现次数和还要多

遍历数组时保存两个值,数组中的数字 以及 次数

(1) 如果下一个数与当前数字相同,times + 1

(2) 如果不同,则times - 1 ,如果times == 0,则保存下一个数,times = 1

代码

#################面试题39#######################
#数组中出现次数超过一半的数字
class Solution:
    def CheckMoreThanHalf(self,numbers,length,num): #检查该数在数组中个数是否超过一半
        count = 0
        for i in range(length):
            if num == numbers[i]:
                count += 1
        if count > length >> 1: # length >> 1 等价于 length // 1
            return True
        else:
            return False
    def partition(self,numbers,start,end):
        K = numbers[start]
        small = start   # small 小于标杆数字部分数组(也就是左边数组)的最后一个元素
        i = start + 1
        while i < end + 1 :
            if numbers[i] >= K:
                i += 1
            else:
                small += 1
                numbers[i],numbers[small] = numbers[small],numbers[i]
                i += 1
        numbers[small],numbers[start] = numbers[start],numbers[small]
        return small
    def MoreThanHalfNum_Solution(self, numbers):
        if numbers == None:
            return None
        length = len(numbers)
        if length == 1:
            return numbers[0]
        middle = length  >> 1
        start = 0
        end = length  - 1
        Index = self.partition(numbers,start,end)
        if Index > middle:
            end = Index - 1
            Index = self.partition(numbers,start,end)
        else:
            start = Index + 1
            Index = self.partition(numbers,start,end)
        result = numbers[middle]
        if not self.CheckMoreThanHalf(numbers,length,result):
            result = 0
        return result

测试

################测试1#################
#输入的数组中存在一个出现次数超过数组长度一半的数字
numbers = [1,2,3,3,3,3,3,4,3]
s = Solution()
s.MoreThanHalfNum_Solution(numbers)  #return 3

################测试2#################
#输入的数组中不存在一个出现次数超过数组长度一半的数字
numbers = [1,2,3,3,4,5,6,8,7]
s = Solution()
s.MoreThanHalfNum_Solution(numbers)  #return 0

################测试3#################
#输入的数组中只有一个数字
numbers = [1]  #数组中只有一个数,则返回本身
s = Solution()
s.MoreThanHalfNum_Solution(numbers)  #return 1

################测试4#################
#输入None
s = Solution()
s.MoreThanHalfNum_Solution(None)  #return None

##########partition函数测试##############
def partition(numbers, start, end):
    K = numbers[start]
    small = start  # small 小于标杆数字部分数组(也就是左边数组)的最后一个元素
    i = start + 1
    while i < end + 1:
        if numbers[i] >= K:
            i += 1
        else:
            small += 1
            numbers[i], numbers[small] = numbers[small], numbers[i]
            i += 1
    numbers[small], numbers[start] = numbers[start], numbers[small]
    return small

numbers = [3,2,3,2,2,2,5,4,2]
start = 0
end = len(numbers)-1
partition(numbers, 0, len(numbers)-1)
##########partition函数测试##############

解法二:根据数组特点找出时间复杂度为O(n)的算法

思路:数组中有一个数字出现次数超过数组长度的一半,也就是说,它出现的次数比其他所有数字出现次数和还要多

遍历数组时保存两个值,数组中的数字 以及 次数

(1) 如果下一个数与当前数字相同,times + 1

(2) 如果不同,则times - 1 ,如果times == 0,则保存下一个数,times = 1

代码

#################面试题39#######################
#数组中出现次数超过一半的数字

class Solution:
    def CheckMoreThanHalf(self,numbers,length,num): #检查该数在数组中个数是否超过一半
        count = 0
        for i in range(length):
            if num == numbers[i]:
                count += 1
        if count > length >> 1: # length >> 1 等价于 length // 1
            return True
        else:
            return False
    def MoreThanHalfNum_Solution(self, numbers):
        if numbers == None:
            return None
        length = len(numbers)
        if length == 1:
            return numbers[0]
        times = 1
        result = numbers[0]
        for i in range(1,len(numbers)):
            if times == 0:
                result = numbers[i]
                times = 1
            elif result == numbers[i]:
                times += 1
            else:
                times -= 1
        if not self.CheckMoreThanHalf(numbers, length, result):
            result = 0
        return result

测试

################测试1#################
#输入的数组中存在一个出现次数超过数组长度一半的数字
numbers = [1,2,3,3,3,3,3,4,3]
s = Solution()
s.MoreThanHalfNum_Solution(numbers)  #return 3

################测试2#################
#输入的数组中不存在一个出现次数超过数组长度一半的数字
numbers = [1,2,3,3,4,5,6,8,7]
s = Solution()
s.MoreThanHalfNum_Solution(numbers)  #return 0

################测试3#################
#输入的数组中只有一个数字
numbers = [1]  #数组中只有一个数,则返回本身
s = Solution()
s.MoreThanHalfNum_Solution(numbers)  #return 1

################测试4#################
#输入None
s = Solution()
s.MoreThanHalfNum_Solution(None)  #return None

面试题42:连续子数组的最大和

题目:数组中出现次数超过一半的数字

​ 输入一个整型数组,数组里有正数也有负数。数组中的一个或连续多个整数组成一个子数组。

输入: 数组:[1,-2,3,10,-4,7,2,-5]

输出:最大的子数组为$[3,10,-4,7,2] ,因此输出该数组的和为18

思路

动态规划思想:

i f(i) = pData[i] if i=0 o r f(i-1) <=0 f(i) = f(i-1) + pData[i] if i!=0 a n d f(i-1) > 0 max(f(i))$

以第 i 个数字结尾的子数组中所有数字的和小于0时 f ( i 1 ) < 0 ,把这个负数与第i个数累加 f ( i 1 ) + p D a t a [ i ] ,得到结果比 p D a t a [ i ] 还小。该情况下, f ( i ) = p D a t a [ i ]

代码

#################面试题42#######################
#连续子数组的最大和
def FindGreatestSumOfSubArray(Array):
    if not Array:
        return 
    n = len(Array)
    maxSumArrayOfsubArray = [0] *(n+1)
    maxSum = float('-inf')
    for i in range(1,n+1):
        if maxSumArrayOfsubArray[i-1] <= 0:
            maxSumArrayOfsubArray[i] = Array[i-1]
        else:
            maxSumArrayOfsubArray[i] = maxSumArrayOfsubArray[i-1] + Array[i-1]
        if maxSum < maxSumArrayOfsubArray[i]:
            maxSum = maxSumArrayOfsubArray[i]
    return maxSum

测试

################测试1#################
#功能测试
Array = [1,-2,3,10,-4,7,2,-5]
result = FindGreatestSumOfSubArray(Array)
print(result)

Array = [-2,-3,-10,-4,-7,-2,-5]
result = FindGreatestSumOfSubArray(Array)
print(result)

Array = [2,3,10,4,7,2,5]
result = FindGreatestSumOfSubArray(Array)
print(result)

#特殊输入测试
result = FindGreatestSumOfSubArray(None)
print(result)

面试题43:1~n整数中1出现的次数

题目:1~n整数中1出现的次数

​ 输入一个整数n,求1~n这n个整数的十进制表示中1出现的次数,

输入: 12

输出:1~12这些整数中包含1的数字有1、10、11、12,一共出现5次

思路

数字规律着手:

每次去掉最高位后进行递归
例子21345
分为两段 1-1345 1346-21345


在1346-21345中,1出现分为两种情况

(1)1出现在最高位 10000 - 19999 一共10000次

(2)1出现在除高位之外的其他四位数中的情况

  • 1346 - 21345 中后四位数中1出现的次数是8000次

    ​ 把1346-21345 再分为2段 1346-11345 11346-21345

    ​ 每一段剩下的4位数中,选择其中一位是1.其余三位可以再0~9这10个数字中任意选择

    ​ 总共出现次数为2 * 4 * 1000 = 8000

代码【未完成】

#################面试题43#######################
#1~n整数中1出现的次数

测试

################测试1#################
#功能测试

面试题44:数字序列中某一位的数字

题目:数字序列中某一位的数字

​ 数字以0123456789101112131415…..的格式序列化到一个字符序列中,在这个序列中,第5位是5,(从0计),第13位是1,第19位是4,请写一个函数,求任意第n位对应的数字

输入: n= 5 字符串0123456789101112131415.....

输出:5

思路

数字规律着手:

个位数:10个 0-9
两位数:180个 (90*2) 10 - 99
三位数:270个 (900 * 3) 100 - 999

代码

#################面试题44#######################
#数字序列中某一位的数字
def digitAtIndex(index):
    if index < 0:
        return -1
    digits = 1
    sumOfcount = 0
    while True:
        sumOfcount += countOfIntegers(digits) * digits
        if index < sumOfcount:
            indexOfpreDigit = (index - sumOfcount + countOfIntegers(digits) * digits)
            number = beginNumber(digits) + indexOfpreDigit // digits
            indexOfnumber = index % digits
            return str(number)[indexOfnumber]
        digits += 1
    return -1


# 输入位数,返回该位数共有多少个数字
def countOfIntegers(digits):
    if digits < 1:
        return False
    if digits == 1:
        return 10
    return (pow(10, digits) - pow(10, digits - 1))


def beginNumber(digits):
    if digits < 1:
        return False
    if digits == 1:
        return 0
    return pow(10, digits - 1)

测试

################测试1#################
# 功能测试
digitAtIndex(10)
digitAtIndex(190)
digitAtIndex(1000)

# 边界值测试
digitAtIndex(0)
digitAtIndex(1)

面试题45:把数组排成最小的数

题目:数字序列中某一位的数字

​ 输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个

输入: [3,32,321]

输出:则打印出这3个数字能排成的最小数字321323

思路

代码【未完成】

#################面试题44#######################
#数字序列中某一位的数字

测试

################测试1#################
# 功能测试

面试题49:丑数

题目:数字序列中某一位的数字

​ 数定义:只包含因子2、3、5的数称为丑数。求从小到大的顺序的第1500个丑数。

例子:6、8是丑数 14不是 1为第一个丑数

思路

根据丑数的定义,丑数应该是另一个丑数乘以2、3、5的结果(1除外)

关键:确保丑数是排序的

​ 采用数组来存储已有的丑数,其中最大的丑数为M

​ 已有的每个丑数 * 2 ,超过M的第一个结果为M2

​ 已有的每个丑数 * 3 ,超过M的第一个结果为M3

​ 已有的每个丑数 * 5 ,超过M的第一个结果为M5

​ 下一个丑数为 min(M2、M3、M5)

​ 存在T2使得它前面的丑数乘以2都不超过M,同理有T3 T5

代码

#################面试题49#######################
#丑数
class Solution:
    def GetUglyNumber_Solution(self, index):
        # write code here
        if index <= 0:
            return 0
        pUglyNumbers = [None] * index
        pUglyNumbers[0] = 1
        nextUglyIndex = 1

        T2Index = 0
        T3Index = 0
        T5Index = 0

        while nextUglyIndex < index:
            # M = pUglyNumbers[nextUglyIndex-1]

            pUglyNumbers[nextUglyIndex] = min(pUglyNumbers[T2Index] * 2, pUglyNumbers[T3Index] * 3,
                                              pUglyNumbers[T5Index] * 5)

            while pUglyNumbers[T2Index] * 2 <= pUglyNumbers[nextUglyIndex]:
                T2Index += 1
            while pUglyNumbers[T3Index] * 3 <= pUglyNumbers[nextUglyIndex]:
                T3Index += 1
            while pUglyNumbers[T5Index] * 5 <= pUglyNumbers[nextUglyIndex]:
                T5Index += 1
            nextUglyIndex += 1
        ugly = pUglyNumbers[nextUglyIndex - 1]
        return pUglyNumbers

测试

################测试1#################
# 功能测试
# 输入2、3、4、5、6
s = Solution()
s.GetUglyNumber_Solution(2)
s.GetUglyNumber_Solution(5)

# 特殊输入测试
s = Solution()
s.GetUglyNumber_Solution(1)
s.GetUglyNumber_Solution(0)

# 性能测试
s = Solution()
s.GetUglyNumber_Solution(1500)

面试题50:第一个只出现一次的字符

题目一:字符串中第一个只出现一次的字符

​ 字符串中第一个只出现一次的字符

输入: 'abaccdeff'

输出:'b'

思路

计算每个字符出现的次数,用字典存储

利用数组来创建简单哈希表 根据ascii码值 也可以直接采用字典

扫描两遍,第一遍计算次数 第二编读

  • 扩展1:从第一个字符串中删除在第二个字符串中出现的所有字符

    思路:从头到尾扫描第一个字符串,判断其是否是第二个字符串的

  • 扩展2:删除字符串中所有重复出现的字符

    思路:从头到尾扫描字符串,若出现,则设置为True

    ​ python中可以考虑用集合存储

  • 扩展3:查询两个单词是否是变位词(变位词:字母相同,位置不同)

    思路:统计第一个字符串字符出现次数,从头到尾扫描第二个字符串。相应减一

题目二:字符流中第一个只出现一次的字符

思路

利用数组来存储,第一遍扫描数组,记录

重新扫描数组,找到整个数组中最小的大于等于0的值

代码【未完成】

#################面试题50#######################
#第一个只出现一次的字符

class Solution:
    def FirstNotRepeatingChar(self, string):
        # 题目一
        if not string:
            return
        stringDic = {}
        for s in string:
            if s not in stringDic:
                stringDic[s] = 1
            else:
                stringDic[s] += 1

        for s in string:
            if stringDic[s] == 1:
                return s

    def FirstNotRepeatingstring(self, string):
        # 题目二
        if not string:
            return 0
        stringDic = {}
        Firstcharlist = []
        # i = 0
        # s = string[i]
        for i, s in enumerate(string):
            if i == 0:
                Firstcharlist.append(string[0])
                stringDic[s] = 1
                continue
            if s not in stringDic:
                stringDic[s] = 1
                if not Firstcharlist[i - 1]:
                    Firstcharlist.append(s)
                else:
                    Firstcharlist.append(Firstcharlist[i - 1])
            else:
                stringDic[s] = -1  # 重复出现
                append = False
                for j in range(i):
                    if stringDic[string[j]] > 0:
                        Firstcharlist.append(string[j])
                        append = True
                        break
                if not append:
                    Firstcharlist.append(None)
        return Firstcharlist

    def DeleteString2FromString1(self, string1, string2):
        if not string2:
            return string1
        if not string1:
            return None
        string = ''
        for s in string1:
            if s not in string2:
                string += s
        return string

    def DeleteRepeatingChar(self, string):
        if not string:
            return
        newstring = ''
        stringSet = set()
        for s in string:
            if s not in stringSet:
                stringSet.add(s)
                newstring += s
        return newstring

    def If2stringsAreAnagram(self, string1, string2):
        if not string1 or not string2:
            return False
        stringDic = {}
        for s in string1:
            if s not in stringDic:
                stringDic[s] = 1
            else:
                stringDic[s] += 1
        for s in string2:
            if s not in stringDic:
                return False
            else:
                stringDic[s] -= 1
                if stringDic[s] == 0:
                    result = True
                else:
                    result = False
        return result

测试

################测试#################
# 仅仅测试读入字符流
# 功能测试
# 读入一个字符
s = Solution()
s.FirstNotRepeatingstring('a')

# 读入多个字符
s.FirstNotRepeatingstring('ahsdssas')

# 读入多个字符,字符都是唯一的
s.FirstNotRepeatingstring('ahs')

# 读入所有的字符都是重复的
s.FirstNotRepeatingstring('aaa')

# 特殊输入测试 None
s.FirstNotRepeatingstring(None)

面试题51:数组中的逆序对

题目:数组中的逆序对

​ 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对,输入一个数组,求这个数组中的逆序对的总数

输入: [7 5 6 4]

输出:一共存在5个逆序对,分别是(7,6) (7,5) (7,4) (6,4),(5,4)

思路

  • 先把数组分隔成子数组,统计出子数组内部的逆序对的数目

  • 然后再统计出两个相邻子数组之间的逆序对的数目

    涉及到对数组进行排序

代码【未完成】

#################面试题44#######################
#数组中的逆序对

测试

################测试1#################
# 功能测试

面试题53:在排序数组中查找数字

题目一:数字在排序数组中出现的次数

​ 统计一个数字在排序数组中出现的次数

输入:排序数组[1、2、3、3、3、3、4、5] 和数字3

输出:4

思路

二分查找,确定重复出现的数字的第一个 k 和最后一个 k 的位置

用二分法查找确定数组中第一个 k

​ 数组中间数字与k作比较,

i f m i d d l e > k , k 在数组前半段

i f m i d d l e < k , k 在数组后半段

i f m i d d l e == k ,判断 k 是不是第一个

​ 前一个数字不是 k ,则是第一个,前一个是 k ,则在前半段

类似可求数组中最后一个 k

代码

#################面试题53#######################
#在排序数组中查找数字

class Solution:
    def GetFirstK(self,data,k,start,end):
        if start > end:
            return -1
        middleIndex = (start + end) // 2
        middleData = data[middleIndex]
        if middleData == k:
            if middleIndex > 0 and data[middleIndex-1] != k or middleIndex == 0:
                return middleIndex
            else:
                end = middleIndex - 1
        elif middleData > k:
            end = middleIndex -1
        else:
            start = middleIndex + 1
        return self.GetFirstK(data,k,start,end)

    def GetLastK(self,data,k,start,end):
        if start > end:
            return -1
        middleIndex = (start + end) // 2
        middleData = data[middleIndex]
        if middleData == k:
            if middleIndex < len(data) - 1 and data[middleIndex+1] != k or middleIndex ==  len(data) - 1:
                return middleIndex
            else:
                start = middleIndex + 1
        elif middleData > k:
            end = middleIndex -1
        else:
            start = middleIndex + 1
        return self.GetLastK(data,k,start,end)

    def GetNumberOfK(self, data, k):
        if not data:
            return 0
        length = len(data)
        if data and length > 0:
            first = self.GetFirstK(data,k,0,length-1)
            last = self.GetLastK(data,k,0,length-1)
            if first > -1 and last > -1:
                return last - first + 1
            else:
                return 0

测试

################测试1#################
#功能测试
#数组中包含要查找的数字
data = [1,2,3,3,3,3,4,5]
k = 3
s=Solution()
s.GetNumberOfK(data,k)

#数组中没有要查找的数字
data = [1,2,3,3,3,3,4,5]
k = 6
s=Solution()
s.GetNumberOfK(data,k)

#边界值测试
#查找数组中的最大值、最小值
data = [1,2,3,3,3,3,4,5]
k = 5
s=Solution()
s.GetNumberOfK(data,k)

#特殊输入
data = None
k = 5
s=Solution()
s.GetNumberOfK(data,k)

题目二:0~n-1中缺失的数字

​ 一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字钟有且只有一个数字不在该数组中,请找出该数字

思路

排序数组,下标与数组中数字相似,转换为在排序数组中找出第一个值和下标不相等的元素

利用二分法查找

中间元素和下标相等,下一轮查找右边

中间元素的值和下标不相等,并且它前面一个元素和它下标相等,该值则为第一个不相等元素,下标则为数组不存在的数字

如果中间元素的值和下标不相等,前面一个元素和它的下标不相等,下一轮查找左半边

代码

#################面试题53#######################
#0~n-1中缺失的数字
class Solution:
    def GetMissingNumber(self,numbers):
        if not numbers:
            return -1
        length = len(numbers)
        left = 0
        right = length - 1
        while left <= right:
            middle = (right + left) // 2
            if numbers[middle] != middle:
                if middle == 0 or numbers[middle-1] == middle -1:
                    return middle
                right = middle - 1
            else:
                left = middle + 1
        if left == length:
            return length
        return -1

测试

################测试1#################
#功能测试
numbers = [0,1,2,3,4,6,7,8,9]
s=Solution()
s.GetMissingNumber(numbers)

numbers = [1,2,3,4,5,6,7,8,9]
s=Solution()
s.GetMissingNumber(numbers)


numbers = [0,1,2,3,4,5,6,7,8]
s=Solution()
s.GetMissingNumber(numbers)

#边界值测试
numbers = [0]
s=Solution()
s.GetMissingNumber(numbers)


#特殊输入测试
s=Solution()
s.GetMissingNumber(None)

题目三:数组中数值和下标相等的元素

​ 假设一个单调递增的数组里的每个元素都是整数并且是唯一的。找出数组中任意一个数值等于其下标的元素

输入:数组[-3,-1,1,3,5]

输出:数字3和它下标相等

思路

二分法,假设中间数字为m,下标为i

i f m > i , 查找左半边

i f m < i ,查找右半边

i f m == i ,即得到结果

代码

#################面试题53#######################
#数组中数值和下标相等的元素
class Solution:
    def GetNumberSameAsIndex(self,numbers):
        if not numbers:
            return -1
        length = len(numbers)
        left = 0
        right = length - 1
        while left <= right:
            middle = (left + right) >> 1
            if numbers[middle] == middle:
                return middle
            if numbers[middle] > middle:
                right = middle - 1
            else:
                left = middle + 1
        return -1 
    def GetNumberSameAsIndex2(self,numbers):
        if not numbers:
            return -1
        length = len(numbers)
        left = 0
        right = length - 1
        while left <= right:
            middle = (left + right) >> 1
            if numbers[middle] == middle :
                NumberSameAsIndex = [middle]
                while  middle != 0 and numbers[middle-1] == middle-1:
                    NumberSameAsIndex.append(middle-1)
                    middle -= 1
                while  middle != length-1 and  numbers[middle + 1] == middle + 1:
                    NumberSameAsIndex.append(middle+1)
                    middle += 1
                if len(NumberSameAsIndex) == 1:
                    return middle
                else:
                    NumberSameAsIndex.sort()
                    return NumberSameAsIndex
            if numbers[middle] > middle:
                right = middle - 1
            else:
                left = middle + 1
        return -1 

测试

################测试1#################
#功能测试
numbers = [-3,-1,1,3,5]
s=Solution()
s.GetNumberSameAsIndex(numbers)

numbers = [1,2,3,4,5,6,7,8,9]
s=Solution()
s.GetNumberSameAsIndex(numbers)


numbers = [0,1,2,3,4,5,6,7,8]
s=Solution()
s.GetNumberSameAsIndex2(numbers)

#边界值测试
numbers = [0]
s=Solution()
s.GetNumberSameAsIndex(numbers)


#特殊输入测试
s=Solution()
s.GetNumberSameAsIndex(None)

面试题56:数组中数字出现的次数

题目一:数组中只出现一次的两个数字

​ 一个整形数组里除两个数字之外,其他数字都出现了两次。找出这两个数字,时间复杂度是O(n),空间复杂度是O(1)

思路

异或性质:任何一个数字异或自己都等于0

  • 先考虑数组中指出现一次的数字
  • 把原本数组的数字分为两组,使得不同的数字分别出现在两个组中
  • 采用该数字二进制表示中右边第n位是否为1来划分

代码【需修改】

#################面试题56#######################
#数组中只出现一次的两个数字

class Solution:
    def FindNumsAppearOnce(self, data):
        if not data:
            return 
        LEN = len(data)
        if LEN < 2:
            return
        resultExclusiveOR = 0
        for i in range(LEN):
            resultExclusiveOR ^= data[i]
        indexOf1 = self.FindFirstBitIs1(resultExclusiveOR)

        num1 = num2 = 0
        for j in range(LEN):
            if self.IsBit1(data[j],indexOf1):
                num1 ^= data[j]
            else:
                num2 ^= data[j]
        return num1,num2

    def FindFirstBitIs1(self, num):
        indexBit = 0
        while (num & 1) == 0 and indexBit < 8 * 32:
            num = num >> 1
            indexBit += 1

        return indexBit

    def IsBit1(self, num,indexBit):
        num = num >> indexBit
        return num & 1


测试【未完成】

################测试1#################
# 功能测试

猜你喜欢

转载自blog.csdn.net/qq_29737811/article/details/81200325
今日推荐