用整数和单词对字符串进行排序,位置不发生任何变化

问题描述 投票:0回答:6

假设我有一个字符串 a。

a = "12 I have car 8 200 a"

我需要以输出应为:

的方式对该字符串进行排序
8 a car have 12 200 I

即,对字符串进行排序,使所有单词按字母顺序排列,所有整数按数字顺序排列。此外,如果字符串中的第 n 个元素是整数,则它必须保持整数,如果它是单词,它必须保持单词。

这是我尝试过的:

a = "12 I have car 8 200 a"


def is_digit(element_):
    """
    Function to check the item is a number. We can make using of default isdigit function
    but it will not work with negative numbers.
    :param element_:
    :return: is_digit_
    """
    try:
        int(element_)
        is_digit_ = True
    except ValueError:
        is_digit_ = False

    return is_digit_



space_separated = a.split()

integers = [int(i) for i in space_separated if is_digit(i)]
strings = [i for i in space_separated if i.isalpha()]

# sort list in place
integers.sort()
strings.sort(key=str.lower)

# This conversion to iter is to make use of next method.
int_iter = iter(integers)
st_iter = iter(strings)

final = [next(int_iter) if is_digit(element) else next(st_iter) if element.isalpha() else element for element in
         space_separated]

print " ".join(map(str, final))
# 8 a car have 12 200 I

我得到了正确的输出。但我使用两个单独的排序函数来对整数和单词进行排序(我认为这很昂贵)。

是否可以使用单个排序函数完成整个排序?

python python-2.7 performance sorting iterator
6个回答
5
投票

numpy
允许写得更简洁,但并不能消除对两种单独排序的需要:

$ python3
Python 3.5.2 (default, Nov 23 2017, 16:37:01) 
[GCC 5.4.0 20160609] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy as np
>>> from numpy.core.defchararray import isdecimal, lower
>>> 
>>> s = "12 I have car 8 200 a"
>>> 
>>> a = np.array(s.split())
>>> 
>>> integer_mask = isdecimal(a)
>>> string_mask = ~integer_mask
>>> strings = a[string_mask]
>>> 
>>> a[integer_mask] = np.sort(np.int_(a[integer_mask]))
>>> a[string_mask]  = strings[np.argsort(lower(strings))]
>>> 
>>> ' '.join(a)
'8 a car have 12 200 I'

4
投票

是否可以使用单个排序函数完成整个排序?。

不,不是真的。

为什么不呢?事实证明答案已经在您的代码中了。

integers.sort()
strings.sort(key=str.lower)

请注意您需要如何在此处按两个不同的函数进行排序。第一个是整数排序,第二个是小写字符串排序。我们可以尝试这样的事情:

def get_sort_order(element):
    try:
        value = int(element)
    except ValueError:
        value = element.lower()
    return value

a.sort(key=get_sort_order)

但这也行不通;它只是给我们结果

['8', '12', '200', 'a', 'car', 'have', 'I']

您可能可以将其强制作为解决方案,但这不会很好。

不过,还有一点我想强调一下:

但是我使用两个单独的排序函数来对整数和单词 进行排序(我认为这很昂贵)。

无论如何,对两个不同的列表进行排序基本上总是会更快。要找出原因,只需看看这两个任务的时间复杂度即可:

假设一个长度为 1000 的列表,正好一半是整数,一半是字符串,排序算法为 O(nlog(n)):

单一排序:1000 * log(1000) = 3000

两种不同的排序:2 * (500 * log(500) = ~2699

因此,在一次运行中对列表进行排序既更难实现,也更慢!


4
投票

通过在“排序”方法中应用自定义函数作为上述用户,可以实现一种排序。我已经尝试过相同的简化版本。默认的“排序”方法只需稍加调整即可产生奇迹。希望它能解决您的疑问。

import re

input = "12 I have car 8 200 a"
splitted = input.split()
s_lst=sorted(splitted, key=lambda a:int(a) if a.isdigit() else a.lower())

count_nos = re.findall(r'\d+',' '.join(s_lst))
str_index = len(count_nos)
no_index = 0
result=[]
for i in range(0,len(splitted)):
    if splitted[i].isdigit():
        result.append(s_lst[no_index])
        no_index+=1
    else:
        result.append(s_lst[str_index])
        str_index+=1
print ' '.join(result)

3
投票

如果您编写一个自定义函数进行比较,您可以采用一种方法。 这个想法是在同一个列表中按升序对单词进行排序,并按降序对整数进行排序。比较单词和整数的情况,然后将单词视为比单词更小的单词。

然后,为了打印最终结果,如果找到单词,则增加单词的索引,如果找到数字,则减少整数的索引。

以下代码适用于 python2:

a = "12 I have car 8 200 a"

def custom_compare(x,y):
    if x.isdigit() and y.isdigit():
        return int(y) - int(x) #do a descending order
    if x.isdigit() and y.isdigit() == False:
        return 1
    if x.isdigit() == False and y.isdigit():
        return -1
    if x.isdigit() == False and y.isdigit() == False:
        # do ascending order
        if x.lower() == y.lower():
            return 0
        elif x.lower() < y.lower():
            return -1
        else:
            return 1

original_list = a.split(" ")
sorted_list = sorted(original_list, cmp=custom_compare)

result = []
integer_index = -1
string_index = 0
for word in original_list:
    if word.isdigit():
        result.append(sorted_list[integer_index])
        integer_index = integer_index - 1
    else:
        result.append(sorted_list[string_index])
        string_index = string_index + 1

result
['8', 'a', 'car', 'have', '12', '200', 'I']

Python 3: 导入功能工具

a = "12 I have car 8 200 a"

def custom_compare(x,y):
    if x.isdigit() and y.isdigit():
        return int(y) - int(x) #do a descending order
    if x.isdigit() and y.isdigit() == False:
        return 1
    if x.isdigit() == False and y.isdigit():
        return -1
    if x.isdigit() == False and y.isdigit() == False:
        # do ascending order
        if x.lower() == y.lower():
            return 0
        elif x.lower() < y.lower():
            return -1
        else:
            return 1

original_list = a.split(" ")
sorted_list = sorted(original_list, key=functools.cmp_to_key(custom_compare))

result = []
integer_index = -1
string_index = 0
for word in original_list:
    if word.isdigit():
        result.append(sorted_list[integer_index])
        integer_index = integer_index - 1
    else:
        result.append(sorted_list[string_index])
        string_index = string_index + 1

result

PS:单词比较可以高效地写出来。 我来自 C 背景,我不确定 pythonic 的比较方式。


1
投票
s = "2 is a A -3 car 11 I 0 a"

def magick(s):
  s = s.split()

  def reverse(tuples):
    return [(a, b) for (b, a) in tuples]

  def do_sort(tuples):
    firsts  = [a for a, _ in tuples]
    seconds = [a for _, a in tuples]
    return list(zip(sorted(firsts), seconds))

  def str_is_int(x):
    try:
      int(x)
      return True
    except:
      return False

  indexed = list(enumerate(s))

  ints = do_sort([(int(x), ix) for (ix, x) in indexed if     str_is_int(x)])
  strs = do_sort([(    x , ix) for (ix, x) in indexed if not str_is_int(x)])

  return ' '.join([str(b) for _, b in sorted(reverse(ints+strs))])

print(magick(s))

0
投票

该解决方案在将原始输入分组为整数和字符串后,使用单个自定义排序算法:

def gt(a, b):
  return a > b if isinstance(a, int) and isinstance(b, int) else a[0].lower() > b[0].lower()

def type_sort(d):
   '''similar to bubble sort, but does not swap elements of different types. 
      For instance, type_sort([5, 3, 'b', 'a']) => [3, 5, 'a', 'b']
   '''
   for _ in d:
     for i in range(len(d)-1):
       _c = d[i]
       _t = d[i+1]
       if isinstance(_c, type(_t)):
         if gt(_c, _t):
           d[i+1] = _c
           d[i] = _t
   return d

def get_type(x):
  return int(x) if x.isdigit() else x

def sort_in_place(s:str):
  _s = list(map(get_type, s.split()))
  new_s = type_sort([i for i in _s if isinstance(i, int)]+[i for i in _s if isinstance(i, str)])
  ints = iter(i for i in new_s if isinstance(i, int))
  strings = iter(i for i in new_s if isinstance(i, str))
  return ' '.join(map(str, [next(ints) if isinstance(i, int) else next(strings) for i in _s]))

print(sort_in_place(a))

输出:

'8 a car have 12 200 I'
© www.soinside.com 2019 - 2024. All rights reserved.