Java七大排序算法(默认升序排列)
1.冒泡排序(稳定)
属于交换类排序,比较两两相邻的元素,把大的放在右边。注意每一趟过后遍历中的最大元素都会放到最后边,注意遍历范围。这里放入代码
class Solution {
public int[] bubblingSort(int[] nums) {
if (nums == null || nums.length <= 1) return nums;
int n = nums.length;
for (int i = 0; i < n; i++) {
for (int j = 1; j < n - i; j++) { // 注意这里需要减去i
// 如果左边的大于右边的值 则交换
if (nums[j] < nums[j - 1]) {
int t = nums[j];
nums[j] = nums[j - 1];
nums[j - 1] = t;
}
}
}
return nums;
}
}
}
2.插入排序(稳定)
插入排序是在一个已经有序的小序列的基础上,一次插入一个元素,默认开始的第一个元素是有序的。这插入代码,详细看注释
class Solution {
public int[] sort(int[] nums) {
if (nums == null || nums.length <= 1) return nums;
int index; // 待插入索引
int target;
for (int i = 1; i < nums.length; i++) {
index = i;
target = nums[i]; // 先把待插入的数保存起来
// 寻找待插入的位置
while (index > 0 && target < nums[index - 1]) { // 必须判断边界条件
nums[index] = nums[index - 1];
index--;
}
nums[index] = target; // 插入
}
return nums;
}
}
3.选择排序(不稳定)
选择排序是给每个位置选择当前元素最小的,第一趟给第一个位置选择最小的,需要交换,第二趟需要个第二个位置选择第二小的,因为每次都存在交换,所以不稳定。这里给出具体代码。
class Solution{
public void selectSort(int[] nums) {
if (nums == null || nums.length <= 1) return;
int tmp = 0;
for (int i = 0; i < nums.length - 1; i++) {
int min = i;// 最小值的索引
// 往后寻找最小值的索引
for (int j = i + 1; j < nums.length; j++) { // 注意起始边界条件
if (nums[j] < nums[min]) {
min = j; // 保存索引
}
}
// 如果不相等 交换
if (min != i) {
tmp = nums[i];
nums[i] = nums[min];
nums[min] = tmp;
}
}
}
}
4.计数排序(不稳定)
计数排序要求数组元素都为大于等于0的数,以空间换时间,主要是需要开辟一个新的数组,存储元素出现的次数。
class Solution{
public int[] countSort(int[] arr) {
if (arr == null || arr.length <= 1) return arr;
int max = arr[0], min = arr[0];
// 找到最大值和最小值
for (int i = 0; i < arr.length; i++) {
if (max < arr[i]) max = arr[i];
if (min > arr[i]) min = arr[i];
}
int[] sortArr = new int[max - min + 1];
// 开始计数
for (int i = 0; i < arr.length; i++) {
sortArr[arr[i] - min] += 1;
}
// 计数索引
int index = 0;
for (int i = 0; i < sortArr.length; i++) {
// 直到把值变为0;
while (sortArr[i] > 0) {
arr[index++] = i + min;
sortArr[i]--;
}
}
return arr;
}
}
5.快速排序(不稳定)
采用分治递归的思想对冒泡排序的一种改进,每次和一个基准比较,小于该基准的放在左边,大于该基准的放在右边。给出详细代码。
class Solution{
public void fastSort(int[] nums, int low, int high) {
if (low >= high) return;
int i = low, j = high;
int t = 0;
int tmp = nums[low]; // 注意如果基准选的是最左边的数 那么后边的循环必须先看右边
// 如果基准选的是最右边的数 那么后边的循环必须先看左边;
while (i < j) {
// 这里这个while循环必须放在下一个while循环的前面,因为选择的是最左边的元素当基准
while (i < j && nums[j] >= tmp) {
j--;
}
while (i < j && nums[i] <= tmp) {
i++;
}
if (i < j) {
t = nums[i];
nums[i] = nums[j];
nums[j] = t;
}
}
nums[low] = nums[i];
nums[i] = tmp;
fastSort(nums, low, i - 1);
fastSort(nums, i + 1, high);
}
}
6.归并排序(稳定)
归并排序是把序列递归地分成短序列,递归出口是短序列只有1个元素(认为直接有序)或者2个序列(1次比较和交换),然后把各个有序的段序列合并成一个有序的长序列,不断合并直到原序列全部排好序。可以发现,在1个或2个元素时,1个元素不会交换,2个元素如果大小相等也没有人故意交换,这不会破坏稳定性。这里给出详细代码。
class Solution{
public int[] mergeSort(int[] arr, int low, int high) {
if (arr == null || arr.length <= 1) return arr;
if (low >= high) return arr;
int mid = (low + high) / 2;
if (low < high) {
// 左右分治
mergeSort(arr, low, mid);
mergeSort(arr, mid + 1, high);
// 归并整合
merge(arr, low, mid, high);
}
return arr;
}
private void merge(int[] arr, int low, int mid, int high) {
int[] tmp = new int[high - low + 1]; //临时数组
int i = low;
int j = mid + 1; // 注意这里必须加+1
int index = 0;
while (i <= mid && j <= high) {
if (arr[i] < arr[j]) { // 小的先赋值
tmp[index++] = arr[i++]; //注意所有索引++
} else {
tmp[index++] = arr[j++]; //注意所有索引++
}
}
while (i <= mid) {
tmp[index++] = arr[i++];
}
while (j <= high) {
tmp[index++] = arr[j++];
}
for (int t = 0; t < tmp.length; t++) {
arr[low + t] = tmp[t];
}
}
}
7.堆排序(不稳定)
堆分为大根堆和小根堆,是完全二叉树,如果对完全二叉树还不太了解的小伙伴可以先去看看完全二叉树的概念。大根堆的要求是每个节点的值都不大于其父节点的值,即A[PARENT[i]] >= A[i]。在数组的非降序排序中,需要使用的就是大根堆,因为根据大根堆的要求可知,最大的值一定在堆顶,然后依次把堆顶元素与最后一个元素交换,再对前面的部分重新heapify。这里给出详细代码。
class Solution{
@Test
public void testHeapSort() {
int[] tree = {4, 10, 3, 5, 1, 2};
int n = tree.length;
// heapify(tree, n, 0);
// buildHeap(tree, n);
heapSort(tree, n);
System.out.println(Arrays.toString(tree));
}
/**
* 堆排序
*
* @param tree 待排序的数组
* @param n 数组的长度
* @return 有无返回值都无所谓
*/
public int[] heapSort(int[] tree, int n) {
// 先判断特殊情况
if (tree == null || tree.length <= 1) return tree;
// 构建堆 从最后一个节点开始
buildHeap(tree, n);
// 开始排序
for (int i = n - 1; i >= 0; i--) {
// 堆顶元素和最后一个元素交换
swap(tree, i, 0);
// 前一部分数组重新heapify化
heapify(tree, i, 0); // 传入的是i 不是原始数组长度n
}
return tree;
}
/**
* 构建最大堆
*
* @param tree 数组
* @param n 数组长度
*/
public void buildHeap(int[] tree, int n) {
int parentNode = (n - 1) / 2; // 最后一个节点的父节点
// 从最后一个节点的父节点往前开始依次进行heapify
for (int i = parentNode; i >= 0; i--) {
heapify(tree, n, i);
}
}
/**
* heapify操作
*
* @param tree
* @param n 节点的个数
* @param i 当前父节点的位子
*/
public void heapify(int[] tree, int n, int i) {
if (i >= n) return;
// i是当前父节点 分别求出当前节点的左子节点和右子节点
int c1 = 2 * i + 1; // 左孩子节点
int c2 = 2 * i + 2; // 右孩子节点
int max = i; // 假设最大值节点为父节点
if (c1 < n && tree[c1] > tree[max]) max = c1;
if (c2 < n && tree[c2] > tree[max]) max = c2;
// 找到最大值的索引
if (max != i) { // 如果不相等则需要交换
swap(tree, i, max);
heapify(tree, n, max); // 继续往下边进行heapify
}
}
/**
* 交换数组中的两个袁术
*
* @param tree
* @param i
* @param j
*/
public void swap(int[] tree, int i, int j) {
int tmp = tree[i];
tree[i] = tree[j];
tree[j] = tmp;
}
}
堆排序不太懂的话可以推荐看看这个视频,哈哈,我也是搬运工,如果错误欢迎指正,我是小菜鸡一枚。