对列表中以整数开头的字符串进行排序

本文记录了一个小任务,要求按整数前缀从小到大排序字符串列表,如['99_A_item_b', '1_A_item_a']。通过使用sorted函数结合lambda表达式,成功实现了预期的排序效果。注意,排序时需将字符串前缀转换为int类型,否则排序结果不符合预期。此外,还提及了按字母排序和使用正则表达式的解决方案。" 113838885,8452874,Lambda表达式与递归:深入理解递归与Y组合子,"['编程语言', 'lambda', '递归法']

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

近期遇到一个小任务,要求按序号(integer affix:整数前缀)从小到大的顺序输出某列表中所有的字符串元素。当时查了一些资料解决了,在此做个记录。

一、问题描述

输入: li = ['99_A_item_b', '1_A_item_a', '12_A_item_c', '102_A_item_f', '20_A_item_d', '15_A_item_e']

(该列表是自己随便编的,但当时处理的列表有个特点,即每一项都有一个子串“A_”,且其前面是数字加下划线,方便定位查找)

预期输出:['1_A_item_a', '12_A_item_c', '15_A_item_e', '20_A_item_d', '99_A_item_b', '102_A_item_f']

如果用普通的list.sort()方法,会发现它排序的法则与我们预期的不一样:

它是根据字符串的第一位、第二位依次进行排序:比如每个元素的第一位,分别是1,1,1,1,2,9;第二位分别是0,2,5等等。

二、解决方案

sorted函数加上一个lambda函数(笔者认为非常妙,读者觉得香吗)

sorted函数官方文档(若英文不好可略过):

<think>我们有一个字符串列表,其中包含数字和字符串,要求排序时只对字符串部分排序,数字保持原位不变。 例如:输入列表:['5', 'apple', '9', 'banana', '2', 'cherry'] 期望输出:['5', 'apple', '9', 'banana', '2', 'cherry'] 经过排序后,字符串按字母顺序排序,数字位置不变: 变成:['5', 'apple', '9', 'banana', '2', 'cherry'] -> 注意,这里数字的位置没有变,但是字符串按字母顺序排了(这里apple和banana已经是按字母顺序,所以不变) 但如果我们有:['10', 'orange', '3', 'apple', '45', 'banana'] 我们期望的结果是:数字位置不变,字符串按字母顺序:['10', 'apple', '3', 'banana', '45', 'orange'] 思路: 1. 首先,我们需要将列表中的数字和字符串分开处理,但数字在排序后要回到原来的位置。 2. 我们可以先提取出所有的字符串(非数字的项)并排序,同时记录数字的位置。 3. 然后,我们创建一个新列表,按照原来的位置,如果是数字则保留原来的数字,如果是字符串则从排序好的字符串列表中按顺序取。 步骤: a. 遍历原始列表,判断每个元素是否是数字(注意:这里元素是字符串形式,如'5','hello') 我们可以用`element.isdigit()`来判断。 b. 记录数字的位置和值,同时记录非数字(字符串)的位置(以便后续用排序后的字符串替换)。 c. 提取所有字符串,对它们进行排序(按字母顺序,或者自定义顺序)。 d. 然后,我们重新构建列表:遍历原始列表的每个位置,如果该位置原来是数字,则保留原值;如果是字符串,则从排序后的字符串列表中按顺序取一个。 但是注意:字符串排序顺序,我们可能需要考虑大小写?题目没有特别说明,我们默认区分大小写(或者可以统一转换成小写再排序?)但题目要求保留原样,所以我们按原字符串排序,但通常我们可能希望不区分大小写。这里我们按照题目要求,题目没有明确,我们就按原字符串排序(即区分大小写,因为Python默认排序区分大小写,大写字母排在小写字母前面)。 然而,题目要求是“保留数字在原位”,所以数字的位置不能动,非数字的位置用排序后的字符串填充。 例如:['10', 'orange', '3', 'Apple', '45', 'banana'] 注意:'Apple'和'orange','banana',如果区分大小写,则'A'在字母表中比'o'和'b'都靠前(因为大写字母的ASCII码小于小写字母)。所以排序后的字符串列表应该是:['Apple', 'banana', 'orange']。 然后我们按照原位置替换:原列表位置1是字符串(原来是'orange')->替换为排序后的第一个字符串'Apple';位置3是字符串(原来是'Apple')->替换为排序后的第二个字符串'banana';位置5是字符串(原来是'banana')->替换为排序后的第三个字符串'orange'。 所以最终结果:['10', 'Apple', '3', 'banana', '45', 'orange'] 但是,这个结果可能不是我们想要的,因为我们通常希望不区分大小写,按字母顺序(即apple, Apple, banana, orange)这样,但题目没有明确,所以我们可以先按区分大小写做,然后提供选项。 不过,题目要求是“保留数字在原位”,所以我们的重点在位置不变。至于字符串排序规则,可以根据需要调整。 实现: 步骤: 1. 遍历原始列表,记录每个位置是数字还是字符串,并记录数字的值(字符串的话记录为None,但我们不记录值,而是另外记录字符串的值)。 2. 同时,我们提取所有的字符串到一个新列表。 3. 对这个字符串列表排序(可以自定义排序规则,比如按小写排序,则用key=str.lower;如果按原字符串,则直接sorted(string_list))。 4. 然后,创建一个迭代器(或者指针)指向排序后的字符串列表开头。 5. 再次遍历原始列表,对于每个位置: 如果这个位置是数字,则保留原元素; 如果是字符串,则从排序后的字符串列表中取出下一个元素(按顺序)放在这个位置。 注意:我们不需要记录每个位置原来的字符串是什么,因为我们要替换成排序后的字符串。我们只需要知道这个位置是不是数字,如果是数字就不动,否则就替换。 代码示例: 假设我们有一个列表:lst = ['10', 'orange', '3', 'Apple', '45', 'banana'] 步骤: 1. 遍历lst,判断每个元素: 位置0: '10' -> 数字 位置1: 'orange' -> 字符串 位置2: '3' -> 数字 位置3: 'Apple' -> 字符串 位置4: '45' -> 数字 位置5: 'banana' -> 字符串 2. 提取所有字符串:['orange', 'Apple', 'banana'] 3. 排序:按原字符串排序:sorted(['orange','Apple','banana']) -> 得到['Apple','banana','orange'] (因为'A'的ASCII小于'b'和'o',而'b'<'o') 4. 创建一个迭代器it = iter(['Apple','banana','orange']) 5. 构建新列表: 位置0: 数字 -> 保留'10' 位置1: 字符串 -> 取迭代器的下一个:'Apple' -> 新列表:['10','Apple'] 位置2: 数字 -> 保留'3' -> ['10','Apple','3'] 位置3: 字符串 -> 取下一个:'banana' -> ['10','Apple','3','banana'] 位置4: 数字 -> '45' -> 加入 位置5: 字符串 -> 取下一个:'orange' -> 加入 最终结果:['10','Apple','3','banana','45','orange'] 但是,我们期望的字母顺序可能是:不区分大小写,那么排序时应该用key=str.lower,这样排序后的字符串列表是:['Apple','banana','orange'](因为按小写:'a'<'b'<'o')和上面一样?不对,注意:'Apple'和'banana',按小写:'a'<'b',所以顺序是Apple, banana, orange。但是如果我们用原字符串排序,由于'Apple'的'A'是大写,而'banana'的'b'是小写,在ASCII中大写字母'A'是65,小写字母'b'是98,所以'Apple'会排在'banana'前面。如果我们不区分大小写,则应该用key=str.lower,这样比较时都转换成小写,则'apple'和'banana'比较就是'a'<'b',所以顺序正确。 所以,我们提供两种方式:区分大小写和不区分大小写。但题目没有明确,我们可以先按不区分大小写排序(因为更常见),或者让用户选择。 由于题目没有明确,我们按不区分大小写排序。 因此,代码实现如下: 注意:原列表中的数字字符串,我们只判断它是不是数字,所以'10'是数字,但注意负数和小数?题目中的数字都是正整数?因为示例都是正整数。如果包含负数或小数,那么用isdigit()就不行了,因为负数有负号,小数有小数点。所以我们需要更通用的判断。 但是题目没有明确,我们假设数字都是非负整数(因为示例都是正整数)。如果有负数或小数,我们可以用异常处理:尝试转换成数字,如果成功则是数字,否则是字符串。 不过,题目要求是“保留数字在原位”,所以数字字符串可能是任意形式(比如科学计数法,负数等),所以我们不能简单地用isdigit(),因为isdigit()只能判断非负整数。 改进:我们可以尝试将每个元素转换为数字,如果转换成功则认为是数字,否则是字符串。 但是,注意:有些字符串可能是数字形式,但我们希望按字符串处理?题目要求是“保留数字在原位”,所以只有原本是数字字符串的才保留。而如何判断?题目没有明确,我们只能按照常规:如果一个字符串可以转换成数字(整数或浮点数),我们就认为它是数字,否则是字符串。 但这样也有问题:比如字符串“123abc”是不能转换成数字的,所以它是字符串。而“-123”可以转换成整数,所以是数字。 所以,我们写一个辅助函数判断一个字符串是否是数字: 方法: try: float(s) # 因为可以包含整数、浮点数、负数 return True except ValueError: return False 但是注意,科学计数法也可以被float转换,比如'1e3'会被转换成1000.0,所以也是数字。 因此,我们这样判断: 步骤: 1. 遍历列表,对每个元素s,尝试float(s),如果成功,则标记为数字,并记录原字符串(因为我们后面还要原样输出,所以不能改变它的表示,比如'001'和'1'是不同的,但float('001')得到1.0,再转字符串会变成'1.0'或'1',这样改变了原字符串的格式)。所以,我们不能改变数字字符串,而是保留原字符串。因此,我们只需要标记它是数字,然后记录它的位置和原字符串即可。 2. 所以,我们不需要转换,只需要判断它是否是数字(用异常捕获),然后原样保留。 但是,注意:有些字符串可能是数字,但表示形式不同(比如前导0),我们保留原字符串,所以直接记录原字符串。 因此,我们不需要转换,只需要判断它是否可转换为数字,然后保留原字符串。 所以,判断函数: def is_number(s): try: float(s) return True except ValueError: return False 然后,我们按照上面的步骤进行。 代码: def sort_list_preserve_numbers(lst): # 判断是否为数字 def is_number(s): try: float(s) return True except ValueError: return False # 第一步:遍历列表,记录数字的位置和值(原字符串),同时收集非数字的字符串 numbers = [] # 记录数字,每个元素是一个元组(index, value) non_numbers = [] # 记录非数字的字符串 for i, item in enumerate(lst): if is_number(item): numbers.append((i, item)) else: non_numbers.append(item) # 第二步:对非数字的字符串进行排序(这里我们不区分大小写,使用key=str.lower) non_numbers_sorted = sorted(non_numbers, key=str.lower) # 第三步:构建结果列表,先全部初始化为None result = [None] * len(lst) # 第四步:将数字放回原来的位置 for idx, num_str in numbers: result[idx] = num_str # 第五步:用一个迭代器指向排序后的非数字列表 it = iter(non_numbers_sorted) # 遍历结果列表,将None的位置用排序后的字符串填充 for i in range(len(result)): if result[i] is None: result[i] = next(it) return result 测试: 输入:['10', 'orange', '3', 'Apple', '45', 'banana'] 期望输出:['10', 'Apple', '3', 'banana', '45', 'orange'] (因为按小写排序:Apple, banana, orange) 但是,注意:排序后的非数字字符串列表是:['Apple','banana','orange'],然后我们按照位置替换:原列表位置1(索引1)是第一个非数字位置,所以用'Apple';位置3(索引3)用'banana';位置5(索引5)用'orange'。 所以结果:索引0: '10',索引1:'Apple',索引2:'3',索引3:'banana',索引4:'45',索引5:'orange' 因此,结果列表:['10','Apple','3','banana','45','orange'] 注意:数字字符串保持了原样,位置不变,字符串排序后放到了原来字符串的位置上。 但是,这个算法改变了原来非数字字符串的位置吗?注意,我们并没有保持原来非数字字符串的顺序,而是用排序后的新顺序替换了。但是题目要求是“保留数字在原位”,对字符串要求排序,但排序字符串的位置在哪里?题目没有明确要求字符串保持原来的相对位置(稳定排序),但排序通常是不稳定的吗?我们这里排序后,字符串之间的相对位置改变了(因为排序了)。但是题目要求是“排序”,所以字符串应该按顺序排列,而数字不动。所以这个结果是符合的。 但是,注意:我们并没有要求字符串在原来字符串的位置上,而是所有字符串被提取出来,排序,然后按照原始列表字符串出现的顺序位置(即从左到右遇到字符串的位置)依次放入排序后的字符串。这样,字符串出现的相对位置(从左到右)就是排序后的顺序。 例如,原始列表:['1','z','2','a','3','b'] -> 字符串位置:索引1:'z',索引3:'a',索引5:'b' 排序字符串:['a','b','z'] 然后,索引1的位置放第一个排序后的字符串'a',索引3的位置放第二个'b',索引5的位置放第三个'z' 结果:['1','a','2','b','3','z'] 这个结果符合要求。 但是,如果原始列表是:['a','1','b'] -> 字符串位置:索引0:'a',索引2:'b' 排序后:['a','b'](按字母顺序,a在b前面) 然后,索引0放'a',索引2放'b',所以结果还是['a','1','b'],看起来没变。 但是,如果原始列表是:['b','1','a'] -> 排序字符串:['a','b'],然后索引0放'a',索引2放'b',结果:['a','1','b'],这样字符串的位置就交换了。 所以,这个算法是:数字位置不变,字符串位置被重新安排,但字符串出现的顺序(在结果列表中从左到右)是排序后的顺序。 这符合题意吗?题目要求“保留数字在原位”,没有说字符串的相对位置,所以这样处理是合理的。 因此,我们按照这个思路编写代码。 但是,注意:如果数字字符串中包含负数或小数,我们的is_number函数可以处理吗?可以,因为float可以转换负数和小数。 例如:['-1.2', 'hello', '3.5', 'world'] -> 数字:['-1.2','3.5'],字符串:['hello','world'],排序字符串:['hello','world'](按字母顺序,hello在world前面)-> 然后,数字位置不变,字符串位置替换:索引0是数字'-1.2'保留,索引1是字符串,放第一个排序后的字符串'hello',索引2是数字'3.5'保留,索引3是字符串,放第二个'world'。结果:['-1.2','hello','3.5','world']。 如果字符串列表是:['world','hello'],那么排序后是['hello','world'],然后替换:索引0是数字,保留;索引1原来是'world',现在替换成'hello';索引2是数字,保留;索引3原来是'hello',现在替换成'world'。结果:['-1.2','hello','3.5','world']。 所以,代码实现如下:</think>### 字符串列表排序(保留数字原位) 要实现字符串列表排序并保留数字在原位,核心思路是: 1. 分离数字和非数字元素 2. 只对非数字元素排序 3. 将排序后的非数字元素放回原位 以下是Python实现代码: ```python def sort_list_preserve_numbers(lst): # 步骤1:分离数字和非数字元素 numbers = [] # 存储数字元素及其位置 non_numbers = [] # 存储非数字元素 for i, item in enumerate(lst): if item.isdigit(): # 判断是否为纯数字 numbers.append((i, item)) else: non_numbers.append(item) # 步骤2:对非数字元素排序(不区分大小写) non_numbers_sorted = sorted(non_numbers, key=lambda x: x.lower()) # 步骤3:构建结果列表 result = [None] * len(lst) # 放回数字元素 for idx, num in numbers: result[idx] = num # 放回排序后的非数字元素 non_num_iter = iter(non_numbers_sorted) for i in range(len(result)): if result[i] is None: result[i] = next(non_num_iter) return result # 测试示例 original_list = ['5', 'orange', '9', 'Apple', '2', 'banana'] sorted_list = sort_list_preserve_numbers(original_list) print("原始列表:", original_list) print("排序结果:", sorted_list) ``` **输出结果:** ``` 原始列表: ['5', 'orange', '9', 'Apple', '2', 'banana'] 排序结果: ['5', 'Apple', '9', 'banana', '2', 'orange'] ``` ### 关键点说明: 1. **数字判断**:使用`isdigit()`检测纯数字字符串 2. **位置记录**:通过元组`(index, value)`记录数字位置 3. **不区分大小写排序**:使用`key=lambda x: x.lower()`实现 4. **原位填充**:先填充数字到原位置,再按顺序填充排序后的字符串 ### 处理特殊情况: 如果需要处理负数/小数等复杂数字格式,可修改判断逻辑: ```python def is_number(s): try: float(s) # 尝试转换为浮点数 return True except ValueError: return False ``` ### 性能说明 - 时间复杂度:$O(n \log n)$(主要来自排序操作) - 空间复杂度:$O(n)$(存储分离的元素) 这种方法确保数字位置绝对不变,只对字符串元素进行字典序排序,且保留原始字符串的大小写形式[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值