十大排序(C++)
准备秋招,根据网上的模板整理了一份c++版的十大排序算法。
#include<algorithm>
#include<iostream>
#include <vector>
#include <math.h>
using namespace std;
void bubble_sort(vector<int>& nums){
//冒泡排序:将最大值或者最小值,依次顶到队尾
for(int i = 0; i < nums.size() - 1; i++){
for(int j = 0; j < nums.size() - 1 - i; j++){
if(nums[j] < nums[j+1]) swap(nums[j], nums[j+1]);
}
}
}
void selective_sort(vector<int>& nums){
for(int i=0; i<nums.size()-1; i++){
int min = i;
for(int j=i+1; j<nums.size(); j++){
if(nums[j] < nums[min]){
min = j;
}
}
swap(nums[i], nums[min]);
}
}
//插入排序,依次遍历:将前边看作是排序序列,然后依次遍历一个后边的数字,放到排序序列中的合适位置
void insert_sort(vector<int>& nums){
for(int i=1; i<nums.size(); i++){
if(nums[i] < nums[i-1]){
int tmp = nums[i];
for(int j=i-1; j>=0 && nums[j]>tmp; j--){
nums[j+1] = nums[j];
nums[j] = tmp;
}
}
}
}
//希尔排序:是插入排序的进阶版;插入排序当序列非常混乱时,耗时严重;希尔排序将序列榆次划分增量,每次将对应增量的小数据进行插入排序
//然后缩小增量,是一个逐步让数据逐渐趋向排序序列的方法
void shellSort(vector<int>& nums){
for(int d=nums.size()/2; d>0; d/=2){ //这里是进行增量划分
for(int i=d; i<nums.size(); i++){
for(int j=i-d; j>=0; j-=d){
if(nums[j] > nums[j+d]){
int tmp = nums[j];
nums[j] = nums[j + d];
nums[j+d] = tmp;
}
}
}
}
}
//快速排序:采用的是分治的方法,将数据先挑一个数,比如中间的数,大于这个数的都放到右侧,小于这个数的都放到左侧;然后依次递归缩小区间直至区间长度为1
void quickSort(vector<int>& num, int low, int high) {
if (low >= high) return;
int pivot = num[(low + high) / 2];
int i = low - 1, j = high + 1;
while (i < j) {
while (num[++i] > pivot);
while (num[--j] < pivot);
if (i < j) std::swap(num[i], num[j]);
}
quickSort(num, low, j);
quickSort(num, j + 1, high);
}
//归并排序:采用分治的思想,就是把序列依次划分为单个数字,当做是已排序的,然后分别从下到上合并,在合并的过程中排序;
//速度最快的稳定排序算法;快排速度的话不稳定
//https://zhuanlan.zhihu.com/p/74820690
void Merge(vector<int>& nums,int low,int mid,int high){
//low为第1有序区的第1个元素,i指向第1个元素, mid为第1有序区的最后1个元素
int i=low,j=mid+1,k=0; //mid+1为第2有序区第1个元素,j指向第1个元素
int *temp=new int[high-low+1]; //temp数组暂存合并的有序序列
while(i<=mid&&j<=high){
if(nums[i]<=nums[j]) //较小的先存入temp中
temp[k++]=nums[i++];
else
temp[k++]=nums[j++];
}
while(i<=mid)//若比较完之后,第一个有序区仍有剩余,则直接复制到t数组中
temp[k++]=nums[i++];
while(j<=high)//同上
temp[k++]=nums[j++];
for(i=low,k=0;i<=high;i++,k++)//将排好序的存回arr中low到high这区间
nums[i]=temp[k];
delete []temp;//释放内存,由于指向的是数组,必须用delete []
}
void MergeSort (vector<int>& nums, int low,int high) {
if(low>=high) { return; } // 终止递归的条件,子序列长度为1
int mid = low + (high - low)/2; // 取得序列中间的元素
MergeSort(nums,low,mid); // 对左半边递归
MergeSort(nums,mid+1,high); // 对右半边递归
Merge(nums,low,mid,high); // 合并
}
//堆排序:就是考虑堆的属性,最大堆或者最小堆,即父节点大于根节点或者父节点小于根节点;构建大顶堆后将根节点与最后一位交换;然后此时堆分为有序区
//和无序区,无序区需要从新大顶堆构建,构建后将根节点与此时的无序区最后一位交换,依次构建大顶堆并缩小无序区的长度
//还没有看懂....
void AdjustDown(vector<int>& nums, int i, int len)
{
int temp = nums[i]; // 暂存A[i]
for(int largest=2*i+1; largest<len; largest=2*largest+1)
{
if(largest!=len-1 && nums[largest+1]>nums[largest])
++largest; // 如果右子结点大
if(temp < nums[largest])
{
nums[i] = nums[largest];
i = largest; // 记录交换后的位置
}
else
break;
}
nums[i] = temp; // 被筛选结点的值放入最终位置
}
void BuildMaxHeap(vector<int>& nums, int len)
{
for(int i=len/2-1; i>=0; --i) // 从i=n/2-1到0,反复调整堆
AdjustDown(nums, i, len);
}
void HeapSort(vector<int>& nums, int n)
{
BuildMaxHeap(nums, n); // 初始建堆
for(int i=n-1; i>0; --i) // n-1趟的交换和建堆过程
{
// 输出最大的堆顶元素(和堆底元素交换)
nums[0] = nums[0]^nums[i];
nums[i] = nums[0]^nums[i];
nums[0] = nums[0]^nums[i];
// 调整,把剩余的n-1个元素整理成堆
AdjustDown(nums, 0, i);
}
}
//计数排序
//非比较类型的排序算法,与归并,堆排比,不用做数字比较,而且比归并来说,虽然速度慢了,但是不耗内存;思路就是:先找到序列的最大最小值,然后设置多少个桶,每个桶里只放一个数字计数,
//然后再依次访问桶,取出相应个数量的数字;进行排序
void countSort(vector<int>& nums){
int maxValue = INT_MIN, minValue = INT_MAX;
for(int num : nums){
if(num > maxValue){
maxValue = num;
}
if(num < minValue){
minValue = num;
}
}
vector<int> cnt(maxValue - minValue + 1, 0); //通的个数,用来计数每个元素
for(int num: nums){
cnt[num - minValue]++;
}
int index = 0;
for(int i = minValue; i<=maxValue; i++){
int ct = i - minValue;
while(cnt[ct]--){
nums[index++] = i;
}
}
}
//桶排序:是计数排序的升级版;计数排序的桶的个数和单个数字是一样多的,不能处理小数或者数很多的情况;桶排序,将一个范围的数映射到一个桶内,然后桶内排序,最后提取出来就行;
//桶排序对于桶内数据分的比较均匀时,速度快;工作的原理是将数据分到有限数量的桶子里,然后每个桶再分别排序(有可能再使用别的排序算法或是以递回方式继续使用桶排序进行排序)
//空间消耗比较大
void bucketSort(vector<int>& nums, int k){ //k是桶的数量
//首先计算最大最小值,判断每个桶装的数字的范围
int maxValue = INT_MIN, minValue = INT_MAX;
for(int num : nums){
if(num > maxValue){
maxValue = num;
}
if(num < minValue){
minValue = num;
}
}
int size = (maxValue - minValue + 1) / k + 1; //这是每个桶与另一个桶之间的数字大小间隔
//创建桶;这里需要确定每个桶多大;动态内存处理;
vector<vector<int>> bucket(k); //二维的数组,行为桶,列为对应的每个桶里的数、
for(int i=0; i<nums.size(); i++){
bucket[(nums[i] - minValue) / size].push_back(nums[i]); //将所有的数据都放到对应的桶里
}
int index = 0;
for(int i=0; i<k; i++){
int sz = bucket[i].size(); //当前桶有多大;对当前桶排序;sort:或者快排
//quickSort(bucket[i], 0, bucket[i].size()-1);
insert_sort(bucket[i]);
for(int j=0; j<sz; j++){
nums[index++] = bucket[i][j];
}
}
}
//基数排序:是一种非比较型的排序算法;思路是将数字统一为同样的长度,然后依次对个位排序,十位排序,依次类推;稳定的排序方式
//基数排序比基于比较的排序算法(比如快速排序)要快。但由于需要额外的内存空间,因此当内存空间稀缺时,原地置换算法(比如快速排序)或许是个更好的选择。
//实现方法:首先找到最大数字,判断最大位数,依次循环遍历每一位的排序结果;对于每一位数字;都利用桶排,查找每位的出现次数,然后将桶的个数相加;倒序遍历数组即可将数组按此位排列到辅助数组中
//缺点:解决负数麻烦
void radix_sort(vector<int>& nums, int exp){
int n = nums.size();
int output[n]; //辅助存储空间,存储按照此位排序后的数组
int buckets[10] = {0}; //存放0-9的10个桶
for(int i=0; i<nums.size(); i++){
buckets[(nums[i] / exp) % 10]++; //统计此前数字的相应位是几
}
//一下操作将桶与output关联起来
for(int i=1; i<10; i++){
buckets[i] += buckets[i-1];
}
//将数组中的数字按照相应位置的桶排序拷贝到辅助数组,拷贝过去的顺序就是按相应位置排序的顺序
for(int i=n-1; i>=0; i--){
output[buckets[(nums[i] / exp) % 10] - 1] = nums[i];
buckets[(nums[i] / exp) % 10]--;
}
//拷贝回原数组
for(int i=0; i < n; i++){
nums[i] = output[i];
}
}
void radixSort(vector<int>& nums){
int maxValue = INT_MIN;
for(int num : nums){
if(num > maxValue) maxValue = num;
}
for(int exp = 1; maxValue/exp > 0; exp *= 10){ //exp位数,依次遍历每一位的排序结果
radix_sort(nums, exp);
}
}
int main(){
vector<int> nums = {1, 20, 11, 45, 1, 101, 66, 2, -2};
//bubble_sort(nums);
//selective_sort(nums);
//insert_sort(nums);
//shellSort(nums);
//quickSort(nums, 0, nums.size()-1);
//MergeSort(nums, 0, nums.size()-1);
//HeapSort(nums, nums.size());
//countSort(nums);
bucketSort(nums, 3);
//radixSort(nums);
cout << "The list has sorted! \n";
for(int num : nums){
cout << num << endl;
}