剑指offer-----数组-----Python

1,133 阅读9分钟

二维数组中的查找

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

想: 先想,不急于做题。

  • 这道题让我们干嘛呢?判断输入数值是否在数组中,其实就是一个判断True or False

  • 两个for 循环行不行?理论上只要内存足够就可以啊。但是,如果是这样,面试官有必要规定数组 ?那么我们怎么基于算法做呢?首先观察这个数组,“每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序”,这说明什么?说明最后一列最大,依次往前...

图示:


如果数值A小于最后一列的第一个数,那说明最后一列都不满足,那就往前挪一列;如果数值A大于最后一列第一个数,说明第一行不满足,那就往下挪一行。....

如果在一个二维数组中找到数字7,则返回true,如果没有找到,则返回false。


# -*- coding:utf-8 -*-
class Solution:
    # array 二维列表
    def Find(self, target, array):
        # write code here
        rows = len(array)
        cols = len(array[0])
        if rows > 0 and cols > 0:
            row = 0
            col = cols - 1
            while row < rows and col >= 0:
                if target == array[row][col]:
                    return True
                elif target < array[row][col]:
                    col -= 1
                else:
                    row += 1
        return False

旋转数组的最小数字

题目:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1。 NOTE:给出的所有元素都大于0,若数组大小为0,请返回0。

想: 先想,不急于做题

  • 让我们干嘛?找最小值,用python的话,偷个懒,不管数组是否旋转,最小值是不会变的,可能最小值的位置会有变化,那直接min()

# -*- coding:utf-8 -*-
class Solution:
    def minNumberInRotateArray(self, rotateArray):
        # write code here
        if len(rotateArray)==0:
            return 0
        else:
            return min(rotateArray)

上面的方法,嗯,算是顺风车。那么怎么用算法做呢?旋转之后的数组实际上可以划分为两个排序的字数组,而且前面的字数组的元素大于或者等于后面字数组的元素。最小的元素刚好是这两个字数组的分界线。


# -*- coding:utf-8 -*-
class Solution:
    def minNumberInRotateArray(self, rotateArray):
        # write code here
        if len(rotateArray)==0:
            return 0
        else:
            length = len(rotateArray)
            for i in range(length-1):
                if rotateArray[i]>rotateArray[i+1]:
                    return rotateArray[i+1]

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

题目:输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有的奇数位于数组的前半部分,所有的偶数位于位于数组的后半部分,并保证奇数和奇数,偶数和偶数之间的相对位置不变

想: 先想,不急于做题

  • 让我们干嘛呢?奇数在前偶数在后,相对位置不变

  • 那就找两个空列表,第一个空列表装奇数,第二个装偶数,再拼接。


# -*- coding:utf-8 -*-
class Solution:
    def reOrderArray(self, array):
        # write code here
        lo = []
        le = []
        for d in array:
            if d % 2 == 1:
                lo.append(d)
            else:
                le.append(d)
        return lo + le

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

题目:数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。

想: 先想,不急于做题

  • 让我们干嘛?找出现次数最多的树,且这个数出现的次数超过数组长度的一半。数组为偶数,一半不用说。数组为奇数,一半是奇数//2,比如9//2=4

# -*- coding:utf-8 -*-
class Solution:
    def MoreThanHalfNum_Solution(self, numbers):
        # write code here
        length = len(numbers)
        if length == 0:
            return 0
        mid = length//2
        for i in range(mid+1):
            if numbers.count(numbers[i]) >mid:
                return numbers[i]
        return 0

连续子数组的最大和

题目:HZ偶尔会拿些专业问题来忽悠那些非计算机专业的同学。今天测试组开完会后,他又发话了:在古老的一维模式识别中,常常需要计算连续子向量的最大和,当向量全为正数的时候,问题很好解决。但是,如果向量中包含负数,是否应该包含某个负数,并期望旁边的正数会弥补它呢?例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。你会不会被他忽悠住?(子向量的长度至少是1)

想: 先想,不急于做题

  • 仔细读题目,其实已经提示我们了-----连续子向量的最大和

class Solution:
    def FindGreatestSumOfSubArray(self, array):
        maxList = []
        tmp = 0
        for a in array:
            tmp += a
            if tmp > 0:
                maxList.append(tmp)
            #是否大于0是判断是否连续的关键
            else: tmp = 0
        if not maxList:
            return -1
        return max(maxList)

把数组排成最小的数

题目:输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。

想: 先想,不急于做题

  • 先将整数数组转为字符串数组,然后用比较器实现字符串比较大小。如果有字符串A和B, A + B < B + A,则A在前;反之B在前。最后将字符串数组连接去除返回值左侧的0。

    我们可以先思考只有两个数字的情况: [3,32],可以看出来 332>323 因此需要把数组改变为[32,3]

知识小小讲堂

如何将数组映射为字符串?

常规做法:

#字符串转数组
str = '1,2,3'
arr = str.split(',')

#结果:
arr:['1','2','3']

#数组转字符串
arr = ['a','b']
str = ','.join(arr)
#结果:
str:'a,b'

a = [1,23,456]
b = ''.join(str(i) for i in a)
#结果:
'123456'

还能怎么做呢?注意python3python2的处理是不一样的。

python3----map

a = [1,23,456]
b = list(map(str,a))
print(b)
#结果:
#['1','23','456']

python2----map

a = [1,23,456]
b = (map(str,a))
print(b)
#结果:
#['1','23','456']

另外,可能会需要用到匿名函数lambdalambda语句构建的其实是一个函数对象。匿名函数有个限制,就是只能有一个表达式,不用写return,返回值就是该表达式的结果。

li = [11, 22, 33]
sl = [1, 2, 3]
new_list = list(map(lambda a, b: a + b, li, sl))
print(list(new_list))
#结果
#[12,24,36]

我们可能还需要比较函数cmp(),但是python3里并没有cmp(),这个函数只在python2里有。在python3里用(a>b)-(a<b)来代替

当然这里我用到了排序

# -*- coding:utf-8 -*-
class Solution:
    def PrintMinNumber(self, numbers):
        # write code here
        if len(numbers)==0:
            return None
        if len(numbers)==1:
            return numbers[0]
        #把数组转换为字符串
        num = list(map(str,numbers))
        #我们用冒泡排序排个序---从小到大
        for i in range(len(num)):
            for j in range(len(num)-1):
                if int(num[j]+num[j+1])>int(num[j+1]+num[j]):
                    num[j],num[j+1] = num[j+1],num[j]
                else:
                    num[j],num[j+1] = num[j],num[j+1]
        final_num = ''.join([i for i in num])
        
        return final_num

数组中的逆序对

题目:在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007。

# -*- coding:utf-8 -*-
class Solution:
    def __init__(self):
        self.i = 0
    def InversePairs(self, data):
        # write code here
        length = len(data)
        for i in range(length):
            for j in range(i+1,length):
                if data[i]>data[j]:
                    self.i +=1
        b = self.i%1000000007
        return b

数字在排序数组中出现的次数

题目:统计一个数字在排序数组中出现的次数。

# -*- coding:utf-8 -*-
class Solution:
    def GetNumberOfK(self, data, k):
        # write code here
        return data.count(k)

数组中重复的数字

题目:在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。

# -*- coding:utf-8 -*-
class Solution:
    # 这里要特别注意~找到任意重复的一个值并赋值到duplication[0]
    # 函数返回True/False
    def duplicate(self, numbers, duplication):
        # write code here
        for i in range(len(numbers)):
            if numbers.count(numbers[i])>1:
                duplication[0] = numbers[i]
                return True
        return False

构建乘积数组

题目:给定一个数组A[0,1,...,n-1],请构建一个数组B[0,1,...,n-1],其中B中的元素B[i]=A[0]*A[1]*...*A[i-1]*A[i+1]*...*A[n-1]。不能使用除法。

课堂小小知识

累乘:reduce() 函数会对参数序列中元素进行累积。

匿名函数lambda用法

  • lambda(a,b: a+b)

    关键字lambda表示匿名函数,冒号前是参数,可以有多个,用逗号隔开,冒号右边的返回值。

    例子:

#举一个简单的例子:
def f(x):
  return x**2
  print f(4)

#Python中使用lambda的话,写成这样,这样是很节省空间的
g = lambda x : x**2
print g(4)

  • 进阶-内置函数----map()

li = [11, 22, 33]
sl = [1, 2, 3]
new_list = list(map(lambda a, b: a + b, li, sl))

  • 进阶-内置函数----reduce()                                                          Python 3里,reduce() 函数已经被从全局名字空间里移除了,它现在被放置在functools 模块里                                                                                                                         对于序列内所有元素进行累计(包括累加、累乘)操作

from functools import reduce
li = [11,22,33]
result = reduce(lambda a,b:a+b,li)

  • 进阶-内置函数----filter()
    • 对于序列中的元素进行筛选,最终获取符合条件的序列

li = [11,22,33]
new_list = filter(lambda a:a>22,li)
print(list(new_list))
#filter第一个参数为空,将获取原来序列
输出结果:
>>>[33]

代码:

# -*- coding:utf-8 -*-
class Solution:
    def multiply(self, A):
        # write code here
        B = []
        for i in range(len(A)):
            B.append(reduce(lambda x,y:x*y, (A[:i] + A[i+1:])))
        return B

接下来,我们需要串一遍十大排序算法