力扣刷题之部分数组经典算法题目

作者vantoker

LeetCode1: 两数之和

题目说明:

说明:给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出和为目标值 的那两个整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。
注意:
1,返回结果为数组下标
2,只会存在一个有效答案
3,你可以按任意顺序返回答案

代码:

//经典解法
public int[] twoSum(int[] nums, int target) {
    Map<Integer, Integer> map = new HashMap<>();
    for (int i = 0; i < nums.length; i++) {
        if (map.containsKey(target - nums[i])) {
            return new int[]{map.get(target - nums[i]), i};
        } else
            map.put(nums[i], i);
    }
    return new int[2];
}

执行结果:

执行用时:0 ms, 在所有 Java 提交中击败了100.00% 的用户
内存消耗:38.7 MB, 在所有 Java 提交中击败了47.08% 的用户

LeetCode59: 螺旋矩阵II

题目说明:

说明:输入一个整数n,返回一个n*n的二维数组存放1到n2的所有元素,顺时针螺旋排列。

代码:

注意点:
1,本题的难点在于边界处理,行尾列尾到头之后,需要将行列的索引进行修正。
2,设置合理的右上边界,和左下边界,并且随着num位置的移动,适时的修改边界值的大小
3,外部循环条件,不可设置为num<=numLimit,会形成死循环,因为num达到numLimit之后不会再进入任何一个自循环进行自增。

public int[][] generateMatrix(int n) {
    int[][] res = new int[n][n];
    int num = 0, row = 0, col = -1;
    int numLimit = n * n, leftUpLimit = 0, rightDownLimit = n - 1;
    while (num != numLimit) {
        while (++col <= rightDownLimit) {
            res[row][col] = ++num;
        }
        col--; //索引修正
        while (++row <= rightDownLimit) {
            res[row][col] = ++num;
        }
        row--;
        rightDownLimit--; //边界值修正
        while (--col >= leftUpLimit) {
            res[row][col] = ++num;
        }
        col++;
        leftUpLimit++;

        while (--row >= leftUpLimit) {
            res[row][col] = ++num;
        }
        row++;
    }
    return res;
}

运行结果:

执行用时:0 ms, 在所有 Java 提交中击败了100.00% 的用户
内存消耗:36.7 MB, 在所有 Java 提交中击败了20.52% 的用户

LeetCode66: 加一

题目说明:

说明:给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。
最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。
你可以假设除了整数 0 之外,这个整数不会以零开头。

代码:

注意点:
1,注意几种特殊的情况,会产生进位或者进位让数组长度加一。
2,如果当前元素没有产生进位,那么本次结束就可以return。
3,判断的核心不是carry是否为1,而是curNum是否大于等于10.

public int[] plusOne(int[] digits) {
    int carry = 1, curNum = 0;
    for (int i = digits.length - 1; i >= 0; i--) {
        curNum = digits[i] + carry;
        if (curNum >= 10) {  //如果产生进位
            curNum = curNum % 10;
            digits[i] = curNum;
        } else {            //如果当前位置没有进位,则return
            digits[i] = curNum;
            return digits;
        }
    }
    int[] res = new int[digits.length + 1];  //如果运行到当前位置,说明原数组所有位置都产生了进位
    res[0] = 1;
    return res;
}

执行结果:

执行用时:0 ms, 在所有 Java 提交中击败了100.00% 的用户
内存消耗:37.1 MB, 在所有 Java 提交中击败了19.22% 的用户

剑指Offer03: 数组中重复的数字

题目说明:

说明:在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,
也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
限制:2 <= n <= 100000

代码一:

//解法一:利用Set集合的特性
public int findRepeatedNumber(int[] nums){
    Set<Integer> set = new HashSet<>();
    for (int num : nums) {
        if (set.contains(num)){
            return num;
        }else
            set.add(num);
    }
    return -1;
}

执行结果:

执行用时:8 ms, 在所有 Java 提交中击败了27.03% 的用户
内存消耗:47.2 MB, 在所有 Java 提交中击败了37.71% 的用户

代码二:

//解法二:排序+遍历
public int findRepeatedNumber2(int[] nums){
    Arrays.sort(nums);
    for (int i = 1; i < nums.length; i++) {
        if (nums[i]==nums[i-1])
            return nums[i];
    }
    return -1;
}

执行结果:

执行用时:3 ms, 在所有 Java 提交中击败了57.86% 的用户
内存消耗:46.1 MB, 在所有 Java 提交中击败了74.85% 的用户

代码三:

//解法三:原地置换
public int findRepeatedNumber3(int[] nums){
     int temp;
     for (int i = 0; i < nums.length; i++) {
         while (nums[i] != i) {  //如果索引i位置的元素不为i,则进入一下处理
             if (nums[i] == nums[nums[i]])  //如果索引为i的位置处数字也为nums[i],说明存在重复
                 return nums[i];
             /*temp = nums[i];
                nums[i] = nums[nums[i]];
                nums[nums[i]] = temp;*/  //此处不可以这样调用,因为nums[i]在第二步发生了改变,不可作为原本的索引了。
             temp = nums[i];
             nums[i]=nums[temp];
             nums[temp]=temp;
         }
     }
     return -1;
 }

执行结果:

执行用时:0 ms, 在所有 Java 提交中击败了100.00% 的用户
内存消耗:46.2 MB, 在所有 Java 提交中击败了63.94% 的用户

LeetCode75: 颜色分类

题目说明:

说明:给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,
并按照红色、白色、蓝色顺序排列。
此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。

代码一:

//解法一:计数+填充数字
public void sortColors(int[] nums) {
    int red = 0, white = 0, blue = 0;
    for (int num : nums) {
        if (num == 0)
            red++;
        else if (num == 1)
            white++;
        else
            blue++;
    }
    int index = -1;
    while (red-- > 0) {
        nums[++index] = 0;
    }
    while (white-- > 0) {
        nums[++index] = 1;
    }
    while (blue-- > 0) {
        nums[++index] = 2;
    }
}

执行结果:

执行用时:0 ms, 在所有 Java 提交中击败了100.00% 的用户
内存消耗:37.2 MB, 在所有 Java 提交中击败了11.64% 的用户

代码二:

注意点:
1,明确first,second指向的准备位置,否则在判断时把小于等于号写为小于号就会出错。
2,first左边位置(不包含本身位置)的元素都是0,second右边位置(不包含本身位置)的元素都是2.

//解法二:双指针快排(一趟遍历)
public void sortColors2(int[] nums) {
    int i = 0;
    int first = 0, second = nums.length - 1;//两个边界指针
    int temp;
    while (i <= second) {  //注意此处是i小于等于边界值second,不是小于second,也不是小于nums.length
        if (nums[i] == 1) {
            i++;
        } else if (nums[i] == 0) {
            temp = nums[i];
            nums[i] = nums[first];
            nums[first] = temp;
            first++;
            i++;
        } else {
            temp = nums[i];
            nums[i] = nums[second];
            nums[second] = temp;
            second--;
        }
    }
}

执行结果:

执行用时:0 ms, 在所有 Java 提交中击败了100.00% 的用户
内存消耗:37 MB, 在所有 Java 提交中击败了55.35% 的用户

LeetCode54: 螺旋矩阵

题目说明:

说明:给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素。
提示:
m == matrix.length
n == matrix[i].length
1 <= m, n <= 10
-100 <= matrix[i][j] <= 100

代码一:

注意点:
1,本题和螺旋矩阵II的区别是,矩阵的行列值不相同,那么设置边界值就需要不同。
2,本题在每个while循环之后都进行了判断,这是防止添加结束之后,不进入while循环,但是会改变边界值导致可能进入后面的while循环。
3,上述说的后面到终点时,会可能继续会进入while循环的可能原因是只设置了三个边界值,如果设置四个边界值,可能就不需要每个while循环之后添加判断。

public List<Integer> spiralOrder(int[][] matrix) {
    int m = matrix.length, n = matrix[0].length, leftLimit = 0;
    int i = 0, j = -1;
    int count = 0, countLimit = m * n;
    List<Integer> res = new ArrayList<>();
    while (true) {
        while (++j < n) {
            res.add(matrix[i][j]);
            count++;
        }
        if (count >= countLimit)
            break;
        j--;
        n--;
        while (++i < m) {
            res.add(matrix[i][j]);
            count++;
        }
        if (count >= countLimit)
            break;
        i--;
        m--;
        while (--j >= leftLimit) {
            res.add(matrix[i][j]);
            count++;
        }
        if (count >= countLimit)
            break;
        j++;
        leftLimit++;
        while (--i >= leftLimit) {
            res.add(matrix[i][j]);
            count++;
        }
        if (count >= countLimit)
            break;
        i++;
    }
    return res;
}

执行结果:

执行用时:0 ms, 在所有 Java 提交中击败了100.00% 的用户
内存消耗:36.6 MB, 在所有 Java 提交中击败了47.37% 的用户

代码二:

//设置四个边界值的尝试
public List<Integer> spiralOrder2(int[][] matrix) {
    int m = matrix.length, n = matrix[0].length, leftUPLimit = 0, leftDownLimit = 0;
    int i = 0, j = -1;
    int count = 0, countLimit = m * n;
    List<Integer> res = new ArrayList<>();
    while (count != countLimit) {
        while (++j < n) {
            res.add(matrix[i][j]);
            count++;
        }
        j--;
        n--;
        while (++i < m) {
            res.add(matrix[i][j]);
            count++;
        }
        i--;
        m--;
        while (--j >= leftDownLimit) {
            res.add(matrix[i][j]);
            count++;
        }
        j++;
        leftDownLimit++;
        leftUPLimit++;
        while (--i >= leftUPLimit) {
            res.add(matrix[i][j]);
            count++;
        }
        i++;
    }
    return res;

}

执行结果:

结论:
1,尝试不成功,并不是边界值少的问题,而是队列的行和列数不相等的关系。
2,由于m,n不等,导致了左边的边界在终点处无法限制终点值往左边移动。

LeetCode560: 和为k的子数组

题目说明:

说明:给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数。
注意 :
数组的长度为 [1, 20,000]。
数组中元素的范围是 [-1000, 1000] ,且整数 k 的范围是 [-1e7, 1e7]。

代码一:

//暴力解法
public int subarraySum(int[] nums, int k) {
    int count = 0, sum = 0;
    int n = nums.length;

    for (int i = 0; i < n; i++) {
        for (int j = i; j < n; j++) {
            sum += nums[j];
            if (sum == k)
                count++;
        }
        sum = 0;
    }
    return count;
}

执行结果:

执行用时:1102 ms, 在所有 Java 提交中击败了37.22% 的用户
内存消耗:41.5 MB, 在所有 Java 提交中击败了27.24% 的用户

代码二:

注意点:
1,利用前缀和来保留前面的累加和,后面可以直接调用,但是执行结果似乎还没有暴力解法的效率高。
2,调用前缀和时注意preNum[j]表示的是j之前的和,不包含j本身的位置。

//解法二:前缀和
public int subarraySum3(int[] nums, int k) {
     int[] preSum = new int[nums.length + 1];
     int count = 0;
     for (int i = 0; i < nums.length; i++) {
         preSum[i + 1] = preSum[i] + nums[i];
     }

     for (int i = 0; i < nums.length; i++) {
         for (int j = i; j < nums.length; j++) {
             if (preSum[j + 1] - preSum[i] == k) //此处注意是j+1
                 count++;
         }
     }
     return count;
 }

执行结果:

执行用时:1790 ms, 在所有 Java 提交中击败了5.03% 的用户
内存消耗:41 MB, 在所有 Java 提交中击败了48.07% 的用户

代码三:

注意点:
1,map里面存放的都是连续的前缀和
2,preSum是从头到当前位置的连续的和,在map中查找pre-k就是查找满足条件的连续前缀和
3,仔细揣摩一下,其实就是类似两数之和的解法思想,每到当前位置就对当前长度进行判断。

//解法三:前缀和优化(利用HashMap)
public int subarraySum4(int[] nums, int k) {
    Map<Integer, Integer> map = new HashMap<>();
    map.put(0, 1);
    int preSum = 0, count = 0;
    for (int i = 0; i < nums.length; i++) {
        preSum += nums[i];
        if (map.containsKey(preSum - k)) {
            count += map.get(preSum - k);
        }
        map.put(preSum, map.getOrDefault(preSum, 0) + 1);

    }
    return count;
}

执行结果:

执行用时:26 ms, 在所有 Java 提交中击败了80.64% 的用户
内存消耗:41.1 MB, 在所有 Java 提交中击败了40.62% 的用户

代码四:

注意点:
1,没有想到用前缀数组来存储前缀的和,会比上一种解法效率高

//前缀和数组+map优化
public int subarraySum5(int[] nums, int k) {
    Map<Integer, Integer> map = new HashMap<>();
    int[] preSum = new int[nums.length + 1];
    int count = 0;
    preSum[0] = nums[0];
    for (int i = 1; i < nums.length; i++) {
        preSum[i] = preSum[i - 1] + nums[i];
    }
    map.put(0, 1);
    for (int i = 0; i < nums.length; i++) {
        Integer needPreSum = map.get(preSum[i]-k);  //此处也可能提高了效率,因为只调用了get没有调用containsKey方法
        if (needPreSum!=null)
            count += needPreSum;

        map.put(preSum[i], map.getOrDefault(preSum[i], 0) + 1);
    }
    return count;
}

执行结果:

执行用时:20 ms, 在所有 Java 提交中击败了97.24% 的用户
内存消耗:41.8 MB, 在所有 Java 提交中击败了13.71% 的用户

posted on 2021-02-02 22:35  vantoker  阅读(143)  评论(0)    收藏  举报