经典排序方法(一):冒泡/插入/归并/快排

本文深入探讨了四种经典的排序算法——插入排序、冒泡排序、归并排序和快速排序的原理与实现。从算法的基本思想出发,详细阐述了每种算法的步骤,包括插入排序的逐步构建有序序列、冒泡排序的元素比较与交换、归并排序的分割与集成,以及快速排序的基准元素选取与分割。并通过具体实例和代码展示了算法的具体应用。

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

1. 插入排序 Insertion Sort

插入排序的工作原理是通过构建有序序列,对于未排序的序列,在已排序的序列中从后向前扫描,找到相应的位置并插入。

算法实现:

  1. 从第一个元素开始,该元素可以认为已经被排序
  2. 取出下一个元素,在已经排序的元素序列中从后向前扫描
  3. 如果已排序的元素大于新元素,将已排序元素移动到下一位置
  4. 重复步骤三,直到找到已排序的元素小于或者等于新元素的位置
  5. 将新元素插入该位置后
  6. 重复步骤2~5
def insert_sort(list):
    n = len(list)
    for i in range(1,n):
        temp = list[i]
        j = i - 1
        while j >= 0 and temp < list[j]:
            list[j+1] = list[j]
            j -= 1
        list[j+1] = temp
    return list

2. 冒泡排序 Bubble Sort

冒泡排序重复地走访要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来,

算法实现:

  1. 比较相邻的元素,如果第一个比第二个大,就交换它们
  2. 对每一对相邻元素作同样的工作。
def bubble_sorted(list_):
    new_list = list(list_)
    list_len = len(new_list)
    for i in range(list_len - 1):
        for j in range(list_len - 1, i, -1):
            if new_list[j] < new_list[j - 1]:
                new_list[j], new_list[j - 1] = new_list[j - 1], new_list[j]
    return new_list

3. 归并排序 Merge Sort

归并排序的基本思想在于把两个已经排序的序列合并成一个序列。其大概可以分为两个步骤:

  • 分割: 递归地把当前序列平均分割成两半
  • 集成: 在保持元素顺序的同时将子序列归并。

在归并操作中,有两种不同的方法,分别为递归法(Top-Down)和迭代法(Bottom-Up),在此我们主要讨论迭代法。其基本过程如下:

  1. 申请空间,使其大小为两个已排序子序列之和;
  2. 设定两个指针,最初位置分别为两个已经排序子序列的起始位置;
  3. 比较两个指针所指向的元素,选择较小的放入到合并空间,并将指针移动到下一位之;
  4. 重复步骤3,直到某一指针到达序列末尾
  5. 将另一列所剩下的元素直接复制到合并序列末尾

算法实现

def merge(left, right):
    result = []
    while left and right:
        if left[0] <= right[0]:
            result.append(left.pop(0)) #pop取出元素,并删除序列对应的元素
        else:
            result.append(right.pop(0))
    if left:
        result += left
    if right:
        result += right
    # print("result",result) #推荐利用print函数查看输出,进一步理解递归
    return result

def merge_sort(L):
    if len(L) <= 1:
        # When only 1 element, just return it
        return L
    mid = len(L) // 2
    left = L[:mid]
    right = L[mid:]

    left = merge_sort(left)
    right = merge_sort(right)
    # conquer sub-problem recursively
    return merge(left, right)
    # return the answer of sub-problem

>>> test = [8,7,6,5,4,3,2,1]
>>> merge_sort(test)

4. 快速排序 Quick Sort

对于该算法的理解,推荐各位读者参考:https://wiki.jikexueyuan.com/project/easy-learn-algorithm/fast-sort.html
快速排序的基本思想在于,对于待排序序列中的某一个基准数,找到一个位置k,并将基准数移动至位置k.使得以k为分界点,左边的数都小于这个基准数。下面以序列6 1 2 7 9 3 4 5 10 8为例,简单说明快速排序的基本原理。

  1. 首先为了简便起见,我们选择6作为我们的基准数,并初始化两个指针分别指向序列起始和末尾位置。为了便于描述,分别命名为指针i指针j。此时指针i指向6,指针j指向8。
  2. 指针j首先开始移动,直到遇到第一个小于基准数的元素停下,此时其指向了元素5。然后指针i开始自左向右移动,并遇到了第一个大于基准的元素停下,此时其指向了元素7
  3. 交换两个指针对应的元素。此时的序列为6 1 2 5 9 3 4 7 10 8.
  4. 重复步骤2~3,直到两个指针相遇,说明该轮探索结束,交换指针元素和基准数,此时序列为3 1 2 5 4 6 9 7 10 8。至此,基准元素6的左侧均比6小,右侧均比6大。
  5. 将旧基准元素的两侧视为新的子序列,重复上述过程。
    该例子可以用下图表达:
    alt text

算法实现

上述例子可以被抽象为:

  1. 挑选基准值(pivot);
  2. 分割:重新排序序列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面。在这个分割结束后,对基准值的排序结束。
  3. 递归排序子序列。

需要注意的是,上述算法实现过程主要用了分治法(divide and conquer)的思想,需要 Ω ( n ) \Omega(n) Ω(n)的额外存储空间。所以现在又有原地分割(In-place)的方法对此加以改进。在原地分割中,在移动元素的过程中,基准元素最后的摆放位置也被寻找。故而节省了一定的额外存储空间,其具体代码实现如下:

def quick_sort(lst):
    lst = lst[:]
    n = len(lst)
    def partition(lst,start,end):       
        i = start - 1
        pivotIndex = end
        pivot = lst[end]
        for j in range(start,end):
            if lst[j] < pivot:
                i = i + 1
                if i != j:
                    lst[i], lst[j] = lst[j], lst[i]
        lst[i+1],lst[pivotIndex] = lst[pivotIndex],lst[i+1]
        #print("in part",lst,i+1)
        return i+1
    def sort(lst,start,end):
        if start >= end:
            return
        p = partition(lst,start,end)
        sort(lst,start,p-1)
        sort(lst,p+1,end)
        #print("in sort",lst)
    sort(lst,0,n-1)
    return lst

References

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值