十大排序算法

面试中,最喜欢考察的是十大排序算法,无论是开发还是算法,这也是刚学算法入门最基础部分。

 

冒泡排序

重复地走访需要排序的元素列表,依次比较两个相邻的元素,如果顺序(如从大到小或从小到大)错误就交换它们的位置。重复地进行直到没有相邻的元素需要交换,则元素列表排序完成。

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

array = [3,44,38,5,47,15,36,26,27,2,46,4,19,50,48]
bubble_sort(array)
print(array)

 

选择排序

选择排序的基本思路是通过循环找到乱序列表中最小的那个值,然后把最小的那个值跟列表中的第一个值交换位置

def selection_sort(arr):
    """选择排序"""
    # 第一层for表示循环选择的遍数
    for i in range(len(arr) - 1):
        # 将起始元素设为最小元素
        min_index = i
        # 第二层for表示最小元素和后面的元素逐个比较
        for j in range(i + 1, len(arr)):
            if arr[j] < arr[min_index]:
                # 如果当前元素比最小元素小,则把当前元素角标记为最小元素角标
                min_index = j
        # 查找一遍后将最小元素与起始元素互换
        arr[min_index], arr[i] = arr[i], arr[min_index]
        
array = [3,44,38,5,47,15,36,26,27,2,46,4,19,50,48]
selection_sort(arr)
print(arr)

 插入排序

def insertSort(arr):
    n = len(arr)
    for i in range(1,n):
        preIndex = i - 1
        current = arr[i]   # 保存当前需要进行插入元素值
        while preIndex >= 0 and arr[preIndex] > current:
            # 在已排序数组中找到cunrent值能够放下的相应索引位置
            arr[preIndex + 1] = arr[preIndex]  # 元素后移,为需要插入的元素腾位置
            preIndex -= 1  
        arr[preIndex + 1] = current
            
array = [11, 17, 51, 7, 30, 24, 27, 45, 15, 5, 36, 21] 
insertSort(array)
print(array)

希尔排序

def shell_sort(arr):
    """
    希尔排序
    :param arr: 待排序的List
    :return: 希尔排序是就地排序(in-place)
    """
    length = len(arr)
    gap = length // 2
    
    while gap > 0:
        for i in range(gap, length):
            j = i
            cur = arr[i]
            while j >= gap and arr[j-gap] > cur:
                arr[j] = arr[j-gap]
                j -= gap
            arr[j] = cur
        gap //= 2
        
mylist = [11, 17, 51, 7, 30, 24, 27, 45, 15, 5, 36, 21]
shell_sort(mylist)
print(mylist)

归并排序

def merge(list_left, list_right):
    """
    入参数组都是有序的,此处将两个有序数组合并成一个大的有序数组
    """
    # 两个数组的起始下标
    l, r = 0, 0
    
    new_list = []
    while l < len(list_left) and r < len(list_right):
        if list_left[l] <= list_right[r]:
            new_list.append(list_left[l])
            l += 1
        else:
            new_list.append(list_right[r])
            r += 1
    new_list += list_left[l:]
    new_list += list_right[r:]
    return new_list
    
def merge_sort(mylist):
    """归并排序
    mylist: 待排序数组
    return: 新数组list
    """
    if len(mylist) <= 1:
        return mylist

    mid = len(mylist) // 2
    list_left = merge_sort(mylist[:mid])
    list_right = merge_sort(mylist[mid:])
    return merge(list_left, list_right)
    
mylist = [11, 17, 51, 7, 30, 24, 27, 45, 15, 5, 36, 21]
result = merge_sort(mylist)
print(result)

快排

基本原理:分治,选择数组中某个数作为基数,通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数都比基数小,另外一部分的所有数都都比基数大,然后再按此方法对这两部分数据分别进行快速排序,循环递归,最终使整个数组变成有序。
基数选择:由于快速排序需要选定一个基数进行划分排序,关于基数选择有很多方式,而基数选择直接关系到快排的效率。事实上,选取基准元素应该遵循平衡子问题的原则:即使得划分后的两个子序列的长度尽量相同此次以待排序数组首元素作为基数进行说明。

def partition(nums,l,r):
    p = nums[l] #初始化一个待比较数据
    i, j = l, r
    while(i<j):
        while(i<j and nums[j]>=p):#从后往前查找,直到找到一个比p更小的数
            j-=1
        nums[i] = nums[j] #将更小的数放入左边
        while(i<j and nums[i]<=p): #从前往后找,直到找到一个比p更大的数
            i+=1
        nums[j] = nums[i] #将更大的数放入右边
    
    #循环结束,i与j相等        
    nums[i] = p #待比较数据p放入最终位置 
    return i #返回待比较数据最终位置i
def quicksort(nums):
    def dfs(nums,l,r):
        if l < r:
            index = partition(nums,l,r)
            dfs(nums,l,index-1)
            dfs(nums,index+1,r)
    dfs(nums,0,len(nums)-1)
        
arr = [4,16,3,8,2,7,16,33,2,1,5]
quicksort(arr)
print(arr)

堆排序

# coding=utf-8
def heap_sort(array):
    first = len(array) // 2 - 1
    for start in range(first, -1, -1):
        # 从下到上,从右到左对每个非叶节点进行调整,循环构建成大顶堆
        big_heap(array, start, len(array) - 1)
    for end in range(len(array) - 1, 0, -1):
        # 交换堆顶和堆尾的数据
        array[0], array[end] = array[end], array[0]
        # 重新调整完全二叉树,构造成大顶堆
        big_heap(array, 0, end - 1)
    return array
    
def big_heap(array, start, end):
    root = start
    # 左孩子的索引
    child = root * 2 + 1
    while child <= end:
        # 节点有右子节点,并且右子节点的值大于左子节点,则将child变为右子节点的索引
        if child + 1 <= end and array[child] < array[child + 1]:
            child += 1
        if array[root] < array[child]:
            # 交换节点与子节点中较大者的值
            array[root], array[child] = array[child], array[root]
            # 交换值后,如果存在孙节点,则将root设置为子节点,继续与孙节点进行比较
            root = child
            child = root * 2 + 1
        else:
            break
        
mylist = [11, 17, 51, 7, 30, 24, 27, 45, 15, 5, 36, 21]
heap_sort(mylist)
print(mylist)

计数排序

def countSort(arr):
    max_value = max(arr)
    res = []
    count_nums = [0 for i in range(max_value + 1)]
    for num in arr:
        count_nums[num] += 1
    for i in range(len(count_nums)):
        if count_nums[i] != 0:
            # 元素i有 count_nums[i]个,添加入最终的排序数组
            res.extend(count_nums[i] * [i])
    return res
    
mylist = [11, 17, 51, 7, 30, 24, 27, 45, 15, 5, 36, 21]
print(countSort(mylist))

桶排序

桶排序(Bucket Sort),也叫箱排序,其主要思想是:将待排序集合中处于同一个值域的元素存入同一个桶中,也就是根据元素值特性将集合拆分为多个区域,则拆分后形成的多个桶,从值域上看是处于有序状态的。对每个桶中元素进行排序,则所有桶中元素构成的集合是已排序的。
桶排序是计数排序的扩展版本,计数排序可以看成每个桶只存储相同元素,而桶排序每个桶存储一定范围的元素。桶排序需要尽量保证元素分散均匀,否则当所有数据集中在同一个桶中时,桶排序失效。
算法流程:
  1. 根据待排序集合中最大元素和最小元素的差值范围和映射规则,确定申请的桶个数;
  2. 遍历排序序列,将每个元素放到对应的桶里去;
  3. 对不是空的桶进行排序;
  4. 按顺序访问桶,将桶中的元素依次放回到原序列中对应的位置,完成排序。
def bucket_sort(arr):
    """桶排序"""
    min_num = min(arr)
    max_num = max(arr)
    # 桶的大小(float)
    bucket_range = (max_num-min_num) / len(arr)
    # 桶数组
    count_list = [ [] for i in range(len(arr) + 1)]
    # 向桶数组填数
    for i in arr:
        count_list[int((i-min_num)//bucket_range)].append(i)
    
    arr.clear()
    # 回填,这里桶内部排序直接调用了sorted
    for i in count_list:
        for j in sorted(i):
            arr.append(j)

# numlist = [0.11, 0.17, 0.51,0.303, 0.7, 0.30, 0.24, 0.27, 0.45, 0.15, 0.5, 0.36, 0.21,0.101]
numlist = [11, 17, 51, 7, 30, 24, 27, 45, 15, 5, 36, 21]
bucket_sort(numlist)
print(numlist)

基数排序

基数排序的发明可以追溯到 1887 年赫尔曼·何乐礼在打孔卡片制表机 (Tabulation Machine) 上的贡献。它是这样实现的:将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。
基数排序的方式可以采用 LSD 或 MSD,LSD 的排序方式由键值的最右边开始,而 MSD 则相反,由键值的最左边开始。
LSD:
# coding=utf-8
def radix_sort(array):
    max_num = max(array)
    
    place = 1 # 计算最大数max_num有几位数字
    while max_num >= 10**place:
        place += 1
    for i in range(place):
        # 从低位开始放入"进制桶"buckets
        buckets = [[] for _ in range(10)]
        for num in array:
            radix = int(num/(10**i) % 10)
            buckets[radix].append(num)
        # 再从"进制桶"buckets取出数
        j = 0
        for k in range(10):
            for num in buckets[k]:
                array[j] = num
                j += 1
    return array
    
array = [11, 17, 51, 7, 30, 24, 27, 45, 15, 5, 36, 21]
print(radix_sort(array))

 

 
 
 
 
 
 
posted @ 2022-05-25 17:13  落樱纷飞  阅读(16)  评论(0编辑  收藏  举报