刷题总结2:排序算法

Posted amberwang2018

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了刷题总结2:排序算法相关的知识,希望对你有一定的参考价值。

一、选择排序

选择排序是每次从未排序的部分选出一个最小的数字,放在已排序部分的最后,即每次选出最小的、第二小的,以此类推。当然,也可以每次从未排序的地方选出最大的数字,放在已排序部分的最前面,即每次选出最大的、第二大的,以此类推。

时间复杂度O(n^2)。

优点是交换次数最少。

代码:

def select_sort(nums):
    n = len(nums)
    for i in range(n):
        min = i
        for j in range(i+1,n):
            if nums[j]<nums[min]:
                min = j
        nums[i],nums[min] = nums[min],nums[i]
    return nums
nums = [3,4,8,2,1]
print(select_sort(nums))

二、插入排序(熟悉)

插入排序是每次将一个数字(主元)【i序列】插入到一个有序的数组【j序列】里,成为一个长度更长的有序数组,有限次操作后,数组整体有序。

时间复杂度O(n^2)。

优点是在几乎有序的数组和短数组上表现很好,为此,在小区间执行排序任务时可以使用插入排序。

代码:

def insert_sort(nums):
    n = len(nums)
    for i in range(1,n):
        pivot = nums[i]
        j = i-1
        while j>=0 and nums[j] > pivot:
            nums[j+1] = nums[j]
            j -= 1
        nums[j+1] = pivot
    return nums
nums = [3,4,8,2,1]
print(insert_sort(nums))

三、归并排序(重点)

 归并排序是借助额外空间,将两个有序数组合并,得到更长的有序数组。额外空间是用来存放合并后数组,然后将该数组再放回大数组里。

以下代码为《算法导论》里的代码,它开辟了两个额外空间存放数组。

def merge(A,l,m,r):
    n1 = m-l+1
    n2 = r-m
    #创建临时数组
    L = [0]*n1
    R = [0]*n2
    #拷贝到临时数组
    for i in range(n1):
        L[i] = A[l+i]
    for j in range(n2):
        R[j] = A[m+1+j]
    #归并临时数组到A[l,r]
    i,j,k=0,0,l
    while i<n1 and j<n2:
        if L[i]<=R[j]:
            A[k] = L[i]
            i += 1
        else:
            A[k] = R[j]
            j += 1
        k += 1
    #拷贝L、R剩余元素
    while i<n1:
        A[k]=L[i]
        i += 1
        k += 1
    while j<n2:
        A[k]=R[j]
        j += 1
        k += 1

def mergesort(A,l,r):
    if r>l:
        m = l+ (r-l)//2
        mergesort(A,l,m)
        mergesort(A,m+1,r)
        merge(A,l,m,r)

#测试实例
A=[5,2,4,7,1,3,2,6]
mergesort(A,0,len(A)-1)
print(A)

也可以每次不用开辟新临时数组,而只用一个。每次开辟新临时数组空间复杂度为nlogn。

def merge(A,l,m,r,temp):
    i,j = l,m+1
    while i<=m and j<=r:
        if A[i]<=A[j]:
            temp.append(A[i])
            i += 1
        else:
            temp.append(A[j])
            j+=1
    while i<=m:
        temp.append(A[i])
        i += 1
    while j<=r:
        temp.append(A[j])
        j += 1
    for i in range(len(temp)):
        A[l+i] = temp[i]
    temp.clear()
def mergesort(A,l,r,temp):
    if r>l:
        m = l+(r-l)//2
        mergesort(A,l,m,temp)
        mergesort(A,m+1,r,temp)
        merge(A,l,m,r,temp)
A=[5,2,4,7,1,3,2,6]
mergesort(A,0,len(A)-1,[])
print(A)

四、快速排序(重点)

 快速排序是每一次都排好一个元素,这个元素就待在了它最终该待的地方,然后递归地处理它左边和右边的部分,直至有序。思想也是分治的思想,但与归并排序不同,采用的是分割的方法,所以没有“合”的过程。

代码为随机化分割:

import random
def partition(A,p,r):
    pivot = A[r] #主元
    i = p-1 #初始化起始序号
    for j in range(p,r):
        if A[j] <= pivot:
            i += 1
            A[i],A[j] = A[j],A[i]
    A[r],A[i+1] = A[i+1],A[r]
    return i+1
def RandomizePartition(A,p,r):
    x = random.randint(p,r)
    A[x],A[r] = A[r],A[x]
    return partition(A,p,r)
def quicksort(A,p,r):
    if p<r:
        q = RandomizePartition(A,p,r)
        quicksort(A,p,q)
        quicksort(A,q+1,r)
    return A
A = [2,8,7,1,3,5,6,4]
quicksort(A,0,len(A)-1)
print(A)

每次随机化选择主元,这样可以避免出现有序或逆序的待排序数组时时间复杂度为O(n^2)的情况。

五、堆排序(重要)

 堆排序是利用堆这种数据结构而设计的一种排序算法,它的时间复杂度为O(nlogn)。它的主要思想是:将待排序序列构造成一个最大堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样就会得到n个元素的次小值。以此类推,就能得到一个有序序列了。

代码如下:

#维护一个最大堆
def heapify(arr, n, i):
    largest = i
    l = 2 * i + 1  # left = 2*i + 1
    r = 2 * i + 2  # right = 2*i + 2

    if l < n and arr[i] < arr[l]:
        largest = l

    if r < n and arr[largest] < arr[r]:
        largest = r

    if largest != i:
        arr[i], arr[largest] = arr[largest], arr[i]  # 交换

        heapify(arr, n, largest)

#堆排序
def heapSort(arr):
    n = len(arr)

    # Build a maxheap.
    for i in range(n, -1, -1):
        heapify(arr, n, i)

    # 一个个交换元素
    for i in range(n - 1, 0, -1):
        arr[i], arr[0] = arr[0], arr[i]  # 交换
        heapify(arr, i, 0)


arr = [12, 11, 13, 5, 6, 7]
heapSort(arr)
print(arr)

堆排序是一种选择排序,整体主要由构建初始堆+交换堆顶元素和末尾元素并重建堆两部分组成。其中构建初始堆经推导复杂度为O(n),在交换并重建堆的过程中,需交换n-1次,而重建堆的过程中,根据完全二叉树的性质,[log2(n-1),log2(n-2)...1]逐步递减,近似为nlogn。所以堆排序时间复杂度一般认为就是O(nlogn)级。

 

以上是关于刷题总结2:排序算法的主要内容,如果未能解决你的问题,请参考以下文章

冒泡排序这 2 个小技巧,你了解吗?

算法刷题范围建议 和 代码规范

算法刷题范围建议 和 代码规范

算法通关手册 刷题笔记2 数组排序之冒泡排序选择排序

[AI助力] 算法通关手册 刷题笔记2 数组排序之冒泡排序选择排序

算法刷题总结