題目來源:
來源:力扣(LeetCode)
鏈接:https
文章目錄
- 二分查找---神奇模板
- 一、二分查找
- 1.x 的平方根(Easy,題號:69)
- 2.尋找比目標字母大的最小字母(Easy)
- 3.有序數組中的單一元素(Meduim,題號:540)
- 4.第一個錯誤的版本(Easy,題號:278)
- 5.尋找旋轉排序數組中的最小值(Medium,題號:153)
- 6. 在排序數組中查找元素的第一個和最後一個位置(Medium,題號:34)
- 7. 搜索旋轉排序數組(Medium,題號:33)
- 8.Pow(x, n)求平方值(Medium,題號:50)
- 10.搜索二維矩陣(Medium,題號:74)
- 11.搜索旋轉排序數組 II(Medium,題號:81)
- 12.尋找峯值(Medium,題號:81)
- 二、查找問題
二分查找—神奇模板
鏈接:二分查找—神奇模板
一、二分查找
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;
}
}