【Java算法系列(一)】八大排序算法(上)

〇、排序算法简介 排序:将一组数据按照指定的顺序进行排列的过程。
排序的分类:

  • 内部排序:将需要处理的所有数据加载到内存中进行排序。
  • 外部排序:由于数据量过大无法全部加载到内存中,需要借助外存进行排序。
我们研究的排序算法主要是内部排序算法。其中内部排序又可以分为冒泡排序、简单选择排序(简称为选择排序)、直接插入排序(简称为插入排序)、希尔排序、快速排序、归并排序、基数排序、堆排序八大排序算法:
【Java算法系列(一)】八大排序算法(上)
文章图片

本篇将介绍冒泡排序、选择排序、插入排序、快速排序。下篇将介绍希尔排序、归并排序、基数排序、堆排序。
一、冒泡排序 基本思想:对待排序序列从前向后,依次比较相邻元素的值,若发现逆序则交换,使值较大的元素逐渐从前移向后部,就象水底下的气泡一样逐渐向上冒。
对于待排序数组[3, 9, -1, 10, 20],冒泡排序算法的图解过程如下:
【Java算法系列(一)】八大排序算法(上)
文章图片

由此可以写出冒泡排序算法的代码:
public static void bubbleSort(int[] arr) { for (int i = 0; i < arr.length - 1; i++) { for (int j = 0; j < arr.length - 1 - i; j++) { if (arr[j] > arr[j + 1]) { int temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } }

该代码可以优化:
public static void bubbleSort(int[] arr) { for (int i = 0; i < arr.length - 1; i++) { int temp; boolean flag = false; for (int j = 0; j < arr.length - 1 - i; j++) { if (arr[j] > arr[j + 1]) { flag = true; temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } if (!flag) { //优化 break; } } }

二、选择排序 基本思想:从待排序的数据中,按指定的规则选出某一元素,再依规定交换位置,达到排序。
对于待排序数组[3, 9, -1, 10, 20],选择排序算法的图解过程如下:
【Java算法系列(一)】八大排序算法(上)
文章图片

由此可以写出选择排序算法的代码:
public static void selectSort(int[] arr) { for (int i = 0; i < arr.length - 1; i++) { int minIndex = i; for (int j = i + 1; j < arr.length; j++) { if (arr[minIndex] > arr[j]) { minIndex = j; } } if (minIndex != i) { int temp = arr[minIndex]; arr[minIndex] = arr[i]; arr[i] = temp; } } }

三、插入排序 基本思想:对于待排序的元素,以插入的方式寻找该元素的适当位置,达到排序。
对于待排序数组[3, 9, -1, 10, 20],插入排序算法的图解过程如下:
【Java算法系列(一)】八大排序算法(上)
文章图片

由此可以写出插入排序算法的代码:
public static void insertSort(int[] arr) { for (int i = 1; i < arr.length; i++) { int insert = arr[i]; int j; for (j = i - 1; j >= 0; j--) { if (insert < arr[j]) { arr[j + 1] = arr[j]; } else { break; } } arr[j + 1] = insert; } }

该代码可以优化:
public static void insertSort(int[] arr) { for (int i = 1; i < arr.length; i++) { int insertVal = arr[i]; int insertIndex = i - 1; while (insertIndex >= 0 && insertVal < arr[insertIndex]) { arr[insertIndex + 1] = arr[insertIndex]; insertIndex--; } if (insertIndex + 1 == i) { //优化 arr[insertIndex + 1] = insertVal; } } }

四、快速排序 基本思想:首先设定一个分界值(主元),通过一趟排序(划分函数),将待排序数组分割成两部分,其中一部分的所有元素都比另外一部分的所有元素要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
主元的选取:主元可以选择首元素、尾元素、中间元素、随机选取、三点中值法、绝对中值法等。选择首元素作为主元往往是不经过充分考虑的。但为了方便起见,本篇选取首元素作为主元。
划分函数的选取:划分函数可以选择单向扫描分区法、双向扫描分区法等。本篇选取双向扫描分区法:选定主元后,确定左右“指针”扫描待排序数组;保证左指针在右指针的左边,当左指针元素不大于主元时不断向右推进,当右指针元素不小于主元时不断向左推进;直到左右指针相交,即为主元的位置。
【【Java算法系列(一)】八大排序算法(上)】由此可以写出快速排序算法的代码:
public static void quickSort(int[] arr, int left, int right) { int l = left; int r = right; int pivot = arr[left]; while (l <= r) { while (l <= r && arr[l] <= pivot) l++; //左指针元素不大于主元时不断向右推进 while (l <= r && arr[r] >= pivot) r--; //右指针元素不小于主元时不断向左推进 if (l <= r) { int temp = arr[l]; arr[l] = arr[r]; arr[r] = temp; } } arr[left] = arr[r]; //arr[r]即为主元的位置 arr[r] = pivot; if (left < r) quickSort(arr, left, r); if (right > l) quickSort(arr, l, right); }

    推荐阅读