陣列理論基礎
陣列是存放在連續記憶體空間上的相同型別資料的集合
陣列徐璈注意的是:
- 陣列的下標都是從0開始的
- 陣列記憶體空間是的地址是連續的
正因為舒適的記憶體空間是連續的,所以在刪除和增添元素的時候,需要移動其他元素的地址。
在c++中,vector的底層實現是array,嚴格來說,vector是容器,不是陣列。
陣列的元素不能刪除,只能被覆蓋。
704 二分查詢
題目連結:https://leetcode.cn/problems/binary-search/
給定一個 n 個元素有序的(升序)整型陣列 nums 和一個目標值 target ,寫一個函式搜尋 nums 中的 target,如果目標值存在返回下標,否則返回 -1。
示例 1:
輸入: nums = [-1,0,3,5,9,12], target = 9
輸出: 4
解釋: 9 出現在 nums 中並且下標為 4
示例 2:
輸入: nums = [-1,0,3,5,9,12], target = 2
輸出: -1
解釋: 2 不存在 nums 中因此返回 -1
之前做過,但是現在一看還是很懵,沒有一個清晰的解題思路。先看以便隨想錄上的解題思路,自己實現,總結收穫。
解題思路:
首先需要看題的條件:有序陣列,陣列中無重複元素。如果是包含重複元素的陣列,使用二分法查詢返回元素下標可能不是唯一的。
區間的定義兩種,左閉右閉即[left, right],或者左閉右開即[left, right)。
第一種解法 左閉右閉:
[left, right]區間注意:
- while (left <= right) 要使用 <= ,因為left == right是有意義的,所以使用 <=
- if (nums[middle] > target) right 要賦值為 middle - 1,因為當前這個nums[middle]一定不是target,那麼接下來要查詢的左區間結束下標位置就是 middle - 1
自己按照思路寫出的程式碼
class Solution {
public:
int search(vector<int>& nums, int target) {
int left = 0;
int right = **nums.size()**-1;
while(left <= right){
//int middle = left + ((right - left) / 2);// 防止溢位 等同於(left + right)/2 這裡不是很懂。
**int mid = (right + left)/2;**
if(nums[mid] > target) {
**right = mid;** //這裡應該是right=mid-1,因為如果當前mid對應的值大於目標值,應該在[left,mid-1]區間繼續找,因為是雙閉的區間。
}else if(nums[mid] < target){
**left = mid;** //所以這裡也就是left = mid+1
}else return mid;
}
return -1;
}
};
總結:
vector的size .size()
為什麼要用left + ((right - left) / 2):
在計算中間值時,常見的表示式是 int middle = (left + right) / 2;。然而,當 left 和 right 是非常大的整數時,它們的和 left + right 可能會超過整數的最大值,從而導致溢位錯誤。此時,結果將是不正確的,可能會引發程式錯誤或崩潰。
第二種解法:左閉右開[left, right)
左閉右開時,僅需注意while(left < right) 兩個不能相等
if(nums[mid] > target) right = mid; 即可
27.移除元素
題目連結:https://leetcode.cn/problems/remove-element/description/
給你一個陣列 nums 和一個值 val,你需要 原地 移除所有數值等於 val 的元素。元素的順序可能發生改變。然後返回 nums 中與 val 不同的元素的數量。
假設 nums 中不等於 val 的元素數量為 k,要透過此題,您需要執行以下操作:
更改 nums 陣列,使 nums 的前 k 個元素包含不等於 val 的元素。nums 的其餘元素和 nums 的大小並不重要。
返回 k。
題目分析:
原地:不使用額外的陣列空間,在原地修改輸入陣列並在O(1)額外空間的條件下完成。
元素順序可以改變,不需要考慮陣列中超出新長度後面的元素。
暴力解法
思路:依次遍歷陣列中的所有元素,若等於val,size--,否則繼續遍歷。這時如何改變nums陣列的值?即遇到值=val如何讓陣列後邊的值覆蓋前邊的值。
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int size = nums.size();
for(int i = 0;i < size;i++){
if(nums[i] == val){
**for(int j = i+1; j < size; j++){ //遇到陣列中值=val,讓後一個陣列值覆蓋當前值**
** nums[j-1] = nums[j]; **
}
**i--;// i--是當前值被後值覆蓋,所以是新值,在下一輪迴圈還需要針對當前的i值做判斷**
size--;
}
}
return size;
}
};
重點 雙指標法:
我的印象是設定一個fast一個slow指標。一個用於遍歷陣列,一個用於指示刪除元素後陣列的下標。
看完程式碼隨想錄後:
遇到陣列值=val,fast繼續向後遍歷,slow不變,之後依次覆蓋前面陣列數值。
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int size = nums.size();
int slow = 0;
for(int fast = 0; fast < size; fast++){
//錯誤寫法 這裡我想錯了,總想等於val的時候應該做什麼操作。
// if(nums[fast] == val){
// fast++;
// nums[fast-1] = nums[fast];
// }
// else slow++;
//只需要nums[fast] != val,才去給nums[slow]賦值就可以了。
if(nums[fast] != val)
nums[slow++] = nums[fast];
}
return slow;
}
};
總結:
暴力解法注意雙層遍歷讓nums陣列後值覆蓋前值。
雙指標法注意只有nums[fast] != val,再去給nums[slow]賦值。
977.有序陣列的平方
題目連結:https://leetcode.cn/problems/squares-of-a-sorted-array/
給你一個按 非遞減順序 排序的整數陣列 nums,返回 每個數字的平方 組成的新陣列,要求也按 非遞減順序 排序。
示例 1:
輸入:nums = [-4,-1,0,3,10]
輸出:[0,1,9,16,100]
解釋:平方後,陣列變為 [16,1,0,9,100]
排序後,陣列變為 [0,1,9,16,100]
示例 2:
輸入:nums = [-7,-3,2,3,11]
輸出:[4,9,9,49,121]
題目分析
陣列是非遞減,輸出的新陣列要求也按照非遞減排序。
我的思路
這裡給每一個陣列元素平方在返回比較簡單,但是如何返回的陣列元素也按照非遞減的順序排列,沒有太好的思路。依舊是雙指標法,雙指標怎麼用?
看完隨想錄後
其實挺簡單,讓兩個指標分別指向頭和尾,定義一個新陣列,兩指標依次向中間遍歷,看平方值的大小來判斷哪個先放入新陣列。
我的錯誤解法
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
vector<int> result;
int i = 0, j = nums.size()-1;
while(i <= j){
//應該預先分配好result的值,再向陣列加元素,這樣減少頻繁push_back
//這個問題很大,我想的是如果i的nums值<j的nums值,就先向陣列加入i的值,再加入j的值,這樣是不對的。
if(nums[i] * nums[i] < nums[j] * nums[j]){
result.push_back(nums[i] * nums[i]);
result.push_back(nums[j] * nums[j]);
}
if(nums[i] * nums[i] == nums[j] * nums[j]){
result.push_back(nums[i] * nums[i]);
result.push_back(nums[i] * nums[i]);
}
i++;
j--;
}
return result;
}
};
正確解法:
class Solution {
public:
vector<int> sortedSquares(vector<int>& nums) {
vector<int> result(nums.size(),0);
int i = 0, j = nums.size()-1;
//定義一個新的全域性k值,用於新陣列的下標值
int k = j;
while(i <= j){
// j--和i++只需要在其中nums[i]和nums[j]加入了新陣列才用
if(nums[i] * nums[i] < nums[j] * nums[j]){
result[k--] = nums[j] * nums[j];
j--;
}else{
result[k--] = nums[i] * nums[i];
i++;
}
}
return result;
}
};
總結
我覺得最大的問題就是我總想著雙指標i和j一起+或者-,這樣是不對的,當滿足條件僅改變其中一個就可以。