《剑指offer》35-38&40、数组类操作:出现次数过半的数、最小的k个数、数据流的中位数、连续子数列之和、把数组排成最小的数

数组

既然要一次介绍那么多数组问题,就有必要老生常谈地聊聊数组:数组(Array)是有序的元素序列。组成数组的各个变量称为数组的分量,也称为数组的元素,有时也称为下标变量。用于区分数组的各个元素的数字编号称为下标。数组是在程序设计中,为了处理方便, 把具有相同类型的若干元素按有序的形式组织起来的一种形式。
早在本科学C++的时候,大家就已经开始接触数组,和数组一起出现的往往是时间复杂度和空间复杂度的考察,比如求两个有序数组的交集,时间复杂度O(m+n);求两个有序数组的中位数,时间复杂度log(len1+len2)等等。对于《剑指offer》中的问题也一样,如果我们考虑暴力求解自然是可以的,但是当面试的时候,考官对时空复杂度提出要求的时候,我们往往要另辟蹊径。
本次出现的5个问题,我以AC为目标,不时出现了一些暴力方法。这些方法虽然能AC,但是并不是坠吼的,请各位读者自行思考是否存在更优解。

数组中出现次数过半的数

offer35的要求是给出一个数组,要求数组中出现次数过半的数,如果不存在则输出0。
这道题的方法就多种多样了。

语法糖

利用sort和count轻松便捷加愉快。

# offer35-solution 1
def MoreThanHalf(self, numbers):
    numbers.sort()
    p = numbers[len(numbers)//2]
    if numbers.count(p)> len(numbers)//2:
        return p
    else:
        return 0

相互抵消的计数器

想要写出空间复杂度是O(1)的代码,就要考察题目本身的内涵——我们通过写一个计数器。设置一个中间变量,比较数组中相邻数字是否相同,若不相同,则相互抵消计数器减1,若相同则计数器加1,中间变量为该值,继续依次比较,最后剩下的数字有可能是次数超过一半的数字。
这种方法需要再写一个额外的for循环得出该数字的次数与数组长度的一半比较。

# offer35-solution 2
def MoreThanHalf(self, numbers):
    if not numbers:
        return 0
    if len(numbers) == 1:
        return numbers[0]
    s = [1]
    p = numbers[0]
    for i in range(1, len(numbers), 1):
        if numbers[i] == p:
        # s就是一个计数器
            s.append(s[i-1]+1)
        else:
            s.append(s[i-1]-1)
        if s[i] == 0:
            s[i] += 1
            p = numbers[i]

    sum = 0
    for i in range(0, len(numbers), 1):
        if numbers[i] == p:
            sum += 1
    if sum > len(numbers)//2:
        return p
    else:
        return 0

顺序操作法

如果不追求空间复杂度,只要求写出时间和空间复杂度都是O(n)的代码,那就挺轻松了
下面的代码没有用到语法糖。

# offer35-solution 3
def MoreThanHalf(self, numbers):
    numCount = {}
    for num in numbers:
        if num in numCount:
             numCount[num] += 1
        else:
            numCount[num] = 1
        if numCount[num] > (len(numbers)>>1):
            return num
    return 0

数组中最小的k个数

offer36要求求出数组中最小的k个数,简直就是语法糖展示教程。注意sort排序的时间复杂度是O(nlogn),我曾在一次面试中被问及说“你用sort,那你能写一个sort出来不?”然而机智的我早就把sort的原理+代码背熟了,看在我只是搞数据的份上,面试官并没有继续为难我(当然那次面试也挂了哈哈哈哈哈哈

# offer36-solution
def GetLeastNumbers_candy(self, tinput, k):
    if k > len(tinput):
        return []
    tinput.sort()  # sort排序的时间复杂度是O(nlogn)
    return tinput[:k]

# offer36-solution 2
def GetLeastNumbers_candy2(self, tinput, k):
    import heapq
    if k > len(tinput):
        return []
    return heapq.nsmallest(k, tinput) # nsmallest的时间复杂度是O(nlogk)

数据流中的中位数

offer37要求先读取数据流,然后求其中位数。
边读数据流边做sort就好了。python没有方便的求中位数的糖,但自己写也不难。

# offer37-solution
class Solution:
    def __init__(self):
        self.array = []

    def Insert(self, num):
        # write code here
        self.array.append(num)
        self.array.sort()

    def GetMedian(self, M):
        # write code here
        length = len(self.array)
        if len(self.array) % 2 == 1:
            return self.array[length // 2]
        else:
            return (self.array[length // 2 - 1] + self.array[length // 2]) / 2.0

连续子数列的最大和

offer38要求连续子数列的最大和,意即给定一个数字序列 [ A 1 , A 2 , A 3 , … A n ] [A1,A2,A3,…An] [A1,A2,A3,An],求 i , j ( 1 < = i < = j < = n ) i,j(1<=i<=j<=n) i,j(1<=i<=j<=n)使得 A i … A j Ai…Aj AiAj和最大。
思路总结为两点:
1、最大和子数列的第一项必不为负数
2、如果前面数的累加值加上当前数后的值会比当前数小,说明累计值对整体和是有害的;如果前面数的累加值加上当前数后的值比当前数大或者等于,则说明累计值对整体和是有益的。
以2作为更新遍历条件,构建变量判断累加值是否大于最大值:如果大于最大值,则最大和更新;否则保留之前的最大和即可。
我也没想到别的什么好方法和好思路,代码也写得略显冗余。

# offer38-solution
class Solution:
    def FindGreatestSumOfSubArray(self, array):
        if not array:
            return
        if max(array) <= 0:
            return max(array)
        p = sum =  array[0]
        for i in range(1,len(array),1):
            if array[i] >= 0 and sum >= 0:
                sum = sum + array[i]
                p = sum
            elif array[i]>=0 and sum < 0:
                p = sum = array[i]
            else:
                sum = sum + array[i]
        return p

把数组排成最小的数

offer40的要求是,输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印出能拼接出的所有数字中最小的一个。例如:
input:[1,32,9,565]
output:1325659
先将整数处理成字符串,通过比较字符串的组合,得到组成顺序。基于这个思路,即便是暴力方法也是可以AC的。
那就奔着AC去了……

# offer40-solution
import itertools
class Solution:
    def PrintMinNumber(self, numbers):
        # 暴力解法
        if len(numbers) <= 0:
            return ""
        str_numbers = [str(i) for i in numbers]
        premu = itertools.permutations(str_numbers)
        res = [''.join(i) for i in premu]
        return min(res)

使用itertools模块的permutations可以事半功倍。

参考

Python3 List sort()方法
使用Python模块:heapq模块(堆)
python之itertools模块
剑指Offer 45.把数组排成最小数 | LeetCode 179.最大数(Python2 / 3)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值