MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Python列表sort排序的应用案例

2024-09-132.8k 阅读

Python列表sort排序的基础应用

简单列表排序

在Python中,list类型提供了一个方便的方法sort(),用于对列表进行排序。如果列表中的元素是数字类型,无论是整数还是浮点数,sort()方法会按照从小到大的顺序对列表进行排序。

例如,假设有一个包含整数的列表:

num_list = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]
num_list.sort()
print(num_list)

上述代码执行后,num_list会被就地排序,输出结果为[1, 1, 2, 3, 3, 4, 5, 5, 5, 6, 9]

同样,对于浮点数列表也可以进行类似的操作:

float_list = [3.14, 1.618, 2.718, 0.577]
float_list.sort()
print(float_list)

输出结果为[0.577, 1.618, 2.718, 3.14]

按特定顺序排序字符串列表

当处理字符串列表时,sort()方法默认按照字典序进行排序。也就是说,它会比较字符串的每个字符,从第一个字符开始,如果相同则继续比较下一个字符,直到找到不同的字符或者到达字符串末尾。

示例如下:

string_list = ['banana', 'apple', 'cherry', 'date']
string_list.sort()
print(string_list)

执行上述代码,输出结果为['apple', 'banana', 'cherry', 'date']

如果希望按照字符串的长度进行排序,可以通过传递key参数来实现。key参数接受一个函数,该函数会应用到列表的每个元素上,sort()方法会根据函数的返回值进行排序。

string_list = ['banana', 'apple', 'cherry', 'date']
string_list.sort(key=len)
print(string_list)

这里,key=len表示按照字符串的长度进行排序,输出结果为['date', 'apple', 'cherry', 'banana']

复杂数据结构的列表排序

列表中包含字典的排序

在实际开发中,经常会遇到列表中包含字典的情况。例如,假设有一个列表,其中每个元素都是一个表示学生信息的字典,包含name(姓名)和score(分数)两个键。

students = [
    {'name': 'Alice','score': 85},
    {'name': 'Bob','score': 78},
    {'name': 'Charlie','score': 92}
]

如果希望按照学生的分数对这个列表进行排序,可以这样做:

students.sort(key=lambda student: student['score'])
for student in students:
    print(student)

这里使用了lambda函数作为key参数。lambda student: student['score']表示从每个学生字典中提取score值,sort()方法会根据这些分数值对列表进行排序。输出结果为:

{'name': 'Bob','score': 78}
{'name': 'Alice','score': 85}
{'name': 'Charlie','score': 92}

如果希望按照分数从高到低排序,可以设置reverse=True参数:

students.sort(key=lambda student: student['score'], reverse=True)
for student in students:
    print(student)

此时输出结果为:

{'name': 'Charlie','score': 92}
{'name': 'Alice','score': 85}
{'name': 'Bob','score': 78}

列表中包含元组的排序

列表中也可能包含元组,元组中的元素可以是不同类型的数据。假设我们有一个列表,其中每个元组包含一个人的姓名和年龄:

people = [('Alice', 25), ('Bob', 20), ('Charlie', 30)]

如果要按照年龄对这个列表进行排序,可以使用如下代码:

people.sort(key=lambda person: person[1])
for person in people:
    print(person)

这里lambda person: person[1]表示提取元组中的第二个元素(即年龄),sort()方法会根据年龄对列表进行排序。输出结果为:

('Bob', 20)
('Alice', 25)
('Charlie', 30)

同样,如果要按照年龄从大到小排序,可以设置reverse=True

people.sort(key=lambda person: person[1], reverse=True)
for person in people:
    print(person)

输出结果为:

('Charlie', 30)
('Alice', 25)
('Bob', 20)

自定义对象列表的排序

定义可排序的自定义类

当处理自定义类的列表时,也可以使用sort()方法进行排序,但需要定义类的比较行为。例如,我们定义一个Point类,表示二维平面上的点,包含xy坐标。

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __repr__(self):
        return f"Point({self.x}, {self.y})"

现在创建一个包含Point对象的列表:

points = [Point(3, 4), Point(1, 2), Point(5, 6)]

如果直接对这个列表使用sort()方法,会抛出TypeError,因为Python不知道如何比较两个Point对象。我们需要在Point类中定义比较方法。

一种常见的方法是定义__lt__方法(小于方法),它会在使用<运算符时被调用。这样sort()方法就可以使用这个比较逻辑来对列表进行排序。

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __repr__(self):
        return f"Point({self.x}, {self.y})"

    def __lt__(self, other):
        if self.x == other.x:
            return self.y < other.y
        return self.x < other.x

现在对points列表进行排序:

points = [Point(3, 4), Point(1, 2), Point(5, 6)]
points.sort()
print(points)

输出结果为[Point(1, 2), Point(3, 4), Point(5, 6)]。这里的排序逻辑是先比较x坐标,如果x坐标相同再比较y坐标。

使用key参数对自定义对象列表排序

除了定义类的比较方法,还可以通过sort()方法的key参数来对自定义对象列表进行排序。例如,还是对于Point类,我们可以按照点到原点的距离进行排序。

import math


class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __repr__(self):
        return f"Point({self.x}, {self.y})"


points = [Point(3, 4), Point(1, 2), Point(5, 6)]
points.sort(key=lambda point: math.sqrt(point.x ** 2 + point.y ** 2))
print(points)

这里lambda point: math.sqrt(point.x ** 2 + point.y ** 2)计算每个点到原点的距离,sort()方法会根据这个距离值对列表进行排序。输出结果为[Point(1, 2), Point(3, 4), Point(5, 6)],因为Point(1, 2)到原点的距离最近,Point(5, 6)到原点的距离最远。

稳定排序与不稳定排序

稳定排序的概念

在排序算法中,稳定排序是指在排序过程中,相等元素的相对顺序保持不变。Python的list.sort()方法是稳定排序。

例如,假设有一个列表[(2, 'a'), (1, 'b'), (2, 'c')],按照第一个元素进行排序。如果是稳定排序,排序后的结果应该是[(1, 'b'), (2, 'a'), (2, 'c')],其中两个第一个元素为2的元组,原来在前的(2, 'a')仍然在(2, 'c')之前。

不稳定排序的情况

不稳定排序则不会保证相等元素的相对顺序。虽然Python的list.sort()是稳定的,但有些排序算法(如快速排序的某些实现)是不稳定的。

假设我们有一个简单的不稳定排序算法示例(这里仅为示例,非Python内置实现):

def unstable_sort(lst):
    for i in range(len(lst)):
        min_index = i
        for j in range(i + 1, len(lst)):
            if lst[j][0] < lst[min_index][0]:
                min_index = j
        lst[i], lst[min_index] = lst[min_index], lst[i]
    return lst


lst = [(2, 'a'), (1, 'b'), (2, 'c')]
print(unstable_sort(lst))

这个简单的选择排序实现可能会改变相等元素的相对顺序,输出结果可能是[(1, 'b'), (2, 'c'), (2, 'a')],其中两个第一个元素为2的元组相对顺序发生了变化。

性能考虑

排序大数据集的性能

当处理大数据集时,排序的性能变得至关重要。Python的list.sort()方法在处理大量数据时表现良好,因为它是用C语言实现的,底层算法高效。

例如,我们生成一个包含100万个随机整数的列表,并对其进行排序:

import random
import time

big_list = [random.randint(1, 1000000) for _ in range(1000000)]
start_time = time.time()
big_list.sort()
end_time = time.time()
print(f"Sorting took {end_time - start_time} seconds")

在我的测试环境中,这个操作在很短的时间内(大约1秒左右)就完成了,这显示了list.sort()方法在处理大数据集时的高效性。

不同排序方式的性能对比

除了list.sort()方法,Python还提供了sorted()函数,它会返回一个新的已排序列表,而不会修改原始列表。虽然功能上与list.sort()相似,但在性能上会有一些差异,尤其是在处理大数据集时。

我们来对比一下list.sort()sorted()在处理大数据集时的性能:

import random
import time

big_list = [random.randint(1, 1000000) for _ in range(1000000)]

start_time = time.time()
new_sorted_list = sorted(big_list)
end_time = time.time()
sorted_time = end_time - start_time

start_time = time.time()
big_list.sort()
end_time = time.time()
sort_time = end_time - start_time

print(f"sorted() took {sorted_time} seconds")
print(f"list.sort() took {sort_time} seconds")

在测试中,通常会发现list.sort()的性能略优于sorted(),因为sorted()需要创建一个新的列表来存储排序后的结果,而list.sort()是就地排序,节省了创建新列表的时间和空间。

高级应用场景

多条件排序

在一些复杂的场景中,可能需要根据多个条件对列表进行排序。例如,假设有一个学生列表,每个学生字典包含name(姓名)、score(分数)和age(年龄)。

students = [
    {'name': 'Alice','score': 85, 'age': 20},
    {'name': 'Bob','score': 78, 'age': 22},
    {'name': 'Charlie','score': 85, 'age': 21}
]

如果希望先按照分数从高到低排序,分数相同的情况下再按照年龄从小到大排序,可以这样实现:

students.sort(key=lambda student: (-student['score'], student['age']))
for student in students:
    print(student)

这里(-student['score'], student['age'])表示一个元组,sort()方法会首先比较元组的第一个元素(即分数的负数,这样可以实现从高到低排序),如果分数相同,再比较第二个元素(年龄),实现年龄从小到大排序。输出结果为:

{'name': 'Alice','score': 85, 'age': 20}
{'name': 'Charlie','score': 85, 'age': 21}
{'name': 'Bob','score': 78, 'age': 22}

动态排序

在某些应用中,排序的条件可能是动态变化的。例如,有一个图形列表,每个图形对象有不同的属性,如面积、周长等。

class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height

    def area(self):
        return self.width * self.height

    def perimeter(self):
        return 2 * (self.width + self.height)

    def __repr__(self):
        return f"Rectangle({self.width}, {self.height})"


rectangles = [Rectangle(3, 4), Rectangle(1, 2), Rectangle(5, 6)]

如果用户可以选择按照面积或周长对这些矩形进行排序,可以通过一个函数来实现动态排序:

def sort_rectangles(rectangles, by='area'):
    if by == 'area':
        rectangles.sort(key=lambda rect: rect.area())
    elif by == 'perimeter':
        rectangles.sort(key=lambda rect: rect.perimeter())
    return rectangles


rectangles = [Rectangle(3, 4), Rectangle(1, 2), Rectangle(5, 6)]
sorted_by_area = sort_rectangles(rectangles, by='area')
print(sorted_by_area)

rectangles = [Rectangle(3, 4), Rectangle(1, 2), Rectangle(5, 6)]
sorted_by_perimeter = sort_rectangles(rectangles, by='perimeter')
print(sorted_by_perimeter)

在这个例子中,sort_rectangles函数根据传入的by参数决定按照面积还是周长对矩形列表进行排序。

通过以上各种应用案例,我们可以看到Python列表sort()方法的灵活性和强大功能,无论是简单的基础排序,还是复杂的数据结构和高级应用场景,它都能很好地满足需求。在实际编程中,根据具体情况合理运用sort()方法的各种特性,可以提高代码的效率和可读性。同时,了解排序算法的稳定性、性能等方面的知识,也有助于在面对不同场景时做出更合适的选择。