LeetCode刷题笔记:数组(更新中)

题型一、一个数组中的多数之和,求其等于某个数的所有可能(不能重复)
相关题目:1、11、15、16、18
解题思路:这类题用暴力算法最为简单直接,但作为中等题肯定没有这么简单。使用暴力算法时,有m个数之和就会有m个循环嵌套,其时间复杂度为O(n^m),往往会超时。这类题最好的解法是使用排序加双指针:先将数组从小到大排序,若要求m个数之和,则设立(m-1)个for循环嵌套,第(m-1)个数作为头指针,第m个数作为尾指针,前面(m-1)个数为从数组头开始依次递增1的关系,依次往后遍历;而第m个数则是从数组尾开始往前遍历。这样能保证在当前的最里层循环中,m数之和是从最大开始的(因为第m个数是从尾部开始往前遍历),只要当这m数之和大于目标数时,第m数的指针就往前移使总和减小,直到小于或等于目标数时或第m数的指针等于第(m-1)数的指针时移动停止。

这样的算法使循环少了一层,并且节省了一部分不必要的计算的时间。总体的时间复杂度为O(n^(m-1))
例题:

//15、三数之和,目标数为0
//排序+双指针
/*
 * 当我们需要枚举数组中的两个元素时,如果我们发现随着第一个元素的递增,第二个元素是递减的,
 * 那么就可以使用双指针的方法,将枚举的时间复杂度从 O(N^2)减少至 O(N)
 */
public List<List<Integer>> threeSum(int[] nums) {
	List<List<Integer>> list = new ArrayList<List<Integer>>();
	//数组排序
	Arrays.sort(nums);
	//i为最左边的指针,必须保证该指针后面至少有两个元素
	for(int i=0;i<nums.length-2;i++) {
		//当i为0或者i与前面遍历过的元素不同时进入循环(防止重复)
		if(i==0 || nums[i]!=nums[i-1]) {
			//k为最右边的指针
			int k = nums.length-1;
			//j为中间指针,必须保证j后至少有一个元素
			for(int j=i+1;j<nums.length-1;j++) {
				if(j==i+1 || nums[j]!=nums[j-1]) {
					//在k>j的前提下,当三者之和大于0时指针k左移(左移三者之和必减小)
					while(k>j && (nums[i]+nums[j]+nums[k]>0)) {
						k--;
					}
					//在k不等于j前提下,当三者之和为0时在list中加入结果
					if(k!=j && (nums[i]+nums[j]+nums[k]==0)) {
	    				List<Integer> temp = new ArrayList<Integer>();
	    				temp.add(nums[i]);
	    				temp.add(nums[j]);
	    				temp.add(nums[k]);
	    				list.add(temp);
					}
				}
			}
		}
	}
	return list;
}

但是目前我的算法还是不太行,有待优化。
每天一道LeetCode题,冲!!!

题型二、根据要求将数组某些元素从原位上删除,不能使用额外数组空间
相关题目:26、27
解题思路:这类题我刚拿上来,第一反应就是将要删除元素的后面元素都向前移动一位,以达到原位删除的目的。虽然这样的做法最终也AC了,但是用时相当多,因为除了外层的循环要遍历寻找指定的元素位置之外,内层还要有一个循环将该元素后面的元素前移一位。最好的解法是用双指针,快指针用于遍历寻找指定的元素位置,慢指针则首先指向数组头部,只当快指针找到要保留的元素时,将该元素保存到慢指针位置上(慢指针位置上的原本元素已被快指针先遍历过,确定了是保留还是舍弃),然后慢指针后移。这样的算法只需一个循环即可搞定,时间复杂度为O(n)
例题:

//27、删除数组中所有指定元素
//双指针
public int removeDuplicates(int[] nums) {
	//不重复的元素总数
	int len = 0;
	//当数组长度大于0时进入,否则输出长度为0的数组
	if(nums.length>0) {
		//len为慢指针,保存不重复元素
		len = 1;
		//i为快指针
		for(int i=1;i<nums.length;i++) {
			/*
			 * 如果nums[i]!=nums[i-1],说明i指针所在元素与其之前的元素都不同,
			 * 则将nums[i]赋值给nums[len],并将len后移
			 */
			if(nums[i]!=nums[i-1]) {
				nums[len] = nums[i];
				len++;
			}
		}
	}
	return len;
}

每天一道LeetCode题,冲!!!

题型三、给定一个目标值,要求找出数组中所有相加的结果等于该值的组合
相关题目:39、40
解题思路:需要找出所有可行解的题目,可用暴力递归+回溯。将数组、当前指针所在数组下标、离目标值的差作为递归函数的实参,每递归一次,指针和离目标值的差就做相应变化。
例题:

//39
class Solution1 {
//全局变量是根据自己想要的变化而变化
List<List<Integer>> res = new ArrayList<>(); //记录答案,为防止答案被修改,答案为全局变量
List<Integer> path = new ArrayList<>();  //记录路径,为防止路径被修改,路径为全局变量

public List<List<Integer>> combinationSum(int[] candidates, int target) {
    dfs(candidates,0, target);
    return res;
}
/**
 * 形参是根据路径的变化而变化
 * @param c	数组长度
 * @param u	当前遍历的数组下标
 * @param target	target和已通过路径之和的差
 */
public void dfs(int[] c, int u, int target) {
	//不合法解,从该路径返回
    if(target < 0) return ;
    //当该路径找到合法解时就从该路径返回
    if(target == 0)
    {
        res.add(new ArrayList<Integer>(path));
        return ;
    }
    for(int i = u; i < c.length; i++){
    	//若当前元素小于或等于target时将该元素纳入路径
        if( c[i] <= target)  
        {
            path.add(c[i]);
            dfs(c,i,target -  c[i]); // 因为可以重复使用,所以还是i
            path.remove(path.size()-1); //回溯,恢复现场
        }
    }
}
}

每天一道LeetCode题,冲!!!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值