各种排序算法的比较


稳定的排序算法有:直接插入,冒泡,归并,基数排序。

一.快速排序

快排的三个步骤:

1.选取枢纽元,一般用三数中值法,即求得left,center,right的中位数(不要用第一个数,如果原始数据是倒序,效率将会很低)。

2.根据枢纽元把输入数据划分成为两部分,左半部分的数比枢纽元小,右半部分比枢纽元大。

3. 分别对左半部分和右半部分递归调用快排,最终得到排序结果。

最难的是第二步,也就是分区函数。

首现把枢纽元放在最左边,设置两个指针low和high,分别指向除枢纽元外的左右两端;

找到左边第一个比枢纽元大的数,并且找到右边第一个枢纽元小的数,如果low<high,那么就交换两者,继续循环,否则跳出循环;

在循环条件部分,左边元素的判断,一定要加等号和low<high,即:

array[low] <= pivot && low < high

右半部分则不用加


上次快排代码写错了,纠正一下,中间while(true)的部分是改完后的代码,交换分割特别要注意

快排代码

public void quickSort(int[] array,int left,int right){		
		if(left < right){
			int p = partition(array,left,right);
			//对左右两边的两部分再递归调用快排
			quickSort(array,left,p-1);
			quickSort(array,p+1,right);
		}		
	}
	
	/*
	 * 将其他数与枢纽元比较,比枢纽元小的放在枢纽元左边,比它大的放在右边,划分为两个部分
	 */
	public int partition(int[] array,int left,int right){
		//选取枢纽元,不要选择第一个元素,一般用三数中值法求枢纽元,把枢纽元放到最左边
		int pivot = median3(array,left,right);
		int low = left+1;//从第二个数开始找
		int high = right;//找到第一个比枢纽元小的数,两者交换
		while(true){
			//找到第一个比枢纽元大的数
			while(array[low] <= pivot && low < high)low++;
			//找到第一个第枢纽元小的数
			while(array[high] > pivot)high--;
			//如果左指针<右指针,则将两者交换
			if(low < high){
				swap(array,low,high);
			}else{
				break;
			}
		}
		/*
		 * 将放在最左边的枢纽元与high指针上的数交换,
		 * 至此枢纽元左边的数都是小于它的数,右边的数都是大于它的数
		 */
		swap(array,left,high);
		return high;
	}
	
	/*
	 * 三数中值分割法确定枢纽元,取left,right,center三个数的中值
	 */
	public int median3(int[] array ,int left,int right){
		int center = (left+right)/2;
		if(array[left] > array[center]){
			swap(array,left,center);
		}
		if(array[left] > array[right]){
			swap(array,left,right);
		}
		if(array[right] < array[center]){
			swap(array,right,center);
		}	
		//经过以上三个判断,中值在center位上
		swap(array,center,left);//把枢纽元放在最左边
		return array[left];//枢纽元的值
	}
	/*
	 * 将i和j上的数字交换
	 */
	public void swap(int[] array,int i,int j){
		int temp = array[i];
		array[i] = array[j];
		array[j] = temp;
	}


主程序调用:

int [] array = {0,2,5,3,2,1,4,2,7,10,2,11,3,4,6,1,3,45,10};
quickSort.quickSort(array, 0, array.length-1);

用同样的测试数据,可以做实验得到冒泡排序,需要交换的次数是50次,而快排只需要32次,故快排比冒泡快一些

在数据量小于20时,用插入排序速度比快排要快,而数据量大于20后,就尽量用快排

二.归并排序

思路:递归+合并有序数组。

首先将数组分成左右两部分,然后分别递归归并左右部分。然后将已排好序的两部分合并。

合并的算法需要用到临时变量,所以归并排序空间复杂度为O(N).

void mergeSort(int[] a){
		int length = a.length;
		int[] temp = new int[length];//需要一个临时变量作为合并两个有序数组用
		mergeSortRec(a,temp,0,length-1);
	}
	
	void mergeSortRec(int[] a,int[] temp,int left,int right){
		if(left<right){
			int center = (left+right)/2;
			mergeSortRec(a,temp,left,center);//归并排序左半部分
			mergeSortRec(a,temp,center+1,right);//归并排序右半部分
			mergerTwoSortedArrays(a,temp,left,center,right);
		}
	}
	
	/*
	 * 将a[leftPos,leftEnd]和a[leftEnd+1,rightEnd]合并成一个有序数组
	 * 这两部分已有序
	 */
	void mergerTwoSortedArrays(int[] a,int[] temp,int leftPos,int leftEnd,int rightEnd){
		int i=leftPos;//左半部分指针
		int j=leftEnd+1;//右半部分指针
		int k = leftPos;//临时数组的指针
		while(i<=leftEnd && j<=rightEnd){
			if(a[i]<a[j]){
				temp[k]=a[i];
				i++;
			}else{
				temp[k]=a[j];
				j++;
			}
			k++;
		}
		while(i<=leftEnd){//右半部分全部遍历完,遍历左半部分
			temp[k]=a[i];
			k++;i++;
		}
		while(j<=rightEnd){//左半部分全部遍历完,遍历右半部分
			temp[k]=a[j];
			k++;j++;
		}
		/*
		 * 把temp里的数copy回原来的数组
		 */
		for(i=leftPos;i<=rightEnd;i++){
			a[i]=temp[i];
		}
	}

三.堆排序

对于堆的定义,在最大堆/最小堆中有详细分析。

堆排序一般利用最大堆进行排序,主要包括两个步骤:

1. 初始化堆,建立最大堆。从最后一个叶子节点的父节点开始到下标0依次调整每个节点。

2. 删除最大的数,方法是将最大的数与最后的叶子节点交换,然后在a[0-i]范围内,对下标0的节点继续调整成为大根堆。

3. 每次调整成为一个最大堆后,重复步骤2。

	void heapSort(int[] a){
		int length = a.length;
		//初始化堆,建立最大堆
		int middle = length/2-1;//最后一个叶子节点的父节点
		for(int i=middle;i>=0;i--){
			heapAdjust(a,i,length);
		}
		int temp=0;
		//删除堆的根节点(把它与最后的叶子节点(除了已排好序的)交换)
		for(int i=length-1;i>=0;i--){
			temp = a[0];
			a[0] = a[i];
			a[i] = temp;
			//因为根节点被交换到最后,所以要从根节点重新调整堆
			heapAdjust(a,0,i);
		}
	}
	/*
	 * 在a[0..n-1]范围内调整第i个数,进行下沉操作,调整成最大堆
	 */
	void heapAdjust(int[] a,int i,int n){
		int leftChild = 2*i;
		int rightChild = leftChild+1;
		int largerIndex = i;//用于比较根节点与左右孩子的值
		int temp=0;
		
		while(leftChild < n || rightChild < n){
			if(leftChild < n && a[leftChild]>a[largerIndex]){//如果左孩子比根大
				largerIndex = leftChild;
			}
			if(rightChild < n && a[rightChild]>a[largerIndex]){
				largerIndex = rightChild;
			}
			if(largerIndex != i){//如果根节点需要调整,即左右孩子有比它大的
				//交换a[largerIndex]与a[i]
				temp = a[largerIndex];
				a[largerIndex] = a[i];
				a[i] = temp;
				//继续调整被交换的那个孩子节点
				i = largerIndex;
				leftChild = 2*i;
				rightChild = leftChild+1;
			}else{//无需调整
				break;
			}
		}	
	}


一些结论:

1. 堆排序和快速排序的复杂度都是nlogn,但一般使用快速排序。因为堆排序在二叉树用上浮的方式构造堆,元素交换的跨度较大。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值