zoukankan      html  css  js  c++  java
  • python数据结构与算法之排序

    排序算法的稳定性:

    假设有一串数据:(4,1)(3,1)(3,7)(5,6);要求按照第一个数排序,结果如下:

    第一种:(3,1)(3,7)(4,1)(5,6)(3相同,维持原来的次序)

    第二种:(3,7)(3,1)(4,1)(5,6)(3相同,次序被改变)

    第一种是稳定的。

    冒泡排序(以从小到大排为例):

    每次走一遍把最大的元素冒泡,排到最后。

    '''
    冒泡排序:它也可以用于之前讲的链表什么的,只是交换部分稍微烦一点,思想一样。这里简单一点 ,以数字为例
    '''
    def bubble_sort(alist):
        '''冒泡排序,参数为列表'''
        n = len(alist)-1
        for j in range(n):
            for i in range(n-j):
                if alist[i]>alist[i+1]:
                    # 前一个大于后一个,交换
                    alist[i], alist[i+1] = alist[i+1], alist[i]    # 这样写在python这种动态语言中可以
    
    if __name__ == '__main__':
        a = [2,0,5,1,10]
        bubble_sort(a)
        print(a)

    冒泡排序的时间复杂度为:最坏可以认为是O(n^2),稳定的

    改进:假如传入的序列就是有序的,比如[1,2,3,4,5,6]。此时按照上面代码还是要一步步比较,复杂度是一样的。改进之后,最优时间复杂度为O(n),最坏时间复杂度不变。

    def bubble_sort(alist):
        '''冒泡排序,参数为列表'''
        n = len(alist)-1
        for j in range(n):
            count = 0
            for i in range(n-j):
                if alist[i]>alist[i+1]:
                    # 前一个大于后一个,交换
                    alist[i], alist[i+1] = alist[i+1], alist[i]    # 这样写在python这种动态语言中可以
                    count += 1
            if count == 0:
                return 

    选择排序:

    思想解释:每次找到最小的值,与无序数中的一个数交换,比如:

    a = [52,100,23,43,55,20,17,108]

    找到最小值是17,将17与52交换,得:

    a = [17,100,23,43,55,20,52,108]

    看除了第一个数17外,其他最小的为20,与“第一个数”100交换:

    a = [17,20,23,43,55,100,52,108]

    此时,前面两个数已经有序,以此往下。

    def select_sort(alist):
        """选择排序,既然是研究数据结构与算法,这里不用min()函数"""
        n = len(alist)
        for j in range(n-1):
            # 记录最小值的位置,这里首次默认是无序中的第一个
            min_index = j
            for i in range(j+1,n):
                if alist[i]<alist[min_index]:
                    min_index = i
            alist[j], alist[min_index] = alist[min_index], alist[j]

    选择排序的时间复杂度:O(n^2),不稳定

    插入排序算法:

    思想理解,与上面选择排序有点雷士,其实还是将序列无形的分为两部分。比如序列[52,100,23,43,55,20,17,108]。

    将序列分为[52,                      100,23,43,55,20,17,108],第一部分是有序的。

    然后将无序中的第一个100与有序中52比较,放在正确的位置[52,100,                      23,43,55,20,17,108],

    同理接着比较23与[52,100],将其插入正确的位置[23,52,100,                            43,55,20,17,108]

     注意:插入的过程其实就是一个小排序,比如插入23时,先与100比,然后与52........

    def insert_sort(alist):
        """插入排序"""
        for j in range(1,len(alist)):
            i = j
            # 从无序部分选一个插入到有序部分的过程
            while i>0:
                if alist[i]<alist[i-1]:
                    alist[i], alist[i-1] = alist[i-1], alist[i]
                    i -= 1
                else:
                    # 因为有序部分是有序的,只要前一个数比当前数小,那前面所有的数都比当前数小
                    break

    最坏时间复杂度是O(n^2),最优时间复杂度是O(n),稳定的

    希尔排序:

    它其实就是插入排序的改进版,思想百度百科一下就可以了。

    简单介绍:它有一个gap(间隙),假设gap=4。原序列为54,26,93,17,77,31,44,55,20;

    因为gap=4,索引相差四的抽出,那原序列变成4层:

    54,                    77 ,             20

         26,                    31,

              93,                    44

                  17                        55

    每一层排序后再插入回去,即经过第一次排序之后:20,26,44,17,54,31,93,55,77;

    再取gap=2,原序列变成两层:

    20          44           54            93          77

          26          17          31              55

    同理每层子序列用插入算法,再取gap=1.得到最后的结果。

    def shell_sort(alist):
        """希尔排序,核心部分其实是插入排序"""
        n = len(alist)
        gap = n//2        # 其实有最优的gap值,这里直接用长度的一半取整
    
        # gap变化为0前,插入算法执行的次数
        while gap>=1:
            # 内层循环其实就是插入算法,与普通的插入算法的区别在于gap
            for j in range(gap,n):
                i = j
                while i>0:
                    if alist[i]<alist[i-gap]:
                        alist[i], alist[i-gap] = alist[i-gap], alist[i]
                        i -= gap
                    else:
                        break
            gap //= 2

    最坏时间复杂度O(n^2),最优时间复杂度根据gap值不同的而不同(优化问题)。不稳定

    快速排序

    具体思想介绍可以百度一下,其实有点像冒泡的改进。举个例子:

    假设原序列为:54,26,93,17,77,31,44,55,20;

    先找到第一个数54在有序情况下的位置,怎么找?设定两个游标,游标low先指向26,游标high先指向最后一个数20.

    当low指向的数小于54时,可以继续向high的方向移动,否则先静止;同理当high指向的数大于54时可以向low的方向移动,否则静止;

    按照上面的要求,此时54,26,93(low),17,77,31,44,55,20(high);

    卡主不动了,这时候交换93,20的位置,数据变成54,26,20(low),17,77,31,44,55,93(high);这时不卡了,继续移动(先动low),一直到low与high重合,如下:

    54,          26,20,17,31,44(low_and_high),77,55,93

    此时确定54的位置:26,20,17,31,44,                  54                     ,77,55,93同理处理54前后两部分;

    大致思想是这样,为了方便编程,稍微变通一下(仍然是快速排序):

    序列还是上面那个,一开始low指向54,high指向20,设一个mid_value=54(把第一个数存起来),游标还是往中间动;

    high先动,能动的条件一样,此时20,high游标不能动,就将20代替low指向的数(54早已存起来)。变成20(low),26,93,17,77,31,44,55,空(high);

    这时候low动起来,一直到:20,26,93(low),17,77,31,44,55,空(high)

    替换:20,26,空(low),17,77,31,44,55,93(high);high动

    最终:20,26,44,17,31,空(low_high),77,55,93

    让空=54,20,26,44,17,31,           54,            77,55,93,同理处理54两边。

     1 def quick_sort(alist):
     2     """快速排序,错误示范,错误的地方在下面递归"""
     3     n = len(alist)
     4     mid_value = alist[0]
     5     low = 0
     6     high = n-1
     7 
     8     while low < high:
     9         # high左移动(等于的情况放一边处理比较好)
    10         while low < high and alist[high] >= mid_value:
    11             high -= 1
    12         alist[low] = alist[high]
    13         # low右移
    14         while low < high and alist[low] < mid_value:
    15             low += 1
    16         alist[high] = alist[low]
    17     # 从while退出,low等于high
    18     alist[low] = mid_value
    19 
    20     # mid_walue两边处理方式与上面一样
    21     quick_sort(alist[:low-1])        #不能切片传,这等于传一个新的列表了,也就是说操作的不是一个列表
    22     quick_sort(alist[low+1:])
    错误示范
    def quick_sort(alist, first, last):
        """
        快速排序
        :param alist: 列表
        :param first: 列表的第一个元素索引,0
        :param last: 列表最后一个元素,len()-1
        :return:
        """
        # 递归的终止条件
        if first >= last:
            return
    
        mid_value = alist[first]
        low = first
        high = last
    
        while low < high:
            # high左移动(等于的情况放一边处理比较好)
            while low < high and alist[high] >= mid_value:
                high -= 1
            alist[low] = alist[high]
            # low右移
            while low < high and alist[low] < mid_value:
                low += 1
            alist[high] = alist[low]
        # 从while退出,low等于high
        alist[low] = mid_value
    
        # mid_walue两边处理方式与上面一样
        quick_sort(alist, first, low-1)
        quick_sort(alist, low+1, last)
    
    
    if __name__ == '__main__':
        b = [54,26,93,17,77,31,44,55,20]
        quick_sort(b,0,len(b)-1)
        print(b)

    快速排序的最优时间复杂度O(nlogn),最坏时间复杂度O(n^2),不稳定。

    归并排序:

    思想:百度一下就好

    def Merge_Sort(lists):
        if len(lists) <= 1:
            return lists
        mid = int(len(lists) / 2)
        # left 采用归并排序后形成的有序的新的列表
        left = Merge_Sort(lists[:mid])
        # right 采用归并排序后形成的有序的新的列表
        right = Merge_Sort(lists[mid:])
        # 将两个有序的子序列合并
        # Merge(left, right)
        r, l=0, 0  # 为两个指针
        result=[]
        while l<len(left) and r<len(right):
            if left[l] < right[r]:
                result.append(left[l])
                l += 1
            else:
                result.append(right[r])
                r += 1
        result += list(left[l:])
        result += list(right[r:])
        return result
    
    
    
    if __name__ == '__main__':
        b = [54,26,93,17,77,31,44,55,20]
        b = Merge_Sort(b)
        print(b)

    上面代码递归有点复杂,执行流程可看https://www.bilibili.com/video/av21540971/?p=35

    最坏与最优时间复杂度都是:O(nlogn),稳定。

    堆排序没介绍,可以百度一下,快速排序用的比较多。

  • 相关阅读:
    关于width属性max/min-content和fit-content
    director.js实现前端路由
    第一篇博文
    AtCoder Beginner Contest 120 题解
    AtCoder Beginner Contest 121 题解
    AtCoder Beginner Contest 115 题解
    Python中list的复制及深拷贝与浅拷贝探究
    Python中print()函数不换行的方法
    高斯消元法的C++简单实现
    Python中list作为默认参数的陷阱
  • 原文地址:https://www.cnblogs.com/maxiaonong/p/10521552.html
Copyright © 2011-2022 走看看