快排和归并排序思维的应用

1.调整数组顺序使奇数位于偶数前面:输入一个整数数组,调整数组中的的数字的顺序,使得所有奇数位于数组前半部分,所有偶数位于数组的后半部分。
(1)利用快排的思路:首先在数组首部和尾部设置left和right指针,如果left指针遇到奇数则left++即向后移动;如果right指针遇到偶数则right–即向前移动。
当left遇到偶数,right遇到奇数时,交换两者所指的数。如此不断循环,直到left>right
(2)利用归并排序的思路:首先开辟一个辅助数组help,在辅助数组的首部设置current指针;在原数组首部设置left指针,原数组尾部设置right指针;current指针依次扫描辅助数组,遇到奇数赋给left所指的原数组中,current++,left++;遇到偶数赋给right所指的原数组中,current++,right–;直到current扫描完整个辅助数组为止。

public class 调整数组顺序奇数在左偶数在右 {
public static void main(String[] args) {
	int []a= {3,5,2,4,6,1,9,8};
	for(int i=0;i<a.length;i++)
	{
		System.out.print(a[i]+" ");
	}
	Quick(a,0,a.length-1);
	System.out.println();
	System.out.println("-----------------");
	for(int i=0;i<a.length;i++)
	{
		System.out.print(a[i]+" ");
	}
	
}
//快排法
private static void Quick(int[] a, int p, int r) {
	// TODO Auto-generated method stub
	int left=p;//左指针
	int right=r;//右指针
	while(left<=right)//截止条件是交错
	{
		if(a[left]%2==1)
		{
			left++;
		}
		if(left>right)
		{
			break;
		}
		if(a[right]%2==0)
		{
			right--;
		}
		if(left>right)
		{
			break;
		}
		if(a[left]%2==0&&a[right]%2==1)
		{
			int term=a[left];
			a[left]=a[right];
			a[right]=term;
		}
	}
}
//归并法
private static void Merge(int[] a, int p, int r) {
	// TODO Auto-generated method stub
	int []help=new int[a.length];
	for(int i=p;i<=r;i++)
	{
		help[i]=a[i];
	}
	int current=p;//辅助数组指针
	int left=p;//原数组的左指针
	int right=r;//原数组右指针
	while(current<=r)
	{
		if(help[current]%2==1)//如果是奇数
		{
			a[left]=help[current];
			current++;
			left++;
		}
		else//如果是偶数 
		{
			a[right]=help[current];
			current++;
			right--;
		}
	}
}
}

2.求第k个元素,以尽量高的效率求出一个乱序数组中按数值顺序的第k个元素值
思路:题目要求输入k值,求第k个数(k从1开始计数)。
直接思路:对这一组数直接排序之后,输出数组下标k-1即可。
因为这里要求高效率求,改进之处是在分区之后,先判断一下k和主元下标的关系,如果小于主元下标,就只对左侧区间进行递归;如果大于主元下标,就对右侧区间进行递归,不用两侧都递归求解,这样就可以提升一点效率。
(为什么k直接与主元下标比较,因为一趟排序之后主元会放到这组数的合适位置即最终下标,与主元下标比较就可以判断要求的第k个数所在哪个区间)

import java.util.*;
//此题意思是给了一组无序的数,然后输入一个k,求第k个数(k从1开始计数)
public class 最快效率求出乱序数组中的第k小的数 {
public static void main(String[] args) {
	int []a= {5,2,3,8,12,0,6};
	Scanner sc=new Scanner(System.in);
	int k=sc.nextInt();//找第k个数
	System.out.println("第"+k+"个数为"+Select(a,0,a.length-1,k));
	sc.close();
}

private static int  Select(int[] a, int p, int r, int k) {
	// TODO Auto-generated method stub
	int h=index(a,p,r);
	int s=h-p+1;//从1开始计数的位置
	if(k==s) return a[h];
	if(s>k)
	{
		return Select(a, p, h-1, k);
	}
	else
	{
	   return Select(a,h+1,r,k-s);
	}
	
}
//单向扫描
private static int index(int[] a, int p, int r) {
	// TODO Auto-generated method stub
	int x=a[p];//定主元
	int scan=p;
	int bigger=r;
	while(scan<=bigger)
	{
		if(a[scan]<=x)
		{
			scan++;
		}
		else 
		{
			int term=a[scan];
			a[scan]=a[bigger];
			a[bigger]=term;
			bigger--;
		}
	}
	//最后交换主元和bigger位置
	int term=a[p];
	a[p]=a[bigger];
	a[bigger]=term;
	return bigger;
}
}

3.给了一组数,求哪个数在数组中出现的次数超过了数组长度的一半?
方法一:利用Arrays.sort内置排序函数,将数组排好序。因为一旦排序之后,出现次数超过一半的元素肯定出现在下标为N/2处。
方法二:Arrays.sort底层也是快速排序,但是并不需要都排好;只要求出下标为N/2即可,这样可以提升一点效率。这时可以利用快速排序分区算法,只求第N/2个数,只对一侧进行递归排序。
方法二:

public class 求数组中出现次数超过半数的元素 {
public static void main(String[] args) {
	int []a= {5,2,2,2,3};
	for(int i=0;i<a.length;i++)
	{
		System.out.print(a[i]+" ");
	}
	Select(a,0,a.length-1,a.length/2);
	System.out.println();
	System.out.println("--------------");
	System.out.println("出现超过半数的元素为"+a[a.length/2]);
}

private static int  Select(int[] a, int p, int r, int k) {
	// TODO Auto-generated method stub
	int h=index(a,p,r);
	int h2=h+1;//从1开始计数时的顺序
	if(k==h2) return a[h];
	if(k<h2) 
	{
		return Select(a, p, h-1, k);
	}
	if(k>h2)
	{
		return Select(a, h+1, r, k-h2);
	}
	return -1;
}
//双向扫描分区算法
private static int  index(int[] a, int p, int r) {
	// TODO Auto-generated method stub
	//定主元
	int x=a[p];
	int begin=p+1;//头指针
	int end=r;//尾指针
	while(begin<=end)
	{
		if(a[begin]<=x)
		{
			begin++;
		}
		if(begin>end)//交错之后立即终止
		{
			break;
		}
		if(a[end]>x)
		{
			end--;
		}
		if(begin>end)//交错之后立即终止
		{
			break;
		}
		if(a[begin]>x&&a[end]<=x)
		{
			int term=a[begin];
			a[begin]=a[end];
			a[end]=term;
		}
	}
	//最后交换主元和end指向的元素
	int term =a[p];
	a[p]=a[end];
	a[end]=term;
	return end;
}
}

方法三:如果题目限定不允许移动数组元素,那么就不能使用排序相关的算法了。解决办法:两两消除法
1.首先确定一个候选值(一般是数组第0个元素),设置变量次数t为1
2.从数组中下标为1的元素开始遍历,当与候选值相同时,次数加一即t++;如果与候选值不同时次数减一即t–;如果此时t已经为0,说明前面都已各自消除掉,然后将此时的值取代候选值,次数t为1
3.当遍历完之后,输出次数的候选值即可

//方法三:不允许移动原有数组,找出元素个数超过一半的元素
public class 发帖水王 {
public static void main(String[] args) {
	int []a= {1,2,3,4};
	//因为要找个数超过一半的元素,从数组开头两两消掉其他元素,最后剩下的就是要求的
	int x=a[0];//候选值
	int t=1;//出现次数
	for(int i=1;i<a.length;i++)
	{
		if(t==0)
		{
			x=a[i];//替换候选值
			t=1;
			continue;
		}
		if(a[i]==x)
		{
			t++;
		}
		else 
		{
			t--;
		}
	}
	System.out.println("这个元素就是"+x);
}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值