1.插入排序
基本思想:数组中有n个元素,那么我们先把第一个元素看成一个有序数列,将第2个元素插入到前面的有序数列中,直至将第n个元素插入到前面长度为n-1的有序数列中就实现了排序。时间复杂度为O(n^2);
2.希尔排序(递减增量排序算法)
基本思想:针对直接插入排序的效率问题(1.插入 排序在对几乎已经排好序的数据 操作时,效率高,即可以达到线性排序的效率;2.一般来说插入排序是低效的,每次只能将数据移动一位。),提出的改进与升级,希尔排序是非稳定排序算法。思想如下,数组中有n个元素,首先求出k=n/2,将下标差值为k的元素利用插入排序变为有序序列,再次求出k=k/2,将下标差值为k的元素利用插入排序变为有序序列,重复上述做法,直至k=1时,执行简单插入排序,这时数组的排序完成。3.简单选择排序
基本思想:将数组分为两个部分,第一部分为已排序数组(初始为空),第二部分为待排序数组(初始为整个数组),那么排序的过程为:第一次在待排序数组中找到最小(最大)的元素,放在已排序数组中,直至待排序数组为空,排序完成。尽管与冒泡排序同为O(n^2),但简单选择排序的性能要略优于冒泡排序。
4.冒泡排序
基本思想:将数组中的元素两两比较,找出最大的放到最右边(或者找出最小的放在最左边),也就是一次冒泡,下一次冒泡开始的时候,对已经找到位置的元素(已经确定顺序位置的元素)不参与本次的冒泡,直到参与冒泡的元素为0,则整个数组有序。
改进思路:设置标志位,明显如果有一趟没有发生交换(flag = false),说明排序已经完成。
5.快速排序
基本思想:在待排序数组中找到一个基准值(一般为数组的首元素),第一趟扫描,将比基准值小的放在基准值的左边,将比基准值大的放在基准值右边,这样基准值的位置就正确了,再用同样的方法递归基准值左右两个部分,直至整个数组有序。时间复杂度为O(nlogn) 。
6.归并排序(二路归并)
基本思想 :首先将原始的无序序列划分成两个子序列,然后分别对每个子序列递归进行排序,最后再将有序子列合并。二路归并排序是首先将初始序列的n个记录看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,得到[n/2]个长度为2(n为奇数时,最后一个序列的长度为1)的有序子序列。在此基础上,在对长度为2的有序子序列进行两两归并,得到若干个长度为4的有序子序列。以此类推,直至得到长度为n的有序序列。 时间复杂度
为O(nlogn),空间复杂度为O(n+logn),如果非递归实现归并,则避免了递归时深度为logn的栈空间,那么空间复杂度为O(n)。
各类排序算法的时间复杂度与空间复杂度对比表
7.代码实现
- 生成乱序数组的工具类
package com.norte.util;
import java.util.Random;
public class ArrayUtil {
/**
* @author Norte
* Date:2018-5-18
*
* 功能:
* 利用随机函数生成一个指定大小的随机数组
* */
public ArrayUtil() {
}
public int[] MakeArray(int n) {
int[] a = new int[n];
for(int i = 0; i < a.length; i++) {
Random random = new Random(); //产生一个Random实例,调用nextInt(int n)生成的随机数是介于0到n的int类型整数。
a[i] = random.nextInt(100);
// a[i] = (int) (Math.random() * 100); //Math.random()生成的随机数是介于0.0到1.0的double类型数值。
}
return a;
}
public static void main(String[] args) {
ArrayUtil arrayUtil = new ArrayUtil();
int[] a = arrayUtil.MakeArray(10);
for(int i = 0; i < a.length; i++) {
System.out.println("a[" + i +"]:" + a[i]);
}
}
}
- 实现各种排序算法的排序类
package com.norte.sort;
public class Sort {
/**
* @author Norte
* Date:2018-5-18
*
* 功能:直接插入排序
*
* 基本思想:数组中有n个元素,那么我们先把第一个元素看成一个有序数列,将第2个元素插入
* 到前面的有序数列中,直至将第n个元素插入到前面长度为n-1的有序数列中就实现了排序。
*
* */
public void insertSort(int[] arr) {
int len = arr.length; //单独拿出数组长度,提高效率
int tmp; //要插入有序数列的数
for(int i = 1; i < len; i++) { //第一个元素不需要插入,从第二个开始
tmp = arr[i];
int j = i - 1; //有序数列的元素个数
while(j >= 0 && tmp <= arr[j]) { //将有序数列的元素从后向前,将大于tmp的依次后移
arr[j+1] = arr[j];
j--;
}
arr[j+1] = tmp; //找到tmp的插入位置
}
}
/**
* @author Norte
* Date:2018-5-18
*
* 功能:希尔排序(递减增量排序算法)
*
* 基本思想:针对直接插入排序的效率问题(1.插入 排序在对几乎已经排好序的数据 操作时,效率高,即可以达到线性排序的效率;
* 2.一般来说插入排序是低效的,每次只能将数据移动一位。),提出的改进与升级,希尔排序是非稳定排序算法。思想如下,数组
* 中有n个元素,首先求出k=n/2,将下标差值为k的元素利用插入排序变为有序序列,再次求出k=k/2,将下标差值为k的元素利用插
* 入排序变为有序序列,重复上述做法,直至k=1时,执行简单插入排序,这时数组的排序完成。
* */
public void sheelSort(int[] arr) {
int len = arr.length;
while(len != 0) {
len = len >> 1;
for(int i = 0; i < len; i++) { //分组,就是整个数组从0开始到结束,符合下标差值为len的子序列组数
for(int j = i + len; j < arr.length; j += len) { //子序列进行插入排序,直至len=0
int k = j - len;
int tmp = arr[j];
while(k >= 0&&arr[k] >= tmp) {
arr[k+len] = arr[k];
k -= len;
}
arr[k+len] = tmp;
}
}
}
}
/**
* @author Norte
* Date:2018-5-18
*
* 功能:简单选择排序
*
* 基本思想:将数组分为两个部分,第一部分为已排序数组(初始为空),第二部分为待排序数组(初始为整个数组),那么排序的过程
* 如下:第一次在待排序数组中找到最小(最大)的元素,放在已排序数组中,直至待排序数组为空,排序完成。
*
* */
public void selectSort(int[] arr) {
int len = arr.length;
int minIndex;
for(int i = 0; i < len - 1; i++) { //控制选择的次数
minIndex = i;
for(int j = i + 1; j < len; j++) { //找出待排序数组中的最小元素的下标minIndex
if(arr[minIndex] > arr[j]) {
minIndex = j;
}
}
if(minIndex != i) { //下标比已排序数组的最后一位小,则进行交换
arr[minIndex] = arr[minIndex] ^ arr[i]; //利用位运算交换提高效率
arr[i] = arr[minIndex] ^ arr[i];
arr[minIndex] = arr[minIndex] ^ arr[i];
}
}
}
/**
* @author Norte
* Date:2018-5-20
*
* 功能:冒泡排序
*
* 基本思想:将数组中的元素两两比较,找出最大的放到最右边(或者找出最小的放在最左边),也就是一次冒泡,
* 下一次冒泡开始的时候,对已经找到位置的元素(已经确定顺序位置的元素)不参与本次的冒泡,直到参与冒泡
* 的元素为0,则整个数组有序。
*
* */
public void bubbleSort(int[] arr) {
int len = arr.length;
for(int i = 0; i < len; i++) { //控制循环次数
for(int j = 0; j < len - i - 1; j++) { //len - i - 1为未排序的元素个数
if(arr[j] > arr[j + 1]) { //前数比后数大则进行交换
arr[j] = arr[j] ^ arr[j + 1];
arr[j + 1] = arr[j] ^ arr[j + 1];
arr[j] = arr[j] ^ arr[j + 1];
}
}
}
}
/**
* @author Norte
* Date:2018-5-20
*
* 功能:快速排序
*
* 基本思想:在待排序数组中找到一个基准值(一般为数组的首元素),第一趟扫描,将比基准值小的放在基准值的左边,
* 将比基准值大的放在基准值右边,这样基准值的位置就正确了,再用同样的方法递归基准值左右两个部分,直至整个
* 数组有序。
* */
public int getMiddle(int[] arr, int start, int end) {
int tmp = arr[start]; //数组的第一个作为基准值
while(start < end) {
while(end > start && arr[end] >= tmp) { //从后向前比较,不小于基准值则向前移动(end--)
end --;
}
arr[start] = arr[end]; //小于基准值,则把该值移动到低端
while(end > start && arr[start] < tmp) { //从前向后比较,小于基准值则向后移动(start++)
start++;
}
arr[end] = arr[start]; //不小于基准值,则把该值移动到高端
}
arr[start] = tmp; //基准值到位(此时的start = end)
return start; //返回基准值的下标
}
public void quickSort(int[] arr, int start, int end) {
if(start < end) {
int middle = getMiddle(arr, start, end); //获取基准值的位置下标,将数组一分为二
quickSort(arr, start, middle - 1); //对低端序列递归排序
quickSort(arr, middle + 1, end); //对高端序列递归排序
}
}
/**
* @author Norte
* Date:2018-5-20
*
* 功能:归并排序(二路归并排序)
*
* 基本思想 :首先将原始的无序序列划分成两个子序列,然后分别对每个子序列递归进行排序,最后再将有序子列合并。
* 二路归并排序是首先将初始序列的n个记录看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,得到[n/2]
* 个长度为2(n为奇数时,最后一个序列的长度为1)的有序子序列。在此基础上,在对长度为2的有序子序列进行两两归并,
* 得到若干个长度为4的有序子序列。以此类推,直至得到长度为n的有序序列。
* */
public void merge(int[] arr, int start, int middle, int end) {
int[] temp = new int[end - start + 1]; //临时数组,两个序列合并后存储于此
int i = start; //第一个序列的指针
int j = middle + 1; //第二个序列的指针
int k = 0; //临时数组的指针
while(i <= middle && j <= end) { //终止条件:某个序列已经全部比较完毕,代表已经有序列全部移入到临时数组中
if(arr[i] < arr[j]) { //第一个序列中较小的元素放到临时数组中
temp[k++] = arr[i++];
} else { //第二个序列中较小的元素放到临时数组中
temp[k++] = arr[j++];
}
}
while(i <= middle) { //如果第一个序列没有移完,则将剩下的移到临时数组中
temp[k++] = arr[i++];
}
while(j <= end) { //如果第二个序列没有移完,则将剩下的移到临时数组中
temp[k++] = arr[j++];
}
for(int index = 0; index < temp.length; index++) { //将临时数组的值赋给原始数组
arr[index + start] = temp[index];
}
}
public void mergeSort(int[] arr, int start, int end) {
int middle = (end + start) / 2;
if(start < end) {
mergeSort(arr, start, middle); //左半边
mergeSort(arr, middle + 1, end); //右半边
merge(arr, start, middle, end); //左右合并
}
}
}
- 测试类
package com.norte.test;
import java.util.Arrays;
import com.norte.sort.Sort;
import com.norte.util.ArrayUtil;
public class SortTest {
public static void main(String[] args) {
ArrayUtil arrayUtil = new ArrayUtil();
Sort sort = new Sort();
int[] array = arrayUtil.MakeArray(10);
System.out.println("排序前:" + Arrays.toString(array));
// sort.insertSort(array); //插入排序
// sort.sheelSort(array); //希尔排序
// sort.selectSort(array); //简单选择排序
// sort.bubbleSort(array); //冒泡排序
// sort.quickSort(array, 0, array.length - 1); //快速排序
sort.mergeSort(array, 0, array.length - 1); //归并排序
System.out.println("排序后:" + Arrays.toString(array));
}
}
- 测试结果