给定一个字符串,找出不含有重复字符的最长子串的长度。
示例:
给定 "abcabcbb"
,没有重复字符的最长子串是 "abc"
,那么长度就是3。
给定 "bbbbb"
,最长的子串就是 "b"
,长度是1。
给定 "pwwkew"
,最长子串是 "wke"
,长度是3。请注意答案必须是一个子串,"pwke"
是 子序列 而不是子串。
class Solution {
public:
int lengthOfLongestSubstring(string s) {
// 双索引滑动查找思路:维护好[i,j]数据段内的数据不重复即可
// 当j往前查询发现存在重复元素时,那么缩减i,直至数据段内没有重复元素
int i = 0;
int j = -1;
int res = 0;
int n = s.length();
int contain[256] = { 0 };
while (i < n) {
// 往前探索的数据存在重复值
if (contain[s[j + 1]]>0) {
contain[s[i++]]--;
}
else { // 往前探索的数据唯一
if (j + 1 >= n)
return res;
contain[s[++j]]++;
res = std::max(res, j - i + 1);
}
}
return res;
}
};
给定 n 个非负整数 a1,a2,...,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
说明:你不能倾斜容器,且 n 的值至少为 2。
示例:
输入: [1,8,6,2,5,4,8,3,7]
输出: 49
class Solution {
public:
int maxArea(vector& height) {
// 对撞指针
int l = 0;
int r = height.size()-1;
int max_val = 0;
while (l < r) {
max_val = std::max(max_val, std::min(height[l], height[r])*(r - l));
if (height[l] < height[r])
l++;
else
r--;
}
return max_val;
}
};
给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
示例 1:
给定数组 nums = [1,1,2], 函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为1
,2
。 你不需要考虑数组中超出新长度后面的元素。
示例 2:
给定 nums = [0,0,1,1,1,2,2,3,3,4], 函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为0
,1
,2
,3
,4
。 你不需要考虑数组中超出新长度后面的元素。
class Solution {
public:
int removeDuplicates(vector& nums) {
if (nums.empty())
return 0;
// 一次遍历,[0,k)为最终的不重复数组
int n = nums.size();
//int val = nums[0];
int k = 1;
for (int i = 1; i < n; i++) {
if (nums[i] != nums[k-1]) {
nums[k] = nums[i];
k++;
}
}
return k;
}
};
// 给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素最多出现两次,返回移除后数组的新长度。
// 不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
// 上面的升级版:使得每个元素最多出现两次
class Solution1 {
public:
int removeDuplicates(vector& nums) {
// 我们只需要在上面的逻辑加上对相同值计数的辅助变量
if (nums.empty())
return 0;
// 一次遍历,[0,k)为最终的每个元素最多出现两次的数组
int n = nums.size();
int k = 1;
int count = 1;
for (int i = 1; i < nums.size(); i++) {
if (nums[i] == nums[k - 1] && count == 1) {
nums[k++] = nums[i];
count++;
}
else if (nums[i] != nums[k - 1]) {
nums[k++] = nums[i];
count = 1;
}
}
return k;
}
};
给定一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
示例 1:
给定 nums = [3,2,2,3], val = 3,
函数应该返回新的长度 2, 并且 nums 中的前两个元素均为 2。
你不需要考虑数组中超出新长度后面的元素。
示例 2:
给定 nums = [0,1,2,2,3,0,4,2], val = 2, 函数应该返回新的长度5
, 并且 nums 中的前五个元素为0
,1
,3
,0
, 4。 注意这五个元素可为任意顺序。 你不需要考虑数组中超出新长度后面的元素。
// 一次遍历,使得遍历过程中[0,k)不为val值,
// [k,i]为val值,最终val值会被移动到最后
class Solution {
public:
// 一次遍历,使得遍历过程中[0,k)不为val值,
// [k,i]为val值,最终val值会被移动到最后
int removeElement(vector& nums, int val) {
int k = 0;
for (int i = 0; i < nums.size(); i++) {
if (nums[i] != val)
if (k != i)
swap(nums[k++], nums[i]);
else
k++;
}
return k;
}
// 优化,使得最终可以不需要一次遍历,可以提前结束
// [0,i]不为val值,(j,nums.size()-1]为val值
// 注意:这个算法会改变数的顺序,但是题目是允许的
// 这个算法步骤会少,但是判断条件鬼多,没有上面的简单明了
int removeElement2(vector& nums, int val) {
int j = nums.size() - 1;
for (int i = 0; i < nums.size(); i++) {
if (i > j)
return i;
// 检测到等于val值,需要交换
if (nums[i] == val) {
// 倒数第一位不是val的数
while (nums[j] == val)
j--;
// 实际上不应该出现这种情况,所以判断一下
if (j < i)
return i;
// 数组中的值都为val
if (j < 0)
return 0;
swap(nums[i], nums[j]);
j--;
}
}
return nums.size();
}
};
给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
注意:
不能使用代码库中的排序函数来解决这道题。
示例:
输入: [2,0,2,1,1,0]
输出: [0,0,1,1,2,2]
进阶:
class Solution {
public:
void sortColors(vector& nums) {
// 计数排序:统计出1,2,3数字的个数,然后往nums赋值即可
int count[3] = { 0 };
int n = nums.size();
for (int i = 0; i < n; i++) {
count[nums[i]]++;
}
int index = 0;
for (int i=0; i < count[0]; i++)
nums[index++] = 0;
for (int i = 0; i < count[1]; i++)
nums[index++] = 1;
for (int i = 0; i < count[2]; i++)
nums[index++] = 2;
}
// 类似三路排序的优化方案:维护好三个指针
// arr[0,zero_index]==0,arr[zero_index+1,i-1]==1,arr[two_index,n-1]==2
// 终止条件:i>=two
void sortColors2(vector& nums) {
int zero_index = -1;
int n = nums.size();
int two_index = n;
for (int i = 0; i < n;) {
if (i >= two_index)
return;
if (nums[i] == 1)
i++;
else if (nums[i] == 0) {
swap(nums[++zero_index], nums[i]);
i++;
}
else if (nums[i] == 2) {
swap(nums[i], nums[--two_index]);
}
}
}
};
给定两个有序整数数组 nums1 和 nums2,将 nums2 合并到 nums1 中,使得 num1 成为一个有序数组。
说明:
示例:
输入:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6], n = 3
输出: [1,2,2,3,5,6]
class Solution {
public:
void merge(vector& nums1, int m, vector& nums2, int n) {
// 另外生成一个空间用于存储merge操作
int *mergeSort = new int[m + n];
int index1 = 0;
int index2 = 0;
for (int i = 0; i < m + n; i++) {
// nums1放置完毕
if (index1 >= m) {
mergeSort[i] = nums2[index2++];
}
else if (index2 >= n) { // nums2放置完毕
mergeSort[i] = nums1[index1++];
}
else if (nums2[index2] < nums1[index1]) {
mergeSort[i] = nums2[index2++];
}
else {
mergeSort[i] = nums1[index1++];
}
}
for (int i = 0; i < m + n; i++)
nums1[i] = mergeSort[i];
delete mergeSort;
}
// 再思考一下有没有可能进行原地操作呢???
// 应该是可以的,将nums2先插入到nums1的后面,然后进行插入排序操作,但是这个时间复杂度会比上面的算法高
// 优点是空间复杂度为1
void merge1(vector& nums1, int m, vector& nums2, int n) {
/*int index = m;
for (int i = 0; i < n; i++)
nums1[index++] = nums2[i];*/
int j = 0;
for (int i = m; i < (m + n); ++i)
{
nums1[i] = nums2[j++];
}
// 开始插入排序
for (int i = 1; i < m + n; i++) {
if (nums1[i] < nums1[i - 1]) {
int j = i-1;
// 寻找的同时维护数据顺序
while (nums1[j] > nums1[i]) {
nums1[j + 1] = nums1[j];
j--;
// 注意这里的j有可能越界,所以要判断一下
if (j < 0)
break;
}
// 找到了该插入的位置
nums1[j + 1] = nums1[i];
}
}
}
// 还有一个抖机灵的办法是在算法一的思维上换成从后往前放置数据
// 那么你可以原地排序了,空间复杂度也为最小
void merge2(vector& nums1, int m, vector& nums2, int n) {
int index1 = m - 1;
int index2 = n - 1;
for (int i = m + n - 1; i >= 0; i--) {
// nums1放置完毕
if (index1 < 0) {
nums1[i] = nums2[index2--];
}
else if (index2 < 0) { // nums2放置完毕
nums1[i] = nums1[index1--];
}
else if (nums2[index2] >= nums1[index1]) {
nums1[i] = nums2[index2--];
}
else {
nums1[i] = nums1[index1--];
}
}
}
};
给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。
说明:本题中,我们将空字符串定义为有效的回文串。
示例 1:
输入: "A man, a plan, a canal: Panama"
输出: true
示例 2:
输入: "race a car"
输出: false
class Solution {
public:
bool isPalindrome(string s) {
int l = 0;
int r = s.length() - 1;
while (l < r) {
// 首先判断一下是否是字母或者数字,同时找寻数字或者字母
while (!isNumOrAlphabet(s[l]))
{
l++;
// 找着找着超过r了说明l和r之间都是可以忽略的,那么返回true
if (l > r)
return true;
}
while (!isNumOrAlphabet(s[r]))
{
r--;
// 同理
if (r < l)
return true;
}
int t = s[l];
// 数字之间的回文比较
if (t <= 57) {
if (s[l] != s[r])
return false;
}
// 字母之间的回文比较
else {
// 已知一个是小写字母的比较
if (t >= 97) {
if (s[l] != s[r] && s[l] != (s[r] + 32))
return false;
}
// 已知一个是大写字母的比较
else {
if (s[l] != s[r] && s[l] != (s[r] - 32))
return false;
}
}
// 进行下一组回文比较
l++;
r--;
}
return true;
}
string reverseString(string s) {
int l = 0;
int r = s.length() - 1;
while (l= 48 && t <= 57) || (t >= 65 && t <= 90) || (t >= 97 && t <= 122))
return true;
else
return false;
}
};
给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。
函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。
说明:
示例:
输入: numbers = [2, 7, 11, 15], target = 9
输出: [1,2]
解释: 2 与 7 之和等于目标数 9 。因此 index1 = 1, index2 = 2 。
// 有一点要注意的是程序内部索引从零开始,外部从1开始
class Solution {
public:
vector twoSum(vector& numbers, int target) {
// 一次遍历,对当前i之后的数据段进行二分查找,使得最终numbers[i]+numbers[j]==target
int r = numbers.size()-1;
for (int i = 0; i < numbers.size(); i++) {
int res = binarySearch(numbers, i + 1, r, target - numbers[i]);
if (res != -1) {
int vec[2] = { i + 1,res + 1 };
return vector(vec, vec + 2);
}
}
throw invalid_argument("The input has no solution.");
}
// 上述算法为O(nlogn)
// 对撞指针O(n)
vector twoSum2(vector& numbers, int target) {
// 初始化需要维护的两个对撞指针
int i = 0;
int j = numbers.size() - 1;
// 题目说明索引相同的两个值加在一起不算
while (i < j) {
if (numbers[i] + numbers[j] == target) {
int res[2] = { i + 1,j + 1 };
return vector(res, res + 2);
}
else if (numbers[i] + numbers[j] > target) {
j--;
}
else {
i++;
}
}
throw invalid_argument("The input has no solution.");
}
private:
// 二分查找,返回相应值的索引
// 关键点在[l,r]之内进行二分查找target
int binarySearch(vector& numbers, int l, int r, int target) {
while (l <= r) {
int mid = l + (r - l) / 2;
if (numbers[mid] == target)
return mid;
else if (target < numbers[mid])
r = mid - 1;
else
l = mid + 1;
}
// 没找到匹配的
return -1;
}
};
给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的连续子数组。如果不存在符合条件的连续子数组,返回 0。
示例:
输入:s = 7, nums = [2,3,1,2,4,3]
输出: 2 解释: 子数组[4,3]
是该条件下的长度最小的连续子数组。
进阶:
如果你已经完成了O(n) 时间复杂度的解法, 请尝试 O(n log n) 时间复杂度的解法。
// 基本思路:题目中说的是正整数数组,考虑用双索引技术维护好滑动窗口
class Solution {
public:
int minSubArrayLen(int s, vector& nums) {
// 根据当前nums[i,j]的值与s的大小关系决定i,j索引的更新
int i = 0;
int j = -1;
int minLen = nums.size() + 1;
int sum = 0;
int n = nums.size();
while (i < n) {
// 当前的滑动窗口sum小于s,那么就要滑动j,使得sun增加
if (sum < s) {
sum += nums[++j];
// 如果滑窗的j滑出了数组右边界,说明i之后的数据都不会符合题意了,可以提前退出
if (j >= n)
break;
}
else {
minLen = std::min(minLen, j - i + 1);
sum -= nums[i++];
}
}
return minLen == n + 1 ? 0 : minLen;
}
};
在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
示例 1:
输入: [3,2,1,5,6,4] 和
k = 2
输出: 5
示例 2:
输入: [3,2,3,1,2,4,5,5,6] 和
k = 4
输出: 4
说明:
你可以假设 k 总是有效的,且 1 ≤ k ≤ 数组的长度。
class Solution {
public:
int findKthLargest(vector& nums, int k) {
// 可以用快排思维查找第k个元素的值
int r = nums.size()-1;
// 外部第k大和内部数组索引之间需要自己维护
int ret = quickSearch(nums, 0, r, k-1);
srand(time(NULL));
return nums[ret];
}
private:
// 子函数:用于寻找[l,r]之间起始数值在区间内的正确排序索引
int _quickSearch(vector& nums, int l, int r, int k) {
// 随机取[l,r]之间的随机数
int t = rand() % (r - l + 1) + l;
swap(nums[l], nums[t]);
// 最终[l,j-1]内的数值都>v
int v = nums[l];
int j = l;
for (int i = l + 1; i <= r; i++) {
if (nums[i] > v) {
swap(nums[++j], nums[i]);
}
}
swap(nums[l], nums[j]);
return j;
}
// 返回每一次快排的起始随机标志的索引
int quickSearch(vector& nums, int l, int r, int k) {
int pivot = _quickSearch(nums, l, r, k);
if (pivot == k)
return pivot;
else if (k < pivot)
return quickSearch(nums, 0, pivot - 1, k);
else
return quickSearch(nums, pivot + 1, r, k);
}
};
给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的一个字母异位词。
示例 1:
输入: s = "anagram", t = "nagaram"
输出: true
示例 2:
输入: s = "rat", t = "car"
输出: false
说明:
你可以假设字符串只包含小写字母。
进阶:
如果输入字符串包含 unicode 字符怎么办?你能否调整你的解法来应对这种情况?
class Solution {
public:
bool isAnagram(string s, string t) {
// 字符串长度不一致直接返回false
if (s.length() != t.length())
return false;
// 判断一下是否空串的情况
if (s.length() == 0)
return true;
unordered_map record_s;
unordered_map record_t;
for (int i = 0; i < s.length(); i++) {
record_s[s[i]]++;
record_t[t[i]]++;
}
for (int i = 0; i
给定一个数组 nums
,编写一个函数将所有 0
移动到数组的末尾,同时保持非零元素的相对顺序。
示例:
输入:[0,1,0,3,12]
输出:[1,3,12,0,0]
说明:
class Solution {
public:
// 时间复杂度O(n)
// 空间复杂度O(n)
void moveZeroes(vector& nums) {
// 策略:一次循环,将取出非零元素,放置进开辟的非零向量中,
// 最后取出非零向量中的元素放置进原向量,后面补0
vector nonZeroElements;
for (int i = 0; i < nums.size(); i++) {
if (nums[i] != 0)
nonZeroElements.push_back(nums[i]);
}
for (int i = 0; i < nonZeroElements.size(); i++) {
nums[i] = nonZeroElements[i];
}
for (int i = nonZeroElements.size(); i < nums.size(); i++) {
nums[i] = 0;
}
}
// 优化成空间复杂度为常数的算法
// 策略: 一次循环,维护好循环索引和非零元素个数索引值,即[0,k)的区间内都是原序的非零索引,
// 一次循环完后,后面补零
void moveZeros2(vector& nums) {
// 初始化两个需要维护的索引的值
int k = 0;
for (int i = 0; i < nums.size(); i++) {
if (nums[i] != 0) {
nums[k] = nums[i];
k++;
}
}
for (int i = k; i < nums.size(); i++)
nums[i] = 0;
}
// 再次优化,使得只有一次循环,不需要额外的补零操作
// 遍历到第i个元素的时候,保证[0,i]中的非零元素
// 都按照顺序放置在[0,k)中
// 零元素都放置在[k,i]中
void moveZeros3(vector& nums) {
int k = 0;
for (int i = 0; i < nums.size(); i++) {
if (nums[i] != 0) {
if (k != i) {
swap(nums[k], nums[i]);
k++;
}
else
k++;
}
}
}
};
给定一种 pattern(模式)
和一个字符串 str
,判断 str
是否遵循相同的模式。
这里的遵循指完全匹配,例如, pattern
里的每个字母和字符串 str
中的每个非空单词之间存在着双向连接的对应模式。
示例1:
输入: pattern ="abba"
, str ="dog cat cat dog"
输出: true
示例 2:
输入:pattern ="abba"
, str ="dog cat cat fish"
输出: false
示例 3:
输入: pattern ="aaaa"
, str ="dog cat cat dog"
输出: false
示例 4:
输入: pattern ="abba"
, str ="dog dog dog dog"
输出: false
说明:
你可以假设 pattern
只包含小写字母, str
包含了由单个空格分隔的小写字母。
// 思路1 : 构建两个map,和,一次遍历,查看对应模式下int数值是否一致
class Solution {
public:
bool wordPattern(string pattern, string str) {
istringstream in(str);
vector words;
unordered_map pat_map;
unordered_map str_map;
int i = 0;
for (string word; in >> word; i++) {
// 如果当前的s[i]和word都不在map中,说明是一对新的对应关系
if (pat_map.find(pattern[i]) == pat_map.end() && str_map.find(word) == str_map.end()) {
pat_map[pattern[i]] = str_map[word] = i + 1; // 这里选择i+1,而不是简单的++是因为能保证value值唯一,不会重复
}
else { // 某一个key值已存在
if (pat_map[pattern[i]] != str_map[word]) // 对应关系不符合
return false;
}
}
return i == pattern.length(); // 到这里如果i==字符串长度,说明分割出来的word数和patter字符长度一致,不一致那么久对应不了
}
};
// 思路2 : 既然是找对应关系,那么可以直接将map对应为key值为patter中的字母,value为string中的word
// 一直向后查找,当key对应的value不同时那么久可以false退出了,哎,程序都差不多,改几处地方就行了,不想写了
给定两个数组,写一个函数来计算它们的交集。
例子:
给定 num1= [1, 2, 2, 1]
, nums2 = [2, 2]
, 返回 [2]
.
提示:
// 利用set
// set:只存key值,且不会重复,重复插入同一数值没有用
class Solution {
public:
vector intersection(vector& nums1, vector& nums2) {
set record(nums1.begin(),nums1.end());
set resultSet;
for (int i = 0; i < nums2.size(); i++)
if (record.find(nums2[i]) != record.end()) // 找到共同元素
resultSet.insert(nums2[i]); // 插入,重复插入没有关系,只是维护一个key值
return vector(resultSet.begin(), resultSet.end());
}
};
给定两个数组,写一个方法来计算它们的交集。
例如:
给定 nums1 = [1, 2, 2, 1]
, nums2 = [2, 2]
, 返回 [2, 2]
.
注意:
跟进:
class Solution2 {
public:
vector intersect(vector& nums1, vector& nums2) {
//map record(nums1.begin(), nums1.end());
map record;
//for (int i = 0; i < nums1.size(); i++) {
// record[nums1[i]]++;
//}
for (int i = 0; i < nums1.size(); i++) {
if (record.find(nums1[i]) == record.end())
record.insert(make_pair(nums1[i], 1));
else
record[nums1[i]]++;
}
vector resVector;
/*for (int i = 0; i < nums2.size(); i++) {
if (record[nums2[i]] > 0) {
resVector.push_back(nums2[i]);
record[nums2[i]]--;
}
}*/
for (int i = 0; i < nums2.size(); i++) {
if (record.find(nums2[i]) != record.end()) {
resVector.push_back(nums2[i]);
record[nums2[i]]--;
if (record[nums2[i]] == 0)
record.erase(nums2[i]);
}
}
return resVector;
}
};
给定一个字符串 s 和一个非空字符串 p,找到 s 中所有是 p 的字母异位词的子串,返回这些子串的起始索引。
字符串只包含小写英文字母,并且字符串 s 和 p 的长度都不超过 20100。
说明:
示例 1:
输入:
s: "cbaebabacd" p: "abc"
输出:
[0, 6]
解释:
起始索引等于 0 的子串是 "cba", 它是 "abc" 的字母异位词。
起始索引等于 6 的子串是 "bac", 它是 "abc" 的字母异位词。
示例 2:
输入:
s: "abab" p: "ab"
输出:
[0, 1, 2]
解释:
起始索引等于 0 的子串是 "ab", 它是 "ab" 的字母异位词。
起始索引等于 1 的子串是 "ba", 它是 "ab" 的字母异位词。
起始索引等于 2 的子串是 "ab", 它是 "ab" 的字母异位词。
class Solution {
public:
vector findAnagrams(string s, string p) {
if (p.length() > s.length())
return vector();
// 滑动技术:保持每一次滑动的窗口与p的长度相等,检查窗口内的字母是否与p相同
int contain_s[26] = { 0 };
int contain_p[26] = { 0 };
int n = s.length();
int len = p.length();
vector res;
// 初始化
for (int i = 0; i < len; i++) {
contain_s[s[i]-'a']++;
contain_p[p[i]-'a']++;
}
// 遍历
for (int i = 0; i < n + 1 - len; i++) {
// 检查以i索引为起点的固定滑窗内的数据的一致性
int j = 0;
for (; j < 26; j++) {
if (contain_s[j] != contain_p[j])
break;
}
if (j == 26)
res.push_back(i);
// 维护下一次检查的contain_s
if (i + 1 >= n + 1 - len)
break;
contain_s[s[i]-'a']--;
contain_s[s[i + len]-'a']++;
}
return res;
}
};
给定一个字符串,请将字符串里的字符按照出现的频率降序排列。
示例 1:
输入:
"tree"
输出:
"eert"
解释:
'e'出现两次,'r'和't'都只出现一次。
因此'e'必须出现在'r'和't'之前。此外,"eetr"也是一个有效的答案。
示例 2:
输入:
"cccaaa"
输出:
"cccaaa"
解释:
'c'和'a'都出现三次。此外,"aaaccc"也是有效的答案。
注意"cacaca"是不正确的,因为相同的字母必须放在一起。
示例 3:
输入:
"Aabb"
输出:
"bbAa"
解释:
此外,"bbaA"也是一个有效的答案,但"Aabb"是不正确的。
注意'A'和'a'被认为是两种不同的字符。
// 思路:
//题目要求我们按照字符出现频率从高到低进行排序,大小写区分。看到字符出现频率,很自然地就想到用unordered_map哈希表来存储每个字符出现的次数。
//然后通过对整个哈希表进行排序,最后将其进行输出。但是这里有一个问题,就是stl的sort算法只能对线性序列容器进行排序(即vector, list, deque)。
//所以我们这里就要设法将存储在哈希表里面的内容转移到vector这些容器中了。这里由于哈希表存储的是key - value对,
//所以vector应该声明成pair某种模板类型,这样在vector中才能通过value找到相对应的key值。
//在将键值对全部插入到vector之后,就要对value值进行排序了。stl的sort函数有三个参数,其中第三个参数是可选的,
//是一个返回值类型可转化为bool的函数,表示sort的方式。如果我们不传入第三个参数,那么sort完成后输出的结果就是按ascii值从小到大进行排序的结果。
//因此,在这里有必要传入第三个参数。
class Solution {
public:
// 这里面要声明成静态的是因为sort函数是外部的一个类,不能直接调用类内的函数,声明成静态那就说明此函数不是该类独有的了
static bool comparePair(const pair &x, const pair &y) {
return x.second > y.second;
}
string frequencySort(string s) {
string res_str = "";
// 将s存储成哈希表
unordered_map sMap;
for (int i = 0; i < s.length(); i++)
sMap[s[i]]++;
// 将哈希表中的pair对放置进vector
vector> vecPair;
for (auto it = sMap.begin(); it != sMap.end(); it++) {
vecPair.push_back(make_pair(it->first, it->second));
}
// 排序
sort(vecPair.begin(), vecPair.end(), comparePair);
// 整理成输出
for (auto it = vecPair.begin(); it != vecPair.end(); it++) {
for (int i = 0; i < it->second; i++) {
res_str += it->first;
}
}
return res_str;
}
};