2017-08-07 244 views
12

這個問題實際上是從one previously asked by Mat.Simage)改編而來。儘管它被刪除了,但我認爲這是一個很好的問題,所以我要以更明確的要求和我自己的解決方案來重新發布它。排序一個清單:數字升序排列,字母降序


鑑於字母和數字的清單,說

['a', 2, 'b', 1, 'c', 3] 

的需求是在下降,在上升的數字和字母排序,而不改變字母和數字的相對位置。我的意思是,如果未排序列表是:

[L, D, L, L, D] # L -> letter; # D -> digit 

然後,排序列表也必須

[L, D, L, L, D] 
  1. 的字母和數字的規律做一定替代 - 他們可以以任意順序出現

  2. 排序後 - 數字遞增,字母遞減。

因此對於輸出上面的例子是

['c', 1, 'b', 2, 'a', 3] 

又如:

In[]: [5, 'a', 'x', 3, 6, 'b'] 
Out[]: [3, 'x', 'b', 5, 6, 'a'] 

什麼是做到這一點的好辦法?

+1

看起來很像一個:https://stackoverflow.com/questions/44685760/how-排序字符串 –

+4

@cᴏʟᴅsᴘᴇᴇᴅ:爲了阻止那些不明白本網站鼓勵自我回答的人,我過去在我的問題下發表了一條評論:* PS。有些人可能會認爲我發佈後回答自己的問題是錯誤的。在downvoting之前,請閱讀[可以問和回答你自己的問題](http://blog.stackoverflow.com/2011/07/its-ok-to-ask-and-answer-your-own-questions/) * –

+0

@ Jean-FrançoisFabre哈哈依稀記得那一個。是的,它有點類似。猜猜我無意中從那裏拿起了另一個訣竅。 –

回答

6

下面是使用defaultdict()bisect()一個優化的方法:

In [14]: lst = [5, 'a', 'x', 3, 6, 'b'] 
In [15]: from collections import defaultdict  
In [16]: import bisect 

In [17]: def use_dict_with_bisect(lst): 
      d = defaultdict(list) 
      for i in lst: 
       bisect.insort(d[type(i)], i) 
      # since bisect doesn't accept key we need to reverse the sorted integers 
      d[int].sort(reverse=True) 
      return [d[type(i)].pop() for i in lst] 
    .....: 

演示:

In [18]: lst 
Out[18]: [5, 'a', 'x', 3, 6, 'b'] 

In [19]: use_dict_with_bisect(lst) 
Out[19]: [3, 'x', 'b', 5, 6, 'a'] 

如果你正在處理較大列表它更優化的使用bisect具有約0複雜度(N )下降,只是使用Python內置sort()功能與NLOG(n)的複雜性。

In [26]: def use_dict(lst): 
      d = defaultdict(list) 
      for i in lst: 
       d[type(i)].append(i) 
      d[int].sort(reverse=True); d[str].sort() 
      return [d[type(i)].pop() for i in lst] 

與其他答案基準測試出使用dict和內置sort幾乎1ms的快比其他方法方面的最新方法:

In [29]: def use_sorted1(lst): 
       letters = sorted(let for let in lst if isinstance(let,str)) 
       numbers = sorted((num for num in lst if not isinstance(num,str)), reverse = True) 
       return [letters.pop() if isinstance(elt,str) else numbers.pop() for elt in lst] 
    .....: 

In [31]: def use_sorted2(lst): 
       f1 = iter(sorted(filter(lambda x: isinstance(x, str), lst), reverse=True)) 
       f2 = iter(sorted(filter(lambda x: not isinstance(x, str), lst))) 
       return [next(f1) if isinstance(x, str) else next(f2) for x in lst] 
    .....: 

In [32]: %timeit use_sorted1(lst * 1000) 
100 loops, best of 3: 3.05 ms per loop 

In [33]: %timeit use_sorted2(lst * 1000) 
100 loops, best of 3: 3.63 ms per loop 

In [34]: %timeit use_dict(lst * 1000) # <-- WINNER 
100 loops, best of 3: 2.15 ms per loop 

這裏是一個基準,顯示了使用bisect如何可以減慢長列表的處理過程:

In [37]: %timeit use_dict_with_bisect(lst * 1000) 
100 loops, best of 3: 4.46 ms per loop 
+0

每當我看到'bisect'時我用_have_來upvote) –

+0

聲稱插入排序是一種「優化方法」,雖然用二進制搜索來找到插入點,但有點過分。很顯然,對於小型輸入來說很好。 –

+0

@SteveJessop當然,實際上使用平分並不是我稱之爲優化的唯一原因。這是因爲它與字典相結合。否則,創建列表並使用使用Tim Sort算法的「sorted」(具有「Nlog(N)」順序)對於較長的列表稍快。 – Kasramvd

5

看,不用iter

lst = ['a', 2, 'b', 1, 'c', 3] 
letters = sorted(let for let in lst if isinstance(let,str)) 
numbers = sorted((num for num in lst if not isinstance(num,str)), reverse = True) 
lst = [(letters if isinstance(elt,str) else numbers).pop()for elt in lst] 

我正在尋找一種方式把它變成一個(恐怖)的單行,但至今沒有運氣 - 建議表示歡迎!

4

我把在此裂紋通過創建兩個發電機,然後從他們服用有條件:

In [116]: f1 = iter(sorted(filter(lambda x: isinstance(x, str), lst), reverse=True)) 

In [117]: f2 = iter(sorted(filter(lambda x: not isinstance(x, str), lst))) 

In [118]: [next(f1) if isinstance(x, str) else next(f2) for x in lst] 
Out[118]: ['c', 1, 'b', 2, 'a', 3] 
1

在一個行:

list(map(list, sorted(zip(lst[::2], lst[1::2]), key=lambda x: x[1] if hasattr(x[0], '__iter__') else x[0]))) 
0

要理論上不推薦,但我很樂意編碼它。

from collections import deque 
from operator import itemgetter 

lst = ['a', 2, 'b', 1, 'c', 3] 
is_str = [isinstance(e, str) for e in lst] 
two_heads = deque(map(itemgetter(1), sorted(zip(is_str, lst)))) 
[two_heads.pop() if a_str else two_heads.popleft() for a_str in is_str] 
0

爲什麼我們不按升序排序列表,但要確保號碼前加字母來:

[D, D, L, L, L] # L -> letter; # D -> digit 

我們可以做到這一點以這樣的方式:

>>> lst = [5, 'a', 'x', 3, 6, 'b'] 
>>> sorted(lst, key=lambda el: (isinstance(el, str), el)) 
[3, 5, 6, 'a', 'b', 'x'] 

然後我們從左到右查看原始數組,如果遇到數字,我們會從排序數組的開始處選擇元素,否則從結尾處開始。然後完整詳細的解決方案將是:

def one_sort(lst): 
    s = sorted(lst, key=lambda el: (isinstance(el, str), el)) 
    res = [] 
    i, j = 0, len(s) 
    for el in lst: 
     if isinstance(el, str): 
      j -= 1 
      res.append(s[j]) 
     else: 
      res.append(s[i]) 
      i += 1 
    return res 

lst = [5, 'a', 'x', 3, 6, 'b'] 
print(one_sort(lst)) # [3, 'x', 'b', 5, 6, 'a'] 

要短得多,但神祕的解決方案將是:

def one_sort_cryptic(lst): 
    s = sorted(lst, key=lambda el: (isinstance(el, str), el)) 
    return [s.pop(-isinstance(el, str)) for el in lst] 

lst = [5, 'a', 'x', 3, 6, 'b'] 
print(one_sort_cryptic(lst)) # [3, 'x', 'b', 5, 6, 'a']