堆排序
核心思路:
构建堆的时候是从下到上的,从一个个分支节点开始调整的。调整一个分支节点的时候,需要与其最大max孩子结点做比较,如果当前分支节点比max孩子小的话,就交换,由于交换可能会破坏max孩子子堆的最大堆性质,所以需要继续朝着最大max孩子方向做调整,直到遇到叶子节点。
出堆的时候,堆顶top之间出去,然后用最后一个元素代替堆顶top,然后从上至下的调整堆。
非递归实现
cpp
class Solution {
public:
bool stringOrder(string a, string b){
//a和b肯定是不一样的
//a的字典序是否比b小呢?
// 如果所有字符都相同,则较短的字符串被认为更小。
int minLen = min(a.size(), b.size());
for (int i = 0; i < minLen; ++i) {
if (a[i] != b[i]) {
return a[i] < b[i];
}
}
return a.size()<b.size();
}
void buildHeap(int node, vector<pair<string, int>>& nums){
int n = nums.size();
//把从node开始往下的所有分支结点都做调整
for(int i = node; i<=n/2-1;){
int max = (i+1)*2 - 1; //默认孩子节点里面最大的是左孩子
if(max+1<n){
//如果右孩子存在的话
//如果右孩子的次数比左孩子多的话那么就让max为右孩子
//或者如果右孩子与左孩子次数一样但是字典序比左孩子小的话
if(nums[max].second<nums[max+1].second ||
(nums[max].second==nums[max+1].second &&
stringOrder(nums[max+1].first, nums[max].first)))
max = max + 1;
}
bool change = false;
//如果结点的次数比孩子max的次数要小的话
//如果次数一样但是max的字典序比node的小
if(nums[i].second<nums[max].second ||
(nums[i].second==nums[max].second &&
stringOrder(nums[max].first, nums[i].first))){
change = true;
}
if(change){
pair<string, int> tmp = nums[i];
nums[i] = nums[max];
nums[max] = tmp;
i = max;
//就向max的方向继续调整
//如果max不是分支节点的话 那么它就会退出for循环
}else break;
//当不需要change的时候 就说明这个堆没问题了就退出for
}
}
string getTop(vector<pair<string, int>>& nums){
pair<string, int> top = nums[0];
int n = nums.size();
nums[0] = nums[n-1]; //用最后一个元素代替第一个元素
nums.pop_back(); //删掉最后一个元素
if(nums.size()>0) buildHeap(0, nums); //调整堆
return top.first;
}
vector<string> topKFrequent(vector<string>& words, int k) {
//构建<字符串,次数>数组nums --- begin ---
map<string, int> cnt;
for(string tmp: words){
if(cnt.find(tmp)!=cnt.end()){
cnt[tmp]++;
}else cnt[tmp]=1;
}
vector<pair<string, int>> nums;
for(auto [word, time]: cnt){
nums.emplace_back(pair<string, int>(word, time));
}
//--- end ---
//从分支节点从下往上调整建立大根堆 --- begin ---
int n = nums.size();
for(int i = n/2-1; i>=0; i--){
buildHeap(i, nums);
}
//--- end ---
vector<string> res;
while(k--){
res.emplace_back(getTop(nums));
}
return res;
}
};
递归实现
cpp
class Solution {
public:
void heap(int node, vector<pair<int, int>>& nums){
//王道书里面采用的for循环实现最大堆的调整
//我这里采用递归我感觉更好理解而且代码更少
if(node>nums.size()/2-1) return; //只允许分支节点操作
//因为索引是从0开始的 那么左孩子的索引应该是 (node+1)*2-1
int max = (node+1)*2-1;
//if(max>=nums.size()) printf("%d ", max);
//max代表node的左右孩子里面最大的那个结点的索引
//默认是left
if(max+1<nums.size()&&nums[max].second<nums[max+1].second)
max = max + 1;
//如果有右孩子并且右孩子的值比左孩子大
//那么max = right
if(nums[node].second<nums[max].second){
//如果是孩子结点比node大 那么就交换值
int tmp = nums[max].second;
nums[max].second = nums[node].second;
nums[node].second = tmp;
int index = nums[max].first;
nums[max].first = nums[node].first;
nums[node].first = index;
heap(max, nums);
//向调整的方向继续调整
}
}
int getHeapTop(vector<pair<int, int>>& nums){
int n = nums.size();
int res = nums[0].first;
nums[0].first = nums[n-1].first;
nums[0].second = nums[n-1].second; //用最后一个元素来替代第一个元素的位置
nums.pop_back(); //舍弃最后一个元素
if(nums.size()>0) heap(0, nums);// 如果还有元素的话就调整堆
return res;
}
vector<int> topKFrequent(vector<int>& nums, int k) {
//首先要统计每个元素的出现次数
map<int, int> cnt;
for(int i: nums){
if(cnt.find(i)!=cnt.end()){
cnt[i]++;
}else cnt[i] = 1;
}
vector<pair<int, int>> values;
for (const auto& [key, value] : cnt) {
values.emplace_back(pair<int, int>(key, value));
}
//基于堆排序实现
int n = values.size();
//对所有的分支节点进行操作
for(int i = n/2-1; i>=0; i--){
heap(i, values);
}
vector<int> res;
while(k--){
res.emplace_back(getHeapTop(values));
}
return res;
}
};
二路归并排序
cpp
class Solution {
public:
void merge(vector<int>& nums, int low, int mid, int high) {
// 合并两个有序的数组
vector<int> res(nums); // 复制数组
int l, m;
l = low;
m = mid + 1;
int index = l;
while (l <= mid && m <= high) {
if (res[l] < res[m]) {
nums[index++] = res[l++];
} else
nums[index++] = res[m++];
}
while (l <= mid)
nums[index++] = res[l++];
while (m <= high)
nums[index++] = res[m++];
}
// 下面是优化版本
// void merge(vector<int>& nums, int low, int mid, int high) {
// vector<int> tmp(high - low + 1); // 1. 只申请这段区间
// int l = low, r = mid + 1, idx = 0;
// while (l <= mid && r <= high)
// tmp[idx++] = (nums[l] < nums[r]) ? nums[l++] : nums[r++];
// while (l <= mid)
// tmp[idx++] = nums[l++];
// while (r <= high)
// tmp[idx++] = nums[r++];
// for (int i = 0; i < idx; ++i) // 2. 拷回原数组对应位置
// nums[low + i] = tmp[i];
// }
void twoRoad(vector<int>& nums, int low, int high) {
if (low < high) {
int mid = (low + high) / 2;
twoRoad(nums, low, mid);
twoRoad(nums, mid + 1, high);
merge(nums, low, mid, high);
}
}
vector<int> sortArray(vector<int>& nums) {
twoRoad(nums, 0, nums.size() - 1);
return nums;
}
};
快速排序
cpp
int sort(vector<int>& nums, int left, int right){
int base = nums[left];
while(left<right){
while(left<right&&nums[right]>=base) --right;
nums[left] = nums[right];
while(left<right&&nums[left]<=base) ++left;
nums[right] = nums[left];
}
nums[left] = base;
return left;
}
void fastSort(vector<int>& nums, int left, int right){
if(left<right){
int p = sort(nums, left, right);
fastSort(nums, left, p-1);
fastSort(nums, p+1, right);
}
}