sort

作者: my_passion | 来源:发表于2022-07-20 10:16 被阅读0次
    默认按 从小到大 排序, insert 到 insertPos 前
    
    内部排序
        
        交换
            bubble
            quick
            
        insert
            直接
            折半
            希尔: 不 易 记, 不 care
            
        选择
            simple select
            heap sort
    
        归并
    

    1 交换 sort: 序列长度 n

    交换: 据 两 element key 的 compare result, 决定是否 交换 其 position

    1.1 bubble sort

    (1) 1 趟排序

    从后往前 / 从前往后 两两比较 相邻元素, 若 逆序, 则 交换;

        => untill 比较完 最后 2 个 => minElem 冒到 first/last position
    

    (2) 下一趟, 前面已冒出 elem 不参与, 待排序列 少 1 个

    (3) 最多 n - 1 趟 就能 sort 好 所有 elem

    void swap(int *v1, int *v2)
    {
        int tmp = *v1;
        *v1 = *v2;
        *v2 = tmp;
    }
    
    void bubbleSort(int arr[], int n)
    {
        //(1) n - 1 times
        for(int times = 0; times < n - 1; times++)
        {
            //(2) 从后往前 两两比较
            int walk;
            for(walk = n - 1; walk > times; walk--)
                if(arr[walk - 1] > arr[walk] )
                    swap(arr + walk - 1, arr + walk);
        }
    }
    

    改进: 加 swapFlag, 若 某趟 中无交换 => sort 提前完成

    void bubbleSort(int arr[], int n)
    {
        for(int times = 0; times < n - 1; times++)
        {
            int swapFlag = false;
            
            int walk;
            for(walk = n - 1; walk > times; walk--)
            {
                if(arr[walk - 1] > arr[walk] )
                {
                    swap(arr + walk - 1, arr + walk);
                    swapFlag = true;
                }
            }
            
            if(swapFlag == false)
                return;
        }
    }
    

    2 quick sort

    思想: 分治

    (1) 划分

        1) 待排序列 a[1..n]  中 
        
            任取一 elem 作 pivot 中心
    
        2) 用 `1 趟 快排` 将 a[1..n] 划分为 two parts
        
            a[1...pivotPos - 1] 和 a[pivotPos + 1.. n],
            
                分别 < 和 >= pivot
    
            => 找到 pivotPos
    
        3) pivotPos == 当前序列 pivot 的 最终排序 pos
    

    (2) 分别对 left / right 2 个 子序列 递归 上述过程

    #include <stdio.h>
    
    int partition(int a[], int left, int right);
        
    void quickSort(int a[], int left, int right)
    {
        // 递归 出口: left == right, 即 1 elem -> do nothing
        if(left < right)
        {
            int pivotPos = partition(a, left, right);
            
            quickSort(a, left, pivotPos - 1);
            quickSort(a, pivotPos + 1, right);
        }
    }
    
    int partition(int a[], int left, int right)
    {   
        // (1) 记录 pivot: 任选, 如取 left 元素 => 左元素无效
        int pivot = a[left];
        
        // (2) 循环出口: left == right 
        while(left < right)
        {
            // 双指针 left / right
            // 1) 从右向左 比 pivot: 右元素 大, 则 右指针 左移; else, 右元素 移到 左指针处 => 右元素 无效 
            while(left < right && a[right] >= pivot )
                right--;
            a[left] = a[right]; // Note:    左元素无效 
            
            // 2) 从左向右 比 pivot:  
            while(left < right && a[left] < pivot )
                left++;
            a[right] = a[left]; // Note:    右元素无效 
        }   
    
        // (3) pivot 放 最终 sorted pos
        a[left] = pivot; // Note:   left == right 
        
        // (4) return pivotPos
        return left;
    }
    
    // 2 1 3 -> left = 0, right = 2
    // pivot = 2 
    // while 1 1 3 -> 1
    

    空间效率

        `递归 => 用 递归工作栈: 栈 容量 与 递归最大深度 一致:`
    
        最好情况 ceil(log_2(n + 1) )
        最坏情况: n - 1 次 递归 => 栈 深度 O(n)
        平均: 栈深度 O(log_2 n)
    
        `最坏/平均 O(n) / O(log_2 n)`
    

    时间效率

        与 划分 是否对称(划分算法) 有关
        最坏: two parts 分别 n - 1 / 0 个 elem
            -> 这种 最大程度的不对称 发生在 每层递归上
            -> O(n^2)
    
        改进: 
        (1) sub_sequence 较小时, no longer 递归调 快排, 
            而是用 direct insert sort
    
        (2) 尽量选 1 个 可将 sequence 中分 的 pivot
    
        eg: 取 head/tail/mid elem -> 再取 3 者 的 middle 
    

    稳定性: 不稳定

        划分 中, 若 right range 有 2 key 相等, 且 < pivot
        => 交换到 left range 后, 2 key 相对位置 会变
    
            seq = {3, `2`, 2} -> 1趟 快排后 -> {2, `2`, 3}                      
    

    3 insert sort

    每次 将 待排 record 按其 key 大小 insertpreSortedSubsequence -> loop

    引出 3 insert 算法

    direct / half / shell insert
    

    3.1 直接插入: 边比较 边 移动元素

    (1) 某时刻, 前 i = 1,2,... 个 已 insert / sorted: sorted arr[0 .. i - 1] / arr[i] / unsorted arr[i + 1 .. ]

    (2) 插 arr[i] 到 sorted arr[0 .. i - 1]

    void directInsert(int arr[], int n)
    {
        for(int i = 1; i < n; i++) // (1) n-1 趟
        {
            if(arr[i - 1] > arr[i]  ) // (2) 逆序才需要插入
            {
                int targetToBeInsert = arr[i];
                
                int walk;
                
                // (3) 每1趟, 从前一 pos 开始, 逐个与 targetToBeInsert 比较, 逆序则 后移1位
                for(walk = i - 1; walk >=0 && arr[walk] > targetToBeInsert; --walk )
                    arr[walk + 1] = arr[walk]; 
                
                // (4) insert 到 targetPos
                arr[walk + 1] = targetToBeInsert;
            }
        }
    

    3.2 折半插入: 分离 比较 和 移动

    (1) 先 折半 find insertPos

    (2) 再 统一移动 insertPos + 之后 elem

    void halfSort(int arr[], int n)
    {
        for(int i = 1; i < n; i++)
        {
            int targetToBeInsert = arr[i - 1];
            int left = 0;
            int right = i - 1;
            int walk = 0;
    
            // exit: right = left + 1
            while(left <= right)
            {
                int mid = left + (right - left) / 2;
                 
                if(arr[mid] > targetToBeInsert)
                    right = mid - 1;
                else // <= 
                    left = mid + 1;
            }
    
            for(walk = i - 1; walk > right; walk--)
                arr[walk + 1] = arr[walk];
    
            arr[right + 1] = targetToBeInsert; 
        }
    }
    

    相关文章

      网友评论

          本文标题:sort

          本文链接:https://www.haomeiwen.com/subject/gcxeirtx.html