排序算法(一)

排序算法(一)

此文档主要包含选择排序、插入排序、冒泡排序、归并排序、快排还有堆排序的内容。

选择排序

思想

选择排序算法的思路为在当前的n个数组中选择出来最大(最小)的那一个,然后将其与其应该在的位置的元素进行交换。

然后再将第二大(小)的元素放到其应该在的位置。

code
def selectionSort(array):
    n = len(array)
    for i in range(n-1,0,-1):
        print(array)
        maxIdx = i
        for j in range(0,i):
            if array[j] > array[maxIdx]:
                maxIdx = j
        swapElement(array,maxIdx,i)
    print(array)

def swapElement(array,x,y):
    temp = array[x]
    array[x] = array[y]
    array[y] = temp
# 原始arry:
[29, 10, 14, 37, 13]
# 第一次选择之后的array,37 换到了最后一个位置(最大的位置)
[29, 10, 14, 13, 37]
# 第二次选择之后的array,29 换到了倒数第二个位置。
[13, 10, 14, 29, 37]
# 第三次选择之后的array,14 的位置未变,因为14就位于他应该在的位置上。
[13, 10, 14, 29, 37]
# 最后一次选择之后的array,13 换到了第二个位置上,此时整个array就排完序了。
[10, 13, 14, 29, 37]
总结

1.时间复杂度
选择排序算法的每一轮要遍历所有元素,共遍历n-1轮,所以时间复杂度是O(N2)

2.空间复杂度
选择排序算法排序过程中需要一个临时变量存储最小元素(最大元素),所需要的额外空间为1,因此空间复杂度为O(1)

3.稳定性
选择排序算法是一种不稳定排序算法,当出现相同元素的时候有可能会改变相同元素的顺序。

插入排序

思想

思路:

  • 插入排序的思想和选择排序类似,将当前的元素往前比较,当遇到比他小的元素的时候,就停止交换。
  • 从第一个元素依次往前比较插入,最后排下来的就是从小到大排好序的数组。
code
def insertionSort(array):
    n = len(array)

    for i in range(1,n):
        print(array)
        next_ = array[i]
        j = i-1

        while j>=0 and array[j]>next_:
            array[j+1] = array[j]
            j=j-1
        array[j+1]=next_
    print(array)
# 原始array
[29, 10, 14, 37, 13]
# 第一次插入,是对10这个元素处理的,往前比较,到头或是到比他小的元素之后,这里10换到了第一个位置。
[10, 29, 14, 37, 13]
#第二次插入,14这个元素换到了10之后,29之前。
[10, 14, 29, 37, 13]
#第三次插入,37这个元素之前没有比他小的,所以37的位置不变。
[10, 14, 29, 37, 13]
#最后一次插入,将13这个元素插入到了10之后,14之前。
[10, 13, 14, 29, 37]
总结

1.时间复杂度
插入排序算法要进行n-1轮,每一轮对比的元素最坏的情况依次是1, 2, 3 … n-1,所以时间复杂度是O(N2)

2.空间复杂度
插入排序算法排序过程中需要一个临时变量存储插入元素,所需要的额外空间为1,因此空间复杂度为O(1)

3.稳定性
插入排序算法在排序过程中,无序数列插入到有序区的过程中,不会改变相同元素的前后顺序,是一种稳定排序算法

冒泡排序

思路:

假设array的长度为n。

  • 从第一个元素到最后一个元素,两两比较,较大的放到后面。这样比较到最后,最大的值就会被换到最后的位置。

  • 这次从第一个元素到倒数第二个元素,再次两两比较,这样比较到最后,第二大的值就会被放到其应该在的位置。

  • 依次直到整个array都被冒泡排完序。这个array就是最大的array。

code
def bubbleSort(array):
    n = len(array)

    for i in range(n-1,0,-1):
        for j in range(1,i+1):
            if array[j-1]>array[j]:
                swapElement(array,j,j-1)
# 原始array
[29, 10, 14, 37, 13]
# 第0个元素和第1个元素比较, 29 > 10所以位置交换。
[10, 29, 14, 37, 13]
# 第1个元素和第2个元素比较,29 > 14所以位置交换。
[10, 14, 29, 37, 13]
# 第2个元素和第3个元素比较,29 < 37所以位置不变。
[10, 14, 29, 37, 13]
# 第3个元素和第4个元素比较,37 > 13所以位置不变。此时第一轮排序已经排完了。
[10, 14, 29, 13, 37]
# 现在开始第二轮排序,第0个元素和第1个元素比较,10 < 14所以位置不变。
[10, 14, 29, 13, 37]
# 第1个元素和第2个元素比较,14 < 29所以位置不变。
[10, 14, 29, 13, 37]
# 第2个元素和第3个元素比较,29 > 13所以位置互换。此时第二轮排序已经排完
[10, 14, 13, 29, 37]
# 第三轮排序开始,第0个元素和第1个元素比较,10 < 14所以位置不变。
[10, 14, 13, 29, 37]
# 第1个元素和第2个元素比较,13 < 14所以位置交换。第三轮排序完成。
[10, 13, 14, 29, 37]
# 第四轮排序开始,10 < 13所以位置未变。第四轮排序完成,整体的排序也完成了。
[10, 13, 14, 29, 37]
Better Version

Better Version的核心点就是先判断这个array是不是已经排好序的,如果已经排好序了,则不需要进行后续的操作了。

def bubbleSort(array):
    n = len(array)

    for i in range(n-1,0,-1):
        isSorted = True


        for j in range(1,i+1):
            if array[j-1]>array[j]:
                swapElement(array,j,j-1)
                isSorted = False
        
        if isSorted:
            return
总结

1.时间复杂度
对于原版的冒泡排序算法,时间复杂度一直都是O(n2)。

Better Version这一版,最坏的情况下的时间复杂度是O(n2),最好的情况下时间复杂度为O(n)。

2.空间复杂度
冒泡排序算法排序过程中需要一个临时变量进行两两交换,所需要的额外空间为1,因此空间复杂度为O(1)

3.稳定性
冒泡排序算法在排序过程中,元素两两交换时,相同元素的前后顺序并没有改变,所以冒泡排序是一种稳定排序算法

归并排序

思想

归并排序的思想其实是将整个array拆分,拆分成一个个子序列后,子序列再进行归并排序,然后再将子序列合并成一个有序的序列。

举个例子,[29, 10, 14, 37, 13]。对这个array进行归并排序。
那么顺序为:
先对[29,10]排序,排完的结果为[10,29]
然后对[10,29,14]排序,排完的结果为[10,14,29]
然后再对[37,13]排序,排完的结果为[13,37]
然后再将[13,37]和[10,14,29]进行合并,得到最终的排序结果[10,13,14,29,37]。

code
def mergeSort(array,low,high):
    if low<high:
        mid = (low+high)//2

        mergeSort(array,low,mid)
        mergeSort(array,mid+1,high)

        merge(array,low,mid,high)

def merge(array,low,mid,high):
    
    n = high-low+1
    result = []
    left = low
    right = mid+1

    while left<=mid and right <=high:
        if array[left] <=array[right]:
            result.append(array[left])
            left+=1
        else:
            result.append(array[right])
            right+=1
    while left <= mid:
        result.append(array[left])
        left+=1

    while right<=high:
        result.append(array[right])
        right+=1
    for k in range(0,n):
        array[low+k] = result[k]

def mergeSortHelper(array):
    mergeSort(array,0,len(array)-1)
# result为每一次归并排序后的结果
result [10, 29]
result [10, 14, 29]
result [13, 37]
result [10, 13, 14, 29, 37]
总结

1.时间复杂度
归并排序算法每次将序列折半分组,共需要logn轮,因此归并排序算法的时间复杂度是O(nlogn)

2.空间复杂度
归并排序算法排序过程中需要额外的一个序列去存储排序后的结果,所占空间是n,因此空间复杂度为O(n)

3.稳定性
归并排序算法在排序过程中,相同元素的前后顺序并没有改变,所以归并排序是一种稳定排序算法

快排

思路

快排是先选取一个pivot,将array内比pivot小的元素放到pivot的左边,比pivot大的元素就自然而然到了pivot的右边。
然后再对左边和右边的数组再进行快速排序。

举个简单的例子:array : [29, 10, 14, 37, 13]
第一次选中的pivot为首元素 29:

code
def swapElement(array,x,y):
    temp = array[x]
    array[x] = array[y]
    array[y] = temp

def quickSort(array,low,high):
    if low < high:
        pivotIdx = partition(array,low,high)

        quickSort(array,low,pivotIdx-1)
        quickSort(array,pivotIdx+1,high)
def partition(array,i,j):
    pivot = array[i]
    middle = i

    for k in range(i+1,j+1):
        if array[k] < pivot:
            middle = middle+1
            swapElement(array,k,middle)
    swapElement(array,i,middle)
    return middle
总结

1.时间复杂度
快速排序算法在分治法的思想下,原数列在每一轮被拆分成两部分,每一部分在下一轮又分别被拆分成两部分,直到不可再分为止,平均情况下需要logn轮,因此快速排序算法的平均时间复杂度是O(nlogn)

在极端情况下,快速排序算法每一轮只确定基准元素的位置,时间复杂度为O(N^2)

2.空间复杂度
快速排序算法排序过程中只是使用数组原本的空间进行排序,因此空间复杂度为O(1)

3.稳定性
快速排序算法在排序过程中,可能使相同元素的前后顺序发生改变,所以快速排序是一种不稳定排序算法

Radix Sort(基数排序)

def radixSort(array):
    numDigit = int(math.log10(max(array))) +1

    for power in [10**i for i in range(numDigit)]:
        digitBin = [[] for d in range(10)]
        distribute(array,digitBin, power)
        collect(digitBin,array)
def distribute(array,digitBin,power):
    for item in array:
        digit = (item//power) %10
        digitBin[digit].append(item)
def collect(digitBin,array):
    startIdx = 0
    for eachBin in digitBin:
        array[startIdx:] = eachBin
        startIdx += len(eachBin)

堆排序

思路

堆排序就是利用大根堆和小根堆来实现排序的效果。

以小根堆为例,位于堆顶的是是堆内最小的值。依次pop出堆顶的值,就可以构成一个排好序的array。

code
# 这里直接运用python拥有的一个堆库 heapq。
import heapq
def heapqSort(array):
    res = []
    heapq.heapify(array)
    while array:
        res.append(heapq.heappop(array))

    return res
总结

1.时间复杂度
下沉调整的时间复杂度等同于堆的高度O(logn),构建二叉堆执行下沉调整次数是n/2,循环删除进行下沉调整次数是n-1,时间复杂度约为O(nlogn)

2.空间复杂度
堆排序算法排序过程中需要一个临时变量进行两两交换,所需要的额外空间为1,因此空间复杂度为O(1)

3.稳定性

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

萌小奇0639

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值