今天总结了一下排序算法,在这里给出总结。主要以代码形式给出,没有涉及过多的解释。但是都比较简单,看代码注释也可以看懂。首先,来个综述:
这是百度上的一个总结,为什么这里说有10种排序算法呢,这是因为加上了计数排序和桶排序,这两种排序方法再加上基数排序是3种线性排序方法。是不基于比较实现的,其他的都是基于比较实现的,其时间复杂度最低为O(nlongn),此性质可以进行证明。
直接插入排序
package com.blog.sort;
import java.util.Arrays;
/**
* @Description: 直接插入排序 O(n^2) 其时间复杂度与输入有关 如果输入是有序的话,则时间复杂度为O(n)
* 稳定的排序算法
* @Author: Jingzeng Wang
* @Date: Created in 9:42 2017/8/26.
*/
public class InsertSort {
/**
* 直接插入排序:将数字插入到已经排序的序列中,使之还是有序的
* 第一个for: 依次插入的数
* 第二个: 将要插入的数与前i个数比较 (或 break 来结束不必要的比较)
* 此操作将算法的最优时间复杂度变为O(n)
*
* @param nums
*/
public static void insertSort(int[] nums) {
if (nums == null || nums.length <= 0) {
return;
}
for (int i = 0; i < nums.length; i++) {
for (int j = i; j > 0 && nums[j] < nums[j - 1]; j--) {
int temp = nums[j];
nums[j] = nums[j - 1];
nums[j - 1] = temp;
}
}
System.out.println(Arrays.toString(nums));
}
public static void main(String[] args) {
int[] array = {11, 12, 3, 8, 5, 1, 2, 3, 18};
insertSort(array);
}
}
希尔排序
package com.blog.sort;
import java.util.Arrays;
/**
* @Description: 希尔排序 其时间复杂度最坏情况下是O(n^2) 其时间复杂度与步长的选择有关 这也是关于希尔排序的优化点
* 插入排序一种改进方法 希尔排序的实质就是分组插入排序 缩小增量排序
* 插入排序在对几乎已经排好序的数据操作时, 效率高, 即可以达到线性排序的效率 插入排序一般来说是低效的, 因为插入排序每次只能将数据移动一位
* 它的基本思想是:对于n个待排序的数列,取一个小于n的整数gap(gap被称为步长)将待排序元素分成若干个组子序列,所有距离为gap的倍数的记录放在同一个组中;
* 然后,对各组内的元素进行直接插入排序。 这一趟排序完成之后,每一个组的元素都是有序的。然后减小gap的值,并重复执行上述的分组和排序。
* 重复这样的操作,当gap=1时,整个数列就是有序的。
* @Author: Jingzeng Wang
* @Date: Created in 11:35 2017/8/26.
*/
public class ShellSort {
/**
* 希尔排序代码可以简单写出:
* 第一个for: 来确定步长,直到步长为1 结束算法
* 第二个for: 是对每一个分组进行插入排序
* 第三个和第四个for就是对每个组的插入排序算法
*
* @param nums
*/
public static void shellSort(int[] nums) {
if (nums == null || nums.length <= 0) {
return;
}
//步长大小选择 gap也就是有几个组 当gap=1时结束
for (int gap = nums.length / 2; gap > 0; gap /= 2) {
//对每一个组都进行插入排序
for (int group = 0; group < gap; group++) {
//每一组的插入排序
for (int j = group; j < nums.length; j += gap) {
for (int k = j; k > group && nums[k] < nums[k - gap]; k -= gap) {
int temp = nums[k];
nums[k] = nums[k - gap];
nums[k - gap] = temp;
}
}
}
}
System.out.println(Arrays.toString(nums));
}
public static void main(String[] args) {
int[] array = {11, 12, 3, 8, 5, 1, 2, 3, 2};
shellSort(array);
}
}
直接选择排序
package com.blog.sort;
import java.util.Arrays;
/**
* @Description: 选择排序 O(n^2) 时间复杂度与输入无关 不稳定
* @Author: Jingzeng Wang
* @Date: Created in 9:52 2017/8/26.
*/
public class SelectSort {
/**
* 选择排序:从待排序的序列中选出最小的放在序列开始位置,直至所有的数都排列完
* 第一个for: 代表选出的最小的数,要替换的位置
* 第二个for: 代表比较循环,从i+1开始,比较其与i的数值大小关系
*
* @param nums
*/
public static void selectSort(int[] nums) {
if (nums == null || nums.length <= 0) {
return;
}
for (int i = 0; i < nums.length - 1; i++) {
for (int j = i + 1; j < nums.length; j++) {
if (nums[j] < nums[i]) {
int temp = nums[j];
nums[j] = nums[i];
nums[i] = temp;
}
}
}
System.out.println(Arrays.toString(nums));
}
public static void main(String[] args) {
int[] array = {8, 5, 6, 9, 1, 2, 2, 3, 11};
selectSort(array);
}
}
堆排序
package com.blog.sort;
import java.util.Arrays;
/**
* @Description: 堆排序 时间复杂度O(nlogn) 利用最大堆实现升序排序
* 其原理是利用最大最小堆这种数据结构实现,其堆顶是序列的最大(最小值)
* 堆是一种完全二叉树 一般用数组来实现。 存储是按照层次存储的
* @Author: Jingzeng Wang
* @Date: Created in 21:04 2017/8/26.
*/
public class HeapSort {
private int[] nums;
public HeapSort(int[] nums) {
this.nums = nums;
}
public void maxHeapSort() {
//先建立最大对
buildMaxHeap();
System.out.println(Arrays.toString(nums));
//排序 将堆顶元素放到最后一个,把最后一个放在根节点,然后再从根节点开始调整
//此过程与选择排序类似
for (int i = nums.length - 1; i > 0; i--) {
int temp = nums[0];
nums[0] = nums[i];
nums[i] = temp;
maxHeapFixDown(nums, 0, i);
}
}
/**
* 初始化时建立最大堆
* 把叶节点当做是已经符合最大堆特点了。所以建立最大堆时从最后一个非叶子节点开始调整
*/
public void buildMaxHeap() {
for (int i = nums.length / 2 - 1; i >= 0; i--) {
maxHeapFixDown(nums, i, nums.length);
}
}
/**
* 从index处调整,使之符合最大堆的性质
* 此调整相是适用于删除操作时的,删除操作时发生在堆顶,然后用最后一个节点替换到堆顶然后进行调整
* 插入操作与此相反,插入是首先插入到数组最后,然后向上调整
*
* @param nums 要调整的数组
* @param i 开始调整的索引
* @param length 要调整的数组长度
*/
public void maxHeapFixDown(int[] nums, int i, int length) {
int leftChild = 2 * i + 1;
int index = leftChild;
while (index <= length - 1) {
int rightChild = 2 * i + 2;
//找到那个最大的那个数的索引
if (rightChild <= length - 1 && nums[leftChild] < nums[rightChild]) {
index = rightChild;
}
//如果根节点大于子节点,则调整结束
if (nums[i] > nums[index]) {
break;
}
int temp = nums[i];
nums[i] = nums[index];
nums[index] = temp;
i = index;
leftChild = 2 * i + 1;
index = leftChild;
}
}
public static void main(String[] args) {
int[] array = {2, 12, 13, 8, 5, 1, 2, 4, 15, 3, 45};
HeapSort heapSort = new HeapSort(array);
heapSort.maxHeapSort();
System.out.println(Arrays.toString(heapSort.nums));
}
}
冒泡排序
package com.blog.sort;
import java.util.Arrays;
/**
* @Description: 冒泡排序 O(n^2) 可通过改进将最优的时间复杂度变为:O(n)
* 稳定的排序算法 排序的稳定性指的是是否具备原序列中相等的元素排序后相对位置不变的性质
* 判断稳定可以在脑海中过一遍算法流程
* @Author: Jingzeng Wang
* @Date: Created in 9:25 2017/8/26.
*/
public class BubbleSort {
public static void bubbleSort(int[] nums) {
if (nums == null || nums.length <= 0) {
return;
}
/**
* 第一层for:代表比较趟数,趟数为num.length-1
* 第二层for:代表每趟需要比较的数:每次只需比较num.length - i 次
* 冒泡排序的原理就是通过若干趟比较,把最大的沉入到最下边
* 可改进1:设置标志位,明显如果有一趟没有发生交换(flag = false),说明排序已经完成
* 此改进可以将冒泡排序的最佳时间变为:O(n)
*/
for (int i = 1; i < nums.length; i++) {
for (int j = 0; j < nums.length - i; j++) {
if (nums[j] > nums[j + 1]) {
int temp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = temp;
}
}
}
System.out.println(Arrays.toString(nums));
}
public static void main(String[] args) {
int[] array = {8, 5, 6, 9, 1, 2, 3, 11};
bubbleSort(array);
}
}
快速排序
归并排序
关于归并排序,之前也写过
归并排序原理及Java实现。
计数排序
package com.blog.sort;
import java.util.Arrays;
/**
* @Description: 计数排序 它与桶排序、基数排序都是线性排序 是不基于比较的排序方法 时间复杂度是线性的
* 其实是利用 hash原理,以空间换时间 还有限制 :就是排序数组的最大值为k
* 基本思想:计数排序要求数据的范围在0到k之间的整数,引入了一个辅助数组countArray,数组countArray的大小为k,存储了待排序数组中值小于等于countArray的索引值的个数。
* 1. 统计出待排序数组值为i的元素个数,存入辅助数组countArray的第i项中
* 2. 对countArray中的数据进行累加,每一项的值等于本项值加上前一项的值
* 3. 扫描待排序数组,每扫描一项m,将其存入新的数组的第countArray(m)项,对countArray(m)的值减一
* @Author: Jingzeng Wang
* @Date: Created in 22:17 2017/8/26.
*/
public class CountSort {
public static int[] countSort(int[] nums, int k) {
if (nums == null || nums.length <= 0 || k <= 0) {
return null;
}
int[] countArray = new int[k + 1]; //辅助数组 以待排序数组值作为数组索引
int[] array = new int[nums.length]; //排好序的数组
//统计每个元素的出现的次数
for (int i = 0; i < nums.length; i++) {
countArray[nums[i]]++;
}
//countArray 存储小于等于index的值的个数
for (int i = 1; i <= k; i++) {
countArray[i] += countArray[i - 1];
}
//扫描待排序的数组,将值放入array中
for (int i = 0; i < nums.length; i++) {
array[countArray[nums[i]] - 1] = nums[i];
countArray[nums[i]]--;
}
return array;
}
public static void main(String[] args) {
int[] array = {11, 12, 3, 8, 5, 1, 2, 3, 18};
System.out.println(Arrays.toString(countSort(array, 18)));
}
}
桶排序
基数排序
桶排序和基数排序这两种不基于比较的线性排序算法,没有写,可以参考:
点击打开链接。
面试中的重点当然是快排、归并和堆排序。可以自己写一下。