十大基础排序算法总结

Posted 小马Mark

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了十大基础排序算法总结相关的知识,希望对你有一定的参考价值。

冒泡排序

冒泡排序算法的原理如下:

  1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。
  2. 对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
  3. 针对所有的元素重复以上的步骤,除了最后一个。
  4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
/*
 * 冒泡排序
 * */
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++;
		}
	}
}

快速排序

思想:

  1. 分解 : 数组A[p…r]被划分为两个子数组A[p…q-1]和A [q+1,r],使得A[q]为大小居中的数,左侧A [p…q-1]中的每个元素都小于等于它,而右侧A [ q+1,r]中的每个元素都大于等于它。其中计算下标q也是划分过程的一部分。
  2. 解决 : 通过递归调用快速排序,对子数组A[p…q-1]和A [q+1,r]进行排序。
  3. 合并 : 为子数组都是原址排序的,所以不需要合并,数组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(十大经典排序算法总结(归并排序)

十大经典排序算法总结(桶排序)

十大经典排序算法总结(希尔排序)

十大经典排序算法总结(快速排序)

十大经典排序算法总结(冒泡排序)

十大经典排序算法总结(堆排序)