[杂记] 排序Python与C++实现(冒泡、选择、插入、快速、归并、堆排序)

0.

在这里插入图片描述

1.冒泡排序

'''
冒泡排序
'''

def BubbleSort(array:list) -> list:
    '''
    冒泡排序就是两两比较,大的沉底或者小的上浮,
    一共比较len(array)次,每次比较只比较还未最终上浮(下沉)的
    时间复杂度:O(n^2) 空间复杂度:O(1)
    '''
    for times in range(len(array)):
        for idx in range(len(array) - times - 1):# 一共有times个已经排序完毕 -1是因为后面的索引有idx+1
            
            if array[idx + 1] < array[idx]:
                array[idx + 1],array[idx] = array[idx],array[idx + 1]
    
    return array
template<typename T>
void bubbleSort(T arr[], int length){
    
    
    for (int i = 0; i < length; ++i){
    
    
        for (int j = 0; j < length - i - 1; ++j){
    
    
            if (arr[j] > arr[j + 1])
                std::swap(arr[j], arr[j + 1]);
        }
    }
}

2.快速排序

'''
快速排序
'''

# 方法1
def QuickSort(array:list,left_index:int,right_index:int) -> list:
    '''
    采用分治法
    初始时对第一个元素视为基准元素,应当排成基准元素的左边都比基准元素小,右边都比基准元素大.
    然后用两个头尾指针.头指针向后,尾指针向前.
    如果尾指针遇到比基准数小的,就和头指针互换,之后头指针向后,遇到比基准值大的,和尾指针互换,
    之后尾指针再向前,重复这个过程.

    头指针等于尾指针时结束,并将这个位置放入基准值.

    分:将基准值的左右分开处理
    治:分别按照上述算法处理

    时间复杂度O(nlogn),空间复杂度O(nlogn)(采用递归)
    '''
    left , right = left_index , right_index
    temp = array[left] # 将第一个元素选为基准元素,暂存

    if not left < right: # 如果不是left<right,也就是array只有一个元素,直接返回
        return array

    # 整个while做的事情就是i和j不断地寻找遍历
    while left != right:

        while array[right] >= temp and right > left: # 如果right处的值大于等于基准值且不等于left,就继续找

            right -= 1
         
        if right == left: # 等于就是我们的目标 用基准值覆盖此处值 退出
            array[left] = temp
            break
        else: # 否则就说明找到了第一个小于基准值的点 将left处的值赋成该点的值
            array[left] = array[right]

        while array[left] <= temp and left < right: # 如果left处的值小于等于基准值且不等于right,就继续找

            left += 1 
        
        if left == right:# 等于就是我们的目标 用基准值覆盖此处值 退出
            array[right] = temp
            break
            
        else: # 否则就说明找到了第一个比基准值大的点 将right处的值赋成该点值
            array[right] = array[left]

    # 以基准点为中心 分别对左右序列递归排序 注意left - 1 < left_index和left + 1 > right_index
    # 的情形已经包含在开头if not left < right: 中了.
    QuickSort(array,left_index,left - 1)
    QuickSort(array,left + 1,right_index)

    return array
# 方法2
'''
利用快指针和慢指针来选择基准值应该在的位置(上个方法是头指针和尾指针)
快指针快速遍历整个序列,慢指针保证所有小于慢指针的索引的值都小于等于基准值

'''

def partition(array:list,left:int,right:int) -> int:
    '''
    找出基准值应插入的点
    
    '''
    if left == right:
        return left
    # 将基准点设置为第一个点
    p = left

    # 慢指针初始化为基准点右边的点
    index = p + 1

    for i in range(index,right + 1): # 取值范围index~right
        if array[i] < array[p]: # 如果小于基准值 就交换和index的位置 且将index加1 保证小于index的都是小于等于基准值
            array[index] , array[i] = array[i] , array[index]
            index += 1
        
    # 小于index的位置都是小于等于基准值,则基准值应插入在前一个位置
    array[p] , array[index - 1] = array[index - 1] , array[p]

    return index - 1 # 分界点为index - 1


def QuickSort(array:list,left_index:int,right_index:int) -> list:
    '''
    同理 递归
    '''
    if left_index < right_index:
        p = partition(array,left_index,right_index)
        QuickSort(array,left_index,p - 1)
        QuickSort(array,p + 1,right_index)

    return array

template<typename T>
void quickSort(T arr[], int length){
    
    
    if (length < 2) return;  // 递归停止条件

    int pLeft = 0, pRight = length - 1;  // 初始化双指针
    T tempValue = arr[pLeft];  // 将第一个元素作为基准元素
    while(pLeft != pRight){
    
    
        while(pRight > pLeft && arr[pRight] > tempValue) pRight--;  // 基准值的右边应该都比基准值大

        // 上一个while退出 说明要么找到了<=tempValue的点 要么和left相遇
        if (pRight == pLeft) break;  // 相遇就退出
        else {
    
    arr[pLeft] = arr[pRight]; pLeft++;}  // 把小的值赋给左指针 左指针加一
        
        while(pRight > pLeft && arr[pLeft] < tempValue) pLeft++;  // 基准值的左边应该都比基准值小

        // 上一个while退出 说明要么找到了>=tempValue的点 要么和right相遇
        if (pRight == pLeft) break;  // 相遇就退出
        else {
    
    arr[pRight] = arr[pLeft]; pRight--;}  // 把小的值赋给左指针 左指针加一
    }

    arr[pLeft] = tempValue;  // 相遇的点应为temp

    // 递归 对左右分别再排序
    quickSort<T>(arr, pLeft);
    quickSort<T>(arr + pLeft + 1, length - pLeft - 1);
}

3.简单插入排序

'''
插入排序
'''

# 方法1
def InsertSort(array:list) -> list:
    '''
    插入排序:
    跟选择排序很像,初始将第一个元素视为有序,其余视为无序.从无序中
    选择第一个元素,将其插入有序序列,与有序序列元素逐个比较,找到它的位置.
    时间复杂度:O(n^2) 空间复杂度:O(1)

    不能用递归实现,因为新元素还要在有序序列中排序,而不是像选择排序那样有序之后不管了.
    '''

    ordered = 1 # 有序序列的长度
    
    while ordered < len(array):
        item = array[ordered:][0] # 取无序序列第一个

        insert_pos = ordered 

        for _ in range(ordered):# 在有序序列里遍历
            if item <= array[insert_pos - 1]:# 如果待插入值小于当前遍历值 就往前挪
                insert_pos -= 1
            else:
                break
        
        # 更新array:在有序序列中,取insert_pos之前的元素,insert_pos加入item,取(原本有序序列)insert_pos之后元素,
        # 在无序序列跳过第一个,即跳过索引为ordered,取余下无序序列.
        array = array[:insert_pos] + [item] + array[insert_pos : ordered] + array[ordered + 1:]

        ordered += 1 # 有序序列长度加1

    
    return array

# 方法2
def InsertSort(array:list) -> list:
    '''
    更简单的实现,取无序第一个元素,在有序序列后面开始逐一比较
    和法1不同的是没有明显地区分有序和无序
    '''
    for item in range(1,len(array)): # 无序序列item从1开始
        
        pre_index , current = item - 1 , array[item] # pre_index是理论插入位置的前一元素,current是当前元素的值

        while pre_index >=0:
            if current < array[pre_index]:
                array[pre_index + 1] = array[pre_index] # 如果当前值小于前一索引值 就挪 腾地方
                
                pre_index -= 1
            else:
                break

        array[pre_index + 1] = current # 该插入的地方是pre_index + 1
    
    return array
template<typename T>
void insertionSort(T arr[], int length){
    
    
    // 对于位置i, 假设0~i-1个位置已经排序好了
    // 只要在排序好的位置中找到第i个的位置

    // 用递归 写明递归停止条件
    if (length < 2) return; 

    // 假定0~i-1个位置已经排序好了
    
    insertionSort<int>(arr, length - 1);
    
    for (int idx = length - 1; idx > 0; --idx){
    
    
        if (arr[idx] < arr[idx - 1])
            std::swap(arr[idx], arr[idx - 1]);
    }
}

4.简单选择排序

'''
选择排序
'''

def SelectionSort(array:list) -> list:
    '''
    简单选择排序:
    初始:将整个序列视为无序序列,将无序序列最小的数与第一个数进行交换,并将第一个数认为有序序列
    重复
    时间复杂度:O(n^2) 空间复杂度:O(1)(用递归就不是了)
    '''
    if len(array) == 1: # 只剩一个元素递归结束
        return array
    # 找最小的数
    min_idx = 0
    for idx in range(len(array)):
        if array[min_idx] > array[idx]:
            min_idx = idx 
    
    # 交换
    array[0],array[min_idx] = array[min_idx],array[0]

    # 递归 有序为[array[0]] 无序为SelectionSort(array[1:]), 继续排序
    return [array[0]] + SelectionSort(array[1:])
template<typename T>
void selectionSort(T arr[], int length){
    
    
    // 找最小的 和序列第一个数交换
    // 递归查找第一个数右边的序列

    // 递归停止条件
    if (length < 2) return;

    // 找最小的
    int minIndex = 0;
    for (int idx = 1; idx < length; ++idx){
    
    
        if (arr[idx] < arr[minIndex]) minIndex = idx;
    }

    std::swap(arr[0], arr[minIndex]);

    selectionSort<T>(++arr, --length);  // 递归调用剩余的
}

5.堆排序

'''
堆排序
'''

def adjust(array:list,length:int,node:int) -> None:
    '''
    调整树的结构,让树保持最大堆结构,也即保证父节点大于等于两个子节点.
    node为父节点标号,length为arr长度
    '''

    largest = node # 初始化最大节点为父节点

    left , right = 2 * node + 1 , 2 * node + 2 # 左右子节点标号 完全二叉树的性质

    # 一下两个if是为了找到三个节点中最大节点 别忘了判断<length 因为有的父节点没有子节点
    if left < length and array[left] > array[largest]:
        largest = left 

    if right < length and array[right] > array[largest]:
        largest = right
    
    if largest != node: # 如果largest改变 将node和最大节点互换 并且递归地向下调整,因为可能影响到后面的子树
        array[largest] , array[node] = array[node] , array[largest]
        adjust(array,length,largest)

def HeapSort(array:list) -> list:
    '''
    堆排序就是首先将数组变为堆结构(任一父节点都不小于其子节点)
    然后将堆的根节点(最大值)和最末,次末,...节点互换 然后调整堆
    直到所有的节点都被换(遍历)过
    '''

    # 构建最大堆
    for node in range(len(array),-1,-1): # 注意,必须从最后一个节点开始遍历才能遍历整个树,从头结点就容易遗漏
        # 例如array = [1,2,3,4,5] 从上至下调:
        # 第一次:[3,2,1,4,5] 递归至1,没有子节点 结束
        # 第二次:[3,5,1,4,2] 递归至4,2 无子节点 结束 可见结果不正确
        adjust(array,len(array),node)
    
    # 交换len(array)次节点 调整
    for idx in range(len(array) - 1,-1,-1):
        array[0] , array[idx] = array[idx] , array[0]
        adjust(array,idx,0) # 自上而下调整就ok 但只调前index个,因为已经排序的排除在外
    
    return array

6.二路归并排序

'''
归并排序
'''
from math import floor


def merge(array1:list,array2:list):
    '''
    将list1和list2合并成有序序列
    由于array1和array2已经是有序的,因此只要逐位比较.将小的放入结果数组,
    且将对应的指针右移.
    '''
    result = [] # 结果数组

    p1,p2 = 0,0 # 两个指针

    while p1 < len(array1) and p2 < len(array2): # 有一个到头了就退出
        if array1[p1] <= array2[p2]:
            result.append(array1[p1])
            p1 += 1
        else:
            result.append(array2[p2])
            p2 += 1

    # 两者必有一个为空
    if p1 == len(array1):
        result += array2[p2:]
    else:
        result += array1[p1:]

    return result


def MergeSort(array:list) -> list:

    if len(array) == 1: # 只有一个就结束
        return array
    
    return merge(MergeSort(array[:floor(len(array) / 2)]),MergeSort(array[floor(len(array) /2 ):]))
template<typename T>
void mergeArr(T arr[], int length1, int length2){
    
    
    // 聚合arr[: length1 - 1], arr[length1: ]
    int totalLength = length1 + length2;

    int p1 = length1 - 1, p2 = totalLength - 1;  // 两个指针初始指向两部分的尾部

    T arr_[totalLength];

    int idxOfArr_ = totalLength - 1;

    while(idxOfArr_ >= 0){
    
    
        // std::cout << p1 << p2 << idxOfArr_ << '\n';
        if (p1 < 0){
    
      // 有一方遍历完了 就赋值另一方
            arr_[idxOfArr_] = arr[p2];
            --p2;
        }
        else if (p2 < length1){
    
    
            arr_[idxOfArr_] = arr[p1];
            --p1;
        }          
        else if (arr[p2] > arr[p1]){
    
      // 正常情形
            arr_[idxOfArr_] = arr[p2];
            --p2;
        }            
        else{
    
    
            arr_[idxOfArr_] = arr[p1];
            --p1;
        }
        --idxOfArr_;
    }


    for (int i = 0; i < totalLength; ++i) arr[i] = arr_[i]; // 更新arr的值

}

template<typename T>
void mergeSort(T arr[], int length){
    
    
    // 分成左右两部分排序
    // 每一步 假设左右两部分已经排好了 只需要合并即可
    // 合并采用双指针法

    // 递归调用 定义停止条件
    if (length < 2) return;

    int length1 = length / 2, length2 = length - length1;  // 分成两部分的长度
    // 假定这两部分已经排好序
    
    mergeSort<T>(arr, length1);
    mergeSort<T>(arr + length1, length2);

    // 将排好序的部分聚合
    mergeArr<T>(arr, length1, length2);

}

猜你喜欢

转载自blog.csdn.net/wjpwjpwjp0831/article/details/122135929