所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。排序算法,就是如何使得记录按照要求排列的方法。排序算法在很多领域得到相当地重视,尤其是在大量数据的处理方面。一个优秀的算法可以节省大量的资源。在各个领域中考虑到数据的各种限制和规范,要得到一个符合实际的优秀算法,得经过大量的推理和分析。
常见的排序算法可以分为以下几类,分别是:冒泡排序,选择排序,插入排序,快速排序,合并排序,堆排序,希尔排序以及多路归并排序。
1.冒泡排序: 该排序算法是所有排序算法中最基本的一种排序算法,其思想主要是通过与相邻元素进行比较,交换。重复多次来实现排序。是一种稳定的排序算法,时间复杂度为O(n*n)。 基本步骤如下: (1)对数组的各数据进行依次的比较。 (2)如果前面数据大于(小于)后面数据则交换这两个数据,经过第一轮的多次比较之后,便可以将该数组中的最小(最大)数据排好。 (3)再利用同样的方法将剩下的数据逐个进行比较,最后便可以按照从小到大(从大到小)的顺序排好数组中的各数据顺序。 示例代码如下:
#include <iostream>
using namespace std;
#include <time.h>
#include <stdlib.h>
//交换函数
void swap(int *a,int *b)
{
int temp=*a;
*a=*b;
*b=temp;
}
//冒泡排序算法
void BubbleSort(int a[],int n)
{
for(int i=0;i<n-1;i++)
{
for(int j=i+1;j<n;j++)
{
if(a[i]>a[j])
{
swap(a[i],a[j]);
}
}
}
}
int main()
{
int n;
cout<<"请输入要生成数组的大小:";
cin>>n;
int *a=new int[n];
srand(time(NULL));
for(int i=0;i<n;i++)
{
a[i]=rand()/1000+100;
}
cout<<"排序前的数组为:"<<endl;
for(int i=0;i<n;i++)
{
cout<<a[i]<<" ";
}
cout<<endl;
BubbleSort(a,n);
cout<<"排序后的数组为:"<<endl;
for(int i=0;i<n;i++)
{
cout<<a[i]<<" ";
}
cout<<endl;
return 0;
}
2.选择排序: 该算法的思路是在每一步中选择最小值来进行重新排序,从而达到排序的目的。是一种不稳定的排序算法,时间复杂度为O(n*n)。 基本步骤如下: (1)首先从原始数组中选择一个最小(最大)的数据,将其和位于第一个位置的数据交换。 (2)从剩下n-1个数据中选择次小(次大)的的一个数据,将其和第二个位置的数据交换。 (3)这样不断重复,直到最后两个数据完成交换。最后,完成对原始数据从小到大(从大到小)的排序。 示例代码如下:
#include <iostream>
using namespace std;
#include <time.h>
#include <stdlib.h>
void swap(int *a,int *b)
{
int temp=*a;
*a=*b;
*b=*a;
}
//选择排序算法
void SelectSort(int a[],int n)
{
int k;
for(int i=0;i<n-1;i++)
{
k=i;
for(int j=i+1;j<n;j++)
{
if(a[j]<a[k])
{
k=j;
}
}
if(k!=i)
{
swap(a[i],a[k]);
}
}
}
int main()
{
int n;
cout<<"请输入要生成数组的大小:";
cin>>n;
int *a=new int[n];
srand(time(NULL));
for(int i=0;i<n;i++)
{
a[i]=rand()/1000+100;
}
cout<<"排序前的数组为:"<<endl;
for(int i=0;i<n;i++)
{
cout<<a[i]<<" ";
}
cout<<endl;
SelectSort(a,n);
cout<<"排序后的数组为:"<<endl;
for(int i=0;i<n;i++)
{
cout<<a[i]<<" ";
}
cout<<endl;
return 0;
}
3.插入排序: 该排序算法是通过对未排序的数据逐个插入到合适的位置而完成排序,是一个稳定的排序算法,时间复杂度为O(n*n)。 基本步骤如下: (1)首先对数组的前两个数据进行从小到大的排序 (2)将第三个数据与已经排好序的两个数据进行比较,将第三个数据插入到合适的位置。 (3)将第四个数据插入到已经排好序的前三个数据中。 (4)不断重复上述过程,直到把最后一个数据插入到合适的位置。最后便完成了对原始数据从小到大(从大到小)的排序。
示例代码如下:
#include <iostream>
using namespace std;
#include <stdlib.h>
#include <time.h>
//选择排序算法
void InsertionSort(int a[],int n)
{
int i,j,temp;
for(i=1;i<n;i++)
{
temp=a[i];
j=i-1;
while(j>=0&&a[j]>temp)
{
a[j+1]=a[j];
j--;
}
a[j+1]=temp;
}
}
int main()
{
int n;
cout<<"请输入要生成数组的大小:";
cin>>n;
int *a=new int[n];
srand(time(NULL));
for(int i=0;i<n;i++)
{
a[i]=rand()/1000+100;
}
cout<<"排序前的数组为:"<<endl;
for(int i=0;i<n;i++)
{
cout<<a[i]<<" ";
}
cout<<endl;
InsertionSort(a,n);
cout<<"排序后的数组为:"<<endl;
for(int i=0;i<n;i++)
{
cout<<a[i]<<" ";
}
cout<<endl;
return 0;
}
4.希尔排序: 希尔排序算法严格来说是基于插入排序算法的思想。又称为Shell排序和缩小增量排序。是一种不稳定的排序算法,时间复杂度为O(N3/2) 基本步骤: (1)将有n个元素的数组分成n/2个数字序列,第i个数据和第n/2+i个数据为一对 (2)一次循环使得每个序列对有序 (3)变为n/4个序列,再次排序 (4)不断重复上述过程,随着序列减少至最后变为1个,完成整个排序 示例代码如下:
#include <iostream>
using namespace std;
#include <stdlib.h>
#include <time.h>
void ShellSort(int a[],int n)
{
int i,j,r;
int temp;
for(r=n/2;r>=1;r/=2)
{
for(i=r;i<n;i++)
{
temp=a[i];
j=i-r;
while(j>=0&&temp<a[j])
{
a[j+r]=a[j];
j-=r;
}
a[j+r]=temp;
}
}
}
int main()
{
int n;
cout<<"请输入要生成数组的大小:";
cin>>n;
int *a=new int[n];
srand(time(NULL));
for(int i=0;i<n;i++)
{
a[i]=rand()/1000+100;
}
cout<<"排序前的数组为:"<<endl;
for(int i=0;i<n;i++)
{
cout<<a[i]<<" ";
}
cout<<endl;
ShellSort(a,n);
cout<<"排序后的数组为:"<<endl;
for(int i=0;i<n;i++)
{
cout<<a[i]<<" ";
}
cout<<endl;
return 0;
}
5.快速排序: 快速排序与冒泡排序法类似,都是基于交换排序思想,但是快速排序对冒泡排序法进行了改进,从而使其具备了更高的执行效率。该排序算法是一种不稳定的排序算法,时间复杂度为O(nlogn)。 基本步骤如下: (1)首先设定一个分界值(基准元素),通过该分界值将数组分为左右两个部分。 (2)将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于或等于分界值,右边部分中各元素都大于或等于分界值。 (3)左边跟右边的数据可以进行独立排序,对于左侧的数组数据可以另取一个新的分界值,将该部分数据分为左右两部分,同样在左边放置较小值,右边放置较大值。而右侧数组数据也可以做类似处理。 (4)不断重复以上步骤直至排序完成。 ps:一般来说,基准元素的选取有以下几种方法: ①取第一个元素 ②取最后一个元素 ③取中间元素 ④取第一个,最后一个,中间位置元素之间的中位数 ⑤取第一个和最后一个之间位置的随机数k(low<=k<=high),选取a[k]作为基准元素。 示例代码:
#include <iostream>
using namespace std;
#include <stdlib.h>
#include <time.h>
//交换函数
void swap(int *a,int *b)
{
int temp=*a;
*a=*b;
*b=temp;
}
//划分函数
int Partition(int a[],int low,int high)
{
int i=low,j=high,privot=a[low];
while(i<j)
{
while(i<j&&a[j]>privot)
{
j--;//向左扫描
}
if(i<j)//意味着向左扫描过程出现比基准元素小的元素
{
swap(a[i++],a[j]);//a[i]和a[j]交换之后i右移一位
}
while(i<j&&a[i]<privot)
{
i++;//向右扫描
}
if(i<j)//意味着向右扫描过程中出现了比基准元素大的元素
{
swap(a[i],a[j--]);//a[i]和a[j]交换之后j向左移动一位
}
}
return i;//返回最终划分完成后基准元素所在的位置
}
//快速排序函数
void QuickSort(int a[],int low,int high)
{
int mid;
if(low<high)
{
mid=Partition(a,low,high);//基准位置
QuickSort(a,low,mid-1);
QuickSort(a,mid+1,high);
}
}
int main()
{
int n;
cout<<"请输入要生成数组的大小:";
cin>>n;
int *a=new int[n];
srand(time(NULL));
for(int i=0;i<n;i++)
{
a[i]=rand()/1000+100;
}
cout<<"排序前的数组为:"<<endl;
for(int i=0;i<n;i++)
{
cout<<a[i]<<" ";
}
cout<<endl;
QuickSort(a,0,n-1);
cout<<"排序后的数组为:"<<endl;
for(int i=0;i<n;i++)
{
cout<<a[i]<<" ";
}
cout<<endl;
return 0;
}
6.堆排序算法: 堆排序算法是基于选择排序的思想,利用堆结构和二叉树的一些性质来完成数据的排序。该排序算法不是一种稳定的排序算法,时间复杂度为O(nlogn)。
相关概念: 堆结构:堆结构是一种树结构,准确点来说是一个完全二叉树。在这个树中每一个结点对应原始数据的一个记录,并且每个结点都应该满足以下条件: ①如果按照从小到大的顺序排序,要求非叶子结点的数据要大于或等于其左右子结点的数据。 ②如果按照从大到小的顺序排序,要求非叶子结点的数据要小于或等于其左右子结点的数据。 基本步骤如下:
- 将初始待排序关键字序列(R1,R2….Rn)构建成大顶堆,此堆为初始的无序区;
- 将堆顶元素R[1]与最后一个元素R[n]交换,此时得到新的无序区(R1,R2,……Rn-1)和新的有序区(Rn),且满足R[1,2…n-1]<=R[n];
- 由于交换后新的堆顶R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,……Rn-1)调整为新堆,然后再次将R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2….Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为n-1,则整个排序过程完成。
如何构造一个堆结构? 假定一个非叶子结点Ai,其左子树和右子树均已进行筛运算(即左右子树均已构成堆结构) ①比较Ai的左子树和右子树的最大值,将最大值放在Aj中。 ②将Ai的数据与Aj的数据进行比较,如果Ai大于等于Aj,表示以Ai为根的子树已构成堆结构,可以终止筛运算。 ③如果Ai小于Aj,则Ai与Aj互换位置 ④经过第③步后,可能会破坏以Ai为根的堆,因为此时Ai的值为原来的Aj。下面以Aj为根重复前面的步骤,直到满足堆结构的定义,也就是父结点数据大于子结点。这样,以Aj为根的子树就会被调整为一个堆结构。
示例代码如下:
#include <iostream>
using namespace std;
#include <stdlib.h>
#include <time.h>
//交换函数
void swap(int *a,int *b)
{
int temp=*a;
*a=*b;
*b=temp;
}
//堆调整函数
void AjustHeap(int a[],int father,int n)
{
int j;
while(2*father+1<n)//第father结点有左子树
{
j=2*father+1;//左子结点序号
if(j+1<n)//存在右子结点
{
if(a[j]<a[j+1])//左子树小于右子树,则需要比较右子树
{
j++;//序号加一,指向右子树
}
}
if(a[father]<a[j])
{
swap(a[father],a[j]);
father=j;//堆被破坏,需要重新调整
}
else//比较左右子结点均小于父结点,则堆未被破坏,无需重建
{
break;
}
}
}
//建立大顶堆函数
void BuildMaxHeap(int a[],int n)
{
for(int i=n/2-1;i>=0;i--)//将a[0,n-1]建成大根堆
{
AjustHeap(a,i,n);
}
}
//堆排序函数
void HeapSort(int a[],int n)
{
BuildMaxHeap(a,n);//初始化大顶堆
for(int i=n-1;i>=0;i--)
{
swap(a[0],a[i]);//交换
AjustHeap(a,0,i);//重新调整堆结构
}
}
int main()
{
int n;
cout<<"请输入要生成数组的大小:";
cin>>n;
int *a=new int[n];
srand(time(NULL));
for(int i=0;i<n;i++)
{
a[i]=rand()/1000+100;
}
cout<<"排序前的数组为:"<<endl;
for(int i=0;i<n;i++)
{
cout<<a[i]<<" ";
}
cout<<endl;
HeapSort(a,n);
cout<<"排序后的数组为:"<<endl;
for(int i=0;i<n;i++)
{
cout<<a[i]<<" ";
}
cout<<endl;
return 0;
}
7.合并排序算法: 合并排序就是采取分冶策略,将一个大的问题分成若干个小问题,先解决小问题,再通过小问题的答案解决大问题。将一个无序表分解成两个规模大致相等的子无序表,如果该子序表还不易排序,则可以将得到的子序表进行再次的分解,直到子序表中包含的元素为1.因为单个元素的序列本身是有序的,此时便可以进行合并,从而完成排序。该排序算法不是一个稳定的排序算法,时间复杂度为O(nlogn)。 基本步骤如下: (1)首先,将含有n个元素的待排序数据序列看作有n个长度为1的有序子表 (2)将这些子表进行两两合并,得到长度为2的若干有序子表 (3)然后再将这些子表进行两两合并,得到若干个长度为4的有序子表 (4)重复上述过程,直到最后的表长度为n,从而完成排序过程
示例代码:
#include <iostream>
using namespace std;
#include <time.h>
#include <stdlib.h>
//合并函数
void Merge(int a[],int low,int mid,int high)
{
int *b=new int[high-low+1];//申请一个辅助数组
int i=low,j=mid+1,k=0;
while(i<=mid&&j<=high)//按照从小到大的顺序存放到辅助数组b[]中
{
if(a[i]<=a[j])
{
b[k++]=a[i++];
}
else
{
b[k++]=a[j++];
}
}
//将数组中剩下的元素复制到数组b中去(此时无论是a[low,mid]有剩下元素还是a[mid+1,high]数组有剩下元素,它们都是有序的,直接拷贝进b数组即可)
while(i<=mid)
{
b[k++]=a[i++];
}
while(j<=high)
{
b[k++]=a[j++];
}
for(i=low,k=0;i<=high;i++)
{
a[i]=b[k++];//将b数组中已经排好序的元素拷贝到a数组中去
}
}
//合并排序函数
void MergeSort(int a[],int low,int high)
{
if(low<high)
{
int mid=(low+high)/2;//取中点
MergeSort(a,low,mid);//对a[low,mid]中的元素进行合并排序
MergeSort(a,mid+1,high);//对a[mid+1,high]中的元素进行合并排序
Merge(a,low,mid,high);//合并
}
}
int main()
{
int n;
cout<<"请输入要生成数组的大小:";
cin>>n;
int *a=new int[n];
srand(time(NULL));
for(int i=0;i<n;i++)
{
a[i]=rand()/1000+100;
}
cout<<"排序前的数组为:"<<endl;
for(int i=0;i<n;i++)
{
cout<<a[i]<<" ";
}
cout<<endl;
MergeSort(a,0,n-1);
cout<<"排序后的数组为:"<<endl;
for(int i=0;i<n;i++)
{
cout<<a[i]<<" ";
}
cout<<endl;
return 0;
}