Java之美[从菜鸟到高手演变]之常见的几种排序算法-插入、选择、冒泡、快排、堆排等 .
2013-04-25 09:32
525 查看
五、堆排序
我们这里不详细介绍概念,堆的话,大家只要记得堆是一个完全二叉树(什么是完全二叉树,请不懂的读者去查资料),堆排序分为两种堆,大顶堆和小顶堆,大顶堆的意思就是堆顶元素是整个堆中最大的,小顶堆的意思就是堆顶元素是整个堆中最小的,满足:任何一非叶节点的关键字不大于或者不小于其左右孩子节点的关键字。堆排序是一个相对难理解的过程,下面我会较为清楚、详细的讲解一下堆排序。堆排序分为三个过程:
建堆:从一个数组顺序读取元素,建立一个堆(完全二叉树)
初始化:将堆进行调整,使得堆顶为最大(最大堆)或者最小(最小堆)的元素
维护:将堆顶元素出堆后,需要将堆的最后一个节点补充到堆顶,因为这样破坏了堆的秩序,所以需要进行维护。下面我们图示一下:
一般情况,建堆和初始化同步进行,
最后为如下所示,即为建堆、初始化成功。
我们可以观察下这个最大堆,看出堆顶是整个堆中最大的元素,而且除叶子节点外每个节点都大于其子节点。下面的过程就是当我们输出堆顶元素后,对堆进行维护。
过程是这样:将堆顶元素出堆后,用最后一个元素补充堆顶元素,这样破坏了之前的秩序,需要重新维护堆,在堆顶元素的左右节点中选出较小的和堆顶互换,然后一直递归下去,所以每次出一个元素,需要一次维护,堆排序适合解决topK问题,能将复杂度降到nlogK。下面是代码:
[java]
view plaincopyprint?
package com.xtfggef.algo.sort;
public class HeapSort {
private static int[] sort = new int[] { 1, 0, 10, 20, 3, 5, 6, 4, 9, 8, 12,
17, 34, 11 };
public static void main(String[] args) {
buildMaxHeapify(sort);
heapSort(sort);
print(sort);
}
private static void buildMaxHeapify(int[] data) {
// 没有子节点的才需要创建最大堆,从最后一个的父节点开始
int startIndex = getParentIndex(data.length - 1);
// 从尾端开始创建最大堆,每次都是正确的堆
for (int i = startIndex; i >= 0; i--) {
maxHeapify(data, data.length, i);
}
}
/**
* 创建最大堆
*
* @param data
* @param heapSize
* 需要创建最大堆的大小,一般在sort的时候用到,因为最多值放在末尾,末尾就不再归入最大堆了
* @param index
* 当前需要创建最大堆的位置
*/
private static void maxHeapify(int[] data, int heapSize, int index) {
// 当前点与左右子节点比较
int left = getChildLeftIndex(index);
int right = getChildRightIndex(index);
int largest = index;
if (left < heapSize && data[index] < data[left]) {
largest = left;
}
if (right < heapSize && data[largest] < data[right]) {
largest = right;
}
// 得到最大值后可能需要交换,如果交换了,其子节点可能就不是最大堆了,需要重新调整
if (largest != index) {
int temp = data[index];
data[index] = data[largest];
data[largest] = temp;
maxHeapify(data, heapSize, largest);
}
}
/**
* 排序,最大值放在末尾,data虽然是最大堆,在排序后就成了递增的
*
* @param data
*/
private static void heapSort(int[] data) {
// 末尾与头交换,交换后调整最大堆
for (int i = data.length - 1; i > 0; i--) {
int temp = data[0];
data[0] = data[i];
data[i] = temp;
maxHeapify(data, i, 0);
}
}
/**
* 父节点位置
*
* @param current
* @return
*/
private static int getParentIndex(int current) {
return (current - 1) >> 1;
}
/**
* 左子节点position 注意括号,加法优先级更高
*
* @param current
* @return
*/
private static int getChildLeftIndex(int current) {
return (current << 1) + 1;
}
/**
* 右子节点position
*
* @param current
* @return
*/
private static int getChildRightIndex(int current) {
return (current << 1) + 2;
}
private static void print(int[] data) {
int pre = -2;
for (int i = 0; i < data.length; i++) {
if (pre < (int) getLog(i + 1)) {
pre = (int) getLog(i + 1);
System.out.println();
}
System.out.print(data[i] + " |");
}
}
/**
* 以2为底的对数
*
* @param param
* @return
*/
private static double getLog(double param) {
return Math.log(param) / Math.log(2);
}
}
慢慢理解一下,还是容易明白的!
六、归并排序
归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。
首先考虑下如何将将二个有序数列合并。这个非常简单,只要从比较二个数列的第一个数,谁小就先取谁,取了后就在对应数列中删除这个数。然后再进行比较,如果有数列为空,那直接将另一个数列的数据依次取出即可。
[java]
view plaincopyprint?
package com.xtfggef.algo.sort;
public class SortTest {
// 将有序数组a[]和b[]合并到c[]中
static void MemeryArray(int a[], int n, int b[], int m, int c[]) {
int i, j, k;
i = j = k = 0;
while (i < n && j < m) {
if (a[i] < b[j])
c[k++] = a[i++];
else
c[k++] = b[j++];
}
while (i < n)
c[k++] = a[i++];
while (j < m)
c[k++] = b[j++];
}
public static void main(String[] args) {
int a[] = { 2, 7, 8, 10, 299 };
int b[] = { 5, 9, 14, 20, 66, 88, 92 };
int c[] = new int[a.length + b.length];
MemeryArray(a, 5, b, 7, c);
print(c);
}
private static void print(int[] c) {
for (int i = 0; i < c.length; i++) {
System.out.print(c[i] + " ");
}
}
}
可以看出合并有序数列的效率是比较高的,可以达到O(n)。解决了上面的合并有序数列问题,再来看归并排序,其的基本思路就是将数组分成二组A,B,如果这二组组内的数据都是有序的,那么就可以很方便的将这二组数据进行排序。如何让这二组组内数据有序了?可以将A,B组各自再分成二组。依次类推,当分出来的小组只有一个数据时,可以认为这个小组组内已经达到了有序,然后再合并相邻的二个小组就可以了。这样通过先递归的分解数列,再合并数列就完成了归并排序。下面是归并排序代码:
[java]
view plaincopyprint?
package com.xtfggef.algo.sort;
public class MergeSort {
private static void mergeSort(int[] data, int start, int end) {
if (end > start) {
int pos = (start + end) / 2;
mergeSort(data, start, pos);
mergeSort(data, pos + 1, end);
merge(data, start, pos, end);
}
}
private static void merge(int[] data, int start, int pos, int end) {
int len1 = pos - start + 1;
int len2 = end - pos;
int A[] = new int[len1 + 1];
int B[] = new int[len2 + 1];
for (int i = 0; i < len1; i++) {
A[i] = data[i + start - 1];
}
A[len1] = Integer.MAX_VALUE;
for (int i = 0; i < len2; i++) {
B[i] = data[i + pos];
}
B[len2] = Integer.MAX_VALUE;
int m = 0, n = 0;
for (int i = start - 1; i < end; i++) {
if (A[m] > B
) {
data[i] = B
;
n++;
} else {
data[i] = A[m];
m++;
}
}
}
private static void print(int[] data) {
for (int i = 0; i < data.length; i++) {
System.out.print(data[i] + " ");
}
}
public static void main(String args[]) {
int data[] = { 8, 16, 99, 732, 10, 1, 29, 66 };
print(data);
System.out.println();
mergeSort(data, 1, data.length);
print(data);
}
}
七、希尔排序(不稳定、O(nlogn))
d1 = n/2,d2 = d1/2 ...
举例一下:{9,8,7,6,5,4,3,2,1,0} 10个数,现分为5组(9,4),(8,3),(7,2),(6,1),(5,0),然后分别对每组进行直接插入排序得到:
(4,9),(3,8),(2,7),(1,6),(0,5),再将这5组分为2组(4,3,2,1,0),(9,8,7,6,5)分别对这两组进行直插排序,得:(0,1,2,3,4),(5,6,7,8,9)最终有序。
[java]
view plaincopyprint?
package com.xtfggef.algo.sort;
public class ShellSort {
static void shellsort(int[] a, int n) {
int i, j, temp;
int gap = 0;
while (gap <= n) {
gap = gap * 3 + 1;
}
while (gap > 0) {
for (i = gap; i < n; i++) {
j = i - gap;
temp = a[i];
while ((j >= 0) && (a[j] > temp)) {
a[j + gap] = a[j];
j = j - gap;
}
a[j + gap] = temp;
}
gap = (gap - 1) / 3;
}
}
static void print(int data[]) {
for (int i = 0; i < data.length; i++) {
System.out.print(data[i] + " ");
}
}
public static void main(String[] args) {
int data[] = { 2, 68, 7, 19, 1, 28, 66, 200 };
print(data);
System.out.println();
shellsort(data, data.length);
print(data);
}
}
八、其他排序
我们这里不详细介绍概念,堆的话,大家只要记得堆是一个完全二叉树(什么是完全二叉树,请不懂的读者去查资料),堆排序分为两种堆,大顶堆和小顶堆,大顶堆的意思就是堆顶元素是整个堆中最大的,小顶堆的意思就是堆顶元素是整个堆中最小的,满足:任何一非叶节点的关键字不大于或者不小于其左右孩子节点的关键字。堆排序是一个相对难理解的过程,下面我会较为清楚、详细的讲解一下堆排序。堆排序分为三个过程:
建堆:从一个数组顺序读取元素,建立一个堆(完全二叉树)
初始化:将堆进行调整,使得堆顶为最大(最大堆)或者最小(最小堆)的元素
维护:将堆顶元素出堆后,需要将堆的最后一个节点补充到堆顶,因为这样破坏了堆的秩序,所以需要进行维护。下面我们图示一下:
一般情况,建堆和初始化同步进行,
最后为如下所示,即为建堆、初始化成功。
我们可以观察下这个最大堆,看出堆顶是整个堆中最大的元素,而且除叶子节点外每个节点都大于其子节点。下面的过程就是当我们输出堆顶元素后,对堆进行维护。
过程是这样:将堆顶元素出堆后,用最后一个元素补充堆顶元素,这样破坏了之前的秩序,需要重新维护堆,在堆顶元素的左右节点中选出较小的和堆顶互换,然后一直递归下去,所以每次出一个元素,需要一次维护,堆排序适合解决topK问题,能将复杂度降到nlogK。下面是代码:
[java]
view plaincopyprint?
package com.xtfggef.algo.sort;
public class HeapSort {
private static int[] sort = new int[] { 1, 0, 10, 20, 3, 5, 6, 4, 9, 8, 12,
17, 34, 11 };
public static void main(String[] args) {
buildMaxHeapify(sort);
heapSort(sort);
print(sort);
}
private static void buildMaxHeapify(int[] data) {
// 没有子节点的才需要创建最大堆,从最后一个的父节点开始
int startIndex = getParentIndex(data.length - 1);
// 从尾端开始创建最大堆,每次都是正确的堆
for (int i = startIndex; i >= 0; i--) {
maxHeapify(data, data.length, i);
}
}
/**
* 创建最大堆
*
* @param data
* @param heapSize
* 需要创建最大堆的大小,一般在sort的时候用到,因为最多值放在末尾,末尾就不再归入最大堆了
* @param index
* 当前需要创建最大堆的位置
*/
private static void maxHeapify(int[] data, int heapSize, int index) {
// 当前点与左右子节点比较
int left = getChildLeftIndex(index);
int right = getChildRightIndex(index);
int largest = index;
if (left < heapSize && data[index] < data[left]) {
largest = left;
}
if (right < heapSize && data[largest] < data[right]) {
largest = right;
}
// 得到最大值后可能需要交换,如果交换了,其子节点可能就不是最大堆了,需要重新调整
if (largest != index) {
int temp = data[index];
data[index] = data[largest];
data[largest] = temp;
maxHeapify(data, heapSize, largest);
}
}
/**
* 排序,最大值放在末尾,data虽然是最大堆,在排序后就成了递增的
*
* @param data
*/
private static void heapSort(int[] data) {
// 末尾与头交换,交换后调整最大堆
for (int i = data.length - 1; i > 0; i--) {
int temp = data[0];
data[0] = data[i];
data[i] = temp;
maxHeapify(data, i, 0);
}
}
/**
* 父节点位置
*
* @param current
* @return
*/
private static int getParentIndex(int current) {
return (current - 1) >> 1;
}
/**
* 左子节点position 注意括号,加法优先级更高
*
* @param current
* @return
*/
private static int getChildLeftIndex(int current) {
return (current << 1) + 1;
}
/**
* 右子节点position
*
* @param current
* @return
*/
private static int getChildRightIndex(int current) {
return (current << 1) + 2;
}
private static void print(int[] data) {
int pre = -2;
for (int i = 0; i < data.length; i++) {
if (pre < (int) getLog(i + 1)) {
pre = (int) getLog(i + 1);
System.out.println();
}
System.out.print(data[i] + " |");
}
}
/**
* 以2为底的对数
*
* @param param
* @return
*/
private static double getLog(double param) {
return Math.log(param) / Math.log(2);
}
}
package com.xtfggef.algo.sort; public class HeapSort { private static int[] sort = new int[] { 1, 0, 10, 20, 3, 5, 6, 4, 9, 8, 12, 17, 34, 11 }; public static void main(String[] args) { buildMaxHeapify(sort); heapSort(sort); print(sort); } private static void buildMaxHeapify(int[] data) { // 没有子节点的才需要创建最大堆,从最后一个的父节点开始 int startIndex = getParentIndex(data.length - 1); // 从尾端开始创建最大堆,每次都是正确的堆 for (int i = startIndex; i >= 0; i--) { maxHeapify(data, data.length, i); } } /** * 创建最大堆 * * @param data * @param heapSize * 需要创建最大堆的大小,一般在sort的时候用到,因为最多值放在末尾,末尾就不再归入最大堆了 * @param index * 当前需要创建最大堆的位置 */ private static void maxHeapify(int[] data, int heapSize, int index) { // 当前点与左右子节点比较 int left = getChildLeftIndex(index); int right = getChildRightIndex(index); int largest = index; if (left < heapSize && data[index] < data[left]) { largest = left; } if (right < heapSize && data[largest] < data[right]) { largest = right; } // 得到最大值后可能需要交换,如果交换了,其子节点可能就不是最大堆了,需要重新调整 if (largest != index) { int temp = data[index]; data[index] = data[largest]; data[largest] = temp; maxHeapify(data, heapSize, largest); } } /** * 排序,最大值放在末尾,data虽然是最大堆,在排序后就成了递增的 * * @param data */ private static void heapSort(int[] data) { // 末尾与头交换,交换后调整最大堆 for (int i = data.length - 1; i > 0; i--) { int temp = data[0]; data[0] = data[i]; data[i] = temp; maxHeapify(data, i, 0); } } /** * 父节点位置 * * @param current * @return */ private static int getParentIndex(int current) { return (current - 1) >> 1; } /** * 左子节点position 注意括号,加法优先级更高 * * @param current * @return */ private static int getChildLeftIndex(int current) { return (current << 1) + 1; } /** * 右子节点position * * @param current * @return */ private static int getChildRightIndex(int current) { return (current << 1) + 2; } private static void print(int[] data) { int pre = -2; for (int i = 0; i < data.length; i++) { if (pre < (int) getLog(i + 1)) { pre = (int) getLog(i + 1); System.out.println(); } System.out.print(data[i] + " |"); } } /** * 以2为底的对数 * * @param param * @return */ private static double getLog(double param) { return Math.log(param) / Math.log(2); } }
慢慢理解一下,还是容易明白的!
六、归并排序
归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。
首先考虑下如何将将二个有序数列合并。这个非常简单,只要从比较二个数列的第一个数,谁小就先取谁,取了后就在对应数列中删除这个数。然后再进行比较,如果有数列为空,那直接将另一个数列的数据依次取出即可。
[java]
view plaincopyprint?
package com.xtfggef.algo.sort;
public class SortTest {
// 将有序数组a[]和b[]合并到c[]中
static void MemeryArray(int a[], int n, int b[], int m, int c[]) {
int i, j, k;
i = j = k = 0;
while (i < n && j < m) {
if (a[i] < b[j])
c[k++] = a[i++];
else
c[k++] = b[j++];
}
while (i < n)
c[k++] = a[i++];
while (j < m)
c[k++] = b[j++];
}
public static void main(String[] args) {
int a[] = { 2, 7, 8, 10, 299 };
int b[] = { 5, 9, 14, 20, 66, 88, 92 };
int c[] = new int[a.length + b.length];
MemeryArray(a, 5, b, 7, c);
print(c);
}
private static void print(int[] c) {
for (int i = 0; i < c.length; i++) {
System.out.print(c[i] + " ");
}
}
}
package com.xtfggef.algo.sort; public class SortTest { // 将有序数组a[]和b[]合并到c[]中 static void MemeryArray(int a[], int n, int b[], int m, int c[]) { int i, j, k; i = j = k = 0; while (i < n && j < m) { if (a[i] < b[j]) c[k++] = a[i++]; else c[k++] = b[j++]; } while (i < n) c[k++] = a[i++]; while (j < m) c[k++] = b[j++]; } public static void main(String[] args) { int a[] = { 2, 7, 8, 10, 299 }; int b[] = { 5, 9, 14, 20, 66, 88, 92 }; int c[] = new int[a.length + b.length]; MemeryArray(a, 5, b, 7, c); print(c); } private static void print(int[] c) { for (int i = 0; i < c.length; i++) { System.out.print(c[i] + " "); } } }
可以看出合并有序数列的效率是比较高的,可以达到O(n)。解决了上面的合并有序数列问题,再来看归并排序,其的基本思路就是将数组分成二组A,B,如果这二组组内的数据都是有序的,那么就可以很方便的将这二组数据进行排序。如何让这二组组内数据有序了?可以将A,B组各自再分成二组。依次类推,当分出来的小组只有一个数据时,可以认为这个小组组内已经达到了有序,然后再合并相邻的二个小组就可以了。这样通过先递归的分解数列,再合并数列就完成了归并排序。下面是归并排序代码:
[java]
view plaincopyprint?
package com.xtfggef.algo.sort;
public class MergeSort {
private static void mergeSort(int[] data, int start, int end) {
if (end > start) {
int pos = (start + end) / 2;
mergeSort(data, start, pos);
mergeSort(data, pos + 1, end);
merge(data, start, pos, end);
}
}
private static void merge(int[] data, int start, int pos, int end) {
int len1 = pos - start + 1;
int len2 = end - pos;
int A[] = new int[len1 + 1];
int B[] = new int[len2 + 1];
for (int i = 0; i < len1; i++) {
A[i] = data[i + start - 1];
}
A[len1] = Integer.MAX_VALUE;
for (int i = 0; i < len2; i++) {
B[i] = data[i + pos];
}
B[len2] = Integer.MAX_VALUE;
int m = 0, n = 0;
for (int i = start - 1; i < end; i++) {
if (A[m] > B
) {
data[i] = B
;
n++;
} else {
data[i] = A[m];
m++;
}
}
}
private static void print(int[] data) {
for (int i = 0; i < data.length; i++) {
System.out.print(data[i] + " ");
}
}
public static void main(String args[]) {
int data[] = { 8, 16, 99, 732, 10, 1, 29, 66 };
print(data);
System.out.println();
mergeSort(data, 1, data.length);
print(data);
}
}
package com.xtfggef.algo.sort; public class MergeSort { private static void mergeSort(int[] data, int start, int end) { if (end > start) { int pos = (start + end) / 2; mergeSort(data, start, pos); mergeSort(data, pos + 1, end); merge(data, start, pos, end); } } private static void merge(int[] data, int start, int pos, int end) { int len1 = pos - start + 1; int len2 = end - pos; int A[] = new int[len1 + 1]; int B[] = new int[len2 + 1]; for (int i = 0; i < len1; i++) { A[i] = data[i + start - 1]; } A[len1] = Integer.MAX_VALUE; for (int i = 0; i < len2; i++) { B[i] = data[i + pos]; } B[len2] = Integer.MAX_VALUE; int m = 0, n = 0; for (int i = start - 1; i < end; i++) { if (A[m] > B ) { data[i] = B ; n++; } else { data[i] = A[m]; m++; } } } private static void print(int[] data) { for (int i = 0; i < data.length; i++) { System.out.print(data[i] + " "); } } public static void main(String args[]) { int data[] = { 8, 16, 99, 732, 10, 1, 29, 66 }; print(data); System.out.println(); mergeSort(data, 1, data.length); print(data); } }
七、希尔排序(不稳定、O(nlogn))
d1 = n/2,d2 = d1/2 ...
举例一下:{9,8,7,6,5,4,3,2,1,0} 10个数,现分为5组(9,4),(8,3),(7,2),(6,1),(5,0),然后分别对每组进行直接插入排序得到:
(4,9),(3,8),(2,7),(1,6),(0,5),再将这5组分为2组(4,3,2,1,0),(9,8,7,6,5)分别对这两组进行直插排序,得:(0,1,2,3,4),(5,6,7,8,9)最终有序。
[java]
view plaincopyprint?
package com.xtfggef.algo.sort;
public class ShellSort {
static void shellsort(int[] a, int n) {
int i, j, temp;
int gap = 0;
while (gap <= n) {
gap = gap * 3 + 1;
}
while (gap > 0) {
for (i = gap; i < n; i++) {
j = i - gap;
temp = a[i];
while ((j >= 0) && (a[j] > temp)) {
a[j + gap] = a[j];
j = j - gap;
}
a[j + gap] = temp;
}
gap = (gap - 1) / 3;
}
}
static void print(int data[]) {
for (int i = 0; i < data.length; i++) {
System.out.print(data[i] + " ");
}
}
public static void main(String[] args) {
int data[] = { 2, 68, 7, 19, 1, 28, 66, 200 };
print(data);
System.out.println();
shellsort(data, data.length);
print(data);
}
}
package com.xtfggef.algo.sort; public class ShellSort { static void shellsort(int[] a, int n) { int i, j, temp; int gap = 0; while (gap <= n) { gap = gap * 3 + 1; } while (gap > 0) { for (i = gap; i < n; i++) { j = i - gap; temp = a[i]; while ((j >= 0) && (a[j] > temp)) { a[j + gap] = a[j]; j = j - gap; } a[j + gap] = temp; } gap = (gap - 1) / 3; } } static void print(int data[]) { for (int i = 0; i < data.length; i++) { System.out.print(data[i] + " "); } } public static void main(String[] args) { int data[] = { 2, 68, 7, 19, 1, 28, 66, 200 }; print(data); System.out.println(); shellsort(data, data.length); print(data); } }
八、其他排序
相关文章推荐
- Java之美[从菜鸟到高手演变]之常见的几种排序算法-插入、选择、冒泡、快排、堆排等
- Java之美[从菜鸟到高手演变]之常见的几种排序算法-插入、选择、冒泡、快排、堆排等 .
- Java之美[从菜鸟到高手演变]之常见的几种排序算法-插入、选择、冒泡、快排、堆排等---转载
- Java之美[从菜鸟到高手演变]之常见的几种排序算法-插入、选择、冒泡、快排、堆排等 .
- Java之美[从菜鸟到高手演变]之常见的几种排序算法-插入、选择、冒泡、快排、堆排等
- Java之美[从菜鸟到高手演变]之常见的几种排序算法-插入、选择、冒泡、快排、堆排等
- Java之美[从菜鸟到高手演变]之常见的几种排序算法-插入、选择、冒泡、快排、堆排等
- Java之美[从菜鸟到高手演变]之常见的几种排序算法-插入、选择、冒泡、快排、堆排等
- Java之美[从菜鸟到高手演变]之常见的几种排序算法-插入、选择、冒泡、快排、堆排等
- Java之美【从菜鸟到高手】之常见的几种排序算法-插入、选择、冒泡、快排
- Java之常见的几种排序算法-插入、选择、冒泡、快排、堆排等
- Java常见的几种排序算法-插入、选择、冒泡、快排、堆排等
- java之美之常见的几种排序算法 插入 选择 冒泡 快排 堆排
- Java常见的几种排序算法-插入、选择、冒泡、快排、堆排等
- Java之美之常见的几种排序算法-插入、选择、冒泡、快排、堆排等
- Java常见的几种排序算法-插入、选择、冒泡、快排、堆排等
- Java 常见的几种排序算法-插入、选择、冒泡、快排、堆排等
- Java之常见的几种排序算法-插入、选择、冒泡、快排、堆排等 .
- Java中常见的数组排序算法(包括冒泡,选择,插入,快速排序)
- 常见的几种排序算法-插入、选择、冒泡、快排、堆排等