利用C++实现七种常见的排序算法:
具体的排序方法如下所示:
(1)冒泡排序
基本思想:比较相邻的两个数,如果前者比后者大,则进行交换。每一轮排序结束,选出一个未排序中最大的数放到数组后面。常见冒泡排序算法如下所示:
void bubblesort(int *arr,int n)
{
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n-i-1; j++)
{
if(arr[j]>arr[j+1])
{
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
升级版冒泡排序法:通过从低到高选出最大的数放到后面,再从高到低选出最小的数放到前面,如此反复,直到左边界和右边界重合。当数组中有已排序好的数时,这种排序比传统冒泡排序性能稍好。
void superbubblesort(int *arr,int n)
{
int left = 0, right = n-1;
while(left <right)
{
for(int i = left; i<right;i++)
{
if(arr[i]>arr[i+1])
{
int tempi = arr[i];
arr[i] = arr[i+1];
arr[i+1] = tempi;
}
}
right--;
for (int j = right; j >left; j--)
{
if(arr[j]>arr[j+1])
{
int tempj = arr[j];
arr[j] = arr[j+1];
arr[j+1] = tempj;
}
}
left++;
}
}
(2)快速排序
基本思想:选取一个基准元素,通常为数组最后一个元素(或者第一个元素)。从前向后遍历数组,当遇到小于基准元素的元素时,把它和左边第一个大于基准元素的元素进行交换,即从前往后找小的,从后往前找大的。利用分治策略从已经分好的两组中分别进行以上步骤,直到排序完成。
//把值进行交换
void swap(int *x,int *y)
{
int temp = *x;
*x = *y;
*y = temp;
}
//把数组分成两部分
int partition(int *a,int left ,int right)
{
int j = left; //用来遍历数组
int i = j-1; //用来指向小于基准元素的位置
int key = a[right]; //基准元素
//快速排序的思想,从前往后找大于基准元素的放到右边
for (; j < right; ++j)
{
if(a[j] <= key)
swap(&a[j], &a[++i]);
}
//把基准元素放到中间
swap(&a[right], &a[++i]);
//返回数组中间的位置
return i;
}
//快速排序的函数
void quicksort(int *a,int left ,int right)
{
if(left >= right)
return;
int mid = partition(a,left,right);
quicksort(a,left,mid-1);
quicksort(a,mid+1,right);
}
(3)直接插入排序
基本思想:和交换排序不同的是它不用进行交换操作,而是用一个临时变量存储当前值。当前面的元素比后面大时,先把后面的元素存入临时变量,前面元素的值放到后面元素位置,再到最后把其值插入到合适的数组位置。所以相应的辅助空间为O(1)。
void insertsort(int *a ,int n) //直接插入排序
{
int temp =0;
for (int i = 1; i < n; i++)
{
int j =i - 1;
if(a[i]<a[j])
{
temp = a[i];
a[i] = a[j];
while(temp <a[j-1])
{
a[j] = a[j-1];
j--;
}
a[j] = temp;
}
}
}
(4)希尔排序
基本思想: 在直接插入排序的思想下设置一个最小增量dk,刚开始dk设置为n/2。进行插入排序,随后再让dk=dk/2,再进行插入排序,直到dk为1时完成最后一次插入排序,此时数组完成排序。
void shellsort(int *a ,int n) //希尔排序主函数
{
int dk =n/2;
while (dk>1)
{
shellinsertsort(a , n , dk);
dk /= 2;
}
}
void shellinsertsort(int *a ,int n,int dk)
{
for (int i = dk; i < n; i++)
{
int j = i-dk;
if(a[i]<a[j])
{
int tmp = a[i];
a[i] = a[j];
while(a[j]>tmp)
{
a[j+dk] =a[j];
j-=dk;
}
a[j+dk] = tmp;
}
}
}
(5)直接选择排序
基本思想:依次选出数组最小的数放到数组的前面。首先从数组的第二个元素开始往后遍历,找出最小的数放到第一个位置。再从剩下数组中找出最小的数放到第二个位置。以此类推,直到数组有序。
void selectsort(int *a ,int n)
{
for (int i = 0; i < n; i++)
{
int key = i; //临时变量用来存储最小的变量
for (int j = i+1; j < n; j++)
{
if(a[j]<a[key])
key =j; //交换,找到最小的数值
}
if(key !=i)
{
int temp = a[key];
a[key] = a[i];
a[i] =temp;
}
}
}
(6)堆排序
基本思想:先把数组构造成一个大顶堆(父亲节点大于其子节点),然后把堆顶(数组最大值,数组第一个元素)和数组最后一个元素交换,这样就把最大值放到了数组最后边。把数组长度n-1,再进行构造堆,把剩余的第二大值放到堆顶,输出堆顶(放到剩余未排序数组最后面)。依次类推,直至数组排序完成。
//进行堆排序,依次选出最大值放在最后面
void heapsort(int *a ,int n)
{
creatheap(a,n/2-1,n);
for (int j = n-1; j >=0; j--)
{
int temp = a[0];
a[0] = a[j];
a[j] =temp;
int i = j/2-1;
creatheap(a, i, j);
}
}
// 创建大堆顶,i为当前节点,n为堆的大小
// 从第一个非叶子结点i从下至上,从右至左调整结构
// 从两个儿子节点中选出较大的来与父亲节点进行比较
// 如果儿子节点比父亲节点大,则进行交换
void creatheap(int *a,int i,int n)
{
for(;i>=0;--i)
{
int left = i*2 + 1; //左子树结点
int right = i*2 + 2; //右子树结点
int j =0;
if(right < n)
{
a[left]>a[right] ? j=left : j = right;
}
else
j=left;
if(a[j] >a[i])
{
int temp = a[i];
a[i] = a[j];
a[j] =temp;
}
}
}
(7)归并排序
基本思想:归并算法应用到分治策略,简单说就是把一个大的问题分解成易于解决的小问题后一个个解决,最后在把小问题的一步步合并成总问题的解。这里的排序应用递归来把数组分解成一个个小数组,直到小数组的数有序,再把有序的小数组两两合并而成有序的大数组。
void mergesort(int *a ,int left ,int right)
{
if(left >right)
return ;
int mid = (left+right)/2;
mergesort(a, left , mid-1);
mergesort(a, mid+1, right);
merge(a, left ,right ,mid);
}
//合并两个已经排好序的数组
void merge (int *a ,int left, int mid ,int right)
{
int len = right -left + 1; //数组的长度
int *temp = new int[len]; //分配len个长度的临时数组
int k = 0;
int i = left ;
int j = mid +1;
while (i<=mid && j<=right)
{
//选择较小的存入临时数组
temp[k++] = a[i] <= a[j] ? a[i++] : a[j++];
}
while(i<= mid)
{
temp[k++] = a[i++];
}
while(j<=right)
{
temp[k++] = a[j++];
}
for (int k = 0; k < len; k++)
{
a[left++] = temp[k];
}
}
运行,对相应的函数建立头文件,在主函数进行调用,主函数代码如下:
#include <iostream>
#include "BubbleSort.h"
#include "QuickSort.h"
#include "InsertSort.h"
#include "SelectSort.h"
#include "MergeSort.h"
using namespace std;
void main()
{
int arr[] = {10,6,5,2,3,8,7,4,9,1};
int n = sizeof(arr) / sizeof(arr[0]);
for(int i = 0; i < n ; i++)
cout<<arr[i]<<" ";
cout<<endl;
bubblesort(arr, n);
/*
最差时间复杂度为O(n^2);
平均时间复杂度为O(n^2);
稳定性:稳定;
辅助空间O(1)
*/
superbubblesort(arr,n);
/*
这种排序比传统冒泡排序性能稍好
*/
quicksort(arr,0,n-1);
/*
最差时间复杂度:O(n^2);
最优时间复杂度:O(nlogn);
平均时间复杂度:O(nlogn);
稳定性:不稳定的;
辅助空间:O(nlogn)
*/
insertsort(arr,n);
/*
最差时间复杂度:O(n^2);
最优时间复杂度:O(n);
平均时间复杂度:O(n^2);
稳定性:稳定;
辅助空间:O(1)
*/
shellsort(arr,n);
/*
最差时间复杂度:O(n^2);
最优时间复杂度:O(n);
平均时间复杂度:O(n^1.3);
稳定性:不稳定的;
辅助空间:O(1)
*/
selectsort(arr,n);
/*
最差时间复杂度:O(n^2);
最优时间复杂度:O(n^2);
平均时间复杂度:O(n^2);
稳定性:不稳定的;
辅助空间:O(1)
*/
heapsort(arr,n);
/*
最差时间复杂度:O(nlogn);
最优时间复杂度:O(nlogn);
平均时间复杂度:O(nlogn);
稳定性:不稳定的;
辅助空间:O(1)
*/
mergesort(arr,0,n-1);
/*
最差时间复杂度:O(nlogn);
最优时间复杂度:O(nlogn);
平均时间复杂度:O(nlogn);
稳定性:不稳定的;
辅助空间:O(n)
*/
for(int i = 0; i < n ; i++)
cout<<arr[i]<<" ";
cout<<endl;
}