搞不懂的算法-排序篇

  最近在学习算法,跟着<Algorithms>这本书,可能是自己水平不够吧,看完排序算法后各种,希尔,归并,快排,堆的实现在脑子里乱成一锅粥,所以就打算大概总结一下,不求精确,全面,只想用平白的语言来理一理,如有错误之处,请直言。

  为什么所有的算法书籍都重墨介绍排序,一、对一组数据进行排序在生活中是如此的常见,我们常常需要使用它;二、排序是实现很多一些高级算法的基础,一些复杂问题,如果处理的是已经拍过序的数据,那么就容易处理很多;三、排序算法中包含了很多重要的思想和方法,对于其他算法的研究也具有借鉴意义。研究排序算法之前,有一些关于算法的基础是需要掌握的。什么是好的算法?当然是算的快的算法,就是所谓的时间复杂性分析。计算机的内存是有限的,如果算法使用的空间也比较小,那就更好了,所谓的空间复杂度分析。如果代码写起来简单又好理解,这简直就是完美。此外针对不同的输入,同一个算法的性能也有很大的区别,所以也可以分别讨论。一些搞算法的人将这些情况总结起来,形成了科学的方法,甚至抽象成了思想。包括:算法的时间复杂度,该算法的运行时间?这个问题,我们能达到的最快运行时间;空间复杂度,算法解决问题使用多少空间。输入模型,针对不容的输入情况,比如大致排好序的,重复元素多的等等。算的又快,空间利用少,针对不同输入保持稳定高效,这样的算法是完美的算法。

  排序-比如,给你一个包含N个随机double元素的数组,想要得到一个按大小排好序的数组。

1,选择排序/selection sort

  选择排序大概是最简单易于理解的排序方法了,想象有一群高矮不同的人群,现在要将他们按高矮排一列。你可以从这群人中找出最矮的那个,放到最前面,然后找出第二矮的,依次下去直到排完。

 1 public class Selection{
 2     private static boolean less(Comparable[] a,int i,int j){
 3         return a[i].compareTo(a[j])<0;
 4     }
 5     private static void exch(Comparable[] a,int i,int j){
 6         Comparable temp=a[i];
 7         a[i]=a[j];
 8         a[j]=temp;
 9     }
10     public static void sort(Comparable[] a){
11         int N=a.length;
12         for(int i=0;i<N-1;i++){
13             int temp=i;
14             for(int j=i+1;j<N;j++)
15                 if(less(a,j,temp)) temp=j;
16             exch(a,i,temp);
17         }
18     }
19 }

这里面有两个函数:less(),exch(),前者以索引比较数组中的两个元素的大小,后者以索引交换两个元素的位置。之所以将两个操作封装成两个函数,是为了算法分析的方便。以后所有的算法都只使用着两个操作,分析也主要集中于不同算法执行的着两个操作的次数,因为其他的操作都是常数次的,只有这两个操作是与N有关的。

分析:对于选择排序,我们可以看到,比较的次数为(N-1)+(N-2)+...1=N2/2,平方级别;交换的次数为N-1,线性级别。非常稳定,无论输入如何,效率不变。

2.插入排序/Insertion sort

  从数组左边到右边遍历元素,如果元素i小于前一个元素,将i与i-1交换,重复这个操作直到i落到合适的位置。

 1 public class Insertion{
 2     private static boolean less(Comparable[] a,int i,int j){
 3         return a[i].compareTo(a[j])<0;
 4     }
 5     private static void exch(Comparable[] a,int i,int j){
 6         Comparable temp=a[i];
 7         a[i]=a[j];
 8         a[j]=temp;
 9     }
10     public static void sort(Comparable[] a){
11         int N=a.length;
12         for(int i=1;i<N;i++)
13             while(i>0 && less(a,i,i-1)){
14                 exch(a,i,i-1);
15                 i--;
16             }
17     }
18 }

分析:这个算法是依赖于输入的,最好的情况是数组已经排好序,那么只需要经过N-1次比较,0次交换,就能完成。最坏的情况,数组是逆序的,那么需要1+2+...(N-1)=N2/2次比较,同样多次的交换达成目标。平均情况下同样分析可以知道,需要N2/4次比较和同样多次交换达成目标。值得注意的一点是,插入排序对于基本有序的数组排序很有效果,因为只需要线性次数的比较和较少次操作,就能达成目标。

3.冒泡排序/bubble sort

  对于很多没学过算法的人,听到这个名字感觉很高大上,但其实这是个很简单的算法,效率也较低,基本上不怎么用。方法就是进行一个N次的循环,每次循环遍历数组元素,将相邻两个元素中较大的放后面,因为元素像泡泡一样浮出得名。有两个优化策略,1、每次重复,遍历数组长度-1,2、当一次遍历没有进行交换,表明已经排好序,跳出循环而不用执行固定的循环次数。

 1 public class Bubble{
 2     private static boolean less(Comparable[] a,int i,int j){...代码上同}
 3     private static void exch(Comparable[] a,int i,int j){...代码上同}
 4     public static void sort(Comparable[] a){
 5         int N=a.length;
 6         int temp=1;
 7         for(int i=0;i<N-1 && temp==1;i++){
 8             temp=0;
 9             for(int j=0;j<N-i-1;j++){
10                 if(less(a,j+1,j)){
11                     exch(a,j,j+1);
12                     temp=1;
13                 }
14             }
15         }
16     }
17 }

分析:冒泡排序感觉跟插入排序有很多相同,也是依赖于输入的,最好的情况:顺序排列的数组,需要N-1次比较和0次交换,达成目标。最差的情况:逆序排列:需要1+2+...(N-1)=N2/2次比较,同样多次的交换达成目标。

  以上是三种最基本的排序方式,可以看到三者一般情况下都是平方级别的,所以相互之间的速度差别为常数级别的,试验发现,插入排序相当是较快的一种,但快的有限,大概比选择排序快0.7倍。

  实际上通过分析可以发现,三种排序方法效率都有某种程度的浪费,所以可以对其进行优化,比如一种对插入排序的优化,希尔排序。

4.希尔排序/shell short

时间: 2024-08-24 03:47:26

搞不懂的算法-排序篇的相关文章

算法——基础篇——快速排序

快速排序是一个经常使用的算法,由于每次用的时候,都感觉没有理解清楚,特写一篇文章记录一下. 算法介绍 快速排序有点类似有冒泡排序,冒泡排序从相邻的两个元素比较,小的在左边,大的在右边,这个算法很容易理解.而快速排序它相当于是在一头一尾两边分别排序比较,比较的对象是当前元素值,和一个选定的key值,主题的思想就是通过跟key值比较,把大于key的值放在右边,小于的放在左边这样就完成了一次排序,接着在对key值左边的序列进行同样的操作,右边也是,最后便能将所有的元素给排好序,由于它每次排序,都会分成

GC 算法(实现篇) - GC参考手册

您应该已经阅读了前面的章节: 垃圾收集简介 - GC参考手册 Java中的垃圾收集 - GC参考手册 GC 算法(基础篇) - GC参考手册 学习了GC算法的相关概念之后, 我们将介绍在JVM中这些算法的具体实现.首先要记住的是, 大多数JVM都需要使用两种不同的GC算法 -- 一种用来清理年轻代, 另一种用来清理老年代. 我们可以选择JVM内置的各种算法.如果不通过参数明确指定垃圾收集算法, 则会使用宿主平台的默认实现.本章会详细介绍各种算法的实现原理. 下面是关于Java 8中各种组合的垃圾

4. GC 算法(实现篇) - GC參考手冊

您应该已经阅读了前面的章节: 垃圾收集简单介绍 - GC參考手冊 Java中的垃圾收集 - GC參考手冊 GC 算法(基础篇) - GC參考手冊 学习了GC算法的相关概念之后, 我们将介绍在JVM中这些算法的详细实现. 首先要记住的是, 大多数JVM都须要使用两种不同的GC算法 -- 一种用来清理年轻代, 还有一种用来清理老年代. 我们能够选择JVM内置的各种算法. 假设不通过參数明白指定垃圾收集算法, 则会使用宿主平台的默认实现. 本章会详细介绍各种算法的实现原理. 以下是关于Java 8中各

微软数据挖掘算法:Microsoft 神经网络分析算法原理篇(9)

前言 本篇文章继续我们的微软挖掘系列算法总结,前几篇文章已经将相关的主要算法做了详细的介绍,我为了展示方便,特地的整理了一个目录提纲篇:大数据时代:深入浅出微软数据挖掘算法总结连载,有兴趣的童鞋可以点击查阅,在开始Microsoft 神经网络分析算法之前,本篇我们先将神经网络分析算法做一个简单介绍,此算法由于其本身的复杂性,所以我打算在开始之前先将算法原理做一个简单的总结,因为本身该算法就隶属于高等数学的研究范畴,我们对算法的推断和验证过程不做研究,只介绍该算法特点以及应用场景,且个人技术能力有

hdu3078 建层次树+在线LCA算法+排序

题意:n个点,n-1条边构成无向树,每个节点有权,Q次询问,每次或问从a->b的最短路中,权第k大的值,/或者更新节点a的权, 思路:在线LCA,先dfs生成树0,标记出层数和fa[](每个节点的父亲节点).在对每次询问,走一遍一次公共祖先路上 的权,保持,快排.n*logn*q #include<iostream> //187MS #include<algorithm> #include<cstdio> #include<vector> using

js 数组排序和算法排序

1.算法排序 a.插入排序 var arr = [23,34,3,4,23,44,333,444]; var arrShow = (function insertionSort(array){ if(Object.prototype.toString.call(array).slice(8,-1) ==='Array'){ for (var i = 1; i < array.length; i++) { var key = array[i]; var j = i - 1; while (j >

mahout贝叶斯算法拓展篇3---分类无标签数据

代码测试环境:Hadoop2.4+Mahout1.0 前面博客:mahout贝叶斯算法开发思路(拓展篇)1和mahout贝叶斯算法开发思路(拓展篇)2 分析了Mahout中贝叶斯算法针对数值型数据的处理.在前面这两篇博客中并没有关于如何分类不带标签的原始数据的处理.下面这篇博客就针对这样的数据进行处理. 最新版(适合Hadoop2.4+mahout1.0环境)源码以及jar包可以在这里下载Mahout贝叶斯分类不含标签数据: 下载后参考使用里面的jar包中的fz.bayes.model.Baye

算法——动态规划篇——最长公共子序列

问题描述      最长公共子序列,英文缩写为LCS(Longest Common Subsequence).其定义是,一个序列 S ,如果分别是两个或多个已知序列的子序列,且是所有符合此条件序列中最长的,则 S 称为已知序列的最长公共子序列.       解决最长公共子序列,一种常用的办法,就是穷举法,组合出所有的情况,但是这样对于长序列的情况来说,是非常不实际.. 假设现在有两个序列,x[]={'A','B','C','B','D','A','B'};y[]={'B','D','C','A'

算法——基础篇——二分查找

     二分查找又称折半查找,优点是比较次数少,查找速度快,平均性能好:其缺点是要求待查表为有序表,且插入删除困难.因此,折半查找方法适用于不经常变动而查找频繁的有序列表.     首先,假设表中元素是按升序排列,将表中间位置记录的关键字与查找关键字比较,如果两者相等,则查找成功:否则利用中间位置记录将表分成前.后两个子表,如果中间位置记录的关键字大于查找关键字,则进一步查找前一子表,否则进一步查找后一子表.重复以上过程,直到找到满足条件的记录,使查找成功,或直到子表不存在为止,此时查找不成功