代碼隨想錄算法訓練營|第一天|704.二分查找、27.移除元素。刷题心得(c++)

目录

讀題

704. 二分查找

自己看到题目的第一想法

看完代码随想录之后的想法

27. 移除元素

自己看到题目的第一想法

看完代码随想录之后的想法

704. 二分查找 - 實作

思路

Code

27. 移除元素 實作

思路

Code

Submit 第一次: 兩個都Submit 失敗

總結


讀題

704. 二分查找

自己看到题目的第一想法

在看到的一瞬間,對於這個題目使用二分法去解的理解不難,甚至感覺自己馬上就可以解出來

看完代码随想录之后的想法

看完代碼隨想錄後,忽然意識到自己不是了解,只是因為沒有碰到這個問題,當下看完卡哥的講解後,才發現自己沒有注意到的問題

  1. 對於區間的定義不明確,之前根本沒有去思考是左閉右閉、還是左閉右開
  2. 對於buffer overflow 的問題也沒有去思考

並且在訓練營中看到同伴有詢問((right - left) >> 1)這個操作,才發現自己也可以來嘗試使用這樣的bitwise 右移操作將數值除以2的n次方,來嘗試使用看看。

27. 移除元素

自己看到题目的第一想法

在這個題目中,看到移除元素的操作很直覺,就是刪掉,在實作上也有了解到要作覆蓋的動作,解釋如下

數組因為是一個連續記憶體位置,假設一個a陣列刪除a[2],那就是需要用覆蓋的,如果直接把值刪除,假設取a[2]這個元素,並不會如我們預想的出現原本a[3]的數值,所以在刪除元素上,是需要將a[2]後面的值依序往前移

看完代码随想录之后的想法

在看完代碼隨想錄之後,發現自己之前的作法很沒有效率,利用雙指針,可以讓刪除這個動作由原本的O(n^2) 縮減到 O(n) 值行完成,方法很巧妙,但是之前自己就是想不到,接下來會來試試看暴力解以及快慢指針的實作

704. 二分查找 - 實作

思路

實作三個作法

  1. 左閉右閉
    1. Left == Right 是有意義的
    2. left = 0, right = nums.size() - 1;
    3. while( left ≤ right)
    4. middle = left + ( right - left ) / 2
    5. if nums[middle] > target, then right = middle -1
    6. if nums[middle] < target, then left = middle + 1
  2. 左閉右開
    1. Left == Right 是沒有意義的,因為搜尋範圍中不包含 right
    2. left = 0, right = nums.size();
    3. while( left < right)
    4. middle = left + ( right - left ) / 2
    5. if nums[middle] > target, then right = middle
    6. if nums[middle] < target, then left = middle + 1
  3. 使用bitwise進行操作
    1. middle = left + (( right - left ) >> 1)

Code

  1. 左閉右閉

    class Solution{
    public:
    	int search(vector& nums, int target){
    		int left = 0;
    		int right = nums.size() - 1;
    		int middle = 0;
    	
    	  while (left <= right){
    			middle = left + ((right - left) / 2);
    			if(nums[middle] > target){
    				right = middle - 1;
    			}		
    			else if(nums[middle] < target){
    				left = middle + 1;	
    			}
    			else{
    				return middle;
    			}
    		}
    	return -1;
    	}
    };
    
  2. 左閉右開

    class Solution{
    public:
    	int search(vector& nums, int target){
    		int left = 0;
    		int right = nums.size();
    		int middle = 0;
    	
    	  while (left < right){
    			middle = left + ((right - left) / 2);
    			if(nums[middle] > target){
    				right = middle;
    			}		
    			else if(nums[middle] < target){
    				left = middle + 1;	
    			}
    			else{
    				return middle;
    			}
    		}
    	return -1;
    	}
    };
    
  3. 左閉右閉,使用bitwise操作

    class Solution{
    public:
    	int search(vector& nums, int target){
    		int left = 0;
    		int right = nums.size();
    		int middle = 0;
    	
    	  while (left < right){
    			middle = left + ((right - left) >> 1);
    			if(nums[middle] > target){
    				right = middle;
    			}		
    			else if(nums[middle] < target){
    				left = middle + 1;	
    			}
    			else{
    				return middle;
    			}
    		}
    	return -1;
    	}
    };
    

27. 移除元素 實作

思路

  1. 暴力解
    1. 因為 0 ≤ nums[i] ≤ 50 ,並且 0 ≤ val ≤ 100,所以假設nums[i] > 50,則陣列中沒有需要刪除的元素,直接return 原陣列長度
    2. 設定k = nums.size();
    3. 建立第一個迴圈,因為 0 ≤ nums.length ≤ 100 所以,設置for迴圈,遍歷k的數量,設定為k是因為陣列長度會有改變
    4. 設定一個判斷式,假設 nums[i] == val,則執行第二個迴圈
    5. 第二個迴圈,假設 nums[i] == val 代表目前陣列位置為 i 中的數值需要被後一個值覆蓋,所以設定j = i; j < k - 1; j++,k - 1的原因是,因為要使用後面一個數值,所以要避免overflow的狀況發生,所以設定k-1
    6. 跳出第二個迴圈後,仍然在判斷式的範圍當中,設置k--; 將nums.size() - 1,並且因為所有數值往前一位,所以也要將i--;
    7. 跑完迴圈後 return k;
  2. 快慢指針法
    1. 因為 0 ≤ nums[i] ≤ 50 ,並且 0 ≤ val ≤ 100,所以假設nums[i] > 50,則陣列中沒有需要刪除的元素,直接return 原陣列長度
    2. 設定兩個指針
      1. 快指針 = 0;
      2. 慢指針 = 0;
    3. 建立一個for 迴圈完整跑完陣列範圍,i = 0; i < nums.size(); i++ → 快指針走的位置
    4. 設定一個條件是,只有快指針指到不是val,則慢指針的數值才增加
    5. 跑完迴圈後,回傳慢指針的數值, 因為陣列的起始為0;

Code

  1. 暴力解
class Solution{
public:
	int RemoveElement(vector&nums, int val){
			if(val > 50){
				return nums.size();
			}
			int k = nums.size();
			for( int i = 0; i < k; i++){
				if(nums[i] == val){
					for(int j = i; j < k - 1; j++){
						nums[j] = nums[j + 1];
					}
					i--;
					k--;
				}
			}
			return k;
	}
};
  1. 快慢指針解
class Solution{
public:
	int RemoveElement(vector&nums, int val){
			if(val > 50){
				return nums.size();
			}
			int FastPoint = 0;
			int SlowPoint = 0;
			for( FastPoint = 0; FastPoint < 100; FastPoint++){
				if(nums[FastPoint] != val){
					nums[SlowPoint] = nums[FastPoint];
					SlowPoint++;
				}
			}
			return SlowPoint;
	}
};

Submit 第一次: 兩個都Submit 失敗

  1. 暴力解
    在第二個迴圈中,我會把當下陣列下標為i 值由i+1來替換掉,後續依序補上但出了這個迴圈後,如果沒有i-- 那第一個迴圈就會在 i ++ ,但如果這樣的話,這個下標原本為i + 1 的值,會沒有被檢查到,就被省略掉了,所以必須在陣列被修改的時候,將i 也向前移動一位
  2. 快慢指針解
    for迴圈太快去寫範圍是100,導致程式執行失敗,應該要改為nums.size();慢指針的數值不能加一,忽略了在最後一次,慢指針仍然會在結束時加一,數值剛好就是nums.size().

總結

● 自己实现过程中遇到哪些困难

  • 704. 二分查找
    對於區間的定義明確後,解起來就沒有太困難的地方,主要是要把思路想好,讓自己可以跟著思路走

  • 27. 移除元素

    真實說明了,概念很清楚,實作就矇了,在暴力解跟快慢指針上都在小細節上敗了

    • 暴力解

      第二個迴圈中,我會把當下陣列下標為i 值由i+1來替換掉,後續依序補上,沒有i-- ,導致錯誤產生

    • 快慢指針

      for迴圈太快去寫範圍是100,導致程式執行失敗,應該要改為nums.size();

● 今日收获,记录一下自己的学习时长

總結了這兩題的所遇到的困難,感覺是在自己對於定義清不清晰

二分查找的定義有釐清清楚,所以在代碼的實現上沒有太大的問題

移除元素

  • 暴力解沒有去思考好移動的定義
  • 快慢指針沒有去想清楚執行的範圍

今天在學習上大約花了兩到三小時,主要是在讀題記錄筆記、寫題記錄思路以及最後的統整花了比較多時間,但這些紀錄東西記錄下來後,其實更加知道自己的不足在哪

你可能感兴趣的:(c++,leetcode)