Python按字符串排序字典可能不稳定? (在Hackerrank)

时间:2017-11-03 09:10:45

标签: python sorting dictionary

我正在使用Hackerrank进行Python 3学习。

在任务Most Common中,您将获得一个仅包含小写英文字符的字符串,您需要找到该字符串中最常见的三个字符。

我遇到了一些问题。

我对此问题的解决方案如下:

#!/bin/python3
import sys

if __name__ == "__main__":
  s = input().strip()
  ch_dict = {}
  for ch in s:
      if ch in ch_dict : ch_dict[ch] +=1
      else: ch_dict[ch] = 1

  result = sorted(ch_dict.items(),key=lambda d:d[1],reverse=True)
  for i in result:
      if i[1] != 1:
          print(" ".join(map(str,i)))

当我在本地环境中测试此代码时,它可以工作!

但在线测试中,可能失败!

对于此输入:

aabbbccde

我提交了很多次,有时会得到这样的正确答案:

b 3
a 2
c 2

也可以得到这个:

b 3
c 2
a 2

似乎排序可能不稳定?或者我的代码有什么关系?或者在Hackerrank环境中出了什么问题?

我如何保证输出?

6 个答案:

答案 0 :(得分:5)

Python词典无序。当您迭代其内容时,顺序取决于实现,请参阅Why is the order in dictionaries and sets arbitrary?

您只按值对商品进行排序,因此,如果您按任意顺序列出商品,有时('a', 2)对将首先出现,有时为('c', 2)对是。

如果您想要稳定订单,也可以通过对键进行排序来打破值之间的联系。

您的挑战声明:

  

按出现次数的降序对输出进行排序   如果出现次数相同,则按升序对字符进行排序。

因此您需要先按值排序,然后按键,这两个之间的方向不同

您可以通过两次排序或对分数进行排序来实现此目的:

# Sort forward by key, to produce a stable order between keys
by_key = sorted(ch_dict.items(), key=lambda d: d[0])
# Sort backward by value, ties are left in the original order, so by key
result = sorted(by_key, key=lambda d: d[1], reverse=True)

或一步到位:

sorted(ch_dict.items(), key=lambda d: (-d[1], d[0]))

否定计数排序,然后按键排序,不要反转。

请注意,挑战实际上只询问前三个字符。挑战不使用大量输入,但如果有,那么使用排序实际上是低效的。您不需要排序所有键值对,只需排在前3位。您如何才能获得前3名?您可以使用heap queue,它可以有效地为您提供任何序列的前N个:

import heapq

result = heapq.nsmallest(3, ch_dict.items(), key=lambda d: (-d[1], d[0]))

如果排序需要O(NlogN)时间(N是字典的大小),则heapq需要O(NlogK)时间,N是相同的值,但K是最高项的计数;这里是3.对于包含10,000个项目的字典,排序需要大约133k步才能完成,但堆队列只需要16k步。这几乎要快10倍!

答案 1 :(得分:3)

问题在于:

key=lambda d:d[1]

键仅考虑第二个值,而是使用两个值。

答案 2 :(得分:2)

字典无序。您只按值对输出进行排序,但由于原始字典中的键顺序无法保证,因此输出中每个值的排序可能会有所不同。

您可以通过以下两种方式进行排序来解决此问题:

sorted(ch_dict.items(), key=lambda d: (d[1], d[0]), reverse=True)

答案 3 :(得分:0)

dict.items可以按任何顺序返回(键,值)对,取决于实现或键插入顺序等细节。 sorted然后以dict.items返回的顺序迭代这些对。

如果您需要确定性输出,请使用key=lambda d: (d[1], d[0]),以便按字典顺序对键(键,值)对进行排序(如果值恰好相同)。

(如果您使用的是Python 2,key=lambda key, value: (value, key)看起来更好。)

答案 4 :(得分:0)

sorted() is actually stable因为它保留了与您提供的关键功能提取的密钥相同的项目顺序 - 在这种情况下,密钥是值。但由于dict s是无序的,因此对于具有相同值的项目,保留的顺序是未定义的。

解决方案是按(value, key)元组排序:

result = sorted(ch_dict.items(), key=lambda d: (-d[1], d[0]))

注意删除的反转参数,替换为否定值,因为您似乎希望按升序对键进行排序,并按降序对值进行排序。

答案 5 :(得分:0)

在Hackerrank层次结构中,您位于Collections部分。所以解决方案可能是:

#!/bin/python3
import sys,collections
if __name__ == "__main__":
    s = 'abcdebbca' # input().strip()
    res=collections.Counter(s).items(s)
    sortres= sorted ( res, key=(lambda x : (-x[1],x[0])))
    for k,v in sortres[:3] : print k,v

sortres= sorted ( res, key=(lambda x : (-x[1],x[0])))是必要的,如@Martijn Pieters所解释的那样。

修改

由于问题来自dict,另一个仅使用listssetssorted稳定性的答案:

import sys

if __name__ == "__main__":
    s = raw_input().strip()
    set_k, list_kv = set() , list() 
    for x in sorted(s):
        if x not in set_k:
            if set_k : list_kv.append((-count,val))
            set_k.add(x)
            count , val = 0 , x
        count+=1
    for k,v in sorted(list_kv)[:3] : print v,-k