今日做一道很常規(guī)的算法題,從數(shù)組中找出三個數(shù)和為0,用簡單的“三個指針”實現(xiàn),卻踩了一個非常愚蠢的坑。。。
bug出現(xiàn)
- 思路:先排序,然后i、j、k有序查找
- 報錯:非法訪問內存
- 錯誤代碼:
/* 3 pointers */
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
sort(nums.begin(), nums.end());
vector<vector<int>> res;
for (int i = 0; i < nums.size() - 2; i++) {
if (i > 0 && nums[i] == nums[i-1]) {
continue; // exclude duplicates
}
int j = i + 1, k = nums.size() - 1;
while (j < k) {
if (nums[i] + nums[j] + nums[k] < 0) {
j++;
} else if (nums[i] + nums[j] + nums[k] > 0) {
k--;
} else {
res.push_back(vector<int>{nums[i], nums[j], nums[k]});
j++;
while (j < k && nums[j] == nums[j-1]) {
j++; // exclude duplicates
}
k--;
while (j < k && nums[k] == nums[k+1]) {
k--; // exclude duplicates
}
}
}
}
return res;
}
};
分析
上面代碼的bug在哪里?(題目要求無序三元組不能重復,于是做了一些去重處理,但這并不是問題的關鍵)我真的找了好久。。。
后來發(fā)現(xiàn)在輸入向量的長度為0或1時才會報這個內存有關的錯誤,于是定位到了最外層for循環(huán)的這一行:
for (int i = 0; i < nums.size() - 2; i++)
由于循環(huán)變量i是第一個數(shù)的下標,而一共有三個數(shù),我便將循環(huán)條件設為了i < nums.size() - 2,問題就出在這里:
nums.size()是unsigned int類型,如果它是0或1,那么減去2之后就變成了一個很大的無符號數(shù)(正數(shù))。于是循環(huán)里面肯定就有非法內存被訪問、被使用。
解決
如果用IDE寫代碼,會報出一個int類型的i與unsigned int類型的nums.size()不匹配的警告,然而我以前卻常常無視它。。。
注意如果這樣寫成下面這樣,雖然類型一致了,但是bug還在:
for (unsigned int i = 0; i < nums.size() - 2; i++)
所以解決這個問題,如果不改變代碼邏輯,就要用強制類型轉換:
for (int i = 0; i < (int)nums.size() - 2; i++)
至此,bug排除,這個故事告訴我們:
- 不要無視warning
- 隱式類型轉換要慎用