数据结构与算法(六)排序算法

一.排序算法

1.排序算法

假设含有n个记录的序列为{r1,r2,……,rn},其相应的关键字分别为{k1,k2,……,kn},需确定 1,2……,n的一种排列p1,p2,……,pn,使其相应的关键字满足kp1≤kp2≤……≤kpn(非递减或非递增)关系,即使得序列成为一个按关键字有序的序列{rp1,rp2,……,rpn},这样的操作就称为排序。
**对一序列对象根据某个关键字进行排序。**多关键字得排序最终都可以转化为单个关键字的排序。
在这里插入图片描述
在这里插入图片描述

2.常用术语

(1)稳定与不稳定

稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面;
不稳定:如果a原本在b的前面,而a=b,排序之后a可能会出现在b的后面;
在这里插入图片描述

(2).内排序与外排序

内排序:所有排序操作都在内存中完成;
外排序:由于数据太大,因此把数据放在磁盘中,而排序通过磁盘和内存的数据传输才能进行;
在这里插入图片描述

3.排序算法的性能三大影响因素

  1. 时间性能(时间复杂度): 一个算法执行所耗费的时间。
  2. 辅助空间 (空间复杂度):运行完一个程序所需内存的大小。
  3. 算法的复杂性 : 算法本身的复杂度,而不是指算法的时间复杂度。
    在这里插入图片描述

4.小结

在这里插入图片描述

二.交换排序:冒泡排序

1.冒泡排序

(1)什么是冒泡排序

**冒泡排序(Bubble Sort)**一种交换排序,它的基本思想是:两两比较相邻记录的关键字,如果反序则交换,直到没有反序的记录为止。这个算法的名字由来是因为越大的元素会经由交换慢慢“浮”到数列的顶端。

(2)冒泡排序的算法

冒泡排序算法的运作如下:
1. 比较相邻的元素。如果第一个比第二个大(升序),就交换他们两个。
2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
3. 针对所有的元素重复以上的步骤,除了最后一个。
4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
在这里插入图片描述

(3)冒泡排序复杂度分析

最好的情况,也就是要排序的表本身就是有序的,那么我们比较次数,可以推断出就是
n‐1 次的比较,没有数据交换,时间复杂度为O(n)。
最坏的情况,即待排序表是逆序的情况,此时需要比较1+2+3+4+…+(n-1)=n(n-1)/2次,
即时间复杂度为O(n**2)。
稳定性: 稳定

(4)冒泡排序的代码实现
def BubbleSort(nums):
    """
    冒泡排序
    需要排n趟, i趟需要比较(n-i-1)
    :param nums: 需要排序的数值
    :return:
    """
    nums_len = len(nums)
    for count in range(nums_len):
        for index in range(nums_len-count-1):
            if nums[index]  < nums[index+1]:
                nums[index], nums[index+1] = nums[index+1], nums[index]

    return  nums

if __name__ == '__main__':
    nums = [12, 34, 23, 45, 66, 1, 2, 0]
    sorted_nums = BubbleSort(nums)
    print(sorted_nums)

在这里插入图片描述

三.交换排序:快速排序

1.快速排序

**快速排序(Quick Sort)**的基本思想是:通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序的目的。
在这里插入图片描述
层数为O(logn)(即调用栈的高度为O(logn)),而每层需要的时间为O(n)。因此整个算法需要的时间为O(n) * O(logn) = O(nlogn)

2.快速排序的代码实现

def quicksort(nums):
    if len(nums) < 2:
        return nums
    else:
        keyword = nums[0]
        less = [num for num in nums[1:] if num < keyword]
        greater = [num for num in nums[1:] if num >= keyword]
        return quicksort(less) + [keyword] + quicksort(greater)

print(quicksort([5, 2, 6, 8, 10, 23]))

在这里插入图片描述

四.插入排序:直接插入排序

1.直接插入排序

**直接插入排序(Straight Insertion Sort)**的基本操作是将一个记录插入到已经排好序的有序表中,从而得到一个新的、记录数增 1 的有序表。
在这里插入图片描述

2.直接插入排序的原理、步骤

原理:每次将排序中的元素,插入到前面已经排好序的有序序列中去,直到排序完成。
步骤

  • 第一步,a[0]为有序区,待排序区为a[1…n-1]。令i=1。
  • 第二步,将a[1]与a[0]中元素比较,将小的元素放在第一个位置。
  • 第三步,以此类推,直到待排序中全部元素插入完成为止。
    最好的情况,也就是要排序的表本身就是有序的, 因此没有移动的记录,时间复杂度为 O(n)。
    最坏的情况,即待排序表是逆序的情况,时间复杂度为 O(n**2)。

3.直接插入排序的代码实现

import random


def insert_sort(nums):
    """
      # 插入排序
    :param nums:
    :return:
    """
    count = len(nums)
    for i in range(1, count):
        key = nums[i]
        j = i - 1
        while j >= 0:
            if nums[j] > key:
                nums[j + 1] = nums[j]
                nums[j] = key
            j -= 1
    return nums


if __name__ == '__main__':
    nums = [2, 8, 7, 6, 3, 23]
    sort_nums = insert_sort(nums)
    print(sort_nums)

在这里插入图片描述

五.插入排序:希尔排序

1.希尔排序

希尔排序是D.L.Shell于 1959 年提出来的一种排序算法,在这之前排序算法的时间复杂度基本
都是O(n2)的,希尔排序算法是突破这个时间复杂度的第一批算法之一。
基本思想
算法先将要排序的一组数按某个增量d(n/2,n为要排序数的个数)分成若干组,每组中记录的然后
再用一个较小的增量(d/2)对它进行分组,在每组中再进行直接插入排序。当增量减到1时,进行
直接插入排序后,排序完成。
一般在记录的数量多的情况下,希尔排序的排序效率较直接插入排序高
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.希尔算法的代码实现

def insert_sort(nums):
    """
      # 插入排序
    :param nums:
    :return:
    """
    count = len(nums)
    for i in range(1, count):
        key = nums[i]
        j = i - 1
        while j >= 0:
            if nums[j] > key:
                nums[j + 1] = nums[j]
                nums[j] = key
            j -= 1
    return nums
def shellSort(nums):
   step = int(len(nums)/2)
   while step > 0:
       print('step=', step)
       nums_len = len(nums)
       for index in range(nums_len):
           if index + step < nums_len:
               current_value = nums[index]
               if current_value > nums[index + step]:
                   nums[index], nums[index + step] = nums[index + step], nums[index]
               step = int(step/2)
   else:
       return insert_sort(nums)

nums = [12, 34, 54, 2, 3, 4, 5, 2, 1, 44]
sorted_nums = shellSort(nums)
print(sorted_nums)

在这里插入图片描述

六.选择排序

1.选择排序

选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理如下。首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。

2.选择排序的代码实现

def findSmallest(nums):
    smallest = nums[0]
    smallest_index = 0
    for num in range(1, len(nums)):
        if nums[num] < smallest:
            smallest = nums[num]
            smallest_index = num
    return smallest_index

def selectionSort(nums):
    new_nums = []
    for i in range(len(nums)):
        smallest = findSmallest(nums)
        new_nums.append(nums.pop(smallest))
    return new_nums


li=[3,2,5,8,6]
print(selectionSort(li))

在这里插入图片描述

3.选择排序的时间复杂度

  • 最优时间复杂度:O(n2 )
  • 最坏时间复杂度:O(n2 )
    稳定性:不稳定(考虑升序每次选择最大的情况)

七.选择排序:堆排序

1.堆排序

堆排序是指利用堆积树(堆)这种数据结构所设计的一种排序算法,利用数组的特点快速
定位指定索引的元素。堆分为大根堆小根堆,是完全二叉树。大根堆的要求是每个节点的值都不大于其父节点的值。最大的值一定在堆顶。
在这里插入图片描述

八.归并排序

1.归并排序

归并排序(Merge sort),是创建在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。
分治法:

  • 分割:递归地把当前序列平均分割成两半。
  • 集成:在保持元素顺序的同时将上一步得到的子序列集成到一起(归并)。
    在这里插入图片描述

2.归并排序的代码实现

def merge(a, b):
    c = []
    h = j = 0
    while j < len(a) and h < len(b):
        if a[j] < b[h]:
            c.append(a[j])
            j += 1
        else:
            c.append(b[h])
            h += 1

    if j == len(a):
        for i in b[h:]:
            c.append(i)
    else:
        for i in a[j:]:
            c.append(i)

    return c


def merge_sort(lists):
    if len(lists) <= 1:
        return lists
    middle = len(lists)/2
    left = merge_sort(lists[:middle])
    right = merge_sort(lists[middle:])
    return merge(left, right)


if __name__ == '__main__':
    a = [4, 7, 8, 3, 5, 9]
    print merge_sort(a)

九.基数排序

1.基数排序

基数排序(radix sort)它是透过键值的部份资讯,将要排序的元素分配至某些“桶”中,达到排序的作用,基数排序法是属于稳定性的排序,其时间复杂度为O (nlog®m),其中r为所采取的基数,而m为堆数,在某些时候,基数排序法的效率高于其它的稳定性排序法。

2.基数排序的代码实现

def RadixSort(Lst):
    d = len(str(max(Lst)))  # 列表中的最大元素的位数
    for i in range(d):  # 0-9一共10个桶
        BucketLst = [[] for k in range(10)]
        for j in range(len(Lst)):
            BucketLst[Lst[j] // (10 ** i) % 10].append(Lst[j])  # 10**i是关注意点,之前一直是10**d,debug才发现
        Lst = [number for B in BucketLst for number in B]  # 关键2-依次拿出桶中的元素,给原列表Lst,
        # 而不是给缓存 temp = [number for B in BucketLst for number in B]


    return Lst


if __name__ == "__main__":
    Lst1 = [810, 700, 821, 892, 846, 199]
    print(RadixSort(Lst1))
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值