除自身以外数组的乘积

给定长度为 n 的整数数组 nums,其中 n > 1,返回输出数组 output ,其中 output[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积。

示例:

输入: [1,2,3,4]
输出: [24,12,8,6]

说明: 不要使用除法,且在 O(n) 时间复杂度内完成此题。

进阶:
你可以在常数空间复杂度内完成这个题目吗?( 出于对空间复杂度分析的目的,输出数组不被视为额外空间。)

解题思路:

如果能用除法的话,问题会变得很简单,只需要将所有整数的乘积求出,然后除以当前数,即为除了当前数以外的所有数字的乘积(不考虑有0的情况)。在不能使用除法的情况下,可以通过减法来实现除法,但是当数组中元素个数很大的时候,减法的次数会变得非常之多,时间复杂度也会远远超过O(n)。在这种情况下,考虑用二进制移位和加减的方式来实现除法,时间复杂度为降为O(n)。

另外,还要考虑数组中有0的情况,如果0的个数不止一个的话,则得到的所有输出全为0;

如果0的个数只有一个的话,那么除了0所在位置以外,所有位置的输出都为0,只有0所谓位置的输出为其他所有数字的乘积;

如果0的个数为0,那么就按照正常情况来处理,这样就把情况分为三种,加一个if判断,不增加时间复杂度。

注意:二进制移位加减的时候,需要考虑正负号,最好的做法是先把正负号提出来,单独处理,所有的计算都是基于正数。

    public static int[] productExceptSelf(int[] nums) {
		int length = nums.length;
    	int[] result = new int[length];
		int chengji = 1;
		int temp = 1;//temp用来存放当前数字
		int flag=0;//flag用来表示0的个数
		int flag1 = 0;//flag用来记录0出现的位置
		//需要考虑乘积是否为0,也就是数组中有没有0元素的存在
		for (int i=0; i < length; i++) {
			if(nums[i]==0){
				flag1=i;
				flag++;
			}
		}
		//如果0的个数不止一个,也就是flag>1,那么所有数都为0
		if(flag>1){
//			System.out.println("flag>1");
			for (int i=0; i < length; i++) {
				result[i]=0;
			}
			return result;
		}else if(flag==1){
//			System.out.println("flag=1");
			//如果0的个数只有1个,也就是只有1个0,那么只有该0的位置有乘积,其他位置的乘积全部为0
			nums[flag1]=1;
//			System.out.println("flag1="+flag1);
			for (int i=0; i < length; i++) {
				System.out.println("nums[i]="+nums[i]);
				chengji = chengji * nums[i];
				System.out.println("chengji="+chengji);
			}
			for (int i=0; i < length; i++) {
				result[i]=0;
			}
			result[flag1] = chengji;
			return result;
		}else if(flag==0){
//			System.out.println("flag=0");
			//如果乘积不为0,就按照正常步骤处理
			// 先把数组的乘积求出来
			for (int i = 0; i < length; i++) {
				chengji = chengji * nums[i];
			}
//			String chengji1 = Integer.toBinaryString(chengji);
			for (int i = 0; i < length; i++) {
				temp = nums[i];// 获取当前的数字
				// 二进制相除,chengji/temp
				int a = myDiv(chengji, temp);
				result[i]=a;
			}
		}
		return result;
	}

	public static int myDiv(int a, int b) {
		boolean flag = (a < 0) ^ (b < 0);//如果一正一负,则返回的时候加-
		//把所有操作变成正数相除
		if (a < 0)
			a = -a;
		if (b < 0)
			b = -b;
		//除数比被除数大,就不用除了(这种情况在正常情况下是不会发生的)
		if (a < b)
			return 0;
		int msb = 0;
		//以24(11000)和4(100)为例,b需要向左移动 msb=3 位(100000=32),才能大于等于a
		while ((b << msb) < a) {
			msb++;
		}
		int q = 0;
		for (int i = msb; i >= 0; i--) {
			//b左移i位,如果大于a,则跳出本次循环
			if ((b << i) > a)
				continue;
			/* 还是以24(11000)和4(100)为例,
			 * b向左移动 i=3 位(32=100000)的时候,大于a,忽视下面的步骤,结束本次循环
			 * b向左移动 i=2 位(16=10000)的时候,小于等于a,进入下面的步骤,处理后,得到余数 a=24-16=8,q=000|100=100
			 * b向左移动 i=1位(8=1000)小于等于a=8,进入下面的步骤,处理后,得到余数 a=8-8=0,q=100|10=110
			 * 结束循环
			 */
			//q=商(循环相加)
			q |= (1 << i);//q=q|(1 << i),例如000和100或的结果是100
			//a=余数
			a -= (b << i);//用a减去左移后的b,得到差值,24-16=8
		}
		if (flag)
			return -q;
		return q;
	}

另外一种比较简便的方法:

采取分治法,把整个数组分成两部分,一部分从左到右计算乘积,另一部分从右到左计算乘积。然后用左右的乘积获得当前数字的乘积。

具体做法就是:

利用2个辅助数组,一个保存顺序遍历的乘积,一个保存逆序的乘积(可以一次遍历完成)。

如数组N=[1,2,3,4],我们一次遍历的时候,得到顺序的乘积为ins=[1,2,6,24],逆序的乘积为ret=[24, 24, 12, 4],那么最后对于数组N中的除当前元素之外其余各元素的乘积,我们就可以利用ins和ret这两个数组来计算得到,假设输出数组为output,对于output中的每一个元素,存在output [i] = ins [i-1] * ret [i+1](注意边界条件),最后得到的结果为output = [24, 12, 8, 6]

    public static int[] productExceptSelf(int[] nums) {
		int length = nums.length;
    	int[] result = new int[length];
    	int[] result1 = new int[length];//从左到右的乘积
    	int[] result2 = new int[length];//从右到左的乘积	
    	int chengji1 =1;
    	int chengji2 =1;
		//从左往右乘
		for (int i=0; i

 

你可能感兴趣的:(leetcode)