其他题目---出现次数的TopK问题

【题目】

  给定String类型的数组strArr,再给定整数k,请严格按照排名顺序打印出现次数前k名的字符串。要求时间复杂度O(Nlogk)。

【进阶题目】

  设计并实现TopKRecord结构,可以不断地向其中加入字符串,并且可以根据字符串出现的情况随时打印加入次数最多的前k个字符串,具体为:

  1.k在TopKRecord实例生成时指定,并且不再变化(k是构造函数的参数)。
   2.含有add(String str)方法,即向TopKRecord中加入字符串。
  3.含有printTopK()方法,即打印加入次数最多的前k个字符串,打印有哪些字符串和对应出现的次数即可,不要求严格按排名顺序打印。

  要求:

  1. 在任何时刻,add方法的时间复杂度不超过O(logk)。
  2. 在任何时刻,printTopK方法的时间复杂度不超过O(k)。

【基本思路】

  原问题。使用一个哈希表记录每种字符串出现的次数,遍历一遍哈希表,根据该哈希表构建一个大小为k的小根堆,该小根堆以词频作为衡量标准,小根堆中的字符串就是出现次数前TopK的字符串。具体过程见如下代码:

class FreNode:
    def __init__(self, st, times):
        self.str = st
        self.times = times

#python3.5
#原问题
def printTopKAndRank(strArr, k):
    def heapInsert(heap, i):
        parent = (i - 1) // 2
        while parent >= 0 and heap[parent].times > heap[i].times:
            heap[parent], heap[i] = heap[i], heap[parent]
            i = parent
            parent = (i - 1) // 2

    def heapify(heap, i, heapSize):
        left = 2 * i + 1
        right = 2 * i + 2
        most = i
        while left < heapSize:
            if heap[left].times < heap[i].times:
                most = left
            if right < heapSize and heap[right].times < heap[most].times:
                most = right
            if most == i:
                break
            else:
                heap[most], heap[i] = heap[i], heap[most]
                i = most
                left = 2 * i + 1
                right = 2 * i + 2

    if strArr == None or len(strArr) == 0 or k < 1 or k > len(strArr):
        return
    map = {}
    for element in strArr:
        if element in map:
            map[element] += 1
        else:
            map[element] = 1
    heap = [0 for i in range(k)]
    index = 0
    for key,value in map.items():
        curNode = FreNode(key, value)
        if index != k:
            heap[index] = curNode
            heapInsert(heap, index)
            index += 1
        else:
            if heap[0].times < curNode.times:
                heap[0] = curNode
                heapify(heap, 0, k)
    for i in range(index-1, 0, -1):
        heap[0], heap[i] = heap[i],heap[0]
        heapify(heap,0,i)
    for i in range(index):
        print("No." + str(i+1) + " :" + heap[i].str + " times: " + str(heap[i].times))

  进阶问题。进阶问题的关键在于,字符串出现的次数是动态的,当然也可以向原问题一样,每加入一个字符串,就更新哈希表以及小根堆。这样可以做到add方法的时间复杂度为O(1),但是,每次printTopK的时候,都需要遍历一遍哈希表并且重新构建小根堆,时间复杂度为O(Nlogk),显然不符合题意。

  要做到printTopK的时间复杂度为O(logk),我们就希望每加入一个字符串的时候,可以利用到之前创建的小根堆,而不是直接重建小根堆。

  因此,我们在原问题的基础上改进一下,每次放入小根堆的元素都记录下它在小根堆中的位置以及它的词频。这样的好处是:假设一个字符串出现了一次,如果字符串已经在小根堆中,此时只需要在小根堆中找到这个字符串所在的位置,让该字符串的词频加1,然后从该位置开始向下调整小根堆即可。如果该字符串之前不在小根堆中,只需要看它的词频加一后是否大于堆顶的词频,如果大的话,更新堆顶,并向下调整堆。每次调整时间复杂度都为O(logk)。

  具体的实现参见如下代码:

#进阶问题
class TopKRecord:
    index = 0    #目前堆中的元素个数
    strNodeMap = {}   #记录字符串和node的对应关系
    nodeIndexMap = {}  #记录node在堆中的位置,如果不在堆中则为-1

    def __init__(self, size):
        self.heap = [0 for i in range(size)]

    def add(self, str1):
        preIndex = -1
        curNode = None
        if str1 not in self.strNodeMap:
            curNode = FreNode(str1, 1)
            self.strNodeMap[str1] = curNode
            self.nodeIndexMap[curNode] = -1
        else:
            self.strNodeMap[str1].times += 1
            curNode = self.strNodeMap[str1]
            preIndex = self.nodeIndexMap[curNode]
        if preIndex == -1:
            if self.index == len(self.heap):
                if curNode.times > self.heap[0].times:
                    self.nodeIndexMap[self.heap[0]] = -1
                    self.nodeIndexMap[curNode] = 0
                    self.heap[0] = curNode
                    self.heapify(0, self.index)
            else:
                self.nodeIndexMap[curNode] = self.index
                self.heap[self.index] = curNode
                self.heapInsert(self.index)
                self.index += 1
        else:
            self.heapify(preIndex, self.index)

    def printTopK(self):
        print("TOP:")
        for i in range(self.index):
            print("Str: " + self.heap[i].str + " Times:" + str(self.heap[i].times))

    def heapify(self, i, heapSize):
        left = 2 * i + 1
        right = 2 * i + 2
        smallest = i
        while left < heapSize:
            if self.heap[left].times < self.heap[i].times:
                smallest = left
            if right < heapSize and self.heap[right].times < self.heap[smallest].times:
                smallest = right
            if smallest == i:
                break
            else:
                self.nodeIndexMap[self.heap[i]] = smallest
                self.nodeIndexMap[self.heap[smallest]] = i
                self.heap[i], self.heap[smallest] = self.heap[smallest], self.heap[i]
                i = smallest
                left = 2 * i + 1
                right = 2 * i + 2

    def heapInsert(self, i):
        while i > 0:
            parent = (i - 1) // 2
            if self.heap[parent].times > self.heap[i].times:
                self.nodeIndexMap[self.heap[i]] = parent
                self.nodeIndexMap[self.heap[parent]] = i
                self.heap[i], self.heap[parent] = self.heap[parent], self.heap[i]
                i = parent
            else:
                break
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值