排序python代码_经典排序算法-附python代码

参考链接:

1.排序算法是什么?

排序算法是《数据结构与算法》中最基本的算法之一。

排序算法可以分为内部排序和外部排序。

内部排序是数据记录在内存中进行排序。

外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存。

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

Algorithm算法

Time Complexity时间复杂度

Space Complexity空间复杂度

Best

Average

Worst

Worst

稳定性

QuickSort 快排

O(n log(n))

O(n log(n))

O(n^2)

O(n log(n))

不稳定

MergeSort 归并

O(n log(n))

O(n log(n))

O(n log(n))

O(n)

稳定

BubbleSort 冒泡

O(n)

O(n^2)

O(n^2)

O(1)

稳定

InsertionSort 插入

O(n)

O(n^2)

O(n^2)

O(1)

稳定

SelectionSort 选择

O(n^2)

O(n^2)

O(n^2)

O(1)

不稳定

2.时间复杂度

平方阶

O(n^2)

插入

选择

冒泡

线性对数阶

O(n log(n))

快排

归并

堆排序

线性阶

O(n)

基数排序

桶排序

箱排序

3.稳定性

稳定

冒泡

插入

归并

基数

不稳定

选择

快排

希尔

堆排序

4.冒泡排序

Bubble Sort是一种简单的排序算法,它反复遍历列表,比较相邻的两个元素,如果顺序不对,则交换它们。该算法是一种比较排序,它是以较小或较大元素“冒泡”到列表顶部的方式命名的。虽然算法很简单,但对于大多数问题来说,它太慢了。

4.1-算法步骤解析

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

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

针对所有的元素重复以上的步骤,除了已经排序好的。

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

4.2-图解

4.3-代码

"""

def bubble_sort(alist):

# 方式1

for i in range(len(alist)-1):

for j in range(len(alist)-1-i):

if alist[j] > alist[j+1]:

alist[j], alist[j+1] = alist[j+1], alist[j]

return alist

"""

def bubble_sort(alist):

# 方式2

for i in range(len(alist)-1):

flag = True

for j in range(len(alist)-1-i):

if alist[j] > alist[j + 1]:

alist[j], alist[j + 1] = alist[j + 1], alist[j]

flag = False

if flag is True:

break

return alist

def test_bubble_sort():

import random

alist = list(range(10))

random.shuffle(alist)

result = bubble_sort(alist)

print(result)

test_bubble_sort()

4.4-解析

一、方式1

n-1轮循环

在每一轮循环中,不管相邻元素的大小是否已经有序。都会进行大小对比。

递归的不断判断,经过n-1轮查询后,得到最终结果。

二、方式2

n-1轮查询

每一轮查询,相邻元素判断大小之前,立flag:指默认 不需要替换。每一次进入循环后,如果有满足条件相邻元素前面大于后面,那么便交换数据,且更改flag为false,直到本轮循环结束。

下一轮循环开始,初始化flag,当本轮循环如果所有相邻元素都满足顺序(升序或者降序),便跳出本轮循环。

当初始默认参数是(升序或降序时),经过n轮外循环,内循环只执行break1次,便跳出循环,最优时间复杂度便为O(n)。最坏情况,便是内循环也需要执行n次,最坏时间复杂度为O(n^2)。

5.选择排序

Selection Sort是一种就地比较排序算法,时间复杂度O(n^2) ,因此它在大型列表中效率比较低下。

但该算法以其简单性著称,在某些情况下,它比更复杂的算法具有性能优势,特别是当辅助内存有限的时候。

Selection Sort将输入列表分为两部分:已排序子列表(在列表的前端),以及待排序子列表。最初,已排序子列表是空的,未排序子列表是整个列表。算法通过在待排序子列表中查找最小的元素,将其与最左边的待排序元素交换,并将已排序子列表边界向右移动一个元素来继续。

5.1-算法步骤解析

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

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

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

5.2-图解

5.3-代码

def select_sort(alist):

for i in range(len(alist)):

min_num = i

for j in range(i+1, len(alist)):

if alist[j] < alist[min_num]:

min_num = j

alist[i], alist[min_num] = alist[min_num], alist[i]

return alist

5.4-解析

循环从未排序列表中找到最小值(和已经排序列表的最后一个值对比大小),和已排序列表最后一个值交换。

初始列表中找第一个最小值,设置默认初值,所以需要外循环len(alist)

设置最小值默认处置为外循环的变量i

内循环选择最小(和初始值对比最小)的元素,如果找到则更新最小值的下标,直到本轮内循环结束,将最小值与初始值交换;如果直到本轮内循环结束还未找到,开始下一轮外循环。

6.插入排序

Insertion Sort是一种简单的排序算法,它只需要一次遍历即可生成最终排序的数组。它在大列表中的效率比更高级的算法低,但是,它有以下几个优点:

一、实现简单,几行代码即可完成。

二、对(相当)小的数据集很有效。

三、在实践中比其他简单的O(n^2) 算法更有效。

四、自适应,当输入中的每个元素离其最终位置不超过 k时,时间复杂度仅 O(kn)。

五、稳定,不改变具有相等值的元素的相对顺序。

六、空间复杂度低,仅 O(1) 。

七、在线算法,可以边读边排序,不需要先读取全部数组。

6.1-算法步骤解析

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

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

6.2-图解

6.3-代码

def insert_sort(alist):

for i in range(1, len(alist)):

for j in range(i, 0, -1):

if alist[j] < alist[j-1]:

alist[j], alist[j-1] = alist[j-1], alist[j]

else:

break

return alist

6.4-解析

抽象分析:

​还是当我们玩牌时,怎么整理牌最快?

我们以第一张牌为初始值

第一轮循环判断,判断第2张牌是否比第一张大,没有则交换顺序,有则位置不动,跳出本轮循环。

第二轮循环,判断第3张牌是后比第二张大,没有则交换顺序,有则位置不动;在与第1张牌对比,如果没有第1张牌大,则继续交换顺序,有则位置不动。跳出本轮循环。

以此类推,经过n-1轮循环后,得到最终结果。

时间复杂度:

best:O(n)

worst:O(n^2)

关键在于代码中的else, break。当列表顺序已经是升序或降序了,比较两个元素大小时不满足交换条件,说明该元素比前一个元素大或者小,由于前一个元素比它之前的所有元素都大或小,所有便可以跳出本次循环。相当于经过n轮循环,每次循环都只执行一次break,所以最优时间复杂度为O(n)。

7.归并排序

Merge sort是一种高效、通用、基于比较的排序算法,该算法利用了分治的思想,将规模较大的排序问题化归到较小的规模上解决。

Merge sort的步骤如下:

一、将未排序的列表划分为两个元素数量相同的子数组。

二、排序这两个子数组,再将它们进行合并。

7.1-定义

归并排序是分而治之算法,先分,在组合(组合前进行对比大小排序)。主要两个步骤。

连续划分未排序列表,直到有N个子列表,其中每个子列表有1个“未排序”元素,N是原始数组中的元素数。

重复合并,即一次将两个子列表合并在一起,生成新的排序子列表,直到所有元素完全合并到一个排序数组中。

7.2-图解

7.3-代码实现

# coding:utf-8

def merge_sort(alist):

num = len(alist)

if num == 1:

return alist

mid = num // 2

left_li = merge_sort(alist[:mid])

right_li = merge_sort(alist[mid:])

left_pointer, right_pointer = 0, 0

result = []

while left_pointer < len(left_li) and right_pointer < len(right_li):

if left_li[left_pointer] < right_li[right_pointer]:

result.append(left_li[left_pointer])

left_pointer += 1

else:

result.append(right_li[right_pointer])

right_pointer += 1

result += left_li[left_pointer:]

result += right_li[right_pointer:]

return result

result2 = merge_sort([1, 8, 7, 3, 5, 6, 4, 2, 9])

print(result2)

7.4-解析

拆分:直到拆分结果是独立个体,个体长度为1时,准备合并。

设置全局变量接受合并后的结果

设定左指针和右指针的初始值为0

设定循环判断条件左右指针长度必须小于拆分后的左右列表的长度(and与条件)。(防止列表取值超出指针可索引范围),当不符合条件时,跳出循环。

合并:根据指针值对左右列表的值对比大小。小的先存入全局变量合并结果内,且相应的拆分列表指针+=1

当某一个拆分列表的值全部存储合并结果后,该拆分列表的指针值达到最大可索引值。另外一个拆分列表,在5步中对比大小时肯定比已经存入合并结果的值大,且该拆分列表也是有序状态的,所以可以直接在已经合并结果的基础上,加上该拆分列表的指针值到最后的所有元素。(注意:当某一个拆分列表指针值达到最大时,取该指针值到最后的结果是空列表,例如a = [0,1,2,3],而a[4:]的值是空列表)

返回结果

时间复杂度:

best:O(n log(n))

worst:O(n log(n))

7.5-代码运行步骤

def merge_sort([1, 8, 7, 3, 5, 6, 4, 2]):

mid = 4

left_li = def merge_sort([1, 8, 7, 3]):

mid = 2

left_li = def merge_sort([1, 8]):

mid = 1

left_li = def merge_sort([1]):

return alist # 到这里第7含的merge函数执行完毕(得到结果left_li=[1]),第7行的函数知识第5含函数的一含代码,继续向下执行.

right_li = def merge_sort([8]):

return alist # 同第8行代码一样,第9含的函数也是第5含代码函数的一行代码,得到结果right_li=[8]),继续向下执行.

# 左右指针=0

# result = []

# 循环判断

result= [1], 左指针 = 1, 右指针 = 0

result = [1] + [] = [1]

result = [1] + [8] = [1,8]

return result # 到这里第5行代码执行完毕,得到结果left_li = [1, 8]

right_li = def merge_sort([7, 3]):

mid = 1

left_li = [7]

right_li = [3]

# 左右指针=0

# result = []

# 循环判断

result= [3], 右指针 = 1, 左指针 = 0

result = [3] + [] = [3]

result = [3] + [7] = [3,7]

return result # 到这里第18行代码执行完毕,得到结果right_li = [3, 7]

left_li = [1, 8]

right_li = [3, 7]

# 左右指针=0

# result = []

# 循环判断

result= [1], 左指针 = 1, 右指针 = 0

result= [1,3], 右指针 = 1, 左指针 = 1

result= [1,3,7], 右指针 = 2, 左指针 = 1

result = [1,3,7] + [8] = [1,3,7,8]

result = [1,3,7,8] + [] = [1,3,7,8]

return result # 到这里第3行代码执行完毕,得到结果left_li = [1,3,7,8]

right_li = def merge_sort([5,6,4,2]):

# 执行步骤同第4行到第39行代码一样.

return result # 到这里第40行代码执行完毕,得到结果right_li = [2,4,5,6]

left_li = [1,3,7,8]

right_li = [2,4,5,6]

# 左右指针=0

# result = []

# 循环判断

result= [1], 左指针 = 1, 右指针 = 0

result= [1,2], 右指针 = 1, 左指针 = 1

result= [1,2,3], 左指针 = 2, 右指针 = 1

result= [1,2,3,4], 左指针 = 2, 右指针 = 2

result= [1,2,3,4,5], 右指针 = 3, 左指针 = 2

result= [1,2,3,4,5,6], 右指针 = 4, 左指针 = 2

# 到这里右指针 = 4, 左指针 = 2, 不满足循环条件,

result = [1,2,3,4,5,6] + [7,8] = [1,2,3,4,5,6,7,8]

result = [1,2,3,4,5,6,7,8] + [] = [1,2,3,4,5,6,7,8]

return result # 到这里第1行代码执行完毕,得到结果result = [1,2,3,4,5,6,7,8]

8.快速排序

8.1-定义

快速排序也是一种分而治之的算法,如归并排序。虽然它有点复杂,但在大多数标准实现中,它的执行速度明显快于归并排序,并且很少达到最坏情况下的复杂度O(n) 。它有三个主要步骤:

从数组中选择一个元素,称为pivot。

对数组进行排序,使所有小于pivot的元素都位于pivot之前,而所有值大于pivot的元素都位于pivot之后(相等的值可以朝任何方向移动)。这一步操作通常称为partition。

递归地将上述步骤应用于pivot之前和之后的子数组。

递归的基本情况是大小为0或1的数组,它们是按定义排列的,因此不需要对它们进行排序。

8.2-图解

8.3-代码实现

方式1:

def quick_sort(alist):

if len(alist) < 2: # 递归出口,当数组是空数组或者只有一个元素的数组都是有序的。

return alist

else:

pivot_index = 0

pivot = alist[pivot_index]

less_part = [i for i in alist[pivot_index+1:] if i <= pivot]

great_part = [i for i in alist[pivot_index+1:] if i > pivot]

return quick_sort(less_part) + [pivot] + quick_sort(great_part)

方式2:

def quick_sort(li, start, end):

if start >= end:

return

l_pointer = start

r_pointer = end

pivot = li[l_pointer]

while l_pointer < r_pointer:

while l_pointer < r_pointer and li[r_pointer] >= pivot:

r_pointer -= 1

li[l_pointer], li[r_pointer] = li[r_pointer], li[l_pointer]

while l_pointer < r_pointer and li[l_pointer] < pivot:

l_pointer += 1

li[l_pointer], li[r_pointer] = li[r_pointer], li[l_pointer]

li[l_pointer] = pivot

quick_sort(li, start, l_pointer - 1)

quick_sort(li, l_pointer + 1, end)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值