#include <stdio.h>
#include <iostream>
#include <vector>
#include <chrono>
#include <map>
using namespace std;
//冒泡排序
void bubbleSort(vector<int> &a) {
for (int i = 0; i < a.size()-1; i++) {
for (int j = 0; j < a.size() - i - 1; j++) {
if (a[j] > a[j + 1]) {
int temp = a[j];
a[j] = a[j + 1];
a[j + 1] = temp;
}
}
}
}
//选择排序
//在序列中找到最小的元素,放在第一个位置,从剩余未排序元素中继续寻找最小元素,放在第二个位置
void selectSort(vector<int> &a) {
for (int i = 0; i < a.size() - 1; i++) {
int minIndex = i;
int min = a[i];
for (int j = i + 1; j < a.size(); j++) {
if (min > a[j]) {
min = a[j];
minIndex = j;
}
}
//将当前最小值与当前循环头交换位置
a[minIndex] = a[i];
a[i] = min;
}
}
//插入排序
//把n个待排序的元素第一位看成有序表,其他的看成无序表,排序过程中,每次从无序表中取出一个数,依次与有序表中的数进行比较,插入到合适的位置
void InsertSort(vector<int> &a) {
int insertVal = 0;
int insertIndex = 0;
for (int i = 1; i < a.size(); i++) {
insertVal = a[i];
//由于前面已经是有序表,遍历当前元素前面的元素的下标寻找插入的位置
insertIndex = i - 1;
while (insertIndex >= 0 && insertVal < a[insertIndex]) {
a[insertIndex + 1] = a[insertIndex];
insertIndex--;
}
// 当退出while循环时,说明插入的位置找到, insertIndex + 1
if (insertIndex + 1 != i){
a[insertIndex + 1] = insertVal;
}
}
}
//希尔排序
/*
希尔排序也是一种插入排序,它是简单插入排序进过改进之后的一个更高效的版本,也称为缩小增量排序
与插入排序的不同之处在于,它会优先比较较远的元素
*/
//希尔冒泡
void shellSort(vector<int> &a) {
for (int step = a.size() / 2; step > 0; step /= 2) {
for (int i = step; i < a.size(); i++) {
for (int j = i - step; j >= 0; j -= step) {
if (a[j] > a[j + step]) {
int temp = a[j];
a[j] = a[j + step];
a[j + step] = temp;
}
}
}
}
}
//希尔插入
void shellSort_Insert(vector<int> &a) {
for (int step = a.size() / 2; step >= 0; step /= 2) {
for (int i = step; i < a.size(); i++) {
int j = i;
int temp = a[j];
if (a[j] < a[j - step]) {
while (j - step >= 0 && temp < a[j - step]) {
a[j] = a[j - step];
j -= step;
}
a[j] = temp;
}
}
}
}
//快速排序
//通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小
//则可分别对这两部分记录进行排序,以达到整个排序的过程
/*
算法描述
快速排序使用分治法把一个串分为两个子串
找一个基准点,暂时选中间点为基准点
重新排列数列,比基准值小的放在基准点前面,大的放在后面
递归的把小于基准值的子数列和大于基准值的子数列排序
*/
void quicklySort(vector<int>& a,int left,int right) {
//数组的左下标和右下标
int l = left;
int r = right;
//中轴值
int pivot = a[(left + right) / 2];
//临时变量
int temp = 0;
/*
while循环的目的是让比pivot值小放到左边,比pivot值大的放在右边
*/
while (l < r) {
//在pivot的左边一直找,找到大于等于pivot值,才退出
while (a[l] < pivot) {
l += 1;
}
//在pivot的右边一直找,找到小于等于pivot值,才退出
while (a[r] > pivot) {
r -= 1;
}
//若l>=r说明pivot的左右两值,已经按照左边全部是小于等于pivot值,右边全部是大于等于pivot值
if (l >= r) {
break;
}
//交换
temp = a[l];
a[l] = a[r];
a[r] = temp;
//若交换后,发现a[l] == pivot值 相等 r--,前移
if (a[l] == pivot) {
r -= 1;
}
if (a[r] == pivot) {
l += 1;
}
}
//若l==r,必须l++,r--,否则为出现栈溢出
if (l == r) {
l += 1;
r -= 1;
}
//向左递归
if (left < r) {
quicklySort(a, left, r);
}
//向右递归
if (right > l) {
quicklySort(a, l, right);
}
}
//归并排序
/*
归并排序是建立在归并操作上的一种有效的排序算法,该算法采用分治法的一个非常典型的应用。将已有序的子序列合并
得到完全的有序的序列;即先使每个子序列有序,再使子序列时间段间有序。若将两个有序表合并成一个有序表
称为2-路归并
算法描述:
1. 把长度为n的输入序列分成两个长度为n/2的子序列;
2. 对这两个子序列分别采用归并排序;
3. 将两个排序好的子序列合并成一个最终的排序序列。
*/
void merge(vector<int>& a, int start,int end,int mid) {
//开辟一个空间用于合并
int *tmp = (int*)malloc((end - start + 1) * sizeof(int));
int i = start; //左子区间起始下标
int j = mid + 1; //右子区间起始下标
int k = 0; //合并数组的下标
while (i <= mid && j <= end) {
if (a[i] <= a[j]) {
//左边值小或相等
tmp[k++] = a[i++];
}
else {
//右边值小
tmp[k++] = a[j++];
}
}
while (i <= mid) {
//右边值全部放入,但是左边还有值
tmp[k++] = a[i++];
}
while (j <= end) {
//左边值全部放入,但是右边还有值
tmp[k++] = a[j++];
}
for (int i = 0; i < k; i++) {
a[start + i] = tmp[i];
}
free(tmp);
}
void mergeSort(vector<int> &a, int start, int end) {
if (!a.empty() && start >= end) {
return;
}
int mid = (end + start) / 2;
mergeSort(a, start, mid);
mergeSort(a, mid + 1, end);
merge(a, start, end, mid);
}
/*
堆排序(Heap Sort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近视完全二叉树的结构
*/
void heapify(vector<int>& a, int len, int i) {
if (i >= len) return;
int left = 2 * i + 1;
int right = 2 * i + 2;
int max = i;
if (left < len && a[left]>a[max]) {
max = left;
}
if (right < len && a[right]>a[max]) {
max = right;
}
if (max != i) {
int temp = a[max];
a[max] = a[i];
a[i] = temp;
//此处递归是保证重新构造的堆顶必然大于(或小于)当前堆顶下的左右子节点
heapify(a, len, max);
}
}
void buildMaxHeap(vector<int>& a, int len) {
int parent = (len - 2) / 2;
for (int i = parent; i >= 0; i--) {
heapify(a, len, i);
}
}
void heapSort(vector<int>& a, int len) {
buildMaxHeap(a, len);
for (int i = len-1; i >= 0; i--) {
int temp = a[i];
a[i] = a[0];
a[0] = temp;
heapify(a, i, 0);
}
}
/*
计数排序(Counting Sort)
其核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。作为一种线性时间复杂度的排序
计数排序要求输入的数据必须是有确定范围的整数
算法描述
1. 找出待排序的数组中最大和最小的元素;
2. 统计数组中每个值为i的元素出现的次数,存入数组C的第i项;
3. 对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加);
4. 反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1。
*/
void coutSort(vector<int>& a, int len) {
if (len <= 0)return;
map<int, int> m;
for (int i = 0; i < len; i++) {
m[a[i]]++;
}
a.clear();
for (map<int, int>::iterator it = m.begin(); it != m.end(); it++) {
while ((*it).second--) {
a.push_back((*it).first);
}
}
}
/*
桶排序(BucketSort)是计数排序的升级版。他利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定
工作原理:假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别排序
(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序)
算法描述:
设置一个定量的数组当作空桶;
遍历输入数据,并且把数据一个一个放到对应的桶里去;
对每个不是空的桶进行排序;
从不是空的桶里把排好序的数据拼接起来。
*/
void bucketSort(vector<int>&a,int len) {
int i, j = 0;
//确定最大值和最小值
int max = INT_MIN;
int min = INT_MAX;
for (int i = 0; i < a.size(); i++) {
if (a[i] > max) max = a[i];
if (a[i] < min) min = a[i];
}
//生成桶数组
//设置最小的值为索引0,每个桶间隔为1,桶间隔为1相当于计数排序
int bucketLen = max - min + 1;
//初始化桶
int* bucket = new int[bucketLen];
for (int i = 0; i < bucketLen; i++) bucket[i] = 0;
//放入桶中
int index = 0;
for (int i = 0; i < len; i++) {
index = a[i] - min;
bucket[index]++;
}
//替换原序列
int start = 0;
for (int i = 0; i < bucketLen; i++) {
for (int j = start; j < start + bucket[i]; j++) {
a[j] = min + i;
}
start += bucket[i];
}
delete[] bucket;
}
/*
基数排序(RadixSort)是按照地位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位
算法描述:
1. 取得数组中的最大数,并取得位数;
2. arr为原始数组,从最低位开始取每个位组成radix数组;
3. 对radix进行桶排序(利用计数排序适用于小范围数的特点);
*/
int maxbit(vector<int>& a, int len) {
int d = 1;//保存最大的位数
int p = 10;
for (int i = 0; i < len; i++) {
while (a[i] >= p) {
p *= 10;
++d;
}
}
return d;
}
void RadixSort(vector<int>& a, int len) {
int d = maxbit(a, len);
int* tmp = new int[len];
int count[10];
int i, j, k;
int radix = 1;
//进行d次排序
for (int i = 1; i <= d; i++){
for (int j = 0; j < 10; j++) {
count[j] = 0;//每次分配前清空计数器
}
for (int j = 0; j < len; j++) {
k = (a[j] / radix) % 10;
count[k]++;
}
for (int j = 1; j < 10; j++) {
//将tmp中的位置依次分配给每个桶
count[j] = count[j - 1] + count[j];
}
for (int j = len - 1; j >= 0; j--) {
//将桶中记录依次收集到tmp中
k = (a[j] / radix) % 10;
tmp[count[k] - 1] = a[j];
count[k]--;
}
for (int j = 0; j < len; j++) {
a[j] = tmp[j];
}
radix *= 10;
}
delete[] tmp;
}
int main() {
int array[10] = { 1,23,4,5,6,7,8,9,0.7 };
vector<int> test_a = { 0,14,2,4323,34,5,64,17,68,9 };
int left = 0;
int right = test_a.size();
bubbleSort(test_a);
selectSort(test_a);
InsertSort(test_a);
shellSort(test_a);
quicklySort(test_a,left,right);
/*归并相当于遍历树,然后合并树*/
mergeSort(test_a, left, right - 1);
heapSort(test_a, right);
coutSort(test_a, right - 1);
bucketSort(test_a, right);
RadixSort(test_a, right);
for (auto i : test_a) {
cout << i << endl;
}
return 0;
}
C++排序算法
于 2023-04-07 17:20:31 首次发布