Java 排序实现

来源:互联网 发布:优化实践技能 编辑:程序博客网 时间:2024/05/20 16:01

  最近在看JAVA的一些排序算法,和大家分享一下,感觉非常有帮助~~为了简化,这里直接采用数组进行排序,没有引入比较策略。

为了便于测试,编写了一个生产数组的方法,如下:

/** * 为排序生产测试数据 * @author Administrator * */public class NumberFactory {/** * 成产1个含有n个数的数组,每个数的大小在1-n之间 * @param n 数据的个数 * @return 生产的数组 */public static int[] buildNumber(int n){int[] num = new int[n];Random random = new Random();for(int i = 0;i < num.length;i++){num[i] = random.nextInt(n);}return num;}}

1 插入排序

1.1基本思路

     从前往后进行循环(由n-1趟循环完成),当到循环到第p个数时,该算法保证前面的p-1个数都是有序的。

1.2复杂度

插入排序的平均复杂度为O(n2)。在下面的方法中,如果待排数组是以排序的,那么复杂度为O(n)。

1.3实现

import java.util.Arrays;/** * 插入排序 * @author Administrator * */public class InsertSequenceTest {/** * 插入排序 * @param array */public static void insertSequence(int array[]){int j;for(int i=0;i<array.length;i++){//大循环int p = array[i];for(j=i;j>0&&p<array[j-1];j--){//将元素判断插入到前面array[j] = array[j-1];}array[j] = p;}}public static void main(String[] args) {int[] a = NumberFactory.buildNumber(10);System.out.println("***********插入排序前**************");System.out.println(Arrays.toString(a));System.out.println("***********插入排序后**************");InsertSequenceTest.insertSequence(a);System.out.println(Arrays.toString(a));}}

2希尔排序

2.1基本思路

该方法引入一个增量序列h1,h2,h3...,该增量是递减的,算法保证每轮循环中每隔增量h的数都是已排序的,如当增量为3时数组{4,2,3,6,8,7,10,9,8},当增量为1时,排序完成。

2.2复杂度

该算法有着亚二次时间界。使用希尔排序的对坏情形的复杂度为O(n2),但是当增量h选择合理时最坏情形可达到O(n3/2)。

2.3实现

import java.util.Arrays;/** * 希尔排序 * @author Administrator * */public class ShellsortSquenceTest {/** * 希尔排序 * @param array 代排序数组 */public static void shellsort(int array[]){for(int shell = array.length/2;shell>0;shell/=2){//递减的增量int j;for(int i = shell;i<array.length;i++){int o = array[i];for(j = i;j-shell >= 0&&o < array[j-shell];j -= shell){array[j] = array[j-shell];}array[j] = o;}}}}

3堆排序

3.1基本思路

         堆是一个被完全填满的二叉树,如果是大顶堆则每个父亲节点的值不小于其所有孩子的值,小顶堆则相反。在堆中很容易找到她的左孩子(2n+1)。堆排序首先先建立一个堆,然后让其根节点与最后一个叶子节点交换,此时堆的结构被破坏(根节点被替换),然后将根节点下滤到适合她的位置,循环直到剩下最后一个根节点(值得注意的是,在下滤的过程中由于不能肯定节点是否有右孩子,所以应做判断)。

3.2复杂度

构建堆需要O(n)的时间,每个元素下滤需要O(logn)的时间,n个元素的时间为O(nlogn),所以复杂度为O(n)+O(nlogn)即O(nlogn)。

3.3实现

import java.util.Arrays;public class HeapSequenceTest {/** * 堆排序 * @param a */public static void heapSort(int[] a){for(int i = a.length/2; i >= 0; i--) //先建立堆percolatedown(a,i,a.length);for(int i = a.length-1; i >= 0; i--){//将头尾互换,建立堆changeNum(a, 0, i);percolatedown(a,0,i);}}/** * 建立堆主方法            * @param a  * @param begin 开始位置 * @param len 需要的总长度 */private static void percolatedown(int[] a, int begin, int len) {int child;    //与父亲交换位置的孩子int temp = a[begin];  //用于存放变量int j = 1;for(; leftchild(begin) < len; begin = child){child = leftchild(begin);//如果存在右孩子,选择一个最小的if(child+1 < len&& a[child] < a[child+1])  child++;if(temp < a[child]){a[begin] = a[child];}elsebreak;j++;}a[begin] = temp;}/** * 返回左孩子的下标 * @param begin * @return  */private static int leftchild(int begin) {return 2*begin+1;}/** * 交换次序,该方法用于交换数组内两个下标的数字 * @param a 待交换的数组 * @param left 被交换的下标1 * @param center 被交换的下标2 */private static void changeNum(int[] a, int arg1, int arg2) {int tem = a[arg1];a[arg1] = a[arg2];a[arg2] = tem;}public static void main(String[] args) {int[] a = NumberFactory.buildNumber(19);System.out.println("***********堆排序前**************");System.out.println(Arrays.toString(a));System.out.println("***********堆排序后**************");HeapSequenceTest.heapSort(a);System.out.println(Arrays.toString(a));}}

4归并排序

4.1基本思路

归并排序是合并已经排序的表,基本思路如下(递归分治思想)
待排序列{56,12,34,1,7,65,9}
第一步结果{[12,56][1,34][7,65][9]}
第二步结果{[1,12,34,56][7,9,65]}
第三部结果{[1,7,9,12,34,56,65]}
该方法是经典的分治策略,她将问题分成一些小的问题然后递归求解。

4.2复杂度

归并排序的复杂度是O(nlogn),但是有个明显的缺点是该方法需要拷贝的线性附加内存,所以内存开销会比较大,但是她的明显优点是有着很少的比较次数。

4.3实现

/** * 归并排序 * @author Administrator * */public class MergeSequenceTest {/** * 引出归并排序 * @param a 待排序数组 */public static void mergeSort(int[] a){int[] temp = new int[a.length];mergeSort(a,temp,0,a.length-1);}/** * 递归调用归并 * @param a 待排序数组 * @param temp 被拷贝的数组 * @param left 待排数组起始 * @param right 待排数组结尾 */private static void mergeSort(int[] a, int[] temp, int left, int right) {if(left<right){int center = (left + right)/2;mergeSort(a,temp,left,center);mergeSort(a,temp,center+1,right);merge(a,temp,left,center+1,right);}}/** * 归并排序主要算法 * @param a 待排序数组 * @param temp 被拷贝的数组 * @param leftpos 第一个数组的起始下标 * @param rightpos 第二个数组的起始下标 * @param rightend 结尾下标 */private static void merge(int[] a, int[] temp, int leftpos, int rightpos, int rightend) {int leftend = rightpos-1;                //第一个数组的最后下标int temppos = leftpos;                   //拷贝数组的起始下标int sum = rightend - leftpos+1;//总的个数//比较拷贝到被拷贝数组while(leftpos<=leftend&&rightpos<=rightend)if(a[leftpos]<a[rightpos])temp[temppos++] = a[leftpos++];elsetemp[temppos++] = a[rightpos++];while(leftpos<=leftend)temp[temppos++] = a[leftpos++];while(rightpos<=rightend)temp[temppos++] = a[rightpos++];//拷贝回来for(int i = 0;i<sum;i++,rightend--)a[rightend] = temp[rightend];}public static void main(String[] args) {int[] a = NumberFactory.buildNumber(100);System.out.println("***********归并排序前**************");System.out.println(Arrays.toString(a));System.out.println("***********归并排序后**************");MergeSequenceTest.mergeSort(a);System.out.println(Arrays.toString(a));}}


5快速排序

5.1基本思路

快速排序也是采用分治的思想,在快速排序中首先是找到一个枢纽元素,一轮排序后比枢纽小的放在枢纽前面,比枢纽大的放在枢纽后面。
待排序列{56,12,34,1,7,65,9}
选取枢纽34后(在下面的算法实现中选取枢纽采用了(首+中+尾)三点选取中值所以结果与下方有所不同,但是思想没变)
第一步结果{9,12,1,7,34,65,56}(9与56互换。。。)
第二步结果{9,1,7,12,34,56,65}(下划线为第二轮枢纽)
第三部结果{1,7,9,12,34,56,65}

5.2复杂度

快速排序的最坏运行时间为O(n2),但是如果枢纽选择合理很少会出现最坏情况,她的平均时间为O(nlogn)。

5.3实现

在下面的方法中,枢纽采用三个点选取中值的方法。另外考虑到存在相等元素的情况,为了保证分治的平衡(如果全部相等则会出现最坏情况,即分治不均匀)所以遇到相等的也会进行交换。
package com.example.sequence;import java.util.Arrays;public class QuickSequenceTest {/** * 快速排序入口 * @param a */public static void quickSort(int[] a){quickSort(a,0,a.length-1);}/** * 快速排序主方法 * @param a * @param left * @param right */private static void quickSort(int[] a, int left, int right) {if(left+1 < right){//查找枢纽中值int flag = findFlag(a,left,right);int i = left, j = right-1;for(;;){while(a[++i] < flag){}    //此行可看出快速排序的优势。while(a[--j] > flag){}    //相等也进行交换,防止递归不均匀if(i<j){//交换位置changeNum(a, i, j);}elsebreak;}changeNum(a,i,right-1);  //枢纽归位quickSort(a,left,i-1);quickSort(a,i+1,right);}else{//主要是对3个元素进行排序findFlag(a,left,right);}}/** * 找到每轮的枢纽,采用中值法(首+尾+中),并且对该3个元素进行排序 * @param a 待查找的数组 * @param left 待查找的数组的首下标 * @param right 待查找的数组的尾下标 * @return 枢纽中值,并且将3个元素排序 */private static int findFlag(int[] a, int left, int right) {if(left<right){int center = (left+right)/2;if(a[center] < a[left]) changeNum(a,left,center);if(a[right] < a[left]) changeNum(a,right,left);if(a[right] < a[center]) changeNum(a,right,center);changeNum(a,center,right-1);//因为right已经大于枢纽值,所以不参加比较return a[right-1];}return 0;}/** * 交换次序,该方法用于交换数组内两个下标的数字 * @param a 待交换的数组 * @param left 被交换的下标1 * @param center 被交换的下标2 */private static void changeNum(int[] a, int arg1, int arg2) {int tem = a[arg1];a[arg1] = a[arg2];a[arg2] = tem;}public static void main(String[] args) {int[] a = NumberFactory.buildNumber(10);System.out.println("***********快速排序前**************");System.out.println(Arrays.toString(a));System.out.println("***********快速排序后**************");QuickSequenceTest.quickSort(a);System.out.println(Arrays.toString(a));}}