(算法很美)查找和排序第二部分

(算法很美)查找和排序第二部分

3.1 分治法(10种排序算法要熟练掌握)
一、分治法
1、分治法:将原问题划分为若干个规模较小的而结构与原问题一致的子问题;递归地解决这些子问题,然后再合并其结果,就得到问题的解。
2、分治算法优点之一:容易确定运行时间
3、分治算法在每一层都有三个步骤:
(1)分解:将原问题分解成一系列子问题
(2)解决:递归地解各子问题。若子问题足够小,则直接有解
(3)合并:将子问题的结果合并成原问题的解
4、使用分治的目的:(1)解决问题 (2)提升效率

3.2 快速排序算法
快排的重点在于划分,归并的重点在于合并

    //快排主体递归函数()调用分区函数
	//p和r分别是需要快排的片段
	public static int[] part1(int[] A,int p,int r){
		if(p<r){
		    //q为分区后主元在数组中的位置
			int q=partition2(A,p,r);
			//子问题用相同的方法
			part1(A,p,q-1);
			part1(A,q+1,r);
		}
        return A;
	}

一、一遍单向扫描法
1、思想:用两个指针将数组划分为三个区间
扫描指针左边是确认小于等于主元的
扫描指针到某个指针中间是未知的,因此我们将第二个指针称为未知区域末指针,末指针的右边区域为确认大于主元的元素
2、主元一般选取数组的第一个元素
3、一遍扫描结束标志是末指针在扫描指针左侧,而且末指针指向的是小于等于主元的最后一个元素,扫描指针指向的是大于主元的第一个元素

	//单向扫描分区方法
	public static int partition(int[] A,int p,int r){
		int a;//用于交换
		int scanner=p+1;
		int bigger=r;
		while(scanner<=bigger){
			if(A[scanner]<=A[p])
				scanner++;
			else{
				//交换
				a=A[scanner];
				A[scanner]=A[bigger];
				A[bigger]=a;
				bigger--;
			}
		}
		a=A[p];
		A[p]=A[bigger];
		A[bigger]=a;
		return bigger;
	}

二、双向扫描法
1、思路:头尾指针往中间扫描,从左找到大于主元的元素,从右找到小于等于主元的元素二者交换,继续扫描,直到左侧无大元素,右侧无小元素
2、有脸两头有两个指针,left和right,扫描结束的标志是right>left,right指向的是左边最后一个小于等于的,left指向的是右边第一个大于的,让right跟主元(第一个元素)交换就可以了。

    //双向扫描分区法
	public static int partition2(int[] A,int p,int r){
		int a;
		int left=p+1;
		int right=r;
		while(left<=right){
			while(left<=right&&A[left]<=A[p])//注意加限制条件,不能越界
				left++;
			while(left<=right&&A[right]>A[p])
				right--;
			if(left<right){
				a=A[left];
			    A[left]=A[right];
			    A[right]=a;
			}
		}
		a=A[right];
		A[right]=A[p];
		A[p]=a;
		return right;
	}

三、有相同元素值的快速排序——三分法(对性能提升不大)
1、安排三个指针:scanner、equal、bigger;scanner向右扫描,遇到小于主元的就++,遇到等于主元的则equal=scanner,继续向前,若遇到大于主元的就与bigger交换值,bigger–;遇到等于主元的就scanner++;遇到小于主元的,就交换scanner和equal所指向的值,equal++,scanner++。 最后返回的是两个数,equal(等于主元片段的第一个元素)和bigger(等于主元的最后一个元素)

3.3 快排在工程实践中的优化
(快排是工程中应用最多的排序算法)
1、快排中,如果主元选的不适中,那么可能出现的最差情况的时间复杂度是O(n2)

一、三点中值法
1、思想:在开头,中间,最后三个元素中选中间值作为主元,若中间或最后的元素被选中作为主元,则将其值与第一个元素互换,然后按照常规的快排步骤进行。

二、绝对中值数(用的不多)
1、每五个为一组,每个组都进行插入排序,取出每一组的中间值,这些中间值再进行插入排序,得到中间值作为主元。

	//获取绝对中值数(对性能提升不是很大)
	public static int getMedian(int[] arr,int p,int r){
		int size=r-p+1;//数组的长度
		//每五个元素为一组
		int groupSize=(size%5==0)?(size/5):(size/5+1);
		//该数组用于存储各小组的中值
		int[] medians=new int[groupSize];
		int indexOfMedians=0;
		//对每一组进行插入排序
		for(int j=0;j<groupSize;j++){
			//单独处理最后一组,因为最后一组可能不满5个元素****
			if(j==groupSize-1){
				//InsertionSort是插入排序方法
				InsertionSort(arr,p+j*5,r);//排序最后一组
				//取最后一组中间的元素
				medians[indexOfMedians++]=arr[(p+j*5+r)/2];
			}else{
				InsertionSort(arr,p+j*5,p+j*5+4);//当前组插入排序
				medians[indexOfMedians++]=arr[p+j*5+2];//当前组中间那个  
			}
		}
		//对medians排序
		InsertionSort(medians,0,groupSize-1);
		return medians[medians.length/2];
	}

三、待排序列表较短时,用插入排序
1、在n<=8的时候,用插入排序比用快排省时间。
因为虽然在表面上看插入排序的时间复杂度为O(n),但实际上是n(n-1)/2;快排的时间复杂度表面上是nlgn,但实际上是n(lgn+1);在n<=8是,若考虑到系数和所加的项,那么其实插入排序比快排省时间。

	//在待排序列表较短时,用插入排序
	public static int[] part11(int[] A,int p,int r){
		if(p<r){
			if(p-r+1<=8){
				InsertionSort(A,p,r);
			}else{
		    //q为分区后主元在数组中的位置
			int q=partition2(A,p,r);
			//子问题用相同的方法
			part1(A,p,q-1);
			part1(A,q+1,r);
			}
		}
        return A;
	}

一些java的补充知识
//java的String中的substring方法:
public String substring(int beginIndex,int endIndex)
从下标为beginIndex开始取,到下标为endIndex结束(不包括endIndex的元素),返回截取的字符串
//String、StringBuffer、StringBuilder的区别
1、String类型是不可变的对象,因此在每次对String类型进行改变的时候都等同于生成一个新的String对象,然后指针指向新的String对象,这样效率低下而且浪费有限的内存空间。 在操作少量的数据时用String
2、StringBuffer和StringBuilder类的对象可以多次被修改,并且不产生新的未使用对象,多线程操作字符串缓冲区下操作大量数据用StringBuffer,单线程操作字符串缓冲区下操作大量数据用StringBuilder

//Integer.parseLnt()

  1. 我们平时用到Integer.parseInt(“123”);其实默认是调用了
  2.       int i =Integer.parseInt("123",10);  
    
  3. 其中10代表的默认是10进制的,转换的过程可以看成:
  4.          i=  1*10*10+2*10+3  
    
  5. 若是
  6.        int i = Integer.parseInt("123",16);  
    
  7. 即可以看成:
  8.          i  = 1*16*16+2*16+3  
    

radix的范围在2-36之间超出范围就会抛出异常,s的长度要小于等于7,不然会出错。
//Integer.toString(int par1,int par2), par1表示要转成字符串的数字,par2表示要转成的进制表示

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值