排序
排序笔记
内部排序(数据对象只在内存里进行排序)
外部排序(数对象过多不能同时放入内存,需要借助外存进行排序)
此处只总结内部排序:
冒泡排序
在数组中每次选择两个元素,按照需求进行交换,循环n次(n为总元素个数),每趟循环把最大的泡泡的冒到最下方。
void bubbleSort(int arr[],int n){
int i,j,temp;
for(int i=1;i<n-1;i++){
for(int j=0;j<n-i;j++){
if(arr[j]>arr[j+1]){
temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
}
}//冒泡排序
带入样例中的测试代码如下
#include<cstdio>
void bubbleSort(int arr[],int n){
int i,j,temp;
for(int i=1;i<n-1;i++){
for(int j=0;j<n-i;j++){
if(arr[j]>arr[j+1]){
temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
}
}//冒泡排序
int main(){
int arr[]={1,13,63,5,78,9,12,52,8};
int n=sizeof(arr)/sizeof(int);
bubbleSort(arr,n);
for(int i=0;i<n;i++){
printf("%d ",arr[i]);
}
return 0;
}
选择排序:
简单选择排序:
对一个序列A中的元素A[1]~A[n],令i从1到n枚举,进行n趟操作,每趟从待排序部分[i,n]中选择最小的元素,令其与待排序部分的第一个元素A[i]进行交换,这样元素A[i]就会与当前有序区间[1,i]。n趟操作后,所有元素就会是有序的。
算法逻辑明显:共进行n趟操作,每趟操作选出待排序部分[i,n]中最小的元素,令其与A[i]交换。因此复杂度为O(n2)
void selectSort(){
for(int i=1;i<=n;i++){//进行n趟操作
int k=i;
for(int j=i;i<=n;j++){//找出[i,n]中最小的元素,下标为k
if(A[j]<A[k]){
k=j;
}
}
int temp=A[i];//交换 A[k]与A[i]
A[i]=A[k];
A[k]=temp;
}
}
插入排序
直接插入排序
对于序列A的n个元素A[1]~A[n],令i从2到n枚举,进行n-1趟操作。
假设某一趟时,序列A的前i-1个元素A[1]到A[i-1]已经有序,而范围[i,n]还未有序,那么该趟从范围[1,i-1]中找某个位置j,使得将A[i]插入位置j后。(此时A[j]到A[i-1]会后移一位至A[j+1]到A[i])范围[1,i]有序。
int A[maxn],n; //n为元素个数,数组下标为1到n
void insertSort(){
for(int i=2;i<=n;i++){ //进行n-1趟排序
int temp=A[i],j=i; //temp临时存放A[i],j从i开始往前枚举
while(j>1&&temp<A[j-1]){//只要temp小于前一个元素A[j-1]
A[j]=A[j-1];//把A[j-1]后移一位至A[j]
j--;
}
A[j]=temp;//插入位置为j
}
}
void insertSort(int arr[],int length) {
int i,j;
for(i=1;i<length;i++){
int tmp=arr[i];
for(j=i;j>0&&arr[j-1]>tmp;j--){
arr[j]=arr[j-1];
}
arr[j]=tmp;
}
}
希尔排序
希尔排序是插入排序的一种;又称为“缩小增量排序”,是直接插入排序算法的更新高效的版本。希尔排序是非稳定排序算法。
希尔排序把记录按照下标的一定增量分组,对每组使用直接插入排序算法排序;随着增量逐渐减少(增量即为分成了一组),每组包含的关键词越来越多,当增量减至1时,整个文件最后插入排序微调后,算法终止。
选择增量gap=length/2,缩小增量继续以gap=gap/2的方式;所有增量的集合称为增量序列。希尔排序的增量序列的选择和证明是数学难题,这里选择的增量序列是常用的,也是建议使用的,称为希尔增量。但其实这个增量序列不是最优的。
int shellSort(int arr[],int n){
for(int gap=n/2;gap>0;gap/=2){
for(int i=gap;i<n;i+=1){
int temp=arr[i];
int j;
for(j=i;j>=gap&&arr[j-gap]>temp;j-=gap){
arr[j]=arr[j-gap];
}
arr[j]=temp;
}
}
return 0;
}
归并排序
采用归并的思想实现的排序方法,采用分治策略(分治法将问题分成一些小的问题然后递归求解,而治的阶段则分的阶段得到的各答案“修补”在一起,即分而治之)。
将一个大的无序数组有序,我们可以把大的数组分成两个,然后对这两个数组分别进行排序,之后把这两个数组合并成一个有序的数组。由于两个小的数组都是有序的,所以在合并的时候是很快的。
通过递归的方式将大的数组一直分割,直到数组的大小为1,此时只有一个元素,那么该数组有序,之后再使用递归将两个数组大小为1的合并为2个,再把两个大小为2的合并为4个。。。直到全部小的数组合并起来。
//merge函数实现将已经 排好序的两个小段组合成一个有序的大段
void merge(int arr[],int l,int m,int r){
int i,j,k;
int n1=m-l+1;
int n2=r-m;
int L[n1],R[n2];
for(i=0;i<n1;i++){
L[i]=arr[l+i];
}
for(j=0;j<n2;j++){
R[j]=arr[m+1+j];
}
i=0;
j=0;
k=1;
while(i<n1&&j<n2){
if(L[i]<=R[j]){
arr[k]=L[i];
i++;
}else{
arr[k]=R[j];
j++;
}
k++;
}
while(i<n1){
arr[k]=L[i];
i++;
k++;
}
while(j<n2){
arr[k]=R[j];
j++;
k++;
}
}
void mergeSort(int arr[],int l,int r){
if(l<r){
int m=l+(r-l)/2;
mergesort(arr,l,m);
mergesort(arr,m+1,r);
/*递归把数组不断分割成小的数组,分割为单个元素的数组
递归的终止就是达到递归终止条件时(将数组分割成单个元素
的数组时),开始执行merge()函数,将两个单个元素组合为
两个排好序的数组,接着两个排为四个,以此类推。 */
merge(arr,l,m,r);
}
}
堆排序
利用堆这种数据结果而设计的排序算法,堆排序是一种选择排序,它的最坏,最好的平均时间复杂度均为O(nlogn),他是稳定排序。
拓展:堆:
堆是具有以下性质的完全二叉树。每个节点的值都大于或等于其左右孩子结点的值称为大顶堆;每个节点的值都小于或等于其左右孩子结点的值称为小顶堆,
基本思想:
a、将无序序列构造为一个堆(一般升序采用大顶堆,降序采用小顶堆)
b、将堆顶元素与末尾元素交换,将最大的元素沉到数组末端;
c、重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前元素末尾,反复执行调整+交换步骤,直到整个序列有序。
//建立堆函数
void heapify(int arr[],int n,int i){
int largest=i;
int l=2*i+1;
int r=2*i;
if(l<n&&arr[l]>arr[largest]){
largest=l;
}//如果left比root大的话
if(r<n&&arr[r]>arr[largest]){
largest=r;
}//如果right比root大的话
if(largest!=i){
swap(arr[i],arr[largest]);
heapify(arr,n,largest);//递归地定义子堆
}
}
//堆排序函数
void heapSort(int arr[],int n){
for(int i=n/2-1;i>=0;i--){
heapify(arr,n,i);
}
for(int i=n-1;i>=0;i--){
swap(arr[0],arr[i]);
heapify(arr,i,0);
}
}
桶排序
原理是将数组分到有限数量的桶中,在对每个桶子分别排序(可能使用别的排序算法或以递归的方式继续使用桶排序进行排序),最后将各个桶子中的数据有序的合并起来。
void bucketSort(float arr[],int n){
vector<float> b[n];//创建一个空桶
for(int i=0;i<n;i++){
int bi=n*arr[i];
b[bi].push_back(arr[i]);
} //将数组元素放入不同的桶中
for(int i=0;i<n;i++){
sort(b[i].begin(),b[i].end());
}//排序
int index=0;
for(int i=0;i<n;i++){
for(int j=0;j<b[i].size(),j++){
arr[index++]=b[i][j];
}
}//把所有桶中数据放入数组中
}
基数排序
它是一种非比较型整数排序算法,是桶排序的拓展。其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。排序过程是将所有待比较数值统一为同样的数位长度,数位短的数字前面补零,然后从最低位开始,依次进行依次排序。从最低位排序一直到最高位排序完成之后,数列就变成一个有序序列。
//获取数组的最大值
int getmax(int arr[],int n){
int max=arr[0];
for(int i=1;i<n;i++){
if(arr[i]>max){
max=arr[i];
}
}
return max;
}
//对数组按照"某个位数"进行排序 (桶排序)
void countsort(int arr[],int n,int exp){
int output[n];//存储“被排序数据”的临时数组
int i,count[10]={0};//count:数据出现的次数
for(i=0;i<n;i++){
count[(arr[i]/exp%)10]++;
} //将数据出现的次数存储在count[]中
for(i=1;i<10;i++){
count[i]+=count[i-1];
} //更改count[i] ,目的:让更改后的count[i]的值,是该数据在output[]中的位置
for(i=n-1;i>=0;i--){
output[count[(arr[i]/exp)%10]-1]=arr[i];
count[(arr[i]/exp)%10]--;
}//将数据存储 到临时数组output[]中
for(i=0;i<n;i++){
arr[i]=output[i];
} //将排序好的数据赋值给arr[]
}
void radixSort(int arr[],int n){
int exp;
int max=getmax(arr,n);
for(exp=1;max/exp>0;exp*=10){
countsort(arr,n,exp);
}//从个位开始,对数组按指数进行排序
}
快速排序
快排也是分治的算法,每次选择一个元素并且将整个数组以那个元素分为两个部分,根据实现的算法不同,元素的选择一般有如下几种:
永远选第一个元素;
永远选最后一个元素;
随机选取元素;
取中间值;
快速排序的核心是分区,分区的目的是传入一个数组和选定的一个元素,把所有小于那个元素的其他元素放在左边,大于的放在右边。
1、先从队尾开始扫描且当low<high
若a[high]>temp,high–
若a[high]<temp,arr[low]=a[high],同时从队首开始扫描。
2、从队首开始扫描
若a[low]<temp,low++
若a[low]>temp,a[high]=arr[low],同时从队尾开始扫描。
#include<stdio.h>
void quickSort(int arr[],int low,int high){
if(low>=high)
return;
int first=low;
int last=high;
int temp=arr[first];
while(first<last){
while(first<last&&arr[last]>=temp){
last--;
}
arr[first]=arr[last];
while(first<last&&arr[first]<=temp){
first++;
}
arr[last]=arr[first];
}
arr[first]=temp;
quickSort(arr,low,first-1);
quickSort(arr,first+1,high);
}
int main(){
int arr[]={49,38,65,97,23,22,76,1,5,8};
for(int i=0;i<10;i++){
printf("%d ",arr[i]);
}
printf("\n");
quickSort(arr,0,9);
for(int i=0;i<10;i++){
printf("%d ",arr[i]);
}
return 0;
}