基本排序方法的python实现

本文详细介绍了Python中插入排序、选择排序、冒泡排序、希尔排序、快速排序、归并排序和堆排序等常见排序算法的实现原理和性能特点,通过示例展示了在不同随机度和逆序情况下的运行时间。
摘要由CSDN通过智能技术生成

本篇文章为大家带来基本排序方法的python实现

插入排序

分析:维护一个已排序好的数组,每次像这个数组中插入一个元素,使插入后的数组仍然有序,直到排序结束。是就地排序,不需要占用额外的空间。时间复杂度O(n^{2}),随机度越高,插入排序的表现越好,在接近逆序的情况下表现较差。

def insertion(seq):
    length = len(seq)
    i = 1
    while i < length:
        data, k = seq[i], i
        while seq[k - 1] > data and k > 0:
            seq[k] = seq[k - 1]
            k -= 1
        seq[k] = data
        i += 1
选择排序

分析:在n个元素的数组中,在前n项选择最大值,与第n项交换,然后在前n-1项选择最大值,与第n-1项交换,以此类推,知道排序结束。时间复杂度O(n^{2}),在随机度较高是表现不如插入排序,在接近逆序时则明显好于插入排序。

def selection(seq):
    length = len(seq)
    i = 0
    while i < length:
        maxIndex, j = 0, 0
        while j < length - i:
            if seq[maxIndex] < seq[j]:
                maxIndex = j
            j += 1
        seq[length - 1 - i], seq[maxIndex] = seq[maxIndex], seq[length - i - 1]
        i += 1
冒泡排序

分析:从第一项开始,如果某一项比它的下一项大,则两者交换,然后进行下一项的比较,直至排序结束。时间复杂度O(n^{2})。每次遍历可以设置一个exchange变量,如果某次遍历没有交换,则排序完成。所以,数据如果已经接近有序,则冒泡排序可能在较短的时间内排序完成,但在大多数情况下,冒泡排序的效率不如其他排序算法。

def bubble(seq):
    length = len(seq)
    i = 0
    while i < length:
        j = length - 1
        exchange = False
        while j > i:
            if seq[j] < seq[j - 1]:
                seq[j], seq[j - 1] = seq[j - 1], seq[j]
                exchange = True
            j -= 1
        if not exchange:
            break
        i += 1
希尔排序

希尔排序可看作插入排序的改良,在插入排序时,插入操作每次与相邻的项进行比较,希尔排序则设置一个间隔数组,间隔数组依次减小直至1。在希尔排序中, 插入操作每次待插入的项与间隔x的数依次比较(x属于间隔数组,是当前的间隔)。希尔排序的性能要优于插入排序和选择排序,时间复杂度大致为O(n^{\tfrac{3}{2}})

def shell(seq):
    length = len(seq)
    gap = length // 2
    while gap > 0:
        i = gap
        while i < length:
            data, k = seq[i], i
            while seq[k - gap] > data and k > 0:
                seq[k] = seq[k - gap]
                k -= gap
            seq[k] = data
            i += gap
        gap //= 2

本篇选择的间隔数组是[length//2,length//4,……,1]。

快速排序

快速排序体现了分治的思想,先在数组中选择一个介质,然后将比介质小的元素放在介质左,将比介质大的元素放在介质右,然后对介质左和介质右的数组递归调用快速排序,直至排序完成。快速排序的效率取决于介质的选取,如果介质总是能选中中位数,则效率很高,反之如果介质总是选择了最大值或者最小值,则效率很低。快速排序的效率要好于希尔排序。平均时间复杂度为O(nlogn)。本篇总是选择数组的第一个数作为介质,因此这个快速排序在随机度较高时表现很好,在接近逆序时表现则很差(甚至会爆栈)。

def quick(seq):
    def partition(seq, low, high):
        pivot = seq[low]  # 选择第一个元素作为基准值
        while low < high:
            while low < high and seq[high] >= pivot:
                high -= 1
            seq[low], seq[high] = seq[high], seq[low]
            while low < high and seq[low] <= pivot:
                low += 1
            seq[low], seq[high] = seq[high], seq[low]
        return low

    def _quick_sort(seq, low, high):
        if low < high:
            pivot_index = partition(seq, low, high)
            _quick_sort(seq, low, pivot_index - 1)
            _quick_sort(seq, pivot_index + 1, high)

    _quick_sort(seq, 0, len(seq) - 1)

为了防止爆栈情况的出现,在代码最前面设置一下最大递归调用次数

import sys

sys.setrecursionlimit(3000)
归并排序

归并排序的效率与快速排序相当,快速排序可以看作是用元素大小来划分数组,则归并排序可以看作是用索引值来划分数组,因此,归并排序总是从中间将数组分为两部分,然后将两部分排序后合并。对于每一部分,递归调用归并排序。归并排序的效率比较稳定,在随机度较高或者接近逆序的 时候都有比较好的表现,时间复杂度是O(nlogn)

def merge(seq):
    def _merge(m, n, p, q):
        i, j, res = 0, 0, []
        while i < p and j < q:
            if m[i] < n[j]:
                res.append(m[i])
                i += 1
            else:
                res.append(n[j])
                j += 1
        res.extend(m[i:])
        res.extend(n[j:])
        return res
堆排序

堆排序使用了堆的数据结构,因为python的标准库heapq中有堆排序相关的算法,所以实现堆排序的代码可以很简洁。堆排序维护一个二叉堆(最小堆和最大堆都可以),然后每次弹出最小的元素。堆排序很稳定,在随机度高和接近逆序时表现都很好,时间复杂度是O(nlogn)

事先引入该模块

from heapq import *
def Pyheap(seq):
    heapify(seq)
    return [heappop(seq) for i in range(len(seq))]

二叉堆就是一颗完全二叉树,且保证根节点的元素总是小于子节点的元素(最小堆),根据数组建堆是线性时间复杂度,而弹出元素的时间复杂度则是O(logn)。因为是完全二叉树,所以可以使用一个数组存储

class BinHeap:
    

    def __init__(self):
        self.data = [0]
        self.length = 0

    def percUp(self, index):
        while index // 2 > 0:
            if self.data[index] < self.data[index // 2]:
                self.data[index], self.data[index // 2] = self.data[index // 2], self.data[index]
            index //= 2

    def getMin(self, index):
        if index * 2 > self.length:
            return None
        elif index * 2 + 1 > self.length:
            return index * 2
        else:
            if self.data[index * 2] <= self.data[index * 2 + 1]:
                return index * 2
            else:
                return index * 2 + 1

    def percDown(self, index):
        while True:
            minimum = self.getMin(index)
            if not minimum:
                break
            if self.data[index] > self.data[minimum]:
                self.data[index], self.data[minimum] = self.data[minimun], self.data[index]
            index = minimum

    def insert(self, data):
        self.data.append(data)
        self.length += 1
        self.percUp(self.length)

    def pop(self):
        if self.length == 0:
            raise IndexError('The heap is empty')
        data = self.data[1]
        self.delete(data)
        return data

    def delete(self, data):
        index = self.data.index(data)
        self.data[index] = self.data[self.length]
        self.data.pop()
        self.length -= 1
        self.percDown(index)

    def heapify(self, seq):
        self.data = [0] + seq[:]
        self.length = len(seq)
        i = self.length // 2
        while i > 0:
            self.percDown(i)
            i -= 1

    def output(self):
        print(self.data[1:])

    def __bool__(self):
        return bool(self.length)
def heap(seq):
    heap, res = BinHeap(), []
    heap.heapify(seq)
    while heap:
        res.append(heap.pop())
    return res

以上是常见的排序算法,最后我们来测试一下它们的性能。

import random

num = 20000
l = [random.random() for i in range(num)]
random.shuffle(l)

funcs = [list.sort, Sort.Pyheap, Sort.quick, Sort.merge, Sort.heap, Sort.shell, Sort.insertion, Sort.selection,
         Sort.bubble]


for func in funcs:
    a = time()
    func(l.copy())
    b = time()
    print(func.__name__,":",b-a)

在高随机度的情况下,结果如下

Pyheap : 0.01575779914855957
quick : 0.024766206741333008
merge : 0.02457451820373535
heap : 0.06661224365234375
shell : 6.078854084014893
insertion : 14.36155891418457
selection : 33.40423130989075
bubble : 48.74769401550293

在逆序的情况下

num = 20000
l = [random.random() for i in range(num)]
l.sort(reverse=True)

funcs = [list.sort, Sort.Pyheap, Sort.quick, Sort.merge, Sort.heap, Sort.shell, Sort.insertion, Sort.selection,
         Sort.bubble]


for func in funcs:
    a = time()
    func(l.copy())
    b = time()
    print(func.__name__,":",b-a)
Pyheap : 0.003007173538208008
quick : 5.107363939285278
merge : 0.014906883239746094
heap : 0.05798840522766113
shell : 9.991580724716187
insertion : 17.121063709259033
selection : 12.812097787857056
bubble : 25.45111322402954

回顾往期:

普利姆算法、克鲁斯卡尔算法、迪杰斯特拉算法、弗洛伊德算法C语言实现

栈,队列,二叉树的实现和基本方法

  • 18
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值