排序(学习笔记)

目录:

1. 冒泡排序

2. 选择排序

3. 插入排序

4. 希尔排序

5. 归并排序

6. 快速排序


1.冒泡排序

冒泡排序需要遍历列表 

 

如上图所示,第一轮遍历列表时,我们发现较大的数被放在前面,显然不合顺序(从大到小),我们两个元素两个元素遍历,比较两个数,发现大的就往后排,小的就往前面排 。如果列表中有n个元素,那么第一轮遍历就要比较n-1对元素

第二轮遍历的时候,最大值已经在正确位置上了,还剩n-1个元素需要排列,再遍历交换下去,一直到排序完毕。

给出一段python小代码

#冒泡排序
def bubblesort(alist):
    for passnum in range(len(alist) - 1):
        for i in range(passnum):
            if alist[i]>alist[i+1]:
                temp = alist[i]
                alist[i] = alist[i+1]
                alist[i+1] = temp

我们发现,这段代码的时间复杂度时O(n^2),容易超时,所以我们要对代码进行修改

由于冒泡排序要遍历列表中未排序的部分,因此它具有其他排序算法没有用途。特别是如果在一轮遍历中没有发生元素交换,就可以确定列表已经有序。可以修改冒泡排序函数,使其在遇到这种情况时提前终止。对于只需要遍历几次的列表,冒泡排序可能有优势,因为它能判断出有序列表并终止排序过程。这样的排序被称为短冒泡
#冒泡排序(改进后)
def shortbubblesort(alist):
    exchanges  = True
    passnum = len(alist) - 1
    while passnum > 0 and exchanges:
        exchanges = False
        for i in range(passnum):
            if alist[i] > alist[i+1]:
                exchanges = True
                alist[i+1], alist[i] = alist[i], alist[i+1]
        passnum = passnum -1

2. 选择排序

 选择排序在冒泡排序的基础上进行了改进,每次遍历列表只进行一次交换,就是找出列表里还未排序的数中的最大值,把它扔到后面去

 如上图所示

python代码

#选择排序
def selectionsort(alist):
    for i in range(len(alist)-1, 0, -1):
        max = 0
        for j in range(1, i+1):
            if alist[j] > alist[max]:  #把最大值往后面放
                max = j
        alist[i], alist[max] = alist[max], alist[i]

3.插入排序

插入排序是先将位置0的元素当作单元素的有序子列表,从第一个元素到第n-1个元素,每一轮都将当前元素与上面这个单元素的有序子列表进行比较,小的往前排,大的往后排。插入后,列表扩展,下一个元素再与这两个元素进行比较。

python代码

#插入排序
def insertsort(alist):
    for i in range(len(alist)):
        num = alist[i]
        position = i
        while position>1 and alist[position-1]>num:
            alist[position] = alist[position - 1]
            position -= 1
        alist[position] = num

 4. 希尔排序、

 

希尔排序 也称 递减增量排序 ,它对插入排序做了改进,将列表分成数个子列表,并对每一个子列表应用插入排序。如何切分列表是希尔排序的关键—— 并不是连续切分,而是使用增量
(也就是步长 )选取所有间隔为i的元素组成子列表
将分好的子列表中的元素如下图灰色方块先进行自我排序

将排序好的子列表中的元素一一往下放 

 

最后将填充完元素的列表进行再一次排序 

#希尔排序
def shellsort(alist):
    sublen = len(alist) // 2  #子列表长度
    while sublen > 0:
        for start in range(sublen):
            gapinsertion(alist, start, sublen)
        print("经过增量为", sublen, "后,这个列表是", alist)
        sublen = sublen // 2
def gapinsertion(alist, sta, gap):
    for i in range(sta + gap, len(alist), gap):
        cvalue = alist[i]
        pos = i  #将当前值的位置设置为i
        while pos >= gap and alist[pos - gap]>cvalue:  #循环直到当前值再子列表的正确位置上
            alist[pos] = alist[pos - gap] #把当前位置的元素向右移动
            pos = pos - gap
        alist[pos] = cvalue
alist = [54, 26, 93, 17, 77, 31, 44, 55]
shellsort(alist)

 这段代码的结果为


 5. 归并排序

归并排序本质上是一种递归算法,每次将一个列表一分为二,如果列表为空或者只有一个元素,那默认为有序,如果有两个及以上元素的话,先将列表一分为二,对两部分进行递归调用归并排序,等所有排序完成时,把这些元素一一合并就🆗了

 

python代码

#归并排序
def mergesort(alist):
    print("二分", alist)  # 打印当前二分的列表
    if len(alist) > 1:
        mid = len(alist) // 2  # 计算中间索引位置
        left = alist[:mid]  # 分割左半边列表
        right = alist[mid:]  # 分割右半边列表

        mergesort(left)  # 递归调用归并排序函数对左半边进行排序
        mergesort(right)  # 递归调用归并排序函数对右半边进行排序

        i = 0
        j = 0
        k = 0
        while i < len(left) and j < len(right):
            if left[i] < right[j]:
                alist[k] = left[i]  # 如果左半边元素小于右半边元素,将左半边的元素放到合并后的列表中
                i = i+1
            else:
                alist[k] = right[j]  # 如果右半边元素小于左半边元素,将右半边的元素放到合并后的列表中
                j = j+1
            k = k+1

        while i < len(left):
            alist[k] = left[i]  # 如果左半边还有元素没有加入到合并后的列表中,直接将它们加入到列表中
            i = i + 1
            k = k + 1
        while j < len(right):
            alist[k] = right[j]  # 如果右半边还有元素没有加入到合并后的列表中,直接将它们加入到列表中
            j = j + 1
            k = k + 1
    print("merging ", alist)  # 打印当前合并后的列表
b = [14, 23, 56, 97, 100, 89, 45, 67]
mergesort(b)

 输出结果为

二分 [14, 23, 56, 97, 100, 89, 45, 67]
二分 [14, 23, 56, 97]
二分 [14, 23]
二分 [14]
merging  [14]
二分 [23]
merging  [23]
merging  [14, 23]
二分 [56, 97]
二分 [56]
merging  [56]
二分 [97]
merging  [97]
merging  [56, 97]
merging  [14, 23, 56, 97]
二分 [100, 89, 45, 67]
二分 [100, 89]
二分 [100]
merging  [100]
二分 [89]
merging  [89]
merging  [89, 100]
二分 [45, 67]
二分 [45]
merging  [45]
二分 [67]
merging  [67]
merging  [45, 67]
merging  [45, 67, 89, 100]
merging  [14, 23, 45, 56, 67, 89, 97, 100]

6. 快速排序 

快速排序首先需要选出一个基准值,基准值的作用是帮助切分列表,基准值的位置也叫做分割点。

接着进行划分操作,比基准值大的放右边,比基准值小的放左边

在本例中,我们选取54作为第一个基准值

 

首先加大 leftmark ,直到遇到一个大于基准值的元素。然后减小 rightmark ,直到遇到一个小于基准值的元素。这样一来,就找到两个与最终的分割点错序的元素。本例中,这两个元素就是93 和 20 。互换这两个元素的位置,然后重复上述过程。
当 rightmark 小于 leftmark 时,过程终止。此时,rightmark的位置就是分割点。将基准值与当前于分割点的元素互换,即可使基准值位于正确位置。 分割点左边的所有元素都小于基准值,右边的所有元素都大于基准值。因此,可以在分割点处将列表一分为二,并针对左右两部分递归调用快速
排序函数。

python代码

# 定义快速排序函数
def quicksort(alist):
    # 调用快速排序辅助函数
    quicksortHelper(alist, 0, len(alist) - 1)


# 定义快速排序辅助函数
def quicksortHelper(alist, first, last):
    # 判断是否需要排序
    if first < last:
        # 将列表分为两部分
        splitpoint = partition(alist, first, last)
        # 对左侧子列表进行快速排序
        quicksortHelper(alist, first, splitpoint - 1)
        # 对右侧子列表进行快速排序
        quicksortHelper(alist, splitpoint + 1, last)


# 定义分区函数
def partition(alist, first, last):
    # 选择第一个元素作为基准值
    pivotvalue = alist[first]

    # 初始化左侧和右侧子列表的边界
    leftmark = first + 1
    rightmark = last
    # 标记是否完成排序
    done = False
    # 开始排序
    while not done:
        # 找到左侧子列表中大于基准值的元素
        while leftmark <= rightmark and alist[leftmark] <= pivotvalue:
            leftmark = leftmark + 1
        # 找到右侧子列表中小于基准值的元素
        while alist[rightmark] >= pivotvalue and rightmark >= leftmark:
            rightmark = rightmark - 1
        # 判断是否完成分区
        if rightmark < leftmark:
            done = True
        else:
            # 交换左侧子列表中大于基准值的元素和右侧子列表中小于基准值的元素
            alist[leftmark], alist[rightmark] = alist[rightmark], alist[leftmark]
    # 将基准值放到分区的位置,并返回分区的位置
    alist[first], alist[rightmark] = alist[rightmark], alist[first]
    return rightmark

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值