博客中所有代码均在leetcode912. 排序数组中执行
(一)插入排序类
1、直接插入排序
1)思路
2)特性分析
3)代码
class Solution {
public:
//插入排序
vector<int> sortArray(vector<int>& nums) {
for(int i = 1; i < nums.size(); i++)
{
int end = i - 1;
int tmp = nums[i];
while(end >= 0)
{
if(nums[end] > tmp)
{
nums[end+1] = nums[end];
end--;
}
else break;
}
nums[end+1] = tmp;
}
return nums;
}
};
4)leetcode运行结果
当数据量为50000时会超时
2、希尔排序
1)思路
2)特性分析
3)代码
class Solution {
public:
//希尔排序
vector<int> sortArray(vector<int>& nums) {
int len = nums.size();
int gap = nums.size() / 2;
while(gap >= 1)
{
for(int i = gap; i < len; i++)
{
int end = i - gap;
int tmp = nums[i];
while(end >= 0)
{
if(nums[end] > tmp)
{
nums[end+gap] = nums[end];
end-=gap;
}
else break;
}
nums[end+gap] = tmp;
}
gap /= 2;
}
return nums;
}
};
4)leetcode运行结果
(二)选择排序类
1、直接选择排序
1)思路
2)特性分析
3)代码
一次选一个数最小
class Solution {
public:
//直接选择排序
vector<int> sortArray(vector<int>& nums) {
int len = nums.size();
for(int i = 0; i < nums.size()-1; i++)
{
int minNum = nums[i];
int idx = i;
for(int j = i+1; j < nums.size(); j++)
{
if(minNum > nums[j])
{
minNum = nums[j];
idx = j;
}
}
swap(nums[i],nums[idx]);
}
return nums;
}
};
一次选两个数最小最大(最后交换的时候有一个坑)
class Solution {
public:
//直接选择排序
vector<int> sortArray(vector<int>& nums) {
int left = 0, right = nums.size()-1;
while(left < right)
{
int minIdx = left, maxIdx = left;
for(int i = left + 1; i <= right; i++)
{
if(nums[minIdx] > nums[i])
minIdx = i;
if(nums[maxIdx] < nums[i])
maxIdx = i;
}
swap(nums[left], nums[minIdx]);
//如果left与maxIdx重叠
if(maxIdx == left) swap(nums[right],nums[minIdx]);
else swap(nums[right],nums[maxIdx]);
left++,right--;
}
return nums;
}
};
4)leetcode运行结果
当数据量为50000时会超时
2、堆排序
1)思路
2)特性分析
3)代码
class Solution {
public:
//堆排序
void down(int u, vector<int>& nums, int len) {
int t = u;
int leftChild = u * 2 + 1;
int rightChild = u * 2 + 2;
if (leftChild < len && nums[leftChild] > nums[t]) t = leftChild;
if (rightChild < len && nums[rightChild] > nums[t]) t = rightChild;
if (t != u) {
swap(nums[t], nums[u]);
down(t, nums, len); // 继续向下递归
}
}
vector<int> sortArray(vector<int>& nums) {
// 建堆 (从最后一个非叶子节点开始下沉)
for (int i = nums.size() / 2 - 1; i >= 0; i--) down(i, nums, nums.size());
int len = nums.size();
for (int i = nums.size() - 1; i > 0; i--) {
swap(nums[0], nums[i]); // 堆顶元素和当前堆末尾元素交换
len--; // 缩小堆的范围
down(0, nums, len); // 重新调整堆
}
return nums;
}
};
4)leetcode运行结果
(三)交换排序类
1、冒泡排序
1)思路
2)特性分析
3)代码
class Solution {
public:
//冒泡排序
vector<int> sortArray(vector<int>& nums) {
for(int i = 0; i < nums.size(); i++)//决定遍历次数,从后往前确定排序好的数
for(int j = 1; j < nums.size() - i; j++)//两两比较,将最大的数向后移
if(nums[j-1] > nums[j]) swap(nums[j-1],nums[j]);
return nums;
}
};
优化版
class Solution {
public:
//冒泡排序
vector<int> sortArray(vector<int>& nums) {
for(int i = 0; i < nums.size(); i++)
{
int exchange = 0;
for(int j = 1; j < nums.size() - i; j++)
if(nums[j-1] > nums[j]) exchange = 1, swap(nums[j-1],nums[j]);
if(exchange == 0) break;
}
return nums;
}
};
4)leetcode运行结果
当数据量为50000时会超时
2、快速排序
1)思路
a)hoare法
选取最左位置元素作为key值,从右往左遍历,当元素小于key时停止,再从左往右遍历,至当前元素大于key为止,交换两个元素,继续遍历。
左边做key右边先走原因:保证最后两者相遇位置一定比key值小or 相遇位置就是key的位置
- R找到小,L找大没有找到,L遇到R;
- R找小,没有找到,直接遇到L,要么就是一个比key小的位置或者直接到keyi
缺点:
如果 nums
已经有序(例如 [1, 2, 3, 4, 5]
),在使用上述快速排序算法时可能会出现 性能下降 的问题,具体表现为时间复杂度从预期的平均 O(n log n)
退化为最坏的 O(n^2)
。
原因:
解决方法:
b)挖坑法
c)前后指针法
核心思想:把比key大的值往右翻,比key小的值,翻到左边
步骤:
说明:
d)小区间优化:
当子区间长度小于某个阈值时,使用插入排序对剩余元素进行排序。
e)非递归法
递归调用可能导致的栈溢出问题,下边使用非递归法。
2)特性分析
3)代码
当前三种方法均使用递归法实现。非递归法在该部分最后(使用前后指针法+栈实现)
a)hoare法
以最左端元素作为key值
class Solution {
public:
//快速排序
void quickSort(vector<int>& nums,int left, int right)
{
if(left >= right) return;
int i = left, j = right;
int key = left;
while(i < j)
{
// 从右向左找小于基准的元素
while(i < j && nums[j] >= nums[key]) j--;
// 从左向右找大于基准的元素
while(i < j && nums[i] <= nums[key]) i++;
if(i < j) swap(nums[i],nums[j]);
}
// 最后将基准元素放到中间位置
swap(nums[key],nums[i]);
key = i;
// 递归处理基准元素左右两侧的部分
quickSort(nums,left,key-1);
quickSort(nums,key+1,right);
}
vector<int> sortArray(vector<int>& nums) {
quickSort(nums, 0, nums.size() - 1);
return nums;
}
};
随机选元素作为key值
class Solution {
public:
//快速排序
void quickSort(vector<int>& nums,int left, int right)
{
if(left >= right) return;
int i = left, j = right;
// 随机选择一个基准元素并与左边界元素交换
int pivotIndex = left + rand() % (right - left + 1);
swap(nums[left], nums[pivotIndex]);
int key = left;
while(i < j)
{
// 从右向左找小于基准的元素
while(i < j && nums[j] >= nums[key]) j--;
// 从左向右找大于基准的元素
while(i < j && nums[i] <= nums[key]) i++;
if(i < j) swap(nums[i],nums[j]);
}
// 最后将基准元素放到中间位置
swap(nums[key],nums[i]);
key = i;
// 递归处理基准元素左右两侧的部分
quickSort(nums,left,key-1);
quickSort(nums,key+1,right);
}
vector<int> sortArray(vector<int>& nums) {
quickSort(nums, 0, nums.size() - 1);
return nums;
}
};
三数取中选key值
class Solution {
public:
// 获取三数中值的下标
int getMiddleIndex(int left, int right, vector<int>& nums) {
int middle = left + (right - left) / 2;
int a = nums[left];
int b = nums[middle];
int c = nums[right];
// 比较三者之间的大小,直接返回中间值的下标
if ((a <= b && b <= c) || (c <= b && b <= a)) {
return middle;
} else if ((b <= a && a <= c) || (c <= a && a <= b)) {
return left;
} else {
return right;
}
}
//快速排序
void quickSort(vector<int>& nums,int left, int right)
{
if(left >= right) return;
int i = left, j = right;
// 三数取中选元素
int pivotIndex = getMiddleIndex(left,right,nums);
swap(nums[left], nums[pivotIndex]);
int key = left;
while(i < j)
{
// 从右向左找小于基准的元素
while(i < j && nums[j] >= nums[key]) j--;
// 从左向右找大于基准的元素
while(i < j && nums[i] <= nums[key]) i++;
if(i < j) swap(nums[i],nums[j]);
}
// 最后将基准元素放到中间位置
swap(nums[key],nums[i]);
key = i;
// 递归处理基准元素左右两侧的部分
quickSort(nums,left,key-1);
quickSort(nums,key+1,right);
}
vector<int> sortArray(vector<int>& nums) {
quickSort(nums, 0, nums.size() - 1);
return nums;
}
};
b)挖坑法
class Solution {
public:
// 快速排序 -- 挖坑法
void quickSort(vector<int>& nums, int left, int right) {
if (left >= right) return; // 递归结束条件,当左边界大于右边界时结束
int i = left, j = right;
int position = left; // 记录"坑"的位置
int key = nums[left]; // 基准元素,取当前范围的第一个元素作为基准
while (i < j) {
// 从右向左寻找小于基准元素的元素
while (i < j && nums[j] >= key) j--;
if (i < j) {
nums[position] = nums[j]; // 把找到的小于基准的元素填入"坑"中
position = j; // 更新"坑"的位置
}
// 从左向右寻找大于基准元素的元素
while (i < j && nums[i] <= key) i++;
if (i < j) {
nums[position] = nums[i]; // 把找到的大于基准的元素填入"坑"中
position = i; // 更新"坑"的位置
}
}
// 最后将基准元素放到"坑"的位置
nums[position] = key;
// 递归处理基准元素左右两侧的部分
quickSort(nums, left, position - 1);
quickSort(nums, position + 1, right);
}
// 对外接口,调用快速排序
vector<int> sortArray(vector<int>& nums) {
quickSort(nums, 0, nums.size() - 1);
return nums;
}
};
c)前后指针法
class Solution {
public:
// 快速排序 -- 前后指针法
void quickSort(vector<int>& nums, int left, int right) {
if (left >= right) return; // 递归结束条件,当左边界不小于右边界时结束
int key = nums[left];
int pre = left, cur = left + 1;
while (cur <= right) {
if (nums[cur] < key) swap(nums[++pre], nums[cur]);
cur++;
}
swap(nums[pre], nums[left]);
// 递归处理基准元素左右两侧的部分
quickSort(nums, left, pre - 1);
quickSort(nums, pre + 1, right);
}
// 对外接口,调用快速排序
vector<int> sortArray(vector<int>& nums) {
quickSort(nums, 0, nums.size() - 1);
return nums;
}
};
d)小区间优化:
class Solution {
public:
//插入排序
void insertSort(vector<int>& nums, int left , int right) {
for(int i = left+1; i <= right; i++)
{
int end = i - 1;
int tmp = nums[i];
while(end >= left)
{
if(nums[end] > tmp)
{
nums[end+1] = nums[end];
end--;
}
else break;
}
nums[end+1] = tmp;
}
}
// 快速排序 -- 前后指针法
void quickSort(vector<int>& nums, int left, int right) {
if (left >= right) return; // 递归结束条件,当左边界不小于右边界时结束
int key = nums[left];
int pre = left, cur = left + 1;
while (cur <= right) {
if (nums[cur] < key) swap(nums[++pre], nums[cur]);
cur++;
}
swap(nums[pre], nums[left]);
if(pre - left > 16)//设定阈值为16
quickSort(nums, left, pre - 1);//快速排序
else
insertSort(nums,left,pre-1);//直接插入排序
if(right - pre > 16)
quickSort(nums, pre + 1, right);//快速排序
else
insertSort(nums,pre+1,right);//直接插入排序
}
// 对外接口,调用快速排序
vector<int> sortArray(vector<int>& nums) {
quickSort(nums, 0, nums.size() - 1);
return nums;
}
};
e)非递归法
class Solution {
public:
typedef pair<int,int> PII;
vector<int> sortArray(vector<int>& nums) {
stack<PII> s;
s.push({0,nums.size()-1});
while(!s.empty())
{
int left = s.top().first;
int right = s.top().second;
s.pop();
if(left >= right) continue;
int pre = left, cur = left+1;
int keyIdx = left;
while(cur <= right)
{
if(nums[cur] < nums[keyIdx]) swap(nums[cur],nums[++pre]);
cur++;
}
swap(nums[pre],nums[keyIdx]);
s.push({pre+1,right});
s.push({left,pre-1});
}
return nums;
}
};
4)leetcode运行结果
a)hoare法
以最左端元素作为key值结果:
数据量为50000时会超时
随机选元素作为key值结果:
三数取中选key值方法结果:

b)挖坑法
c)前后指针法
数据量为50000时会超时
(四)归并排序
1、归并排序
1)思路
2)特性分析
3)代码
a)递归法
class Solution {
public:
vector<int> tmp;
void mergeSort(vector<int>& nums, int left, int right) {
if(left >= right) return;
int mid = left + (right - left) / 2;
mergeSort(nums, left, mid);
mergeSort(nums, mid + 1, right);
int begin1 = left, end1 = mid;
int begin2 = mid + 1, end2 = right;
int k = left; // 用来记录 tmp 中的索引
while(begin1 <= end1 && begin2 <= end2) {
if(nums[begin1] < nums[begin2]) tmp[k++] = nums[begin1++];
else tmp[k++] = nums[begin2++];
}
while(begin1 <= end1) tmp[k++] = nums[begin1++];
while(begin2 <= end2) tmp[k++] = nums[begin2++];
for(int i = left; i <= right; i++) {
nums[i] = tmp[i];
}
}
vector<int> sortArray(vector<int>& nums) {
tmp.resize(nums.size()); // 将 tmp 数组的大小调整为 nums 的大小
mergeSort(nums, 0, nums.size() - 1);
return nums;
}
};
b)非递归法
class Solution {
public:
vector<int> tmp;
void mergeSort(vector<int>& nums, int left, int right)
{
int n = nums.size();
tmp.resize(n);
// 非递归合并排序
for(int gap = 1; gap < n; gap *= 2) {
// 合并大小为gap的子数组
for(int i = 0; i + gap < n; i += 2 * gap) {
int begin1 = i, end1 = i + gap - 1;
int begin2 = i + gap, end2 = min(i + 2 * gap - 1, n - 1);
int idx = 0;
// 合并两个子数组
int k = begin1;
while (begin1 <= end1 && begin2 <= end2) {
if(nums[begin1] <= nums[begin2]) tmp[k++] = nums[begin1++];
else tmp[k++] = nums[begin2++];
}
while (begin1 <= end1) tmp[k++] = nums[begin1++];
while (begin2 <= end2) tmp[k++] = nums[begin2++];
// 将临时数组中的排序结果拷贝回原数组
for (int j = i; j <= end2; ++j) {
nums[j] = tmp[j];
}
}
}
}
vector<int> sortArray(vector<int>& nums) {
mergeSort(nums, 0, nums.size() - 1);
return nums;
}
};
4)leetcode运行结果
a)递归法结果
b)非递归法结果:

(五)非比较排序
1、计数排序
1)思路
2)特性分析
3)代码
class Solution {
public:
vector<int> sortArray(vector<int>& nums) {
int maxNum = nums[0], minNum = nums[0];
for(auto x : nums)
{
maxNum = max(maxNum,x);
minNum = min(minNum,x);
}
int range = maxNum - minNum + 1;
vector<int> hash(range,0);
for(auto x : nums)
{
hash[x-minNum]++;
}
int k = 0;
for(int i = 0; i < range; i++)
{
while(hash[i]--) nums[k++] = minNum + i;
}
return nums;
}
};
4)leetcode运行结果
2、基数排序
1)思路
举例 :
2)特性分析
3)代码
当前代码中增加了负数的处理逻辑
#include <vector>
#include <algorithm>
using namespace std;
class Solution {
public:
vector<int> sortArray(vector<int>& nums) {
if (nums.empty()) return nums;
// 分离正数和负数
vector<int> positiveNums, negativeNums;
for (int num : nums) {
if (num >= 0) {
positiveNums.push_back(num);
} else {
negativeNums.push_back(-num); // 负数取反
}
}
// 对正数和负数分别进行基数排序
if (!positiveNums.empty()) {
radixSort(positiveNums);
}
if (!negativeNums.empty()) {
radixSort(negativeNums);
// 负数取反后再排序需要恢复负号
for (int& num : negativeNums) {
num = -num;
}
// 负数部分需要从大到小排序
reverse(negativeNums.begin(), negativeNums.end());
}
// 合并负数和正数
nums = negativeNums;
nums.insert(nums.end(), positiveNums.begin(), positiveNums.end());
return nums;
}
private:
void radixSort(vector<int>& nums) {
int maxVal = *max_element(nums.begin(), nums.end());
for (int exp = 1; maxVal / exp > 0; exp *= 10) {
countingSort(nums, exp);
}
}
void countingSort(vector<int>& nums, int exp) {
int n = nums.size();
vector<int> output(n);
vector<int> count(10, 0);
for (int i = 0; i < n; i++) {
int index = (nums[i] / exp) % 10;
count[index]++;
}
for (int i = 1; i < 10; i++) {
count[i] += count[i - 1];
}
for (int i = n - 1; i >= 0; i--) {
int index = (nums[i] / exp) % 10;
output[count[index] - 1] = nums[i];
count[index]--;
}
for (int i = 0; i < n; i++) {
nums[i] = output[i];
}
}
};