排序算法之冒泡排序及其优化

冒泡排序


其余排序方法:选择排序冒泡排序归并排序快速排序插入排序希尔排序html


思想

比较相邻两个元素,若是前面的元素比后面的元素大,则交换位置。最后一个元素一定会是最大值。
排除掉最后一位元素,继续循环,直至没有元素须要比较python

能够看出,冒牌排序其实和选择排序时很像的,只不过选择排序是比较数据,先记录下最小/大值的下标,等一趟循环结束的时候再交换位置;
而冒泡排序在比较数据的同时也作了交换。算法

性能

时间复杂度与选择排序同样,时间复杂度为O(n^2)。
因为它们的比较次数同样,而冒泡排序的交换次数多,因此通常冒泡排序会比选择排序慢。
但冒泡排序有一个优点,那就是每通过一次循环,数组的有序性就会变高。咱们能够利用这点来优化冒泡排序,优化方法请看下文。数组

代码

普通冒泡排序的Python代码:性能

# 冒泡排序
def bubbleSort(arr):
    size = len(arr)
    for i in range(size - 1, 0, -1):
        for j in range(i):
            if arr[j] > arr[j + 1]:
                arr[j], arr[j + 1] = arr[j + 1], arr[j]

优化

优化一

咱们会发现若是数组一开始就是有序的或者再通过前几回循环的时候就已经变得有序了,但上述程序仍是会继续循环比较。针对这一点咱们能够进行一次优化:优化

# 冒泡排序(优化一)
def bubbleSort(arr):
    size = len(arr)
    for i in range(size - 1, 0, -1):
        # 设一个flag,True表示没有交换
        hasNoChange = True
        for j in range(i):
            if arr[j] > arr[j + 1]:
                arr[j], arr[j + 1] = arr[j + 1], arr[j]
                # 交换数据,设为False
                hasNoChange = False

        # 若是没有交换,证实数组已经有序了,能够结束循环
        if hasNoChange: break

通过优化以后,最好的状况下,时间复杂度为O(n)。code

优化二

优化一针对的是整个数组已经有序的状况。那若是一个数组,假设有一万个数据,其实从一开始或者几回循环以后,后5千个数据已是有序的且比前5千个数据要大,
在上述代码中,每次循环仍然会循环到上次循环找出的最大值的前一位。针对这一点,咱们还能够再作一次优化:htm

# 冒泡排序(优化二)
def bubbleSort(arr):
    size = len(arr)
    # 最后一次交换的位置
    lastChangedIdx = size - 1
    for i in range(len(arr), 0, -1):
        tmpIdx = -1
        # 只须要比较到上次交换的位置
        for j in range(lastChangedIdx):
            if arr[j] > arr[j + 1]:
                arr[j], arr[j + 1] = arr[j + 1], arr[j]
                tmpIdx = j

        # 若是没有交换,证实数组已经有序了,能够结束循环
        if tmpIdx == -1:
            break
        # 若是有交换,将最后一次交换的位置赋给lastChangedIdx
        else:
            lastChangedIdx = tmpIdx

优化三

假如仍是刚才那个数组,后五千个数据已经有序,但最后一位是整个数组中的最小值,咱们会发现优化二中的优化根本起不了做用。
那想要让优化二起做用,那么就须要把那个最小值拿到前面来,有什么办法呢?很简单,就是来一次反向冒泡就好了。
这样的算法被叫作双向冒泡排序(也叫鸡尾酒排序),为了高雅地装逼,咱们就叫它鸡尾酒排序吧。blog

# 冒泡排序(优化三,鸡尾酒排序)
def bubbleSort(arr):
    size = len(arr)
    # 正向最后一次交换的位置
    rightLastChangedIdx = size - 1
    # 反向最后一次交换的位置
    leftLastChangedIdx = 0
    for i in range(size >> 1):
        tmpIdx = -1
        # 正向冒泡:从反向最后一次交换的位置到正向最后一次交换的位置
        for j in range(leftLastChangedIdx, rightLastChangedIdx):
            if arr[j] > arr[j + 1]:
                arr[j], arr[j + 1] = arr[j + 1], arr[j]
                tmpIdx = j

        # 若是没有交换,证实数组已经有序了,能够结束循环
        if tmpIdx == -1:
            break
        # 若是有交换,将最后一次交换的位置赋给rightLastChangedIdx
        else:
            rightLastChangedIdx = tmpIdx

        tmpIdx = -1
        # 反向冒泡:从正向最后一次交换的位置到反向最后一次交换的位置
        for k in range(rightLastChangedIdx, leftLastChangedIdx, -1):
            if arr[k] < arr[k - 1]:
                arr[k], arr[k - 1] = arr[k - 1], arr[k]
                tmpIdx = k

        # 若是没有交换,证实数组已经有序了,能够结束循环
        if tmpIdx == -1:
            break
        # 若是有交换,将最后一次交换的位置赋给leftLastChangedIdx
        else:
            leftLastChangedIdx = tmpIdx

其余排序方法:选择排序冒泡排序归并排序快速排序插入排序希尔排序排序

相关文章
相关标签/搜索