// 该函数实现了快速排序算法
void quick_sort(vector<int> &nums, int l, int r)
{
//1.定义一个出口(当l+1>=r时)
// 当l大于等于r时,结束排序
if (l + 1 >= r) {
return;
}
// 设置首尾指针和关键元素
int first = l, last = r - 1, key = nums[first];
//2.小的放左边
//3.大的放右边
// 当first小于last时,执行循环
while (first < last){
// 移动尾,寻找小于key的元素
while (first < last && nums[last] >= key)
--last;
// 将小于key的元素放到数组的左边
nums[first] = nums[last];
// 移动头,寻找大于key的元素
while (first < last && nums[first] <= key) {
++first;
}
// 将大于key的元素放到数组的右边
nums[last] = nums[first];
}
//4.key放到正确位置
// 把key放到正确的位置,此时,key左边的元素都小于它,右边的元素都大于它
nums[first] = key;
//5.对左边元素递归
//6.对右边元素递归
// 递归地对key的左边元素进行快速排序
quick_sort(nums, l, first);
// 递归地对key的右边元素进行快速排序
quick_sort(nums, first + 1, r);
}
void merge_sort(vector<int> &nums, int l, int r, vector<int> &temp) {
// 基本情况:如果待排序数组的长度小于等于1,那么不需要排序,直接返回
if (l + 1 >= r) {
return;
}
//1.不断的进行划分,直到只剩一个元素
// 分治排序的“分”步骤
// 计算中点m,将数组一分为二,分别为[l, m)和[m, r)
int m = l + (r - l) / 2;
// 递归对左半部分进行排序
merge_sort(nums, l, m, temp);
// 递归对右半部分进行排序
merge_sort(nums, m, r, temp);
//2.对左右两个子序列进行合并排序
// 分治排序的“治”步骤
// 开始合并两个已排序的子数组
int p = l, q = m, i = l;
while (p < m || q < r)
{ //左半边/右半边没处理完
// 判断条件,如果右边的数组已经处理完,或者左边元素没有处理完并且左边值小于右边值
if (q >= r || (p < m && nums[p] <= nums[q])) {
// 将左边(小的)的当前元素复制到临时数组中
temp[i++] = nums[p++];
} else {
// 否则,将右边(大的)的当前元素复制到临时数组中
temp[i++] = nums[q++];
}
//向临时数组中从小到大的放序列
}
// 将排序后的元素从临时数组复制回原数组
for (i = l; i < r; ++i) {
nums[i] = temp[i];
}
}
插入排序是一种简单的排序算法,它的工作原理是通过构建有序序列,对未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。插入排序在实现上,通常采用 in-place 排序(即只需用到 O(1) 的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。
主要思路:
默认从第一个元素开始往右挨个看,每个元素向左逐个对比,直到向右走完所有元素。
void insertion_sort(vector<int>&nums, int n)
{
for (int i = 0; i < n; ++i)
{
for (int j = i; j > 0 && nums[j] < nums[j-1]; --j)
{
swap(nums[j], nums[j-1]);
}
}
}
冒泡排序被这样命名是因为,随着排序过程的进行,最大的元素(如果是升序排序)或者最小的元素(如果是降序排序)就像水中的"气泡"一样,慢慢"浮"到数组的顶端或者尾部。
在冒泡排序的每一轮排序过程中,都会通过两两比较并交换位置,使得当前最大(或最小)的元素移动到正确的位置。这个过程就像是气泡在水中不断上浮的过程,因此这种排序算法被称为"冒泡排序"。
冒泡排序的一个特点是越小(或越大)的元素会经由交换慢慢“浮”到数列的顶端,故名冒泡排序。
冒泡:最大或最小的一个个的冒到水上
主要步骤:
1.冒出一个泡(从第一个开始,向右)
2.进行冒泡操作(将一个泡层层的冒出,向右)
void bubble_sort(vector &nums, int n)
{
bool swapped;
for (int i = 1; i < n; ++i)
{
swapped = false;
for (int j = 1; j < n - i + 1; ++j) //(每次都必须从第一个开始往后捋,这样才能每次捋出一个最大/最小的)
//后部分值为冒上去的最大的值(已经有序)
{
if (nums[j] < nums[j-1])
{
swap(nums[j], nums[j-1]);
swapped = true;
}
}
if (!swapped)
break;
}
}
思路:冒泡排序为从前往后捋,鸡尾酒排序为前后一起捋
void cocktail_sort(vector<int>&nums)
{
int start = 0, end = nums.size()-1;
bool swaped = true;
while (swaped)
{
swaped = false;
for (int i = start; i < end; ++i)
{
if (nums[i] > nums[i + 1])
{
swap(nums[i], nums[i + 1]);
swaped = true;
}
}
if (!swaped)
break;
swaped = false;
end--;
for (int i = end; i > start; --i)
{
if (nums[i] < nums[i-1])
{
swap(nums[i - 1], nums[i]);
swaped = true;
}
}
start++;
}
}
选择排序是一种简单直观的排序算法,其基本思想是从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,然后,再从剩余的未排序元素中寻找到最小(或最大)元素,然后放到已排序的序列的末尾。以此类推,持续进行此过程,直到全部待排序的数据元素排完。
注意
选择排序的效率较低,因为它每次都要从未排序的部分扫描最小或者最大元素,然后进行交换,这样会导致大量的比较和交换操作。
操作思路:
1.从左向右一个个元素挨个拿出来
2.对于拿出来的元素,挨个向右对比找到比拿出元素还小的(找最小的过程,必须有一个索引来锁定住最小的变量)
3.对于找出的较小元素与拿出元素交换位置
void selection_sort(vector &nums, int n)
{
int mid;
for (int i = 0; i < n - 1; ++i) //最后一个元素没有必要在排序
{
mid = i;
for(int j = i + 1; j < n; ++j) //找到最小的单个元素
{
if (nums[j] < nums[mid])
mid = j;
}
swap(nums[mid], nums[i]);
}
}
题目描述
在一个未排序的数组中,找到第 k 大的数字。
输入输出样例
输入一个数组和一个目标值 k,输出第 k 大的数字。题目默认一定有解。
Input: [3,2,1,5,6,4] and k = 2
Output: 5
题解
快速选择一般用于求解 k-th Element 问题,可以在 O(n) 时间复杂度,O(1) 空间复杂度完成求 解工作。快速选择的实现和快速排序相似,不过只需要找到第 k 大的枢(pivot)即可,不需要对 其左右再进行排序。与快速排序一样,快速选择一般需要先打乱数组,否则最坏情况下时间复杂 度为 O(n 2 ),我们这里为了方便省略掉了打乱的步骤。
这种方法的平均时间复杂度是 O(n)。但在最差情况下,它可能会退化到 O(n^2)。为了防止这种情况发生,通常会使用随机化技巧。
1.进行折半选择,比目标值大就向左,比目标值小就向右
2.使用一个快速选择算法,随便定一个基准,然后把小的放左边大的放右边
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
int l = 0, r = nums.size() - 1, mid, target = nums.size() - k;
while (l < r) {
mid = quick(nums, l, r);
if (mid == target)
return nums[mid];
else if (mid < target)
l = mid + 1;
else
r = mid - 1;
}
return nums[l];
}
int quick(vector<int>& nums, int l, int r) {
int i = l + 1, j = r;
while (i <= j) {
while (i <= j && nums[i] <= nums[l]) // find smaller than l
++i;
while (i <= j && nums[j] >= nums[l]) // find larger than l
--j;
if (i < j) swap(nums[i], nums[j]);
}
swap(nums[l], nums[j]);
return j;
}
};
桶排序是一个分发式的排序算法,它将数组分成多个区间(或“桶”),然后对每个桶中的数据进行排序。最后,再将所有桶的数据按桶的顺序连接起来,得到排序后的序列。它通常用在输入数据是均匀分布的场合。
以下是桶排序的基本思想:
确定桶的数量: 根据数据的特性和分布范围,确定要创建的桶的数量。
分配到桶中: 遍历输入数据,将每个数据放到对应的桶中。数据应该被分配到范围内对应的桶。例如,如果数据范围是[0,100)并且我们有10个桶,那么数字范围[0,10)的数据将被放入第一个桶,范围[10,20)的数据放入第二个桶,以此类推。
排序每个桶: 对每个桶中的数据进行排序。这可以使用其他排序算法,如插入排序。
合并桶: 一旦所有的桶都被排序,就按照桶的顺序将数据合并回一个数组。
性质与用途:
示例:
假设有一个数组:[4.5, 0.84, 3.25, 2.18, 0.5]
,我们想使用桶排序对其进行排序。
[0.5, 0.84, 2.18, 3.25, 4.5]
。注意:在实际应用中,选择桶的数量和桶的大小是关键。太少或太多的桶都可能导致算法效率降低。
给定一个数组,求前 k 个最频繁的数字。
输入是一个数组和一个目标值 k。输出是一个长度为 k 的数组。
Input: nums = [1,1,1,1,2,2,3,4], k = 2
Output: [1,2]
顾名思义,桶排序的意思是为每个值设立一个桶,桶内记录这个值出现的次数(或其它属 性),然后对桶进行排序。针对样例来说,我们先通过桶排序得到四个桶 [1,2,3,4],它们的值分别 为 [4,2,1,1],表示每个数字出现的次数。
vector<int> topKFrequent(vector<int>& nums, int k) {
unordered_map<int, int> counts;
int max_count = 0;
// 从数组中找频率
for (const int & num : nums) {
max_count = max(max_count, ++counts[num]);
}
// 桶排序的核心部分
// 创建一个桶的数组
vector<vector<int>> buckets(max_count + 1); //为了使索引和桶数相对应,所以+1
//将对应频率的数字放入桶中,填充完后桶即为有序(利用空间来排序(通过频率与空间顺序的结合))
// 填充桶,buckets[p.second]表示出现次数为p.second的所有元素
for (const auto & p : counts) {
buckets[p.second].push_back(p.first);
}
// 用于存放结果的数组
vector<int> ans;
//拿出桶来
// 从出现次数最高的桶开始,取出前k个桶的所有元素
for (int i = max_count; i >= 0 && ans.size() < k; --i) {
for (const int & num : buckets[i]) {
ans.push_back(num);
if (ans.size() == k) {
break;
}
}
}
return ans;
}
Sort Characters By Frequency
Given a string s
, sort it in decreasing order based on the frequency of the characters. The frequency of a character is the number of times it appears in the string.
Return the sorted string. If there are multiple answers, return any of them.
Example 1:
Input: s = “tree”
Output: “eert”
Explanation: ‘e’ appears twice while ‘r’ and ‘t’ both appear once.
So ‘e’ must appear before both ‘r’ and ‘t’. Therefore “eetr” is also a valid answer.
Example 2:
Input: s = “cccaaa”
Output: “aaaccc”
Explanation: Both ‘c’ and ‘a’ appear three times, so both “cccaaa” and “aaaccc” are valid answers.
Note that “cacaca” is incorrect, as the same characters must be together.
Example 3:
Input: s = “Aabb”
Output: “bbAa”
Explanation: “bbaA” is also a valid answer, but “Aabb” is incorrect.
Note that ‘A’ and ‘a’ are treated as two different characters.
Constraints:
1 <= s.length <= 5 * 105
s
consists of uppercase and lowercase English letters and digits.
class Solution {
public:
string frequencySort(string s) {
int max_count = 0;
unordered_map<char,int> count;
// 1. 遍历字符串s,计算每个字符出现的次数,并找到最大次数。
for(char c : s) {
max_count = max(max_count, ++count[c]);
}
// 2. 使用桶排序思想,创建一个大小为字符串s的长度+1的向量。
vector<vector<char>> bucket(s.size() + 1);
// 3. 根据每个字符出现的次数,将其放入对应的桶中。
for(auto &p : count) {
bucket[p.second].push_back(p.first);
}
string ans;
// 4. 从最大次数开始,按频率从高到低的顺序将字符添加到答案字符串中。
for(int i = max_count; i > 0; --i) {
int k = bucket[i].size() - 1;
// 对于每个桶,考虑其中的每个字符。
while(k >= 0) {
int j = i;
// 将每个字符重复i次,即它在字符串s中出现的次数。
while(j-- > 0) {
ans += bucket[i][k];
}
k--;
}
}
return ans; // 返回重新排序后的字符串。
}
};
Sort Colors
Given an array nums
with n
objects colored red, white, or blue, sort them in-place so that objects of the same color are adjacent, with the colors in the order red, white, and blue.
We will use the integers 0
, 1
, and 2
to represent the color red, white, and blue, respectively.
You must solve this problem without using the library’s sort function.
Example 1:
Input: nums = [2,0,2,1,1,0]
Output: [0,0,1,1,2,2]
Example 2:
Input: nums = [2,0,1]
Output: [0,1,2]
Constraints:
n == nums.length
1 <= n <= 300
nums[i]
is either 0
, 1
, or 2
.它首先选择一个“增量”或“间隔”(在这个实现中,间隔是数组长度的一半,并在每次迭代中减半)。然后,它对间隔内的所有元素进行插入排序。这一过程重复,每次都将间隔减小,直到间隔为1,这时整个数组都被排序。
下面是您代码的基本步骤:
您的实现基于希尔排序的基本思想,使用了一个三层循环结构来实现。外部循环控制间隔的递减,中间循环遍历每个间隔组,而内部循环则是基于插入排序的元素交换。
这个实现采用的增量序列为最简单的“每次除以2”,但在实际应用中,有些其他的增量序列可能会导致更好的性能。不过这个实现已经很好地展示了希尔排序的基本思想。
思路:通过对序列分组然后分别对每组进行排序,再把拍好的序列进行嵌合,简单来说就是不断缩小所排元素的密度最终完成最序列的排序。
void shell_sort(std::vector<int>& nums)
{
int n = nums.size();
for (int gap = n / 2; gap > 0; gap /= 2)
{
for (int i = gap; i < n; ++i)
{
for (int j = i; j >= gap && nums[j] < nums[j - gap]; j -= gap)
std::swap(nums[j], nums[j - gap]);
}
}
}