首页 > 编程语言 > 详细

[leetcode刷题]——数组与矩阵

时间:2021-01-24 00:54:54      阅读:29      评论:0      收藏:0      [点我收藏+]

重点查看第五题,有序矩阵的第k小元素

重点查看第七题,查找数组中重复的数

 

一、把数组的0移动到末尾

283.移动 0  (easy) 2021-01-18

给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。

示例:

输入: [0,1,0,3,12]
输出: [1,3,12,0,0]

  两个月前做过,居然一点印象也没。

class Solution {
    public void moveZeroes(int[] nums) {
        int n = nums.length, i = 0;
        for (int j = 0; j < n; j++){
            if (nums[j] != 0) {
                nums[i++] = nums[j];
            }
        } 
        while (i < n) {
            nums[i++] = 0;
        }
    }
}

 

二、改变矩阵的维度

566.重塑矩阵  (easy) 2021-01-18

在MATLAB中,有一个非常有用的函数 reshape,它可以将一个矩阵重塑为另一个大小不同的新矩阵,但保留其原始数据。

给出一个由二维数组表示的矩阵,以及两个正整数r和c,分别表示想要的重构的矩阵的行数和列数。

重构后的矩阵需要将原始矩阵的所有元素以相同的行遍历顺序填充。

如果具有给定参数的reshape操作是可行且合理的,则输出新的重塑矩阵;否则,输出原始矩阵。

class Solution {
    public int[][] matrixReshape(int[][] nums, int r, int c) {
        if(r * c != nums.length * nums[0].length){
            return nums;
        }
        int[][] ans = new int[r][c];
        int a = 0, b = 0;
        for(int i = 0; i < r; i ++){
            for(int j = 0; j < c; j ++){
                
                ans[i][j] = nums[a][b];
                b++;
                if(b == nums[0].length){
                    a++;
                    b = 0;
                }

            }
        }
        return ans;
    }
}

 

三、找出数组中最长连续的1

485.最长连续的 1  (easy) 2021-01-18

给定一个二进制数组, 计算其中最大连续1的个数。

  

class Solution {
    public int findMaxConsecutiveOnes(int[] nums) {
        int len = 0;
        int max = 0;
        for(int i = 0; i < nums.length; i++){
            if(nums[i] == 1){
                len++;
            }
            if(nums[i] != 1){
                max = Math.max(max, len);
                len = 0;
            }
        }
        return Math.max(max, len);
    }
}

 

四、有序矩阵查找

240.搜索二维矩阵Ⅱ  (medium) 2021-01-19

编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性:

每行的元素从左到右升序排列。
每列的元素从上到下升序排列。

     两个月前在剑指刷的题,居然忘得一干二净。

  此题的关键在于找到矩阵的规律,从左下角开始检索,上面的比它小,右边的比它大。

  

class Solution {
    public boolean searchMatrix(int[][] matrix, int target) {
        int i = matrix.length - 1, j =0;
        while(i >= 0 && j < matrix[0].length){
             if(matrix[i][j] == target){
                return true;
            }else if(matrix[i][j] > target){
                 i--;
            }else{
                j++;
            }
        }
        return false;
    }
}

 

五、有序矩阵的第K个最小的元素

378.有序矩阵的第K个最小的元素   (medium) 2021-01-19

给定一个 n x n 矩阵,其中每行和每列元素均按升序排序,找到矩阵中第 k 小的元素。
请注意,它是排序后的第 k 小元素,而不是第 k 个不同的元素。

   没有思路了,我要休息了。

   这个题需要尤其注意,完全没有思路。

  二分法( 比较通用的解法,事实上并没有用到矩阵中元素升序的信息 ):

public int kthSmallest(int[][] matrix, int k) {
    int m = matrix.length, n = matrix[0].length;
    int lo = matrix[0][0], hi = matrix[m - 1][n - 1];
    while (lo <= hi) {
        int mid = lo + (hi - lo) / 2;
        int cnt = 0;
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n && matrix[i][j] <= mid; j++) {
                cnt++;
            }
        }
        if (cnt < k) lo = mid + 1;
        else hi = mid - 1;
    }
    return lo;
}

 

  堆解法:

public int kthSmallest(int[][] matrix, int k) {
    int m = matrix.length, n = matrix[0].length;
    PriorityQueue<Tuple> pq = new PriorityQueue<Tuple>();
    for(int j = 0; j < n; j++) pq.offer(new Tuple(0, j, matrix[0][j]));
    for(int i = 0; i < k - 1; i++) { // 小根堆,去掉 k - 1 个堆顶元素,此时堆顶元素就是第 k 的数
        Tuple t = pq.poll();
        if(t.x == m - 1) continue;
        pq.offer(new Tuple(t.x + 1, t.y, matrix[t.x + 1][t.y]));
    }
    return pq.poll().val;
}

class Tuple implements Comparable<Tuple> {
    int x, y, val;
    public Tuple(int x, int y, int val) {
        this.x = x; this.y = y; this.val = val;
    }

    @Override
    public int compareTo(Tuple that) {
        return this.val - that.val;
    }
}

 

六、一个数组元素在[1, n]之间,其中一个数被替换为另一个数,找出重复的数和丢失的数

645.错误的集合 (easy) 2021-01-20

集合 s 包含从 1 到 n 的整数。不幸的是,因为数据错误,导致集合里面某一个数字复制了成了集合里面的另外一个数字的值,导致集合 丢失了一个数字 并且 有一个数字重复 。

给定一个数组 nums 代表了集合 S 发生错误后的结果。

请你找出重复出现的整数,再找到丢失的整数,将它们以数组的形式返回。

  我的方法,排序暴力解法。

  

class Solution {
    public int[] findErrorNums(int[] nums) {
        int[] ans =  new int[2];
        Arrays.sort(nums);

        for(int i = 0 ; i < nums.length - 1; i++){
            if(nums[i] == nums[i + 1]){
                ans[0] = nums[i];
            }
            if(nums[i + 1] - nums[i] == 2){
                ans[1] = nums[i] + 1;
            }
        }
        if(nums[0] != 1){
            ans[1] = 1;
        }
        if(nums[nums.length - 1] != nums.length){
            ans[1] = nums.length;
        }
        return ans;
    }
}

  大佬的方法,时间复杂度为O(N),空间复杂度为O(1)。主要思想是通过交换数组元素,使得数组上的元素在正确的位置上。

public int[] findErrorNums(int[] nums) {
    for (int i = 0; i < nums.length; i++) {
        while (nums[i] != i + 1 && nums[nums[i] - 1] != nums[i]) {
            swap(nums, i, nums[i] - 1);
        }
    }
    for (int i = 0; i < nums.length; i++) {
        if (nums[i] != i + 1) {
            return new int[]{nums[i], i + 1};
        }
    }
    return null;
}

private void swap(int[] nums, int i, int j) {
    int tmp = nums[i];
    nums[i] = nums[j];
    nums[j] = tmp;
}

 

七、找出数组中重复的数,数值在[1, n] 之间

287.寻找重复数  (medium) 2021-01-21

给定一个包含 n + 1 个整数的数组 nums ,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。

假设 nums 只有 一个重复的整数 ,找出 这个重复的数 。

 

 

进阶:

 

如何证明 nums 中至少存在一个重复的数字?
你可以在不修改数组 nums 的情况下解决这个问题吗?
你可以只用常量级 O(1) 的额外空间解决这个问题吗?
你可以设计一个时间复杂度小于 O(n2) 的解决方案吗?

 

    哈哈哈哈,我用的双重循环方法,时间和空间完全都不满足进阶的要求,就不贴出来了。

  重点看看二分法,比暴力双重循环复杂度小。 

public int findDuplicate(int[] nums) {
     int l = 1, h = nums.length - 1;
     while (l <= h) {
         int mid = l + (h - l) / 2;
         int cnt = 0;
         for (int i = 0; i < nums.length; i++) {
             if (nums[i] <= mid) cnt++;
         }
         if (cnt > mid) h = mid - 1;
         else l = mid + 1;
     }
     return l;
}

     快慢指针,特别妙的算法,时间复杂度为O(n)。

   这个算法太妙了,发明人简直天才。可用于判断单向链表是否有环,在此题中,可以将此数组抽象为链表进行操作。

  举个例子,nums = [2,5,9,6,9,3,8,9,7,1],构造成链表就是:2->[9]->1->5->3->6->8->7->[9],也就是在[9]处循环。他们不一定在[9 ]处相遇,事实上它们在7 处相遇。

 

  理论分析: 这里涉及到很严谨的数学分析。首先设数组长度为n, 环的长度为c ,那么数组外不属于环的部分长度为m = n - c。

       快指针的速度是慢指针速度的2 倍, 同时出发,相遇时快指针比慢指针多走了整数倍 c 的长度。不妨看成c ,那么慢指针走的长度也为 c。所以,慢指针在环中的位置为 c - m。

  第二次循环将快指针从头开始,并且速度和慢指针相同。那么当快指针行走m 步进环时, 慢指针也正好行走 m 步走到环的开始位置。两指针相遇,返回环口值。=

public int findDuplicate(int[] nums) {
    int slow = nums[0], fast = nums[nums[0]];
    while (slow != fast) {  //快指针每次走两步,慢指针每次走一步。相遇时快之这份多走一个环
        slow = nums[slow];
        fast = nums[nums[fast]];
    }
    fast = 0;
    while (slow != fast) {
        slow = nums[slow];
        fast = nums[fast];
    }
    return slow;
}

 

八、数组相邻差值的个数

667.优美的排列Ⅱ  (medium)2021-01-22

给定两个整数 n 和 k,你需要实现一个数组,这个数组包含从 1 到 n 的 n 个不同整数,同时满足以下条件:

① 如果这个数组是 [a1, a2, a3, ... , an] ,那么数组 [|a1 - a2|, |a2 - a3|, |a3 - a4|, ... , |an-1 - an|] 中应该有且仅有 k 个不同整数;.

② 如果存在多种答案,你只需实现并返回其中任意一种.

示例 1:

输入: n = 3, k = 1
输出: [1, 2, 3]
解释: [1, 2, 3] 包含 3 个范围在 1-3 的不同整数, 并且 [1, 1] 中有且仅有 1 个不同整数 : 1

    这个题又是完全没有思路,诶。

  这种数学找规律的题真的是伤脑筋。需要k 个间隔,那么就让k 递减,然后正负交替放进数组。

public int[] constructArray(int n, int k) {
    int[] ret = new int[n];
    ret[0] = 1;  
    //前 1 + k 个数k个间隔
    //数组下标编号为奇,则它为前一个数 + interval
    //数组小标编号为偶,则它为前一个数 - interval
    //其中interval从 k 递减到 1 
    for (int i = 1, interval = k; i <= k; i++, interval--) {
        ret[i] = i % 2 == 1 ? ret[i - 1] + interval : ret[i - 1] - interval;
    }
    //从第k + 1个数开始,间隔为1
    for (int i = k + 1; i < n; i++) {
        ret[i] = i + 1;
    }
    return ret;
    }

 

九、数组的度

697.  数组的度  (easy) 2021-01-23

给定一个非空且只包含非负数的整数数组 nums, 数组的度的定义是指数组里任一元素出现频数的最大值。

你的任务是找到与 nums 拥有相同大小的度的最短连续子数组,返回其长度。

示例 1:

输入: [1, 2, 2, 3, 1]
输出: 2
解释:
输入数组的度是2,因为元素1和2的出现频数最大,均为2.
连续子数组里面拥有相同度的有如下所示:
[1, 2, 2, 3, 1], [1, 2, 2, 3], [2, 2, 3, 1], [1, 2, 2], [2, 2, 3], [2, 2]
最短连续子数组[2, 2]的长度为2,所以返回2.

    我的方法是,将数组存放进哈希表,然后找出最大的出现频次。遍历数组找出最大出现频次的几个数,每个数再遍历数组找出最先出现和最后出现的位置。算法可行,但是时间超出限制。

  总所周知,以空间换时间。建立三个哈希表,分别存放频次,首次位置,末次位置。

public int findShortestSubArray(int[] nums) {
    Map<Integer, Integer> numsCnt = new HashMap<>();
    Map<Integer, Integer> numsLastIndex = new HashMap<>();
    Map<Integer, Integer> numsFirstIndex = new HashMap<>();
    for (int i = 0; i < nums.length; i++) {
        int num = nums[i];
        numsCnt.put(num, numsCnt.getOrDefault(num, 0) + 1);
        numsLastIndex.put(num, i);
        if (!numsFirstIndex.containsKey(num)) {
            numsFirstIndex.put(num, i);
        }
    }
    int maxCnt = 0;
    for (int num : nums) {
        maxCnt = Math.max(maxCnt, numsCnt.get(num));
    }
    int ret = nums.length;
    for (int i = 0; i < nums.length; i++) {
        int num = nums[i];
        int cnt = numsCnt.get(num);
        if (cnt != maxCnt) continue;
        ret = Math.min(ret, numsLastIndex.get(num) - numsFirstIndex.get(num) + 1);
    }
    return ret;
}

 

十、对角元素相等的矩阵

766. 托普利茨矩阵   (easy) 2021-01-23

如果矩阵上每一条由左上到右下的对角线上的元素都相同,那么这个矩阵是 托普利茨矩阵 。

给定一个 M x N 的矩阵,当且仅当它是托普利茨矩阵时返回 True。

示例 1:

输入:
matrix = [
  [1,2,3,4],
  [5,1,2,3],
  [9,5,1,2]
]
输出: True
解释:
在上述矩阵中, 其对角线为:
"[9]", "[5, 5]", "[1, 1, 1]", "[2, 2, 2]", "[3, 3]", "[4]"。
各条对角线上的所有元素均相同, 因此答案是True。

  

class Solution {
    public boolean isToeplitzMatrix(int[][] matrix) {
        for(int i = 0; i < matrix.length; i++){ //
            if(!isToeplitzCatercorner(matrix, i, 0)){
                return false;
            }
        }
        for(int i = 1; i < matrix[0].length; i++){ //
            if(!isToeplitzCatercorner(matrix, 0, i)){
                return false;
            }
        }
        return true;
    }

    //判断(i,j)为起点的对角线的元素是否均相同
    public boolean isToeplitzCatercorner(int[][] matrix, int a, int b){
        int m = matrix.length;
        int n = matrix[0].length;
        int num = matrix[a][b];
        int i = a, j = b;
        while(i < m && j < n){
            if(matrix[i][j] != num){
                return false;
            }
            i++;
            j++;
        }
        return true;
    }
}

 

十一、嵌套数组

565. 嵌套数组  (medium) 2021-01-23

索引从0开始长度为N的数组A,包含0到N - 1的所有整数。找到最大的集合S并返回其大小,其中 S[i] = {A[i], A[A[i]], A[A[A[i]]], ... }且遵守以下的规则。

假设选择索引为i的元素A[i]为S的第一个元素,S的下一个元素应该是A[A[i]],之后是A[A[A[i]]]... 以此类推,不断添加直到S出现重复的元素。

  刚学了个快慢指针,迅速地用上了,但是漏洞不止一个。首先快指针比慢指针是快n圈而不是一圈,其次,我想用第二次快慢指针相遇的步长但是这种做法显然只针对nums[0]开始的循环链表,而不能搜索 整个链表。

   整个算法逻辑还是挺清晰的,就是遍历然后将遍历到的元素标记为-1,若是遍历到-1则结束当前遍历,进入下一轮。

public int arrayNesting(int[] nums) {
    int max = 0;
    for (int i = 0; i < nums.length; i++) {
        int cnt = 0;
        for (int j = i; nums[j] != -1; ) {
            cnt++;
            int t = nums[j];
            nums[j] = -1; // 标记该位置已经被访问
            j = t;
        }
        max = Math.max(max, cnt);
    }
    return max;
}

 

十二、分隔数组

769. 最多能完成排序的块  (medium) 2021-01-23

数组arr是[0, 1, ..., arr.length - 1]的一种排列,我们将这个数组分割成几个“块”,并将这些块分别进行排序。之后再连接起来,使得连接的结果和按升序排序后的原数组相同。

我们最多能将数组分成多少块?

示例 1:

输入: arr = [4,3,2,1,0]
输出: 1
解释:
将数组分成2块或者更多块,都无法得到所需的结果。
例如,分成 [4, 3], [2, 1, 0] 的结果是 [3, 4, 0, 1, 2],这不是有序的数组。

   数组的题目确实好多需要找数学规律,一开始看到这个题都毫无思路。

  这个题有个规律就是,如果数组的前i 个数的最大值就是i ,那么这前 n 个数就可以分块。找个这个规律,medium题瞬间变easy。

class Solution {
    public int maxChunksToSorted(int[] arr) {
        int count = 0, max = 0;
        for(int i = 0; i < arr.length; i++){
            max = Math.max(max, arr[i]);
            if(max == i)  count++;
        }
        return count ;
    }
}

 

[leetcode刷题]——数组与矩阵

原文:https://www.cnblogs.com/nobita/p/14295463.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!