数据结构-------八大必会排序算法(算法总结、动图图解及python代码实现)

1. 冒泡排序

1.1 算法简介

​ 1. 比较相邻的元素,前一个比后一个大(或者前一个比后一个小)调换位置

​ 2. 每一对相邻的元素进行重复的工作,从开始对一直到结尾对,这步完成后,结尾为做大或最小的数

​ 3. 针对除了最后一个元素重复进行上面的步骤。

​ 4. 重复1-3步骤直到完成排序

1.2 算法性质

​ 1. 时间复杂度

​ 最优:O(n) 最差:O(n^2) 平均时间复杂度:O(n^2)

​ 2. 空间复杂度

​ 平均空间复杂度:O(1)

​ 3. 稳定性

​ 稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面

​ 4. 排序方式

​ 内排序:所有排序都在缓存中完成

1.3 动图演示

在这里插入图片描述

1.4 代码实现
def bubble_sort(arr):
    for i in range(1,len(arr)):
        for j in range(len(arr) - i):
            if arr[j] > arr[j + 1]:
                arr[j], arr[j + 1] = arr[j + 1], arr[j]
    return arr


arr = [9,5,6,8,2,7,3,4,1]
arr = bubble_sort(arr)
print(arr)
1.5 算法优化

​ 假如从开始的第一对到结尾的最后一对,相邻的元素之间都没有发生交换的操作,这意味着右边的元素总是大于等于左边的元素,此时的数组已经是有序的了,我们无需再对剩余的元素重复比较下去了

def bubble_sort(arr):
    for i in range(1,len(arr)):
        flag = True
        print(arr)
        for j in range(len(arr) - i):
            if arr[j] > arr[j + 1]:
                flag = False
                arr[j], arr[j + 1] = arr[j + 1], arr[j]
        if flag:
            break
    return arr


arr = [3,2,1,4,5,6,7,8,9,]
arr = bubble_sort(arr)
print(arr)

2. 选择排序

2.1 算法简介

​ 1. 在未排序的列表中,假设第一个元素的位置为最大(小)元素的位置

​ 2. 然后在未排序的列表中依次找出最大(小)元素位置,

​ 3. 跟假设的位置进行比较,如果原有位置与找到的最大(小)位置不是同一个位置

​ 4. 则将这两个位置的元素进行交换

​ 5. 对这次排序的第一个元素外的其他元素重复上面动作,直至完成排序

2.2 算法性质

​ 1. 时间复杂度

​ 最优:O(n^2) 最差:O(n^2) 平均时间复杂度:O(n^2)

​ 2. 空间复杂度

​ 平均空间复杂度:O(1)

​ 3. 稳定性

​ 不稳定:如果a原本在b的前面,而a=b,排序之后a可能会出现在b的后面

​ 4. 排序方式

​ 内排序:所有排序都在缓存中完成

2.3 动图演示

在这里插入图片描述

2.4 代码实现
def selection_sort(arr):
    for i in range(len(arr)):
        # 假设最小值的位置
        min_pos = i
        
        for j in range(i+1,len(arr)):
            # 获取真实的最小元素的位置
            if arr[j] < arr[min_pos]:
                min_pos = j
                
        # 判断真实的最小位置是否与假设位置相同,如果不同交换
        if min_pos != i:
            arr[i], arr[min_pos] = arr[min_pos], arr[i]  
            
    return arr


arr = [9,5,6,8,2,7,3,4,1]
arr = selection_sort(arr)
print(arr)

3. 插入排序

3.1 算法简介

​ 1. 从第一个元素开始,该元素可以认为已经被排序

​ 2. 取出下一个元素,在已经排序的元素序列中从后向前扫描

​ 3. 如果该元素(已排序)大于新元素,将该元素移到下一位置

​ 4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置

​ 5. 将新元素插入到该位置后

​ 6. 重复步骤2~5,直到排序完成

3.2 算法性质

​ 1. 时间复杂度

​ 最优:O(n) 最差:O(n^2) 平均时间复杂度:O(n^2)

​ 2. 空间复杂度

​ 平均空间复杂度:O(1)

​ 3. 稳定性

​ 稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面

​ 4. 排序方式

​ 内排序:所有排序都在缓存中完成

3.3 动图演示

在这里插入图片描述

3.4 代码实现
def insertion_sort(arr):
    for i in range(1,len(arr)):
        temp = arr[i] # 将要插入的数据提取出来
        for j in range(i,0,-1):
            # 将temp与排序好的数据从后往前一次比较,如果排序好的大于temp,排序好的元素后移
            if temp < arr[j - 1]:
                arr[j] = arr[j - 1]
                # 将空出的下标赋值给index
                index = j - 1
            else:
                break
        # 将空出的位置赋值temp
        arr[index] = temp
    return arr


arr = [9,5,6,8,2,7,3,4,1]
arr = insertion_sort(arr)
print(arr)

4. 希尔排序

4.1 算法简介

​ 1. 设元数据为一组,长度为n

​ 2. 第一次将数据分为h = n / 2组,并且将每组进行插入排序

​ 3. 第二次将数据分为h = n / 4组,并且将每组进行插入排序

​ 4. 以此类推

​ 5. 直到h = 1时,用插入排序进行微调完成排序

4.2 算法性质

​ 1. 时间复杂度

​ 最优:O(n log^2 n) 最差:O(n log n) 平均时间复杂度:O(n log n)

​ 2. 空间复杂度

​ 平均空间复杂度:O(1)

​ 3. 稳定性

​ 不稳定:如果a原本在b的前面,而a=b,排序之后a可能会出现在b的后面

​ 4. 排序方式

​ 内排序:所有排序都在缓存中完成

4.3 动图演示

在这里插入图片描述

4.4 代码实现
def shell_sort(list):
    n = len(arr)
    gap = int(n/2)
  
    while gap >= 1: 
  
        for i in range(gap,n): 
  
            temp = arr[i] 
            j = i 
            while  j >= gap and arr[j-gap] >temp: 
                arr[j] = arr[j-gap] 
                j -= gap 
            arr[j] = temp 
        gap = int(gap/2)
    return arr
        
        
arr = [9,5,6,8,2,7,3,4,1]
arr = shell_sort(arr)
print(arr)

5.快速排序

5.1 算法简介

​ 1.首先设定一个分界值(一般是第一个值),通过该分界值将数组分成左右两部分

​ 2.将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于或等于分界值,而右边部分中各元素都大于或等于分界值

​ 3.然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理

​ 4.重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了

5.2 算法性质

​ 1. 时间复杂度

​ 最优:O(n^2) 最差:O(n log n) 平均时间复杂度:O(n log n)

​ 2. 空间复杂度

​ 平均空间复杂度:O(log n)

​ 3. 稳定性

​ 不稳定:如果a原本在b的前面,而a=b,排序之后a可能会出现在b的后面

​ 4. 排序方式

​ 内排序:所有排序都在缓存中完成

5.3 动图演示

在这里插入图片描述

5.4 代码实现
#arr:待排序的序列;start排序的开始index,end序列末尾的index
#对于长度为length的序列:start = 0;end = length-1
def quick_sort(arr,start,end):
    if start < end:
        i , j , temp = start , end , arr[start]
        while i < j:
            #从右开始向左寻找第一个小于temp的值
            while (i < j) and (arr[j] >= temp):
                j -= 1
            #将小于pivot的值移到左边
            if (i < j):
                arr[i] = arr[j]
                i += 1
            #从左开始向右寻找第一个大于pivot的值
            while (i < j) and (arr[i] < temp):
                i += 1 
            #将大于pivot的值移到右边
            if (i < j):
                arr[j] = arr[i]
                j -= 1
        #循环结束后,说明 i=j,此时左边的值全都小于temp,右边的值全都大于temp
        #temp的位置移动正确,那么此时只需对左右两侧的序列调用此函数进一步排序即可
        #递归调用函数:依次对左侧序列:从0 ~ i-1//右侧序列:从i+1 ~ end
        arr[i] = temp
        #左侧序列继续排序
        quick_sort(arr,start,i-1)
        #右侧序列继续排序
        quick_sort(arr,i+1,end)
    return arr
        
        
arr = [9,5,6,8,2,7,3,4,1]
arr = quick_sort(arr, 0, 8)
print(arr)

6. 归并排序

6.1算法简介

​ 1. 采用递归的方法,首先将待排序列分成A,B两组;然后重复对A、B序列分组

​ 2. 直到分组后组内只有一个元素,此时我们认为组内所有元素有序,则分组结束

​ 3. 首先依次从第一段与第二段中取出元素比较,将较小的元素赋值给temp[]

​ 4. 重复执行上一步,当某一段赋值结束,则将另一段剩下的元素赋值给temp[]

​ 5. 将各分组按3.4步进行合并,最终合并为一组

​ 6. 此时将temp[]中的元素复制给arr[],则得到的arr有序

6.2 算法性质

​ 1. 时间复杂度

​ 最优:O(n log n) 最差:O(n log n) 平均时间复杂度:O(n log n)

​ 2. 空间复杂度

​ 平均空间复杂度:O(n)

​ 3. 稳定性

​ 稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面

​ 4. 排序方式

​ 外排序:由于数据太大,因此把数据放在磁盘中,而排序通过磁盘和内存的数据传输才能进行

6.3 动图演示

在这里插入图片描述

6.4 代码实现
def merge_sort(arr):
    #声明一个长度为len(L)的空列表
    temp = [None] * len(arr)
    
    #合并
    def merge_temp(arr,first,mid,last,temp):
        #对i,j,k分别进行赋值
        i,j,k = first,mid+1,0
        #当左右两边都有数时进行比较,取较小的数
        while (i <= mid) and (j <= last):
            if arr[i] <= arr[j]:
                temp[k] = arr[i]
                i += 1
                k += 1
            else:
                temp[k] = arr[j]
                j += 1
                k += 1
        #如果左边序列还有数
        while (i <= mid):
            temp[k] = arr[i]
            i += 1
            k += 1
        #如果右边序列还有数
        while (j <= last):
            temp[k] = arr[j]
            i += 1
            k += 1
        #将temp当中该段有序元素赋值给arr待排序列使之部分有序
        for x in range(0,k):
            arr[first+x] = temp[x]
            
    # 这是分组的函数
    def group_array(arr,first,last,temp):
        if first < last:
            mid = (int)((first + last) / 2)
            #使左边序列有序
            group_array(arr,first,mid,temp)
            #使右边序列有序
            group_array(arr,mid+1,last,temp)
            #将两个有序序列合并
            merge_temp(arr,first,mid,last,temp)

    group_array(arr,0,len(arr)-1,temp)
    return arr
    
arr = [9,5,6,8,2,7,3,4,1]
arr = merge_sort(arr)
print(arr)

7. 堆排序

7.1 算法简介

​ 1. 将序列依次读取构建堆(堆必须父节点大于子节点)

​ 2. 将根节点取出赋值给序列的最后一个元素

​ 3. 更新序列长度及堆

​ 4.重复2.3动作直至取完堆中的值,排序完成

7.2 算法性质

​ 1. 时间复杂度

​ 最优:O(n log n) 最差:O(n log n) 平均时间复杂度:O(n log n)

​ 2. 空间复杂度

​ 平均空间复杂度:O(1)

​ 3. 稳定性

​ 不稳定:如果a原本在b的前面,而a=b,排序之后a可能会出现在b的后面

​ 4. 排序方式

​ 内排序:所有排序都在缓存中完成

7.3 动图演示

在这里插入图片描述

7.4 代码实现
def reactor_sort(arr):
    #获取左右叶子节点
    def get_left(i):
        return 2*i + 1
    def get_right(i):
        return 2*i + 2

    # 调整堆 arr:待调整序列 length: 序列长度 i:需要调整的结点
    def adjust_reactor(arr,length,i):
        #定义一个int值保存当前序列最大值的下标
        largest = i
        #执行循环操作:两个任务:1 寻找最大值的下标;2.最大值与父节点交换
        while True:
            #获得序列左右叶子节点的下标
            left,right = get_left(i),get_right(i)
            #当左叶子节点的下标小于序列长度 并且 左叶子节点的值大于父节点时,将左叶子节点的下标赋值给largest
            if (left < length) and (arr[left] > arr[i]):
                largest = left
            else:
                largest = i
            #当右叶子节点的下标小于序列长度 并且 右叶子节点的值大于父节点时,将右叶子节点的下标值赋值给largest
            if (right < length) and (arr[right] > arr[largest]):
                largest = right
            #如果largest不等于i 说明当前的父节点不是最大值,需要交换值
            if (largest != i):
                temp = arr[i]
                arr[i] = arr[largest]
                arr[largest] = temp
                i = largest
                continue
            else:
                break
    # 构建堆            
    def build_reactor(arr):
        length = len(arr)
        for x in range(int((length-1)/2),-1,-1):
            adjust_reactor(arr,length,x)

    #先建立大顶堆,保证最大值位于根节点;并且父节点的值大于叶子结点
    build_reactor(arr)
    #i:当前堆中序列的长度.初始化为序列的长度
    i = len(arr)
    #执行循环:1. 每次取出堆顶元素置于序列的最后(len-1,len-2,len-3...)
    #         2. 调整堆,使其继续满足大顶堆的性质,注意实时修改堆中序列的长度
    while (i > 0):
        temp = arr[i-1]
        arr[i-1] = arr[0]
        arr[0] = temp
        #堆中序列长度减1
        i -= 1 
        #调整大顶堆
        adjust_reactor(arr,i,0)
    
    return arr
        
        
arr = [9,5,6,8,2,7,3,4,1]
arr = reactor_sort(arr)
print(arr)

8. 基数排序

8.1 算法简介

​ 1. 将列表中的元素取出,首先确定其个位上的数字,根据该数字分配到与之序号相同的桶中

​ 2. 当序列中所有的元素都分配到对应的桶中,再按照顺序依次将桶中的元素收集形成新的一个待排序列

​ 3. 对新形成的序列重复执行分配和收集元素中的十位、百位…直到分配完该序列中的最高位,则排序结束

8.2 算法性质

​ 1. 时间复杂度

​ 最优:O(nk) 最差:O(nk) 平均时间复杂度:O(nk)

​ 2. 空间复杂度

​ 平均空间复杂度:O(n + k)

​ 3. 稳定性

​ 稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面

​ 4. 排序方式

​ 外排序:由于数据太大,因此把数据放在磁盘中,而排序通过磁盘和内存的数据传输才能进行

8.3 动图演示

在这里插入图片描述

8.4 代码实现
def radix_sort(arr):
    #排序的次数跟序列中最大数的位数相关
    def radix_sort_nums(arr):
        maxNum = arr[0]
        #寻找序列中的最大数
        for x in range(0,len(arr)):
            if maxNum < arr[x]:
                maxNum = arr[x]

        #确定排序的次数
        times = 0
        while maxNum > 0:
            maxNum = int(maxNum / 10)
            times += 1
        return times

    #找到num从低到高第pos位的数据
    def get_pos(num,pos):
        return (int(num/(10**(pos-1))))%10 
    
    #从低位到高位依次执行循环
    times = radix_sort_nums(arr)
    #暂时存放排序结果
    bucket = []
    for i in range(0,10):
        bucket.append([])
    for pos in range(1,times + 1):
        #将数据依次装入桶中
        for x in range(0,len(arr)):
            #求出元素第pos位的数字
            j = get_pos(arr[x],pos)
            #放入对应的桶中
            if j != 0:
                bucket[j].append(arr[x])
            else:
                bucket[0].append(arr[x])
        arr = []
        # 将已分配好的桶中数据再倒出来,此时已是对应当前位数有序的表
        for i in range(0,10):
            if bucket[i] != None:
                for j in range(0,len(bucket[i])):
                    arr.append(bucket[i][j])
        bucket[i] = []

    return arr


arr = [9,5,6,8,2,7,3,4,1]
arr = radix_sort(arr)
print(arr)

总结

​ 运行时进行了一下时间比较:

​ 1万数据时的输出:

​ 冒泡排序:6.687218999999999

​ 选择排序:3.645136000000001

​ 插入排序:11.615608

​ 希尔排序:13.012008

​ 快速排序:9.999999974752427e-07

​ 快速排序有误:实际上并未执行

​ RecursionError: maximum recursion depth exceeded in comparison

​ 归并排序:0.05638299999999674

​ 堆排序:0.09587900000000005

​ 基数排序:0.08150400000000246

​ 10万数据时的输出:

​ 冒泡排序:751.274449

​ 选择排序:466.66974500000015

​ 插入排序:1233.581131

​ 希尔排序:1409.8012320000003

​ 快速排序:1.0000003385357559e-06

​ 快速排序有误:实际上并未执行

​ RecursionError: maximum recursion depth exceeded in comparison

​ 归并排序:0.8262230000000272

​ 堆排序:1.2036720000000969

​ 基数排序:1.1162899999999354

从运行结果上来看,堆排序、归并排序、基数排序排序结果快。*

猜你喜欢

转载自blog.csdn.net/qq_42546127/article/details/103418419