十大基础排序算法总结
Posted 小马Mark
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了十大基础排序算法总结相关的知识,希望对你有一定的参考价值。
冒泡排序
冒泡排序算法的原理如下:
- 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
- 对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
- 针对所有的元素重复以上的步骤,除了最后一个。
- 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
/*
* 冒泡排序
* */
import java.util.Arrays;
public class BubbleSort {
public static void main(String[] args) {
int[] arr = { 1, 4, 5, 5, 67, 3, 2, 5, 6, 8, 10 };
// sort1(arr);
sort2(arr);
System.out.println(Arrays.toString(arr));
}
private static void sort2(int[] arr) {
// 冒泡排序(写法二)
for (int i = arr.length; i >= 0; i--) {
for (int j = 0; j < i - 1; j++) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
private static void sort1(int[] arr) {
// 冒泡排序(写法一)
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr.length - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
}
选择排序
思路:
首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
import java.util.Arrays;
/*
* 选择排序
* */
public class SelectSort {
public static void main(String[] args) {
int[] arr = { 1, 11, 4, 5, 5, 67, 3, 2, 5, 6, 8, 10 };
// sort1(arr);
sort2(arr);
System.out.println(Arrays.toString(arr));
}
// 写法一(递增排序)
private static void sort2(int[] arr) {
for (int i = arr.length; i > 0; i--) {
int index = 0; // 最大值索引
int max = arr[0]; // 最大值
for (int j = 0; j < i; j++) { // 找到前n-1个数的最大值和索引位置
if (arr[j] > max) {
index = j;
max = arr[j];
}
}
// 把前n-1个数最大值放在数组后面
int temp = arr[i - 1];
arr[i - 1] = max;
arr[index] = temp;
}
}
// 写法二(递增排序)
private static void sort1(int[] arr) {
for (int i = 0; i < arr.length; i++) {
int index = i;
int min = arr[i];
for (int j = i; j < arr.length; j++) {
if (arr[j] < min) {
index = j;
min = arr[j];
}
}
int temp = arr[i];
arr[i] = min;
arr[index] = temp;
}
}
}
插入排序
基本思路:
插入排序是指在待排序的元素中,假设前面n-1(其中n>=2)个数已经是排好顺序的,现将第n个数插到前面已经排好的序列中,然后找到合适自己的位置,使得插入第n个数的这个序列也是排好顺序的。按照此法对所有元素进行插入,直到整个序列排为有序的过程,称为插入排序
/*
* 插入排序
* */
import java.util.Arrays;
public class InsertSort {
public static void main(String[] args) {
int[] arr = { 1, 11, 4, 5, 5, 67, 3, 2, 5, 6, 8, 10 };
sort(arr);
System.out.println(Arrays.toString(arr));
}
private static void sort(int[] arr) {
for (int i = 0; i < arr.length; i++) {
int value = arr[i]; // 拿到每一张牌,即将要插入的数
int lastIndex = i - 1;
// 依次与前面的数进行比较
while (lastIndex > -1 && value <arr[lastIndex]) { // 注意这里下标越界问题
arr[lastIndex + 1] = arr[lastIndex];
lastIndex--;
}
arr[lastIndex + 1] = value; // 将牌插入,即插入元素
}
}
}
希尔排序
希尔排序(Shell’s Sort)是插入排序的一种,又称“缩小增量排序”(Diminishing Increment Sort),是直接插入排序算法的一种更高效的改进版本。希尔排序是非稳定排序算法。该方法因 D.L.Shell 于 1959 年提出而得名。
上图来源https://www.cnblogs.com/chengxiao/p/6104371.html
/*
* 希尔排序
* */
import java.util.Arrays;
public class ShellSort {
public static void main(String[] args) {
int[] arr = { 1, 11, 4, 0, 5, 67, 3, 2, 5, 6, 8, 10 };
sort(arr);
System.out.println(Arrays.toString(arr));
}
private static void sort(int[] arr) {
for (int gap = arr.length / 2; gap > 0; gap = gap / 2) { // 设置每一次的增量(增量依次减半)
// 直接插入排序(每组进行插入排序,互不干扰)
for (int i = gap; i < arr.length; i++) {
int target = arr[i];
int j = i - gap;
while (j > -1 && target < arr[j]) {
arr[j + gap] = arr[j];
j -= gap;
}
arr[j + gap] = target;
}
}
}
}
归并排序
归并排字(Merge Sort)算法完全依照了分治模式
- 分解 : 将n个元秦分成各含n / 2个元素的子序列
- 解决 : 对两个子序列递归地排序
- 合并 : 合并两个已排序的子序列以得到排序结果
import java.util.Arrays;
import java.util.Random;
/*
* 归并排序
* */
public class M {
public static void main(String[] args) {
int[] arr = new int[15];
Random rd = new Random();
for (int i = 0; i < arr.length; i++) {
arr[i] = rd.nextInt(20);
}
System.out.println(Arrays.toString(arr));
mergeSort(arr, 0, arr.length - 1);
System.out.println(Arrays.toString(arr));
}
private static void mergeSort(int[] arr, int left, int right) {
if (left < right) {
int mid = left + ((right - left) >> 1);
mergeSort(arr, left, mid);
mergeSort(arr, mid + 1, right);
merge(arr, left, mid, right); // 合并
}
}
private static void merge(int[] arr, int low, int mid, int high) {
int[] helper = Arrays.copyOf(arr, arr.length); // 辅助数组容器(容器可以放在外面,以防止多次创建)
int current = low; // 注意这里需要指向数组当前所划分的区域的最小位置
int left = low;
int right = mid + 1;
while (left <= mid && right <= high) {
if (helper[left]<=helper[right]) {
arr[current] = helper[left];
left++;
}else {
arr[current] = helper[right];
right++;
}
current++;
}
// 这里只考虑左半部分的数是否有还没有放入原数组中,右半部分本身就是较大的数所以会在元素中的末尾部分,无需考虑
while (left<=mid) {
arr[current] = helper[left];
left++;
current++;
}
}
}
快速排序
思想:
- 分解 : 数组A[p…r]被划分为两个子数组A[p…q-1]和A [q+1,r],使得A[q]为大小居中的数,左侧A [p…q-1]中的每个元素都小于等于它,而右侧A [ q+1,r]中的每个元素都大于等于它。其中计算下标q也是划分过程的一部分。
- 解决 : 通过递归调用快速排序,对子数组A[p…q-1]和A [q+1,r]进行排序。
- 合并 : 为子数组都是原址排序的,所以不需要合并,数组A[p…r]已经有序。
关键点:在于划分问题。
单向扫描分区法
一遍扫描法的思路是,用两个指针将数组划分为三个区间,
扫描指针(sp)左边是确认小于等于主元的,
扫描指针到某个指针(next_bigger_pos)中间为未知的
因此我们将第二个指针(next bigger pos)称为未知区间末指针,末指针的右边区间为确认大于主元的元素
import java.util.Arrays;
import java.util.Random;
/*
* 快排:单向扫描分区法
* */
public class Q1 {
public static void main(String[] args) {
// int[] arr = { 9, 10, 7, 8, 3, 6, 3, 11, 2, 1, 4, 5, 13, 12 };
int[] arr = new int[15];
Random rd = new Random();
for (int i = 0; i < arr.length; i++) {
arr[i] = rd.nextInt(20);
}
System.out.println(Arrays.toString(arr));
quickSort(arr, 0, arr.length - 1);
System.out.println(Arrays.toString(arr));
}
private static void quickSort(int[] arr, int p, int r) {
if (p < r) {
int q = partition(arr, p, r); // 划分,将索引q的左边变为都是小于arr[q]的数,右边变为都是大于arr[q]的数
quickSort(arr, p, q - 1); // 左边排序
quickSort(arr, q + 1, r); // 右边排序
}
}
private static int partition(int[] arr, int p, int r) {
int first = arr[p]; // 将区间的第一个元素作为划分比较数
int sp = p + 1; // 扫描指针,从第二元素开始
int bigger = r; // 最右边的数的索引
while (sp <= bigger) {
if (arr[sp]<first) { // 小于比较数,扫描指针继续右移,数据不变
sp++;
}
else { // 大于等于比较数,先交换sp和bigger的数,然后最右边的指针向左移
swap(arr, sp, bigger);
bigger--;
}
}
swap(arr, p, bigger); // 将比较数(主区数)放在中间划分位置
return bigger;
}
private static void swap(int[] arr, int a, int b) {
// 交换数组中的两个数的位置
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
}
双向扫描分区法
思路:
- 头尾指针往中间扫描
- 从左找到大于主元的元素
- 从右找到小于等于主元的元素
- 二者交换
- 继续扫描,直到左侧无大元素,右侧无小元素
private static void quickSort(int[] arr, int p, int r) {
if (p < r) {
int q = partition(arr, p, r);
quickSort(arr, p, q - 1);
quickSort(arr, q + 1, r);
}
}
private static int partition(int[] arr, int p, int r) {
int first = arr[p];
int left = p + 1; // 左指针
int right = r; // 右指针
while (left <= right) {
while (left <= right && arr[left] <=first)
// (left <= right):在进行比较的过程中,防止左指针一直右移超过右指针(极端)
left++;
while (left <= right && arr[right] > first)
// (left <= right):在进行比较的过程中,防止右指针一直左移超过左指针
right--;
if (left < right) { // 防止指针溢出,还进行数组索引报错
swap(arr, left, right);
}
}
swap(arr, p, right);
return right;
}
private static void swap(int[] arr, int a, int b) {
int temp = arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
三指针分区法
有相同元素值的快速排序
private static void quickSort(int[] arr, int p, int r) {
if (p < r) {
int[] q = partition(arr, p, r);
quickSort(arr, p, q[0] - 1);
quickSort(arr, q[1] + 1, r);
}
}
private static int[] partition(十大经典排序算法总结(归并排序)