摘自《程序员面试攻略.第三版》
选择排序算法,应该将内存使用、稳定性、最好情况、平均情况和最差情况的性能都作为评判标准。
没有一个排序算法在所有情况下都是最优的,你知道都有哪些排序算法是可用的,你应用这些知识来选择一个合适的排序算法并明智的讨论不同的选择运行时和内存之间的取舍。
关于数据,我们应该知道,数据是否已经排序或基本有序?数据集合可能会有多大?是否可能会有重复的关键值?
对于排序有什么要求?你是否想要优化最好情况、最差情况或平均情况的性能?是否需要稳定排序算法?
关于系统,我们知道什么?最大的待排序数据集合是小于、等于还是大于可用内存?
通常对大多数数据集合使用归并排序(如果稳定性很重要)或者快速排序,通常对于非常小的数据集合转而使用插入排序(通常n小于10)。
稳定性,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,ri=rj,且ri在rj之前,而在排序后的序列中,ri仍在rj之前,则称这种排序算法是稳定的;否则称为不稳定的。堆排序、快速排序、希尔排序、直接选择排序不是稳定的排序算法,而基数排序、冒泡排序、直接插入排序、折半插入排序、归并排序是稳定的排序算法。
1、冒泡排序,是先找n个数中最大值或最小值,然后再循环找n-1中。
2、简单选择排序,与冒泡法类似,不同的是,它没有立即进行数据交换,而是数组的下标交换。减少了数据交换次数,最后判断是否交换,才交换最终找到的最大值或最小值。
简单选择排序的递归实现,原理就是递归的找出left到right数组中的最小值或最大值。
void diguiPaixu(int *a,int left,int right)
{
int i,min,key;
if(left==right)
{
return;
}
min=left;
for(i=left+1;i<right;i++)
{
if(a[left]>a[i])
{
min=i;
}
}
if(left!=min)
{
key=a[left];
a[left]=a[min];
a[min]=key;
}
diguiPaixu(a,left+1,right);
}
3、插入排序,是假设右边数据已经排好了,然后再对左边的数据依次插入到右边排好的数据中去。
4、快速排序,是根据关键数据,根据关键数据将数据分成两部分,即将关键数据放到中间位置,使它左边为大于它的数,右边是小于它的数。完成第一次数据的交换,然后递归关键字左边数据和右边数据,用同样的方法,直到前后左右的哨兵相等就退出递归。
voidquickSort(int *a,int left,int right)
{
int i=left;
int j=right;
int key=a[left];
int temp;
if(left>=right) /*如果左边索引大于或者等于右边的索引就代表已经整理完成一个组了*/
{
return;
}
while(i!=j) //如果j=i了,就不需要再交换了
{
while(i<j&&key<=a[j])//找到左边第一个小于关键数的数
{
j--;
}
while(i<j&&key>=a[i])//找到右边第一个大于关键数的数
{
i++;
}
//交换两个数在数组中的位置,即在左右找到的大于或小于关键数的数,将它们交换放在关键数的左边小数边和右边大数边。
if(i<j)
{
temp=a[i];
a[i]=a[j];
a[j]=temp;
}
}
//最终将基准数归位,上述的交换结束后,就说明找到了关键数在这组数中间的位置。
a[left]=a[i];
a[i]=key;
quickSort(a,left,i-1);
quickSort(a,i+1,right);
}
5、希尔排序
直接插入排序的改进版本,相当于是不断减小增量的交换数据的方式。
6、归并排序,指的是将两个顺序序列合并成一个顺序序列的方法。
步骤一、申请空间,为其两个已经排序的序列之和,该空间用来存放合并后的序列
步骤二、设定两个指针,最初位置分别为两个已经排序序列的起始位置
步骤三、比较两个指针所指向的元素,选择相对较小的元素放入到合并空间,并移动指针到下一位置
重复步骤三,直到某一指针超出序列尾。
将另一序列剩下的所有元素直接复制到合并序列尾。
例子:翻煎饼的问题,从下面大的开始排起,每次都将最大的先翻到最上面,再翻到最下面即可。最坏花费2n的时间。