Python系列之简单算法

排序算法可以分为内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存。

见的内部排序算法有:冒泡排序、插入排序、希尔排序、选择排序、归并排序、快速排序、堆排序、基数排序等。

文将依次介绍上述八大排序算法。

冒泡排序

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

冒泡排序示意图

  • 算法步骤:

    • 1)比较相邻的元素。如果第一个比第二个大,就交换他们两个。

    • 2)对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。

    • 3)针对所有的元素重复以上的步骤,除了最后一个。

    • 4)持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

  • 实例

    from random import randint
    li = [randint(-10, 1000) for _ in range(10)]  # 生成10个-10到100之间的随机数字
    for n in range(len(li)):  # 循环列表中所有的元素
        for i in range(len(li) - 1 - n):  # 每次求最大值,放到最后,不对比匹配到最大的那个值
            if li[i] > li[i + 1]:  # 当前值大于当前值的下一个
                li[i], li[i + 1] = li[i + 1], li[i]  # 位置调换一下
    print(li)  # 输出

插入排序

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

插入排序示意图

  • 算法步骤:

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

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

  • 实例

    from random import randint
    List = [randint(-10, 10000) for _ in range(10)]  # 生成10个-10到100之间的随机数字
    print(List)
    for Index in range(1, len(List)):
        I = Index  # 刚开始往左边走的第一个位置
        val = List[I]  # 先把当前值存起来
        while I > 0 and val < List[I - 1]:
            List[I] = List[I - 1]
            I -= 1
        List[I] = val
    print(List)

希尔排序

希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本。但希尔排序是非稳定排序算法。

希尔排序示意图

  • 希尔排序是基于插入排序的以下两点性质而提出改进方法的:

插入排序在对几乎已经排好序的数据操作时, 效率高, 即可以达到线性排序的效率
但插入排序一般来说是低效的, 因为插入排序每次只能将数据移动一位
希尔排序的基本思想是:先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序。

  • 算法步骤:

    • 1)选择一个增量序列t1,t2,…,tk,其中ti>tj,tk=1;

    • 2)按增量序列个数k,对序列进行k 趟排序;

    • 3)每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m 的子序列,分别对各子表进行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。

from random import randint
List = [randint(-20, 100) for _ in range(10)]  # 生成10个-10到100之间的随机数字
print(List)
Step = int(len(List) / 2)  # 将列表切分成两半
while Step >= 1:  # 如果切分的值大于等于1
    for Index in range(len(List) - Step):
        if List[Index] > List[Index + Step]:
            List[Index], List[Index + Step] = List[Index + Step], List[Index]  # 位置交换
    Step = int(Step / 2)
else:  # 进入插入排序
    for Index in range(len(List)):
        while Index > 0 and List[Index] < List[Index - 1]:
            List[Index], List[Index - 1] = List[Index - 1], List[Index]
            Index -= 1
print(List)

选择排序

选择排序(Selection sort)也是一种简单直观的排序算法。

选择排序示意图

  • 算法步骤:

    • 1)首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置

    • 2)再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。

    • 3)重复第二步,直到所有元素均排序完毕。

  • 实例

from random import randint
List = [randint(-10, 1000) for _ in range(100000)]  # 生成10个-10到100之间的随机数字
for Index in range(len(List)):  # 循环列表所有索引
    Sam = Index  # 最小值的索引
    for I in range(Index, len(List)):  # 每次循环都不取上次取到的最大值
        if List[Sam] > List[I]:  # 列表的某个值大于当前小循环的值
            Sam = I  # 最小值的所以赋值给sma
    List[Index], List[Sam] = List[Sam], List[Index]  # 交换位置
print(List)

归并排序

归并排序(Merge sort)是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。

归并排序示意图

  • 算法步骤:

      1. 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
      1. 设定两个指针,最初位置分别为两个已经排序序列的起始位置
      1. 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
      1. 重复步骤3直到某一指针达到序列尾
      1. 将另一序列剩下的所有元素直接复制到合并序列尾
import time
from random import randint
def merge(left, right):
    i, j = 0, 0
    result = []
    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 += left[i:]
    result += right[j:]
    return result

def merge_sort(lists):
    # 归并排序
    if len(lists) <= 1:
        return lists
    num = int(len(lists) / 2)
    left = merge_sort(lists[:num])
    right = merge_sort(lists[num:])
    return merge(left, right)

List = [randint(0, 1000) for _ in range(100000)]  # 生成10个-10到100之间的随机数字
data = time.time()
print(merge_sort(List))
print(data)
print(time.time() - data)

快速排序

快速排序是由东尼·霍尔所发展的一种排序算法。在平均状况下,排序 n 个项目要Ο(n log n)次比较。在最坏状况下则需要Ο(n2)次比较,但这种状况并不常见。事实上,快速排序通常明显比其他Ο(n log n) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构上很有效率地被实现出来。

快速排序示意图

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

  • 算法步骤:

    • 1 从数列中挑出一个元素,称为 “基准”(pivot),

    • 2 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。

    • 3 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。

递归的最底部情形,是数列的大小是零或一,也就是永远都已经被排序好了。虽然一直递归下去,但是这个算法总会退出,因为在每次的迭代(iteration)中,它至少会把一个元素摆到它最后的位置去。

  • 实例
# -*- coding:utf-8 -*-
# AUTHER   @ Alvin
from random import randint
def QuickSort(myList,start,end):
    #判断low是否小于high,如果为false,直接返回
    if start < end:
        i,j = start,end
        #设置基准数
        base = myList[i]

        while i < j:
            #如果列表后边的数,比基准数大或相等,则前移一位直到有比基准数小的数出现
            while (i < j) and (myList[j] >= base):
                j = j - 1

            #如找到,则把第j个元素赋值给第个元素i,此时表中i,j个元素相等
            myList[i] = myList[j]

            #同样的方式比较前半区
            while (i < j) and (myList[i] <= base):
                i = i + 1
            myList[j] = myList[i]
        #做完第一轮比较之后,列表被分成了两个半区,并且i=j,需要将这个数设置回base
        myList[i] = base

        #递归前后半区
        QuickSort(myList, start, i - 1)
        QuickSort(myList, j + 1, end)
    return myList


myList = li = [randint(0, 1000) for _ in range(100000)]
print("Quick Sort: ")
QuickSort(myList,0,len(myList)-1)
print(myList)

堆排序

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

堆排序示意图

  • 算法步骤:

    • 1)创建一个堆H[0..n-1]

    • 2)把堆首(最大值)和堆尾互换

    • 3)把堆的尺寸缩小1,并调用shift_down(0),目的是把新的数组顶端数据调整到相应位置

    • 4) 重复步骤2,直到堆的尺寸为1

from random import randint
def sift_down(arr, start, end):
    root = start
    while True:
        # 从root开始对最大堆调整
        child = 2 * root + 1
        if child > end:
            break

        # 找出两个child中交大的一个
        if child + 1 <= end and arr[child] < arr[child + 1]:
            child += 1

        if arr[root] < arr[child]:
            # 最大堆小于较大的child, 交换顺序
            arr[root], arr[child] = arr[child], arr[root]

            # 正在调整的节点设置为root
            root = child
        else:
            # 无需调整的时候, 退出
            break


def heap_sort(arr):
    # 从最后一个有子节点的孩子还是调整最大堆
    first = len(arr) // 2 - 1
    for start in range(first, -1, -1):
        sift_down(arr, start, len(arr) - 1)

    # 将最大的放到堆的最后一个, 堆-1, 继续调整排序
    for end in range(len(arr) -1, 0, -1):
        arr[0], arr[end] = arr[end], arr[0]
        sift_down(arr, 0, end - 1)

def main():
    l = [randint(0, 1000) for _ in range(100000)]
    print(l)
    heap_sort(l)
    print(l)


if __name__ == "__main__":
    main()

更新中。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值