算法学习【7】常见排序算法

        部分内容参考:http://blog.csdn.net/whuslei/article/details/6442755

        常见排序算法的时间复杂度:


        注:图片来源http://blog.chinaunix.net/uid-21457204-id-3060260.html

        在平均时间复杂度为O(nlogn)的排序算法中,归并排序是唯一稳定的

        在平均时间复杂度为O(n*n)的排序算法中,选择排序是唯一不稳定的

        最好时间复杂度为O(n)的排序算法:插入排序、希尔排序、冒泡排序。

        时间复杂度和数据的初始排列无关:堆排序、归并排序、选择排序

        快速排序、基数排序需要额外空间。


        1、冒泡排序

        每次遍历比较相邻元素大小,通过交换相邻元素将最大元素后移。

	public static void myBubbleSort(int[] arr, int len){
		for(int i=0;i<len-1;i++){
			boolean flag = true;
			for(int j=0;j<len-1-i;j++){
				if(arr[j]>arr[j+1]){
					swap(arr, j, j+1);
					flag = false;
				}
			}
			if(flag)
				return;
		}
	}

        相邻元素相等时不交换。在第一次遍历中加上标志位flag,才能使最好情况达到O(n)。


        2、选择排序

        每次遍历直接找出最小元素然后与首位置元素交换。

	public static void mySelectSort(int[] arr, int len){
		for(int i=0;i<len-1;i++){
			int minNo = i;
			for(int j=i+1;j<len;j++){
				if(arr[j]<arr[minNo]){
					minNo = j;
				}
			}
			swap(arr, minNo, i);
		}
	}


        3、插入排序

        前n个元素已排序,现将第n+1个元素插入到前n个元素中,因此从后向前依次与前一位元素比较。若前一位小于等于元素n则停止,否则将前一位元素后移。为了避免交换相邻元素带来的额外开销,可将待插入元素暂存于temp,若前一元素大于元素n,直接赋值arr[n]=arr[n-1],覆盖原来待插入元素即可。

	public static void myInsertSort(int[] arr, int len){
		for(int i=1;i<len;i++){
			int temp = arr[i];
			int j=i-1;
			for(;j>=0;j--){
				if(arr[j]<=temp){
					break;
				}else{
					arr[j+1]=arr[j];
				}
			}
			arr[j+1]=temp;
		}
	}


        4、快速排序

        选取一个数(通常取第一个数),将数组分成两部分,一部分小于等于该数,一部分大于该数,然后采用递归思想,再对这两部分分别拆分,当拆分后数组长度小于2时递归停止。

	public static void myQuickSort(int[] arr, int left, int right){
		if(right-left<=0){
			return;
		}
		int pivot = arr[left];
		int i = left;
		int j = right;
		while(i<j){
			while(arr[j]>pivot&&i<j)
				j--;
			while(arr[i]<=pivot&&i<j)
				i++;
			swap(arr,i,j);
		}
		swap(arr,left,i);
		myQuickSort(arr,left,i-1);
		myQuickSort(arr,i+1,right);
	}

        5、希尔排序

        希尔排序是插入排序的一种改进。它将记录按下标的一定增量分组(例,选择步长d=3,则将arr[0]、arr[3]、arr[6]作为一组,arr[1]、arr[4]、arr[7]作为一组,...),然后对每组进行直接插入排序。然后将步长逐步减少,每组元素个数越来越多,当步长减至1时,整个数组合成一组,排序结束希尔排序的时间复杂度与所选步长有关,一般取初始步长d = n/2,然后逐步减半

        示例如下,图片来自百度百科。


	public static void myShellSort(int[] arr, int len){
		int d = len/2;
		while(d>0){
			for(int i=d;i<len;i++){
				int temp = arr[i];
				int j=i-d;
				for(;j>=0;j=j-d){
					if(arr[j]<=temp){
						break;
					}else{
						arr[j+d]=arr[j];
					}
				}
				arr[j+d]=temp;
			}
			d = d/2;
		}
	}

        6、归并排序

       归并排序先将数组拆成一个一个数,每个数认为是一个有序表,然后将相邻两个有序表合并成一个有序表,依次两两合并直到只剩一个有序表。一般采用递归形式编写。两个有序表合并成一个有序表的过程中一般需要使用额外空间,因此空间复杂度为O(n),因此上表这处有误。


        图片来源http://www.cnblogs.com/horizonice/p/4102553.html

	public static void myMergeSort(int[] arr, int st, int fh){
		if(st>=fh){
			return;//已经拆成一个数字
		}else{
			int mid = (st+fh)/2;
			myMergeSort(arr, st, mid);
			myMergeSort(arr, mid+1, fh);
			Merge(arr, st, mid, fh);//将两个有序表arr[st:mid]和arr[mid+1:fh]合并成一个
		}
	}

	static int[] temp = new int[arr.length];
	public static void Merge(int[] arr, int st, int mid, int fh){
		int i = st;    //两个指针指向两个子有序表的开头
		int j = mid+1;
		int k = 0;
		while(i<=mid&&j<=fh){
			if(arr[i]<=arr[j]){
				temp[k] = arr[i];
				i++;
				k++;
			}else{
				temp[k] = arr[j];
				j++;
				k++;
			}
		}

		while(i<=mid){
			temp[k] = arr[i];
			i++;
			k++;
		}
		while(j<=fh){
			temp[k] = arr[j];
			j++;
			k++;
		}

		for(int n=0;n<k;n++){
			arr[st+n] = temp[n];
		}
	}

        7、堆排序

        堆排序是利用堆这种数据结构进行排序的算法。堆这种数据结构是一种特殊的完全二叉树,最大堆要求每个节点的值都不大于其父节点的值;反之,最小堆要求每个节点的值都不小于其父节点的值。

        堆排序详细内容见:http://www.cnblogs.com/mengdd/archive/2012/11/30/2796845.html,讲的很好。

public class HeapSort{
	private static int[] sort=new int[]{1,0,10,20,3,5,6,4,9,8,12,17,34,11};

	public static void main(String[] args){
		buildMaxHeapify(sort);
		heapSort(sort);
		print(sort);
	}

	private static void buildMaxHeapify(int[] data){
		//没有子节点的才需要创建最大堆,从最后一个的父节点开始
		int startIndex=getParentIndex(data.length-1);
		//从尾端开始创建最大堆,每次都是正确的堆
		for(int i=startIndex;i>=0;i--){
			maxHeapify(data,data.length,i);
		}
	}

	/**
	*创建最大堆
	*
	*@paramdata
	*@paramheapSize需要创建最大堆的大小,一般在sort的时候用到,因为最多值放在末尾,末尾就不再归入最大堆了
	*@paramindex当前需要创建最大堆的位置
	*/
	private static void maxHeapify(int[] data,int heapSize,int index){
		//当前点与左右子节点比较
		int left=getChildLeftIndex(index);
		int right=getChildRightIndex(index);

		int largest=index;
		if(left<heapSize&&data[index]<data[left]){
			largest=left;
		}
		if(right<heapSize&&data[largest]<data[right]){
			largest=right;
		}
		//得到最大值后可能需要交换,如果交换了,其子节点可能就不是最大堆了,需要重新调整
		if(largest!=index){
			int temp=data[index];
			data[index]=data[largest];
			data[largest]=temp;
			maxHeapify(data,heapSize,largest);
		}
	}

	/**
	*排序,最大值放在末尾,data虽然是最大堆,在排序后就成了递增的
	*
	*@paramdata
	*/
	private static void heapSort(int[] data){
		//末尾与头交换,交换后调整最大堆
		for(int i=data.length-1;i>0;i--){
			int temp=data[0];
			data[0]=data[i];
			data[i]=temp;
			maxHeapify(data,i,0);
		}
	}

	/**
	*父节点位置
	*
	*@paramcurrent
	*@return
	*/
	private static int getParentIndex(int current){
		return(current-1)>>1;
	}

	/**
	*左子节点position注意括号,加法优先级更高
	*
	*@paramcurrent
	*@return
	*/
	private static int getChildLeftIndex(int current){
		return(current<<1)+1;
	}

	/**
	*右子节点position
	*
	*@paramcurrent
	*@return
	*/
	private static int getChildRightIndex(int current){
		return(current<<1)+2;
	}

	private static void print(int[] data){
		int pre=-2;
		for(int i=0;i<data.length;i++){
			if(pre<(int)getLog(i+1)){
				pre=(int)getLog(i+1);
				System.out.println();
			}
			System.out.print(data[i]+"|");
		}
	}

	/**
	*以2为底的对数
	*
	*@paramparam
	*@return
	*/
	private static double getLog(double param){
		return Math.log(param)/Math.log(2);
	}
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值