用java语言使用递归于非递归方式实现快速排序

  1. 快速排序是大多数公司面试人员的必考题,今天将自己参考别人的思想所写的两种快速排序的方式;
  2. 快速排序使用场景:无序列表,时间复杂度为O(log2N);
  3. 快速排序的思想:

         3.1、在一个数组中选取一个基准数,根据这个基准数将数组分为两组,一组为大于基准数,另外一组为小于基准数;

         3.2、找出两个下标,一个为末尾下标,一个为开始下标;

         3.3、先从末尾下标开始于基准数比较,有两种情况:

                 3.3.1、arr[end] > base 当末尾数大于基准数时,end下标变成倒数第二位,即end--;

                 3.3.2、arr[end] < base 当末尾数小于基准数时,首尾交换位置即arr[start] = arr[end],同时start++;

        3.4、分成两堆之后,再次进行3.3操作;

4、递归实现快速排序:

/**
 * @author 橙橙橙。
 * 描述:快速排序思想:
 * 1、选择一个基准数;
 * 2、选定两个坐标,一个为左边第一个数,另外一个是右边第一个数;
 * 3、先通过最右边的数与基准数比较,
 *    如果大于等于,执行right--操作;
 *    如果小于,进行arr[left] = arr[right]操作;
 *    	小于,交换之后,开始从左边比较,因为左边有空位
 *      当左边出现大于基准的数的时候,又进行交换:arr[right] = arr[left];
 *      然后遇到左边的数小于基准数,则进行left++;
 * 3、通过一趟排序将数组分为两个部分,左边部分为小于基准数,右边部分为大于基准数
 * 4、对这两个部分进行1、2、3、步骤
 */
public class QuickSort1 {
	public static void main(String[] args) {
		
		int[] arr = {4,5,9,7,8,9,12,4,65,32,78,24,55,76,77,44,32,11};
		int[] arr1 = QuickSort(arr, 0, arr.length-1);
		for(int i = 0; i < arr1.length; i++) {
			System.out.print(arr1[i] + " ");
		}
	}
	
	public static int[] QuickSort(int[] arr, int left, int right) {
		
		//这个if必不可少,因为是递归结束的条件
		if(left < right) {
			int baseIndex = getTargetIndex(arr, left, right);
			QuickSort(arr, left, baseIndex - 1);
			QuickSort(arr, baseIndex + 1,right);
		}
		return arr;
	}
	
	//用于获取每一次基准数下表的函数
	public static int getTargetIndex(int[] arr, int left, int right) {
		//这个检验是为了保证程序的健壮性
		if(left >= right || arr == null) {
			return -1;
		}
		
		//key为基准数,其它数都要与这个key比较
		int key = arr[left];
		while(left < right) {    //为什么使用while?因为我们要循环的操作不止一次
			while(key < arr[right] && left < right) //当末尾数大于基准数时的情况
				right--;   //当最右边的数大于基准数时,末尾下标进行--操作,while的作用域只有这一行
			arr[left] = arr[right];   //当最右边的数小于基准数时,要做的操作
			
			while(key >= arr[left] && left < right) //当末尾数小于基准数时的情况,这里包含了相等的情况
				left++;    //当最右边的数小于基准数时,开始下标进行++操作,while的作用域只有这一行
			arr[right] = arr[left];   //当最左边的数大于基准数时,要做的操作
		}
		arr[left] = key;  //当所有下标都遍历完了以后,将基准数放在left下标位置
		System.out.println(left +"***");
		return left;     //返回基准数的下标
	} 
}

请查看结果:

1***
6***
2***
3***
4***
8***
11***
10***
13***
16***
14***
4 4 5 7 8 9 9 11 12 24 32 32 44 55 65 76 77 78 

5、非递归实现快速排序的方法,其实也是也是使用堆栈实现的,如果对堆栈不是很了解的博友,请看它:https://baike.baidu.com/item/%E5%A0%86%E6%A0%88/1682032?fr=aladdin

public class QuickSort2 {
	public static void main(String[] args) {
		int[] arr = {3,5,6,8,9,2,3,4,4,2,88,1,1,2,7,3,5,4,4,32};
		int[] arr1 = GenerQuickSort(arr, 0, arr.length - 1);
		for(int i = 0; i < arr1.length; i++) {
			System.out.print(arr1[i] + " ");
		}

	}
	public static int[] GenerQuickSort(int[] arr, int left, int right) {
		//定义一个栈
		Stack<Integer> stack = new Stack<>();
		//前提条件就是left < right
		if(left < right) {
			stack.push(left);  //将left压进栈中
			stack.push(right);  //将right压进栈中
			while(!stack.isEmpty()) {  //当栈不为空时,进行以后的操作
				int height = stack.pop();  //取出栈中第一位,我最后压进的是right,所以它是最末位的下标;
				int low = stack.pop();  //最底下的是left的下标。到底哪个对应那个,要看你是怎么压进去的
				
				int index = getTargetIndex(arr, low, height);  //得到基准数的下标
				if(index-1 > low) {   //判断基准数左边的数的下标与开始下标的大小
					stack.push(low);    //根据情况压进去以基准数分开的下标的左半边部分的低位
					stack.push(index -1);   //根据情况压进去以基准数分开的下标的左半边部分的高位
				}
				
				if(index+1 < height) {  //判断基准数右边的数的下标与开始下标的大小
					stack.push(index + 1);   //根据情况压进去以基准数分开的下标的右半边部分的低位
					stack.push(height);   //根据情况压进去以基准数分开的下标的右半边部分的高位
				}
			}
		}
		return arr;
	}
	
	//这是寻求中间位置的方法,此处不再赘述
	public static int getTargetIndex(int[] arr, int left, int right) {
		if(left >= right || arr == null) {
			return -1;
		}
		
		int key = arr[left];
		while(left < right) {
			while(key < arr[right] && left < right)
				right--;
			arr[left] = arr[right];
			
			while(key >= arr[left] && left < right)
				left++;
			arr[right] = arr[left];
		}
		arr[left] = key;
		System.out.println(left +"***");
		return left;
	}
}

结果:

7***
11***
16***
17***
19***
13***
15***
10***
9***
6***
5***
4***
3***
1***
1 1 2 2 2 3 3 3 4 4 4 4 5 5 6 7 8 9 32 88 

简要的说一下堆栈排序的原理:

1、先将最左边与最右边的的下标压进去;

2、在进入while循环,再取出堆栈中仅有的两个数,得到基准数的下标;

3、对基准数两边的数进行继续压栈;

4、压栈时要注意顺序,因为堆栈是先进后出数据结构。将两边的下标压入之后,在寻找其中一边的基准数,再进行对这个基准数的压栈操作,之后,循环3、4步骤;

备注:

对于堆栈有一种保护现场的操作,具体的体现可以参考递归。保护现场,就是我没有执行完一次操作,因为这次操作依赖下次操作,我会放下这次的操作,操作系统会讲我这部分未完成的操作保护起来,等待我完成下次操作再回来完成这次操作,就像斐波那契数列,我们想知道当自变量等于10的时候,函数的值是多少,那么你肯定要先知道9,8,7,6,5,4,3,2,1,的值是多少,而我们最先知道的是1和2的值,再会知道n=3的值,其余都处于现场保护环节。

路过走过,请指点一二~

那就完了~

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值