LeetCode -- 查找問題

題目來源
來源:力扣(LeetCode)
鏈接:https

二分查找—神奇模板

鏈接二分查找—神奇模板

一、二分查找

1.x 的平方根(Easy,題號:69)

第0次
題目鏈接:x 的平方根
題目描述
實現 int sqrt(int x) 函數。

計算並返回 x 的平方根,其中 x 是非負整數。

由於返回類型是整數,結果只保留整數的部分,小數部分將被捨去。
在這裏插入圖片描述
解題思路:利用二分查找,當x <= 1時,直接返回x,當x > 1時,令l = 1 , h = x,然後求l和h的中間值,然後令sqrt = x/m,比較sqrt和m的大小來調整l和h。回頭再想想我的垃圾代碼
代碼
參考代碼(使用模板):

    public int mySqrt(int x) {
        if(x == 0)
            return 0;
        int l = 1;
        int h = x;
        while(l < h){
            int m = (l + h + 1) >>> 1;
            if(m > x/m)
                h = m - 1;
            else 
                l = m;
        }
        return l;
    }

我的問題代碼:

class Solution {
    public int mySqrt(int x) {
        if(x == 0 || x ==1)
            return x;
        int l = 1;
        int h = x;
        while(l <= h){
            int m = l + (h-l)/2;
            if(m*m == x){
                return m;
            }
            else if(m*m < x){
                l = m + 1;
            }
            else {
                h = m - 1 ;
            }
        }
        return h;
    }
}

2.尋找比目標字母大的最小字母(Easy)

第1次
題目鏈接:尋找比目標字母大的最小字母
題目描述
給定一個只包含小寫字母的有序數組letters 和一個目標字母 target,尋找有序數組裏面比目標字母大的最小字母。
數組裏字母的順序是循環的。舉個例子,如果目標字母target = ‘z’ 並且有序數組爲 letters = [‘a’, ‘b’],則答案返回 ‘a’。
在這裏插入圖片描述
解題思路
代碼
參考代碼:

public char nextGreatestLetter(char[] letters, char target) {
    int n = letters.length;
    int l = 0, h = n - 1;
    while (l <= h) {
        int m = l + (h - l) / 2;
        if (letters[m] <= target) {
            l = m + 1;
        } else {
            h = m - 1;
        }
    }
    return l < n ? letters[l] : letters[0];
}

我的冗餘代碼:

    public char nextGreatestLetter(char[] letters, char target) {
        int l = 0;
        int h = letters.length - 1;
        int t = (int)target;
        if(target >= letters[h]){
            target = (char)((int)target - 26);
        }
        while(l <= h){
            int mid = l + (h-l)/2;
            if(letters[mid] == target){
                while(mid < letters.length - 1 && letters[mid] == target){
                    mid++;
                }
                return letters[mid];
            }
            else if(letters[mid] > target){
                h = mid -1;
            }
            else {
                l = mid +1;
            }
        }
        return letters[h+1];
    }

3.有序數組中的單一元素(Meduim,題號:540)

第1次
題目鏈接:有序數組中的單一元素
題目描述
給定一個只包含整數的有序數組,每個元素都會出現兩次,唯有一個數只會出現一次,找出這個數。
在這裏插入圖片描述
解題思路
我的思路:中規中矩地按照二分進行查找,根據中間數nums[m]與前一個數以及後一個數的比較,得出單個數的前後位置,考慮了所有情況;
參考思路:比較高級,保證中間數的左邊和右邊都爲偶數個數,這時,只要中位數與左邊相鄰的數相等時,則單個數在中位數左邊,中位數與右邊相鄰的數相等時,則單個數在中位數右邊。
代碼
我的代碼:

    public int singleNonDuplicate(int[] nums) {
        int l = 0;
        int h = nums.length - 1;     
        while(l < h){
            int m = l + (h-l)/2;
            if(nums[m]!= nums[m-1] && nums[m] != nums[m+1])
                return nums[m];
            else if(nums[m] == nums[m-1] && m%2 == 0)
                h = m;
            else if(nums[m] == nums[m+1] && m%2 == 0)
                l = m;
            else if(nums[m] == nums[m-1] && m%2 == 1)
                l = m +1;
            else if(nums[m] == nums[m+1] && m%2 == 1)
                h = m-1;
        }   
        return nums[h];
    }

參考代碼:

public int singleNonDuplicate(int[] nums) {
    int l = 0, h = nums.length - 1;
    while (l < h) {
        int m = l + (h - l) / 2;
        if (m % 2 == 1) {
            m--;   // 保證 l/h/m 都在偶數位,使得查找區間大小一直都是奇數
        }
        if (nums[m] == nums[m + 1]) {
            l = m + 2;
        } else {
            h = m;
        }
    }
    return nums[l];
}

4.第一個錯誤的版本(Easy,題號:278)

第1次
題目鏈接:第一個錯誤的版本
題目描述
你是產品經理,目前正在帶領一個團隊開發新的產品。不幸的是,你的產品的最新版本沒有通過質量檢測。由於每個版本都是基於之前的版本開發的,所以錯誤的版本之後的所有版本都是錯的。

假設你有 n 個版本 [1, 2, …, n],你想找出導致之後所有版本出錯的第一個錯誤的版本。

你可以通過調用 bool isBadVersion(version) 接口來判斷版本號 version 是否在單元測試中出錯。實現一個函數來查找第一個錯誤的版本。你應該儘量減少對調用 API 的次數。
在這裏插入圖片描述
解題思路
代碼
我的垃圾代碼:

    public int firstBadVersion(int n) {
        int l = 1;
        int h = n;

        while(l < h-1){
            int m = l + (h-l)/2;
            if(isBadVersion(m) == true)
                h = m;
            else if(isBadVersion(m) == false)
                l = m;
        }
        if(isBadVersion(l) == true)
            return l;
        else 
            return h;  
    }

參考代碼:

public int firstBadVersion(int n) {
    int l = 1, h = n;
    while (l < h) {
        int mid = l + (h - l) / 2;
        if (isBadVersion(mid)) {
            h = mid;
        } else {
            l = mid + 1;
        }
    }
    return l;
}

5.尋找旋轉排序數組中的最小值(Medium,題號:153)

第2次
題目鏈接:尋找旋轉排序數組中的最小值
題目描述
假設按照升序排序的數組在預先未知的某個點上進行了旋轉。
( 例如,數組 [0,1,2,4,5,6,7] 可能變爲 [4,5,6,7,0,1,2] )。
請找出其中最小的元素。
你可以假設數組中不存在重複元素。
在這裏插入圖片描述
解題思路
代碼
參考模板思路:

    public int findMin(int[] nums) {
        int l = 0;
        int h = nums.length-1;

        while(l < h){
            int m = l + (h-l)/2;
            if(nums[m] < nums[h])
                h = m;
            else 
                l = m + 1;
        }
        return nums[l];
    }

6. 在排序數組中查找元素的第一個和最後一個位置(Medium,題號:34)

第1次
題目鏈接:在排序數組中查找元素的第一個和最後一個位置
代碼:

class Solution {
    public int[] searchRange(int[] nums, int target) {
        if(nums.length == 0)
            return new int[]{-1,-1};
        int l = 0;
        int h = nums.length-1;
        while(l < h){
            int m = l + (h-l)/2;
            if(nums[m] < target){
                l = m + 1;
            }
            else if(nums[m] > target){
                h = m-1;
            }
            else {
                l = m;
                h = m;
            }
        }
        
        while(l-1 >=0 && nums[l-1] == target){
            l--;
        }
        while(h+1 < nums.length && nums[h+1] == target){
            h++;
        }
        return nums[l] == target ? new int[]{l,h} : new int[]{-1,-1};

    }
}

7. 搜索旋轉排序數組(Medium,題號:33)

第1次
題目鏈接:搜索旋轉排序數組
題目描述
假設按照升序排序的數組在預先未知的某個點上進行了旋轉。
( 例如,數組 [0,1,2,4,5,6,7] 可能變爲 [4,5,6,7,0,1,2] )。
搜索一個給定的目標值,如果數組中存在這個目標值,則返回它的索引,否則返回 -1 。
你可以假設數組中不存在重複的元素。
你的算法時間複雜度必須是 O(log n) 級別。
在這裏插入圖片描述
解題思路:二分查找,通過判斷中間數nums[m]與nums[h]的大小,來判定中間數的左邊有序還是右邊有序,判斷要查找的數在不在有序的一半數組中,從而丟棄一半,實現二分查找。
代碼

class Solution {
    public int search(int[] nums, int target) {
        if(nums.length == 0)
            return -1;
        
        int l = 0;
        int h = nums.length-1;
        while(l < h){
            int m = l + (h-l)/2;
            if(nums[m] > nums[h]){    //數組左邊有序
                if(target >= nums[l] && target <= nums[m]){
                    h = m;
                }
                else {
                    l = m + 1;
                }
            }
            else{               //數組右邊有序
                if(target > nums[m] && target <= nums[h]){
                    l = m+1;
                }
                else {
                    h = m;
                }
            }
        }
        return nums[l] == target ? l : -1;

    }
}

8.Pow(x, n)求平方值(Medium,題號:50)

第0次
題目鏈接:Pow(x, n)求平方值
題目描述
實現 pow(x, n) ,即計算 x 的 n 次冪函數。
在這裏插入圖片描述
解題思路
通過折半計算的方式,減少了計算次數,降低了時間複雜度。
代碼

class Solution {
    public double myPow(double x, int n) {
        double res = 1.0;
        for(int i = n; i != 0; i /= 2){
            if(i % 2 != 0){
                res *= x;
            }
            x *= x;
        }
        return  n < 0 ? 1 / res : res;
    }
} 

10.搜索二維矩陣(Medium,題號:74)

第1次
題目鏈接:搜索二維矩陣
題目描述
編寫一個高效的算法來判斷 m x n 矩陣中,是否存在一個目標值。該矩陣具有如下特性:
  每行中的整數從左到右按升序排列。
  每行的第一個整數大於前一行的最後一個整數。
在這裏插入圖片描述
解題思路:使用兩次二分法
代碼

    public boolean searchMatrix(int[][] matrix, int target) {
        if(matrix.length == 0 || matrix[0].length == 0)
            return false;
        int l = 0;
        int h = matrix.length - 1;
        int l1 = 0;
        int h1 = matrix[0].length - 1;
        while(l < h){
            int m = (l + h) >>> 1;
            if(matrix[m][h1] < target)
                l = m + 1;
            else 
                h = m;
        }        
        while(l1 < h1){
            int m = (l1 + h1) >>> 1;
            if(matrix[l][m] < target)
                l1 = m + 1;
            else
                h1 = m;
        }        
        if(matrix[l][l1] == target)
            return true;
        else 
            return false;
    }

11.搜索旋轉排序數組 II(Medium,題號:81)

第1次
題目鏈接:搜索旋轉排序數組 II
題目描述
假設按照升序排序的數組在預先未知的某個點上進行了旋轉。
( 例如,數組 [0,0,1,2,2,5,6] 可能變爲 [2,5,6,0,0,1,2] )。
編寫一個函數來判斷給定的目標值是否存在於數組中。若存在返回 true,否則返回 false。
在這裏插入圖片描述
解題思路:先用兩個while循環去掉與nums[l]和nums[h]相鄰的重複元素,然後再分三種情況,一:中位數直接等於target,二:中位數左邊有序(nums[m] >= nums[l],右邊不一定有序),三:中位數右邊有序(nums[m] < nums[l],左邊不一定有序)。最後判斷剩下的數nums[h](這裏最後剩下的nums[h]一定的不變的,由於兩個while循環,nums[l]可能已經越界)是否等於target。
代碼

    public boolean search(int[] nums, int target) {
        if(nums.length == 0)
            return false;
        int l = 0;
        int h = nums.length - 1;
        while(l < h){
            while(l < h && nums[l] == nums[l+1]) l++;
            while(l < h && nums[h] == nums[h-1]) h--;
            int m = (l + h) >>> 1;
            
            if(nums[m] == target){
                l = m;
                h = m;
            }
            else if(nums[m] >= nums[l]){
                if(target > nums[m] || target < nums[l])
                     l = m + 1;
                else 
                     h = m;
            }
            else {
                if(target > nums[m] && target <= nums[h])
                    l = m + 1;
                else 
                    h = m;
            }
        }
        if(nums[h] == target)
                return true;
            else 
                return false;
    }

12.尋找峯值(Medium,題號:81)

第1次
題目鏈接:尋找峯值
題目描述
峯值元素是指其值大於左右相鄰值的元素。
給定一個輸入數組 nums,其中 nums[i] ≠ nums[i+1],找到峯值元素並返回其索引。
數組可能包含多個峯值,在這種情況下,返回任何一個峯值所在位置即可。
你可以假設 nums[-1] = nums[n] = -∞。
在這裏插入圖片描述
解題思路
O(N)解法:不符合要求,從l=0往後遍歷,尋找一個下降點;
O(logN)解法:符合要求,二分法,若中位數nums[m]爲即將下降的點,則中位數左邊必然存在峯值點(若從nums[l]開始就一直遞減,那麼nums[l]算是峯值點,因爲nums[-1] = 負無窮),否則,中位數右邊一定存在峯值點(因爲nums[h] = 負無窮,若nums[m]之後一直遞增,那麼至少最後一個元素算是峯值點)。

代碼
O(N)解法:

    public int findPeakElement(int[] nums) {
        if(nums.length == 0)
            return 0;
        int l = 0;
        int h = nums.length - 1;
        while(l < h){
            if(nums[l] < nums[l+1]){
                l++;
            }
            else 
                h = l;
        }
        return l;
    }

O(logN)解法:

    public int findPeakElement(int[] nums) {   //優秀的代碼
        if(nums.length == 0)
            return 0;
        int l = 0;
        int h = nums.length - 1;
        while(l < h){
            int m = (l + h) >>>1;
            if(nums[m] > nums[m+1]){
                h = m;
            }
            else {
                l = m + 1;
            }
        }
        return l;
    }

二、查找問題

1.兩個數組的交集(Easy,題號:349)

第1次
題目鏈接:兩個數組的交集
題目描述
給定兩個數組,編寫一個函數來計算它們的交集。
在這裏插入圖片描述
解題思路
使用Set:使用兩個Set集合,set和set1,先將數組nums1中的元素都放到set裏,然後遍歷nums2,看看set中是否包含nums2中的元素,包含則將該元素加到set1中,最後再遍歷set1,將其中的元素都轉化爲數組。
代碼
使用Set:

    public int[] intersection(int[] nums1, int[] nums2) {
        Set<Integer> set = new HashSet<Integer>();
        for(int i=0; i < nums1.length; i++){
            set.add(nums1[i]);
        }
        Set<Integer> set1 = new HashSet<Integer>();
        for(int x : nums2){
            if(set.contains(x)){
                set1.add(x);
            }
        }
        
        int index = 0;
        int[] nums = new int[set1.size()];
        for(int e : set1) {
        	nums[index++] = e;
        }
        return nums;
    }

2.兩個數組的交集 II(Easy,題號:350)

第1次
題目鏈接:兩個數組的交集 II
題目描述
給定兩個數組,編寫一個函數來計算它們的交集。
在這裏插入圖片描述
解題思路
使用Map:使用一個Map,先將nums1中的元素作爲鍵Key放到map中,值Value爲頻次(通過獲取上一個Value值+1的方式來得到),遍歷nums2中元素,看是否在map中,如果在,則將其存入list中,最後將list轉化爲數組。
代碼
使用Map:

    public int[] intersect(int[] nums1, int[] nums2) {
        Map<Integer,Integer> map = new HashMap<Integer,Integer>();

        for(int i=0; i < nums1.length; i++){
            if(map.containsKey(nums1[i])){
                map.put(nums1[i],map.get(nums1[i]) + 1);
            }
            else {
                map.put(nums1[i],1);
            } 
        }
        List<Integer> list = new ArrayList<Integer>();
        for(int i=0; i < nums2.length; i++){
            if(map.containsKey(nums2[i])&& map.get(nums2[i]) > 0){
                list.add(nums2[i]);
                map.put(nums2[i],map.get(nums2[i])-1);
            }
        }
        
        int[] array = new int[list.size()];
        for(int i=0; i < array.length; i++){
            array[i] = list.get(i);
        }
        return array;
    }

3.有效的字母異位詞(Easy,題號:242)

第1次
題目鏈接:有效的字母異位詞
題目描述
給定兩個字符串 s 和 t ,編寫一個函數來判斷 t 是否是 s 的字母異位詞。
在這裏插入圖片描述
解題思路:通過map,先將一個字符串以字符的方式放入map中,然後再遍歷遍歷另一個字符串,若map中包含對應字符串,則將其value-1,最後將value值爲0的鍵值對刪除。
代碼

    public boolean isAnagram(String s, String t) {
        Map<Character,Integer> map = new HashMap<Character,Integer>();
        for(int i=0; i < s.length(); i++){
            if(map.containsKey(s.charAt(i)))
                map.put(s.charAt(i),map.get(s.charAt(i))+1);
            else 
                map.put(s.charAt(i),1);
        }      
        for(int i=0; i < t.length(); i++){
            if(map.containsKey(t.charAt(i))){
                map.put(t.charAt(i),map.get(t.charAt(i))-1);
            }
            else 
                return false;
            
            if(map.get(t.charAt(i)) == 0)
  **加粗樣式**              map.remove(t.charAt(i));
        }
        return map.isEmpty();
    }

在這裏插入圖片描述

4.快樂數(Easy,題號:202)

第1次
題目鏈接:快樂數
題目描述
編寫一個算法來判斷一個數是不是“快樂數”。
一個“快樂數”定義爲:對於一個正整數,每一次將該數替換爲它每個位置上的數字的平方和,然後重複這個過程直到這個數變爲 1,也可能是無限循環但始終變不到 1。如果可以變爲 1,那麼這個數就是快樂數。
在這裏插入圖片描述
解題思路:使用Set,將數字拆分求和使用遞歸。
代碼

    public boolean isHappy(int n) {
        Set<Integer> set = new HashSet<Integer>();

        while(!set.contains(n)){
            set.add(n);
            n = fun(n);
            if(n == 1)
                return true;
        }
        return false;
    }
    
    public int fun(int n){
        if(n == 0)
            return 0;
        int a = n%10;
        return a*a + fun(n/10);
    }

在這裏插入圖片描述
代碼2020.5.7:

class Solution {
    public boolean isHappy(int n) {
        Set<Integer> set = new HashSet<Integer>();
        int x = pow(n);
        while(!set.contains(x)){
            set.add(x);
            x = pow(x);
            if(x == 1)
                return true;
        }
        return false;
    }
    
    public int pow(int n){
        if(n/10 == 0)
            return n*n;
        int res = 0;
        while(n/10 > 0){
            res = res + (n%10)*(n%10);
            n = n/10;
        }
        res = res + n*n;
        return res;
    }
}

5.單詞規律(Easy,題號:290)

第2次
題目鏈接:單詞規律
題目描述
給定一種規律 pattern 和一個字符串 str ,判斷 str 是否遵循相同的規律。
這裏的 遵循 指完全匹配,例如, pattern 裏的每個字母和字符串 str 中的每個非空單詞之間存在着雙向連接的對應規律。
在這裏插入圖片描述
解題思路:使用map,同時將str字符串使用split函數以正則表達式方式分割成字符串數組,然後使用一次for循環遍歷。
代碼

    public boolean wordPattern(String pattern, String str) {
        if(pattern == null || str == null){
            return false;
        }
        Map<Character,String> map = new HashMap<Character,String>();
        String[] arr_s = str.split(" ");
        if(pattern.length() != arr_s.length)
            return false;
        for(int i=0; i < pattern.length(); i++){

            if(map.containsKey(pattern.charAt(i))){
                if(!arr_s[i].equals(map.get(pattern.charAt(i))))
                    return false;
            }
            else {
                if (map.containsValue(arr_s[i]))
                    return false;
                else
                    map.put(pattern.charAt(i),arr_s[i]);
            }
        } 
        return true;
    }

在這裏插入圖片描述

6.同構字符串(Easy,題號:205)

第1次
題目鏈接:同構字符串
題目描述
給定兩個字符串 s 和 t,判斷它們是否是同構的。
如果 s 中的字符可以被替換得到 t ,那麼這兩個字符串是同構的。
所有出現的字符都必須用另一個字符替換,同時保留字符的順序。兩個字符不能映射到同一個字符上,但字符可以映射自己本身。
在這裏插入圖片描述
解題思路:使用map集合,遍歷到一對s.charAt(i)和t.charAt(i),看看以s.charAt(i)爲Key的map中有沒有,若有,判斷其Value是否爲t.charAt(i),不是則返回false;若沒有,判斷map中是否有以t.charAt(i)爲Value的值,若有,判斷其Key是否爲s.charAt(i),不是則返回false。
代碼

    public boolean isIsomorphic(String s, String t) {
        if(s.length() == 0 && t.length() == 0)
            return true;
        Map<Character,Character> map = new HashMap<Character,Character>();
        for(int i=0; i < s.length(); i++){
            if(map.containsKey(s.charAt(i))){
                if(map.get(s.charAt(i)) != t.charAt(i)){
                    return false;
                }
            }
            else {
                if(map.containsValue(t.charAt(i)))
                    return false;
                map.put(s.charAt(i),t.charAt(i));
            }
        }
        return true;
    }

在這裏插入圖片描述

7.兩數之和(Easy,題號:1)

第1次
題目鏈接:兩數之和
題目描述
給定一個整數數組 nums 和一個目標值 target,請你在該數組中找出和爲目標值的那 兩個 整數,並返回他們的數組下標。
你可以假設每種輸入只會對應一個答案。但是,你不能重複利用這個數組中同樣的元素。
在這裏插入圖片描述
解題思路
O(N^2):暴力解法,使用雙層for循環進行遍歷;
O(N):使用Map數據結構,nums[i]作爲鍵,索引i作爲值,判斷Map裏有沒有target-nums[i],若有,直接返回,若沒有,將nums[i]添加到Map中。
代碼
O(N^2):

    public int[] twoSum(int[] nums, int target) {
        int[] arr = new int[2];
        for(int i=0; i < nums.length; i++){
            for(int j = i + 1; j < nums.length ; j++){
                if(target - nums[i] == nums[j]){
                    arr[0] = i;
                    arr[1] = j;
                }
            }
        }
        return arr;
    }

O(N):

    public int[] twoSum(int[] nums, int target) {
        int[] arr = new int[2];
        Map<Integer,Integer> map = new HashMap<Integer,Integer>();
        for(int i=0; i < nums.length; i++){
            if(map.containsKey(target-nums[i])){
                    arr[0] = map.get(target-nums[i]);
                    arr[1] = i;
                    return arr;         
            }
            else 
                map.put(nums[i],i);
        }       
        return arr;
    }

8.三數之和(Medium,題號:15)

第1次
題目鏈接:三數之和
題目描述
給定一個包含 n 個整數的數組 nums,判斷 nums 中是否存在三個元素 a,b,c ,使得 a + b + c = 0 ?找出所有滿足條件且不重複的三元組。
注意:答案中不可以包含重複的三元組。
在這裏插入圖片描述
解題思路
代碼

class Solution {
    public List<List<Integer>> threeSum(int[] nums) {
        List<List<Integer>> lists = new ArrayList<List<Integer>>();
        Set<List<Integer>> set = new HashSet<List<Integer>>();
         
        for(int i=0; i < nums.length; i++){
            Set<Integer> s1 = new HashSet<Integer>();  
            int target = 0-nums[i];
            for(int j=i+1; j < nums.length; j++){
                if(!s1.contains(target-nums[j])){
                    s1.add(nums[j]);
                }
                else{
                    List<Integer> list = new ArrayList<Integer>();
                    int min = Math.min(Math.min(nums[i],target-nums[j]),nums[j]);
                    int max = Math.max(Math.max(nums[i],target-nums[j]),nums[j]);
                    list.add(min);
                    list.add(0-min-max);
                    list.add(max);
                    set.add(list);
                
                }
            }
        }
        
        for(List<Integer> x : set){
            lists.add(x);
        }
        
        return lists;

    }
}

在這裏插入圖片描述

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