栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C++中stable

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C++中stable

比较

std::stable_sort - cppreference.com

  • 二者使用时都需要引入 "> 头文件,
  • stable_sort是基于归并排序的,是一种稳定排序。比较适合对比较复杂的数据进行排序,比如一些订单数据,,已经按照订单号排好了,现在想对交易日期再进行排序,就可以使用归并排序,排序后元素之间相对位置没有发生变化。
  • sort是基于快排实现的,是一种不稳排序,理想情况下时间复杂度 O(NlogN),空间复杂度O(logN),极端情况下时间复杂度O(N^2),空间复杂度O(N)
  • 二者都可以自定义comp函数,如果结果为true,则排序为args1, args2,否则args2,args1。所以,
    • 升序 return args1
    • 降序 return args1>args2
基于二叉树的实现 归并排序

总结:先把左半边数组排好,再把右半边数组排好,再把两个数组合并

这个过程可以在逻辑上抽象成一个二叉树,节点是数组区间,叶子结点的值就是数组元素。和二叉树的后续遍历是一样,

class Merge {
    // 用于辅助合并有序数组
    // 不在merge中new辅助数组,避免在递归中频繁分配内存和释放内存带来的性能问题
private:
    vector  tmp;
public:
    void sort(vector *nums) {
        tmp.clear();
        // 排序整个数组(原地修改)
        sort(nums, 0, nums->size() - 1);
    }
private:
    void sort(vector *nums, int low, int high) {
        // 单个元素不排序
        if (low == high) {
            return;
        }
        int mid = (high - low) / 2 + low;
        sort(nums, low, mid);
        sort(nums, mid+1, high);
        merge(nums, low, mid, high);
    }
    
    void merge(vector *nums, int low, int mid, int high) {
        // 先把num[low, high] 复制到tmp中
        // 以便合并后结果能直接存入nums
        for (int i=low; i<=high; i++) {
            tmp[i] = (*nums)[i];
        }
        // 数组双指针技巧,合并两个有序数组
        int i = low;
        int j = mid + 1;
        for (int idx=low; idx<=high; idx++) {
            if (i == mid+1) {
                (*nums)[idx] = tmp[j++];
            } else if (j == high+1) {
                (*nums)[idx] = tmp[i++];
            } else if (tmp[i] < tmp[j]) {
                (*nums)[idx] = tmp[i++];
            } else {
                (*nums)[idx] = tmp[j++];
            }
        }
    }
}

执行的次数是二叉树节点的个数,每次执行的复杂度就是每个节点代表的子数组的长度,所以总时间复杂度就是整棵树中数组元素的个数。整个树高度是logN,一层元素个数是N,所以总的时间复杂度是O(NlogN)

快速排序

总结:快速排序是先将一个元素排好序(左边元素都小于它,右边元素都大于它),再将剩下的元素排好序(递归左边的也排好,右边的也排好)。

这个过程抽象出来就是一个二叉搜索树的构建过程。为避免极端情况,可以先用洗牌算法将数组乱序,或者随机选择元素作为分界点。

class Quick {
    public:
        void sort(vector *nums) {
        // 避免极端情况,先随机打乱
        shuffle(nums);
        // 排序整个数组,原地修改
        sort(nums, 0, nums.size()-1);
    }
    private:
        void sort(vector *nums, int low, int high) {
        if (low >= high) {
            return;
        }
        int p = partition(nums, low, high);
        sort(nums, low, p);
        sort(nums, p+1, high);
    }
    private:
        void partition(vector *nums, int low, int high) {
            int pivot = (*nums)[low];
            int i = low + 1;
            int j = high;
            while (i <= j) {
                while(i pivot
                while(j>low && (*nums)[j]>pivot) {
                    j--;
                }  // 结束时,nums[j]<=pivot
                if (i>=j) break;
                swap(nums, i, j);
            }
            // 将pivot放在合适的位置
            swap(nums, low, j);
            return j;
        }
    // 洗牌算法
    private:
        void shuffle(vector *nums) {
        
            int n = nums.size()-1;
            for (int i = 0; i *nums, int i, int j) {
        int tmp = (*nums)[i];
        (*nums)[i] = (*nums)[j];
        (*nums)[j] = tmp;
    }
}

partiton 执行次数就是二叉树及诶蛋的个数,每次执行的复杂度就是每个节点代表的子数组nums[low, high]的长度,所以总的时间复杂度就是整棵树中数组元素的个数

假设数组元素个数为N,那么二叉树每一层元素个数之和就是O(N);分界点分布均匀情况下,树的层数是O(logN),所以理想时间复杂度O(NlogN)

由于快排米有使用任何辅助数组,所以空间复杂度就是递归的深度,即树高O(logN)

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/931972.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号