IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> 【每日一题】力扣451. 根据字符出现频率排序 -> 正文阅读

[数据结构与算法]【每日一题】力扣451. 根据字符出现频率排序


题目

题目链接:451. 根据字符出现频率排序

给定一个字符串 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 由大小写英文字母和数字组成

解题思路

👉哈希表

可以根据题目的意思直接求解

用哈希表统计每个字符出现的频次,然后对哈希表进行从大到小排序,再逐个把每个字符按照频次连接,返回连接后的字符串。

虽然思路简单,但是具体的操作过程还是有点麻烦的,尤其对哈希表不熟悉的话就很难实现了。我认为具体的代码实现有下面几个难点:

  • 遍历哈希表
  • 对哈希表从大到小排序

对于哈希表里的数据类型应该都没什么问题,就是 charint 型。遍历所给字符串,统计字符频次存入哈希表。然后就需要对哈希表排序,哈希表的排序需要用到一个 vector 模板,把哈希表对应的键值对放入一个模板数组中,然后对模板数组排序,后面就使用模板数组操作。

所以排序哈希表需要遍历哈希表,如果熟悉C++11,那么可以直接使用 auto 遍历。但我一直不太用的习惯它,所以我选择定义迭代器遍历。

vector<pair<char, int>> vec;			//定义一个模板数组
unordered_map<char, int>::iterator iter;
for (iter = map.begin(); iter != map.end(); iter ++ ) {
    vec.push_back(make_pair(iter->first, iter->second));
    //将哈希表内容放入数组中。make_pair是一个转换模板的数组
}

C++没有提供模板的排序,所以模板的排序需要自己定义。不过不是完全自己定义,可以用 sort 函数,但排序规则需要自己写。sort 函数的第三个参数是一个函数,写法如下:

static bool cmp(参数) {	//必须是 static bool
	内容
}

排序后即可使用该数组将字符放入新的字符串。

👉暴力

还有一种纯暴力的写法,用一个数组存储每个字符的频次,然后用双重循环按频次逐个从大到小找到每个字符。

  • 第一重:遍历数组
  • 第二重:找到频次最大的那个字符,结束后将字符连入字符串,同时将其频次置 0

找到字符后按照字符的频次将字符连入新的字符串。

虽然这种方法很暴力,但是我认为这种方法比第一种方法好一点。虽然是双重循环,但循环的次数最大不会超过 256 ,所以双重循环的时间复杂度可以近似看成一个常数。

👉桶排序

这才是这题的正解

桶排序相当于是对方法一进行了优化,虽然也使用了哈希表,但是除去了对哈希表的排序,用一种很巧妙的思路解题。

首先还是把字符的频次放入哈希表中,同时找出最大的频次数 maxnum 。用一个大小为 maxnum 的字符串数组,按照字符的频次把字符放入对应的数组空间中。

这样每个字符的位置就对应着它的频次,可以逆向遍历该数组,然后再遍历每个数组空间里的字符串,根据其对应的频次放入新字符串中。

为什么要遍历数组空间的字符串?

频次相同的字符会放入同一个空间中,所以不能确定一个空间中是否只含有一种字符。

代码(C++)

哈希表

class Solution {
public:
    static bool cmp(pair<char, int> map1, pair<char, int> map2) {
        return map1.second > map2.second;
    }
    unordered_map<char, int> map;
    string frequencySort(string s) {
        for (int i = 0; i < s.size(); ++ i )
            map[s[i]] ++;
        vector<pair<int, int>> m;
        unordered_map<char, int>::iterator iter;
        for (iter = map.begin(); iter != map.end(); iter ++ ) {
            m.push_back(make_pair(iter->first, iter->second));
        }
        sort(m.begin(), m.end(), cmp);
        string str;
        vector<pair<int, int>>::iterator vec;
        for (vec = m.begin(); vec != m.end(); vec ++ ) {
            string sb;
            for (int i = 0; i < vec->second; ++ i ) {
                sb += vec->first;
            }
            str += sb;
        }
        return str;
    }
};

暴力

class Solution {
public:
    int f[200];
    string frequencySort(string s) {
        for (int i = 0; i < s.size(); i ++ ) {
            f[s[i]] ++;
        }
        string str;
        for (int i = 0; i < 200; i ++ ) {
            int m = 0, n = 0;
            for (int j = 0; j < 200; j ++ ) {
                if (f[j] > m) {
                    m = f[j];
                    n = j;
                }
            }
            if (m > 0) {
                for (int j = 0; j < m; j ++ ) {
                    str += (char)n;
                }
                f[n] = 0;
            }
        }
        return str;
    }
};

桶排序

class Solution {
public:
    string frequencySort(string s) {
        unordered_map<char, int> map;
        int maxunm = 0;
        for (int i = 0; i < s.size(); i ++ ) {
            maxunm = max(maxunm, ++ map[s[i]]);
        }
        vector<string> vec(maxunm + 1);
        unordered_map<char, int>::iterator iter;
        for (iter = map.begin(); iter != map.end(); iter ++ ) {
            vec[iter->second].push_back(iter->first);
        }
        string str;
        for (int i = maxunm; i >= 0; i -- ) {
            string ch = vec[i];
            for (int j = 0; j < vec[i].size(); ++ j) {
                for (int k = 0; k < i; ++ k ) {
                    str += ch[j];
                }
            }
        }
        return str;
    }
};

总结

如果第一种方法对于哈希表的排序不了解,可以多看看哈希表的用法。第二种方法应该是很多人可以想到的,但为了方便估计多数会选择哈希做法。对于桶排序,我觉得还是有点难想到的。总的来说这题不算太难,但也不算太简单,算是一道中等题。

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-04-07 22:56:50  更:2022-04-07 22:57:09 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 9:36:58-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码