十大排序算法:Python实现

首先我们需要一个序列, 通过排序生成一下

# 添加一个标志位, 
# 随机生成一个乱序数组
import random
list1 = []
for i in range(15):
    list1.append(random.randint(0,100))
print(list1)

一、冒泡算法

原理: 简单描述:从左到右两两比较大小

  • 时间复杂度:O(n^2)
  • 空间复杂度:O(1)
  • 稳定

编:就像可乐的气泡一样,从底部逐渐向上浮出水面

优化思想: 如果当前轮次,列表元素没有发生交换, 那么可以判断是有序的提前结束, 提高效率

import time

class BubbleSort():
    # -> 返回类型
    def __init__(self, nlist:list) -> list:
        self.mylist = nlist
    def sortV1(self, length):
        #waste time:  0.00010442733764648438
        if length < 2:
            return
        temp = 0 # 交换位
        for i in range(length - 1): # -1, 防止越界
            if self.mylist[i] > self.mylist[i+1]:
                flag = True
                temp = self.mylist[i+1]
                self.mylist[i+1] = self.mylist[i]
                self.mylist[i] = temp
        self.sortV1(length=length-1)
	# 优化
    def sortV2(self, length):
        #waste time:   6.580352783203125e-05
        if length < 2:
            return
        temp = 0 # 交换位
        flag = False # 标志位,如果没发生交换,则可以提前结束排序
        for i in range(length - 1): # -1, 防止越界
            if self.mylist[i] > self.mylist[i+1]:
                flag = True
                temp = self.mylist[i+1]
                self.mylist[i+1] = self.mylist[i]
                self.mylist[i] = temp
        if flag == False:
            return
        self.sortV2(length=length-1)  

    def get_list(self):
        return self.mylist


if __name__ == '__main__':
    start_time = time.time()
    list1 = [23, 75, 83, 9, 1, 63, 87, 16, 23, 91, 87, 59, 38, 21, 83]
    # list1 = [23]
    print(list1)
    BS = BubbleSort(nlist=list1)
    BS.sortV2(length=len(list1))
    print(BS.get_list())
    print("waste time: ",time.time()-start_time)

二、选择算法

原理: 简单描述:从头到尾扫描, 找到最小的放到第一个元素. 循环此操作

  • 时间复杂度:O(n^2)
  • 空间复杂度:O(1)
  • 不稳定

编:左小右大一起扫,加速加速加速

# 选择排序
# 选择排序
import time

class ChoiceSort():
    # -> 返回类型
    def __init__(self, nlist:list) -> list:
        self.mylist = nlist
    def sortV1(self, length, first=0):
        # waste time1:  3.123283386230469e-05
        if length < 2:
            return
        if length - 1 == first:
            return 
        temp = self.mylist[first] # 最小和临时交换位 且 默认第一个是最小位置
        sub_min = first # 最小的下标位
        # 每次找出一个最小位置
        for i in range(sub_min, length): 
            if self.mylist[i] < temp: # 如果比最小位要小,暂时进入min
                # 如果是链表, 即已知原来的序号, 那么可以根据这个来判断谁前谁后, 就会稳定.
                temp = self.mylist[i]
                sub_min = i
        # 交换
        self.mylist[sub_min] = self.mylist[first] # 被本轮首位替换
        # 本轮最小值换到首位
        self.mylist[first] = temp 
        
        self.sortV1(length=length,first=first+1)

    def sortV2(self, length, first=0):
        # waste time2:  1.2636184692382812e-05
        if length < 2:
            return
        if length - 1 == first:
            return 
        flag = False
        min = self.mylist[first] # 最小和临时交换位 且 默认第一个是最小位置
        max = self.mylist[length-first-1] # 最大和临时交换位 且 默认第一个是最小位置
        sub_min = first # 最小的下标位
        sub_max = length-first-1 # 最大的下标位
        # 每次找出一个最小位置
        for i in range(sub_min, sub_max): 
            if self.mylist[i] < min: # 如果比最小位要小,暂时进入min
                # 如果不加等号, 那么这里会报错, 不会进入if线.
                # 如果是链表, 即已知原来的序号, 那么可以根据这个来判断, 就会稳定.
                min = self.mylist[i]
                sub_min = i
                flag = True
            if self.mylist[length-i-1] > max: # 如果比最小位要小,暂时进入min
                # 如果不加等号, 那么这里会报错, 不会进入if线.
                # 如果是链表, 即已知原来的序号, 那么可以根据这个来判断, 就会稳定.
                max = self.mylist[length-i-1]
                sub_max = length-i-1
                flag = True
        if flag == False:
            return
        # 最小值交换
        self.mylist[sub_min] = self.mylist[first] # 被本轮首位替换
        # 本轮最小值换到首位
        self.mylist[first] = min 
        # 最大值交换
        self.mylist[sub_max] = self.mylist[length-first-1] # 被本轮首位替换
        # 本轮最大值换到尾位
        self.mylist[length-first-1] = max 

        self.sortV2(length=length,first=first+1)

    def get_list(self):
        return self.mylist
    def set_list(self, list_in:list):
        self.mylist = list_in


if __name__ == '__main__':
    list1 = [23, 75, 83, 9, 1, 63, 87, 16, 23, 91, 87, 59, 38, 21, 83]
    # list1 = [23]
    print(list1)
    BS = ChoiceSort(nlist=list1)
    # V1方法
    start_time = time.time()
    BS.sortV1(length=len(list1), first=0)
    print(BS.get_list())
    V1_time = time.time()

    BS.set_list(list_in=list1)
    # V2优化方法
    middle_time = time.time()
    BS.sortV2(length=len(list1), first=0)
    print(BS.get_list())
    V2_time = time.time()

    print("waste time1: ",V1_time-start_time)
    print("waste time2: ",V2_time-middle_time)

三、插入算法

原理: *简单描述:构建另一个有序列表,把无序列表的每一个值从后往前插入有序列表中,就像玩纸牌一样.

  • 时间复杂度:O(n^2)
  • 空间复杂度:O(1)
  • 稳定

不足: 1)寻找插入位置; 2)移动元素
优化: 1)对已经排序好的序列, 采用二分查找
2)携带多个元素
3)数据链表化
4)希尔排序

编:可是我的纸牌每次都排不好

# 选择排序
import time

class InsertSort():
    # -> 返回类型
    def __init__(self, nlist:list) -> list:
        self.mylist = nlist
    def sortV1(self, length):
        # waste time1:  3.123283386230469e-05
        if length < 2:
            return
        # 先插入一个值
        new_list = [self.mylist[0]]
        # 按顺序遍历无序列表,把每一个值插入有序列表new_list中
        for i in range(1, length):
            now_value = self.mylist[i]
            # 设立一个flag, 如果已经插入, 就不需要在循环
            flag = False
            for j in range(len(new_list)):
                if flag==True: # 如果flag是true, 就跳出循环
                    break
                # 从后向前遍历, 如果该元素大于某个值,就插入这里的下标
                if now_value >= new_list[len(new_list)-j-1]:
                    new_list.insert(len(new_list)-j, now_value)
                    flag = True
                # 如果遍历到最后一个,且还没有插入, 那就插入第一个地方
                if j==len(new_list)-1 and flag == False:
                    new_list.insert(0, now_value)
                    flag = True
                # 如果不成立, 肯定插入首位
                # else:
                #     new_list.insert(0, now_value)
                #     flag = True
        self.mylist = new_list

    def sortV2(self, length, first=0):
    #不足: 1)寻找插入位置; 2)移动元素

    #优化:     1)对已经排序好的序列, 采用二分查找
            # 2)携带多个元素, 插入时,把多个大的元素一起后移,提高效率
            # 3)数据链表化, 链表化后,只需要改自身地址
            # 4)希尔排序
            pass

    def get_list(self):
        return self.mylist

    def set_list(self, list_in:list):
        self.mylist = list_in


if __name__ == '__main__':
    list1 = [23, 75, 83, 9, 1, 63, 87, 16, 23, 91, 87, 59, 38, 21, 83]
    # list1 = [23]
    print(list1)
    BS = InsertSort(nlist=list1)
    # V1方法
    start_time = time.time()
    BS.sortV1(length=len(list1))
    print(BS.get_list())
    V1_time = time.time()

    BS.set_list(list_in=list1)
    # V2优化方法

    # middle_time = time.time()
    # BS.sortV2(length=len(list1), first=0)
    # print(BS.get_list())
    # V2_time = time.time()

    print("waste time1: ",V1_time-start_time)
    # print("waste time2: ",V2_time-middle_time)

四、希尔排序算法

原理: *简单描述:针对插入算法的改进.先分组(隔几个元素分为一组), 每组进行插入排序, 再调整分组方式重复操作, 最后使用一次插入排序.

  • 时间复杂度:O(nlogn) 最坏O(n^2)
  • 空间复杂度:O(1)
  • 数组不稳定, 链表稳定

希尔增量问题: 增量就是间隔, 从大到小

*编:这个从几个元素开始分,我是直接默认从一半开始。插入排序记得加flag位跳过有序数列

# 希尔排序
import time

class ShellSort():
    # -> 返回类型
    def __init__(self, nlist:list) -> list:
        self.mylist = nlist

    def sortV1(self):
        inlist = self.mylist
        length_inlist = len(inlist) # 总长度
        for idx in range(length_inlist//2):
            # idx = 0,1,....,(len//2)
            # 以总长度的一半作为最大间隔量
            steps = length_inlist//2 - 1 - idx
            # 要想不停分组到全部遍历, 起始点要到间隔数为止.
            for step in range(steps):
                # 等间隔抽取
                new_list = inlist[step:length_inlist:steps]
                new_list = self.insertsort(new_list, len(new_list))
                # 等间隔赋值
                inlist[step:length_inlist:steps] = new_list
                print("间隔=>{}: ".format(steps), inlist)

        self.mylist = inlist

    def insertsort(self, list_sort, length):
        # waste time1:  3.123283386230469e-05
        if length < 2:
            return
        # 先插入一个值
        new_list = [list_sort[0]]
        # 按顺序遍历无序列表,把每一个值插入有序列表new_list中
        for i in range(1, length):
            now_value = list_sort[i]
            # 设立一个flag, 如果已经插入, 就不需要在循环
            flag = False
            for j in range(len(new_list)):
                if flag==True: # 如果flag是true, 就跳出循环
                    break
                # 从后向前遍历, 如果该元素大于某个值,就插入这里的下标
                if now_value >= new_list[len(new_list)-j-1]:
                    new_list.insert(len(new_list)-j, now_value)
                    flag = True
                # 如果遍历到最后一个,且还没有插入, 那就插入第一个地方
                if j==len(new_list)-1 and flag == False:
                    new_list.insert(0, now_value)
                    flag = True
                # 如果不成立, 肯定插入首位
                # else:
                #     new_list.insert(0, now_value)
                #     flag = True
        return new_list

    def get_list(self):
        return self.mylist

    def set_list(self, list_in:list):
        self.mylist = list_in


if __name__ == '__main__':
    list1 = [23, 75, 83, 9, 1, 63, 87, 16, 23, 91, 87, 59, 38, 21, 83]
    # list1 = [23]
    print(list1)
    BS = ShellSort(nlist=list1)
    # V1方法
    start_time = time.time()
    BS.sortV1()
    print(BS.get_list())
    V1_time = time.time()

    # BS.set_list(list_in=list1)
    # V2优化方法

    # middle_time = time.time()
    # BS.sortV2(length=len(list1), first=0)
    # print(BS.get_list())
    # V2_time = time.time()

    print("waste time1: ",V1_time-start_time)
    # print("waste time2: ",V2_time-middle_time)

五、快速排序算法(!该部分未完全独立思考,参考了部分网络资料)

原理: *简单描述:取一个基准数,把无序列表中小于它的数放到左区间,大于它的放右区间,然后重复执行直到各区间小于两个元素.

  • 时间复杂度:O(nlogn) 最坏O(n^2)
  • 空间复杂度:O(logn)
  • 不稳定

编:这个标志位的移动真的搞死我了

# 选择排序
import time

class FastSort():
    # -> 返回类型
    def __init__(self, nlist:list) -> list:
        self.mylist = nlist
    def sortV1(self):
        # 从右标志开始,找小的数放入基准位,然后左标志开始移动,找到大的数放入左标志位
        inlist = self.mylist
        
        if len(inlist) < 2:
            return
        # 栈,存储排序子数组的起始和结束位置
        stack = [(0, len(inlist)-1)] # stack = [(0,14)]
        # 设置左右下标志位, 分别位于首尾
        while stack:
            # 弹出栈顶的子数组的起始和结束位置,
            low, high = stack.pop() # low=0, high=14
            if low >= high:
                continue
            # 选择最后一个作为基准数
            pivot = inlist[high]
            print("pivot:",pivot)
            i = low - 1 # 初始化时(-1),先保证所有元素都不在i的右侧
            # ≤到左侧,>到右侧
            for j in range(low, high):
                if inlist[j] <= pivot: # 从左往右找小于基准数的元素
                    i += 1 # 找到了,i+1,i置于这些小于基准数的右侧
                    inlist[i], inlist[j] = inlist[j], inlist[i] #i,j对换
            # 直到if条件不再满足,i不再变化。这里i+1就是基准数该去的位置
            # 基准数放入i+1位置,原来的数放入最右
            inlist[i+1], inlist[high] = inlist[high], inlist[i+1]
            print("=>",inlist)
            # 将左右区间的起始和结束位置压入栈中,继续处理左右区间
            stack.append((low, i))
            stack.append((i+2, high)) # i+2 跳过基础数
        self.mylist = inlist

    def sortV2(self):
        # 等间隔随机抽取一个中间数作为基准数,提高排序效率
        new_list = inlist[0:len(inlist):len(inlist)//4]
        # 当元素少于10个时,采用插入排序,效率更高。

    def get_list(self):
        return self.mylist
    def set_list(self, list_in:list):
        self.mylist = list_in


if __name__ == '__main__':
    list1 = [23, 75, 83, 9, 1, 63, 87, 16, 23, 91, 87, 59, 38, 21, 83]
    # list1 = [23]
    print(list1)
    BS = FastSort(nlist=list1)
    # V1方法
    start_time = time.time()
    BS.sortV1()
    print(BS.get_list())
    V1_time = time.time()

    # BS.set_list(list_in=list1)
    # # V2优化方法
    # middle_time = time.time()
    # BS.sortV2(length=len(list1), first=0)
    # print(BS.get_list())
    # V2_time = time.time()

    print("waste time1: ",V1_time-start_time)
    # print("waste time2: ",V2_time-middle_time)

六、归并算法

原理: *简单描述:对多个有序子数列进行两两合并,每两个子数列各有一个标志位,比较时,标志位上更小的先进入最终数列.

  • 时间复杂度:O(nlogn)
  • 空间复杂度:O(n)
  • 稳定

编:要考虑合并时任何一个数列没有被合并完的情况

# 归并排序
import time

class MergeSort():
    # -> 返回类型
    def __init__(self, nlist:list) -> list:
        self.mylist = nlist
    def sortV1(self):
        inlist = self.mylist
        if len(inlist) < 2:
            return
        # 对子列表分别进行合并(不重合的两两合并)
        # 如果刚好是偶数
        if len(inlist) % 2 != 0:
            new_inlist = [inlist[-1]]
        else:
            new_inlist = []

        for k in range(len(inlist)//2):
            new_inlist.append(self.merge_two_list(list1=inlist[2*k], list2=inlist[2*k-1]))

        self.mylist = new_inlist
        print("middle_list:", self.mylist)
        # 递归
        self.sortV1()
        # self.mylist = inlist

    def merge_two_list(self, list1, list2):
        flag1, flag2 = 0, 0
        new_list = []
        while flag1 < len(list1) and flag2 < len(list2):
            # 小就放入新列表
            if list1[flag1] < list2[flag2]:
                new_list.append(list1[flag1])
                flag1 += 1
             # 大则下一个, flag+1
            else:
                new_list.append(list2[flag2])
                flag2 += 1 
        # 此时还有其他情况
        # list2都进入new_list了, list1还有
        if flag2 == len(list2):
            for j in range(flag1,len(list1)):
                new_list.append(list1[j])
        # list1都进入new_list了, list2还有
        if flag1 == len(list1):
            for j in range(flag2,len(list2)):
                new_list.append(list2[j])
            # 直到循环结束
        return new_list

    def get_list(self):
        return self.mylist

    def set_list(self, list_in:list):
        self.mylist = list_in


if __name__ == '__main__':
    list1 = [23, 75, 83, 9, 1, 63, 87, 16, 23, 91, 87, 59, 38, 21, 83]
    # 默认是一个把要合并的列表合在一个列表里的二维列表,实际情况另说
    list2 = [[x] for x in list1]
    # print(len(list2))
    # exit(0)
    # list1 = [23]
    print(list2)
    BS = MergeSort(nlist=list2)
    # V1方法
    start_time = time.time()
    BS.sortV1()
    print("getlist:",BS.get_list()[0])
    V1_time = time.time()

    BS.set_list(list_in=list1)
    # V2优化方法

    # middle_time = time.time()
    # BS.sortV2(length=len(list1), first=0)
    # print(BS.get_list())
    # V2_time = time.time()

    print("waste time1: ",V1_time-start_time)
    # print("waste time2: ",V2_time-middle_time)

七、堆(heap)排序算法 (!该部分未完全独立思考,参考了部分网络资料)

简单知识:

  • 堆是完全二叉树:从上到下,每一层节点都是满的,最下一层所有的节点都连续集中在最左边
  • 堆的节点的值≥左右子节点的值,称为大顶堆;节点的值≤左右子节点的值,称为小顶堆
  • 不符合上述要求,就不是堆
  • 设i是堆N的下标,N[i]的父节点在(i-1)//2,左子节点是2i+1, 右子节点是2i+2
    原理: 简单描述:先将数组排成完全二叉树。在最后一个父节点的位置上,与自己的子节点进行比较排序.逐渐排成大小顶堆,然后弹出第一个父节点,将最后一个子节点放到第一个父节点上,重新进行大小顶堆的排序,然后弹出第一个父节点,以此类推。
  • 时间复杂度:O(nlogn)
  • 空间复杂度:O(1)
  • 不稳定

编:二叉树您怎么又在我的脑子里复活啦

# 堆排序
import time

class HeapSort():
    # -> 返回类型
    def __init__(self, nlist:list) -> list:
        self.mylist = nlist
    def sortV1(self):
        inlist = self.mylist
        n = len(inlist) # 数组长度
        
        # 构建最大堆并开始 初次排序
        # 从最后一个非叶子节点(最后一个父节点)的索引开始,依次向上调整每个父节点(数组长度除2并向下取整然后减1,就是最后一个父节点)
        for i in range(n//2-1, -1, -1): # start,end,step
            inlist = self.heapify(inlist, n, i)

        # 初次排序后,逐个提取最大堆的元素
        # 将根节点与最后一个节点交换,然后继续构建最大堆,并继续排序
        for i in range(n-1, 0, -1):
            inlist[i], inlist[0] = inlist[0], inlist[i] # 交换根节点和最后一个子节点
            # 由于数组已经基本有序,只需要对第一个父节点做堆排序就行。
            inlist = self.heapify(inlist, i, 0)

        # 设置最终数组
        self.set_list(inlist)

    def heapify(self, arr, n, i):
        # arr 数组, n 数组长度, i当前节点索引
        # 对当前节点进行排序
        largest = i # 将当前节点设为最大值的索引
        left = 2 * i + 1 # 左子节点的索引
        right = 2 * i + 2 # 右子节点的索引
        # 检查左子节点是否大于当前节点
        if left < n and arr[i] < arr[left]:
            largest = left
        # 检查右子节点是否大于当前节点
        if right < n and arr[i] < arr[right]:
            largest = right      
        # 如果最大值发生变化,则进行交换以保持最大堆性质
        if largest != i:
            arr[i], arr[largest] = arr[largest], arr[i]
            # 继续对子节点的子节点进行排序
            self.heapify(arr, n, largest)
        return arr

    def get_list(self):
        return self.mylist

    def set_list(self, list_in:list):
        self.mylist = list_in


if __name__ == '__main__':
    list1 = [23, 75, 83, 9, 1, 63, 87, 16, 23, 91, 87, 59, 38, 21, 83]
    print("原始数组: ", list1)
    # list1 = [23]
    BS = HeapSort(nlist=list1)
    # V1方法
    start_time = time.time()
    BS.sortV1()
    print("堆排序数组: ",BS.get_list())
    V1_time = time.time()

    BS.set_list(list_in=list1)
    # V2优化方法

    # middle_time = time.time()
    # BS.sortV2(length=len(list1), first=0)
    # print(BS.get_list())
    # V2_time = time.time()

    print("waste time1: ",V1_time-start_time)
    # print("waste time2: ",V2_time-middle_time)

八、计数排序算法

简单知识:

原理: 简单描述:对每个元素出现的次数进行计数,计数的数值放入拥有从0-max的有序数组中的数值。
优化: 将计数数组长度定为max-min+1,

  • 时间复杂度:数组链表:O(n+m) 其中n是数据规模,m就是(max-min+1)
  • 空间复杂度:O(m)
  • 稳定

编:如果不经过优化,用到最小数很大的数组上,那这个计数数组真的占空间

# 计数排序
import time

class CountSort():
    # -> 返回类型
    def __init__(self, nlist:list) -> list:
        self.mylist = nlist
    def sortV1(self):
        inlist = self.mylist
        n = len(inlist) # 数组长度
        # 找出数组的最小值和最大值
        min_num, max_num = min(inlist), max(inlist)
        # 创建一个长度为max_num-min_num+1的计数数组
        count_list = [0]*(max_num-min_num+1)
        # 遍历原数组,统计每个元素出现的次数。对于一个元素A,将count_list[A-min_num]的值+1
        for i in range(n):
            count_list[inlist[i]-min_num] += 1
        # 创建一个新数组,一个个放进去
        new_list = []         
        # 遍历计数数组,通过索引与最小值相加的方法,逐个将元素填进去,填进去的数量与索引位对应的值相关。 
        for j in range(len(count_list)):
            if count_list[j] != 0: # 如果索引位值不等于0
                for k in range(count_list[j]):
                    new_list.append(min_num+j)

        # 设置最终数组
        self.set_list(new_list)

    def get_list(self):
        return self.mylist

    def set_list(self, list_in:list):
        self.mylist = list_in


if __name__ == '__main__':
    list1 = [23, 75, 83, 9, 1, 63, 87, 16, 23, 91, 87, 59, 38, 21, 83]
    print("原始顺序数组: ", list1)
    # list1 = [23]
    BS = CountSort(nlist=list1)
    # V1方法
    start_time = time.time()
    BS.sortV1()
    print("计数排序数组: ",BS.get_list())
    V1_time = time.time()

    BS.set_list(list_in=list1)
    # V2优化方法

    # middle_time = time.time()
    # BS.sortV2(length=len(list1), first=0)
    # print(BS.get_list())
    # V2_time = time.time()

    print("waste time1: ",V1_time-start_time)
    # print("waste time2: ",V2_time-middle_time)

九、【重要】桶排序算法

简单知识:假设数据分布均匀的情况下,把数据分到有限数量的桶(不同区间)里,再对每个桶进行其他排序,最后合并所有桶
桶思想:现实世界,大部分数据分布是均匀的,或者说在设计之处就让他分布均匀,那么就可以应用桶排序了

  • 在数据量很大的情况下,桶排序的思想会极大提高效率
    原理: 简单描述:设置多个有序的桶,每个桶包含一个数据区间,无序数组的元素按这个区间放入各个桶,最后对桶内进行排序
  • 时间复杂度:最好平均:O(n+m), 最坏:O(n^2) m是桶的数量
  • 空间复杂度:O(n+m)
  • 稳定

编:我更想叫他多区间排序

# 桶排序
import time

class BucketSort():
    # -> 返回类型
    def __init__(self, nlist:list) -> list:
        self.mylist = nlist
    def sortV1(self):
        inlist = self.mylist
        n = len(inlist) # 数组长度
        # 找出数组的最大值, 作为桶的数量来源, 桶数=(最大值//区间长度)+1
        max_num = max(inlist)
        bucket_num = max_num // 20 + 1
        # print(bucket_num)
        # 给我来bucket_num个桶
        # buckets = [[]]*bucket_num 会出bug,使用append方法会同时给所有子列表加值
        buckets = []
        for bn in range(bucket_num):
            buckets.append([])
        # print(buckets)
        # 遍历原数组,把数据按规则一个个放到桶里
        for i in range(n):
            now_num = inlist[i] # 当前元素
            for j in range(bucket_num):
                if now_num >= j*20 and now_num< (j+1)*20:
                    buckets[j].append(now_num) # 放入指定区间的桶
        # print(buckets)
        # 设置有序数组
        new_list = []
        # 调用其他排序算法,对每个桶进行排序
        for j in range(bucket_num):
            self.BubbleSort(arr=buckets[j], length=len(buckets[j]))
            # 每个桶排序结束,就把数据从里面倒出来。
            new_list.extend(buckets[j])
            
        # 设置最终数组
        self.set_list(new_list)

    def BubbleSort(self, arr, length):
        if length < 2:
            return
        temp = 0 # 交换位
        flag = False # 标志位,如果没发生交换,则可以提前结束排序
        for i in range(length - 1): # -1, 防止越界
            if arr[i] > arr[i+1]:
                flag = True
                temp = arr[i+1]
                arr[i+1] = arr[i]
                arr[i] = temp
        if flag == False:
            return
        self.BubbleSort(arr=arr, length=length-1)  


    def get_list(self):
        return self.mylist

    def set_list(self, list_in:list):
        self.mylist = list_in


if __name__ == '__main__':
    list1 = [23, 75, 83, 9, 1, 63, 87, 16, 23, 91, 87, 59, 38, 21, 83]
    print("原顺序数组: ", list1)
    # list1 = [23]
    BS = BucketSort(nlist=list1)
    # V1方法
    start_time = time.time()
    BS.sortV1()
    print("桶排序数组: ", BS.get_list())
    V1_time = time.time()

    BS.set_list(list_in=list1)
    # V2优化方法

    # middle_time = time.time()
    # BS.sortV2(length=len(list1), first=0)
    # print(BS.get_list())
    # V2_time = time.time()

    print("waste time1: ",V1_time-start_time)
    # print("waste time2: ",V2_time-middle_time)

十、【重要】基数排序算法

简单知识:桶排序从区间分配升级到个位十位百位逐个比较分配。

  • 桶排序升级版
    原理: 简单描述:先比较个位大小,放入每个桶,然后排序;接着比较十位大小,同理以此类推到结束
  • 时间复杂度:数组链表:O(k*n) 这里的k就是最大值的最高位次,比如千位,k就是3
  • 空间复杂度:O(n+k)
  • 稳定
# 桶排序
import time

class RadixSort():
    # -> 返回类型
    def __init__(self, nlist:list) -> list:
        self.mylist = nlist
    def sortV1(self):
        inlist = self.mylist
        n = len(inlist) # 数组长度

        max_num = max(inlist) # 找到最大值,看一下他是几位数,就要比到他的最高位
        compare_bit = len(str(max_num))
        # 基数排序每次排序桶的数量都是10,编:那肯定的,十进制数
        bucket_num = 10
        # 最终数组
        new_list = []
        # 遍历原数组,把数据按规则一个个放到桶里: 这里是按个位(排序一次)->十位(排序一次)->百位(排序一次)这样比较下去...
        # 从个位开始遍历
        for bitdx in range(1, compare_bit+1):
            # 每一次遍历都要更新桶
            # buckets = [[]]*bucket_num 会出bug,使用append方法会同时给所有子列表加值
            buckets = []
            for bn in range(bucket_num):
                buckets.append([])
            # 遍历原数组
            for i in range(n):
                # 如果遍历到的元素没有bitdx位<==>就不需要排序
                if len(str(inlist[i])) < bitdx:
                    new_list.append(inlist[i]) # 进入最终数组然后直接跳过当前循环
                    continue # 跳过这个元素
                else:
                    now_num = int(str(inlist[i])[-bitdx]) # 取出当前元素的bitdx位
                
                buckets[now_num].append(inlist[i]) # 放入指定区间的桶
            print(bitdx, "==>", buckets)
            # 设置有序数组
            inlist = []
            for j in range(bucket_num):
                # 每个桶排序结束,就把数据从里面倒出来。
                inlist.extend(buckets[j])

        # 最后做一次倒桶        
        for j in range(bucket_num):
            new_list.extend(buckets[j])       
        # 设置数组
        self.set_list(new_list)


    def get_list(self):
        return self.mylist

    def set_list(self, list_in:list):
        self.mylist = list_in


if __name__ == '__main__':
    list1 = [23, 75, 83, 9, 1, 63, 87, 16, 23, 91, 87, 59, 38, 21, 83]
    print("原顺序数组: ", list1)
    # list1 = [23]
    BS = RadixSort(nlist=list1)
    # V1方法
    start_time = time.time()
    BS.sortV1()
    print("桶排序数组: ", BS.get_list())
    V1_time = time.time()

    BS.set_list(list_in=list1)
    # V2优化方法

    # middle_time = time.time()
    # BS.sortV2(length=len(list1), first=0)
    # print(BS.get_list())
    # V2_time = time.time()

    print("waste time1: ",V1_time-start_time)
    # print("waste time2: ",V2_time-middle_time)

总结:终于写完了,花了三天,再花点时间理解一下空间复杂度和时间复杂度

更新等:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
当谈到排序算法时,有许多经典的排序算法可以使用。以下是十大常见的排序算法及其详细解释,使用Python编写的示例代码: 1. 冒泡排序(Bubble Sort): 冒泡排序是一种简单的交换排序算法。它重复地遍历要排序的列表,比较相邻的元素并交换它们,直到整个列表排序完成。 ```python def bubble_sort(arr): n = len(arr) for i in range(n-1): for j in range(n-i-1): if arr[j] > arr[j+1]: arr[j], arr[j+1] = arr[j+1], arr[j] return arr ``` 2. 选择排序(Selection Sort): 选择排序是一种简单直观的排序算法。它将列表分为已排序和未排序两部分,每次从未排序部分选择最小的元素,并将其放入已排序部分的末尾。 ```python def selection_sort(arr): n = len(arr) for i in range(n-1): min_idx = i for j in range(i+1, n): if arr[j] < arr[min_idx]: min_idx = j arr[i], arr[min_idx] = arr[min_idx], arr[i] return arr ``` 3. 插入排序(Insertion Sort): 插入排序是一种简单且高效的排序算法。它将列表分为已排序和未排序两部分,每次从未排序部分选择一个元素,并将其插入到已排序部分的正确位置。 ```python def insertion_sort(arr): n = len(arr) for i in range(1, n): key = arr[i] j = i - 1 while j >= 0 and arr[j] > key: arr[j + 1] = arr[j] j -= 1 arr[j + 1] = key return arr ``` 4. 快速排序(Quick Sort): 快速排序是一种高效的分治排序算法。它选择一个基准元素,将列表分为小于基准值和大于基准值的两部分,然后递归地对这两部分进行排序。 ```python def quick_sort(arr): if len(arr) <= 1: return arr pivot = arr[len(arr) // 2] left = [x for x in arr if x < pivot] middle = [x for x in arr if x == pivot] right = [x for x in arr if x > pivot] return quick_sort(left) + middle + quick_sort(right) ``` 5. 归并排序(Merge Sort): 归并排序是一种稳定的排序算法,它采用分治策略将列表分为较小的部分,然后递归地合并这些部分以获得排序结果。 ```python def merge_sort(arr): if len(arr) <= 1: return arr mid = len(arr) // 2 left = arr[:mid] right = arr[mid:] left = merge_sort(left) right = merge_sort(right) return merge(left, right) def merge(left, right): result = [] i, j = 0, 0 while i < len(left) and j < len(right): if left[i] < right[j]: result.append(left[i]) i += 1 else: result.append(right[j]) j += 1 result.extend(left[i:]) result.extend(right[j:]) return result ``` 6. 堆排序(Heap Sort): 堆排序利用堆的性质进行排序。它将列表视为完全二叉树,并使用堆数据结构来选择最大或最小元素并将其放在正确的位置上。 ```python def heapify(arr, n, i): largest = i left = 2 * i + 1 right = 2 * i + 2 if left < n and arr[i] < arr[left]: largest = left if right < n and arr[largest] < arr[right]: largest = right if largest != i: arr[i], arr[largest] = arr[largest], arr[i] heapify(arr, n, largest) def heap_sort(arr): n = len(arr) for i in range(n // 2 - 1, -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) return arr ``` 7. 计数排序(Counting Sort): 计数排序是一种稳定的整数排序算法,它使用额外的计数数组来确定每个元素在排序后的位置。它适用于元素范围较小的情况。 ```python def counting_sort(arr): max_val = max(arr) count = [0] * (max_val + 1) for num in arr: count[num] += 1 sorted_arr = [] for i in range(len(count)): sorted_arr.extend([i] * count[i]) return sorted_arr ``` 8. 桶排序(Bucket Sort): 桶排序是一种将元素分配到

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值