Leetcode Tags(2)Array

  一、448. Find All Numbers Disappeared in an Array

给定一个范围在  1 ≤ a[i] ≤ n ( n = 数组大小 ) 的 整型数组,数组中的元素一些出现了两次,另一些只出现一次。
找到所有在 [1, n] 范围之间没有出现在数组中的数字。
您能在不使用额外空间且时间复杂度为O(n)的情况下完成这个任务吗? 你可以假定返回的数组不算在额外空间内。
输入:[4,3,2,7,8,2,3,1]
输出:[5,6]

  1.算法思路(本题关键点:注意数组下标的范围和要求的数之间有什么联系。

  既然要遍历[1,n]中的数,由于数组中的长度已经保证了为n,因此数组的下标加上1就正好满足从1到n,因此,要对下标做文章。

  2.代码实现

  将[4,3,2,7,8,2,3,1]变成[-4,-3,-2,-7,8,2,-3,-1]然后遍历到8和2这两个正数时,就将数组下标加上1就是要求的结果了。

    public List<Integer> findDisappearedNumbers(int[] nums) {
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < nums.length; i++) {
            int val = Math.abs(nums[i]) - 1;
            if (nums[val] > 0) nums[val] = -nums[val];// 如果不进行判断,那么2和3就会反转两次,还是变成2和3,最后结果为[2,3,5,6]
        }
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] > 0) list.add(i+1);
        }
        return list;
    }

  

  二、283. Move Zeroes

Input: [0,1,0,3,12]
Output: [1,3,12,0,0]

  1.算法思路:双指针,前一个指针指的是数组的索引,后一个指针指的是非零元素的索引

  2.代码实现

  (1)最普通的思路最清晰的双指针

    public void moveZeroes(int[] nums) {
        int prev = 0;
        int curr = 0;
        while (curr < nums.length) {
            if (nums[prev] == 0 && nums[curr] != 0) {
                nums[prev] = nums[curr];
                nums[curr] = 0;
                prev++;
                curr++;
            } else if (nums[prev] != 0 && nums[curr] == 0) {
                prev++;
                curr++;
            } else if (nums[prev] != 0 && nums[curr] != 0) {
                prev++;
                curr++;
            } else {
                curr++;
            }
        }
    }
Low Code

  (2)高级的双指针写法1,(关键点:nums[lastNonZeroFoundAt++]的写法)同样使用指针lastNonZeroFoundAt和指针i。

void moveZeroes(vector<int>& nums) {
    int lastNonZeroFoundAt = 0;
    for (int i = 0; i < nums.size(); i++) {
        if (nums[i] != 0) nums[lastNonZeroFoundAt++] = nums[i];    
    }
    for (int i = lastNonZeroFoundAt; i < nums.size(); i++) nums[i] = 0;
}

  (3)高级的双指针写法2

void moveZeroes(vector<int>& nums) {
    for (int lastNonZeroFoundAt = 0, cur = 0; cur < nums.size(); cur++) {
        if (nums[cur] != 0) swap(nums[lastNonZeroFoundAt++], nums[cur]);
    }
}

  三、(二维数组--九宫格问题)661. 图片平滑器

输入:
[[1,1,1],
 [1,0,1],
 [1,1,1]]
输出:
[[0, 0, 0],
 [0, 0, 0],
 [0, 0, 0]]
解释:
对于点 (0,0), (0,2), (2,0), (2,2): 平均(3/4) = 平均(0.75) = 0
对于点 (0,1), (1,0), (1,2), (2,1): 平均(5/6) = 平均(0.83333333) = 0
对于点 (1,1): 平均(8/9) = 平均(0.88888889) = 0

  1.算法思路

  之前的思路是为了防止计算的时候数周围的数没有九个,因此建立一个比原始矩阵大一圈的矩阵,然后将原来的矩阵复制到大矩阵的中间。也就是在原矩阵四周圈上一圈0;

  但是有可以不用新建矩阵的方法。(关键点:在做加法之前先判断索引是否有效,如果有效才进行加法计算)

  2.代码实现

    public int[][] imageSmoother(int[][] M) {
        int[][] res = new int[M.length][M[0].length];
        for (int i = 0; i < M.length; i++) {
            for (int j = 0; j < M[0].length; j++) {
                int count = 0;
                for (int x = i - 1; x <= i + 1; x++) {
                    for (int y = j - 1; y <= j + 1; y++) {
                        if (0 <= x && x < M.length && 0 <= y && y < M[0].length) {
                            res[i][j] += M[x][y];
                            count++;
                        }
                    }
                }
                res[i][j] /= count;
            }
        }
        return res;
    }

  四、(双指针问题,单个指针for循环)830. Positions of Large Groups

Input: "abcdddeeeeaabbbcd"
Output: [[3,5],[6,9],[12,14]]

  1.解法思路

  使用prev指针指向子字符串的起始位置,使用curr指向子字符串的终止位置,如果curr和prev的距离相差3就添加到列表中。

  2.代码实现(关键点:prev待定,curr指针for循环,如果当前字符和下一字符相等就什么也不做,for循环里面curr会自动加1向后移动

    public List<List<Integer>> largeGroupPositions(String S) {
        int prev = 0;
        List<List<Integer>> result = new ArrayList<>();
        for (int curr = 0; curr < S.length(); curr++) {
            if (curr == S.length() - 1 || S.charAt(curr) != S.charAt(curr+1)) {
                if (curr-prev+1 >= 3) {
                    result.add(Arrays.asList(new Integer[] {prev, curr}));
                }
                prev = curr + 1;
            }
        }
        return result;
    }

  五、628. Maximum Product of Three Numbers

Input: [1,2,3,4]
Output: 24

  1.解法思路

  (1)常规算法,排序,由于可能存在负数的原因,而两个负数乘起来就是负数,例如{-4,-3,-2,-1,60},因此需要比较排序后的Math.max(nums[nums.length-1] * nums[nums.length-2] * nums[nums.length-3], nums[0] * nums[1] * nums[nums.length-1]);

  时间复杂度O(nlog(n)),空间复杂度O(log(n)

  (2)更快的算法,按照自己的规则手动进行排序,时间复杂度O(n),空间复杂度O(1)

    public int maximumProduct(int[] nums) {
        int min1 = Integer.MAX_VALUE, min2 = Integer.MAX_VALUE;
        int max1 = Integer.MIN_VALUE, max2 = Integer.MIN_VALUE, max3 = Integer.MIN_VALUE;
        for (int n: nums) {
            if (n <= min1) {
                min2 = min1;
                min1 = n;
            } else if (n <= min2) {     // n lies between min1 and min2
                min2 = n;
            }
            if (n >= max1) {            // n is greater than max1, max2 and max3
                max3 = max2;
                max2 = max1;
                max1 = n;
            } else if (n >= max2) {     // n lies betweeen max1 and max2
                max3 = max2;
                max2 = n;
            } else if (n >= max3) {     // n lies betwen max2 and max3
                max3 = n;
            }
        }
        return Math.max(min1 * min2 * max1, max1 * max2 * max3);
    }

  六、(数组插入问题)(三指针问题)88. Merge Sorted Array

Input:
nums1 = [1,2,3,0,0,0], m = 3
nums2 = [2,5,6],       n = 3

Output: [1,2,2,3,5,6]

  1.解题思路

  由于数组插入数据的困难性,再要求将nums中的数插入到nums1的合适位置中时,可以从后往前比较,将最大的数放在nums1的最末位,一次比较。

  2.解题代码(注意点:如果nums2中的数全都先于nums1插入完成,那么nums1中无序改变了;如果nums1中的数全部都移向了nums1的后面,那么nums1前面的数将由nums2中的数依次填充。

    public void merge(int[] nums1, int m, int[] nums2, int n) {
        int i = m - 1;
        int j = n - 1;
        int k = m + n - 1;
        while (i >= 0 && j >= 0) {
            if (nums1[i] > nums2[j]) nums1[k--] = nums1[i--];
            else nums1[k--] = nums2[j--];
        }
        while (j >= 0) nums1[k--] = nums2[j--];
    }

  七、(放花问题)605. Can Place Flowers

假设你有一个很长的花坛,一部分地块种植了花,另一部分却没有。可是,花卉不能种植在相邻的地块上,它们会争夺水源,两者都会死去。
输入: flowerbed = [1,0,0,0,1], n = 1
输出: True
输入: flowerbed = [1,0,0,0,1], n = 2
输出: False

  1.解题思路(关键点:找出位置之后,直接种上花,就不用再烦恼连续的0能种多少花的问题。)

  和前面的二维数组的边界问题一样,可以在if判断语句上面添加对索引的限制。

  遍历一次,如果存在值为0且左右都为0,那么count加1并且将该位置设为1,即种上花。

  2.代码实现(关键点:解决第一个位置和最后一个位置的问题,这种关系表达式的表达技巧

    public boolean canPlaceFlowers(int[] flowerbed, int n) {
        int count = 0, i = 0;
        while (i < flowerbed.length) {
            if (flowerbed[i] == 0 &&
               (i == 0 || flowerbed[i-1] == 0) &&
               (i == flowerbed.length-1 || flowerbed[i+1] == 0)) {
                count++;
                flowerbed[i] = 1;
            }
            i++;
        }
        return count <= n;
    }

  七点五、(坐座位问题)849. Maximize Distance to Closest Person

输入:[1,0,0,0,1,0,1]
输出:2
解释:如果亚历克斯坐在第二个空位(seats[2])上,他到离他最近的人的距离为 2 。果亚历克斯坐在其它任何一个空位上,他到离他最近的人的距离为 1 。
因此,他到离他最近的人的最大距离是 2 。 

  1.思路:和上面的养花问题一样,无非就是寻找距离1最远距离的位置,同样存在边界问题。

  2.解法

    public int maxDistToClosest(int[] seats) {
        int N = seats.length;
        int prev = -1, future = 0;
        int ans = 0;

        for (int i = 0; i < N; ++i) {
            if (seats[i] == 1) {
                prev = i;
            } else {
                while (future < N && seats[future] == 0 || future < i)
                    future++;

                int left = prev == -1 ? N : i - prev;
                int right = future == N ? N : future - i;
                ans = Math.max(ans, Math.min(left, right));
            }
        }

        return ans;
    }
双指针解法
    public int maxDistToClosest(int[] seats) {
        int N = seats.length;
        int K = 0; //current longest group of empty seats
        int ans = 0;

        for (int i = 0; i < N; ++i) {
            if (seats[i] == 1) {
                K = 0;
            } else {
                K++;
                ans = Math.max(ans, (K + 1) / 2);
            }
        }

        for (int i = 0; i < N; ++i)  if (seats[i] == 1) {
            ans = Math.max(ans, i);
            break;
        }

        for (int i = N-1; i >= 0; --i)  if (seats[i] == 1) {
            ans = Math.max(ans, N - 1 - i);
            break;
        }

        return ans;
    }
按0分组解法

  

  八、(最短无序连续子数组)581. Shortest Unsorted Continuous Subarray(TODO)

输入: [2, 6, 4, 8, 10, 9, 15]
输出: 5
解释: 你只需要对 [6, 4, 8, 10, 9] 进行升序排序,那么整个表都会变为升序排序。

  1.思路:

  九、(int和Integer的问题)414. Third Maximum Number

Input: [3, 2, 1]
Output: 1
Explanation: The third maximum is 1.

Input: [1, 2]
Output: 2
Explanation: The third maximum does not exist, so the maximum (2) is returned instead.

Input: [2, 2, 3, 1]
Output: 1
Explanation: Note that the third maximum here means the third maximum distinct number.
Both numbers with value 2 are both considered as second maximum.

  1.解题思路:上面的有一题也是按照自己的顺序对数组进行一次遍历,然后排序找出了5个数。这里也可以用同样的原理:

  2.有问题的代码,在定义了int类型的Integer.MIN_VALUE后,如果输入是{2,1,Integer.MIN_VALUE}那么根据return语句可以知道返回结果并不是Integer.MIN_VALUE,这里就有了一个问题。自己想的解决思路是再设置三个标志位,如果输入Integer.MIN_VALUE确实修改了就将标志位设置为true,但是这样会造成额外的开销。

    public int thirdMax(int[] nums) {
        int first = Integer.MIN_VALUE, second = Integer.MIN_VALUE, third = Integer.MIN_VALUE;
        for (int i : nums) {
            if (i < second && i > third) {
                third = i;
            } else if (first > i && i > second) {
                third = second;
                second = i;
            } else if (i > first) {
                third = second;
                second = first;
                first = i;
            }
        }
        return third == Integer.MIN_VALUE ? first : third;
    }

  3.解决问题的方法:将int类型改成Integer类型即可,其他完全一样,这样在return中就可以使用third==null?来进行判断。

  不过由于考虑到初始时first,second,third为null时直接替换,在if条件中加上了first == null ||与条件,这就会发生另外一种情况,就是{2,2,3,1}时,第二个2会直接进入到second,也就是无法去除重复元素,因此需要在前面加上一条判断语句, if (i.equals(first) || i.equals(second) || i.equals(third)) continue;,即如果和三个中的其中一个相同就直接退出循环。

    public int thirdMax(int[] nums) {
        Integer first = null, second = null, third = null;
        for (Integer i : nums) {
            if (i.equals(first) || i.equals(second) || i.equals(third)) continue;
            if (first == null || i > first) {
                third = second;
                second = first;
                first = i;
            } else if (second == null || (first > i && i > second)) {
                third = second;
                second = i;
            } else if (third == null || second > i && i > third) {
                third = i;
            }
        }
        return third == null ? first : third;
    }

  十、(考虑完各种情况)665. Non-decreasing Array

输入: [4,2,3]
输出: True
解释: 你可以通过把第一个4变成1来使得它成为一个非递减数列。

输入: [4,2,1]
输出: False
解释: 你不能在只改变一个元素的情况下将其变为非递减数列。

  1.思路:主要是要分清各种情况

  首先,定位到A[p-1]>A[p],如果p不是唯一的或者不存在,那么就是false。

  定位到之后,分析下面的情况:

  • p == -1:整个数组本身就是非递减数列。
  • p ==  1:也就是nums的nums[0] > nums[1],这个时候,只要让nums[0] = nums[1]
  • p == nums.length - 1:也就是nums的nums[nums.length - 2] > nums[nums.length - 1],这个时候,只要让最后一个点的值大于或等于倒数第二个点的值即可。
  • 如果nums[p-2]、nums[p-1]、nums[p]、nums[p+1]都存在的情况下:
  • nums[p-2] <= nums[p]:类似于{1,3,2,4}这种情况,可以把3变成1和2之间的数。
  • nums[p-1] <= nums[p+1]:类似于{1,3,2,4}这种情况,也可以把2变成3和4之间的数。

  2.代码:需要注意的是,由于最多只能改变一个数的值,因此,

  for循环中的if-else语句就是判断是否有两个或两个以上的值满足要求,如果是,就返回false。也就是说,p只能被赋值一次!

    public boolean checkPossibility(int[] nums) {
        int p = -1;
        for (int i = 1; i < nums.length; i++) {
            if (nums[i-1] > nums[i]) {
                if (p != - 1) return false;
                else p = i;
            }
        }
        return (p == -1 || p == 1 || p == nums.length-1 || nums[p-2] <= nums[p] || nums[p-1] <= nums[p+1]);
    }

  十一、739. Daily Temperatures

  十二、565.Array Nesting 854 / 856 test cases passed

输入: A = [5,4,0,3,1,6,2]
输出: 4
解释: 
A[0] = 5, A[1] = 4, A[2] = 0, A[3] = 3, A[4] = 1, A[5] = 6, A[6] = 2.

其中一种最长的 S[K]:
S[0] = {A[0], A[5], A[6], A[2]} = {5, 6, 2, 0}

  1.常规做法(O(n2) + O(1)),854 / 856 test cases passed,Time Limit Exceeded

    public int arrayNesting(int[] nums) {
        int maxlen = 0;
        int len = 0;
        int tmp = 0;
        for (int i = 0; i < nums.length; i++) {
            len = 1;
            tmp = nums[i];
            while (nums[tmp] != nums[i]) {
                len++;
                tmp = nums[tmp];
            }
            maxlen = Math.max(maxlen, len);
        }
        return maxlen;
    }

  2.更快的做法1(O(n) + O(n)):Using Visited Array  

  思路就是,例如A = [5,4,0,3,1,6,2],如果我们在A[0]=5的过程中已经确定了5, 6, 2, 0,那么我们将0,2,5,6这些位置进行标记,之后在遍历到2/5/6时直接跳过即可,

    public int arrayNesting(int[] nums) {
        boolean[] visited = new boolean[nums.length];
        int res = 0;
        for (int i = 0; i < nums.length; i++) {
            if (!visited[i]) {
                int start = nums[i], count = 0;
                do {
                    start = nums[start];
                    count++;
                    visited[start] = true;
                }
                while (start != nums[i]);
                res = Math.max(res, count);
            }
        }
        return res;
    }

  3.更快的做法2(O(n) + O(1)):Without Using Extra Space

  思路就是,例如A = [5,4,0,3,1,6,2],如果我们在A[0]=5的过程中已经确定了5, 6, 2, 0,那么2/5/6在之后的遍历过程中可以不用了,也就是在原数组中修改这些位置上的值即可,反正也再也用不到了。

    public int arrayNesting(int[] nums) {
        int res = 0;
        for (int i = 0; i < nums.length; i++) {
            if (nums[i] != Integer.MAX_VALUE) {
                int start = nums[i], count = 0;
                while (nums[start] != Integer.MAX_VALUE) {
                    int temp = start;
                    start = nums[start];
                    count++;
                    nums[temp] = Integer.MAX_VALUE;
                }
                res = Math.max(res, count);
            }
        }
        return res;
    }

  十三、medium 529(DFS+BFS)

  1.

    public char[][] updateBoard(char[][] board, int[] click) {
        int m = board.length, n = board[0].length;
        int row = click[0], col = click[1];
        
        if (board[row][col] == 'M') {
            board[row][col] = 'X';
        } else {
            int count = 0;
            for (int i = -1; i < 2; i++) {
                for (int j = -1; j < 2; j++) {
                    if (i == 0 && j == 0) continue;
                    int r = row + i, c = col + j;
                    if (r < 0 || r >= m || c < 0 || c >= n) continue;
                    if (board[r][c] == 'M' || board[r][c] == 'X') count++;
                }
            }
            if (count > 0) {
                board[row][col] = (char)(count + '0');
            } else {
                board[row][col] = 'B';
                for (int i = -1; i < 2; i++) {
                    for (int j = -1; j < 2; j++) {
                        if (i == 0 && j == 0) continue;
                        int r = row + i, c = col + j;
                        if (r < 0 || r >= m || c < 0 || c < 0 || c >= n) continue;
                        if (board[r][c] == 'E') updateBoard(board, new int[] {r, c});
                    }
                }
            }
        }
        return board;
    }

  十四、

原文地址:https://www.cnblogs.com/BigJunOba/p/9568728.html