常用的排序算法

一.排序较慢的三个排序方法

1.冒泡排序

自然语言描述

按照列表中待排序的先后顺序,依次比较相邻的两个数,若两者是升序则不做任何操作,否则交换两者位置。

核心算法举例

以第一趟为例
1 5 7 3 9 2 6 8 1 与 5比较,不变
1 5 7 3 9 2 6 8 5 与 7比较,不变
1 5 3 7 9 2 6 8 7 与 3比较,交换位置
1 5 3 7 9 2 6 8 7 与 9比较,不变
1 5 3 7 2 9 6 8 9 与 2比较, 交换位置
1 5 3 7 2 6 9 8 9 与 6比较,交换位置
1 5 3 7 2 6 8 9 9 与 8比较,交换位置
所以第一趟结束后,排序结果为
1 5 3 7 2 6 8 9 9为最大数,后续不需要比较

算法优劣分析

  • 一共比较((N-1)+(N-2)+...+2+1)次
  • 最好情况下交换0次
  • 最坏情况下交换((N-1)+(N-2)+...+2+1)次
  • 平均时间复杂度O(n²)
def bubble_sort(lists):
    for i in range(len(lists)):
        for v in range(i + 1, len(lists)):
            if lists[i] > lists[v]:
                lists[i], lists[v] = lists[v], lists[i]
    return lists
list1 = [67, 23, 89, 35, 28, 90, 10, 24]

print(bubble_sort(list1))

2.选择排序

自然语言描述

  • 遍历列表所有元素,最小(大)的元素放在最左(右)边。
  • 确定第一个元素位置后,遍历剩下的所有元素,最小(大)的元素放在最左(右)边。
  • 以此类推,直到倒数第二个元素。

核心算法举例

第二趟比较为例(以找最小数为例)
此时min_index = 1
1 7 5 9 4
7 与 5 作比较,min_index更新为2
5 与 9 作比较,min_index不变
5 与 4 作比较,min_index更新为4
将index = 4与index=1的元素交换位置
1 4 5 9 7

算法优劣分析

  • 一共比较((N-1)+(N-2)+...+2+1)次,交换N次。
  • 平均时间复杂度O(n²)
def select_sort(lists):
    count = len(lists)
    for i in range(count):
        min_num = i
        for v in range(i + 1, count):
            if lists[min_num] > lists[v]:
                min_num=v
        lists[min_num], lists[i]=lists[i], lists[min_num]
    return lists

list1 = [67, 23, 89, 35, 28, 90, 10, 24]

print(select_sort(list1))

# 每次都把最小的数给取出来排序

3.插入排序

自然语言描述

在前面已经排好序的列表中插入新元素。步骤:

  • 将第二元素与第一个元素比较,如果小于第一个元素则交换位置,反之不变。
  • 将第三个元素分别与前两个元素比较(这里的算法用的是与之前一个的位置比较),插入合适位置。
  • 以此类推,直到最后一个元素。

核心算法举例

第二趟比较为例
此时 current_value = 1
5 7 1
current_value = 1 与7作比较
5 7 7
current_value = 1 与5作比较
5 5 7
将 current_value = 1 插入
1 5 7

算法优劣分析

  • 最坏的情况下,一共比较并交换(1+2+...+(N-1))次
  • 最好的情况下,只需比较(N-1)次,无需交换
  • 平均时间复杂度O(n²)
  • 稳定
def insert_sort(lists):
    count = len(lists)
    for i in range(1, count):
        key = lists[i]
        j = i - 1
        while j >= 0:
            if lists[j] > key:
                lists[j + 1] = lists[j]
                lists[j] = key
            j -= 1
    return lists


list1 = [67, 23, 89, 35, 28, 90, 10, 24]
print(insert_sort(list1))

 二.排序比较快的三个排序方法

1.快速排序

自然语言描述

对冒泡排序的有效改进。快速排序是一种不稳定的排序算法,即多个相同的值的相对位置也许会在算法结束时产生变动

假设要排序的列表a[0],a[1]...a[n-1],首先任意选取一个数据(通常选用a[0])作为关键数据,然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面,这个过程称为一趟快速排序。整个排序过程可以递推进行,从而使整个列表变得有序。

def quick_sort(lists, left, right):
    """
    :param lists: 需要排序的列表
    :param left: 列表的开始索引
    :param right: 列表的结束索引
    :return:
    """
    if left > right:
        return lists

    key = lists[left]
    low = left
    high = right
    while left < right:
        while left < right and lists[right] >= key:
            right -= 1
        lists[left] = lists[right]
        while left < right and lists[left] <= key:
            left += 1
        lists[right] = lists[left]
    lists[right] = key
    # 把大小分成两部分后,在对着两部分进行排序
    quick_sort(lists, low, left - 1)
    quick_sort(lists, left + 1, high)
    return lists


list1 = [67, 23, 89, 35, 28, 90, 10, 24]
left = 0
right = len(list1) - 1
print(quick_sort(list1, left, right))

2.堆排序

选择排序 每次在n个记录中选择一个最小的需要比较n-1次,但是这样的操作并没有把每一趟的比较结果保存下来,在后一趟的比较中,有许多的比较在前一趟就已经做过了,但是由于前一趟排序时并未保存这些比较结果,所以后一趟排序时又重复执行了这些比较操作,因而记录的比较次数比较多
如果可以做到每次在选择最小记录时,并根据比较结果对其他记录做出相应的调整,那样排序的总体效率就会非常高了,而堆排序就是对简单选择排序进行的一种改进,这种改进的效率是非常明显的

一.堆结构
1.堆是具有下列性质的完全二叉树:
(1)每个结点的值都大于或等于其左右孩子结点的值,称为大顶堆;
(2)或者每个结点的值都小于或等于其左右孩子结点的值,称为小顶堆;
从堆的定义可知:根节点一定是堆中所有结点中共的最大值或者最小值

2.按照层序遍历的方式给结点进行编号,那么非叶结点的编号满足如下关系:
1<=i<=[n/2] [n/2]表示不超过n/2的最大整数
因为完全二叉树的性质:(这里的i指的是编号)
(1)如果2i>n,那么这个i对应的节点是叶节点,且没有左孩子,反之,我们知道不是叶节点的节点就满足2i<=n,即得到了上面的表达式
(2)编号为i的节点的左右子节点编号分别是2i和2i+1
那么按照层序遍历的方式,将最大堆和最小堆存入数组,那么一定满足上面的关系

二.堆排序算法
1.基本思想
将待排序的序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点,将它移走,然后将剩余的n-1个序列重新构造一个堆,这样就会得到n个元素中的次大值,如此反复执行,便能得到一个有序序列了
那么实现这个思想要解决两个问题
(1)如何由一个无序序列构建成一个堆
(2)在输出堆顶元素后,如何调整剩余元素称为一个新的堆

# 调整堆
def adjust_heap(lists, i, size):
    lchild = 2 * i + 1
    rchild = 2 * i + 2
    max = i
    if i < size / 2:
        if lchild < size and lists[lchild] > lists[max]:
            max = lchild
        if rchild < size and lists[rchild] > lists[max]:
            max = rchild
        if max != i:
            lists[max], lists[i] = lists[i], lists[max]
            adjust_heap(lists, max, size)

# 创建堆
def build_heap(lists, size):
    for i in range(0, (size // 2))[::-1]:
        adjust_heap(lists, i, size)

# 堆排序
def heap_sort(lists):
    size = len(lists)
    build_heap(lists, size)
    for i in range(0, size)[::-1]:
        lists[0], lists[i] = lists[i], lists[0]
        adjust_heap(lists, 0, i)
    return lists

list1 = [67, 23, 89, 35, 28, 90, 10, 24]

print(heap_sort(list1))

3.归并排序

归并排序:

归并排序(英语:Merge sort,或mergesort),是创建在归并操作上的一种有效的排序算法,效率为O(n log n)。1945年由约翰·冯·诺伊曼首次提出。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用,且各层分治递归可以同时进行。

分治法:

字面上的解释是“分而治之”,就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题……直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并。

分治法的设计思想是:

将一个难以直接解决的大问题,分割成一些规模较小的相同问题,以便各个击破,分而治之。

# 归并排序
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 = len(lists) // 2
    left = merge_sort(lists[:num])
    right = merge_sort(lists[num:])
    return merge(left, right)

list1 = [67, 23, 89, 35, 28, 90, 10, 24]
print(merge_sort(list1))

 

posted @ 2018-07-25 18:01  明-少  阅读(144)  评论(0)    收藏  举报