今天计划整理一下常见的几种排序算法冒泡法:
第p次冒泡,会在剩下的n-p个数字钟找到最小的min值,将其放在剩下的n-p个数字的最前面。
我的方法是找剩下中的最小值放到开头,而不是两个相邻值互相交换
代码如下:
template<typename comparable>
void maopaosort(vector<comparable>& a)
{
comparab tmp;
int pos;
for(int i=0;i<a.size()-1;i++)
{
tmp=a[i];
pos=i;
for(int j=i+1;j<a.szie();j++)
{
if(a[j]<tmp)
{
tmp=a[j];
pos=j;
}
}
a[i]=a[pos];
a[pos]=tmp;
}
}
标准的排序算法是,每一轮都会有一个最大值沉到数组的底部。
标准的冒泡算法如下:
void bubble_sort(int a[], int n)
{
int i, j, temp;
for (j = 0; j < n - 1; j++)
for (i = 0; i < n - 1 - j; i++)
{
if(a[i] > a[i + 1])
{
temp = a[i];
a[i] = a[i + 1];
a[i + 1] = temp;
}
}
}
插入排序
算法思想:
第p次时,将位于p的元素插入到前面p个已经排序好的元素序列的适当位置。
代码如下:
template<typename com>
void insertsort(vector<com>& a)
{
int j;
for(int p=1;p<a.size();p++){
com tmp=a[p];
for(j=p;j>0&&tmp<a[j-1];j--)
a[j]=a[j-1];
a[j]=tmp;
}
}
这里容易出错:for语句结束后j会自动减去1,将tmp赋值给a[j]即可,j是位于for(j)循环之外定义的。
谢尔排序(shell sort)
缩减增量排序
谢尔排序的性能在实践中是完全可以接受的。编程的简单特点是的她称为对适度的大量输入数据经常选用的算法。o(n的3/2次幂)的边界适用于广泛的增量序列。
shell排序推荐的增量序列为h=【n/2】。每次排序都是一次插入排序。
代码如下:
template<typename com>
void shellSort(vector<com>& a)
{
for(int gap=a.size()/2;gap>0;gap=/2)
{
for(int i=gap;i<a.size();i++)
{
int j=gap;
com temp=a[gap];
for(;j>0&&temp<a[j-gap];j=-gap)
a[j]=a[j-gap]
a[j]=temp;
}
}
}
可以看到第二层的for循环就是插入排序。
归并排序(mergesort)
时间复杂度:O(nlogn)
空间复杂度:虽然归并排序运行时间为O(nlogn),但是很难用于主存排序,主要问题在于合并两个排序的表时,需要线性附加内存,在整个算法排序过程中还要花费将数据复制到临时数组,再复制回来泽阳一些附加操作,结果就是严重减慢了算法速度
算法思想:基本操作是合并两个已经排序的表。
递归编写的归并排序如下:
template<typename comparable>
void mergesort(vector<comparable>& a)
{
vector<comparable> temp(a.size());
mergesort(a,temp,0,a.size()-1);
}
template<typename comparable>
void mergesort(vector<comparable>& a,vector<comparable>& tempArry,int left,int right)
{
if(left<right)
{
int center=(left+right)/2;
mergesort(a,tempArry,left,center-1);
mergesort(a,tempArry,center,right);
merge(a,tempArry,left,cneter,right);
}
}
template<comparable>
void merge(vector<comparable>& a,vector<comparable>& tempArry,int leftPos,int rightPos,int rightEnd)
{
int leftEnd=rightPos-1;
int tempPos=leftPos;
int numElements=rightPos-leftPos+1;
while(leftPos<=leftEnd&&rightPos<=rightEnd)
if(a[leftPos]<a[rightPos])
tempArry[tempPos++]=a[leftPos++];
else
tempArry[tempPos++]=a[rightPos++];
while(leftPos<=leftEnd)
tempArry[tempPos++]=a[leftPos++];
while(rightPOs<rightEnd)
tempArry[tempPos++]=a[rightPos++];
for(int i=0;i<numElements;i++,rightEnd--)
a[rightEnd]=tempArry[rightEnd];
}
归并排序非递归代码如下:
思想:分为两个步骤,两两合并,onePass:在数组上将亮亮合并应用一下,步长每次翻倍。
void mergeTwo(vector<int>& a,vector<int>& b,int leftPos,int len,int rightEnd)
{
int rightPos=leftPos+len;
int leftEnd=leftPos+len-1;
int bPos=leftPos;
int numElements=rightEnd-leftPos+1;
while(rightPos<=rightEnd&&leftPos<=leftEnd)
{
if(a[leftPos]<a[rightPos])
b[bPos++]=a[leftPos++];
else
b[bPos++]=a[rightPos++]
}
while(rightPos<=rightEnd)
b[bPos++]=a[rightPos++];
while(leftPos<=leftEnd)
b[bPos++]=a[leftPos++];
for(int i=0;i<numElements;i++)
a[rightEnd--]=b[rightEnd--];
}
void mergeOnePass(vector<int>& a,vector<int>& b)
{
for(int len=1;len<=a.size()/2;len*=2)
{
int i=0;
for(;i+2*len<=a.size();i=i+len*2)
mergeTwo(a,b,i,len,i+2*len);
if(i+len<a.size())
mergeTwo(a,b,i,len,.size()-1);
else
;
}
}
快速排序(quicksort)
思想:
1、找到枢纽元素(下面代码中的枢纽元素选取为i,开头、中间、结尾三个中的中值)
2、将大于和小于枢纽值的元素分成两拨
3、递归对分成的两拨元素进行快排;
无额外空间消耗,时间复杂度为O(nlogn)
代码如下:
const int median(vector<int>& a,int left,int right)
{
int mid=(left+right)/2;
if(a[mid]<a[left])
swap(a[left],a[right]);
if(a[right]<a[left])
swap(a[left],a[right]);
if(a[mid]<a[right])
swap(a[mid],a[right]);
swap(a[right-1],a[mid]);
return a[right-1];
}
void quicksort(vector<int>& a,int left,int right)
{
if(left+10<=right)
{
int midval=midan(a,left,right);
int leftPos=left,rightPos=right-1;
for(;;)
{
while(a[++leftPos]<a[right-1]){}
while(a[right-1]<a[--rightPos]){}
if(leftPos<rightPos)
swap(a[leftPos],a[rightPos]);
else
break;
}
swap(a[right-1],a[leftPos]);
quicksort(a,left,leftPos-1);
quicksort(a,leftPos+1;right);
}
else
; //shell sort
}
算法在取中值时多了一些额外步骤节省了一次对比,再有就是for(;;)中,while(a[++leftPos])会自动跳过等于中值的情况而不会无限循环。
堆排序(heapsort)
使用了堆的性质:
最大堆:根节点比任何一个儿子节点的值都大。堆的下滤操作可以处理。