Codility刷题之旅 - Leader

410 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 8 天,点击查看活动详情

今天继续Codility的Lessons部分的第8个主题——Leader

Codility - Lessons - Leader

还是按先总结红色部分PDF,然后再完成蓝色的Tasks部分的节奏来~

image.png

PDF Reading Material

  • 前言: 介绍了leader的概念,即在一个数组中出现次数超过数组元素一半的元素(所以一个数组只会有一个leader)
  • 8.1-Solution with O(n2) time complexity: 介绍第一种找leader的,复杂度为O(n2)的方法,即采用双循环,第一层循环是选定候选元素,第二层循环是在整个Array里循环一次来计算出现次数,最后判断是否大于n//2,是则跳出返回
  • 8.2-Solution with O(n log n) time complexity: 首先用复杂度为O(nlogn)的排序算法将数组排序,接着只需要对位于数组中央的元素进行是否出现次数超数组长度一半的判定,最终复杂度是O(nlogn)
  • 8.3-Solution with O(n) time complexity: 复杂度为O(n)的这种方法非常巧妙,因为如果长度为n的数组中存在着leader数字A,那么他的出现次数一定是大于n/2的,此时如果我们从数组里任意去掉一对不同的数,即使不小心去掉了一个leader数字,此时剩余的leader量也一定大于n/2-1,因为转化一下写法就是(n-2)/2,也就是说依然是新数组的leader(新数组的元素数正好是n-2)。 所以,只需要用一个栈循环一遍数组中的元素,并在元素入栈过程中,不断和栈顶元素进行不一致就一起丢掉的判断及操作即可,最后结束循环后,依然存留在栈中的剩余元素,就可能是原始数组的leader。这里说可能,是因为如果原数组有leader的话他才是leader,所以我们最后还要把这个元素在原数组里循环一遍,看看是否真的是leader。

Tasks

  • Dominator image.png

本题说的dominator,就是PDF部分的Leader,不过需要返回的不是leader本身,而是数组A中任意一个值为leader的index。但整体核心计算方式就直接使用PDF中介绍的O(n)复杂度的算法即可,只在最终返回时返回A.index(candidate)即可:

def solution(A):
    # 利用Stack进行一次O(n)的双元素不一致筛除操作
    stack = []
    for a in A:
        if stack and stack[-1]!=a:
            stack.pop() 
        else:
            stack.append(a)
    # 剩余元素再进行判断
    if stack: 
        candidate = stack[0]
        # 回到原数组,检查是否真的是dominator
        size = 0 
        for a in A:
            if candidate==a:
                size+=1 
        if size>len(A)//2:
            return A.index(candidate)
        return -1
    else:
        return -1

image.png

  • Equileader image.png

本题思路是先确定整个Array的leader,方法复用上题方法、也就是PDF中复杂度O(n)的方法。

确定leader存在后,接下来是分别从A的左侧和右侧开始循环,一变计算每个index下leader的占比是否依然符合区间leader,一边记录下符合的index。最后将两侧各扫一遍得到的index做一个去重,也就得到题干要求的Equileader的个数了。

整体代码框架直接贴了上题的,然后在返回leader的分支下完善了新增逻辑:

def solution(A):
    # 利用Stack进行一次O(n)的双元素不一致筛除操作
    stack = []
    for a in A:
        if stack and stack[-1]!=a:
            stack.pop() 
        else:
            stack.append(a)
    # 剩余元素再进行判断
    if stack: 
        candidate = stack[0]
        # 回到原数组,检查是否真的是dominator
        size = 0 
        for a in A:
            if candidate==a:
                size+=1 
        if size>len(A)//2: # 确定有leader
            N = len(A)
            #从左到右,从右到左扫两次,确定能使candidate依然是左侧/右侧区间leader的index
            tmp_N, tmp_index1 = 0, []
            for i, a in enumerate(A):
                if a==candidate:
                    tmp_N += 1
                if tmp_N>(i+1)//2:
                    tmp_index1.append(i)

            tmp_N, tmp_index2 = 0, []
            for i, a in enumerate(A[1:][::-1]):
                if a==candidate:
                    tmp_N += 1
                if tmp_N>(i+1)//2:
                    tmp_index2.append(N-2-i)
            # 两组index做一个去重
            return len(set(tmp_index1)&set(tmp_index2))
        return 0
    else:
        return 0

image.png