背景:最近在找实习,各家公司面试时关于排序方面被问到最多的就是手写快速排序,其次就是手写堆排序了。关于快速排序,大家可能都比较熟悉,可是堆排序就练习的有点少了其实,我也不例外,也因此丢掉很多很好的实习机会。(PS:关于查找那块,问的最多的就是手写二分查找了;树的那块,问的多的就是手写求树的深度、非递归前中后层序遍历二叉树;建议诸位道友,面试前狠很的手写练练这些算法。。。不然后果,你懂得。。捂脸。。)
1、快速排序
特点:是对冒泡排序的改进,属于不稳定的方法。其平均性能是迄今为止所有内排序算法中最好的一种,得到广泛应用,例如UNIX系统的库函数qsort()函数。
时间复杂度: 最好情况O( n*log(n) ) ,最坏情况O( n^2 ),平均情况O( n*log(n) )
空间复杂度:O( n*log(n) )
代码如下:
int Partition(int a[], int first,int end) //快速排序的一次划分,可用于寻找数组中的第K大数字
{
int i=first;//初始化
int j=end;
int tmp;
while(i<j) //默认比较轴值为第一个元素
{
while(i<j&&a[i]<a[j]) //右侧扫描
j--;
if(i<j) //将较小的记录移到前面
{
tmp=a[i];
a[i]=a[j];
a[j]=tmp;
i++;
}
while(i<j&&a[i]<a[j]) //左侧扫描
i++;
if(i<j) //将较大的记录移到前面
{
tmp=a[i];
a[i]=a[j];
a[j]=tmp;
j--;
}
}
return i; //i为轴值记录的最终位置
}
void QuickSort(int a[],int first,int end)
{
int pivot;
if(first<end) //区间长度大于1,执行一次划分,否则递归结束
{
pivot=Partition(a,first,end); //一次划分
QuickSort(a,first,pivot-1); //递归对左侧子序列进行快速排序
QuickSort(a,pivot+1,end); //递归对右侧子序列进行快速排序
}
}
2、堆排序
特点:它的思想是利用的堆这种数据结构,堆可以看成一个完全二叉树,所以在排序中比较的次数可以做到很少。不稳定排序,原地排序。
时间复杂度: 最好情况O( n*log(n) ) ,最坏情况O( n*log(n) ),平均情况O( n*log(n) )
空间复杂度:O( 1 )
代码如下:
void HeadAdjust(int a[],int k,int m)//建立最大堆
{
int i=k; //i指向被筛选节点
int j=2*i+1; //j指向其左孩子,因为数组下标从0开始,故+1
int tmp;
while(j<=m) //筛选还没进行到的叶子节点
{
if(j<m&&a[j]<a[j+1]) //比较i的左右孩子,j指向较大者
j++;
if(a[i]>a[j]) //根节点已经大于左右孩子的最大者
break;
else
{
tmp=a[i]; //否则交换
a[i]=a[j];
a[j]=tmp;
i=j; //对变换后的较大孩子进行调整
j=2*i+1;
}
}
}
void HeapSort(int a[], int n)//堆排序中的调整堆
{
int tmp;
for(int i=n/2-1;i>=0;i--) //初始化堆,从最后一个非叶子节点开始,数组下标从0开始,故-1
HeadAdjust(a,i,n-1);
for(int j=n-1;j>0;j--) //重复执行移走堆顶,然后重建堆
{
tmp=a[0];
a[0]=a[j];
a[j]=tmp;
HeadAdjust(a,0,j-1);
}
}
对以上快速排序和堆排序的测试代码:
int main()
{
int a[8]={36,30,18,40,32,45,22,50};
for(int j=0;j<8;j++)
cout<<a[j]<<" ";
cout<<endl;
HeapSort(a,8);//两者任意测试一个
QuickSort(a,0,7);//两者任意测试一个
for(int i=0;i<8;i++)
cout<<a[i]<<" ";
cout<<endl;
return 0;
}