• 【数据结构与算法】数组与矩阵经典题


    移动零

    五星
    LeetCode:移动零

    题目描述:

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

    示例:

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

    思想:

    直接在正确的位置赋值,覆盖掉的数不影响最终结果,最后腾出的几位都赋值0

    代码:

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

    重塑矩阵

    LeetCode:重塑矩阵

    题目描述:

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

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

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

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

    示例:

    输入: 
    nums = 
    [[1,2],
     [3,4]]
    r = 1, c = 4
    输出: 
    [[1,2,3,4]]
    解释:
    行遍历nums的结果是 [1,2,3,4]。新的矩阵是 1 * 4 矩阵, 用之前的元素值一行一行填充新矩阵。
    

    思想:

    这题没啥精妙的思想,就是有点复杂。

    注意:index可以累加,不用每次都计算

    代码:

    class Solution {
        public int[][] matrixReshape(int[][] nums, int r, int c) {
            int m = nums.length;
            int n = nums[0].length;
            if(r*c!=m*n) return nums;
            int[][] res = new int[r][c];
            int index=0;
            for(int i=0;i<r;++i){
                for(int j=0;j<c;++j){
                    res[i][j] = nums[index/n][index%n];
                    index++;
                }
            }
            return res;
        }
    }
    

    搜索二维矩阵2

    五星
    LeetCode:搜索二维矩阵2

    题目描述:

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

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

    示例:

    现有矩阵 matrix 如下:
    
    [
      [1,   4,  7, 11, 15],
      [2,   5,  8, 12, 19],
      [3,   6,  9, 16, 22],
      [10, 13, 14, 17, 24],
      [18, 21, 23, 26, 30]
    ]
    
    

    思想:

    横竖来进行遍历,这题的关键是要找到合适的遍历起点:

    如果从左上角或右下角开始,每次下一步时,存在两种情况。所以应该从左下角或右上角开始。

    代码:

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

    有序矩阵中第K小的元素

    五星
    LeetCode:有序矩阵中第K小的元素

    题目描述:

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

    示例:

    matrix = [
       [ 1,  5,  9],
       [10, 11, 13],
       [12, 13, 15]
    ],
    k = 8,
    
    返回 13。
    

    思想:

    两个方法,二分查找和优先队列。
    二分查找思路:直观方法是遍历每个数,计算它们的排位,和k比较。因为二维数组可以看做有序的,所以可以将遍历的过程抽象成二分查找,大大提高效率。将左上角的数和右下角的数分别作为low和high

    代码:

    二分查找

    class Solution {
        public int kthSmallest(int[][] matrix, int k) {
            int n = matrix.length;
            int low = matrix[0][0],high=matrix[n-1][n-1];
            int numBefore,mid;
            while(high>=low){
                mid = low+(high-low)/2;
                numBefore = getNumBefore(matrix,mid);
                if(numBefore<k){
                    low = mid + 1;
                }else{
                    high = mid - 1;
                }
            }
            return high;
        }
    
        private int getNumBefore(int[][] matrix, int val){
            int n = matrix.length;
            int ret = 0;
            for(int i=0;i<n;++i){
                for(int j=0;j<n;++j){
                    if(matrix[i][j]>=val) break;
                    ret++;
                }
            }
            return ret;
        }
    }
    

    优先队列,堆解法

    class Solution {
        public int kthSmallest(int[][] matrix, int k) {
            int n = matrix.length;
            PriorityQueue<item> queue = new PriorityQueue<>();
            for(int i=0;i<n;++i) queue.offer(new item(0,i,matrix[0][i]));
            for(int i=0;i<k-1;++i){
                item temp = queue.poll();
                if(temp.x==n-1) continue;
                queue.offer(new item(temp.x+1,temp.y,matrix[temp.x+1][temp.y]));
            }
            return queue.poll().val;
        }
        static class item implements Comparable<item>{
            int x;
            int y;
            int val;
    
            public item(int x, int y, int val) {
                this.x = x;
                this.y = y;
                this.val = val;
            }
    
            @Override
            public int compareTo(item o) {
                return this.val - o.val;
            }
        }
    }
    

    寻找重复数

    五星
    LeetCode:寻找重复数

    题目描述:

    给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。
    注意:
    1.不能更改原数组(假设数组是只读的)。
    2.只能使用额外的 O(1) 的空间。
    3.时间复杂度小于 O(n2) 。
    4.数组中只有一个重复的数字,但它可能不止重复出现一次。

    示例:

    输入: [1,3,4,2,2]
    输出: 2
    
    输入: [3,1,3,4,2]
    输出: 3
    

    思想:

    因为重复数字可能不止出现一次,所以不能按照 错误的集合 这题的做法来做,即使用位运算。

    该种数组形式可以抽象成一个带环的链表。本题可以抽象成,求环入口的问题。思想如下

    • 注意起点是0,不是nums[0];
    • 快慢指针遍历,必会相遇,可以求得相遇点;
    • 从起点到环入口距离,等于相遇点到环入口的距离。画个图就能看出来这个性质;

    代码:

    class Solution {
        public int findDuplicate(int[] nums) {
            int slow=nums[0],fast=nums[slow];
            while(slow!=fast){
                slow = nums[slow];
                fast = nums[nums[fast]];
            }
            slow = 0;
            while(slow!=fast){
                slow = nums[slow];
                fast = nums[fast];
            }
            return slow;
        }
    }
    

    数组相邻差值的个数

    五星
    LeetCode:数组相邻差值的个数

    题目描述:

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

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

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

    示例:

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

    思想:

    前k+1个数这样排列:1,1+k,2,k,3,k-1......可满足条件,后面的位置按顺序排
    例如k为4的情况,1,5,3,2,4的差值分别是4,3,2,1满足题设。
    凭感觉这样是对的但不知道怎么证明。
    兴许可以按照以下思路来思考:

    • 贪心思想,先用最少的数完成最多的符合条件数目的差值;
    • 假设一个序列按顺序排列,从中取数放进res数组,先取最左边的,再取最右边的,再取最左边第二个,最右边第二个,以此类推,得到的结果,相邻之间差值最离散;

    完成以上思路,需要解决一个问题,如何保证前k+1个数排完之后,后面的数不会出现新的差值?

    因为第k+2个数值为k+2,它与前一个数的差值不可能大于k,除非前一个数是1,但1是排在首位的,不可能有这样的情况。

    代码:

    class Solution {
        public int[] constructArray(int n, int k) {
            int[] res = new int[n];
            for(int i=0,m=1;i<k+1;i+=2,++m){
                res[i] = m;
            }
            for(int i=1,m=k+1;i<k+1;i+=2,--m){
                res[i] = m;
            }
            for(int i=k+1;i<n;++i){
                res[i] = i+1;
            }
            return res;
        }
    }
    

    数组的度

    LeetCode:数组的度

    题目描述:

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

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

    示例:

    输入: [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.
    
    

    思想:

    这题关键要想到用三个map来做,使用迭代器遍历map比直接遍历数组要快。

    代码:

    class Solution {
        public int findShortestSubArray(int[] nums) {
            Map<Integer,Integer> numsCnt = new HashMap<>();
            Map<Integer,Integer> numsFirstIndex = new HashMap<>();
            Map<Integer,Integer> numsLastIndex = new HashMap<>();
            for(int i=0;i<nums.length;++i){
                int item = nums[i];
                numsCnt.put(item,numsCnt.getOrDefault(item,0)+1);
                if(!numsFirstIndex.containsKey(item)) numsFirstIndex.put(item,i);
                numsLastIndex.put(item,i);
            }
            Iterator it = numsCnt.entrySet().iterator();
            int maxNum = 0;
            while(it.hasNext()){
                Map.Entry entry = (Map.Entry) it.next();
                maxNum = Math.max(maxNum,(Integer)entry.getValue());
            }
            it = numsCnt.entrySet().iterator();
            int minRes = nums.length;
            while(it.hasNext()){
                Map.Entry entry = (Map.Entry) it.next();
                int val = (Integer)entry.getValue();
                if(val == maxNum){
                    int key = (Integer)entry.getKey();
                    minRes = Math.min(minRes,numsLastIndex.get(key)-numsFirstIndex.get(key)+1);
                }
            }
            return minRes;
        }
    }
    

    数组嵌套

    LeetCode:数组嵌套

    题目描述:

    索引从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出现重复的元素。

    示例:

    输入: 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,下次不用再访问,可大大缩减循环次数。

    代码:

    class Solution {
        public int arrayNesting(int[] nums) {
            int res= 0;
            for(int i=0;i<nums.length;++i){
                int j = i;
                int cnt = 0;
                while(nums[j]!=-1){
                    int temp = nums[j];
                    nums[j] = -1;
                    j=temp;
                    ++cnt;
                }
                //if(cnt>nums.length/2) return cnt;
                res = Math.max(res, cnt);
            }
            return res;
        }
    }
    

    分隔数组

    五星
    LeetCode:最多能完成排序的块

    题目描述:

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

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

    示例:

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

    思想:

    这题需要动笔画一画,仔细看看arr[i] 和 i之间的关系。

    • 当 i 与 arr[i] 相等时,单元素划分一个块;
    • 当 arr[i] 小于 i 时,该元素一定被划分到前一个块中;
    • 当 arr[i] 大于 i 时,并不一定开启一个新的块,一个块中可能有多个arr[i]都大于它们的下标;
    • 无法获得块开始标志,那就找块结束的标志。一个块中元素的最大值一定等于块结束位置的下标。

    掌握以上规律之后,一趟遍历即可得到答案。

    代码:

    class Solution {
        public int maxChunksToSorted(int[] arr) {
            int max = -1;
            int cnt = 0;
            for(int i=0;i<arr.length;++i){
                max = Math.max(max,arr[i]);
                if(max==i) ++cnt;
            }
            return cnt;
        }
    }
    
  • 相关阅读:
    敏捷实践-学习实践资料汇总
    从数据仓库到数据湖—浅谈数据架构演进
    JVM知识点汇总备忘
    Protobuf的使用和原理
    kafka数据定时导入hive便于后续做数据清洗
    Mybatis Mapper接口动态代理实现原理及二次开发
    软考论文-写作大纲-备考思路总结
    css3另一个属性写法
    css3动画效果
    jquery点击鼠标后关闭图片
  • 原文地址:https://www.cnblogs.com/buptleida/p/13137493.html
Copyright © 2020-2023  润新知