演算法筆記(沒寫完)
關於指標的操作
int x;
int * p1 = &x; // 指標可以被修改,值也可以被修改
const int * p2 = &x; // 指標可以被修改,值不可以被修改(const int)
int * const p3 = &x; // 指標不可以被修改(* const),值可以被修改
const int * const p4 = &x; // 指標不可以被修改,值也不可以被修改
//指標函式:返回一個型別是指標的函式 int* func(int a,int b){ int* sum=new int(a+b); return sum; } //函式指標:指向函式的指標int subtraction(int a, int b) { return a - b; } //minus就是函式指標 int (*minus)(int,int)=subtraction; int operation(int x, int y, int (*func)(int, int)) { return (*func)(x,y); }
167、
給你一個下標從 1 開始的整數陣列numbers ,該陣列已按 非遞減順序排列 ,請你從陣列中找出滿足相加之和等於目標數target 的兩個數。如果設這兩個數分別是 numbers[index1] 和 numbers[index2] ,則 1 <= index1 < index2 <= numbers.length 。
以長度為 2 的整數陣列 [index1, index2] 的形式返回這兩個整數的下標 index1 和 index2。
你可以假設每個輸入 只對應唯一的答案 ,而且你 不可以 重複使用相同的元素。
你所設計的解決方案必須只使用常量級的額外空間。
class Solution { public: vector<int> twoSum(vector<int>& numbers, int target) { int index1=0; int index2=numbers.size()-1; while(index1<index2) {if(numbers[index1]+numbers[index2]>target) index2--; if(numbers[index1]+numbers[index2]<target) index1++; if(numbers[index1]+numbers[index2]==target) return {index1+1,index2+1}; } return {-1,-1}; } };
88、
給你兩個按 非遞減順序 排列的整數陣列nums1 和 nums2,另有兩個整數 m 和 n ,分別表示 nums1 和 nums2 中的元素數目。
請你 合併 nums2 到 nums1 中,使合併後的陣列同樣按 非遞減順序 排列。
注意:最終,合併後陣列不應由函式返回,而是儲存在陣列 nums1 中。為了應對這種情況,nums1 的初始長度為 m + n,其中前 m 個元素表示應合併的元素,後 n 個元素為 0 ,應忽略。nums2 的長度為 n 。
class Solution { public: void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) { int p1 = 0, p2 = 0; int sorted[m + n]; int cur; while (p1 < m || p2 < n) { if (p1 == m) { cur = nums2[p2++]; } else if (p2 == n) { cur = nums1[p1++]; } else if (nums1[p1] < nums2[p2]) { cur = nums1[p1++]; } else { cur = nums2[p2++]; } sorted[p1 + p2 - 1] = cur; } for (int i = 0; i != m + n; ++i) { nums1[i] = sorted[i]; } } };
142、
給定一個連結串列的頭節點 head,返回連結串列開始入環的第一個節點。如果連結串列無環,則返回null。
如果連結串列中有某個節點,可以通過連續跟蹤 next 指標再次到達,則連結串列中存在環。 為了表示給定連結串列中的環,評測系統內部使用整數 pos 來表示連結串列尾連線到連結串列中的位置(索引從 0 開始)。如果 pos 是 -1,則在該連結串列中沒有環。注意:pos 不作為引數進行傳遞,僅僅是為了標識連結串列的實際情況。
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: ListNode *detectCycle(ListNode *head) { ListNode* m=head,*k=head; while(k!=NULL&&k->next!=NULL) { k=k->next->next; m=m->next; if(k==m) { k=head; while(k!=m) { k=k->next; m=m->next; } return k; } } return NULL; } };
第一次出錯:對快慢表理解不正確,當第一次相遇時只能確定存在環區,第二次相遇才是才是環的起點
第二次出錯:由於快指標一次走兩個,無環情況快指標可能會訪問到空指標出現錯誤(還有幾次也是這個錯誤,要細心考慮不能太急躁了)
76、
給你一個字串s
、一個字串t
。返回s
中涵蓋t
所有字元的最小子串。如果s
中不存在涵蓋t
所有字元的子串,則返回空字串""
。
class Solution { public: //檢查hs中是否包含了ht的全部字元以及個數 bool check(unordered_map<char, int> &hs, unordered_map<char, int> &ht){ for(unordered_map<char,int>::iterator it=ht.begin();it!=ht.end();it++){ if(hs[it->first]<it->second){ return false; } } return true; } string minWindow(string s, string t) { unordered_map<char, int> hs; //用於儲存字串s的滑動窗口裡元素出現次數 unordered_map<char, int> ht; //儲存字串t中各元素出現次數 int minlen = INT_MAX; //記錄最小長度 int ansL = -1; //子串的左邊起點 for(int i=0;i<t.size();i++) ht[t[i]]++; //遍歷t,並統計各字元出現次數 //滑動視窗,讓k不斷往右移動,找到一個滿足條件的子串 for(int i=0,j=0;i<=j&&j<s.size();){ //如果滑動視窗內元素是空的並且j所指元素不在ht中,可以讓i和j都先右移,直到碰見第一個元素在ht裡面 if(hs.empty()&&ht.find(s[j])==ht.end()){ i++; j++; } //如果j處元素在ht中有,那麼j處元素也需要儲存到hs中 if(ht.find(s[j])!=ht.end()) hs[s[j]]++; //往hs裡面填充有效元素,非必要的不要放進去 //如果此時hs已經包含了ht中全部字元,就需要讓i往右移 while(check(hs,ht)&&i<=j){ //如果當前子串長度更小,則更新 if(j-i+1<minlen){ minlen = j-i+1; ansL = i; //記錄起點 } //如果i處元素在ht中有,將出現hs中對應出現次數-1 if(ht.find(s[i])!=ht.end()) hs[s[i]]--; //元素出現次數減1 i++; //i往右移 } j++; //j繼續右移 } if(ansL==-1) return ""; else{ return s.substr(ansL, minlen); } } };
可以想到利用滑動視窗確定範圍,但不知道怎麼確定範圍中的字元是否存在。
此程式碼和以下解析來源於作者:traveller-lzx。
利用滑動視窗的思想,不斷更新視窗的左邊界和右邊界;
利用雜湊表ht儲存t中所有的元素和對應出現次數,雜湊表hs儲存滑動視窗中出現的有效元素(就是該元素在t/ht中出現過的元素)及對應出現次數;
讓視窗的右邊界j不斷右移,直到視窗內的有效元素hs集合中元素出現次數>=ht中相應元素出現次數,此時讓視窗左邊界i開始向右縮小,並逐個剔除元素,直到視窗不能再縮小為止,儲存此時視窗內有效子串的長度為minlen,並記錄該子串的起點為ansL;
迴圈迭代上述過程,最後利用substr()函式,返回子串即可;
優化思路:
此程式碼基本按照官方的題解思路進行編寫,有以下幾個細節需要注意:
剛開始當還沒有建立滑動視窗時,可能j指標會指向很多無效元素,例如"s=xxxABCxx,t=ABC",我們可以讓i和j一開始先跳過前面的xxx,具體看程式碼實現;
雜湊表hs需要存放有效元素(就是該元素在t/ht中出現過的元素),而不是每遍歷一個元素,都要存進去,這樣會浪費記憶體,最後不一定能通過;
check()函式裡傳遞引數用引用傳遞的方式,如果是拷貝構造,可能會因為資料量過大,最後一個測試用例過不了;
更新res子串並不需要在調整視窗左邊界的過程中進行,而是用子串長度minlen和子串起始位置ansL來記錄即可,更新記錄,最後再用substr()返回;
程式碼
作者:traveller-lzx
連結:https://leetcode-cn.com/problems/minimum-window-substring/solution/zui-xiao-fu-gai-zi-chuan-hua-dong-chuang-fado/
來源:力扣(LeetCode)