python 手写排序算法

目录

复杂度为O(n²)的排序

1.冒泡排序:

2.选择排序:

3.插入排序:

复杂度为O(nlogn)的排序

4.希尔排序:

5.归并排序:

6.快排:

7.堆排: 

复杂度为O(n+k)的排序

8.计数排序: 

9.桶排序:

复杂度为O(n*k)的排序

10.基数排序:

 

各种排序的算法复杂度如下:

python 手写排序算法_第1张图片

各个算法具体的排序逻辑可视化图可见:https://visualgo.net/zh

复杂度为O(n²)的排序

1.冒泡排序:

时间复杂度:O(n²),冒泡排序相对比较稳定

def bs(L):
    for i in range(len(L)):
        for j in range(i,len(L)):
            if L[i] > L[j]:
                L[i],L[j] = L[j],L[i]
    return L

2.选择排序:

时间复杂度:O(n²),选择排序相对来说不太稳定,最好和最坏情况复杂度都是O(n²)

在进行选择排序的时候,数据规模越小越好。唯一的好处可能就是不占用额外的内存空间了吧。

1. 算法步骤

  1. 默认序列起始位置的值为最小值min,并对起始位置之后的序列进行遍历

  2. 如果存在某个值小于min,则将此值定义为min,直到遍历到序列末尾

  3. 将起始位置的值和当前min的值进行对换,确保最小值始终在起始位置,起始位置+1

  4. 重复1-3步,直到结束。

def selectionSort(arr):
    for i in range(len(arr) - 1):
        # 记录最小数的索引
        minIndex = i
        for j in range(i + 1, len(arr)):
            if arr[j] < arr[minIndex]:
                minIndex = j
        # i 不是最小数时,将 i 和最小数进行交换
        if i != minIndex:
            arr[i], arr[minIndex] = arr[minIndex], arr[i]
    return arr

3.插入排序:

时间复杂度:O(n²)

1. 算法步骤

  1. 将第一待排序序列第一个元素看做一个有序序列,把第二个元素到最后一个元素当成是未排序序列。

  2. 从头到尾依次扫描未排序序列,将扫描到的每个元素插入有序序列的适当位置。(如果待插入的元素与有序序列中的某个元素相等,则将待插入元素插入到相等元素的后面。)

def insertionSort(arr):
    for i in range(len(arr)):
        preIndex = i-1
        current = arr[i]
        while preIndex >= 0 and arr[preIndex] > current:
            arr[preIndex+1] = arr[preIndex]
            preIndex-=1
        arr[preIndex+1] = current
    return arr

复杂度为O(nlogn)的排序

4.希尔排序:

关于希尔排序的解释可以看这篇文章:https://blog.csdn.net/qq_39207948/article/details/80006224

希尔排序是一种优化的插入排序,对于数据量较大且比较无序的数组效果较好。排序不需要外部空间,不稳定。

它把较大的数据集合分割成若干个小组(逻辑上分组),然后对每一个小组分别进行插入排序,此时,插入排序所作用的数据量比较小(每一个小组),插入的效率比较高

def shell_sort(arr):
    n = len(arr)
    gap = n//2  #这里的代码显示了分成两组的情况,分成多组情况修改数字2
    while gap > 0:
        for i in range(gap, n):
            while i >= gap and arr[i] < alist[i - gap]:
                arr[i], arr[i - gap] = arr[i - gap], arr[i]
                i -= gap
                #print(arr)
        gap //= 2
    return arr

5.归并排序:

(照搬了这篇文章https://blog.csdn.net/perfer258/article/details/81985349)

归并思想其实是依据了一种二分的思维。在进行排序后合并时,有点像合并两个有序数组。

时间复杂度:O(n log n),空间复杂度:O(n),排序需要外部空间,较为稳定

算法逻辑:

1.确定middle划分左右子集(递归思想);

2.对比左右子集的值,进行左右子集的排序合并,并对arr的相应序列进行改变;

3.返回排序的数组

def merge_sort(arr):
    sort(arr, 0, len(arr)-1)
 
def sort(arr, low, high):
    if low < high:
        mid = (low + high) // 2
        sort(arr, low, mid)
        sort(arr, mid+1, high)
        merge(arr, low, mid, high)
 
def merge(arr, low, mid, high):
    container = []    # 用于存储有序数字
    i, j = low, mid+1
    while i <= mid and j <= high:
        if arr[i] <= arr[j]:
            container.append(arr[i])
            i += 1
        else:
            container.append(arr[j])
            j += 1
    if i <= mid:
        container.extend(arr[i:mid+1])
    elif j <= high:
        container.extend(arr[j:high+1])
    arr[low:high+1] = container

 arr = merge_sort(arr)

6.快排:

时间复杂度:O (nlogn)

快排的算法中其实包含了一种二分的思维在里面,因此可以用递归的方式简化

def qs(L):
    # 终止条件必须要有
    if L == []: return []
    axis = L[0]
    less = qs([i for i in L[1:] if i < axis])
    more = qs([j for j in L[1:] if j >= axis])
    #axis外加[]
    return less + [axis] + more

7.堆排: 

时间复杂度:O (nlogn),空间复杂度:O(1),不稳定。

#根结点和最右最下侧节点进行替换
def adjust_heap(l, parent):
    length = len(l)
    parent_value = l[parent]
    child = parent * 2 + 1
    while child < length:
        if child + 1 < length and l[child + 1] > l[child]:
            child += 1
        if l[child] <= parent_value:
            break
        l[parent] = l[child]
        parent = child
        child = 2 * parent + 1
    l[parent] = parent_value

def heap_sort(l, result=None):
    if len(l) == 1:
        result.append(l[0])
        return l
    if result is None:
        result = []
    parents = [i for i in range(len(l))]
    parents.reverse()
    for parent in parents:
        adjust_heap(l, parent)
    l[0],l[len(l) - 1] = l[len(l) - 1],l[0]
    result.append(l.pop())
    heap_sort(l, result)
    return result

l = [99,7, 4, 5, 3, 6, 9, 7, 8, 0]
s = heap_sort(l)

复杂度为O(n+k)的排序

8.计数排序: 

时间复杂度:O (n+k),空间复杂度:O(k),稳定算法。

计数排序的逻辑其实非常简单,就是构建一个maxValue+1长度的列表,初始化值均为0。此列表的索引代表值,列表的值代表每个值出现的次数。统计完成后,循环更新原列表的值即可。

def countingSort(arr, maxValue):
    bucketLen = maxValue+1
    bucket = [0]*bucketLen
    sortedIndex =0
    arrLen = len(arr)
    for i in range(arrLen):
        # if not bucket[arr[i]]:
        #     bucket[arr[i]]=0
        bucket[arr[i]]+=1
    for j in range(bucketLen):
        while bucket[j]>0:
            arr[sortedIndex] = j
            sortedIndex+=1
            bucket[j]-=1
    return arr

9.桶排序:

桶排序是计数排序的升级版(https://www.jianshu.com/p/204ed43aec0c)。不同于计数排序的是,桶排序是基于映射关系生成序列,而不是一个长度为maxValue的序列

它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。为了使桶排序更加高效,我们需要做到这两点:

  1. 在额外空间充足的情况下,尽量增大桶的数量

  2. 使用的映射函数能够将输入的 N 个数据均匀的分配到 K 个桶中

同时,对于桶中元素的排序,选择何种比较排序算法对于性能的影响至关重要。

1. 什么时候最快

当输入的数据可以均匀的分配到每一个桶中。

2. 什么时候最慢

当输入的数据被分配到了同一个桶中。

算法示例:

def bucketSort(arr):
    maximum, minimum = max(arr), min(arr)
    bucketArr = [[] for i in range(maximum // 10 - minimum // 10 + 1)]  # set the map rule and apply for space
    for i in arr:  # map every element in array to the corresponding bucket
        index = i // 10 - minimum // 10
        bucketArr[index].append(i)
    arr.clear()
    for i in bucketArr:
        heapSort(i)   # sort the elements in every bucket
        arr.extend(i)  # move the sorted elements in bucket to array

 

复杂度为O(n*k)的排序

10.基数排序:

基数排序是按照数字的位数进行排序的,从个位到十位,百位依次进行排序。

为了更直观的理解,可以参见其可视化演示图:https://visualgo.net/zh/sorting

def RadixSort(a):
    i = 0                                             #初始为个位排序
    n = 1                                           #最小的位数置为1(包含0)
    max_num = max(a)                       #得到带排序数组中最大数
    while max_num > 10**n:              #得到最大数是几位数
        n += 1
    while i < n:
        bucket = {}                             #用字典构建桶
        for x in range(10):
            bucket.setdefault(x, [])    #将每个桶置空
        for x in a:                               #对每一位进行排序
            radix =int((x / (10**i)) % 10)   #得到每位的基数
            bucket[radix].append(x) #将对应的数组元素加入到相应位基数的桶中
        j = 0
        for k in range(10):
            if len(bucket[k]) != 0:       #若桶不为空
                for y in bucket[k]:         #将该桶中每个元素
                    a[j] = y                       #放回到数组中
                    j += 1
        i += 1

 

你可能感兴趣的:(leetcode,Python)