八大排序(归并和快排要熟练!)(基线排序暂未写入)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_38358305/article/details/85863462

转自:https://www.cnblogs.com/hokky/p/8529042.html

性能比较:

直接插入排序

直接插入排序的核心思想就是:将数组中的所有元素依次跟前面已经排好的元素相比较,如果选择的元素比已排序的元素小,则交换,直到全部元素都比较过。
因此,从上面的描述中我们可以发现,直接插入排序可以用两个循环完成:

  1. 第一层循环:遍历待比较的所有数组元素
  2. 第二层循环:将本轮选择的元素(selected)与已经排好序的元素(ordered)相比较。
    如果:selected > ordered,那么将二者交换

#直接插入排序
def insert_sort(L):
    #遍历数组中的所有元素,其中0号索引元素默认已排序,因此从1开始
    for x in range(1,len(L)):
    #将该元素与已排序好的前序数组依次比较,如果该元素小,则交换
    #range(x-1,-1,-1):从x-1倒序循环到0
        for i in range(x-1,-1,-1):
    #判断:如果符合条件则交换
            if L[i] > L[i+1]:
                temp = L[i+1]
                L[i+1] = L[i]
                L[i] = temp

希尔排序 

希尔排序的算法思想:将待排序数组按照步长gap进行分组,然后将每组的元素利用直接插入排序的方法进行排序;每次将gap折半减小,循环上述操作;当gap=1时,利用直接插入,完成排序。
同样的:从上面的描述中我们可以发现:希尔排序的总体实现应该由三个循环完成:

  1. 第一层循环:将gap依次折半,对序列进行分组,直到gap=1
  2. 第二、三层循环:也即直接插入排序所需要的两次循环。具体描述见上。

#希尔排序
def insert_shell(L):
    #初始化gap值,此处利用序列长度的一般为其赋值
    gap = (int)(len(L)/2)
    #第一层循环:依次改变gap值对列表进行分组
    while (gap >= 1):
    #下面:利用直接插入排序的思想对分组数据进行排序
    #range(gap,len(L)):从gap开始
        for x in range(gap,len(L)):
    #range(x-gap,-1,-gap):从x-gap开始与选定元素开始倒序比较,每个比较元素之间间隔gap
            for i in range(x-gap,-1,-gap):
    #如果该组当中两个元素满足交换条件,则进行交换
                if L[i] > L[i+gap]:
                    temp = L[i+gap]
                    L[i+gap] = L[i]
                    L[i] =temp
    #while循环条件折半
        gap = (int)(gap/2)

简单选择排序

简单选择排序的基本思想:比较+交换。

  1. 从待排序序列中,找到关键字最小的元素;
  2. 如果最小元素不是待排序序列的第一个元素,将其和第一个元素互换;
  3. 从余下的 N - 1 个元素中,找出关键字最小的元素,重复(1)、(2)步,直到排序结束。
    因此我们可以发现,简单选择排序也是通过两层循环实现。
    第一层循环:依次遍历序列当中的每一个元素
    第二层循环:将遍历得到的当前元素依次与余下的元素进行比较,符合最小元素的条件,则交换。

 

# 简单选择排序
def select_sort(L):
#依次遍历序列中的每一个元素
    for x in range(0,len(L)):
#将当前位置的元素定义此轮循环当中的最小值
        minimum = L[x]
#将该元素与剩下的元素依次比较寻找最小元素
        for i in range(x+1,len(L)):
            if L[i] < minimum:
                temp = L[i];
                L[i] = minimum;
                minimum = temp
#将比较后得到的真正的最小值赋值给当前位置
        L[x] = minimum

堆排序(最小k个数可见剑指offer题30

堆的概念
堆:本质是一种数组对象。特别重要的一点性质:<b>任意的叶子节点小于(或大于)它所有的父节点</b>。对此,又分为大顶堆和小顶堆,大顶堆要求节点的元素都要大于其孩子,小顶堆要求节点元素都小于其左右孩子,两者对左右孩子的大小关系不做任何要求。
利用堆排序,就是基于大顶堆或者小顶堆的一种排序方法。下面,我们通过大顶堆来实现。

基本思想:
堆排序可以按照以下步骤来完成:

首先将序列构建称为大顶堆;
(这样满足了大顶堆那条性质:位于根节点的元素一定是当前序列的最大值)

(图中节点标注有错 应该是0开始)

取出当前大顶堆的根节点,将其与序列末尾元素进行交换;
(此时:序列末尾的元素为已排序的最大值;由于交换了元素,当前位于根节点的堆并不一定满足大顶堆的性质)

对交换后的n-1个序列元素进行调整,使其满足大顶堆的性质;

重复2.3步骤,直至堆中只有1个元素为止

1

# -*- coding:utf-8 -*- 
def build_max_head(arr,heapsize,root_index):
    #根据给定的根节点计算左右节点的index
    left_index = 2*root_index+1
    right_index = left_index+1
    max_num_index = root_index    
    #如果左节点大于根节点,那么max_num_index=left_index
    if left_index<heapsize and arr[left_index]>arr[max_num_index]:
        max_num_index=left_index    
    #如果右节点大于根节点,那么max_num_index=right_index
    if right_index<heapsize and arr[right_index]>arr[max_num_index]:
        max_num_index=right_index    
    if  max_num_index != root_index:
        arr[root_index],arr[max_num_index] = arr[max_num_index],arr[root_index]        #进行下一个节点的build_max
        build_max_head(arr,heapsize,max_num_index)

        
def head_sort(arr):
    #从最后一个节点开始,对整个堆(完全二叉树)进行build_max
    for i in range((len(arr)+1)//2-1,-1,-1):#根索引最大范围(若是k,则将len换成k)#因为建立最大堆的最后一行的关系,所以要倒着来
        build_max_head(arr,len(arr),i) #(若是k,则将len换成k)   
    #对从最后一个节点开始往前遍历
    for i in range(len(arr)-1, -1, -1):  #(若是k,则将len换成k)     
        #这个时候最大值就在根节点
        #所以把这个最大值放到有序队列中
        #简单来说就是把最大值放到最后
        arr[i],arr[0] = arr[0],arr[i]        
        #互换之后,堆中就少了一个元素,所以当前堆的个数变了,变为i
        #此时由于堆只变了根节点,因此只需要对根节点进行build_max
        build_max_head(arr,i,0)

冒泡排序

思路:

  1. 将序列当中的左右元素,依次比较,保证右边的元素始终大于左边的元素;
    ( 第一轮结束后,序列最后一个元素一定是当前序列的最大值;)
  2. 对序列当中剩下的n-1个元素再次执行步骤1。
  3. 对于长度为n的序列,一共需要执行n-1轮比较
    (利用while循环可以减少执行次数)

def bubble_sort(L):
    length = len(L)
    for x in range(length):
        for i in range(0,length-x):
            if L[x] > L[i]:
                L[x],L[i] = L[i],L[x]

快速排序(及优化)


快速排序的基本思想:挖坑填数+分治法

  1. 从序列当中选择一个基准数(pivot)
    在这里我们选择序列当中第一个数最为基准数
  2. 将序列当中的所有数依次遍历,比基准数大的位于其右侧,比基准数小的位于其左侧
  3. 重复步骤1.2,直到所有子集当中只有一个元素为止。
    用伪代码描述如下:
    1.i =L; j = R; 将基准数挖出形成第一个坑a[i]。
    2.j--由后向前找比它小的数,找到后挖出此数填前一个坑a[i]中。
    3.i++由前向后找比它大的数,找到后也挖出此数填到前一个坑a[j]中。
    4.再重复执行2,3二步,直到i==j,将基准数填入a[i]中
def quick_sort(L,start,end):
    if start < end:
        i,j,mid = start,end,L[start]
        while i < j:
            while(i<j) and (L[j] >= mid):
                j = j-1
            if(i<j):
                L[i] = L[j]
            while(i<j) and (L[i] <= mid):
                i = i+1
            if(i<j):
                L[j] = L[i]
        L[i] = mid
        quick_sort(L,start,i-1)
        quick_sort(L,i+1,end)
    return L

优化1:基准元素不再选择第一个元素,而是随机选择一个,这样避免了有序数组,时间复杂度最坏为log(n2) 的情况

优化2:三路快拍,把和基准元素相等的元素放在中间,比他小的在左边,大的在右边。这样避免了重复元素过多的情况

import random


def quicksort(arr, left, right):
    # 只有left < right 排序
    if left >=right:
        return 
    #在列表里随机选一个数来作为基准元素
    random_index = random.randint(left, right)
    #把基准元素和第一个元素交换
    arr[left], arr[random_index] = arr[random_index], arr[left]
    pivot = arr[left]
    #定义lt:小于v部分元素 的下标,初始是空的,因为arr[left]是基准元素
    lt = left # arr[left+1...lt] < v
    #gt 大于v 部分开始的下标,初始为空
    gt = right + 1 # arr[gt...right] > v
    i = left + 1 # arr[lt+1...i] == v
    #终止条件:下标i 和gt 遇到一起,说明都排完了
    while i < gt:
        if arr[i] < pivot:
            arr[i], arr[lt+1] = arr[lt+1], arr[i]
            lt += 1
            i += 1
        elif arr[i] > pivot:
            arr[i], arr[gt-1] = arr[gt-1], arr[i]
            gt -= 1
        else:
            i += 1
     #最后把第一个元素(基准元素)放到等于v的部分
    arr[left], arr[lt] = arr[lt], arr[left]
    #递归排序
    quicksort(arr, left, lt-1)
    quicksort(arr, gt, right)

归并排序

思路:

     归并排序采用分而治之的原理:

     一、将一个序列从中间位置分成两个序列;

     二、在将这两个子序列按照第一步继续二分下去;

     三、直到所有子序列的长度都为1,也就是不可以再二分截止。这时候再两两合并成一个有序序列即可。

def merge_sort(arr):
    if len(arr) <= 1:
        return arr
    mid = len(arr)//2 #mid = (int)(len(arr)/2)
    left_arr = merge_sort(arr[:mid])
    right_arr = merge_sort(arr[mid:])
    return merge(left_arr,right_arr)
def merge(left_arr,right_arr):
    empty = []
    left_index = 0
    right_index = 0
    while left_index < len(left_arr) and right_index < len(right_arr):
        if left_arr[left_index] < right_arr[right_index]:
            empty.append(left_arr[left_index])
            left_index += 1
        else:
            empty.append(right_arr[right_index])
            right_index += 1
    if left_index ==len(left_arr):
        for num in right_arr[right_index:]:
            empty.append(num)       
    else:
        for num in left_arr[left_index:]:
            empty.append(num)
    return empty
if __name__ =='__main__':
    arr = [1,3,5,7,9,2,8,0,-1,-2]
    output = merge_sort(arr)
    print(output)

猜你喜欢

转载自blog.csdn.net/qq_38358305/article/details/85863462