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

1、题目描述: 

给定一个字符串 s ,根据字符出现的 频率 对其进行 降序排序 。一个字符出现的 频率 是它出现在字符串中的次数。

返回 已排序的字符串 。如果有多个答案,返回其中任何一个。

示例 1:

输入: s = "tree"
输出: "eert"
解释: 'e'出现两次,'r'和't'都只出现一次。
因此'e'必须出现在'r'和't'之前。此外,"eetr"也是一个有效的答案。

示例 2:

输入: s = "cccaaa"
输出: "cccaaa"
解释: 'c'和'a'都出现三次。此外,"aaaccc"也是有效的答案。
注意"cacaca"是不正确的,因为相同的字母必须放在一起。

示例 3:

输入: s = "Aabb"
输出: "bbAa"
解释: 此外,"bbaA"也是一个有效的答案,但"Aabb"是不正确的。
注意'A'和'a'被认为是两种不同的字符。

提示:

  • 1 <= s.length <= 5 * 105
  • s 由大小写英文字母和数字组成

2、代码:

使用stl自带的排序函数(老鸟推荐)
#include <string>
#include <unordered_map>
#include <vector>
#include <algorithm>

using namespace std;

class Solution {
public:
    string frequencySort(string s) {
        // 统计每个字符的频率
        unordered_map<char, int> freq;
        for (char c : s) {
            freq[c]++;
        }
        
        // 将字符及其频率存入vector以便排序
        vector<pair<char, int>> vec;
        for (const auto& entry : freq) {
            vec.emplace_back(entry);
        }
        
        // 按频率降序排序,频率相同则顺序任意
        sort(vec.begin(), vec.end(), [](const pair<char, int>& a, const pair<char, int>& b) {
            return a.second > b.second;
        });
        
        // 构建结果字符串
        string result;
        for (const auto& entry : vec) {
            result += string(entry.second, entry.first);
        }
        
        return result;
    }
};
自写的排序算法(菜鸟推荐)
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>

using namespace std;

class Solution {
public:
    /**
     * @brief 根据字符出现频率对字符串进行降序排序
     * @param s 输入字符串
     * @return 按字符频率降序排列的新字符串(相同字符连续出现)
     * 
     * 实现步骤:
     * 1. 统计字符出现频率
     * 2. 将统计结果转换为可排序的容器
     * 3. 使用归并排序按频率降序排列
     * 4. 生成结果字符串
     */
    string frequencySort(string s) {
        // 阶段1:统计字符频率
        unordered_map<char, int> freq;
        for (auto& ch : s) {
            ++freq[ch];  // 使用哈希表统计字符出现次数
        }

        // 阶段2:转换存储结构
        vector<pair<char, int>> elements;
        for (auto& [ch, cnt] : freq) {
            elements.push_back(pair<char, int>(ch, cnt));  // 将哈希表转为vector便于排序
        }

        // 自定义降序比较规则(按频率值比较)
        auto cmp = [](const pair<char, int>& a, const pair<char, int>& b) {
            return a.second > b.second;  // 降序排列比较器
        };

        // 阶段3:执行归并排序
        mergeSort(elements, 0, elements.size() - 1, cmp);

        // 阶段4:构建结果字符串
        string result;
        for (auto& [ch, ctn] : elements) {
            result += string(ctn, ch);  // 根据排序结果生成最终字符串
        }
        return result;
    }

private:
    /**
     * @brief 归并排序递归主体
     * @param elements 待排序容器
     * @param left 当前区间左边界
     * @param right 当前区间右边界
     * @param cmp 自定义比较函数
     * 
     * 分治策略:
     * 1. 递归分解数组至单个元素
     * 2. 逐层合并有序子数组
     */
    template <typename Compare>
    void mergeSort(vector<pair<char, int>>& elements, int left, int right, Compare cmp) {
        if (left >= right) return;  // 递归终止条件
        
        int mid = left + (right - left) / 2;  // 防溢出计算中点
        mergeSort(elements, left, mid, cmp);    // 递归处理左半区间
        mergeSort(elements, mid + 1, right, cmp); // 递归处理右半区间
        merge(elements, left, right, mid, cmp);   // 合并两个有序区间
    }

    /**
     * @brief 合并两个有序子数组
     * @param elements 待合并容器
     * @param left 合并区间左边界
     * @param right 合并区间右边界
     * @param mid 合并分界点
     * @param cmp 自定义比较函数
     * 
     * 合并流程:
     * 1. 创建左右子数组临时副本
     * 2. 双指针遍历选择较大元素
     * 3. 处理剩余未合并元素
     */
    template <typename Compare>
    void merge(vector<pair<char, int>>& elements, int left, int right, int mid, Compare cmp) {
        // 创建左右子数组拷贝
        vector<pair<char, int>> leftElements(elements.begin() + left, 
                                           elements.begin() + mid + 1);
        vector<pair<char, int>> rightElements(elements.begin() + mid + 1, 
                                            elements.begin() + right + 1);

        int i = 0, j = 0, k = left;  // 三指针控制合并过程
        
        // 双路归并主循环
        while (i < leftElements.size() && j < rightElements.size()) {
            // 根据比较规则选择元素(注意保持排序稳定性)
            if (cmp(leftElements[i], rightElements[j])) {
                elements[k++] = leftElements[i++];
            } else {
                elements[k++] = rightElements[j++];
            }
        }

        // 处理左数组剩余元素
        while (i < leftElements.size())
            elements[k++] = leftElements[i++];
            
        // 处理右数组剩余元素(已修正原版错误)
        while (j < rightElements.size())
            elements[k++] = rightElements[j++];
    }
};

3、解题思路: 

注: 如果你是菜鸟,请使用第二种方法,对于注释,我自认为已经写得很漂亮了,你只需要跟着我敲就行了,对了,c++的新特性你一定要会一点,比如lambda表达式等,最后,送菜鸟们一句话:“要想能够超越,必先学会模仿!”

要解决这个问题,我们需要根据字符出现的频率对字符串进行降序排序,同时相同字符必须连续出现。具体步骤如下:

  1. 统计字符频率:遍历字符串,使用哈希表记录每个字符的出现次数。
  2. 转换为可排序结构:将哈希表中的键值对存入向量(vector)以便排序。
  3. 按频率排序:使用自定义的排序规则,按字符频率降序排列。频率相同的字符顺序可以任意。
  4. 生成结果字符串:遍历排序后的向量,将每个字符重复其出现次数次,拼接成结果字符串。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值