力扣leetcode-演算法基礎21天刷題記錄①
力扣【leetcode】 演算法基礎21天刷題 記錄篇一
菜雞演算法刷題打卡!!
⭐二分查詢
34. 在排序陣列中查詢元素的第一個和最後一個位置
給定一個按照升序排列的整數陣列 nums,和一個目標值 target。找出給定目標值在陣列中的開始位置和結束位置。
如果陣列中不存在目標值 target,返回 [-1, -1]。
進階:
你可以設計並實現時間複雜度為 O(log n) 的演算法解決此問題嗎?
二分查詢
兩次二分查詢應用
class Solution { public: vector<int> searchRange(vector<int>& nums, int target) { int n = nums.size(); int l = 0, r = n - 1; int index1 = n, index2 = n; // 獲取第一個跟目標數相等的數的下標 while(l <= r){ int mid = l + (r - l) / 2; if(nums[mid] >= target) { r = mid - 1; index1 = mid; } else{ l = mid + 1; } } // 獲取最後一個跟目標數相等的數的下標 l = 0, r = n - 1; while(l <= r){ int mid = l + (r - l) / 2; if(nums[mid] > target) { r = mid - 1; index2 = mid; } else{ l = mid + 1; } } index2--; if(index1 <= index2 && index2 < n && nums[index1] == target && nums[index2] == target){ return vector<int>{index1, index2}; } else { return vector<int>{-1, -1}; } } };
33. 搜尋旋轉排序陣列
整數陣列 nums 按升序排列,陣列中的值 互不相同 。
在傳遞給函式之前,nums 在預先未知的某個下標 k(0 <= k < nums.length)上進行了 旋轉,使陣列變為 [nums[k], nums[k+1], ..., nums[n-1], nums[0], nums[1], ..., nums[k-1]](下標 從 0 開始 計數)。例如, [0,1,2,4,5,6,7] 在下標 3 處經旋轉後可能變為 [4,5,6,7,0,1,2] 。
給你 旋轉後 的陣列 nums 和一個整數 target ,如果 nums 中存在這個目標值 target ,則返回它的下標,否則返回 -1 。
// 旋轉排序陣列本質上就是兩段升序陣列,實際上,抽象講就是經歷一次的二分查詢 class Solution { public: int search(vector<int>& nums, int target) { int n = nums.size(); int l = 0, r = n - 1; while(l < r) { // r獲取兩段升序的銜接 if (nums[r] - nums[l] < 0){ r--; } else { break; } } // 分情況二分查詢 int i, j; if (target == nums[r]){ return r; } // 注意等號!若無,則會忽略第一個元素是尋找數的情況 else if(target >= nums[0] && target < nums[r]){ // target落在 0~r 間的升序陣列 i = 0; j = r - 1; } // target落在 r~n-1 間的升序陣列 else if (target < nums[0]){ i = r + 1; j = n - 1; } else { return -1; } // 二分查詢 while(i <= j) { int mid = i + (j - i) / 2; if (nums[mid] == target) { return mid; } else if ( nums[mid] < target) { i = mid + 1; } else{ j = mid - 1; } } return -1; } };
74. 搜尋二維矩陣
編寫一個高效的演算法來判斷 m x n 矩陣中,是否存在一個目標值。該矩陣具有如下特性:
每行中的整數從左到右按升序排列。
每行的第一個整數大於前一行的最後一個整數。
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
int n = matrix[0].size(), m = matrix.size();
int l = 0, r = m * n -1;
while(l <= r){
int mid = l + (r - l) / 2;
if (matrix[mid / n][mid % n] == target) {
return true;
}
else if (matrix[mid / n][mid % n] < target) {
l = mid + 1;
}
else {
r = mid - 1;
}
}
return false;
}
};
153. 尋找旋轉排序陣列中的最小值
已知一個長度為 n 的陣列,預先按照升序排列,經由 1 到 n 次 旋轉 後,得到輸入陣列。例如,原陣列 nums = [0,1,2,4,5,6,7] 在變化後可能得到:
若旋轉 4 次,則可以得到 [4,5,6,7,0,1,2]
若旋轉 7 次,則可以得到 [0,1,2,4,5,6,7]
注意,陣列 [a[0], a[1], a[2], ..., a[n-1]] 旋轉一次 的結果為陣列 [a[n-1], a[0], a[1], a[2], ..., a[n-2]] 。給你一個元素值 互不相同 的陣列 nums ,它原來是一個升序排列的陣列,並按上述情形進行了多次旋轉。請你找出並返回陣列中的 最小元素 。
// 利用旋轉,產生兩段升序的陣列,則找到分界點,利用升序特點進行求解最小值
class Solution {
public:
int findMin(vector<int>& nums) {
int n = nums.size();
int l = 0, r = n - 1;
while(l <= r) {
if (nums[r] - nums[l] < 0) {
r--;
}
else{
break;
}
}
// 若直接是升序陣列,則兩段升序陣列並不存在,可直接返回
if (r != n-1) {
return min(nums[0],nums[r + 1]);
}
else {
return nums[0];
}
}
};
162. 尋找峰值
峰值元素是指其值大於左右相鄰值的元素。
給你一個輸入陣列 nums,找到峰值元素並返回其索引。陣列可能包含多個峰值,在這種情況下,返回 任何一個峰值 所在位置即可。
你可以假設 nums[-1] = nums[n] = -∞ 。
// 需要再考慮一種情況,即升序,無降,此時返回陣列最後一個位置
class Solution {
public:
int findPeakElement(vector<int>& nums) {
int n = nums.size();
int i = 0;
int index = -1;
while(i < n-1){
if (nums[i] < nums[i+1]) {
i++;
}
else {
index = i;
break;
}
}
if (i == n-1){
index = n-1;
}
return index;
}
};
⭐雙指標
82. 刪除排序連結串列中的重複元素 II
存在一個按升序排列的連結串列,給你這個連結串列的頭節點 head ,請你刪除連結串列中所有存在數字重複情況的節點,只保留原始連結串列中 沒有重複出現 的數字。
返回同樣按升序排列的結果連結串列。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* deleteDuplicates(ListNode* head) {
if(!head){
return head;
}
ListNode* dumpy = new ListNode(0, head);
ListNode* cur = dumpy;
// 遍歷整個連結串列
while(cur->next && cur->next->next) {
// 找到重複的連結串列,進行迴圈替代
if(cur->next->val == cur->next->next->val) {
int x = cur->next->val;
while(cur->next && cur->next->val == x){
cur->next = cur->next->next;
}
}
else{
cur = cur->next;
}
}
return dumpy->next;
}
};
15. 三數之和
給你一個包含 n 個整數的陣列 nums,判斷 nums 中是否存在三個元素 a,b,c ,使得 a + b + c = 0 ?請你找出所有和為 0 且不重複的三元組。
注意:答案中不可以包含重複的三元組。
// 對陣列進行升序排序,k指標從頭遍歷.利用i j雙指標進行移動
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
int n = nums.size();
vector<vector<int>> sum;
// 進行排序
sort(nums.begin(), nums.end());
for(int k = 0; k < nums.size(); ++k){
// 正數,相加也不會等於0
if(nums[k] > 0) {
break;
}
// 相等
if(k > 0 && nums[k] == nums[k - 1]) {
continue;
}
int i = k + 1, j = n - 1;
while(i < j) {
int ans = nums[k] + nums[i] + nums[j];
// <0 說明負數這邊更大,因此移動左指標,使得和接近0
if( ans < 0) {
while(i < j && nums[i] == nums[++i]);
}
else if (ans > 0) {
while(i < j && nums[j] == nums[--j]);
}
else{
sum.push_back(vector<int> {nums[k], nums[i], nums[j]});
while(i < j && nums[i] == nums[++i]);
while(i < j && nums[j] == nums[--j]);
}
}
}
return sum;
}
};
844. 比較含退格的字串
給定 S 和 T 兩個字串,當它們分別被輸入到空白的文字編輯器後,判斷二者是否相等,並返回結果。 # 代表退格字元。
注意:如果對空文字輸入退格字元,文字繼續為空。
class Solution {
public:
bool backspaceCompare(string s, string t) {
return backTo(s) == backTo(t);
}
string backTo(string str) {
string s;
for (char ch : str) {
if(ch != '#') {
// 把非#的字元插入字串
s.push_back(ch);
}
else if (! s.empty()) {
// 遇到#,將此時字串中的最後一個字元切除
s.pop_back();
}
}
return s;
}
};
986. 區間列表的交集
給定兩個由一些 閉區間 組成的列表,firstList 和 secondList ,其中 firstList[i] = [starti, endi] 而 secondList[j] = [startj, endj] 。每個區間列表都是成對 不相交 的,並且 已經排序 。
返回這 兩個區間列表的交集 。
形式上,閉區間 [a, b](其中 a <= b)表示實數 x 的集合,而 a <= x <= b 。
兩個閉區間的 交集 是一組實數,要麼為空集,要麼為閉區間。例如,[1, 3] 和 [2, 4] 的交集為 [2, 3] 。
class Solution {
public:
vector<vector<int>> intervalIntersection(vector<vector<int>>& firstList, vector<vector<int>>& secondList) {
vector<vector<int>> ans;
int i = 0, j = 0;
while (i < firstList.size() && j < secondList.size()) {
int low = max(firstList[i][0], secondList[j][0]);
int high = min(firstList[i][1], secondList[j][1]);
if (low <= high) {
ans.push_back(vector<int> {low, high});
}
if (firstList[i][1] < secondList[j][1]) {
i++;
}
else{
j++;
}
}
return ans;
}
};
11. 盛最多水的容器
給你 n 個非負整數 a1,a2,...,an,每個數代表座標中的一個點 (i, ai) 。在座標內畫 n 條垂直線,垂直線 i 的兩個端點分別為 (i, ai) 和 (i, 0) 。找出其中的兩條線,使得它們與 x 軸共同構成的容器可以容納最多的水。
說明:你不能傾斜容器。
class Solution {
public:
int maxArea(vector<int>& height) {
int l = 0, r = height.size() - 1;
int ans = 0;
while (l < r) {
int area = min(height[l], height[r]) * (r - l);
ans = max(ans, area);
if (height[l] <= height[r]) {
++l;
}
else {
--r;
}
}
return ans;
}
};
⭐滑動視窗
438. 找到字串中所有字母異位詞
給定兩個字串 s 和 p,找到 s 中所有 p 的 異位詞 的子串,返回這些子串的起始索引。不考慮答案輸出的順序。
異位詞 指字母相同,但排列不同的字串。
// 類似567. 字串的排列。也就是說只需考慮字母出現的次數以及總長度即可
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
int n = p.length();
vector<int> a(26,0);
for(auto c : p){
a[c-'a']++;
}
int l = 0, r = 0;
vector<int> a1(a);
vector<int> index;
while(r < s.size()){
a1[s[r] - 'a']--;
while(a1[s[r] - 'a'] < 0){
a1[s[l] - 'a']++;
l++;
}
if(n == r - l + 1) {
index.push_back(l);
}
r++;
}
return index;
}
};
713. 乘積小於K的子陣列
給定一個正整數陣列
nums
和整數k
。請找出該陣列內乘積小於
k
的連續的子陣列的個數。
簡單思路,就是從第一個數開始往後迭乘,超過k則跳出迴圈、
class Solution { public: int numSubarrayProductLessThanK(vector<int>& nums, int k) { int n = nums.size(); int l = 0, r = 0, cnt; int sum = 0; while(l < n) { r = l; cnt = 1; while(r < n) { cnt *= nums[r++]; if(cnt >= k){ break; } sum++; } l++; } return sum; } }; // 但是很遺憾,超出時間限制。於是我們換個思路來解決該難題。 // 之前我們一直是遍歷陣列進行迭乘,那麼我們是否可以只迭乘一次? // 將右指標固定移動遍歷。利用左指標縮短左右指標間隔? 如下:
√ 通過
class Solution {
public:
int numSubarrayProductLessThanK(vector<int>& nums, int k) {
// 正整數乘積小於1,不存在直接返回。
if(k <= 1)
return 0;
int l = 0, r = 0, n = nums.size();
int sum = 0, cnt = 1;
while(r < n) {
// 右指標進行移動,一直迭乘
cnt *= nums[r];
while(cnt >= k) {
// 左指標進行移動條件為 迭乘超過k
cnt /= nums[l++];
}
// 左右指標間隔差即為小於k的連續子陣列的個數
sum += r - l + 1;
r++;
}
return sum;
}
};
209. 長度最小的子陣列
給定一個含有 n 個正整數的陣列和一個正整數 target 。
找出該陣列中滿足其和 ≥ target 的長度最小的 連續子陣列 [numsl, numsl+1, ..., numsr-1, numsr] ,並返回其長度。如果不存在符合條件的子陣列,返回 0 。
跟上一題差不多
class Solution {
public:
int minSubArrayLen(int target, vector<int>& nums) {
int n = nums.size();
if(n == 0){
return 0;
}
int mins = n;
int l = 0, r = 0, len = 0;
int sum = 0;
bool flag = false;
while(r < n){
sum += nums[r];
while(sum >= target) {
len = r - l + 1;
mins = min(mins, len);
sum -= nums[l];
l++;
// 標記存在符合條件的連續子陣列
flag = true;
}
r++;
}
if(!flag)
mins = 0;
return mins;
}
};