1 冒泡排序
--比较相邻两个数据,若前面的数据大于后面的数据,则对其进行交换
--这样对整个数组从头到尾操作一遍,最大的数据便沉到了数组末尾
--接下来将数组长度减一,再进行比较,这样第二大数据沉到了数组倒数第二个位置上,依次类推,便可达到排序
可看出,冒泡排序复杂度为O(n^2),由于两相邻数据相等时不必进行交换,所以冒泡排序是稳定的!
改进方案: 可以设一个标志位,若这一趟发生了数据交换,则置标志位位true,若没有发生,则值false,很明显,若有一趟没有发生数据交换,即标志位为false,则说明数组已排好序,即可终止排序!
2 插入排序 :将待排序的数字插入已排序的数组
--初始时,数组第一个数据自成一个有序区,无序区位a[1--n].令i=1
--将a[i]插入到有序数组a[0---i-1]中形成a[0--i]的有序区
--++i并重复第2步,直到数组末尾,排序完成
void insert_sort(int nums[],int len)
{
if(nums==NULL || len<=0) return;
//i指向已排序数组的末尾 j指向待排序的数据
int i=0,j=1;
while(j<len)
{
int key=nums[j];
int k=i;
//寻找合适的位置将数据插入到排序数组中
while(k>=0 && nums[k]>key)
{
nums[k+1]=nums[k];
--k;
}
if(k==-1)
nums[0]=key;
else
nums[k+1]=key;
++j;
++i;
}
}
将数据插入到已排序数组需O(n),所以插入排序的复杂度为O(n^2)! 插入排序对相等元素的插入规则也是一样,不回改变其顺序,因此
插入排序也是稳定的排序!
3 选择排序
-- 在整个数组中找到最小的元素 与第一个元素位置交换
--从第二个元素起,在其后找最小的元素,并与第二个元素交换
--依次类推,可完成排序
void select_sort(int nums[],int len)
{
if(nums==NULL || len<=0) return;
//min指向最下元素值,key指向最小元素下标
int min,key;
for(int i=0;i<len;++i)
{
key=i;
min=nums[i];
//在(i-n)的范围内寻找最小元素,与i作交换
for(int j=i+1;j<len;++j)
{
if(nums[j]<min)
{
min=nums[j];
key=j;
}
}
if(i!=key)
{
int t=nums[i];
nums[i]=nums[key];
nums[key]=t;
}
}
}
可看出 选择排序复杂度为O(n^2) 也是稳定排序!
4 归并排序
--将数组拆分成两个字数组,问题便成了对两个字数组排序,再将两个已排序的字数组合并即可
--对两个子数组排序,是一个递归的过程,当递归到只有一个数据时,即可认为排好序!
--所以问题关键在合并两个有序子数组的问题上!
//合并两个有序子数组
void merge_sumArray(int nums[],int first,int mid,int last)
{
int len=last-first+1;
//用copy保存合并的数据
int *copy = new int[len];
int i=first,j=mid+1,k=0;
while(i<=mid && j<=last)
{
//取两个子数组中数据的较小者
if(nums[i]<nums[j])
copy[k++]=nums[i++];
else
copy[k++]=nums[j++];
}
//处理子数组中未处理完的数据
while(i<=mid)
copy[k++]=nums[i++];
while(j<=last)
copy[k++]=nums[j++];
//将copy中的数据复制到原数组对应位置
for(int p=0;p<len;++p)
nums[first++]=copy[p];
delete []copy;
}
void merge_sort(int nums[],int first,int last)
{
//last对应数组尾端数据,first对应数组首端数据
if(first<last)
{
int mid=(first+last)/2;
//对两个子数组排序
merge_sort(nums,first,mid);
merge_sort(nums,mid+1,last);
//合并两个有序子数组
merge_sumArray(nums,first,mid,last);
}
}
void merge_sort(int nums[],int len)
{
if(nums==NULL || len<=0) return;
merge_sort(nums,0,len-1);
}
合并两个有序子数组耗时o(n),由于递归是折半的,所以需递归O(lgn)次, 所以归并排序总的复杂度位O(nlgn)! 是稳定排序
5 快速排序
--首先对整个数组。取一个基准值,将数组分为两半,小于基准值的在左边,大于基准值的在右边
--取得基准值的下标,对其两边的数据分别进行第一步的操作
--递归第二步的操作,即可完成排序!
//挖坑填数操作,将数组以一个基准数位基准分为两半
int partition(int nums[],int first,int last)
{
//取数组首端数据作为基准数
int key=nums[first];
int i=first,j=last;
while(i<j)
{
//从数组尾端寻找第一个小于基准数的数据
while(nums[j]>key && i<j)
--j;
if(i<j) //用该数据填充之前的坑 并留出一个新坑
nums[i++]=nums[j];
//从数组首端寻找第一个大于基准数的数据
while(nums[i]<key && i<j)
++i;
if(i<j) //用该数据填充之前的坑,并留出一个新坑
nums[j--]=nums[i];
}
nums[i]=key;
return i;
}
void quick_sort(int nums[],int first,int last)
{
//数组只剩下一个元素时,停止递归
if(first==last)
return;
//对整个数组作partition操作,分为两半
int index=partition(nums,first,last);
//对左边数据递归
if(index>first)
quick_sort(nums,first,index-1);
//对右边数据递归
if(index<last)
quick_sort(nums,index+1,last);
}
void quick_sort(int nums[],int len)
{
if(nums==NULL || len<=0) return;
quick_sort(nums,0,len-1);
}
整个挖坑填数耗时O(N),递归lgn次,总的复杂度为O(lgn) 在寻找大于或小于基准数的数据的时候,我们有可能将两个相邻且相等数据其中一个移动到另一边,这样便破坏的稳定性,因此快速排序是不稳定的!
快速排序的改进方案:随机选择基准数,区间内数据较少时可选用其他的排序方法,以减少递归深度!