Python算法系列之常见的排序算法-------------------冒泡排序、选择排序、插入排序、快速排序、希尔排序、归并排序

 

排序算法

定义:是一种能将一串数据依照特定的顺序进行排列的一种算法。

1.冒泡排序

思路:比较相邻的元素。如果第一个比第二个大,就交换他们两个。以此类推,对每一对相邻元素做相同处理,从开始第一队到结尾的最后一对。这不做完之后最后的元素就会是最大的数。然后对所有的元素进行重复以上的步骤,每次都出去最后的一个。

冒泡排序
无序的序列:[54,26,93,17,77,31,44,55,20]
第一次比较,54>26,交换:26,54, 93,17,77,31,44,55,20
第二次比较,54<93,不处理:26,54, 93,17,77,31,44,55,20
第三次比较,93>17, 交换:26,54, 17,93, 77,31,44,55,20
第四次比较,93>77, 交换:26,54, 17, 77, 93, 31,44,55,20
第五次比较,93>31, 交换:26,54, 17, 77, 31,93, 44,55,20
第六次比较,93>44, 交换:26,54, 17, 77, 31, 44,93, 55,20
第七次比较,93>55, 交换:26,54, 17, 77, 31, 44, 55,93, 20
第八次比较,93>20, 交换:26,54, 17, 77, 31, 44, 55, 20,93
小结:1、最大的数93排在了队列的末尾
           2、列表的长度n = 9,我们比较了n-1次
重复上面的过程:
  26, 17,54, 31, 44, 55, 20, 77,93
小结:1、比较了n-2次
           2、77放在了无序列表尾部 
继续:
17, 26, 31, 44, 54, 20,55, 77,93    # 比较了 n-3 ,把55放在了无序列表的尾部
17, 26, 31, 44, 20, 54,55, 77,93    # 比较了 n-4 ,把54放在了无序列表的尾部
17, 26, 31, 20, 44, 54,55, 77,93    # 比较了 n-5 ,把54放在了无序列表的尾部
17, 26, 20, 31, 44, 54,55, 77,93    # 比较了 n-6 ,把31放在了无序列表的尾部
17, 20, 26, 31, 44, 54,55, 77,93    # 比较了 n-7 ,把26放在了无序列表的尾部
17, 20, 26, 31, 44, 54,55, 77,93    # 比较了 n-8 ,得到一个有序的序列
总结:
相邻元素两两比较把最大值排在无序序列尾部这个过程,要循环n-1

代码实现:

def bubbling_order(lis):
    for i in range(len(lis)-1, 0, -1):
        for j in range(i):
            if lis[j] > lis[j+1]:
                lis[j], lis[j+1] = lis[j+1], lis[j]
    return lis


listA = [32, 56, 6, 78, 44, 34, 45, 48]
print('冒泡排序:', bubbling_order(listA))

 

2.选择排序

思路:首先在没有排序的序列只能够找到最大(小)元素,存放到排序序列的其实位置,再从剩余未排序元素中继续寻找最大(小)元素,放到已排序序列的末尾,重复以上操作,直到所有元素均以排序完成。

选择排序
无序的序列:[54,26,93,17,77,31,44,55,20]
第一次,从无序序列中挑出一个最小值,放在有序序列的尾部:
17,54,26,93, 77,31,44,55,20
第二次,从无序序列中挑出一个最小值,放在有序序列的尾部:
17,20,54,26,93, 77,31,44,55
第三次,从无序序列中挑出一个最小值,放在有序序列的尾部:
17,20, 26,54, 93, 77,31,44,55
第四次,从无序序列中挑出一个最小值,放在有序序列的尾部:
17,20, 26, 31,54, 93, 77, 44,55
第五次,从无序序列中挑出一个最小值,放在有序序列的尾部:
17,20, 26, 31, 44,54, 93, 77, 55
第六次,从无序序列中挑出一个最小值,放在有序序列的尾部:
17,20, 26, 31, 44,54, 93, 77, 55
第七次,从无序序列中挑出一个最小值,放在有序序列的尾部:
17,20, 26, 31, 44,54, 55, 93, 77
第八次,从无序序列中挑出一个最小值,放在有序序列的尾部:
17,20, 26, 31, 44,54, 55, 77, 93

代码实现:

# 选择排序
def choice_order(lis):
    n = len(lis)
    for i in range(1, n):
        min_index = i-1
        for j in range(i, n):
            if lis[j] < lis[min_index]:
                min_index = j
        if min_index != i-1:
            lis[i-1], lis[min_index] = lis[min_index], lis[i-1]
    return lis


listB = [34, 56, 89, 7, 43, 23, 73, 21]
print('选择排序:', choice_order(listB))

 

3.插入排序

插入排序是一种简单直观的排序算法

思路:通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相对应位置并插入。

 

插入排序
无序的序列:[54,26,93,17,77,31,44,55,20]
第一次插入:[20,54,26,93,17,77,31,44,55]
第二次插入:[20,54,55,26,93,17,77,31,44]
第三次插入:[20,44,54,55,26,93,17,77,31]
第四次插入:[20,31,44,54,55,26,93,17,77]
第五次插入:[20,31,44,54,55,77,26,93,17]
第六次插入:[17,20,31,44,54,55,77,26,93]
第七次插入:[17,20,31,44,54,55,77, 93,26]
第八次插入:[17,20,26,31,44,54,55,77, 93]
总结:
待排序序列元素数 : n
插入的次数: n-1

代码实现:

# 插入排序
def insert_order(lis):
    n = len(lis)
    for i in range(1, n):
        for j in range(i, 0, -1):
            if lis[j] < lis[j-1]:
                lis[j], lis[j-1] = lis[j-1], lis[j]
                # print(lis)
    return lis


listC = [12, 45, 9, 39, 18, 89, 48, 59]
print('插入排序:', insert_order(listC))

4.快速排序

思路:快速排序又称为划分交换排序,将要排序的数据分割成独立的两部分,其中一部分的所有数据逗比另外一部分所有数据都要小,然后在按照此方法对两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

 

快速排序
无序的序列:[54,26,93,17,77,31,44,55,20]
第一次快速排序,那第一个元素54作为基准值,进行分割:
[26,17,31,44,20,54, 93,77,55]
经过第一次分割排序,基准值左边的序列的所有元素都小于基准右边序列的所有元素
对54 左边的序列进行递归的分割:
拿26作为基准值,进行分割:
[17,20,26, 31,44,54, 93,77,55]
对26 左边的序列进行递归的分割:
拿17作为基准值,进行分割:
[17,20,26, 31,44,54, 93,77,55]
对17 右边的序列进行递归的分割:
右边序列只有一个元素20,不能在分割,返回

对26 右边的序列进行递归的分割:
拿31作为基准值,进行分割:
[17,20,26, 31,44,54, 93,77,55]
31小于44,不处理

对54 右边的序列进行递归的分割:
拿93作为基准值,进行分割:
[17,20,26, 31,44,54, 77, 55,93]
对93 右边的序列进行递归的分割:
拿77作为基准值,进行分割:
[17,20,26, 31,44,54, 55, 77,93]
接下来,把整个序列进行排序:
[17,20,26, 31,44,54, 55, 77,93]

代码实现:

# 快速排序
def quick_order(lis, start, end):
    # 判断low是否小于high,如果为false直接返回
    if start >= end:
        return lis
    mid = lis[start]   # 设置基准值
    low = start
    high = end
    while low < high:
        # 如果列表后面的数比基准值大或者相等,则前移以为直到有比基准值小点的数出现
        while low < high and lis[high] >= mid:
            high -= 1
        # 找到就把索引[high]的值赋给第[low]个元素
        lis[low] = lis[high]
        # 基准值列表之前的做同样处理
        while low < high and lis[low] < mid:
            low += 1
        lis[high] = lis[low]
    lis[low] = mid
    quick_order(lis, start, low-1)
    quick_order(lis, low+1, end)
    return lis


listD = [23, 56, 5, 67, 81, 21, 41, 14]
print('快速排序:', quick_order(listD, 0, len(listD) - 1))

5.希尔排序

希尔排序是插入排序的一种。也称缩小增量排序,是直接插入排序算法的一种更高效的改进版本。

思路:希尔排序是把记录按下标的一定增量分组,对每组使用直接插入排序算法排序,随着增量逐渐减少,每组包含的关键词越来越多,当增量减至1时,整个文件恰被分成一组,算法便终止。

代码实现:

# 希尔排序
def shell_order(lis):
    n = len(lis)
    gap = n//2  # 设置初始步长
    while gap > 0:
        # 按照步长进行插入排序
        for i in range(gap, n):
            j = i
            # 进行插入排序
            while j >= gap and lis[j-gap] > lis[j]:
                lis[j-gap], lis[j] = lis[j], lis[j-gap]
                j -= gap
        gap = gap//2  # 设置新的步长 进行循环下一次
    return lis


listE = [68, 34, 56, 7, 23, 75, 90, 54]
print('希尔排序:', shell_order(listE))

6.并归排序

思路:

假如我们有一个n个数的数列,下标从0到n-1
首先是拆分的过程
1 、我们按照 n//2 把这个数列分成两个小的数列
2 、把两个小数列 再按照新长度的一半 把每个小数列都分成两个更小的
3、一直这样重复,一直到每一个数列包含单个元素

之后是合并排序的过程:
3、按照最后分开的两个数比较大小并合并成有序的数组
4 、对每组数据按照上次分开的结果,进行排序并合并成更大的数组
5、最后的到一个有序的数组

归并排序

无序的序列:[54,26,93,17,77,31,44,55,20]

拆分过程:

第一次拆分,n = 9   n//2 = 4

拆分结果:[54,26,93,17]和[77,31,44,55,20]

递归拆分[54,26,93,17]的部分:

n = 5   n//2 = 2

拆分结果:[54,26]和[93,17]

递归拆分[54,26]的部分:

n = 2   n//2 = 1

拆分结果:[54]和[26]

合并[54]和[26],合并的结果 [26,54]

递归拆分[93,17]的部分:

n = 2   n//2 = 1

拆分结果:[93]和[17]

合并[93]和[17],合并的结果[17, 93]

合并left=[26,54]和right=[17, 93]

创建results =[]

定义两个指针 lp=0,rp=0

比较left[lp]和right[rp] , 26>17

把17添加到results中,result.append(right[rp])

results=[17]

rp+=1   rp = 1

比较left[lp]和right[rp] , 26<93

把26添加到results中,result.append(left[lp])

results=[17,26]

lp+=1   lp =1

比较left[lp]和right[rp] , 54<93

把54添加到results中,result.append(left[lp])

results=[17,26,54]

lp+=1 lp=2

把93添加到results中,result.append(right[rp])

results=[17,26,54, 93]

返回results

递归拆分[77,31,44,55,20]的部分:

同样的道理,递归拆分合并,

得到结果: [20,31, 44, 55,77]

合并left=[17,26,54, 93]和right=[20,31, 44, 55,77]

定义results = []

定义lp=0和rp =0

比较left[lp]和right[rp] , 17<20

把17添加到results中,result.append(left[lp])

results=[17]

lp+=1  lp = 1

比较left[lp]和right[rp] , 26>20

把20添加到results中,result.append(right[rp])

results=[17,20]

rp+=1

比较left[lp]和right[rp] , 26<31

把26添加到results中,result.append(left[lp])

results=[17,20,26]

lp+=1  lp = 2

。。。。。

最终结果:

[17, 20,26, 31, 44, 54, 55,77, 93]

代码实现:

def merger_order(lis):
    if len(lis) <= 1:
        return lis
    else:
        num = len(lis)//2
        left_part = merger_order(lis[:num])
        right_part = merger_order(lis[num:])
        return merger(left_part, right_part)


def merger(left, right):
    h, r = 0, 0
    result = []
    while h < len(left) and r < len(right):
        if left[h] < right[r]:
            result.append(left[h])
            h += 1
        else:
            result.append(right[r])
            r += 1
        result += left[h:]
        result += right[r:]
        return result


listF = [29, 4, 44, 52, 21, 42, 78, 25]
print('归并排序:', shell_order(listF))

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章