参考此处
一、快排:
1、算法思想:
选择一个基准元素,将比基准元素小的元素放在其前面,比基准元素大的元素放在其后面,然后在将小于基准值元素的子数列和大于基准元素的子数列按原来的方法排序,直到整个序列有序;
2、应用场景:
a.求数组中第k小的数
将数组中某一个元素m作为划分依据,即m=arr[0]。若m前面的元素个数大于k,则第k小的数一定在m前面的元素中,这时我们只需要继续在m前面的元素中找第k小的数;若m前面的元素小于k,则第k小的数一定在m后面的元素中,这时我们只需要在m后面的元素中找第k小的数;
3、代码如下:
#include<iostream>
#include<vector>
using namespace std;
int dfs(vector<int>&nums, int low, int high) {
int pivot = nums[low];
while (low < high) {
while (low < high&&nums[high]>=pivot) {
high--;
}
nums[low] = nums[high];
while (nums[low] <= pivot && low < high) {
low++;
}
nums[high] = nums[low];
}
nums[low] = pivot;
return low;
}
void quickSearch(vector<int>&nums, int left, int right, int k) {
int j = dfs(nums, left, right);
if (j == k - 1) return;
else if (j > k - 1) quickSearch(nums, left, j - 1, k);
else quickSearch(nums, j + 1, right, k);
}
int main() {
vector<int>nums = { 9,1,2,8,4 };
int k = 5;
int left = 0;
int right = nums.size() - 1;
quickSearch(nums, left, right, k);
/*vector<int>res(nums.begin(), nums.begin() + k);
for (int num : res) {
cout << num << endl;
}*/
cout << nums[k - 1] << endl;
return 0;
}
二、冒泡排序:
1、算法思想:
在要排序的一组数中,对当前还未排好序的范围内的全部数,自上而下对相邻的两个数依次进行比较,让较大的数往下沉,较小的往上冒。
即:每当两相邻的数比较后发现他们的排序与排序要求相反时,就将他们互换。
2、优缺点:
优点:稳定
缺点:慢,每次只能移动两个相邻的数据;
3、代码如下:
#include<iostream>
#include<vector>
using namespace std;
int main() {
vector<int>nums = { 9,1,2,8,4 };
int n = nums.size();
for (int i = 0; i < n; i++) {
for (int j = 0; j < n - 1 - i; j++) {
if (nums[j] > nums[j + 1]) {
swap(nums[j], nums[j + 1]);
}
}
}
for (int num : nums) {
cout << num << " ";
}
cout << endl;
return 0;
}
三、插入排序:
1、算法思想:
将一个记录插入到已排序好的有序表中,从而得到一个新的,记录数增1的有序表。即先将序列的第一个记录看成是一个有序的子序列,然后从第二个记录逐个进行插入,直至整个序列有序为止。
2、优缺点:
优点:稳定,快
缺点:比较次数不一定,比较次数越少,插入点后的数据移动越多,特别是数据量庞大的时候
3、代码如下:
#include<iostream>
#include<vector>
using namespace std;
int main() {
vector<int>nums = { 9,1,2,8,4 };
int n = nums.size();
for (int i = 1; i < n; i++) {
int j = i;
while (j > 0) {
if (nums[j] < nums[j - 1]) {
swap(nums[j], nums[j - 1]);
}
j--;
}
}
for (int num : nums) {
cout << num << " ";
}
cout << endl;
return 0;
}
四、希尔排序:
可以参考此处
1、算法思想:
先将整个待排序元素序列分割成若干子序列(由相隔某个“增量”的元素组成的)分别进行直接插入排序,然后依次缩减增量再进行排序,待整个序列中的元素基本有序(增量足够小)时,再对全体元素进行一次直接插入排序(因为直接插入排序在元素基本有序的情况下,效率很高);
改进的插入排序;
2、适用场景:
比较在希尔排序中是最主要的操作,而不是交换。用已知最好的步长序列的希尔排序比直接插入排序要快,甚至在小数组中比快速排序和堆排序还快,但在涉及大量数据时希尔排序还是不如快排。
3、代码如下:
#include<iostream>
#include<vector>
using namespace std;
void intersort(vector<int>&arr,int gap,int i) {
while (i >= gap) {
if (arr[i] < arr[i - gap]) {
swap(arr[i], arr[i - gap]);
}
i -= gap;
}
}
int main() {
vector<int>nums = { 9,1,2,8,4 };
int n = nums.size();
for (int gap = n / 2; gap > 0; gap /= 2) {
for (int i = gap; i < n; i++) {
intersort(nums, gap, i);
}
}
for (int num : nums) {
cout << num << " ";
}
cout << endl;
return 0;
}
五、直接选择排序:
1、算法思想:
选择排序(Selection sort)是一种简单直观的排序算法。它的工作原理是每一次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完;
第一趟:从第一个记录开始,将后面n-1个记录进行比较,找到其中最小的记录和第一个记录进行交换;
第二趟:从第二个记录开始,将后面n-2个记录进行比较,找到其中最小的记录和第2个记录进行交换;
…
第i趟:从第i个记录开始,将后面n-i个记录进行比较,找到其中最小的记录和第i个记录进行交换;
以此类推,经过n-1趟比较,将n-1个记录排到位,剩下一个最大记录直接排在最后;
2、代码如下:
#include<iostream>
#include<vector>
using namespace std;
int main() {
vector<int>nums = { 9,1,2,8,4 };
int n = nums.size();
for (int i = 0; i < n; i++) {
int temp = i;
for (int j = i + 1; j < n; j++) {
if (nums[j] < nums[temp]) {
temp = j;
}
}
swap(nums[i], nums[temp]);
}
for (int num : nums) {
cout << num << " ";
}
cout << endl;
return 0;
}
六、归并排序:
参考此处
1、算法思想:
归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。
2、代码如下:
(1)递归版:
#include <iostream>
#include <vector>
using namespace std;
void merge(vector<int> &nums, int left, int mid, int right, vector<int> &temp)
{
int i = left;
int j = mid + 1;
int t = 0;
while (i <= mid && j <= right)
{
if (nums[i] <= nums[j])
{
temp[t] = nums[i];
t++;
i++;
}
else
{
temp[t] = nums[j];
t++;
j++;
}
}
while (i <= mid)
{ //将左边剩余元素填充到temp中
temp[t] = nums[i];
t++;
i++;
}
while (j <= right)
{ //将右边剩余元素填充到temp中
temp[t] = nums[j];
t++;
j++;
}
t = 0;
//将temp中的元素全部拷贝到原数组中
while (left <= right)
{
nums[left] = temp[t];
left++;
t++;
}
}
void MS_sort(vector<int> &nums, int left, int right, vector<int> &temp)
{
if (left < right)
{
int mid = left + (right - left) / 2;
MS_sort(nums, left, mid, temp); //左边归并排序,使得左子序列有序
MS_sort(nums, mid + 1, right, temp); //右边归并排序,使得右子序列有序
merge(nums, left, mid, right, temp); //将两个有序子数组合并操作
}
}
int main()
{
vector<int> nums = {4, 1, 2, 8, 5};
vector<int> temp(nums.size());
MS_sort(nums, 0, nums.size() - 1, temp);
for (int num : nums)
{
cout << num << " ";
}
cout << endl;
return 0;
}
(2)迭代版:
#include <iostream>
#include <vector>
using namespace std;
void MergeSort(vector<int> &nums, int n)
{
int i, next, left_min, left_max, right_min, right_max;
//开辟一个与原来数组一样大小的空间用来存储
vector<int> temp(n, 0);
//逐级上升,第一次比较2个,第二次比较4个,第三次比较8个.......
for (i = 1; i < n; i *= 2)
{
//每次都从0开始,数组的头元素开始
for (left_min = 0; left_min < n - i; left_min = right_max)
{
right_min = left_min + i;
left_max = left_min + i;
right_max = left_max + i;
if (right_max > n)
{
right_max = n;
}
next = 0;
while (left_min < left_max && right_min < right_max)
{
if (nums[left_min] < nums[right_min])
{
temp[next] = nums[left_min];
next++;
left_min++;
}
else
{
temp[next] = nums[right_min];
next++;
right_min++;
}
}
while (left_min < left_max)
{
right_min--;
left_max--;
nums[right_min] = nums[left_max];
}
while (next > 0)
{
right_min--;
next--;
nums[right_min] = temp[next];
}
}
}
}
int main()
{
vector<int> nums = {4, 1, 2, 8, 5};
int n = nums.size();
MergeSort(nums, n);
for (int num : nums)
{
cout << num << " ";
}
cout << endl;
return 0;
}
七、堆排序:
参考此处
1、算法思想:
a.将无需序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆;
b.将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端;
c.重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。
2、代码如下:
#include <iostream>
#include <vector>
using namespace std;
/*调整大顶堆(仅仅是调整过程,建立在大顶堆已构建的基础之上)*/
void adjust_heap(vector<int> &nums, int i, int len)
{
int temp = nums[i]; //先取出当前元素i
for (int k = i * 2 + 1; k < len; k = k * 2 + 1)
{
if (k + 1 < len && nums[k] < nums[k + 1])
{
k++;
}
if (nums[k] > temp)
{
nums[i] = nums[k];
i = k;
}
else
{
break;
}
}
nums[i] = temp;
}
void heap_sort(vector<int> &nums)
{
//构建大顶堆
for (int i = nums.size() / 2 - 1; i >= 0; i--)
{
//从第一个非叶子节点从下至上,从右至左调整结构
adjust_heap(nums, i, nums.size());
}
//调整堆结构+交换堆顶元素与末尾元素
for (int j = nums.size() - 1; j > 0; j--)
{
swap(nums[0], nums[j]); //将堆顶元素与末尾元素进行交换
adjust_heap(nums, 0, j); //重新对堆进行调整
}
}
int main()
{
vector<int> nums = {4, 1, 2, 8, 5};
int n = nums.size();
heap_sort(nums);
for (int num : nums)
{
cout << num << " ";
}
cout << endl;
return 0;
}
八、计数排序:
参考此处
1、算法思想:
第一步:找出原数组中元素值最大的,记为max。
第二步:创建一个新数组count,其长度是max加1,其元素默认值都为0。
第三步:遍历原数组中的元素,以原数组中的元素作为count数组的索引,以原数组中的元素出现次数作为count数组的元素值。
第四步:创建结果数组result,起始索引index。
第五步:遍历count数组,找出其中元素值大于0的元素,将其对应的索引作为元素值填充到result数组中去,每处理一次,count中的该元素值减1,直到该元素值不大于0,依次处理count中剩下的元素。
第六步:返回结果数组result
2、代码如下:
(1)最基础的方法:
#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
#include "limits.h"
using namespace std;
vector<int> cnt_sort(vector<int> &nums)
{
int max_num = INT_MIN;
for (int i = 0; i < nums.size(); i++)
{
max_num = max(max_num, nums[i]);
}
vector<int> temp(max_num + 1, 0);
for (int num : nums)
{
temp[num]++;
}
vector<int> res;
for (int i = 0; i < temp.size(); i++)
{
while (temp[i] > 0)
{
res.push_back(i);
temp[i]--;
}
}
return res;
}
int main()
{
vector<int> nums = {4, 1, 2, 8, 5};
int n = nums.size();
vector<int> res = cnt_sort(nums);
for (int num : res)
{
cout << num << " ";
}
cout << endl;
return 0;
}
(2)简单优化后:
#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
#include "limits.h"
using namespace std;
vector<int> cnt_sort(vector<int> &nums)
{
int max_num = INT_MIN;
int min_num = INT_MAX;
for (int i = 0; i < nums.size(); i++)
{
max_num = max(max_num, nums[i]);
min_num = min(min_num, nums[i]);
}
vector<int> temp(max_num - min_num + 1, 0);
for (int num : nums)
{
temp[num - min_num]++;
}
vector<int> res;
for (int i = 0; i < temp.size(); i++)
{
while (temp[i] > 0)
{
res.push_back(i + min_num);
temp[i]--;
}
}
return res;
}
int main()
{
vector<int> nums = {4, 1, 2, 8, 5};
int n = nums.size();
vector<int> res = cnt_sort(nums);
for (int num : res)
{
cout << num << " ";
}
cout << endl;
return 0;
}
九、桶排序:
参考此处
1、算法思想:
一句话总结:划分多个范围相同的区间,每个子区间自排序,最后合并。
桶排序是计数排序的扩展版本,计数排序可以看成每个桶只存储相同元素,而桶排序每个桶存储一定范围的元素,通过映射函数,将待排序数组中的元素映射到各个对应的桶中,对每个桶中的元素进行排序,最后将非空桶中的元素逐个放入原序列中。
桶排序需要尽量保证元素分散均匀,否则当所有数据集中在同一个桶中时,桶排序失效
2、代码如下:
#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
#include "limits.h"
using namespace std;
vector<int> bucket_sort(vector<int> &nums)
{
int maxNum = INT_MIN;
int minNum = INT_MAX;
int n = nums.size();
for (int i = 0; i < n; i++)
{
maxNum = max(maxNum, nums[i]);
minNum = min(minNum, nums[i]);
}
//计算桶的数量
int bucketNum = (maxNum - minNum) / n + 1;
vector<vector<int>> bocketArr(bucketNum);
//把元素放到桶中
for (int num : nums)
{
bocketArr[(num - minNum) / n].push_back(num);
}
//对桶中的元素进行排序
vector<int> res;
for (auto &arr : bocketArr)
{
sort(arr.begin(), arr.end());
for (int t : arr)
{
res.push_back(t);
}
}
return res;
}
int main()
{
vector<int> nums = {4, 1, 2, 8, 5};
int n = nums.size();
vector<int> res = bucket_sort(nums);
for (int num : res)
{
cout << num << " ";
}
cout << endl;
return 0;
}
十、基数排序:
参考此处
1、算法思想:
LSD(Least significant digital):排序方式由数值的最右边(低位)开始
MSD(Most significant digital):由数值的最左边(高位)开始。
2、代码如下:
(1)LSD:
#include <iostream>
#include <vector>
#include <cmath>
#include <algorithm>
using namespace std;
//辅助函数,求数据的最大位数
int GetNumInPos(int num, int pos)
{
int temp = 1;
for (int i = 0; i < pos - 1; i++)
{
temp *= 10;
}
return (num / temp) % 10;
}
void radix_sort(vector<int> &nums)
{
int n = nums.size();
vector<vector<int>> radixArr(10); //分为0~9的序列空间
for (int pos = 1; pos <= 10; pos++)
{
for (int i = 0; i < n; i++)
{ //分配过程
int temp = GetNumInPos(nums[i], pos);
radixArr[temp].push_back(nums[i]);
}
for (int i = 0, j = 0; i < 10; i++)
{ //收集
while (!radixArr[i].empty())
{
nums[j] = radixArr[i].front(); //取首部数据依次插入原数组
radixArr[i].erase(radixArr[i].begin()); //移除首部元素
j++;
}
}
}
}
int main()
{
vector<int> nums = {4, 1, 2, 8, 5};
int n = nums.size();
radix_sort(nums);
for (int num : nums)
{
cout << num << " ";
}
cout << endl;
return 0;
}