Java 排序实现

  最近在看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];
			}
			else
				break;
			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++];
			else
				temp[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);
				}
				else
					break;
			}
			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));

	}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值