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;
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章