您的位置:首页 > 编程语言 > Java开发

Java实现各大经典排序算法(插入、希尔、简单选择、冒泡、快速、归并)

2018-05-20 23:58 399 查看

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
;
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));
}
}
  • 测试结果
         

阅读更多
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐