分治法之图解快速排序

分治法Q3——快速排序

快速排序写法一:

package 分治;

public class 快速排序 {
	
	/**快速排序法 分治实现
	 * 重点在分区根据分区方法不同分为:单向扫描法  双向扫描法 
	 * template:1.terminator 2.process(prepare data,split your big problem)
	 * 3.drill down ,merge 4.reverse the current level states
	 * quickSort(nums,begin,end)
	 * 		-->subproblem:
	 * 					主元元素下标为mid	分为[begin,mid-1] [mid+1,end]
	 * 		-->subresult:
	 * 			quickSort(nums,begin,mid-1) quickSort(nums,mid+1,end)
	 * 
	 * 
	 */
	
	/**
	 * 对[begin,end]区间元素排序
	 */
	void quickSort(int []nums,int begin,int end) {
		if(begin<end) {//terminator 当区间只有一个元素时
		//求主元位置 分区
		int position=partion_bilateral(nums,begin,end);
		
		quickSort(nums,begin,position-1);
		quickSort(nums,position+1,end);
		}
	}
	//分区法一:单向扫描法
	public int partion_single(int[] nums,int begin,int end) {
		int pivot=nums[begin];
		int sp=begin+1;	//扫描指针 自左向右扫描待排序元素
		int bigger=end;//右侧指针
		while(sp<=bigger) {
			if(nums[sp]<=pivot) {//扫描元素不大于主元
				sp++;
			}else {//扫描元素大于主元
			swap(nums,sp,bigger);
			bigger--;
			}
		}
		swap(nums,bigger,begin);
//		System.out.print(nums[bigger]+": ");
//		for (int i : nums) {
//			System.out.print(i+" ");
//		}
		System.out.println();
		return bigger;
	}
	//分区法二:双向扫描法
	public int partion_bilateral(int[] nums,int begin,int end) {
		int pivot=nums[begin];
		int left=begin+1;	//左侧扫描指针 自左向右扫描待排序元素
		int right=end;//右侧扫描指针 自右向左扫描待排序元素
		//右侧指针扫到左侧指针左面则终止扫描
		while(left<=right) {
			//发现第一个大于主元的元素退出第一层while循环 left不动
			//考略极端情况下 数组全部有序 left一直++到越界 出现访问过界
			//所以应该先把left<=right放前面 简单与&& 回优先判掉left越界的情况 不进行nums[left]<=pivot的访问
			while(left<=right&&nums[left]<=pivot) left++;
			//发现第一个小于主元的元素退出第一层while循环 right不动
			while(left<=right&&nums[right]>pivot) right--;
			//两个指针还没错位时 交换指针的元素
			if(left<right) {
			swap(nums,left,right);
			}
		}
		//扫描结束 右侧指针的位置就是主元应该待的位置
		swap(nums,begin,right);
		System.out.println(nums[right]);
		return right;
	}
	public void swap(int[] array,int i,int j) {
		int temp=array[i];
		array[i]=array[j];
		array[j]=temp;
	}


	public static void main(String[] args) {
		int begin=0;
		int []nums=new int[] {8,9,6,2,1,7,5,3,10,4};
		//int []nums=new int[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
		int end=nums.length-1;
		快速排序 a=new 快速排序();
		a.quickSort(nums,begin,end);
		for (int i : nums) {
			System.out.print(i+" ");
		}
	}

}

 在所选取的轴点左右元素数量比较均匀的情况下 
 同时也是最好时间复杂度:T(n)=2*(n/2)+O(n) 	为O(n*logn)
 在全部有序情况下,此时没有达到分区的效果 每次切出来的区段为一个元素
 同时也是最坏时间复杂度;T(n)=T(n-1)+O(n) 	为O(n^2)
 空间复杂度:为递归调用次数logn*辅助空间O(1)  O(logn)

由递推式推出时间复杂度参见我之前文章算法Day2——递归笔记
单向扫描法
两种分区法
快速排序写法二:

package suanfa3;
import java.util.Arrays;
public class QuickSort {
	//[0,7]	
	public static void quickSort(int a[] ,int l,int r) {
		if(l>r)
			return;
		int pivot=partion(a, l, r);
		//System.out.println(l+" "+r+" "+pivot);
		quickSort(a, l, pivot-1);
		quickSort(a, pivot+1, r);
	}
	//找到基准元素的位置
	public static int partion(int a[],int l,int r) {
		int i=l;
		int target=a[i];
		//双指针扫描交换
		while(l<r) {
			//从右往左扫 遇到第一个小于基准元素的r指针就停下来
			while(l<r&&a[r]>=target) {
				r--;
			}
			//从左往右扫 遇到第一个大于基准元素的l指针就停下来
			while(l<r&&a[l]<=target) {//等于的时候指针也要移动
				l++;
			}
			//交换两指针停下来位置的元素 同时指针各自移动“前进”以进行新一轮扫描
			if(l<r) {
				int tmp=a[l];
				a[l]=a[r];
				a[r]=tmp;
			}
			//System.out.println(Arrays.toString(a));
		}
		a[i]=a[l];
		a[l]=target;
		//System.out.println(Arrays.toString(a));
		return l;
		
	}
	public static void main(String[] args) {
		int a[ ]={8,4,3,7,1,5,6,2};
		//int []a={5,4,3,9,1,5,6,2};
		quickSort(a, 0, a.length-1);
		System.out.println(Arrays.toString(a));
	}
}

排序过程:
在这里插入图片描述

排序结果:
在这里插入图片描述

随机化的快速排序算法

package suanfa3;
/**
* @author JohnnyLin
* @version Creation Time:2020年9月26日 上午9:58:01
* 类说明
*/

import java.util.Arrays;
import java.util.Random;


public class QuickSort_random {
	static Random random = new Random();
	//[0,7]	
	public static void quickSort(int a[] ,int l,int r) {
		if(l>r)
			return;
		
		int pivot=partion(a, l, r);
		//System.out.println(l+" "+r+" "+pivot);
		quickSort(a, l, pivot-1);
		quickSort(a, pivot+1, r);
		
	}
	//找到基准元素的位置
	public static int partion(int a[],int l,int r) {
		int x=random.nextInt(r - l + 1) + l;
		//基准元素值
		int target=a[x];
		//保留区间第一个元素下标
		int left=l;
		//初始化 交换该区间第一个元素与基准元素
		int t=a[l];
		a[l]=target;
		a[x]=t;
		//双指针扫描交换
		while(l<r) {
			//从右往左扫 遇到第一个小于基准元素的r指针就停下来
			while(l<r&&a[r]>=target) {
				r--;
			}
			//从左往右扫 遇到第一个大于基准元素的l指针就停下来
			while(l<r&&a[l]<=target) {//等于的时候指针也要移动
				l++;
			}
			//交换两指针停下来位置的元素
			if(l<r) {
				int tmp=a[l];
				a[l]=a[r];
				a[r]=tmp;
			}
		}
		a[left]=a[l];
		a[l]=target;
		return l;
		
	}
	public static void main(String[] args) {
		//int a[ ]={8,4,3,7,1,5,6,2};
		//int []a={5,4,3,9,1,5,6,2};
		int a[]=new int[1000];
       //产生1-100的逆序数组用于测试
		for(int i=0;i<1000;i++)
			a[i]=1000-i;
		double startTime = System.currentTimeMillis();
		for(int i=0;i<=100;i++)
			quickSort(a, 0, a.length-1);
		double endTime = System.currentTimeMillis();
		System.out.println("随机数快速排序时间:"+(endTime-startTime)/100);
		System.out.println(Arrays.toString(a));

	}

}

分别对优化前和优化后的快速排序算法进行测试:

在这里插入图片描述
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值