数据结构——leetcode(数组与矩阵)【java】

这篇博客主要介绍了LeetCode中涉及数组与矩阵的Java解题思路,包括移动零、重塑矩阵、最大连续1的个数等经典问题,详细解析了时间复杂度和空间复杂度,并提供了高效解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

leetcode-283-移动零

力扣-283
题目:给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
示例

输入: [0,1,0,3,12]
输出: [1,3,12,0,0]
必须在原数组上操作,不能拷贝额外的数组。
尽量减少操作次数。

思路:用index记录,当出现0的时候,index++(从0开始)的位置将后面的不为零的元素一次放到前面,然后当index<nums.length,后面的元素全部为0。

 public void moveZeroes(int[] nums) {
        int index = 0;
        for(int i=0;i<nums.length;i++){
            if(nums[i]!=0){
                nums[index++]=nums[i];
            }
        }
        while(index<nums.length){
            nums[index++]=0;
        }
    }

时间复杂度: O(n)
空间复杂度: O(1)

leetcode-566-重塑矩阵

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

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

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

示例

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

输入: 
nums = 
[[1,2],
 [3,4]]
r = 2, c = 4
输出: 
[[1,2],
 [3,4]]
解释:
没有办法将 2 * 2 矩阵转化为 2 * 4 矩阵。 所以输出原矩阵。

思路
1.判断是否可以重塑 mn=rs
2.重塑矩阵的各个元素 reshapenums[i][j] = nums[index/n][index%n];

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

时间复杂度: O(mn)。我们只遍历整个矩阵 mn。这里,m和 n 指的是给定矩阵中的行数和列数。

空间复杂度: O(m*n)。使用大小为 m∗n 的结果矩阵。

leetcode-485-最大连续1的个数

力扣-485
题目:给定一个二进制数组, 计算其中最大连续1的个数。
示例

输入: [1,1,0,1,1,1]
输出: 3
解释: 开头的两位和最后的三位都是连续1,所以最大连续1的个数是 3.
 public int findMaxConsecutiveOnes(int[] nums) {
        int max = 0;
        int count = 0;
        for(int i=0;i<nums.length;i++){
            if(nums[i]==1){
                count++;
            }
            else{
                max = Math.max(max,count);
                count = 0;
            }
        }
        return Math.max(max,count);
    }

时间复杂度: O(N) N 值得是数组的长度。
空间复杂度: O(1),仅仅使用了 count 和 maxCount

leetcode-240-搜索二维矩阵Ⅱ

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

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

示例

[
  [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]
]

思路:找到矩阵的row col ,如果当前指向的值大于目标值,则可以 “向上” 移动一行。 否则,如果当前指向的值小于目标值,则可以移动一列。因为行是从左到右排序的,所以我们知道当前值右侧的每个值都较大。 因此,如果当前值已经大于目标值,我们知道它右边的每个值会比较大。也可以对列进行非常类似的论证,因此这种搜索方式将始终在矩阵中找到目标。

  public boolean searchMatrix(int[][] matrix, int target) {
        if(matrix==null||matrix.length==0||matrix[0].length==0){
            return false;
        }
        int m = matrix.length;
        int n = matrix[0].length;
        int row = 0;
        int col =n-1;
        while(row<m&&col>=0){
            if(target==matrix[row][col]) return true;
            else if(target<matrix[row][col]) col--;
            else row++;
        }
        return false;
    }

时间复杂度: O(m+n)
空间复杂度: O(1)

leetcode-378-有序矩阵中第k小的元素

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

示例

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

思路
1.暴力解法
2.二分法查找
在这里插入图片描述

3.小根堆,优先级队列数据结构根本是二叉树
在这里插入图片描述

//直接排序法
public int kthSmallest(int[][] matrix, int k) {
        int m = matrix.length;
        int n = matrix[0].length;
        int [] sorted = new int [m*n];
        int index = 0;
        for(int []row:matrix){
            for(int num: row){
                sorted[index++] = num;

            }
        }
        Arrays.sort(sorted);
        return sorted[k-1];
    }
//时间复杂度: O (n^2\log{n}) 对 n^2个数排序。
//空间复杂度: O(n^2)


//二分查找
public int kthSmallest(int[][] matrix, int k) {
        int n = matrix.length-1;
        int left = matrix[0][0];
        int right = matrix[n][n];
        while(left<right){
            int mid = left+(right-left)/2;
            int count = FindCountMid(matrix,mid,n);
            if(count<k) left = mid+1;
            else right = mid;
        }
        return right;
    }
    public int FindCountMid(int[][] matrix,int mid,int n){
        int x = n;
        int y = 0;
        int count = 0;
        while(x>=0&&y<=n){
            if(matrix[x][y]<=mid){
                count+=x+1;
                ++y;
            }else{
                --x;
            }
        }
        return count;
    }
//时间复杂度:O(n\log(r-l),二分查找进行次数为 O(n\log(r-l)),每次操作时间复杂度为 O(n)。
//空间复杂度:O(1)

//小根堆
public int kthSmallest(int[][] matrix, int k) {
        int m = matrix.length;
        int 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;
    }
}
//时间复杂度:O(k\log{n}),归并 k、k 次,每次堆中插入和弹出的操作时间复杂度均为logn。
//空间复杂度:O(n),堆的大小始终为 n

leetcode-645-错误的集合

力扣-485
题目:一个数组元素在 [1, n] 之间,其中一个数被替换为另一个数,找出重复的数和丢失的数
示例
输入: nums = [1,2,2,4]
输出: [2,3]
思路:1.数组
2.hashmap [num,count],如果count=2,则对应num为重复值
3.位运算

 public int[] findErrorNums(int[] nums) {
        Arrays.sort(nums);
        int dup = -1;
        int missing = 1;
        for(int i=0;i<nums.length-1;i++){
            if(nums[i+1]==nums[i]){
                dup=nums[i+1];
            }
            else if(nums[i+1]>nums[i]+1){
                missing = nums[i]+1;
            }
        }
        return new int []{dup,nums[nums.length-1]!=nums.length?nums.length:missing};
    }
//时间复杂度:O(nlogn),排序需要O(nlogn) 的时间。
//空间复杂度:O(logn),排序需要O(logn) 的空间。

//hashmap
public int[] findErrorNums(int[] nums) {
       Map<Integer,Integer> map=new HashMap();
       int dup=-1;
       int missing=1;
       for(int n:nums){
           map.put(n,map.getOrDefault(n,0)+1);
       } 
       for(int i=1;i<=nums.length;i++){
            if(map.containsKey(i)){
                if(map.get(i)==2)
                    dup = i;
            }else missing =i;
       }
       return new int[]{dup,missing};
    }
//时间复杂度:O(n)。遍历 nums需要时间O(n),在 map中检查每个数字需要时间O(n)。
//空间复杂度:O(n),map最多需要存储1到n共n个数字。

时间复杂度: O(N) N 值得是数组的长度。
空间复杂度: O(1),仅仅使用了 count 和 maxCount

leetcode-287-寻找重复数

力扣-287
题目:给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数

示例

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

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

说明
1.不能更改原数组(假设数组是只读的)。
2.只能使用额外的 O(1) 的空间。
3.时间复杂度小于 O(n2) 。
4.数组中只有一个重复的数字,但它可能不止重复出现一次。
思路:1.二分法,找到mid,看一半数组中大于或小于mid 的数量是否大于mid,则可以判断重复数据在哪一侧数组中了
2.双指针解法(不是常规思想)类似于有环链表中找出环的入口:fast走2步,slow走一步,当fast ==slow,重新取或者fast指针指向链表/数组头,走一步遍历和slow速度相同一同继续行走,直到新fast指针与slow指针重合为环入口。

//二分法
 public int findDuplicate(int[] nums) {
        int left = 1;
        int h = nums.length-1;
        while(left<=h){
            int mid = left+(h-left)/2;
            int count = 0;
            for(int i=0;i<nums.length;i++){
                if(nums[i]<=mid) count++;
            }
            if(count>mid) h=mid-1;
            else left=mid+1;
        }
        return left;
    }

//快慢指针
 public int findDuplicate(int[] nums) {
       int fast = 0;
       int slow = 0;
       while(true){
           //快指针走两步,慢指针走一步
           fast = nums[nums[fast]];
           slow = nums[slow];
           if(slow==fast){
               fast = 0;
               while(nums[slow]!=nums[fast]){
                   fast = nums[fast];
                   slow = nums[slow];
               }
               return nums[slow];
           }
       }
    }

leetcode-667-数组相邻差值的个数

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

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

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

示例

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

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

思路:数组元素为 1~n 的整数,要求构建数组,使得相邻元素的差值不相同的个数为 k。

让前 k+1 个元素构建出 k 个不相同的差值,序列为:1 k+1 2 k 3 k-1 … k/2 k/2+1.

 public int[] constructArray(int n, int k) {
        int[] ret = new int[n];
        ret[0]=1;
        for(int i=1,j=k;i<=k;i++,j--){
            ret[i]=i%2==1?ret[i-1]+j:ret[i-1]-j;
        }
        for(int i=k+1;i<n;i++){
            ret[i]=i+1;
        }
        return ret;
    }

leetcode-697-数组的度

力扣-697
题目
给定一个非空且只包含非负数的整数数组 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.

输入: [1,2,2,3,1,4,2]
输出: 6
public int findShortestSubArray(int[] nums) {
        if(nums==null||nums.length==0) return 0;
        if(nums.length==1) return 1; 
        //map1 记录每个元素出现次数
        Map<Integer,Integer> map1 = new HashMap<>();
        //map2 记录数组元素第一次出现时的位置
        Map<Integer,Integer> map2 = new HashMap<>();
        //map3 记录数组相同元素第一次与最后一次出现的长度
        Map<Integer,Integer> map3 = new HashMap<>();

        for(int i=0;i<nums.length;i++){
            map1.put(nums[i],map1.getOrDefault(nums[i],0)+1);
            if(!map2.containsKey(nums[i])){
                map2.put(nums[i],i);
            }
            map3.put(nums[i],i-map2.get(nums[i])+1);
        }
        int maxCount=0;//记录数组的度
        //取出map1中最大值
        for(int num:map1.values()){
            maxCount = Math.max(maxCount,num);
        }
        //minLength用来记录最终的答案,保存最短连续子数组的长度,初始化为最大值
        int minLength = Integer.MAX_VALUE;
        for(int key:map1.keySet()){
            if(maxCount==map1.get(key)){
                minLength = Math.min(minLength,map3.get(key));
            }
        }
        return minLength;
    }

leetcode-766-对角元素相等的矩阵

力扣-766
题目:如果矩阵上每一条由左上到右下的对角线上的元素都相同,那么这个矩阵是 托普利茨矩阵 。
给定一个 M x N 的矩阵,当且仅当它是托普利茨矩阵时返回 True
示例

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。

思路:检查右下元素是否相同

 public boolean isToeplitzMatrix(int[][] matrix) {
        for(int i=0;i<matrix.length-1;i++){
            for(int j=0;j<matrix[0].length-1;j++){
                if(matrix[i][j]!=matrix[i+1][j+1]){
                    return false;
                }
            }
        }
        return true; 
    }

时间复杂度: O(i*j)
空间复杂度: O(1)

leetcode-565-数组嵌套

力扣-565
题目:索引从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.

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

leetcode-769-分割数组

力扣-769
题目
数组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 = [1,0,2,3,4]
输出: 4
解释:
我们可以把它分成两块,例如 [1, 0], [2, 3, 4]。
然而,分成 [1, 0], [2], [3], [4] 可以得到最多的块数。

思路

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

时间复杂度: O(N)
空间复杂度: O(1)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值