进阶排序算法

一、希尔排序:

(可以看做插入排序的升级,属于插入排序类)

基本思想:

将待排序列划分为若干组,在每一组内进行插入排序,以使整个序列基本有序,然后再对整个序列进行插入排序。

基本有序的概念:就是小的关键字基本在前面,大的基本在后面,不大不小的基本在中间。

基本有序举例:{ 2,1,3, 6,4,7, 5,8,9 }

划分为若干组的目的:减少待排序记录的个数,并使整个序列向基本有序发展。

如何划分:一般采用跳跃分割策略:将相距某个“增量”的记录组成一个子序列,这样才能保证在子序列内分别进行直接插入排序后得到的结果是基本有序而不是局部有序。

注:对于增量序列的增量值的选取值,迭代的最后一个增量值必须等于 1,因为需要对基本有序的序列进行插入排序。

还有由于跳跃式的移动,希尔排序并不是一种稳定的排序算法。

实现代码:

void shell_sort(int p[], int len)
{
 int i = 0, j = 0;
 int gap = len;

 do
 {
  gap = gap/3 + 1;

  for(i=gap; i<len; i++)
  {
   int tmp = p[i];

   /*可对比插入排序,插入排序即是 gap = 1的希尔排序*/
   for(j=i-gap; (j>=0) && (p[j] > tmp); j -= gap)
   {
    p[j+gap] = p[j];
   }

   p[j+gap] = tmp;
  }
 }while(gap > 1);

二、快速排序

(可以看做冒泡排序的升级,属于交换排序类)

基本思想:

1》任取待排序序列中的某个数据元素(例如:第一个元素)作为基准,按照该元素的关键字代销将整个序列划分为左右两个子序列:

?左侧子序列中所有元素都小于或等于基准元素

?右侧子序列中所有元素都大于基准元素

?基准元素排在这两个子序列中间

2》分别对这两个子序列重复施行上述方法,直到所有的对象都排在相应位置上为止。

从上面的基本思想可以得出,快速排序也利用的递归分治的思想:首先对无序的记录序列进行“一次划分”,之后分别对分割所得两个子序列”递归“进行快速排序。

还有由于跳跃式的移动,快速排序并不是一种稳定的排序算法。

学习博客:http://blog.csdn.net/morewindows/article/details/6684558#reply

实现代码:

/*采用交换的方式,返回枢轴位置*/
int partition(int p[], int low, int high)
{
/直接采用第一元素做枢轴,有待优化。详见下文*/
 int pv = p[low];

 while(low < high)
 {
  while(low < high && p[high] >= pv)
  {
   high--;
  }
  swap(p, low, high);

  while(low < high && p[low] <= pv)
  {
   low++;
  }
  swap(p, low, high);
 }

 return low;
}

void q_sort(int p[], int low, int high)
{
 int pv = 0;

 if(low < high)
 {
  /*将传进来的序列一分为二,选出枢轴*/
  pv = partition(p, low, high);

  /*对低值序列进行递归排序*/
  q_sort(p, low, pv-1);

  /*对高值序列进行递归排序*/
  q_sort(p, pv+1, high);
 }

}

void quick_sort(int p[], int len)
{
 q_sort(p, 0, len-1);
}

快速排序的优化:

优化1:关于快速排序程序中枢轴的选取(int
pv = p[low];),若此值恰在整个序列的中间,则我们可以将整个序列分成小数集合和大数集合。但是如果此值偏大,或者偏小,将影响算法的性能。因此有必要使用一定的方法对枢轴进行选取

a> 随机获取low与high之间的数 rnd,但是这种选取方法却有些运气成分。。

b>三数取中法:取三个关键字先进行排序,将中间数作为枢轴,一般是取左端、右端、中间三个数。

对于非常大的待排序的序列来说为了选取合适的枢轴,还可以采用九数取中法:先从数组中分三次取样,每次取三            个数,三个样品各取出中数,然后在这三个中数中再取出一个中数作为枢轴。

优化2:可以优化不必要的交换(以取第一个元素做枢轴值)

采用”挖数填坑“的方式,从右边(high)遍历第一个比枢轴值小的元素,填到选取 枢轴位置 的”坑“里(此也为”挖数过程",为下一元素挖的”坑“)。然后从左边( low )遍历第一个比枢轴大的元素,填到刚才的坑里。如此往复。直到( low = = high).将备份的枢轴值填进去。省去了不必要的交换过程.

优化3:优化小序列的排序方案

如果数组非常小,使用直接插入排序算法的性能反而更好,也就是说快速排序更适合非常大的序列排序。这样我们最好在程序里设置合适的阈值以选择合适的排序算法。譬如下述算法:

 if( (high - low) > max_length_insert_sort)
 {
  /*将传进来的序列一分为二,选出枢轴*/
  pv = partition(p, low, high);

  /*对低值序列进行递归排序*/
  q_sort(p, low, pv-1);

  /*对高值序列进行递归排序*/
  q_sort(p, pv+1, high);
 }
else
    insert_sort(p, len)

三、归并排序

(属于归并排序类)

基本思想:

将两个或两个以上的有序序列合并成一个新的有序序列:

有序序列V[1]...V[M] 和 V[m+1]...V[n] → V[1]...V[n]

上述的归并方法称为2路归并。

由此还可以延伸出3路归并、多路归并。。

归并排序是一种稳定的排序算法。

归并步骤:

即:将两个有序序列A和B 归并到 C后 新的有序序列过程

? i 和 j都在两个序列内变化,根据 i 和 j下标指向关键字大小将较小的数据元素排放到新的序列C中,放到 k 所指的位置

? 当 i 和 j中有一个已经超出序列时,将另一个序列中剩余部分照搬到新序列中。

实现代码:

void merge(int src[], int des[], int low, int mid, int high)
{
 int i = low;
 int j = mid + 1;
 int k = low;

 while( (i <= mid) && (j <= high) )
 {
  if(src[i] <= src[j])
  {
   des[k++] = src[i++];
  }
  else
  {
   des[k++] = src[j++];
  }
 }
 /*若 i 指向的序列还有剩余,则把剩余的搬过去*/
 while(i <= mid)
 {
  des[k++] = src[i++];
 }
 /*若 j 指向的序列还有剩余,则把剩余的搬过去*/
 while(j <= high)
 {
  des[k++] = src[j++];
 }
}

划分步骤:

将初始无序序列划分为有序队列

显然归并的为有序序列,但是提供的一般为无序的序列,那么怎么将无序的序列转化为有序的序列呢?

我们知道若在初始队列中含有 n 个记录,则可以看成是 n 个有序的子序列,每个子序列的长度为1.

则我们可以依照上述情景对初始队列进行划分。。

实现代码:

void m_sort(int src[], int des[], int low, int high, int max)
{
 /*划分到只有一个数据元素的情况
  递归划分、归并的终止条件*/
 if(low == high)
 {
  des[low] = src[low];
 }
 else
 {
  int mid = (low + high) / 2;

  /*辅助空间,每次递归都申请空间的话,消耗有点多*/
  int* space = (int*)malloc(sizeof(int) * max);

  if(space != NULL)
  {
   /*容易想到的在第一次划分后,一直是先对划分的左表进行划分、归并、排序*/
   m_sort(src, space, low, mid, max);

    /*之后对第一次划分后的右表进行划分、归并*/
   m_sort(src, space, mid+1, high, max);

   merge(space, des, low, mid, high);
  }

  free(space);
 }

}

归并排序函数:

void merge_sort(int p[], int len)
{
 /*将p数组中的无序序列 归并排序 到p数组中,
 中间过程需要使用辅助空间*/
 m_sort(p, p, 0, len-1, len);
}

注意思考每种排序使用的场合,归并排序是以上三种排序算法中唯一一个可以直接作为外排序的排序算法。

对各个算法的各个指标的对比:

时间: 2024-10-10 22:02:25

进阶排序算法的相关文章

排序算法进阶--排序算法优化

排序算法进阶 上篇文章中我们主要介绍了经典的八大排序算法,从算法思想,动图演示,代码实现,复杂度及稳定性分析等角度进行学习.还没阅读的童鞋可以点这里进行浏览. 求知若渴的你肯定不会满足于入门的内容,今天,小编在上一篇的基础上,对多种排序算法进行优化,让我们一起来康康吧~~ 01冒泡排序 1. 优化一 优化思路:优化外层循环,我们知道,冒泡排序的每一轮都会对未排序部分进行一次遍历,如果在某次循环中没有交换操作,就说明数组已经有序,不用继续排序. 实现代码 1 public static int[]

排序算法(三)冒泡、选择排序的Python实现及算法优化详解

说在前面 最近一年太忙,博客长草了.近日用Python实现了常用排序算法,供大家参考. Java版本排序算法及优化,请看以前的文章. <排序算法之简单排序(冒泡.选择.插入)> <排序算法(二)堆排序> 1.排序概念 这里不再赘述,请参看前面2篇文章 2.简单排序之冒泡法Python实现及优化 原理图 2.1.基本实现 num_list = [     [1, 9, 8, 5, 6, 7, 4, 3, 2],     [1, 2, 3, 4, 5, 6, 7, 8, 9] ] nu

几种常用的排序算法

什么是算法 我想很多程序员恐怕误解了「算法」的意义,一想到算法就是动态规划,机器学习之类的高大名词.算法其实就是数学中的「解题过程」,解题过程要求精确,考虑各种情况,需要人看得懂.算法不需要你在键盘上选择什么编程语言实现,只需要在本子上详细的写出每一个步骤就可以了. 算法真的很重要吗? 我经常在社区里看到有人说初级开发不需要懂算法,这是非常真切的,很多的业务构建都是很常规的套路,查个数据库返回,没有太多复杂的计算,哪需要什么解题过程. 但是我想遇到稍微复杂一点的业务,或者想要系统运行得更流畅.更

[算法] 十个经典排序算法

动图演示参考:https://www.cnblogs.com/onepixel/articles/7674659.html 基数排序参考:https://blog.csdn.net/double_happiness/article/details/72452243 1.常见的排序算法 2.算法分析 3.算法的实现 1)排序类 1 #ifndef _SORT_H_ 2 #define _SORT_H_ 3 4 #include<vector> 5 6 class Sort { 7 public:

[java初探06]__排序算法的简单认识

今天,准备填完昨天没填的坑,将排序算法方面的知识系统的学习一下,但是在简单的了解了一下后,有些不知如何组织学习了,因为排序算法的种类,实在是太多了,各有优略,各有适用的场景.有些不知所措,从何开始. 最后按照常规思路,我将逐次从排序算法的了解,常用的几种排序算法的原理及实现,几种算法的对比以及适用场景.三个方面展开对排序算法的学习. 排序算法的基本了解 在我们学习一样知识,技术之前,首先我们应当对它有一个基本的了解,然后在了解的基础上逐渐深入学习. 在计算机科学与数学中,排序算法(Sorting

一文弄懂计数排序算法!

这是小川的第385次更新,第413篇原创 01 计数排序算法概念 计数排序不是一个比较排序算法,该算法于1954年由 Harold H. Seward提出,通过计数将时间复杂度降到了O(N). 02 基础版算法步骤 第一步:找出原数组中元素值最大的,记为max. 第二步:创建一个新数组count,其长度是max加1,其元素默认值都为0. 第三步:遍历原数组中的元素,以原数组中的元素作为count数组的索引,以原数组中的元素出现次数作为count数组的元素值. 第四步:创建结果数组result,起

算法学习?挑战高薪的必经之路!让面试官满意的排序算法(图文解析)

让面试官满意的排序算法(图文解析) 这种排序算法能够让面试官面露微笑 这种排序算法集各排序算法之大成 这种排序算法逻辑性十足 这种排序算法能够展示自己对Java底层的了解 这种排序算法出自Vladimir Yaroslavskiy.Jon Bentley和Josh Bloch三位大牛之手,它就是JDK的排序算法--java.util.DualPivotQuicksort(双支点快排) 想看以往学习内容的朋友可以看我的GitHub:https://github.com/Meng997998/And

经典排序算法 - 冒泡排序Bubble sort

 原文出自于 http://www.cnblogs.com/kkun/archive/2011/11/23/bubble_sort.html 经典排序算法 - 冒泡排序Bubble sort 原理是临近的数字两两进行比较,按照从小到大或者从大到小的顺序进行交换, 这样一趟过去后,最大或最小的数字被交换到了最后一位, 然后再从头开始进行两两比较交换,直到倒数第二位时结束,其余类似看例子 例子为从小到大排序, 原始待排序数组| 6 | 2 | 4 | 1 | 5 | 9 | 第一趟排序(外循环) 第

排序算法比较及其应用

一.将各种数据排序 只要实现了Comparable接口的数据类型就可以被排序. 但要使算法能够灵活地用不同字段进行排序,则是后续需要考虑的问题. 1.指针排序 在Java中,指针操作是隐式的,排序算法操作的总是数据引用,而不是数据本身. 2.键不可变 如果在排序后,用例还可以改变键值,那么数组很可能就不是有序的了.类似,优先队列也会乱套. Java中,可以用不可变数据类型作为键来避免这个问题,如String,Integer,Double和File都是不可变的. 3.廉价交换 使用引用的另一个好处