栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

(Java)算法基础2:递归/归并排序/荷兰国旗/快速排序

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

(Java)算法基础2:递归/归并排序/荷兰国旗/快速排序

递归

求中点位置,一般来说mid = (L+R)*0.5,这样写有一些问题

如果数组长度比较大,L+R可能会溢出,这样算出来的mid是不对的。

所以可以这么写

mid = L + (R-L)*0.5
//下面为右移一位(等同于除2),等同于上面
mid = L + (R-L)》》1

如下图就是递归的底层调用,相当于栈、树的后序遍历

p(0,2)相当于在0到2范围内求最大值
p(3,5)相当于在3到范围内求最大值

p(0,5)要调用要先调用p(0,2),p(3,5)压入栈中,
同理,p(0,2)要先调用p(0,1),p(2,2)压入栈中,即二分
p(0,1)要先调用p(0,0),p(1,1)压入栈中
p(0,0)算完,返回给p(0,1),左侧部分返回值有了,右侧还没有,继续让p(0,1)进栈,算p(1,1),算完以后返回p(0,1)的值就有了

往上依次类推

public static int process(int[] arr, int L, int R)
{
	//找出arr中最大值
	if(L == R)
	return arr[L];

	int mid = L + ((L-R) >> 1);
	int leftmax = process(arr, L,mid);
	int rightmax = process(arr,mid + 1, R);	
	return Math.max(leftmax,rightmax);
}
归并排序(mergeSort)


上图2:外排序:两个指针比较东西,放到外面新数组里面,最后再拷贝回来,这就叫外排序。
排序次数没有浪费,所以才能O(N*logN)
用指针。
左边小,将左边的数放入辅助数组,leftpoint右移1位,右边小,将右边的数字放入辅助数组,rightpoint右移一位,相等默认放左边。一边的指针越界以后,剩下的数字直接放。

最后将辅助数组copy到源数组。
)

上图,左侧一个下标指向1,右侧一个下标指向2

最后将辅助数组copy到源数组,如下图

package class02;
impoort java.util.Arrays;

public class Code01_MergeSort
{
	public static void mergeSort(int[] arr)
	{
		if(arr == null || arr.length < 2) 
		return;
		mergeSort(arr,0,arr.length-1);
	}

	public static void mergeSort(int[] arr,int L, int R)
	{
		if(L == R)
		return;
		int mid = L + ((R-L) >> 1);
		mergeSort(arr, L,mid);//递归,左边排好序
		mergeSort(arr,mid+1.R);//递归,右边拍好序
		merge(arr,L,mid,R);//整体排好序
	}
	
	public static void merge(int[] arr,int L,int mid,int R)
	{
		int[] help = new int[R-L+1];
		int p1 = L;//指针p1记录左边排好序的部分
		int p2 = mid+1;//指针p2记录右边排好序的部分
		int i = 0;//i记录辅助数组

		while(p1 <= mid && p2 <= R)
		{
			help[i ++] = arr[p1] < arr[p2]?arr[p1 ++]:arr[p2 ++;]
		}
		while(p1 <= mid)
		{
			help[i ++] = arr[p1 ++];
		}

		while(p2 <= R)
		{
			help[i ++] = arr[p2 ++];
		}

		for(int j = 0; j < help.length.j ++)
		{
			arr[L+j] = help[j];
		}
		
	}
}


//LeetCode,牛客上找逆序对问题来熟练一下。

上图逆序直接逆向思维,[1,3,4,2,5],1右边比1大的数字有4个,4*1.3右边比3大的数有2个,所以2*3,同理4*1+3*2+4*1+2*1=16

面对左组右组相等情况一定要先拷贝右组。

mergesort必考

package class02;
import java.util.Arrays;

public class02 Code02_SmallSum
{
	public static void smallSum(int[] arr)
	{
		if(arr == null || arr.length < 2)
		return;
		mergeSort(arr, 0, arr.length -1);
	}

	public static int mergeSort(int[] arr, int L, int R)
	{
		if(L == R)  return 0;

		int mid = L + ((R-L) >> 1);
		mergeSort(arr,L,mid);//左边排好序且返回小和的数量
		mergeSort(arr,mid+1,R);//右边排好序并且返回小和的数量
		merge(arr,L,mid,R);//整体排好序并且merge时求小和数量
		return mergeSort(arr,L,mid)+mergeSort(arr,mid+1,R)+merge(arr,L,mid,R)//p(0,1)递归为p(0,0)和p(1,1),
		//这时左右两边相等,l=r=mid=0返回的是0,p(0,1)接受到的是L=0,R =1,mid = 0,merge(arr,0,0,1)返回的值,小和数量
		
	}

	public static int merge(int[] arr, int L, int mid, int R)
	{
		int[] help = new int[R-L+1];

		int p1 = 0; //指向左边排好序的指针
		int p2 = mid + 1; //指向右边排好序的指针
		int i = 0;//辅助数组指针
		int res = 0;//记录小和乘以的个数,比如有3个比1大的数,那么最终
		
		while(p1 <= mid && p2 <= R)
		{
			res += arr[p1] < arr[p2]?arr[p1]*(R-mid+1):0;//比1大的数有3个,arr[p1]代指1,R-mid+1代指3,最终结果需要二者相乘
			help[i ++] = arr[p1] < arr[p2] ? arr[p1 ++]:arr[p2 ++]l//当左边数字等于右边数字时,要先copy右边数字
		}

		while(p1 <= mid)
		{
			help[i ++] = arr[p1 ++];
		}

		while(p2 <= R)
		{
			help[i ++] = arr[p2 ++];
		}

		for(int j = 0; j < help.length;j ++)
		{
			arr[L + j] = help[j];
		}

		return res;
	}
}
快速排序(空间复杂度O(logN),最差情况下空间复杂度O(N))

下图就是荷兰国旗问题思路,设置三个区间,分别是num区间。设置一个指针从第一个数开始向后指。每当发现当前指针所指的数比num小,那么就把它和小于区域右边第一个数交换(此时小于区域未包含此数),然后再把小于区域向右扩张,包含此数字。同理,大于区域采用同样的方法。

package class02;
import java.util.Arrys;

public class Code05_NetherlandsFlag{
		public static int[] partition(int[] arr,int L, int R, int p)
		{
			int less = L - 1;//小于区
			int more = R + 1;//大于区
			int i = L;//i指向数组数字,从第一个开始
			while(i < more//i是当前指针指向的数,more是大于区边界
			{
				if(arr[i] < p)
				swap(arr,++less,i ++);//将当前指针所指的数和小于区边界的下一个交换,然后扩展小于区
				else if(arr[i] > p)
				swap(arr,--more,i);//当前指针所指的数和大于区边界左边一个做交换,扩展大于区。由于这时候i所指的数字是交换过来的新数字,所以。i不用继续指向下一个数
				else
				i ++;
			}

			return new int[] {less +1, more -1};//等于区域的左右边界
		}
}

下图快排2.0

快排3.0才能O(N*logN)

package class02;
import java.util.Arrays;

public class Code06_QuickSort
{
	public static void quickSort(int[] arr)
	{
		if(arr == null || arr.length < 2)
		return;
		quickSort(arr,0,arr.length-1)
	}

	public static void quickSort(int[] arr, int l, int r)
	{
		if(l < r)
		{
			swap(arr,l+int(Math.random()*(r-l+1)),r);
			int[] p = partiton(arr,l,r);//用荷兰国旗函数,分为三个区域,返回等于区域的左边界和右边界,必定是个长度为2的数组
			quickSort(arr,l,p[0]-1);//小于区域有序,p[0]是等予区域左边界,p[0]-1是小于区域的右边界
			quickSort(arr,p[0]+1,r);//大于区域有序
		}
//这是一个处理arr[l..r]的函数
//默认以arr[r]作划分,arr[r] ->p ==p >p
//返回等于区域(左边界,右边界),所以返回一个长度为2的数组res,res[0],res[1]
	public static int[] partition(int[] arr,int l, int r);
	{
		int less = l -1;//小于区左边界
		int more = r;//大于区右边界,此时右边界最后一个是我们随机选取的那个数字

		while(l < r)
		{
			if(arr[l] < arr[r])
			{
				swap(arr,++less,l ++);
			}
			else if( arr[l] > arr[r]) 
			swap(arr, --more,l);//l不++,因为此时l指向交换过来的新数据
			else
			l ++
		}

	swap(arr,more,r);//交换最右边的随机选出的值到等于区域

	return new int[] {less+1,more};
	}
	}
}
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/781519.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号