Java算法 - 排序算法
插入排序
思路简介
假定待排序数组长度为N,假设前n-1个数已经排好序,通过比较第n个数与前n-1个数的大小,确定第n个数的位置,通过移位的方式将第n个数插入到前n-1个有序数组中。边界条件就是n=2,只有一个数的时候默认是排好序的。
程序示例
package sort;
import java.util.Arrays;
public class InsertSort
{
private static int[] testcase = {
49, 38, 65, 97, 76, 13, 27, 78, 34, 12, 64,
5, 4, 62, 99, 98, 54, 56, 17, 18, 23, 34,
15
};
public static void Sort(int[] array)
{
for (int i = 1; i < array.length; i++)
{
int tmp = array[i];
int j = i - 1;
for (; j >= 0 && array[j] > tmp; j--)
{
array[j + 1] = array[j];
}
array[j + 1] = tmp;
}
}
public static void main(String[] args)
{
System.out.println("origin array:" + Arrays.toString(testcase));
Sort(testcase);
System.out.println("after sort:" + Arrays.toString(testcase));
}
}
希尔排序
思路简介
希尔排序是对插入排序的一种改良算法。希尔排序是将待排序数组的元素分组进行插入排序。我们使用gap来表示分组元素之间的间隔。标准的希尔排序算法,gap的取值是从1/2 length到1,其中迭代公式为gap/=2。
在插入排序的两重循环的基础上,我们还要添加一个循环用于迭代gap。
程序示例
package sort;
import java.util.Arrays;
public class ShellSort
{
private static int[] testcase = {
49, 38, 65, 97, 76, 13, 27, 78, 34, 12, 64,
5, 4, 62, 99, 98, 54, 56, 17, 18, 23, 34, 15
};
public static void Sort(int[] array)
{
for (int gap = array.length / 2; gap >= 1; gap /= 2)
{
for (int i = gap; i < array.length; i++)
{
int tmp = array[i];
int j = i - gap;
for (; j >= 0 && array[j] > tmp; j-=gap)
{
array[j + gap] = array[j];
}
array[j + gap] = tmp;
}
}
}
public static void main(String[] args)
{
System.out.println("origin array:" + Arrays.toString(testcase));
Sort(testcase);
System.out.println("after sort:" + Arrays.toString(testcase));
}
}
简单选择排序
思路简介
简单选择排序就是从待排序数组中选择一个最小的放在与数组的第一位进行交换,然后再从除第一位之外剩余的数中选择一个最小的与数组的第二组进行交换,直到数组的最后两位进行比较,是一个效率非常低的排序算法
程序示例
package sort;
import java.util.Arrays;
public class SelectSort
{
private static int[] testcase = {
49, 38, 65, 97, 76, 13, 27, 78, 34, 12, 64,
5, 4, 62, 99, 98, 54, 56, 17, 18, 23, 34, 15
};
public static void Sort(int[] array)
{
for (int i = 0; i < array.length - 1; i++)
{
int min = array[i];
int k = i;
for (int j = i + 1; j < array.length; j++)
{
if (array[j] < min)
{
min = array[j];
k = j;
}
}
array[k] = array[i];
array[i] = min;
}
}
public static void main(String[] args)
{
System.out.println("origin array:" + Arrays.toString(testcase));
Sort(testcase);
System.out.println("after sort:" + Arrays.toString(testcase));
}
}
堆排序
思路简介
堆排序是一种基于完全二叉树的选择排序。在介绍这个算法之前,首先要弄清楚大顶堆和顶堆的概念。
大顶堆:树中每个节点的值都比其子节点的值大。(用作从小到大)
小顶堆:树中每个节点的值都比其子节点的值小。(用作从大到小)
我们按照将树上的节点按照从上到小(从根),从左到右进行编号(从0开始),这些编号对应了待排序数组的下标,例如一个待排序数组为[9 8 7 6 5 4 3 2 1],将它表示为如下图所示的树
几个特殊的位置对应的数学表达式:
最后一个非叶子节点:length/2 - 1
一个节点i的左子节点:2 * i + 1
一个节点I的右子节点:2 * i + 2
有了上述基本概念,我们就可以开始理解堆排序了。堆排序分为两个步骤:
- 调整堆使堆满足大顶堆(小顶堆)的定义。(将最大(最小)的元素移到了根节点)
- 然后交换大顶堆(小顶堆)的根节点与堆的最后一个节点,堆的长度减一后,重新从根节点开始调整堆。重复步骤2直到堆的长度减为1。(将最大(最小)的元素移动了数组的末尾)
程序示例
package sort;
import java.util.Arrays;
public class HeapSort
{
private static int[] testcase = {
49, 38, 65, 97, 76, 13, 27, 78, 34, 12, 64,
5, 4, 62, 99, 98, 54, 56, 17, 18, 23, 34, 15
};
public static void Sort(int[] array)
{
//从最后一个非叶子节点调整堆,使其满足大顶端的定义
for (int i = array.length / 2 - 1; i >= 0; i--)
{
AdjustHeap(array, i, array.length);
}
//第二阶段,交换元素并调整堆
for (int i = array.length - 1; i > 0; i--)
{
swap(array, 0, i);
AdjustHeap(array, 0, i);
}
}
public static void AdjustHeap(int[] array, int i, int length)
{
int tmp = array[i];
for (int j = 2 * i + 1; j < length; j = 2 * j + 1)
{
if (array[j] < array[j + 1] && j + 1 < length)
{
j++;
}
if (array[j] > tmp)
{
array[i] = array[j];
i = j;
}
else {
break;
}
}
array[i] = tmp;
}
public static void swap(int[] array, int i, int j)
{
int tmp = array[i];
array[i] = array[j];
array[j] = tmp;
}
public static void main(String[] args)
{
System.out.println("origin array:" + Arrays.toString(testcase));
Sort(testcase);
System.out.println("after sort:" + Arrays.toString(testcase));
}
}
冒泡排序
思路简介
比较待排序的数组相邻两个元素,如何他们的顺序与排序要求不同,则交换两个元素。每一次冒泡可以将一个最大(最小)的元素移动到数组最后。总得次数取决的待排序数组的长度,次数=length - 1。
程序示例
package sort;
import java.util.Arrays;
public class BubbleSort
{
private static int[] testcase = {
49, 38, 65, 97, 76, 13, 27, 78, 34, 12, 64,
5, 4, 62, 99, 98, 54, 56, 17, 18, 23, 34, 15
};
public static void Sort(int[] array)
{
for (int i = 0; i < array.length - 1; i++)
{
for (int j = 0; j < array.length - 1 - i; j++)
{
if (array[j] > array[j + 1])
{
swap(array, j, j + 1);
}
}
}
}
public static void swap(int[] array, int i, int j)
{
int tmp = array[i];
array[i] = array[j];
array[j] = tmp;
}
public static void main(String[] args)
{
System.out.println("origin array:" + Arrays.toString(testcase));
Sort(testcase);
System.out.println("after sort:" + Arrays.toString(testcase));
}
}
快速排序
思路简介
快速排序使用了分治(device and conquer)的思想,将待排序的数组以某个数k为基准,分成左右两部分。以增序为例,k左边的数均比k小,k右边的数均比k大。然后递归地对这两部分使用快速排序。递归地结束条件是每个部分的数据长度为1。
难点在于如何在O(n)的时间里实现两部分的拆分。一般k取数组的第一个元素。
使用指针i和j分别指向数组的第一个元素和最后一个元素。
如果a[i] <= a[j] j--,直到找到了一个j,使a[i] > a[j],那么交换a[j]和a[i]。
如何a[i] <= a[j] i++,直到找到了一个i,使a[i] > a[j],那么交换a[j]和a[i]。
指针的终止条件是i < j 。
程序示例
package sort;
import java.util.Arrays;
public class QuickSort
{
private static int[] testcase = {
49, 38, 65, 97, 76, 13, 27, 78, 34, 12, 64,
5, 4, 62, 99, 98, 54, 56, 17, 18, 23, 34, 15
};
public static void Sort(int[] array)
{
quickSort(array, 0, array.length - 1);
}
public static void quickSort(int[] array, int start, int end)
{
if (start >= end)
{
return;
}
int i = start;
int j = end;
while (i != j)
{
while (array[i] <= array[j] && i < j)
j--;
swap(array, i, j);
while (array[i] <= array[j] && i < j)
i++;
swap(array, i, j);
}
quickSort(array, start, i - 1);
quickSort(array, i + 1, end);
}
public static void swap(int[] array, int i, int j)
{
int tmp = array[i];
array[i] = array[j];
array[j] = tmp;
}
public static void main(String[] args)
{
System.out.println("origin array:" + Arrays.toString(testcase));
Sort(testcase);
System.out.println("after sort:" + Arrays.toString(testcase));
}
}