[数据结构与算法]常用排序算法分析与实现:第一部分

声明:原创作品,转载时请注明文章来自SAP师太技术博客:www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将追究法律责任!原文链接:http://www.cnblogs.com/jiangzhengjun/p/4289903.html

  • 插入排序

直接插入排序、希尔排序

  • 选择排序

简单选择排序、堆排序

  • 交换排序

冒泡排序、快速排序

  • 归并排序
  • 基数排序

排序基类

  1 package sort;
  2
  3 import java.util.Arrays;
  4 import java.util.Comparator;
  5 import java.util.Random;
  6
  7 /**
  8  * 排序接口,所有的排序算法都要继承该抽象类,并且要求数组中的
  9  * 元素要具有比较能力,即数组元素已实现了Comparable接口
 10  *
 11  * @author jzj
 12  * @date 2009-12-5
 13  *
 14  * @param <E>
 15  */
 16 public abstract class Sort<E extends Comparable<E>> {
 17
 18     public final Comparator<E> DEFAULT_ORDER = new DefaultComparator();
 19     public final Comparator<E> REVERSE_ORDER = new ReverseComparator();
 20
 21     /**
 22      * 排序算法,需实现,对数组中指定的元素进行排序
 23      * @param array 待排序数组
 24      * @param from 从哪里
 25      * @param end 排到哪里
 26      * @param c
 27      */
 28     public abstract void sort(E[] array, int from, int end, Comparator<E> c);
 29
 30     /**
 31      * 对数组中指定部分进行排序
 32      * @param from 从哪里
 33      * @param len 排到哪里
 34      * @param array 待排序数组
 35      * @param c 比较器
 36      */
 37     public void sort(int from, int len, E[] array, Comparator<E> c) {
 38         sort(array, 0, array.length - 1, c);
 39     }
 40
 41     /**
 42      * 对整个数组进行排序,可以使用自己的排序比较器,也可使用该类提供的两个比较器
 43      * @param array 待排序数组
 44      * @param c 比较器
 45      */
 46     public final void sort(E[] array, Comparator<E> c) {
 47         sort(0, array.length, array, c);
 48     }
 49
 50     /**
 51      * 对整个数组进行排序,采用默认排序比较器
 52      * @param array 待排序数组
 53      */
 54     public final void sort(E[] array) {
 55         sort(0, array.length, array, this.DEFAULT_ORDER);
 56     }
 57
 58     //默认比较器(一般为升序,但是否真真是升序还得看E是怎样实现Comparable接口的)
 59     private class DefaultComparator implements Comparator<E> {
 60         public int compare(E o1, E o2) {
 61             return o1.compareTo(o2);
 62         }
 63     }
 64
 65     //反序比较器,排序刚好与默认比较器相反
 66     private class ReverseComparator implements Comparator<E> {
 67         public int compare(E o1, E o2) {
 68             return o2.compareTo(o1);
 69         }
 70     }
 71
 72     /**
 73      * 交换数组中的两个元素的位置
 74      * @param array 待交换的数组
 75      * @param i 第一个元素
 76      * @param j 第二个元素
 77      */
 78     protected final void swap(E[] array, int i, int j) {
 79         if (i != j) {//只有不是同一位置时才需交换
 80             E tmp = array[i];
 81             array[i] = array[j];
 82             array[j] = tmp;
 83         }
 84     }
 85
 86     /**
 87      * 数组元素后移
 88      * @param array 待移动的数组
 89      * @param startIndex 从哪个开始移
 90      * @param endIndex 到哪个元素止
 91      */
 92     protected final void move(E[] array, int startIndex, int endIndex) {
 93         for (int i = endIndex; i >= startIndex; i--) {
 94             array[i + 1] = array[i];
 95         }
 96     }
 97
 98     /**
 99      * 以指定的步长将数组元素后移,步长指定每个元素间的间隔
100      * @param array 待排序数组
101      * @param startIndex 从哪里开始移
102      * @param endIndex 到哪个元素止
103      * @param step 步长
104      */
105     protected final void move(E[] array, int startIndex, int endIndex, int step) {
106         for (int i = endIndex; i >= startIndex; i -= step) {
107             array[i + step] = array[i];
108         }
109     }
110
111     //测试方法
112     @SuppressWarnings("unchecked")
113     public static final <E extends Comparable<E>> void testSort(Sort<E> sorter, E[] array) {
114
115         if (array == null) {
116             array = randomArray();
117         }
118         //为了第二次排序,需拷贝一份
119         E[] tmpArr = (E[]) new Comparable[array.length];
120         System.arraycopy(array, 0, tmpArr, 0, array.length);
121
122         System.out.println("源 - " + Arrays.toString(tmpArr));
123
124         sorter.sort(array, sorter.REVERSE_ORDER);
125         System.out.println("降 - " + Arrays.toString(array));
126
127         sorter.sort(tmpArr, sorter.DEFAULT_ORDER);
128         System.out.println("升 - " + Arrays.toString(tmpArr));
129     }
130
131     //生成随机数组
132     @SuppressWarnings("unchecked")
133     private static <E extends Comparable<E>> E[] randomArray() {
134         Random r = new Random(System.currentTimeMillis());
135         Integer[] a = new Integer[r.nextInt(30)];
136         for (int i = 0; i < a.length; i++) {
137             a[i] = new Integer(r.nextInt(100));
138         }
139         return (E[]) a;
140     }
141 }

插入排序

直接插入排序

一般直接插入排序的时间复杂度为O ( n^2 ) ,但是当数列基本有序时,如果按照有数列顺序排时,时间复杂度将改善到O( n ),另外,因直接插入排序算法简单,如果待排序列规模不很大时效率也较高。

在已经排好序的序列中查找待插入的元素的插入位置,并将待插入元素插入到有序列表中的过程。
 
将数组分成两部分,初始化时,前部分数组为只有第一个元素,用来存储已排序元素,我们这里叫 arr1 ;后部分数组的元素为除第一个元素的所有元素,为待排序或待插入元素,我们这里叫 arr2 。
排序时使用二层循环:第一层对 arr2 进行循环,每次取后部分数组(待排序数组)里的第一个元素(我们称为待排序元素或称待插入元素) e1 ,然后在第二层循环中对 arr1 (已排好序的数组)从第一个元素往后进行循环,查到第一个大于待插入元素(如果是升序排列)或第一个小于待插入元素(如果是降序排列) e2 ,然后对 arr1 从 e2 元素开始往后的所有元素向后移,最后把 e1 插入到原来 e2 所在的位置。这样反复地对 arr2 进行循环,直到 arr2 中所有的待插入的元素都插入到 arr1 中。

 1 package sort;
 2
 3 import java.util.Comparator;
 4
 5 /**
 6  *  直接插入排序算法
 7  * @author jzj
 8  * @date 2009-12-5
 9  *
10  * @param <E>
11  */
12 public class InsertSort<E extends Comparable<E>> extends Sort<E> {
13
14     /**
15      * 排序算法的实现,对数组中指定的元素进行排序
16      * @param array 待排序的数组
17      * @param from 从哪里开始排序
18      * @param end 排到哪里
19      * @param c 比较器
20      */
21     public void sort(E[] array, int from, int end, Comparator<E> c) {
22
23         /*
24          * 第一层循环:对待插入(排序)的元素进行循环
25          * 从待排序数组断的第二个元素开始循环,到最后一个元素(包括)止
26          */
27         for (int i = from + 1; i <= end; i++) {
28             /*
29              * 第二层循环:对有序数组进行循环,且从有序数组最第一个元素开始向后循环
30              * 找到第一个大于待插入的元素
31              * 有序数组初始元素只有一个,且为源数组的第一个元素,一个元素数组总是有序的
32              */
33             for (int j = 0; j < i; j++) {
34                 E insertedElem = array[i];//待插入到有序数组的元素
35                 //从有序数组中最一个元素开始查找第一个大于待插入的元素
36                 if (c.compare(array[j], insertedElem) > 0) {
37                     //找到插入点后,从插入点开始向后所有元素后移一位
38                     move(array, j, i - 1);
39                     //将待排序元素插入到有序数组中
40                     array[j] = insertedElem;
41                     break;
42                 }
43             }
44         }
45
46         //=======以下是java.util.Arrays的插入排序算法的实现
47         /*
48          * 该算法看起来比较简洁一j点,有点像冒泡算法。
49          * 将数组逻辑上分成前后两个集合,前面的集合是已经排序好序的元素,而后面集合为待排序的
50          * 集合,每次内层循从后面集合中拿出一个元素,通过冒泡的形式,从前面集合最后一个元素开
51          * 始往前比较,如果发现前面元素大于后面元素,则交换,否则循环退出
52          *
53          * 总感觉这种算术有点怪怪,既然是插入排序,应该是先找到插入点,而后再将待排序的元素插
54          * 入到的插入点上,那么其他元素就必然向后移,感觉算法与排序名称不匹,但返过来与上面实
55          * 现比,其实是一样的,只是上面先找插入点,待找到后一次性将大的元素向后移,而该算法却
56          * 是走一步看一步,一步一步将待排序元素往前移
57          */
58         /*
59         for (int i = from; i <= end; i++) {
60             for (int j = i; j > from && c.compare(array[j - 1], array[j]) > 0; j--) {
61                 swap(array, j, j - 1);
62             }
63         }
64         */
65     }
66
67     /**
68      * 测试
69      * @param args
70      */
71     public static void main(String[] args) {
72         Integer[] intgArr = { 5, 9, 1, 4, 1, 2, 6, 3, 8, 0, 7 };
73         InsertSort<Integer> insertSort = new InsertSort<Integer>();
74         Sort.testSort(insertSort, intgArr);
75         Sort.testSort(insertSort, null);
76     }
77 }

插入排序算法对于大数组,这种算法非常慢。但是对于小数组,它比其他算法快。其他算法因为待的数组元素很少,反而使得效率降低。在Java集合框架中,排序都是借助于java.util.Arrays来完成的,其中排序算法用到了插入排序、快速排序、归并排序。插入排序用于元素个数小于7的子数组排序,通常比插入排序快的其他排序方法,由于它们强大的算法是针对大数量数组设计的,所以元素个数少时速度反而慢。

希尔排序

希尔思想介绍

希尔算法的本质是缩小增量排序,是对直接插入排序算法的改进。一般直接插入排序的时间复杂度为O ( n^2 ) ,但是当数列基本有序时,如果按照有数列顺序排时,时间复杂度将改善到O( n ),另外,因直接插入排序算法简单,如果待排序列规模不很大时效率也较高,Shell 根据这两点分析结果进行了改进,将待排记录序列以一定的增量间隔h 分割成多个子序列,对每个子序列分别进行一趟直接插入排序, 然后逐步减小分组的步长h ,对于每一个步长h 下的各个子序列进行同样方法的排序,直到步长为1 时再进行一次整体排序。
因为不管记录序列多么庞大,关键字多么混乱,在先前较大的分组步长h 下每个子序列的规模都不大,用直接插入排序效率都较高。 尽管在随后的步长h 递减分组中子序列越来越大,但由于整个序列的有序性也越来越明显,则排序效率依然较高。这种改进抓住了直接插入排序的两点本质,大大提高了它的时间效率。

希尔增量研究

综上所述:

(1) 希尔排序的核心是以某个增量h 为步长跳跃分组进行插入排序,由于分组的步长h 逐步缩小,所以也叫“缩小增量排序”插入排序。其关键是如何选取分组的步长序列ht ,. . . , hk ,. . . , h1 , h0 才能使得希尔方法的时间效率最高;

(2) 待排序列记录的个数n 、跳跃分组步长逐步减小直到为1时所进行的扫描次数T、增量的和、记录关键字比较的次数以及记录移动的次数或各子序列中的反序数等因素都影响希尔算法的时间复杂度:其中记录关键字比较的次数是重要因素,它主要取决于分组步长序列的选择;

(3) 希尔方法是一种不稳定排序算法,因为其排序过程中各趟的步长不同,在第k 遍用hk作为步长排序之后,第k +1 遍排序时可能会遇到多个逆序存在,影响排序的稳定性。

试验结果表明,SHELL 算法的时间复杂度受增量序列的影响明显大于其他因素,选取恰当的增量序列能明显提高排序的时间效率,我们认为第k 趟排序扫描的增量步长为 2^k - 1 ,即增量序列为. . . 2^k - 1 ,. . . ,15 ,7 ,3 ,1时较为理想,但它并不是唯一的最佳增量序列,这与其关联函数目前尚无确定解的理论结果是一致的。

  1 package sort;
  2
  3 import java.util.Comparator;
  4
  5 /**
  6  * 希尔排序算法
  7  * @author jzj
  8  * @date 2009-12-5
  9  *
 10  * @param <E>
 11  */
 12 public class ShelltSort<E extends Comparable<E>> extends Sort<E> {
 13
 14     /**
 15      * 排序算法的实现,对数组中指定的元素进行排序
 16      * @param array 待排序的数组
 17      * @param from 从哪里开始排序
 18      * @param end 排到哪里
 19      * @param c 比较器
 20      */
 21     public void sort(E[] array, int from, int end, Comparator<E> c) {
 22         //初始步长,实质为每轮的分组数
 23         int step = initialStep(end - from + 1);
 24
 25         //第一层循环是对排序轮次进行循环。(step + 1) / 2 - 1 为下一轮步长值
 26         for (; step >= 1; step = (step + 1) / 2 - 1) {
 27             //对每轮里的每个分组进行循环
 28             for (int groupIndex = 0; groupIndex < step; groupIndex++) {
 29
 30                 //对每组进行直接插入排序
 31                 insertSort(array, groupIndex, step, end, c);
 32             }
 33         }
 34     }
 35
 36     /**
 37      * 直接插入排序实现
 38      * @param array 待排序数组
 39      * @param groupIndex 对每轮的哪一组进行排序
 40      * @param step 步长
 41      * @param end 整个数组要排哪个元素止
 42      * @param c 比较器
 43      */
 44     private void insertSort(E[] array, int groupIndex, int step, int end, Comparator<E> c) {
 45         int startIndex = groupIndex;//从哪里开始排序
 46         int endIndex = startIndex;//排到哪里
 47         /*
 48          * 排到哪里需要计算得到,从开始排序元素开始,以step步长,可求得下元素是否在数组范围内,
 49          * 如果在数组范围内,则继续循环,直到索引超现数组范围
 50          */
 51         while ((endIndex + step) <= end) {
 52             endIndex += step;
 53         }
 54
 55         // i为每小组里的第二个元素开始
 56         for (int i = groupIndex + step; i <= end; i += step) {
 57             for (int j = groupIndex; j < i; j += step) {
 58                 E insertedElem = array[i];
 59                 //从有序数组中最一个元素开始查找第一个大于待插入的元素
 60                 if (c.compare(array[j], insertedElem) >= 0) {
 61                     //找到插入点后,从插入点开始向后所有元素后移一位
 62                     move(array, j, i - step, step);
 63                     array[j] = insertedElem;
 64                     break;
 65                 }
 66             }
 67         }
 68     }
 69
 70     /**
 71      * 根据数组长度求初始步长
 72      *
 73      * 我们选择步长的公式为:2^k-1,2^(k-1)-1,...,15,7,3,1 ,其中2^k 减一即为该步长序列,k
 74      * 为排序轮次
 75      *
 76      * 初始步长:step = 2^k-1
 77      * 初始步长约束条件:step < len - 1 初始步长的值要小于数组长度还要减一的值(因
 78      * 为第一轮分组时尽量不要分为一组,除非数组本身的长度就小于等于4)
 79      *
 80      * 由上面两个关系试可以得知:2^k - 1 < len - 1 关系式,其中k为轮次,如果把 2^k 表 达式
 81      * 转换成 step 表达式,则 2^k-1 可使用 (step + 1)*2-1 替换(因为 step+1 相当于第k-1
 82      * 轮的步长,所以在 step+1 基础上乘以 2 就相当于 2^k 了),即步长与数组长度的关系不等式为
 83      * (step + 1)*2 - 1 < len -1
 84      *
 85      * @param len 数组长度
 86      * @return
 87      */
 88     private static int initialStep(int len) {
 89         /*
 90          * 初始值设置为步长公式中的最小步长,从最小步长推导出最长初始步长值,即按照以下公式来推:
 91          * 1,3,7,15,...,2^(k-1)-1,2^k-1
 92          * 如果数组长度小于等于4时,步长为1,即长度小于等于4的数组不且分组,此时直接退化为直接插
 93          * 入排序
 94          */
 95         int step = 1;
 96
 97         //试探下一个步长是否满足条件,如果满足条件,则步长置为下一步长
 98         while ((step + 1) * 2 - 1 < len - 1) {
 99             step = (step + 1) * 2 - 1;
100         }
101
102         System.out.println("初始步长 - " + step);
103         return step;
104     }
105
106     /**
107      * 测试
108      * @param args
109      */
110     public static void main(String[] args) {
111         Integer[] intgArr = { 5, 9, 1, 4, 8, 2, 6, 3, 7, 10 };
112         ShelltSort<Integer> shellSort = new ShelltSort<Integer>();
113         Sort.testSort(shellSort, intgArr);
114         Sort.testSort(shellSort, null);
115     }
116 }

选择排序

简单选择排序

每一趟从待排序的数据元素中选出最小(或最大)的一个元素,顺序放在已排好序的数列的最后,直到全部待排序的数据元素排完。
选择排序不像冒泡排序算法那样先并不急于调换位置,第一轮(k=1)先从array[k]开始逐个检查,看哪个数最小就记下该数所在的位置于 minlIndex中,等一轮扫描完毕,如果找到比array[k-1]更小的元素,则把array[minlIndex]和a[k-1]对调,这时 array[k]到最后一个元素中最小的元素就换到了array[k-1]的位置。 如此反复进行第二轮、第三轮…直到循环至最后一元素

 1 package sort;
 2
 3 import java.util.Comparator;
 4
 5 /**
 6  * 简单选择排序算法
 7  * @author jzj
 8  * @date 2009-12-5
 9  *
10  * @param <E>
11  */
12 public class SelectSort<E extends Comparable<E>> extends Sort<E> {
13
14     /**
15      * 排序算法的实现,对数组中指定的元素进行排序
16      * @param array 待排序的数组
17      * @param from 从哪里开始排序
18      * @param end 排到哪里
19      * @param c 比较器
20      */
21     public void sort(E[] array, int from, int end, Comparator<E> c) {
22         int minlIndex;//最小索引
23         /*
24          * 循环整个数组(其实这里的上界为 array.length - 1 即可,因为当 i= array.length-1
25          * 时,最后一个元素就已是最大的了,如果为array.length时,内层循环将不再循环),每轮假设
26          * 第一个元素为最小元素,如果从第一元素后能选出比第一个元素更小元素,则让让最小元素与第一
27          * 个元素交换
28          */
29         for (int i = from; i <= end; i++) {
30             minlIndex = i;//假设每轮第一个元素为最小元素
31             //从假设的最小元素的下一元素开始循环
32             for (int j = i + 1; j <= end; j++) {
33                 //如果发现有比当前array[smallIndex]更小元素,则记下该元素的索引于smallIndex中
34                 if (c.compare(array[j], array[minlIndex]) < 0) {
35                     minlIndex = j;
36                 }
37             }
38
39             //先前只是记录最小元素索引,当最小元素索引确定后,再与每轮的第一个元素交换
40             swap(array, i, minlIndex);
41         }
42     }
43
44     /**
45      * 测试
46      * @param args
47      */
48     public static void main(String[] args) {
49         Integer[] intgArr = { 5, 9, 1, 4, 1, 2, 6, 3, 8, 0, 7 };
50         SelectSort<Integer> insertSort = new SelectSort<Integer>();
51         Sort.testSort(insertSort, intgArr);
52         Sort.testSort(insertSort, null);
53     }
54 }

堆排序

堆实质上是满足如下性质的完全二叉树:树中任一非叶结点的关键字均不大于(或不小于)其左右孩子(若存在)结点的关键字。

【例】关键字序列(10,15,56,25,30,70)和(70,56,30,25,15,10)分别满足堆性质(1)和(2),故它们均是堆,其对应的完全二叉树分别如小根堆示例和大根堆示例所示:

根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最小者的堆称为小顶堆。
根结点(亦称为堆顶)的关键字是堆里所有结点关键字中最大者,称为大顶堆。

堆是一种完全二叉树,一般使用数组来实现。堆排序也是一种选择性的排序,每次选择第i大的元素。

另外排序过程中借助了堆结构,堆就是一种完全二叉树,所以这里先要熟悉要用的二叉树几个性质:

N(N>1)个节点的的完全二叉树从层次从左自右编号,最后一个分枝节点(非叶子节点)的编号为 N/2 取整。

且对于编号 i(1<=i<=N)有:父节点为 i/2 向下取整;若2i>N,则节点i没有左孩子,否则其左孩子为2i;若2i+1>N,则没有右孩子,否则其右孩子为2i+1。

注,这里使用完全二叉树只是为了好描述算法,它只是一种逻辑结构,真真在实现时我们还是使用数组来存储这棵二叉树的,因为完全二叉树完全可以使用数组来存储。

算法描述:

堆排序其实最主要的两个过程:第一步,创建初始堆;第二步,交换根节点与最后一个非叶子节

第一步实现:从最后一个非叶子节点为开始向前循环每个会支节点,比较每个分支节点与他左右子节点,如果其中某个子节点比父节点大,则与父节点交换,交换后原父节点可能还小于原子节点的子节点,所以还需对原父节点进行调整,使用原父节点继续下沉,直到没有子节点或比左右子节点都大为止,调用过程可通过递归完成。当某个非叶子节点调整完毕后,再处理下一个非叶子节点,直到根节点也调整完成,这里初始堆就创建好了,这里我们创建的是大顶堆,即大的元素向树的根浮,这样排序最后得到的结果为升序,因为最大的从树中去掉,并从数组最后往前存放。

第二步实现:将树中的最后一个元素与堆顶元素进行交换,并从树中去掉最后叶子节点。交换后再按创建初始堆的算法调整根节点,如此下去直到树中只有一个节点为止。

 1 package sort;
 2
 3 import java.util.Comparator;
 4
 5 public class HeapSort<E extends Comparable<E>> extends Sort<E> {
 6
 7     /**
 8      * 排序算法的实现,对数组中指定的元素进行排序
 9      * @param array 待排序的数组
10      * @param from 从哪里开始排序
11      * @param end 排到哪里
12      * @param c 比较器
13      */
14     public void sort(E[] array, int from, int end, Comparator<E> c) {
15         //创建初始堆
16         initialHeap(array, from, end, c);
17
18         /*
19          * 对初始堆进行循环,且从最后一个节点开始,直接树只有两个节点止
20          * 每轮循环后丢弃最后一个叶子节点,再看作一个新的树
21          */
22         for (int i = end - from + 1; i >= 2; i--) {
23             //根节点与最后一个叶子节点交换位置,即数组中的第一个元素与最后一个元素互换
24             swap(array, from, i - 1);
25             //交换后需要重新调整堆
26             adjustNote(array, 1, i - 1, c);
27         }
28
29     }
30
31     /**
32      * 初始化堆
33      * 比如原序列为:7,2,4,3,12,1,9,6,8,5,10,11
34      * 则初始堆为:1,2,4,3,5,7,9,6,8,12,10,11
35      * @param arr 排序数组
36      * @param from 从哪
37      * @param end 到哪
38      * @param c 比较器
39      */
40     private void initialHeap(E[] arr, int from, int end, Comparator<E> c) {
41         int lastBranchIndex = (end - from + 1) / 2;//最后一个非叶子节点
42         //对所有的非叶子节点进行循环 ,且从最一个非叶子节点开始
43         for (int i = lastBranchIndex; i >= 1; i--) {
44             adjustNote(arr, i, end - from + 1, c);
45         }
46     }
47
48     /**
49      * 调整节点顺序,从父、左右子节点三个节点中选择一个最大节点与父节点转换
50      * @param arr 待排序数组
51      * @param parentNodeIndex 要调整的节点,与它的子节点一起进行调整
52      * @param len 树的节点数
53      * @param c 比较器
54      */
55     private void adjustNote(E[] arr, int parentNodeIndex, int len, Comparator<E> c) {
56         int minNodeIndex = parentNodeIndex;
57         //如果有左子树,i * 2为左子节点索引
58         if (parentNodeIndex * 2 <= len) {
59             //如果父节点小于左子树时
60             if (c.compare(arr[parentNodeIndex - 1], arr[parentNodeIndex * 2 - 1]) < 0) {
61                 minNodeIndex = parentNodeIndex * 2;//记录最大索引为左子节点索引
62             }
63
64             // 只有在有或子树的前提下才可能有右子树,再进一步断判是否有右子树
65             if (parentNodeIndex * 2 + 1 <= len) {
66                 //如果右子树比最大节点更大
67                 if (c.compare(arr[minNodeIndex - 1], arr[(parentNodeIndex * 2 + 1) - 1]) < 0) {
68                     minNodeIndex = parentNodeIndex * 2 + 1;//记录最大索引为右子节点索引
69                 }
70             }
71         }
72
73         //如果在父节点、左、右子节点三都中,最大节点不是父节点时需交换,把最大的与父节点交换,创建大顶堆
74         if (minNodeIndex != parentNodeIndex) {
75             swap(arr, parentNodeIndex - 1, minNodeIndex - 1);
76             //交换后可能需要重建堆,原父节点可能需要继续下沉
77             if (minNodeIndex * 2 <= len) {//是否有子节点,注,只需判断是否有左子树即可知道
78                 adjustNote(arr, minNodeIndex, len, c);
79             }
80         }
81     }
82
83     /**
84     * 测试
85     * @param args
86     */
87     public static void main(String[] args) {
88         Integer[] intgArr = { 7, 2, 4, 3, 12, 1, 9, 6, 8, 5, 10, 11 };
89         HeapSort<Integer> sort = new HeapSort<Integer>();
90         HeapSort.testSort(sort, intgArr);
91         HeapSort.testSort(sort, null);
92     }
93
94 }
时间: 2024-10-06 05:45:34

[数据结构与算法]常用排序算法分析与实现:第一部分的相关文章

[数据结构与算法]常用排序算法分析与实现:第二部分

声明:原创作品,转载时请注明文章来自SAP师太技术博客:www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将追究法律责任!原文链接:http://www.cnblogs.com/jiangzhengjun/p/4289948.html 交换排序 冒泡排序 将最后一个元素与倒数第二个元素对比,如果最后一个元素比倒数第二个小,则交换两个元素的位置,再用倒数第二个元素与倒数第三个元数对比,直到比到第一个元素,这样经过第一趟排序后得到第一个最小元素.如此反复

javascript数据结构与算法--基本排序算法分析

javascript中的基本排序算法 对计算机中存储的数据执行的两种最常见操作是排序和检索,排序和检索算法对于前端开发尤其重要,对此我会对这两种算法做深入的研究,而不会和书上一样只是会贴代码而已,下面我会一步步从自己的理解的思路来一步步学习各个排序的思想.不过这些算法依赖于javascript中的数组来存储数据.最后我会来测试下基本算法(冒泡排序,选择排序,插入排序)的那个效率更高! 下面啊,我们先可以来封装常规数组操作的函数,比如:插入新数据,显示数组数据,还有交换数组元素等操作来调用不同的排

Java数据结构与算法之排序

排序从大体上来讲,做了两件事情: 1.比较两个数据项: 2.交换两个数据项,或复制其中一项 一.冒泡排序 大O表示法:交换次数和比较次数都为O(N*N). 算法原理: 1.比较相邻的元素.如果第一个比第二个大,就交换他们两个. 2.对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对.在这一点,最后的元素应该会是最大的数. 3.针对所有的元素重复以上的步骤,除了最后一个. 4.持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较. /** * 冒泡排序 demo * */

在Object-C中学习数据结构与算法之排序算法

笔者在学习数据结构与算法时,尝试着将排序算法以动画的形式呈现出来更加方便理解记忆,本文配合Demo 在Object-C中学习数据结构与算法之排序算法阅读更佳. 目录 选择排序 冒泡排序 插入排序 快速排序 双路快速排序 三路快速排序 堆排序 总结与收获 参考与阅读 选择排序 选择排序是一种简单直观的排序算法,无论什么数据进去都是 O(n2) 的时间复杂度.所以用到它的时候,数据规模越小越好.唯一的好处可能就是不占用额外的内存空间了吧. 1.算法步骤 首先在未排序序列中找到最小(大)元素,存放到排

python 数据结构与算法之排序(冒泡,选择,插入)

目录 数据结构与算法之排序(冒泡,选择,插入) 为什么学习数据结构与算法: 数据结构与算法: 算法: 数据结构 冒泡排序法 选择排序法 插入排序法 数据结构与算法之排序(冒泡,选择,插入) 为什么学习数据结构与算法: 计算机重要的几门课: 1.数据结构和算法 2.网络 3.操作系统 4.计算组成原理 数据结构与算法: 算法: 衡量算法的标准: 时间复杂度:就是程序代码执行的大概次数 小结: 时间复杂度是用来估计算法运行时间的一个式子(单位) 一般来说,时间复杂度高的算法比复杂度低的算法慢 常见的

数据结构和算法之排序一:归并排序

我们不得不承认一个事实,java学习过程中如果我们掌握了各种编程手段和工具,确实可以做一些开发,这就是一些培训机构敢告诉你几个月就能掌握一门语言的原因.但是随着时间的发展,我们总会感觉,这一类人如果不提升自己,最后也只会是一个码农.技术会日新月异,随时在发展更新换代,但是这几十年,有谁说过算法会过时,如果我们说java语言的发动机是各种开发手段和技术,那么我们可以毫不客气的说算法会是他的灵魂.一个程序员的提升和拔高一定是万丈高楼平地起,那么我希望这个地基一定是数据结构和算法,掌握这些原理以后其实

数据结构和算法之排序六:希尔排序

经过前面五篇排序方法的介绍,我们了解到了递归思想以及分而治之的归并和快速排序,当然也涉及到了比较简单易懂的数据值传递冒泡,选择,以及插入排序.可以说每一种排序方式都各有千秋,都适合在不同的环境下进行使用,但是我们有时会不会思考一个问题,那就是我们在学习插入排序的时候的主题思想是将每一个数据取出来,然后和后边的前边的数据逐一进行比较,那么,我们是不是要进行N-1次的循环,或者说我们是不是要进行大约N的平方次比较,我们知道,在进行值的交换和比较是非常消耗时间的,那么是不是违背了我们算法的发展.有没有

javascript数据结构与算法--高级排序算法

高级排序算法是处理大型数据集的最高效排序算法,它是处理的数据集可以达到上百万个元素,而不仅仅是几百个或者几千个.现在我们来学习下2种高级排序算法---- 希尔排序和快速排序. 一:希尔排序: 希尔排序的核心理念是:首先比较距离较远的元素,而非相邻的元素. 基本原理:通过定义一个间隔序列来表示在排序过程中进行比较的元素之间有多远的间隔. 下面我们来看看数组[0,9,1,8,7,6,2,3,5,4] 来使用希尔排序的原理:如下图: 代码分析如下: 1. 执行 "间隔序列=3的步骤" A.

数据结构与算法之排序(归纳总结二)

交换类排序主要是通过两两比较待排元素的关键字,若发现与排序要求相逆,则“交换”之.在这类排序方法中最常见的是起泡排序和快速排序,其中快速排序是一种在实际应用中具有很好表现的算法. 1.冒泡排序 a.算法描述 起泡排序的思想非常简单.首先,将n个元素中的第一个和第二个进行比较,如果两个元素的位置为逆序,则交换两个元素的位置:进而比较第二个和第三个元素关键字,如此类推,直到比较第n-1个元素和第n个元素为止:上述过程描述了起泡排序的第一趟排序过程,在第一趟排序过程中,我们将关键字最大的元素通过交换操