LeetCode--数组与矩阵

一、数组类题目

1.移动零(题号:283)

第1次
题目链接:移动零
题目描述:给定一个数组 nums,编写一个函数将所有 0 移动到数组的末尾,同时保持非零元素的相对顺序。
在这里插入图片描述
解题思路:对数组进行遍历,将遍历到的非零元素依次往数组里装,从第一个位置开始装,之后再按照数组的大小在该数组后面补0。定义两个指针,第一个为index,为装非零数的指针,第二个为for循环里的遍历指针。
时间复杂度:O(N)
空间复杂度:O(1)。
代码

    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;
        }
    }

类似引申
27. 移除元素
26. 删除排序数组中的重复项

2.最大连续1的个数(题号:485)

第1次
题目链接:最大连续1的个数
题目描述
给定一个二进制数组, 计算其中最大连续1的个数。
在这里插入图片描述
解题思路:利用增强for循环进行遍历,遇到1将cnt加上1,同时将结果暂存,遇到0将cnt置0,然后取暂存结果的最大值。
代码

class Solution {
    public int findMaxConsecutiveOnes(int[] nums) {
        int result = 0;
        int cnt = 0;
        int pase = 0;
        for(int num : nums){           
            if(num == 1){
                cnt++;
                pase = cnt;
            }
            else {  
                cnt = 0;
            }        
            result = Math.max(result,pase);
        }
        return result;
    }
}

/*参考精简写法
public int findMaxConsecutiveOnes(int[] nums) {
    int max = 0, cur = 0;
    for (int x : nums) {
        cur = x == 0 ? 0 : cur + 1;
        max = Math.max(max, cur);
    }
    return max;
}
*/

3.嵌套数组(题号:565)

第1次
题目链接:嵌套数组
题目描述
索引从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出现重复的元素。
在这里插入图片描述
解题思路:每一个最终结果都是一个圆圈,不管从圆圈的哪个元素开始,回到原来位置时遍历到的长度都是一样的,不同的集合之间互不相交,所以可以将已经遍历的元素标记为-1,知道数组所有元素都变成-1时,则结束遍历,取最大长度的圈(即集合)。
代码

class Solution {
    public int arrayNesting(int[] nums) {
        int length = 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;
                
            }
            length = Math.max(length,cnt);           
        }
        return length;
    }
}

4.分隔数组(题号:769)

第0次
题目链接:分隔数组
题目描述
按照下面的规则,最多能将数组分成多少块?
在这里插入图片描述
解题思路
代码

5.删除排序数组中的重复项 II(题号:769,Medium)

第1次
题目链接:删除排序数组中的重复项 II
题目描述
给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素最多出现两次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
在这里插入图片描述
解题思路:将遍历到的符合条件的数组中元素从数组头开始,从前往后填。
代码
我的垃圾代码:

    public int removeDuplicates(int[] nums) {
        if(nums.length == 0)
            return 0;
        int index = 1;
        int cnt = 1;
        int val = nums[0];
        for(int i=1; i < nums.length; i++){
            if(nums[i] == val && cnt == 1){
                nums[index++] = nums[i];
                cnt++;
            }
            else if(nums[i] != val){
                nums[index++] = nums[i];
                val = nums[i];
                cnt = 1;
            }
        }
        return index;
    }

改进后代码:大赞

    public int removeDuplicates(int[] nums) {   //太优秀优秀优秀了了了
        int i = 0;
        for (int n : nums)
            if (i < 2 || n > nums[i-2])
                nums[i++] = n;
        return i;
    }

6.颜色分类(题号:75,Medium)

第1次
题目链接:颜色分类
题目描述
给定一个包含红色、白色和蓝色,一共 n 个元素的数组,原地对它们进行排序,使得相同颜色的元素相邻,并按照红色、白色、蓝色顺序排列。
此题中,我们使用整数 0、 1 和 2 分别表示红色、白色和蓝色。
在这里插入图片描述
解题思路:三路快排
重要注意:对于数组元素交换,一定是:public void swap(int[] nums, int i, int j),而不是:public void swap(int i, int j)
错误的写法为:

    public void sortColors(int[] nums) {  //错误
        swap(nums[i],nums[j);
    }
    public void swap(int i,int j){
        int temp = j;
        j = i;
        i = temp;
    }

代码

    public void sortColors(int[] nums) {
        int zero = -1;
        int two = nums.length;
        
        for(int i=0;i < two;){
            if(nums[i] == 1)
                i++;
            else if(nums[i] == 2){
                two--;
                //swap(nums[two],nums[i]);
                swap(nums,two,i);       
            }
            else {
                zero++;
               //swap(nums[zero],nums[i++]);
                swap(nums,zero,i++);
            }
        }
    }
   
    public void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }

二、矩阵类题目

1.重塑矩阵(题号:566)

第1次
题目链接:重塑矩阵
题目描述
给出一个由二维数组表示的矩阵,以及两个正整数r和c,分别表示想要的重构的矩阵的行数和列数。
重构后的矩阵需要将原始矩阵的所有元素以相同的行遍历顺序填充。
如果具有给定参数的reshape操作是可行且合理的,则输出新的重塑矩阵;否则,输出原始矩阵。
在这里插入图片描述
在这里插入图片描述
解题思路:首先判断重塑前后两个矩阵的元素个数是否一致,一致则可以重塑,不一致则不可以重塑,返回原矩阵;然后,利用双重for循环,将原矩阵的元素装入所创建的重塑矩阵。
代码

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

2.搜索二维矩阵 II(题号:240)

第0次
题目链接:搜索二维矩阵 II
题目描述
编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target。该矩阵具有以下特性:
  每行的元素从左到右升序排列。
  每列的元素从上到下升序排列。
在这里插入图片描述
解题思路
从最右上角的元素开始一行或者一列地搜索,也就是从例子中的15开始搜索,要是目标值target比15大,则减去15所在的行,要是目标值target比15小,则你去15所在的列,在剩余部分搜索。另外,判断一个二维矩阵是否为空的方法:
1、二维数组首地址是否为空,即array == null;
2、二维数组是否为 {},即 array.length == 0的情况;
3、二维数组是否为{{}},即array.length == 1 && array[0].length == 0的情况;
综上所述,Java中判断二维数组为空的条件为:

if((array==null||array.length==0)||(array.length==1&&array[0].length==0))

转自参考:判断二维矩阵是否为空
代码

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

3.有序矩阵中第K小的元素(题号:378)

第0次
题目链接:有序矩阵中第K小的元素
题目描述
解题思路
代码

4.托普利茨矩阵(题号:766)

第1次
题目链接:托普利茨矩阵
题目描述
如果一个矩阵的每一方向由左上到右下的对角线上具有相同元素,那么这个矩阵是托普利茨矩阵。
给定一个 M x N 的矩阵,当且仅当它是托普利茨矩阵时返回 True。
在这里插入图片描述
在这里插入图片描述
解题思路:用两个for循环,从矩阵的第一行和第一列的每一个元素进行判断,即判断以该元素开始的右斜对角线的元素值是否相等。
代码

class Solution {
    public boolean isToeplitzMatrix(int[][] matrix) {
        boolean result = true;
        for(int i = 0; i < matrix[0].length; i++){
            result = result && check(matrix,0,i);
        }
        for(int i = 1; i < matrix.length; i++){
            result = result && check(matrix,i,0);
        }
        return result;
    }
    
    public boolean check(int[][] matrix, int row, int col){
        int target = matrix[row][col];
        while(row <= matrix.length-1 && col <= matrix[0].length-1){
            if(matrix[row][col] == target){
                row++;
                col++;
            }
            else {
                return false;
            }
        }
        return true;
    }
}

三、双指针类题目

1.两数之和 II - 输入有序数组(题号:167)

第0次
题目链接:两数之和
题目描述
给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数。

函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2。
在这里插入图片描述
解题思路
暴力解法O(N^2):使用两个for循环进行遍历;
时间复杂度O(N):定义两个指针,一个指向数组头部,一个指向数组尾部,在一定条件下分别向中间遍历;
时间复杂度O(Nlog(N)):对数组进行扫描一遍,在扫描到每一个值的时候,使用二分查找;
代码
时间复杂度O(N),空间O(1):

public int[] twoSum(int[] numbers, int target) {
    int i = 0, j = numbers.length - 1;
    while (i < j) {
        int sum = numbers[i] + numbers[j];
        if (sum == target) {
            return new int[]{i + 1, j + 1};
        } else if (sum < target) {
            i++;
        } else {
            j--;
        }
    }
    return null;
}

时间复杂度O(Nlog(N)):

    public int[] twoSum(int[] numbers, int target) {
        int[] result = new int[2];
        for(int i=0; i < numbers.length-1; i++){
            int l = i + 1;
            int h = numbers.length - 1;
            while(l < h){
                int m = (l + h) >>> 1;
                if(numbers[m] < target - numbers[i]){
                    l = m + 1;
                }
                else 
                    h = m;
            }
            if(numbers[i] + numbers[l] == target){
                result[0] = i+1;
                result[1] = l+1;
            }
        }
        return result;
    }

2.平方数之和(题号:633)

第1次
题目链接:平方数之和
题目描述
在这里插入图片描述
解题思路
可以先给c开平方,然后在0~(int)sqrt©之间找两个数进行相加,相加过程可以采用双指针进行同时搜索;

代码

class Solution {
    public boolean judgeSquareSum(int c) {
        int i = 0;
        int j = (int)Math.sqrt(c);

        while(i <= j){
            if(i*i + j*j > c){
                j--;
            }
            else if(i*i + j*j < c){
                i++;
            }
            else {
                return true;
            }
        }
        return false;
    }
}

3.反转字符串中的元音字母(题号:345)

第1次
题目链接:反转字符串中的元音字母
题目描述
编写一个函数,以字符串作为输入,反转该字符串中的元音字母。
在这里插入图片描述
解题思路
解法一:先将字符串s转成字符数组,然后用双指针的方法分别从两头进行遍历,先遍历左边,遍历到元音字母后,遍历右边,两边都遍历到元音字母后交换,然后两边指针都加一,由此循环,直到i < j为止(如果条件是i <= j,则下面的交换条件也是i <= j),最后再将字符数组转成字符串;
解法二:创建一个新数组,使用双指针,从两边进行遍历,然后将遍历到的字符按序填入数组中,再将字符数组转换成字符串;
代码
解法一代码:

class Solution {
    private final static HashSet<Character> vowels = new HashSet<>(
        Arrays.asList('a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'));
    public String reverseVowels(String s) {
        char[] chs = s.toCharArray();
        int i = 0;
        int j = chs.length - 1;
        while(i < j){
          while(!vowels.contains(chs[i]) && i < chs.length - 1){
            i++;
          }
          while(!vowels.contains(chs[j]) && j > 0){
            j--;
          }
          if(i < j){
              swap(chs,i,j);
              i++;
              j--;
          }
        }
      String s2 = new String(chs);
      return s2;
    }
  
    public void swap(char[] chs, int i, int j){
      char t = chs[i];
      chs[i] = chs[j];
      chs[j] = t;
    }
}

解法二代码:

class Solution {
    private final static HashSet<Character> vowels = new HashSet<>(
            Arrays.asList('a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'));

    public String reverseVowels(String s) {
        int i = 0, j = s.length() - 1;
        char[] result = new char[s.length()];
        while (i <= j) {
            char ci = s.charAt(i);
            char cj = s.charAt(j);
            if (!vowels.contains(ci)) {
                result[i++] = ci;
            } else if (!vowels.contains(cj)) {
                result[j--] = cj;
            } else {
                result[i++] = cj;
                result[j--] = ci;
            }
        }
        return new String(result);
    }
}

4.验证回文字符串 Ⅱ(题号:680)

第0次
题目链接:验证回文字符串
题目描述
给定一个非空字符串 s,最多删除一个字符。判断是否能成为回文字符串。
在这里插入图片描述
解题思路
代码

5.合并两个有序数组(题号:88)

第0次
题目链接:合并两个有序数组
题目描述
给定两个有序整数数组 nums1 和 nums2,将 nums2 合并到 nums1 中,使得 num1 成为一个有序数组。
在这里插入图片描述
解题思路
代码

6.环形链表(题号:141)

第1次
题目链接:环形链表
题目描述
给定一个链表,判断链表中是否有环。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
在这里插入图片描述
解题思路:定义两个指针p1和p2,p1指向head,p2指向head.next,往后遍历,p1一次走一步,p2一次走两步,如果有环,则p1和p2一定会相遇(因为p2每次比p1多走一步,肯定会再次追上p1),使用while循环,while循环的条件只需要判断p2和p2.next是否为空就行,不需要判断p1,因为p2走在p1前面。
代码

public class Solution {
    public boolean hasCycle(ListNode head) {
        if(head == null){
            return false;
        }
        ListNode p1 = head;
        ListNode p2 = head.next;
        while(p2 != null && p2.next != null){
            p1 = p1.next;
            p2 = p2.next.next;
            if(p1 == p2){
                return true;
            }
        }
        return false;
    }
}

7.通过删除字母匹配到字典里最长单词(题号:524)

第0次
题目链接:通过删除字母匹配到字典里最长单词
题目描述
给定一个字符串和一个字符串字典,找到字典里面最长的字符串,该字符串可以通过删除给定字符串的某些字符来得到。如果答案不止一个,返回长度最长且字典顺序最小的字符串。如果答案不存在,则返回空字符串。
在这里插入图片描述
解题思路:注意字典的顺序
代码

8.验证回文串(题号:125,Easy)

第1次
题目链接:验证回文串
题目描述
给定一个字符串,验证它是否是回文串,只考虑字母和数字字符,可以忽略字母的大小写。
说明:本题中,我们将空字符串定义为有效的回文串。
在这里插入图片描述
解题思路:首先将字符串中的字母全部转成大写字母,然后双指针。
代码

    public boolean isPalindrome(String s) {
        int l = 0;
        int h = s.length() - 1;
        s = s.toUpperCase();
        while(l < h){
            while(l < h && (s.charAt(l) < '0' || (s.charAt(l) > '9' && s.charAt(l) < 'A') || 
                  s.charAt(l) > 'Z')){
                l++;
            }
            while(l < h && (s.charAt(h) < '0' || (s.charAt(h) > '9' && s.charAt(h) < 'A') || 
                  s.charAt(h) > 'Z')){
                h--;
            }

            if(s.charAt(l) != s.charAt(h))
                    return false;
            else{
                l++;
                h--;
            }
        }
        return true;
    }

9.Container With Most Water(题号:11,Medium)

第1次
题目链接:Container With Most Water
题目描述
在这里插入图片描述
解题思路:使用双指针,左右两边,找到墙高的那一边先保持不变,然后又矮的那一边向高的一边移动,直到两边墙重合。因为只有找到更高的墙,才能形成更大的容量,并且一个巴掌拍不响,找到一边高的墙后,必须从另一边开始寻找,才能有机会找到更大的容量池子。
代码

    public int maxArea(int[] height) {
        int l = 0;
        int h = height.length - 1;
        int area = 0;
        while(l < h){
            area = Math.max(area,(h-l)*Math.min(height[l],height[h]));
            if(height[l] >= height[h]){
                h--;
            }
            else {
                l++;
            }
        }
        return area;
    }

10.长度最小的子数组(题号:209,Medium)

第1次
题目链接:长度最小的子数组
题目描述
给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的连续子数组。如果不存在符合条件的连续子数组,返回 0。
在这里插入图片描述
解题思路:使用同向双指针,而且双指针包括的窗口大小在不断调整,h < nums.length - 1 && sum < s 则++h,否则h >= nums.length - 1 || sum >= s,则l++,在满足sun >= s时进行长度大小判断。
代码
参考优秀代码:建议将这个双指针代码作为模板代码

    public int minSubArrayLen(int s, int[] nums) {
        int l = 0;
        int h = -1;
        int sum = 0;
        int result = nums.length + 1;
        while(l < nums.length){
            if(h < nums.length - 1 && sum < s){
                sum = sum + nums[++h];
            }
            else {    
             /* 这段代码也可以放里面
               if(sum >= s){
                    result = Math.min(result,h-l+1);
                }     */     
                sum = sum - nums[l++];
            }
            if(sum >= s){
                result = Math.min(result,h-l+1);
            }
        }
        if(result == nums.length + 1)
            return 0;
        else 
            return result;
    }

O(n2)级别算法:

class Solution {
    private int min = 0;
    public int minSubArrayLen(int s, int[] nums) {
        int sum = 0;
        for(int i=0; i < nums.length; i++){
            sum += nums[i];
        }
        min = nums.length;
        minSubArrayLen(nums,0,nums.length-1,sum,s);
        return sum < s ? 0 : min;
        
        

    }
    
    public void minSubArrayLen(int[] nums,int l,int r,int sum,int s){
        if(l > r)
            return ;
        if(sum - nums[l] >= s){
            min = Math.min(min,r-l);
            minSubArrayLen(nums,l+1,r,sum-nums[l],s);
        }
        if(sum - nums[r] >= s){
            min = Math.min(min,r-l);
            minSubArrayLen(nums,l,r-1,sum-nums[r],s);
        }
            
        
    }
}

11.无重复字符的最长子串(题号:3,Medium)

第1次
题目链接:添加链接描述
题目描述
给定一个字符串,请你找出其中不含有重复字符的 最长子串 的长度。
在这里插入图片描述
解题思路:同向双指针,在参考代码中,使用大小为256的记忆频率数组(巧妙),然而我的代码中使用传统的for循环判断是否包含某个元素(暴力),不过我能写出来,也值得表扬。
代码
参考优秀代码:建议将这个双指针代码作为模板代码

    public int lengthOfLongestSubstring(String s) {
        int l = 0;
        int h = -1;
        int result = 0;
        int[] freq = new int[256];
        while(l < s.length()){
            if(h < s.length() - 1 && freq[s.charAt(h+1)] == 0){
                freq[s.charAt(++h)] = 1;
            }
            else {
                freq[s.charAt(l++)] = 0;  //通过不断l++找到重复的,并将其置为0,然后h就可以继续向后移动啦!!!
            }
            result = Math.max(result,h-l+1);
        }
        return result;
        
    }

我的代码::

    public int lengthOfLongestSubstring(String s) {
        if(s == null || s.length() == 0)
            return 0;
        int l = 0;
        int h = 0;
        int result = 1;
        while(h < s.length()){
            
            int index = notContain(s,l,h);
            if(index == -1){
                result = Math.max(result,h-l+1);
            }
            else {
                l = index + 1;
            }
            h++;
        }
        return result;
    }
    public int notContain(String s,int l,int h){
        for(int i=l; i < h; i++){
            if(s.charAt(i) == s.charAt(h))
                return i;
        }
        return -1;
    }

12.找到字符串中所有字母异位词(题号:438,Easy)

第0次
题目链接:找到字符串中所有字母异位词
题目描述
给定一个字符串 s 和一个非空字符串 p,找到 s 中所有是 p 的字母异位词的子串,返回这些子串的起始索引。字符串只包含小写英文字母,并且字符串 s 和 p 的长度都不超过 20100。
在这里插入图片描述
解题思路:这题归为Easy,搞笑吧。利用双指针,固定窗口,定义两个长度为26的标记数组freq_s和freq_p,分别来标记s和p,通过固定数组的移动,不断地对freq_s进行更新,并不断与freq_p进行比较,若相同,则将l添加到链表array中,并l++;
代码
非常优秀的参考代码:

    public List<Integer> findAnagrams(String s, String p) {
        List<Integer> array = new ArrayList<Integer>();
        if(p.length() > s.length())
            return array;        
        int [] freq_s = new int[26];
        int [] freq_p = new int[26];
        int l = 0;
        int h = -1;
        for(int i=0; i < p.length(); i++){
            freq_p[p.charAt(i) - 'a']++;
            freq_s[s.charAt(++h) - 'a']++;
        }
        if(Arrays.equals(freq_p, freq_s))
            array.add(l);

        while(h < s.length()-1){
            freq_s[s.charAt(++h) - 'a']++;
            freq_s[s.charAt(l++) - 'a']--;
            if(Arrays.equals(freq_p, freq_s))
                array.add(l);
        }

        return array;
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章