1、稳定排序:如果 a 原本在 b 的前面,且 a == b,排序之后 a 仍然在 b 的前面,则为稳定排序。
2、非稳定排序:如果 a 原本在 b 的前面,且 a == b,排序之后 a 可能不在 b 的前面,则为非稳定排序。
3、原地排序:原地排序就是指在排序过程中不申请多余的存储空间,只利用原来存储待排数据的存储空间进行比较和交换的数据排序。
4、非原地排序:需要利用额外的数组来辅助排序。
int n=sizeof(a)/sizeof(int);//数组长度=总字节/一个元素所占字节
c++函数输入参数为数组时怎么求长度
https://blog.csdn.net/qq_40692109/article/details/102766573
https://blog.csdn.net/hengbao4/article/details/53224080
1.选择排序:
第一趟找到最小的数和第一个元素交换,第二趟在剩下的元素中找到最小的元素,将它与数组的第二个元素交换位置…依次选择
个人认为简便版本:(该版本相等元素不会交换,为稳定排序)
template<class T>
void SelectSort(T &a){
int n=sizeof(a)/sizeof(int);
for(int i=0;i<n-1;i++){//总共n-1趟
for(int j=i+1;j<n;j++){//每趟需要比较的次数n-1-i
if(a[j]<a[i]){
int temp=a[i];
a[i]=a[j];
a[j]=temp;
}
}
}
}
"官方"版本:
for (int i = 0; i < n - 1; i++) {
int min = i;
for (int j = i + 1; j < n; j++) {
if(a[min] > a[j]) min = j;
}
int temp = a[i];
a[i] = a[j];
a[j] = temp;
}
2.冒泡排序:
把第一个元素与第二个元素比较,如果第一个比第二个大,则交换他们的位置…这样一趟比较交换下来之后,排在最右的元素就会是最大的数。
除去最右的元素,我们对剩余的元素做同样的工作,如此重复直到排序完成。
template<class T>
void BubbleSort(T &a){
int n=sizeof(a)/sizeof(int);
for(int i=0;i<n-1;i++){//总共n-1趟
bool flag = 1;
for(int j=0;j<n-1-i;j++){//每趟需要比较的次数n-1-i,即逐渐-1
if(a[j]>a[j+1]){//大则向后交换
flag = 0;
int temp=a[j+1];
a[j+1]=a[j];
a[j]=temp;
}
}
if(flag) break;//一趟下来若flag没改变,即没发生交换,退出循环
}
}
flag为优化,若一趟遍历后相邻的元素都没有发生交换,意味着此时的数组已经是有序的,无需再对剩余的元素重复比较下去了。
3.插入排序:
当我们给无序数组做排序的时候,为了要插入元素,我们需要腾出空间,将其余所有元素在插入之前都向右移动一位,这种算法我们称之为插入排序,可以想一下打扑克牌时的手牌整理。
void InsertSort(T &a){
int n=sizeof(a)/sizeof(int);
for(int i=1;i<n;i++){//从第二个元素开始
int now=a[i],j;//记录待插入元素,等下还要放回
for(j=i-1;j>=0;j--){
if(a[j]>now){//判断有序区是否有大于待插入元素的
a[j+1]=a[j];
}
else break;
}
a[j+1]=now;//放回待插入元素
}
}
4.希尔排序:
希尔排序的思想是采用插入排序的方法,先让数组中任意间隔为 h 的元素有序,刚开始 h 的大小可以是 h = n / 2,接着让 h = n / 4,让 h 一直缩小,当 h = 1 时,也就是此时数组中任意间隔为1的元素有序,此时的数组就是有序的了。
template<class T>
void ShellSort(T &a){
int n=sizeof(a)/sizeof(int);
for(int h=n/2;h>0;h/=2){ //对每组间隔为h的分组进行排序,刚开始 h=n/2;
for(int i=h;i<n;i++){ //对各个局部分组进行插入排序,下面即为插入排序的嵌套,修改了间隔h
int temp=a[i],j;
for(j=i-h;j>=0 && a[j]>temp;j-=h){
a[j+h]=a[j];
}
a[j+h]=temp;
}
}
}
**? ? ? **对各个分组进行插入的时候并不是先对一个组排序完了再来对另一个组排序,而是轮流对每个组进行排序。
5.快速排序:
从数组中选择一个元素作为中轴元素,然后把数组中所有小于中轴元素的元素放在其左边,所有大于或等于中轴元素的元素放在其右边,此时中轴元素所处的位置的是有序的,无需再移动中轴元素的位置。从中轴元素那里开始把大的数组切割成两个小的数组(两个数组都不包含中轴元素),通过递归的方式,让中轴元素左边的数组和右边的数组也重复同样的操作,直到数组的大小为1,此时每个元素都处于有序的位置。
void quicksort(int a[],int l,int r){
int i=l,j=r,flag=a[(l+r)/2],tmp;
do{
while(a[i]<flag)i++;//从左找比中间哨兵大的数
while(a[j]>flag)j--;//从右找比中间哨兵小的数
if(i<=j){
tmp=a[i];
a[i]=a[j];
a[j]=tmp;
i++;j--;
}
}while(i<=j);
if(l<j){
quicksort(a,l,j);
}
if(i<r){
quicksort(a,i,r);
}
}
6.堆排序:
堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。
堆排序就是把堆顶的元素与最后一个元素交换,交换之后破坏了堆的特性,再把堆中剩余的元素再次构成一个大顶堆,然后再把堆顶元素与最后第二个元素交换…如此往复下去,等到剩余的元素只有一个的时候,此时的数组就是有序的了。
#include<bits/stdc++.h>
using namespace std;
void swap(int a[],int x,int y){
int temp=a[x];
a[x]=a[y];
a[y]=temp;
}
void show(int a[],int n){
for(int i=0;i<n;i++){
cout<<a[i]<<' ';
}
cout<<endl;
}
//大顶堆建立函数
void bigtop(int a[],int i,int n){//i是下标,n是数组大小
int father=i,child=2*i+1;
while(child<n){
if(child+1<n && a[child]<a[child+1]){
child++;//判断哪个子结点大,与父结点比较
}
if(a[father]<a[child]){
swap(a,father,child);
father=child;//子结点下标赋给父结点下标
}
child=child*2+1;//换行,比较当前父结点和下面的子结点
}
}
void HeapSort(int a[],int n){
//初始化为大顶堆
for(int i=n/2-1;i>=0;i--){//倒数第二排即数据的一半开始创建大顶堆,从下往上比较
bigtop(a,i,n);
}
//不断交换,不断建立大顶堆
for(int i=n-1;i>0;i--){
swap(a,0,i);//交换顶点和当前最后一个数据,即第i个
bigtop(a,0,i);//从上往下重新建立
}
}
int main(){
int a[] = {6,1,3,4,8,6,9,2,2,5};
int n=sizeof(a)/sizeof(int);
HeapSort(a,n);
show(a,n);
return 0;
}
7.计数排序:
基本思想就是把数组元素作为数组的下标,然后用一个临时数组统计该元素出现的次数,例如 temp[i] = m, 表示元素 i 一共出现了 m 次。最后再把临时数组统计的数据从小到大汇总起来,此时汇总起来是数据是有序的。
void CountSort(int a[],int n){
int min=a[0],max=min;
for(int i=1;i<n;i++){//寻找数组最大值和最小值
if(a[i]>max){
max=a[i];
}
if(a[i]<min){
min=a[i];
}
}
int b[max-min+1]={0};
for(int i=0;i<n;i++){//统计数组内元素出现的次数
b[a[i]-min]++;
}
int k=0;
for(int i=0;i<max-min+1;i++){//汇总到原数组
for(int j=b[i];j>0;j--){ui
a[k++]=i+min;//汇总时min的使用避免排序不了有负数的数组
}
}
}
8.桶排序:
桶排序就是把最大值和最小值之间的数进行瓜分,例如分成 10 个区间,10个区间对应10个桶,把各元素放到对应区间的桶中去,再对每个桶中的数进行排序,最后把全部的数据合并放回原数组中。
void BucketSort(int a[],int n){
vector< vector<int> > bucket(10);
for(int i=0;i<10;i++){//桶的初始化
vector<int> x ={0};//注意编译器对c++11的支持
bucket.push_back(x);
}
for(int i=0;i<n;i++){//分桶放入
bucket[a[i]/10].push_back(a[i]);
}
int k=0;
for(int i=0;i<10;i++){
sort(bucket[i].begin(),bucket[i].end());//单个桶内排序
for(vector<int>::iterator it=bucket[i].begin();it!=bucket[i].end();it++){
a[k++]=*it;//利用迭代器it实现合并
}
}
}
9.基数排序:
基数排序是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数。
步骤举例:按照个位数进行排序;按照十位数进行排序;按照百位数进行排序,此时序列有序。
void RadixSort(int a[],int n){
int max=0;//求数组中的最大值
for(int i=0;i<n;i++) max=a[i]>max?a[i]:max;
for(int digit=1;max/digit>0;digit*=10){//从个位开始按位排序
int b[n];//存放一趟排序后结果的临时数组
int bucket[10]={0};//初始化10个桶
for(int i=0;i<n;i++){
bucket[a[i]/digit%10]++;//统计数据出现次数
}
for(int i=1;i<10;i++){
bucket[i]+=bucket[i-1];//调整bucket各元素的值,调整后的值就是a中元素在b中的位置
}
for(int i=n-1;i>=0;i--){//将a中的元素填充到b中(从后往前排,先入后出)
int d=a[i]/digit%10;
b[bucket[d]-1]=a[i];
bucket[d]--;
}
memcpy(a,b,n*sizeof(int));//排序好的数组b复制回a
}
}
10.归并排序:
先将待排序列划分为子序列,待子序列有序(元素个数为1)后,再将子序列两两合并,从而得到完整有序的序列。
//递归函数
void mergesort(int *a, int *arr, int start, int end) {
if(start>=end) return;//递归终止条件(元素个数为1)
int mid=start+(end-start)/2;//以下5行,将数组划分为2个子数组,分别继续递归
int start1=start,end1=mid;
int start2=mid+1,end2=end;
mergesort(a,arr,start1,end1);
mergesort(a,arr,start2,end2);
int i=start;//已排序数组arrtmp计数器
while(start1<=end1 && start2<=end2) //升序合并两个子数组至arrtmp中
arr[i++]=a[start1]<a[start2]?a[start1++]:a[start2++];
while(start1<=end1) arr[i++]=a[start1++];//剩余元素直接加入数组arrtmp尾部
while(start2<=end2) arr[i++]=a[start2++];
memcpy(a+start,arr+start, (end-start+1)*sizeof(int));//将已排序数组arrtmp复制回到A中,排序完成
}
//主函数
void MergeSort(int *a, int n){
if(n<2) return;
int *arr=new int[n];//分配一个与待排序数组大小相同的数组
mergesort(a,arr,0,n-1);//调用递归函数进行排序
delete[] arr;
}