快速排序算法思想及Java实现

1.快速排序概述

    快速排序是对冒泡排序算法得一种改进。快速排序的思想是通过一趟排序后将要排序的数据分割成独立的两部分,在某个数的左边都是小于(大于)该数的群体,在数的右边都是大于(小于)该数的群体,然后再按照此方法分别对两边的独立群体进行快速排序,整个过程可以使用递归来进行。

2.单轴快速排序的基本原理

    快速排序算法的思想就是在待排序数组中选择一个数作为比较元素(中轴元素),单轴就是每次比较只选定一个中轴元素(一般选取数组的第一个元素作为中轴元素)。在一次循环比较中,将大于中轴元素的数放在右边将小于中轴元素的数放在左边,然后再分别对左右两个群体进行递归排序。

2.1两端同时扫描逼近式排序实现

    定义左边扫描的起点索引为i,右边扫描的起点的索引为j;从左往右扫描,当寻找到大于中轴数的时候停止扫描,记录下大于中轴数的数的位置i,否则i++直到i>j;从右往左扫描,当寻找到小于中轴数的时候停止扫描记录下小于中轴数的输的索引j,否则j--直到i>j;如果i<j的话那么交换i和j位置的数;交换中轴数和j未知的数,因为经过扫描j位置的数一定是左边起最后一个小于中轴数的数(i从左到右逼近,i走过的数必定都比中轴数小,j从右到左扫描,j走过的数必定都比中轴数大,故j停下的位置一定是小于或等于中轴数的最后一个数)。然后在分别对左边小于中轴数的数和右边大于中轴数的数进行递归。

    



算法实现代码如下:

 public static void swap(int[] a,int i,int j){
		  int temp = a[i];
		  a[i] = a[j];
		  a[j] = temp;
}
public static void quickSort(int[] a, int left, int right){
		if(a == null || left < 0 || right >= a.length ){
			throw new IllegalArgumentException("Invalid Parmeters");
		}
	    if(left < right){//递归的边界条件,当 left == right时数组的元素个数为1个
	        int pivot = a[left];//最左边的元素作为中轴,L表示left, R表示right
	        int i = left+1, j = right;
	        //当i == j时,i和j同时指向的元素还没有与中轴元素判断,
	        //小于中轴元素,i++,大于中轴元素j--,
	        //当循环结束时,一定有i = j+1, 且i指向的元素大于中轴,j指向的元素小于等于中轴
	        while(i <= j){
	            while(i <= j && a[i] < pivot){
	                i++;
	            }
	            while(i <= j && a[j] > pivot){
	                j--;
	            }
	            //当 i > j 时整个切分过程就应该停止了,不能进行交换操作
	            //这个可以改成 i < j, 这里 i 永远不会等于j, 因为有上述两个循环的作用
	            if(i <= j){
	                swap(a, i, j);
	                i++;
	                j--;
	            }
	        }
	        //当循环结束时,j指向的元素是最后一个(从左边算起)小于等于中轴的元素
	        swap(a, left, j);//将中轴元素和j所指的元素互换,交换后左边都是比中轴元素小得元素,右边都是比中轴元素大的数,分别对两边进行排序
	        quickSort(a, left, j-1);//递归左半部分
	        quickSort(a, j+1, right);//递归右半部分
	    }
	}
2.2两段扫描,一端"挖坑"另一端填补

    基本思想,使用两个变量i和j,i指向最左边的元素,j指向最右边的元素,我们将首元素作为中轴,将首元素复制到变量pivot中,这时我们可以将首元素i所在的位置看成一个坑,我们 从j的位置 从右向左扫描(如果从左到右为先那么先前的第一个坑仍然需要一个"萝卜"来填补),找一个小于等于中轴的元素A[j],来填补A[i]这个坑,填补完成后,拿去填坑的元素所在的位置j又可以看做一个坑,这时我们在以i的位置从前往后找一个大于中轴的元素来填补A[j]这个新的坑,如此往复,直到i和j相遇(i == j,此时i和j指向同一个坑)。最后我们将中轴元素放到这个坑中。最后对左半数组和右半数组递归上述操作。
代码实现:
public static void quickSort2(int[] a, int left, int right) {
		if (a == null || left < 0 || right >= a.length) {
			throw new IllegalArgumentException("Invalid Parmeters");
		}
		if (left < right) {
			// 最左边的元素作为中轴复制到pivot临存用作比较。然后最左边的元素可以看做一个坑
			int pivot = a[left];
			// 注意这里 i = L,而不是 i = L+1, 因为i代表坑的位置,当前坑的位置位于最左边(区别于双端扫描的方式)
			int i = left, j = right;
			//注意这里条件【不能是i<=j】,等于时会陷入自己挖坑自己填的无限循环,成为一个死循环
			while (i < j) {
				// 下面面两个循环的位置不能颠倒,因为第一次坑的位置在最左边
				// 当找到了一个比中轴元素pivot小的元素的时候将会退出此while循环,并且索引保存在j中
				while (i < j && a[j] > pivot) {
					j--;
				}
				// 填a[i]这个坑,填完后A[j]是个坑
				// 注意不能是a[i++] = a[j],当因i==j时跳出上面的循环时
				// 坑为i和j共同指向的位置,执行a[i++] = a[j],会导致i比j大1,
				// 但此时i并不能表示坑的位置
				a[i] = a[j];
				// 当找到了一个比中轴元素pivot大的元素的时候将会退出此while循环,并且索引保存在i中
				while (i < j && a[i] <= pivot) {
					i++;
				}
				// 填A[j]这个坑,填完后A[i]是个坑,
				// 同理不能是A[j--] = a[i]
				a[j] = a[i];
			}
			// 循环结束后i和j相等,都指向坑的位置,将中轴填入到这个位置
			a[i] = pivot;
			quickSort2(a, left, i - 1);// 递归左边的数组
			quickSort2(a, i + 1, right);// 递归右边的数组
		}
	}



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值