排序题目汇总

排序的提示

  • 排序
  • 第K个
  • 前K个
  • 频率
  • 荷兰国旗问题
  • 时间复杂度、空间复杂度
  • 原地排序(空间复杂度O(1))
  • 只遍历一遍:不能用计数排序

常见排序算法

#include <iostream>
#include <vector>
using namespace std;

// // ************************** 快排 **********************************
// int partition(vector<int> & nums, int l, int r)
// {
//     int p = l + rand() % (r - l + 1); // pivot位置。注意是r-l+1,不是r-l,这里写错了!!!
//     int pivot = nums[p]; // 记录下pivot值,避免换位置后下标取值失效
//     swap(nums[p], nums[r]); // pivot放到最后一个元素,好写一点

//     // !!!这里想不起来怎么写了
//     // 用[l, i)标记<=pivot的元素,用j去遍历数组元素,比pivot小的就换到i上去
//     int i = l;
//     for(int j = i; j < r; ++j) // 这里是j<r、不是j<=r,不取到最后一个值的,写错了!!!
//     {
//         if(nums[j] <= pivot)
//         {
//             swap(nums[i], nums[j]);
//             ++i;
//         }
//     }
//     swap(nums[i], nums[r]); // 把pivot换回去

//     return i;
// }

// void quickSortRange(vector<int> & nums, int l, int r)
// {
//     // 终止迭代条件
//     if (l >= r) return; 
//     // partition
//     int p = partition(nums, l, r);
//     // 递归子数组
//     quickSortRange(nums, l, p - 1);
//     quickSortRange(nums, p + 1, r);
// }

// void quickSort(vector<int> & nums)
// {
//     // 快速排序,Time O(nlogn), Space O(1)
//     int n = nums.size();
//     if(n == 0) return ;
//     quickSortRange(nums, 0, n - 1);
// }

// // ************************** 归并排序 **********************************
// void merge(vector<int> & nums, int l, int mid, int r, vector<int> & tmp)
// {
//     int i = l, j = mid + 1, p = l; // p是从l开始,不是从0开始!!!
//     // while(i <= mid && j <= r)
//     // {
//     //     if(nums[i] <= nums[j])
//     //     {
//     //         tmp[p] = nums[i];
//     //         ++i;
//     //     }
//     //     else 
//     //     {
//     //         tmp[p] = nums[j];
//     //         ++j;
//     //     }
//     //     ++p;
//     // }
//     // while(i <= mid)
//     // {
//     //     tmp[p] = nums[i];
//     //     ++i;
//     //     ++p;
//     // }
//     // while(j <= r)
//     // {
//     //     tmp[p] = nums[j];
//     //     ++j;
//     //     ++p;
//     // }

//     // 以上可以简写
//     while(i <= mid && j <= r)
//     {
//         if(nums[i] <= nums[j]) tmp[p++] = nums[i++];
//         else tmp[p++] = nums[j++];
//     }
//     while(i <= mid) tmp[p++] = nums[i++];
//     while(j <= r) tmp[p++] = nums[j++];

//     for(int m = l; m <= r; ++m) nums[m] = tmp[m]; //记得赋值给nums!!!是l到r,不是0到size-1!!!
//     return ;
// }

// void mergeSortRange(vector<int> & nums, int l, int r, vector<int> & tmp)
// {
//     if(l >= r) return ;
//     int mid = l + (r - l) / 2;
//     mergeSortRange(nums, l, mid, tmp);
//     mergeSortRange(nums, mid + 1, r, tmp); // 这里是从mid开始,不是mid+1,子数组里要包含mid!!!
//     // 就是这么写,对nums[l,...,mid]和nums[mid+1,...,r]进行合并!!!
//     merge(nums, l, mid, r, tmp);
//     return ;
// }

// void mergeSort(vector<int> & nums)
// {
//     if(nums.empty()) return ;
//     vector<int> tmp(nums.size(), 0); // 需要额外的用于排序数组的空间!!!
//     mergeSortRange(nums, 0, nums.size() - 1, tmp); 
//     return ;
// }


// // **************** 快排第二遍 ************** //
// int partition(vector<int> & nums, int l, int r)
// {
//     int p = l + rand() % (r - l + 1);
//     int pivot = nums[p];
//     swap(nums[p], nums[r]);
//     int i = l;
//     for(int j = l; j < r; ++j)
//     {
//         if(nums[j] < pivot) swap(nums[i++], nums[j]);
//     }
//     swap(nums[i], nums[r]);
//     return i;
// }

// void quickSortRange(vector<int> & nums, int l, int r)
// {
//     if(l >= r) return ;
//     int p = partition(nums, l, r);
//     quickSortRange(nums, l, p - 1);
//     quickSortRange(nums, p + 1, r);
// }

// void quickSort(vector<int> & nums)
// {
//     if(nums.empty()) return;
//     quickSortRange(nums, 0, nums.size() - 1);
//     return ;
// }



// // *************** 归并第二遍 **************/
// void merge(vector<int> & nums, int l, int mid, int r, vector<int> & tmp)
// {
//     int i = l, j = mid + 1, k = l; // 分别标记nums左段、右段、tmp指定区间的下标
//     while(i <= mid && j <= r)
//     {
//         if(nums[i] <= nums[j]) tmp[k++] = nums[i++];
//         else tmp[k++] = nums[j++];
//     }
//     while(i <= mid) tmp[k++] = nums[i++];
//     while(j <= r) tmp[k++] = nums[j++];
    
//     for(int p = l; p <= r; ++p) nums[p] = tmp[p];
//     return ;
// }

// void mergeSortRange(vector<int> & nums, int l, int r, vector<int>& tmp)
// {
//     if(l >= r) return ;
//     int mid = l + (r - l) / 2;
//     mergeSortRange(nums, l, mid, tmp);
//     mergeSortRange(nums, mid + 1, r, tmp);
//     merge(nums, l, mid, r, tmp);
//     return ;
// }

// void mergeSort(vector<int> & nums)
// {
//     if(nums.empty()) return ;
//     vector<int> tmp(nums.size(), 0);
//     mergeSortRange(nums, 0, nums.size() - 1, tmp);
//     return ;
// }


// *************** 冒泡排序 ****************
// Time O(n^2), Space O(1)
void bubbleSort(vector<int> & nums)
{
    // 进行n次冒泡,每次冒泡通过相邻元素的两两交换,将未排序部分[0, n - i)的最大值放到最右侧;如果某次冒泡没有交换发生,说明数组已经有序了
    int n = nums.size();
    if(n == 0) return ;
    bool swapped;
    for(int i = 0; i < n; ++i) // i仅表示第几次冒泡,不会影响到冒泡起始下标
    {
        swapped = false;
        for(int j = 0; j < n - i - 1; ++j) // 每次冒泡都是从下标0开始的!!!不是i
        {
            if(nums[j] > nums[j + 1])
            {
                swap(nums[j], nums[j + 1]);
                swapped = true;
            }
        }
        if(!swapped) break; // 某次没有冒泡,就已经有序了
    }
    return ;
}

// ************ 选择排序:知道怎么做的,但自己没写出来 *************
// Time O(n^2), Space O(1)
void selectSort(vector<int> & nums)
{
    int n = nums.size();
    if(n == 0) return;
    // 从右侧未排序数组中选择最小值,放到左侧已排序数组的末尾

    int k; // 标记未排序区间最小值的下标
    for(int i = 0; i < n - 1; ++i)
    {
        k = i;
        for(int j = i; j < n; ++j)
        {
            if(nums[j] <= nums[k]) k = j; // !!!一开始没想出来
        }
        swap(nums[i], nums[k]); // !!!一开始没想出来
    }
    
    return ;
}

// ************ 插入排序 ****************//
// Time O(n^2), Space O(1)
void insertSort(vector<int> & nums)
{
    // 将右侧未排序区间的第一个数,插入到左侧已排序区间的合适位置,插入的方法是交换过去
    int n = nums.size();
    if(n == 0) return ;
    for(int i = 0; i < n - 1; ++i) // [0, i]标记已排序区间
    {
        for(int j = i + 1; j > 0; --j) // !!!写对了
        {
            if(nums[j] < nums[j - 1]) swap(nums[j], nums[j - 1]); // !!!写对了
        }
    }
    return ;
}

int main()
{
    vector<int> nums = {1,3,5,7,2,6,4,8,9,2,8,7,6,0,3,5,9,4,1,0};
    // quickSort(nums);
    // mergeSort(nums);
    // bubbleSort(nums);
    // selectSort(nums);
    insertSort(nums);

    for(auto x: nums) cout << x << ", ";
    return 0;
}

215. 数组中的第K个最大元素

class Solution {
public:
    int partition(vector<int> & nums, int l, int r)
    {
        int p = l + rand() % (r - l + 1);
        int pivot = nums[p];
        swap(nums[p], nums[r]);
        int i = l; // 标记小于pivot的元素中最右侧的位置
        for(int j = l; j < r; ++j)
        {
            if(nums[j] < pivot) swap(nums[i++], nums[j]);
        }
        swap(nums[i], nums[r]);
        return i;
    }

    int findKthLargest(vector<int>& nums, int k) {
        // partition + 二分查找
        // 注意:第k个最大的元素,对应nums排序后下标nums.size() - k
        int n = nums.size();
        int l = 0, r = n - 1, mid;
        while(l <= r)
        {
            mid = partition(nums, l, r);
            if(mid == n - k) return nums[mid];
            if(mid > n - k) r = mid - 1; // 注意n-k是target,target是更小的值,要向左找!!!
            else l = mid + 1;
        }
        return nums[r];
    }
};

347. 前 K 个高频元素

class Solution {
public:
    vector<int> topKFrequent(vector<int>& nums, int k) {
        // 桶排序, Time O(n), Space O(n)
        /*
            1. 先统计{元素:频率},用unordered_map<int, int>存
            2. 再分桶{频率:[元素1, 元素2,...]},用vector<vector<int>>存,注意这里的频率是不一定连续的
            3. 从频率较大的桶开始,返回k个元素
        */

        unordered_map<int, int> count;
        int maxCount = 0;
        for(auto & num : nums)
        {
            ++count[num];
            maxCount = max(maxCount, count[num]);
        }

        // vector<vector<int>> buckets(maxCount + 1, nums.size()); // 不是这么写的!!!
        vector<vector<int>> buckets(maxCount + 1);
        for(auto & p : count)
        {
            buckets[p.second].push_back(p.first);
        }

        vector<int> res;
        for(int i = maxCount; i >= 0 && res.size() < k; --i) // i不能>=maxCount - k + 1,因为maxCount里的值是不连续的!!!
        {
            for(auto & p: buckets[i])
            {
                res.push_back(p);
                if(res.size() == k) break;
            }
        }

        return res;
    }
};

451. 根据字符出现频率排序

class Solution {
public:
    string frequencySort(string s) {
        // 桶排序, Time O(n), Space O(n)
        // 跟347题一样
        // 一遍过

        // 统计{字符: 频率}
        unordered_map<char, int> counts;
        int maxCount = 0;
        for(auto & c : s)
        {
            ++counts[c];
            maxCount = max(maxCount, counts[c]);
        }

        // 分桶 {频率:[字符1, 字符2, ...]},其中频率是数组的下标、不是key
        vector<vector<char>> buckets(maxCount + 1); // 注意下定义方法
        for(auto & ele : counts)
        {
            buckets[ele.second].push_back(ele.first);
        }

        // 按从大到小的桶输出元素,即排好序了
        string res;
        for(int i = maxCount; i >= 0; --i)
        {
            for(auto & x: buckets[i])
            {
                for(int k = 0; k < i; ++k)
                {
                    res += x;
                }
            }
        }

        return res;
    }
};

75. 颜色分类
荷兰国旗问题:循环不变量(声明的变量在遍历的过程中需要保持定义不变)

只遍历一遍:不能用计数排序
原地排序:空间复杂度是O(1)

class Solution {
public:
    void sortColors(vector<int>& nums) {
        /* 循环不变量
            定义:
            [0, p0): 0
            [p0, i): 1,i是遍历的
            (p2, len-1): 2

            1. 初始化为空区间:p0 = 0, i = 0, p2 = len - 1
            2. 循环结束条件:i > p2 (如果i == p2,这个元素会没被看到)
            3. 循环内操作:
                (1)if nums[i] == 0: swap(i, p0), p0++, i++ 
                (2)if nums[i] == 1: i++
                (3)if nums[i] == 2: swap(i, p2), p2--, 没有i--(因为交换过来的元素在i上还没有被看到)

		Time: O(n), Space: O(1)
        */

        // 定义好循环不变量
        // [0, p0): 0
        // [p0, i): 1,i是遍历的
        // (p2, len-1): 2

        int p0 = 0, i = 0, p2 = nums.size() - 1;
        while(i <= p2)
        {
            if(nums[i] == 0) swap(nums[i++], nums[p0++]);
            else if(nums[i] == 1) ++i;
            else swap(nums[i], nums[p2--]);
        }
        return ;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值