zoukankan      html  css  js  c++  java
  • Python排序算法

    基础定义

    • 稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面。
    • 不稳定:如果a原本在b的前面,而a=b,排序之后 a 可能会出现在 b 的后面。
    • 时间复杂度:对排序数据的总的操作次数。反映当n变化时,操作次数呈现什么规律。
    • 空间复杂度:是指算法在计算机内执行时所需存储空间的度量,它也是数据规模n的函数。

    图示

    冒泡排序

    原理

    冒泡排序(Bubble Sort)是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。

    步骤

    冒泡排序算法的运作如下:

    1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
    2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
    3. 针对所有的元素重复以上的步骤,除了最后一个。
    4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

    代码

    def bubble_sort(list):
        length = len(list)
        # 第一级遍历
        for index in range(length):
            # 第二级遍历
            for j in range(1, length - index):
                if list[j - 1] > list[j]:
                    # 交换两者数据,这里没用temp是因为python 特性元组。
                    list[j - 1], list[j] = list[j], list[j - 1]
        return list

    这种排序其实还可以稍微优化一下,添加一个标记,在排序已完成时,停止排序。

    def bubble_sort_flag(list):
        length = len(list)
        for index in range(length):
            # 标志位
            flag = True
            for j in range(1, length - index):
                if list[j - 1] > list[j]:
                    list[j - 1], list[j] = list[j], list[j - 1]
                    flag = False
            if flag:
                # 没有发生交换,直接返回list
                return list
        return list

    选择排序

    原理

    选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理大致是将后面的元素最小元素一个个取出然后按顺序放置。

    步骤

    1. 在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,
    2. 再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。
    3. 重复第二步,直到所有元素均排序完毕。

    代码

    def selection_sort(list):
        n=len(list)
        for i in range (0,n):
            min = i
            for j in range(i+1,n):
                if list[j]<list[min]:
                    min=j
                    list[min],list[i]=list[i],list[min]
        return list

    插入排序

    原理

    插入排序(Insertion Sort)是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。

    步骤

    1. 从第一个元素开始,该元素可以认为已经被排序
    2. 取出下一个元素,在已经排序的元素序列中从后向前扫描
    3. 如果该元素(已排序)大于新元素,将该元素移到下一位置
    4. 重复步骤3,直到找到已排序的元素小于或者等于新元素的位置
    5. 将新元素插入到该位置后
    6. 重复步骤2~5

    代码

    def insert_sort(list):
        n = len(list)
        for i in range(1, n):
            # 后一个元素和前一个元素比较
            # 如果比前一个小
            if list[i] < list[i - 1]:
                # 将这个数取出
                temp = list[i]
                # 保存下标
                index = i
                # 从后往前依次比较每个元素
                for j in range(i - 1, -1, -1):
                    # 和比取出元素大的元素交换
                    if list[j] > temp:
                        list[j + 1] = list[j]
                        index = j
                    else:
                        break
                # 插入元素
                list[index] = temp
        return list

    希尔排序

    原理

    希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本。希尔排序是非稳定排序算法。
    希尔排序是基于插入排序的以下两点性质而提出改进方法的:
    插入排序在对几乎已经排好序的数据操作时,效率高,即可以达到线性排序的效率
    但插入排序一般来说是低效的,因为插入排序每次只能将数据移动一位。

    步骤

    每次以一定步长(就是跳过等距的数)进行排序,直至步长为1.

    代码

    def shell_sort(list):
        n = len(list)
        # 初始步长
        gap = n // 2
        while gap > 0:
            for i in range(gap, n):
                # 每个步长进行插入排序
                temp = list[i]
                j = i
                # 插入排序
                while j >= gap and list[j - gap] > temp:
                    list[j] = list[j - gap]
                    j -= gap
                list[j] = temp
            # 得到新的步长
            gap = gap // 2
        return list

    步长使用的是Donald Shell的建议,另外步长还可以使用Sedgewick提出的(1, 5, 19, 41, 109,…)。
    也可以使用斐波那契数列除去0和1将剩余的数以黄金分区比的两倍的幂进行运算得到的数列。

    归并排序

    原理

    归并操作(归并算法),指的是将两个已经排序的序列合并成一个序列的操作。归并排序算法依赖归并操作。

    步骤

    1.迭代法

    1. 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
    2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置
      3.比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
    3. 重复步骤3直到某一指针到达序列尾
    4. 将另一序列剩下的所有元素直接复制到合并序列尾

    递归法

    假设序列共有n个元素:

    1. 将序列每相邻两个数字进行归并操作,形成 {displaystyle floor(n/2)} floor(n/2)个序列,排序后每个序列包含两个元素
    2. 将上述序列再次归并,形成 {displaystyle floor(n/4)} floor(n/4)个序列,每个序列包含四个元素
    3. 重复步骤2,直到所有元素排序完毕

    代码

    # 递归法
    def merge_sort(list):
        # 认为长度不大于1的数列是有序的
        if len(list) <= 1:
            return list
        # 二分列表
        middle = len(list) // 2
        left = merge_sort(list[:middle])
        right = merge_sort(list[middle:])
        # 最后一次合并
        return merge(left, right)
    # 合并 def merge(left, right): l,r=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 reslut +=left[l:] result+=right[r:] return result

    鄙人不才,不知归并排序的迭代法如何用Python实现,望指教。

    快速排序

    原理

    快速排序使用分治法(Divide and conquer)策略来把一个序列(list)分为两个子序列(sub-lists)。

    步骤

    1. 从数列中挑出一个元素,称为”基准”(pivot),
    2. 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。
    3. 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。

    代码

    普通版

    def quick_sort(list):
        less = []
        pivotList = []
        more = []
        # 递归出口
        if len(list) <= 1:
            return list
        else:
            # 将第一个值做为基准
            pivot = list[0]
            for i in list:
                # 将比急转小的值放到less数列
                if i < pivot:
                    less.append(i)
                # 将比基准打的值放到more数列
                elif i > pivot:
                    more.append(i)
                # 将和基准相同的值保存在基准数列
                else:
                    pivotList.append(i)
            # 对less数列和more数列继续进行排序
            less = quick_sort(less)
            more = quick_sort(more)
            return less + pivotList + more

    下面这段代码出自《Python cookbook 第二版》传说中的三行实现python快速排序。

    def qsort(arr):
        if len(arr) <= 1:
            return arr
        else:
            pivot = arr[0]
            less = [x for x in arr[1:] if x < pivot]
            greater = [x for x in arr[1:] if x >= pivot]
            return qsort(less) + [pivot] + qsort(greater)

    当然还有一行语法糖版本:

    qs = lambda xs : ( (len(xs) <= 1 and [xs]) or [ qs( [x for x in xs[1:] if x < xs[0]] ) + [xs[0]] + qs( [x for x in xs[1:] if x >= xs[0]] ) ] )[0]

    是不是感受到了Python的魅力?

    堆排序

    原理

    堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。

    步骤

    1. 创建最大堆:将堆所有数据重新排序,使其成为最大堆
    2. 最大堆调整:作用是保持最大堆的性质,是创建最大堆的核心子程序
    3. 堆排序:移除位在第一个数据的根节点,并做最大堆调整的递归运算

    代码

    def heap_sort(list):
        # 创建最大堆
        for start in range((len(list) - 2) // 2, -1, -1):
            sift_down(list, start, len(list) - 1)
    
        # 堆排序
        for end in range(len(list) - 1, 0, -1):
            list[0], list[end] = list[end], list[0]
            sift_down(list, 0, end - 1)
        return list
    
    # 最大堆调整
    def sift_down(lst, start, end):
        root = start
        while True:
            child = 2 * root + 1
            if child > end:
                break
            if child + 1 <= end and lst[child] < lst[child + 1]:
                child += 1
            if lst[root] < lst[child]:
                lst[root], lst[child] = lst[child], lst[root]
                root = child
            else:
                break

    计数排序

    原理

    当输入的元素是n个0到k之间的整数时,它的运行时间是Θ(n + k)。计数排序不是比较排序,排序的速度快于任何比较排序算法。

    由于用来计数的数组C的长度取决于待排序数组中数据的范围(等于待排序数组的最大值与最小值的差加上1),这使得计数排序对于数据范围很大的数组,需要大量时间和内存。例如:计数排序是用来排序0到100之间的数字的最好的算法,但是它不适合按字母顺序排序人名。但是,计数排序可以用在基数排序算法中,能够更有效的排序数据范围很大的数组。

    步骤

    1. 找出待排序的数组中最大和最小的元素
    2. 统计数组中每个值为i的元素出现的次数,存入数组 C 的第 i 项
    3. 对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加)
    4. 反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1

    代码

    def count_sort(list):
        min = 2147483647
        max = 0
        # 取得最大值和最小值
        for x in list:
            if x < min:
                min = x
            if x > max:
                max = x
        # 创建数组C
        count = [0] * (max - min +1)
        for index in list:
            count[index - min] += 1
        index = 0
        # 填值
        for a in range(max - min+1):
            for c in range(count[a]):
                list[index] = a + min
                index += 1
        return list

    桶排序

    桶排序原理:

    桶排序也叫计数排序,简单来说,就是将数据集里面所有元素按顺序列举出来,然后统计元素出现的次数。最后按顺序输出数据集里面的元素。

    排序过程如下:

    一、初始化桶的大小

    把数据集里面每一个元素当作一个桶,由上面问题看出,原始数据范围在0--9之间,因此我就需要有10个桶,如下图

    第一行为初始化计数为0,第二行为各个元素。

    二、计数

    接下来读入第一原始数据为6,则在下标为6的桶中增加1,如下图:

    再读入下一个原始数据为8,则在下标为8的桶中增加1,如下图: 

    以此类推,最后遍历完所有原始数据时,10个桶的计数如下图:

    三、输出数据

    在完成原始数据的遍历计数后,接下来遍历各个桶,输出数据:

    元素0计数为1,则输出0,

    元素1计数为2,则输出1 1,

    元素2计数为1,则输出2,

    元素3计数为1,则输出3,

    元素4计数为1,则输出4,

    元素5计数为1,则输出5,

    元素6计数为1,则输出6,

    元素7计数为0,则不输出元素,

    元素8计数为1,则输出8,

    元素9计数为1,则输出9,

    最后结果输出为:0, 1, 1, 2, 3, 4, 5, 6, 8, 9

    代码实现

    由上述原理可以看出,桶排序需要以下三个步骤:

    1.申请一个包含所有元素的数组,并初始化。

    2.遍历原始数据,并计数。

    3.遍历计数完成后的各个数组元素,输出数据。

    以下是python代码的实现:

    class BucketSort(object):
        '''
        self.datas:       要排序的数据列表
        self.bucketSize:  水桶的大小(数据集的范围,如bucketSize=10,
                          则表示数据集的范围为0-9)
        self.result:      保存排序后的结果
        self.bucket:      代表水桶,指数据集内的所有元素
        _sort():          排序函数
        show():           输出结果的函数
    
        用法:
        BucketSort(datas, size)   或者BucketSort(datas),size的默认值为100
    
        BucketSort(datas)._sort() 这样就是开始排序
        BucketSort(datas).show()  这样就可以把排序后的结果输出
        '''
        def __init__(self, datas, size=100):
            self.datas = datas
            self.bucketSize = size
            self.result = [0 for i in range(len(datas))]
            self.bucket = [0 for i in range(self.bucketSize)]
    
        def _sort(self):
            # 读入各个元素,并在对应的位置统计,当bucket里的元素不为0
            # 就保存到result里面
            for num in self.datas:
                self.bucket[num] += 1
            j = 0
            for i in range(self.bucketSize):
                while(self.bucket[i]):
                    self.result[j] = i
                    self.bucket[i] -= 1
                    j += 1
    
        def show(self):
            print "Resutl is:",
            for i in self.result:
                print i,
            print ''
    
    
    if __name__ == '__main__':
        try:
            size = raw_input("Please input size(default=100):")
            if size:
                size = int(size)
            datas = raw_input('Please input some number:')
            datas = datas.split()
            datas = [int(datas[i]) for i in range(len(datas))]
        except Exception:
            pass
        if size:
            bks = BucketSort(datas, size)
        else:
            bks = BucketSort(datas)
        bks._sort()
        bks.show()

    基数排序

    基数排序的映射方法是按照数据特定位上数字放入不同的桶中, 将序列中的数组按照个位放到不同的桶中,然后按照个位0~9的顺序依次取出,再按照十位放到不同的桶中,…….直到达到数据的最高位,最后一次将数据取出时,排序完成,例如:
    在这里插入图片描述
    实现代码:

    def radix_sort(arr):
        def compare(basis,arr):#以数组basis为依据的插入排序
            for i in range(1,len(basis)):
                if basis[i]<basis[i-1]:
                    for j in range(i,0,-1):
                        if basis[j-1]>basis[j]:
                            basis[j],basis[j-1]=basis[j-1],basis[j]
                            arr[j],arr[j-1]=arr[j-1],arr[j]
                        else:
                            break
        upnum=max(arr)
        tmp=1
        while(tmp<upnum):
            basis=[num//tmp%10 for num in arr]
            compare(basis,arr)
            tmp*=10
        return arr

    注意:基数排序在桶内排序时,一定要采用稳定的排序方法,这样才可以利用之前的排序结果,否则之前的排序是无意义的。

  • 相关阅读:
    api接口出现Provisional headers are shown,
    yii消息提示扩展
    php中普通类 接口类 抽象类 浅谈
    读韭菜的自我修养
    读李笑来的《把时间当作朋友》
    Spring整合Struts2框架的第二种方式(Action由Spring框架来创建)(推荐大家来使用的)
    Spring整合Struts2框架的第一种方式(Action由Struts2框架来创建)。在我的上一篇博文中介绍的通过web工厂的方式获取servcie的方法因为太麻烦,所以开发的时候不会使用。
    Spring框架整合Struts2框架的传统方法
    ecplice中代码使用快捷键无法格式化,使用其他方法将代码格式化的步骤
    SSH框架整合jar包时的注意事项
  • 原文地址:https://www.cnblogs.com/lpyy/p/13497875.html
Copyright © 2011-2022 走看看