关于数组的算法题2:删除排序数组中的重复项,三数之和,最接近的三数之和,盛最多水的容器,除自身以外数组的乘积

1. 删除排序数组中的重复项

给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。

示例 1:
给定数组 nums = [1,1,2],
函数应该返回新的长度 2, 并且原数组 nums 的前两个元素被修改为 1, 2。
你不需要考虑数组中超出新长度后面的元素。

示例 2:
给定 nums = [0,0,1,1,1,2,2,3,3,4],
函数应该返回新的长度 5, 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4。
你不需要考虑数组中超出新长度后面的元素。

说明:
为什么返回数值是整数,但输出的答案是数组呢?
请注意,输入数组是以“引用”方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。
你可以想象内部操作如下:
// nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝
int len = removeDuplicates(nums);
// 在函数里修改输入数组对于调用者是可见的。
// 根据你的函数返回的长度, 它会打印出数组中该长度范围内的所有元素。
for (int i = 0; i < len; i++) {
print(nums[i]);
}

java答案

1. 双指针,一个指向没有重复数的数组的下标,一个指向原始数组的下标
class Solution {
    public int removeDuplicates(int[] nums) {
         if (nums.length == 0) return 0;
         int i = 0;
         for (int j = 1; j < nums.length; j++) {
             if (nums[j] != nums[i]) {
                i++;
                nums[i] = nums[j];
             }
         }
         return i + 1;
    }
}
时间复杂度是o(n),空间复杂度是o(1)

JavaScript答案:

1. 双指针
/**
 * @param {number[]} nums
 * @return {number}
 */
var removeDuplicates = function(nums) {
    if(nums.length==0){
       return 0;
    }
    var i=0;
    for(let j=1;j<nums.length;j++){
        if(nums[j]!=nums[i]){
           nums[++i] = nums[j];
        }
    }
    return i+1;
};
2. 使用数组的splice()方法直接删除数组
var removeDuplicates = function(nums) {
    if(nums.length==0){
       return 0;
    }
    for(var i=0;i<nums.length-1;i++){
        while(nums[i]==nums[i+1] && (i+1)<nums.length){
           nums.splice(i,1);
        }
    }
    return nums.length;
};
2. 三数之和

给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。

例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]

java答案

1. 先排序,然后使用双指针
class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        Arrays.sort(nums);//先排序
        List<List<Integer>> res = new ArrayList<>();
        for(int k = 0; k < nums.length - 2; k++){
            if(nums[k] > 0) break;  //若最小的数是大于0的,就直接break
            if(k > 0 && nums[k] == nums[k - 1]) continue; //确保不会出现重复的list集合
            int i = k + 1, j = nums.length - 1;
            while(i < j){
                int sum = nums[k] + nums[i] + nums[j];
                if(sum < 0){
                    while(i<j && nums[i] == nums[++i]) {}
                } else if (sum > 0) {
                    while(i < j && nums[j] == nums[--j]) {}
                } else {
                    res.add(new ArrayList<Integer>(Arrays.asList(nums[k], nums[i], nums[j])));
                    while(i<j && nums[i] == nums[++i]) {}
                    while(i < j && nums[j] == nums[--j]) {}
                }
            }
        }
        return res;
    }
}
时间复杂度是o(n^2)
2. 暴力法,三层for循环,但是要保证最后list集合中没有重复的数据

注意:

  1. java.util.Arrays.asList(Object):将需要转化的数组或数组元素转换为List.
    1、参数类型是数组元素的class,参数必须是对象或者对象数组,而原生数据类型不是对象,当传入一个原生数据类型数组时,asList 的真正得到的参数就不是数组中的元素,而是数组对象本身!
    原生数据类型,比如 int,short,long等,是没有这个属性的,具有 class 属性的是它们所对应的包装类 Integer,Short,Long。
    2、 用 asList 方法产生的 List 是固定大小的,这也就意味着任何改变其大小的操作都是不允许的。
    可以创建一个真正的ArrayList: List myList = new ArrayList(Arrays.asList(myArray));

JavaScript答案:

var threeSum = function(nums) {
    var alist = [];
    nums.sort((a, b) => a - b);
    for(let i=0;i<nums.length-2;i++){
       if(nums[i]>0){
          break;
       }
        if(i>0 && nums[i]===nums[i-1]){
           continue;
        }
        let x=i+1;
        let y = nums.length-1;
        while(x<y){
            var sum = nums[i]+nums[x]+nums[y];
            if(sum<0){
               while(x<y && nums[x]==nums[++x]){}   
            }else if(sum>0){
               while(x<y && nums[y]==nums[--y]){}         
            }else{
                //var list = [];
                //list.push(nums[i],nums[x],nums[y]);
                alist.push([nums[i],nums[x],nums[y]]);
                while(x<y && nums[x]===nums[++x]){}
                while(x<y && nums[y]===nums[--y]){}
            }
        }
    }
    return alist;
};

注意:
有重复数的数组排序: nums.sort((a, b) => a - b);

3. 最接近的三数之和

给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。

例如,给定数组 nums = [-1,2,1,-4], 和 target = 1.
与 target 最接近的三个数的和为 2. (-1 + 2 + 1 = 2).

java答案:

1. 先排序,然后使用双指针
class Solution {
    public int threeSumClosest(int[] nums, int target) {
        Arrays.sort(nums);
        int ans = nums[0] + nums[1] + nums[2];
        for (int k = 0; k < nums.length - 2; k++) {
            if(k > 0 && nums[k] == nums[k - 1]) continue;
            int start = k+1, end = nums.length - 1;
            while(start < end) {
                int sum = nums[start] + nums[end] + nums[k];
                if(Math.abs(target - sum) < Math.abs(target - ans))
                    ans = sum;
                if(sum > target)
                    end--;
                else if(sum < target)
                    start++;
                else
                    return ans;
            }
        }
        return ans;
    }
}
时间复杂度是o(n^2)
2. 暴力法
class Solution {
    public int threeSumClosest(int[] nums, int target) {
        if(nums.length<3)
            return 0;
        int min = nums[0]+nums[1]+nums[2];
        for(int i=0;i<nums.length-2;i++){
            for(int j=i+1;j<nums.length-1;j++){
                for(int x =j+1;x<nums.length;x++){
                    int sum = nums[i]+nums[j]+nums[x];
                    if(Math.abs(target - sum) <= Math.abs(target - min)){
                        min= sum;
                    }
                }
            }
        }
        return min;
    }
}
时间复杂度是o(n^3)

JavaScript答案:

/**
 * @param {number[]} nums
 * @param {number} target
 * @return {number}
 */
var threeSumClosest = function(nums, target) {
    nums.sort((a,b)=>a-b);
    let min = nums[0]+nums[1]+nums[2];
    for(var i=0;i<nums.length-2;i++){
        if(i>0 && nums[i]===nums[i-1]){
           continue;
        }
        var start=i+1;
        var end = nums.length-1;
        while(start<end){
            var sum = nums[i]+nums[start]+nums[end];
            if(Math.abs(target-sum)< Math.abs(target-min)){
               min = sum;
            }
            if(sum>target){
               end--;
            }else if(sum<target){
             start++;   
            }else{
                return min;
            }
        }
    }
    return min;
};

注意:

  • Math.abs(a); //求一个数的绝对值
  • Math.sqrt()//计算平方根
  • Math.cbrt()//计算立方根
  • Math.pow(a, b)//计算a的b次方
  • Math.max( , );//计算最大值
  • Math.min( , );//计算最小值
  • Math.ceil(a);//返回大的整数
  • Math.floor(a)//返回小的整数
  • Math.rint(a);// 四舍五入,返回double值 ,注意.5的时候会取偶数
  • Math.round(a);//四舍五入,float时返回int值,double时返回long值
4. 盛最多水的容器

给定 n 个非负整数 a1,a2,…,an,每个数代表坐标中的一个点 (i, ai) 。在坐标内画 n 条垂直线,垂直线 i 的两个端点分别为 (i, ai) 和 (i, 0)。找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。
说明:你不能倾斜容器,且 n 的值至少为 2。
在这里插入图片描述
图中垂直线代表输入数组 [1,8,6,2,5,4,8,3,7]。在此情况下,容器能够容纳水(表示为蓝色部分)的最大值为 49。

示例:
输入: [1,8,6,2,5,4,8,3,7]
输出: 49

java答案:

1. 快排,双指针,一个在前往后走,一个在后往前走,谁小谁走
class Solution {
    public int maxArea(int[] height) {
        if(height.length<2){
            return 0;
        }
        int max =0;
        int i=0;
        int j=height.length-1;
        int count=height.length-1;
        while(i<j){
            int sum=0;
            if(height[i]<=height[j]){
                sum=height[i]*count;
                i++;
                count--;
            }else{
                sum = height[j]*count;
                j--;
                count--;
            }
            if(max<sum){
                max = sum;
            }
        }
        return max;
    }
}
或
public class Solution {
    public int maxArea(int[] height) {
        int maxarea = 0, l = 0, r = height.length - 1;
        while (l < r) {
            maxarea = Math.max(maxarea, Math.min(height[l], height[r]) * (r - l));
            if (height[l] < height[r])
                l++;
            else
                r--;
        }
        return maxarea;
    }
}
时间复杂度是o(n),空间复杂度是o(1)
2. 暴力法
public class Solution {
    public int maxArea(int[] height) {
        int maxarea = 0;
        for (int i = 0; i < height.length; i++)
            for (int j = i + 1; j < height.length; j++)
                maxarea = Math.max(maxarea, Math.min(height[i], height[j]) * (j - i));
        return maxarea;
    }
}
时间复杂度是o(n^2),空间复杂度是o(1)

JavaScript答案:

/**
 * @param {number[]} height
 * @return {number}
 */
var maxArea = function(height) {
     if(height.length<2){
            return 0;
        }
        var max =0;
        var i=0;
        var j=height.length-1;
        var count=height.length-1;
        while(i<j){
            var sum=0;
            if(height[i]<=height[j]){
                sum=height[i]*count;
                i++;
                count--;
            }else{
                sum = height[j]*count;
                j--;
                count--;
            }
            if(max<sum){
                max = sum;
            }
        }
        return max;
};
5. 除自身以外数组的乘积

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

示例:
输入: [1,2,3,4]
输出: [24,12,8,6]
说明: 请不要使用除法,且在 O(n) 时间复杂度内完成此题。

java答案:

1. 不能使用除法,且时间复杂度是o(n),就不能使用双层for循环了,只能在乘积时记录
  除自身之外的数组乘积,就是这个数左边的乘积乘以右边的乘积,先计算左边乘积记录在最终返回的数组中,再计算右边的乘积
class Solution {
    public int[] productExceptSelf(int[] nums) {
        int[] result = new int[nums.length];
        if(nums.length<=1){
            return result;
        }
        result[0]=1;
        for(int i=1;i<nums.length;i++){
            result[i] = result[i-1]*nums[i-1];
        }
        int rightSum =1;
        for(int i=nums.length-1;i>=0;i--){
            result[i] = result[i]*rightSum;
            rightSum=nums[i]*rightSum;
        }
        return result;
    }
}
2. 上三角,下三角
class Solution {
    public int[] productExceptSelf(int[] nums) {
        int[] res = new int[nums.length];
        int p = 1, q = 1;
        for (int i = 0; i < nums.length; i++) {
            res[i] = p;
            p *= nums[i];
        }
        for (int i = nums.length - 1; i > 0 ; i--) {
            q *= nums[i];
            res[i - 1] *= q;
        }
        return res;
    }
}

JavaScript答案:

1. 上三角,下三角
/**
 * @param {number[]} nums
 * @return {number[]}
 */
var productExceptSelf = function(nums) {
    var res=[];
    if(nums.length<=1){
       return res;
    }
    var i=1,j=1;
    for(var x=0;x<nums.length;x++){
        res[x]=i;
        i=i*nums[x];
    }
    for(var x=nums.length-1;x>=0;x--){
        j=j*nums[x];
        res[x-1] = j*res[x-1];
    }
    return res;
};
2. 分治法,一分为二
var productExceptSelf = function(nums) {
    if(nums.length == 0) return nums;
    let res = new Array(nums.length);    //用来保存结果的数组
    res.fill(1); 
    let merge = (mul1, mul2, l, r, mid)=>{     //左右两个部分的数组相互作积,并返回当前区域数组的积
        for(let i = l; i <= mid; i++){
            res[i] = res[i]*mul2;
        }
        for(let j = mid+1; j <= r; j++){
            res[j] = res[j]*mul1;
        }
        return mul1*mul2;
    }
    let partation = (l, r)=>{  //划分数组,返回当前区域数组的积
        if(l == r){
            return nums[l];
        }
        let mid = Math.floor((l+r)/2);
        return merge(partation(l, mid), partation(mid+1, r), l, r, mid);  //递归调用划分函数
    }
    partation(0, nums.length-1);
    return res;
};
或
 var res;
var merge = function(num1,num2,l,mid,r){
    for(var i=l;i<=mid;i++){
        res[i] = res[i]*num2;
    }
    for(var i=mid+1;i<=r;i++){
        res[i]=res[i]*num1;
    }
    return num1*num2;
};
var partation = function(nums,l,r){
    if(l==r){
       return nums[l];
    }
    let mid = Math.floor((l+r)/2);
    return merge(partation(nums,l,mid),partation(nums,mid+1,r),l,mid,r);
};
var productExceptSelf = function(nums) {
    res=new Array(nums.length);
    if(nums.length<=1){
       return res;
    }
    res.fill(1);
    partation(nums,0,nums.length-1);
    return res;
};

注意:

  1. js中创建数组:var res = new Array(); //或var res=[];
  2. fill() 方法用于将一个固定值替换数组的元素。res.fill(1); 因为数组在新创建出来没有赋值时,都是undefined.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值