4、希尔排序
4.1 简单插入排序问题
简单的插入排序可能存在的问题,数组 arr = { 2, 3, 4, 5, 6, 1 } 这时需要插入的数 1(最小),简单插入排序的过程如下:
{2,3,4,5,6,6}
{2,3,4,5,5,6}
{2,3,4,4,5,6}
{2,3,3,4,5,6}
{2,2,3,4,5,6}
{1,2,3,4,5,6}
结论: 当需要插入的数是较小的数时, 后移的次数明显增多, 对效率有影响
4.2 希尔排序基本介绍
希尔排序是希尔(Donald Shell) 于 1959 年提出的一种排序算法。 希尔排序也是一种插入排序, 它是简单插入排序经过改进之后的一个更高效的版本, 也称为缩小增量排序。
4.3 希尔排序基本思想
希尔排序按照增量将数组进行分组,对每组使用直接插入排序算法排序;随着增量逐渐减少,每组包含的关键词越来越多,当增量减至 1 时,整个文件恰被分成一组,算法便终止
4.4 希尔排序图解(交换法)
4.5 理解希尔排序代码实现(交换法)
···
public class ShellSort {
public static void main(String[] args) {
int[] arr = { 8, 9, 1, 7, 2, 3, 5, 4, 6, 0 };
shellSort(arr);
}
private static void shellSort(int[] arr) {
int temp = 0;
// 希尔排序的第1轮排序
// 第1轮排序,是将10个数据分成了 5组
for (int i = 5; i < arr.length; i++) {
for (int j = i - 5; j >= 0; j-= 5) {
if (arr[j] > arr[j + 5]) {
temp = arr[j];
arr[j] = arr[j+ 5];
arr[j+ 5] = temp;
}
}
}
System.out.println("希尔排序1轮后=" + Arrays.toString(arr));
// 希尔排序的第2轮排序
// 第2轮排序,是将10个数据分成了 5/2 = 2组
for (int i = 2; i < arr.length; i++) {
for (int j = i - 2; j >= 0; j-= 2) {
if (arr[j] > arr[j + 2]) {
temp = arr[j];
arr[j] = arr[j+ 2];
arr[j+ 2] = temp;
}
}
}
System.out.println("希尔排序2轮后=" + Arrays.toString(arr));
// 希尔排序的第3轮排序
// 第3轮排序,是将10个数据分成了 2/2 = 1组
for (int i = 1; i < arr.length; i++) {
for (int j = i - 1; j >= 0; j-= 1) {
if (arr[j] > arr[j + 1]) {
temp = arr[j];
arr[j] = arr[j+ 1];
arr[j+ 1] = temp;
}
}
}
System.out.println("希尔排序3轮后=" + Arrays.toString(arr));
}
}
···
4.6 希尔排序最终实现(交换法)
public class ShellSort {
public static void main(String[] args) {
int[] arr = { 8, 9, 1, 7, 2, 3, 5, 4, 6, 0 };
shellSort(arr);
}
private static void shellSort(int[] arr) {
int temp = 0;
int count = 0;
for (int gap = arr.length /2; gap > 0; gap /= 2) {
// 遍历各组中所有的元素(共gap组,每组有?个元素), 步长gap
for (int i = gap; i < arr.length; i++) {
for (int j = i - gap; j >= 0; j-= gap) {
if (arr[j] > arr[j + gap]) {
temp = arr[j];
arr[j] = arr[j+ gap];
arr[j+ gap] = temp;
}
}
}
System.out.println("希尔排序第"+(++count)+"轮后=" + Arrays.toString(arr));
}
}
}
4.7 测试希尔排序(交换法)性能
public class ShellSort {
public static void main(String[] args) {
// 创建要给80000个的随机的数组
int[] arr = new int[80000];
for (int i = 0; i < 80000; i++) {
arr[i] = (int) (Math.random() * 8000000); // 生成一个[0, 8000000) 数
}
Date date1 = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String date1Str = simpleDateFormat.format(date1);
System.out.println("排序前的时间是=" + date1Str);
shellSort(arr); // 交换式
Date data2 = new Date();
String date2Str = simpleDateFormat.format(data2);
System.out.println("排序前的时间是=" + date2Str);
}
private static void shellSort(int[] arr) {
int temp = 0;
int count = 0;
for (int gap = arr.length /2; gap > 0; gap /= 2) {
// 遍历各组中所有的元素(共gap组,每组有?个元素), 步长gap
for (int i = gap; i < arr.length; i++) {
for (int j = i - gap; j >= 0; j-= gap) {
if (arr[j] > arr[j + gap]) {
temp = arr[j];
arr[j] = arr[j+ gap];
arr[j+ gap] = temp;
}
}
}
System.out.println("希尔排序第"+(++count)+"轮后=" + Arrays.toString(arr));
}
}
}
程序运行结果
排序前的时间是=2022-12-09 14:19:41
排序前的时间是=2022-12-09 14:19:47
缺点:基于交换法的希尔排序算法比简单选择排序算法速度更慢
4.8 编写希尔排序(插入法)
public class ShellSort {
public static void main(String[] args) {
int[] arr = { 8, 9, 1, 7, 2, 3, 5, 4, 6, 0 };
shellSort(arr);
}
public static void shellSort(int[] arr) {
// 增量gap, 并逐步的缩小增量
for (int gap = arr.length /2; gap >0; gap/=2) {
// 从第gap个元素,逐个对其所在的组进行直接插入排序
for (int i = gap; i < arr.length; i++) {
int j = i;
int temp = arr[j];
while (j - gap >= 0 && temp < arr[j - gap]) {
arr[j] = arr[j - gap];
j-= gap;
}
arr[j] = temp;
}
}
System.out.println("希尔排序后=" + Arrays.toString(arr));
}
}
4.9 测试希尔排序(插入法)性能
public class ShellSort {
public static void main(String[] args) {
// 创建要给80000个的随机的数组
int[] arr = new int[80000];
for (int i = 0; i < 80000; i++) {
arr[i] = (int) (Math.random() * 8000000); // 生成一个[0, 8000000) 数
}
Date date1 = new Date();
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String date1Str = simpleDateFormat.format(date1);
System.out.println("排序前的时间是=" + date1Str);
shellSort(arr);
Date data2 = new Date();
String date2Str = simpleDateFormat.format(data2);
System.out.println("排序前的时间是=" + date2Str);
}
public static void shellSort(int[] arr) {
// 增量gap, 并逐步的缩小增量
for (int gap = arr.length /2; gap >0; gap/=2) {
// 从第gap个元素,逐个对其所在的组进行直接插入排序
for (int i = gap; i < arr.length; i++) {
int j = i;
int temp = arr[j];
while (j - gap >= 0 && temp < arr[j - gap]) {
arr[j] = arr[j - gap];
j-= gap;
}
arr[j] = temp;
}
}
}
}
代码运行结果
排序前的时间是=2022-12-09 16:27:36
排序前的时间是=2022-12-09 16:27:36
5 快速排序
5.1 基本介绍
快速排序(Quicksort)是对冒泡排序的一种改进,借用了分治的思想,由东尼·霍尔在1962年提出。
通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列
5.2 快速排序图解
分步解释说明:
- 每次以最后一个元素array[high]为中心元素pivot
- pointer用于指向比中心元素大的元素的下标,从low开始