1.介绍
排序是一个在编程中间是非常基础的和重要的,网上也有很多有关排序算法的文章,再次我也是总结学习别人的知识,将别人的知识转为自己的知识,因此做此笔记,以便更好地掌握java常见排序算法。
2.插入算法
2.1 基本思想
每步将一个待排序的记录,按其顺序码大小插入到前面已经排序的字序列的合适位置(从后向前找到合适位置后),直到全部插入排序完为止。
2.2 实例
2.3 算法实现
public class SortSeries {
static int data[] = {57, 68, 59, 52};
public static void insertSort() {
int tmp, j = 0;
for (int k = 0; k < data.length; k++) {
tmp = data[k];
//比较前面所有的数字,如果有大于当前数字的,那个数字就往后移一位,空出来将temp插入
for (j = k - 1; j >= 0 && tmp < data[j]; j--) {
data[j + 1] = data[j];
}
//因为上面最后运行了一次j--,所有下面是data[j + 1]
data[j + 1] = tmp;
}
}
static void print() {
for (int i = 0; i < data.length; i++) {
System.out.print(data[i] + " ");
}
}
public static void main(String[] args) {
insertSort();
print();
}
}
结果:52 57 59 68
3.选择排序
3.1 基本思想
与直接插入排序正好相反,选择排序是从待排序的数中选出最小的放在已经排好的后面,这个算法选数耗时。
3.2 实例
3.3 算法实现
public class SortSeries {
static int data[] = {57, 68, 59, 52};
public static void selectSort() {
int i = 0;
int j, k, tmp = 0;
for (k = 0; k < data.length - 1; k++) {
//每次默认起始比较k下标就是待比较数字中最小值i的下标
i = k;
//从当前数字开始比较,选择出后面数字最小的小标,存入i值
for (j = k + 1; j < data.length; j++) {
if (data[j] < data[i]) {
i = j;
}
}
tmp = data[k];
//当前位置放入从当前到最末位置最小值,即进项数字交换
data[k] = data[i];
data[i] = tmp;
}
}
static void print() {
for (int i = 0; i < data.length; i++) {
System.out.print(data[i] + " ");
}
}
public static void main(String[] args) {
selectSort();
print();
}
}
结果:52 57 59 68
通过循环,找出最小的数的下标,赋值于i,即i永远保持待排序数据中最小的数的下标,最后和当前位置k互换数据即可。
4.快速排序
4.1 基本思想
通过一趟排序将待排序记录分割成独立的两部分,其中一部分记录的关键字均比另一部分关键字小,则分别对这两部分继续进行排序,直到整个序列有序。
4.2 实例
快排的思路:
设置两个指针:i和j,分别指向第一个和最后一个,i像后移动,j向前移动,选第一个数为标准(一般这样做,当然快排的关键就是这个“标准”的选取,每一轮比较这个标准值是不变的),从后面开始,找到第一个比标准小的数,互换位置,然后再从前面,找到第一个比标准大的数,互换位置,第一趟的结果就是标准左边的都小于标准,右边的都大于标准(但不一定有序),分成两拨后,继续递归的使用上述方法,最终有序!
4.3 算法实现
public class SortSeries {
/**
* 查找中轴(初始默认最低位作为中轴)所在位置
* @param numbers 排列数组
* @param low 最小起始下标
* @param high 最大起始下标
* @return
*/
public static int getMiddle(int[] numbers, int low, int high) {
int temp = numbers[low]; //数组的第一个作为中轴
while (low < high) {
while (low < high && numbers[high] > temp) {
high--;
}
numbers[low] = numbers[high];//比中轴小的记录移到低端
while (low < high && numbers[low] < temp) {
low++;
}
numbers[high] = numbers[low]; //比中轴大的记录移到高端
}
numbers[low] = temp; //中轴记录到尾
return low; // 返回中轴的位置
}
/**
* 递归形式的分治排序算法
* @param numbers
* @param low
* @param high
*/
public static void quickSort(int[] numbers, int low, int high) {
if (low < high) {
int middle = getMiddle(numbers, low, high); //将numbers数组进行一分为二
quickSort(numbers, low, middle - 1); //对低字段表进行递归排序
quickSort(numbers, middle + 1, high); //对高字段表进行递归排序
}
}
static void print(int[] numbers) {
for (int i = 0; i < numbers.length; i++) {
System.out.print(numbers[i] + " ");
}
}
public static void main(String[] args) {
int[] numbers = {20, 9, 17, 33, 49, 24, 15, 56, 1};
quickSort(numbers, 0, numbers.length - 1);
print(numbers);
}
}
结果:1 9 15 17 20 24 33 49 56
结合代码,我们分析一下一趟排序的过程,
待排列数组int[] numbers = {20, 9, 17, 33, 49, 24, 15, 56, 1},
首先选取20作为标准,然后比较标准20跟最后一位1,20>1,交换位置:1, 9, 17, 33, 49, 24, 15, 56, 20;
然后20从左往右找出第一个比它大的值33交换位置:
1, 9, 17, 20,49, 24, 15, 56, 33;
然后20从右往左找出第一个比它小的值15交换位置:
1, 9, 17, 15,49, 24, 20, 56, 33;
然后20从左往右找出第一个比它大的值49交换位置:
1, 9, 17, 15,20, 24, 49, 56, 33;
然后20从右往左找出比它小的值,此时没有了,从左往右找比它大的值也没有了,此时第一趟结束,然后以此对1, 9, 17, 15和24, 49, 56, 33重复该过程,知道最后有序!
5.冒泡排序
5.1 基本思想
冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
5.2 实例
5.3 算法实现
public class SortSeries {
static int[] data = {20, 9, 17, 33, 49, 24, 15, 56, 1};
public static void bubbleSort() {
int i, j, tmp = 0;
for (i = 0; i < data.length - 1; i++) {
for (j = data.length - 1; j > i; j--) {
if (data[j - 1] > data[j]) {
tmp = data[j - 1];
data[j - 1] = data[j];
data[j] = tmp;
}
}
}
}
static void print() {
for (int i = 0; i < data.length; i++) {
System.out.print(data[i] + " ");
}
}
public static void main(String[] args) {
bubbleSort();
print();
}
}
结果:1 9 15 17 20 24 33 49 56
6.希尔算法
6.1 基本思想
希尔排序(缩小增量法) 属于插入类排序,是将整个无序列分割成若干小的子序列分别进行插入排序。希尔排序是对直接插入排序算法的优化和升级。
6.2 实例
以数组{26, 53, 67, 48, 57, 13, 48, 32, 60, 50 }为例,步长序列为{5,2,1}
6.2 算法实现
public class SortSeries {
public static void shellsort(int[] data) {
int j, tmp = 0;
for (int increment = data.length / 2; increment > 0; increment /= 2) {
System.out.println("当前步长序列increment:" + increment);
for (int i = increment; i < data.length; i++) {
tmp = data[i];
for (j = i - increment; j >= 0; j -= increment) {
if (tmp < data[j]) {
data[j + increment] = data[j];
} else {
break;
}
}
//上面循环最后减去了increment,所以此时要加上
data[j + increment] = tmp;
}
for (int i = 0; i < data.length; i++) {
System.out.print(data[i] + " ");
}
System.out.println();
}
}
static void print(int[] data) {
System.out.println("-----最终排序结果-----");
for (int i = 0; i < data.length; i++) {
System.out.print(data[i] + " ");
}
}
public static void main(String[] args) {
int[] data = {26, 53, 67, 48, 57, 13, 48, 32, 60, 50};
shellsort(data);
print(data);
}
}
结果:
当前步长序列increment:5
13 48 32 48 50 26 53 67 60 57
当前步长序列increment:2
13 26 32 48 50 48 53 57 60 67
当前步长序列increment:1
13 26 32 48 48 50 53 57 60 67
-----最终排序结果-----
13 26 32 48 48 50 53 57 60 67
7.归并排序
7.1 基本思想
归并排序(Merge)是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。
7.2 工作原理
(1)申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列;
(2)设定两个指针,最初位置分别为两个已经排序序列的起始位置;
(3)比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置;
(4)重复步骤3直到某一指针达到序列尾;
(5)将另一序列剩下的所有元素直接复制到合并序列尾;
7.3 算法实现
public class MergeSortTest {
/**
* 归并排序
*
* @param nums 待排序数组
* @param low
* @param high
* @return 输出有序数组
*/
public static int[] sort(int[] nums, int low, int high) {
int mid = (low + high) / 2;
if (low < high) {
// 左边
sort(nums, low, mid);
// 右边
sort(nums, mid + 1, high);
// 左右归并
merge(nums, low, mid, high);
// System.out.println(Arrays.toString(nums));
}
return nums;
}
/**
* 将数组中low到high位置的数进行排序
*
* @param nums 待排序数组
* @param low 待排的开始位置
* @param mid 待排中间位置
* @param high 待排结束位置
*/
public static void merge(int[] nums, int low, int mid, int high) {
int[] temp = new int[high - low + 1];
int i = low;// 左指针
int j = mid + 1;// 右指针
int k = 0;
// 把较小的数先移到新数组中
while (i <= mid && j <= high) {
if (nums[i] < nums[j]) {
temp[k++] = nums[i++];
} else {
temp[k++] = nums[j++];
}
}
// 把左边剩余的数移入数组
while (i <= mid) {
temp[k++] = nums[i++];
}
// 把右边边剩余的数移入数组
while (j <= high) {
temp[k++] = nums[j++];
}
// 把新数组中的数覆盖nums数组
for (int k2 = 0; k2 < temp.length; k2++) {
nums[k2 + low] = temp[k2];
}
}
public static void main(String[] args) {
int a[] = {51, 46, 20, 18, 65, 97, 82, 30, 77, 50};
sort(a, 0, a.length - 1);
System.out.println("排序结果:" + Arrays.toString(a));
}
}
结果:
排序结果:[18, 20, 30, 46, 50, 51, 65, 77, 82, 97]
8.堆排序
参考文章:
Java之美[从菜鸟到高手演变]之常见的几种排序算法-插入、选择、冒泡、快排、堆排等
必须知道的八大种排序算法【java实现】(二) 选择排序,插入排序,希尔算法【详解】
【排序算法】希尔排序原理及Java实现