C++版的leetcode,從頭再來!java
下面這個代碼在return的時候一直遇到問題:node
class Solution { public: vector<int> twoSum(vector<int>& nums, int target) { int length = sizeof(nums)/sizeof(nums[0]); for(int i = 0;i < length;++i) for(int j = 0; j<length;++i) if (nums[i] + nums[j] == target) { return {nums[i],nums[j]}; } } };
後來改正的:python
class Solution { public: vector<int> twoSum(vector<int>& nums, int target) { int length = nums.size(); vector<int> ans; for(int i = 0;i < length;i++) for(int j = i + 1; j<length;j++) if (nums[i] + nums[j] == target) { ans.push_back(i); ans.push_back(j); return ans; } return ans; } };
幾點說明:
1.一個函數是必須有返回值的,若是隻有if里加了return,當出現else的 狀況這個函數就沒有了,因此要加else,要麼最後加return也行;
2.返回一個列表時,能夠定義一個容器來裝
3.計算數組長度用size()函數;
用暴力法就沒有意義了,測試用例必定後面的過不了
一開始想對數組進行排序,這樣能夠極大程度的進行剪枝,可是排序以後丟失了原有的下標,若是返回的是元素不是下標的話這樣就是可行的,可是下標不能用這種方法
哈希表的方法:ios
class Solution { public: vector<int> twoSum(vector<int>& nums, int target) { unordered_map<int,int> m; for(int i=0;i<nums.size();i++) m[nums[i]] = i; //向map中添加元素 for(int i=0;i<nums.size();i++) { if(m.find(target-nums[i]) != m.end() && m[target-nums[i]] != i) //若是m中存在對應的鍵值,而且不爲i,後面這個條件很重要,由於會有重複的元素 return {i , m[target-nums[i]]}; } return {}; } }; 做者:zrita 連接:https://leetcode-cn.com/problems/two-sum/solution/c-san-chong-fang-fa-jian-dan-yi-dong-ji-bai-100-z-/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
能夠說有思路,先補齊不齊的,而後再添加到新的鏈表中,最後考慮進位,可是代碼過長既浪費時間又容易出錯git
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) { ListNode* temp1 = l1; int len1 = 0; while(temp1) { temp1 = temp1->next; len1++; } ListNode* temp2 = l2; int len2 = 0; while(temp2) { temp2 = temp2->next; len2++; } temp1 =l1; while(temp1->next) { temp1 = temp1->next; } temp2=l2; while(temp2->next) { temp2=temp2->next; } while(len1 <len2) { ListNode* zero = new ListNode(0); temp1->next=zero; temp1 = zero; len1++; } while(len2<len1) { ListNode* zero = new ListNode(0); temp2->next = zero; temp2 = zero ; len2++; } ListNode* head = new ListNode(l1->val+l2->val); ListNode* temp = head; temp1=l1; temp1 = temp1->next; temp2=l2; temp2 = temp2->next; while(len1-1) { ListNode* cur = new ListNode(temp1->val+temp2->val); temp1=temp1->next; temp2=temp2->next; temp->next = cur; temp = cur; len1--; } temp = head; while(temp->next) { if(temp->val>=10) { temp->val -=10; temp->next->val +=1; } temp =temp->next; } if(temp->val>=10) { ListNode* zero = new ListNode(1); temp->val -=10; temp->next = zero; } return head; } };
相同的思路更簡潔的代碼
裏面值得學習的地方是新建一個頭節點,可是不許備放在終鏈表中,這樣不用再把中間過渡的一些代碼寫出來
還有就是新建節點的時候直接new就能夠的web
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) { int len1=1;//記錄l1的長度 int len2=1;//記錄l2的長度 ListNode* p=l1; ListNode* q=l2; while(p->next!=NULL)//獲取l1的長度 { len1++; p=p->next; } while(q->next!=NULL)//獲取l2的長度 { len2++; q=q->next; } if(len1>len2)//l1較長,在l2末尾補零 { for(int i=1;i<=len1-len2;i++) { q->next=new ListNode(0); q=q->next; } } else//l2較長,在l1末尾補零 { for(int i=1;i<=len2-len1;i++) { p->next=new ListNode(0); p=p->next; } } p=l1; q=l2; bool count=false;//記錄進位 ListNode* l3=new ListNode(-1);//存放結果的鏈表 ListNode* w=l3;//l3的移動指針 int i=0;//記錄相加結果 while(p!=NULL&&q!=NULL) { i=count+p->val+q->val; w->next=new ListNode(i%10); count=i>=10?true:false; w=w->next; p=p->next; q=q->next; } if(count)//若最後還有進位 { w->next=new ListNode(1); w=w->next; } return l3->next; } }; 做者:chenlele 連接:https://leetcode-cn.com/problems/add-two-numbers/solution/liang-shu-xiang-jia-by-gpe3dbjds1/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
用了帶剪枝的暴力法,每次維護一個set,可是最後仍是超時了算法
class Solution { public: int lengthOfLongestSubstring(string s) { //遍歷確定不行,子串表示要連續 //嘗試雙指針遍歷加剪枝 if(s.empty())return 0; if(s.size()==1)return 1; int len = s.size(); int max = 1; for(int i= 0 ; i<len ; i++) { set<int> st; st.insert(s[i]); for(int j = i+1; j<len ;j++) { int k = st.size(); st.insert(s[j]); if(st.size()==k) break; if(st.size()>max)max=st.size(); } } return max; } };
對於字符串的題,最容易出現的就是超時,而這是更好的辦法每每是哈希表法docker
class Solution { public: int lengthOfLongestSubstring(string s) { //s[start,end) 前面包含 後面不包含 int start(0), end(0), length(0), result(0); int sSize = int(s.size()); unordered_map<char, int> hash; while (end < sSize) { char tmpChar = s[end]; //僅當s[start,end) 中存在s[end]時更新start if (hash.find(tmpChar) != hash.end() && hash[tmpChar] >= start) { start = hash[tmpChar] + 1; length = end - start; } hash[tmpChar] = end; end++; length++; result = max(result, length); } return result; } }; 做者:pinku-2 連接:https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/solution/wu-zhong-fu-zi-fu-de-zui-chang-zi-chuan-cshi-xian-/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
雙指針法可使用滑動窗口法,每次要找到是哪裏多出來了,再從這個地方開始數組
class Solution { public: // 在左閉右開[left,right)的字符串s中,找字符target對應的下標位置,若未找到,則返回-1 int getThePosOfSame(int left, int right, const string& s, char target) const { for (int i = left; i < right; i++) { if (s[i] == target) { return i; } } return -1; } int lengthOfLongestSubstring(string s) { const int len = s.length(); int max_len = 0; int i = 0; int j = 0; while (j < len) { int pos = getThePosOfSame(i, j, s, s[j]); if (pos == -1) { j++; } else { max_len = (j - i) > max_len ? (j - i) : max_len; i = pos + 1; } } max_len = (j - i) > max_len ? (j - i) : max_len; return max_len; } }; 做者:hui-mao-zi-2 連接:https://leetcode-cn.com/problems/longest-substring-without-repeating-characters/solution/hua-dong-chuang-kou-by-hui-mao-zi-2/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
本身的寫的滑動窗口,要特別注意對數組的維護和每次更替時i和j的值緩存
class Solution { public: int lengthOfLongestSubstring(string s) { //遍歷確定不行,子串表示要連續 //嘗試雙指針遍歷加剪枝 if(s.empty())return 0; if(s.size()==1)return 1; int len = s.size(); int max = 1; int i = 0; int j = 1; vector<char> temp; temp.push_back(s[0]); while(i<len && j<len) { if( find(temp.begin(),temp.end(),s[j])==temp.end() ) { temp.push_back(s[j]); j++; if(temp.size()>max)max =temp.size(); } else { int k = find(temp.begin(),temp.end(),s[j])-temp.begin(); i = i+k+1; j=i+1; temp.clear(); temp.push_back(s[i]); } } return max; } };
拿到題目以後的思路:
1.每行須要創建一個容器存放值
2.可是因爲行數是題目給定的,並非常量,因此只能先創建一個二維矩陣,行數就是題目的行數
3.設置在到達邊界是當即返回,這個地方還想的不是很清楚
4.可想而知會很是複雜
官方解答:
class Solution { public: string convert(string s, int numRows) { if (numRows == 1) return s; vector<string> rows(min(numRows, int(s.size()))); int curRow = 0; bool goingDown = false; for (char c : s) { rows[curRow] += c; if (curRow == 0 || curRow == numRows - 1) goingDown = !goingDown; curRow += goingDown ? 1 : -1; } string ret; for (string row : rows) ret += row; return ret; } }; 做者:LeetCode 連接:https://leetcode-cn.com/problems/zigzag-conversion/solution/z-zi-xing-bian-huan-by-leetcode/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
首先因爲是字符串型,沒必要設置char型的二維矩陣而是直接設置一維而後進行拼接就好了,其次,對於到終點就返回的一個要求,就簡單定義一個變量,從上到下時每次變量加1,從下到上每次變量鍵1,能夠設置一個bool開關,做爲加一和減一的標誌,以前想的堆棧什麼都太複雜了,雖然容器好用,可是能用一維的就不用二維,能用一兩個指針或者變量的就不用堆棧
class Solution { public: int reverse(int x) { int p = 10; int n; long result = 0; if(x == INT_MIN) { return 0; } if (x >= 0) { while(x > 0) { n = x % p; x = x / p; result = result*10 + n; } if(result > INT_MAX) { return 0; } return result; } else { int y = -x; while(y > 0) { n = y % p; y = y / p; result = result*10 + n; } if(result > INT_MAX) { return 0; } return -result; } } };
幾點說明:
1.INT_MIN ,INT_MAX第一次見
INT_MAX = 2^31-1
INT_MIN= -2^31
另外爲了防止result直接溢出了先試用long類型(8字節)進行定義!
2.反轉後不用放到容器裏而是分離的時候能夠直接構成反轉數;
3.遇到正反兩種狀況時不須要分兩大類,而是在開頭轉換就能夠,使用一次遞歸,以下所示
class Solution { public: int reverse(int x) { long result(0); //利用long避免溢出 if (x == INT_MIN) { return 0; } if (x < 0) { return -reverse(-x);//當x小於0,再次調用該函數解出x大於0的狀況,結果加負號返回 } int digit(0); while (x > 0) { digit = x % 10; x /= 10; result = result * 10 + digit;//這裏能夠直接返回 } if (result > INT_MAX) { return 0; } return int(result); } }; 做者:pinku-2 連接:https://leetcode-cn.com/problems/reverse-integer/solution/zheng-shu-fan-zhuan-cshi-xian-liang-chong-jie-fa-z/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
class Solution { public: int reverse(int x) { int rev = 0; while (x != 0) { int pop = x % 10; x /= 10; if (rev > INT_MAX/10 || (rev == INT_MAX / 10 && pop > 7)) return 0; if (rev < INT_MIN/10 || (rev == INT_MIN / 10 && pop < -8)) return 0; rev = rev * 10 + pop; } return rev; } }; 做者:LeetCode 連接:https://leetcode-cn.com/problems/reverse-integer/solution/zheng-shu-fan-zhuan-by-leetcode/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
這裏使用了整數反轉的思路,直接比對反轉後是否相等就知道是不是迴文數;
另外,爲了防止超範圍,result可使用類型long;
另外,數字和字符串轉換不如算法方式好;
class Solution { public: bool isPalindrome(int x) { if(x < 0){return false;} else { long result = 0; int y = x; int n = 0; while(x > 0) { n = x % 10; x = x / 10; result = result*10 + n; } if (result == y){return true;} else{return false;} } } };
看到一種新寫法
return (x==n) ? true : false;
可代替原來的代碼爲
return (result == y) ? true: false;
這則代碼在map的使用上花費了較多時間,s是string字符串,s[0]能夠去除字符串的第一個元素,可是必定不能使用luoma[「s[0]」]去提取值,由於s[0]這裏不會進行編譯的!因此要提取的話要找自己就是string類型的,而s.substr(n,m)能夠作到這一點,n表明起始位置,m表明提取的個數,將其放入luoma[]中能夠提取到想要的value。
#include <map> #include <iostream> using namespace std; class Solution { public: int romanToInt(string s) { int length = s.size(); std::map<string,int> luoma; luoma.insert(pair<string,int>("I",1)); luoma.insert(pair<string,int>("V",5)); luoma.insert(pair<string,int>("X",10)); luoma.insert(pair<string,int>("L",50)); luoma.insert(pair<string,int>("C",100)); luoma.insert(pair<string,int>("D",500)); luoma.insert(pair<string,int>("M",1000)); long result = 0; if (length >1) { for (int i = 0;i < length-1; i++ ) { if(luoma[s.substr(i,1)] >= luoma[s.substr(i+1,1)]) { result = result + luoma[s.substr(i,1)]; } else { result = result - luoma[s.substr(i,1)]; } } result = result + luoma[s.substr(length-1,1)]; } else{ result = luoma[s.substr(0)]; } return result; } };
官方的
class Solution { public: int romanToInt(string s) { unordered_map<string, int> m = {{"I", 1}, {"IV", 3}, {"IX", 8}, {"V", 5}, {"X", 10}, {"XL", 30}, {"XC", 80}, {"L", 50}, {"C", 100}, {"CD", 300}, {"CM", 800}, {"D", 500}, {"M", 1000}}; int r = m[s.substr(0, 1)]; for(int i=1; i<s.size(); ++i){ string two = s.substr(i-1, 2); string one = s.substr(i, 1); r += m[two] ? m[two] : m[one]; } return r; } }; 做者:QQqun902025048 連接:https://leetcode-cn.com/problems/roman-to-integer/solution/2-xing-python-on-by-knifezhu/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
map:
優勢:
有序性,這是map結構最大的優勢,其元素的有序性在不少應用中都會簡化不少的操做
紅黑樹,內部實現一個紅黑書使得map的不少操做在lgn的時間複雜度下就能夠實現,所以效率很是的高
缺點: 空間佔用率高,由於map內部實現了紅黑樹,雖然提升了運行效率,可是由於每個節點都須要額外保存父節點、孩子節點和紅/黑性質,使得每個節點都佔用大量的空間
適用處:對於那些有順序要求的問題,用map會更高效一些
unordered_map:
優勢: 由於內部實現了哈希表,所以其查找速度很是的快
缺點: 哈希表的創建比較耗費時間
適用處:對於查找問題,unordered_map會更加高效一些,所以遇到查找問題,常會考慮一下用unordered_map
整體來講思路難度不大,
1.要注意函數可能沒有返回值的狀況
2.要求一個公共最小長度,不必定非要建出容器而後求最小值,能夠直接在求每一個值的時候順帶求出最小值
3.substr函數仍是很好用的,主要是還能指定輸出的個數
class Solution { public: string longestCommonPrefix(vector<string>& strs) { int a = strs.size(); if (a == 0) { return ""; } else if (a == 1) { return strs[0]; } else { int aamin = strs[0].size(); for(int i = 1; i <a ; i++) { if(aamin > strs[i].size()){aamin = strs[i].size();} } for (int i = 0; i < aamin; i++ ) { for (int j = 0; j< a; j++) { if (strs[0].substr(i,1) != strs[j].substr(i,1)) { return strs[0].substr(0,i); } } } return strs[0].substr(0,aamin); } } };
基本同樣,不過其並無使用substr函數
class Solution { public: string longestCommonPrefix(vector<string>& strs) { if(strs.size()==0) return ""; string str=""; int min_lenth=strs[0].size(); for(int i=1;i<strs.size();i++) { if(min_lenth>=strs[i].size()) min_lenth=strs[i].size(); } for(int k=0;k<min_lenth;k++) { char s=strs[0][k]; for(int j=1;j<strs.size();j++) { if(s!=strs[j][k]) return str; } str+=s; } return str; } };
直接暴力法,必定是不可取的
class Solution { public: vector<vector<int>> threeSum(vector<int>& nums) { //沒有好的辦法,雙指針不行,矩陣不行,貪心不行 //二維數組 int len = nums.size(); //vector<vector<int> > myvec(len, vector<int>(nums,0)); set<multiset<int> > res; for (int i = 0; i<len; i++) { for(int j= i+1; j<len ; j++) { for (int c=j+1 ;c<len; c++) { //if (c==i || c==j){continue;} if ((nums[i]+nums[j]+nums[c])==0) { multiset<int> x; x.insert(nums[i]); x.insert(nums[j]); x.insert(nums[c]); res.insert(x); } } } } vector<vector<int> > re; for(auto i:res) { vector<int> x; for(auto j:i) { x.push_back(j); } re.push_back(x); } //vector<vector<int> > re = res; return re; } };
再暴力法上加了剪枝,可是仍是超時了,說明這種方法是不行的
class Solution { public: vector<vector<int>> threeSum(vector<int>& nums) { int len = nums.size(); if(len<3)return {}; sort(nums.begin(),nums.end()); int i = 0 ; int j = 1 ; int l = 2; set<vector<int>> res; for(int i = 0 ; i<len ;i++) { if(nums[i]>0) break;//直接中止循環 if(nums[i]+nums[len-2]+nums[len-1]<0)continue;//直接跳過這次循環 for(int j = i+1;j<len ;j++) { if(nums[i]+nums[j]+nums[len-1]<0)continue;//跳過這次循環 for(int k = j+1 ; k<len ;k++) { if(nums[i]+nums[j]+nums[k]==0) { vector<int> temp = {nums[i],nums[j],nums[k]}; res.insert(temp); //爲了防止又相同值這裏不能中止 } if(nums[i]+nums[j]+nums[k]>0) break;//後面的k也都不行了 } } } vector<vector<int>> r(res.begin(),res.end()); return r; } };
正確的作法,雙指針法,一個在前一個在後,尋找0-nums[i]的兩個值,特別適用於已經排序的數組,要記住排序以後數組常用 一前一後的雙指針法
class Solution { public: vector<vector<int>> threeSum(vector<int> &nums) { vector<vector<int>> result; int vecSize = int(nums.size()); if (vecSize <= 2) { return result; } int possibleSize = vecSize - 2; sort(nums.begin(), nums.end()); for (int index = 0; index < possibleSize; index++) { int intNow = nums[index]; if(intNow > 0){ break; } int negativeNow = 0 - intNow; int lo = index + 1; int hi = vecSize - 1; while (lo < hi) { int intLo = nums[lo]; int intHi = nums[hi]; if (intLo + intHi == negativeNow) { vector<int> tmpVec{intNow, intLo, intHi}; result.push_back(tmpVec); //去重 while (lo < hi && nums[lo] == intLo) { lo++; } while (lo < hi && nums[hi] == intHi) { hi--; } } else if (intLo + intHi < negativeNow) { lo++; } else if (intLo + intHi > negativeNow) { hi--; } } //去重 while (index + 1 < possibleSize && nums[index] == nums[index + 1]) { index++; } } return result; } }; 做者:pinku-2 連接:https://leetcode-cn.com/problems/3sum/solution/san-shu-zhi-he-cshi-xian-shuang-zhi-zhen-fa-tu-shi/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
本身寫的,勉強不超時,考慮多是去重那裏費了比較多功夫
class Solution { public: vector<vector<int>> threeSum(vector<int>& nums) { int len = nums.size(); int k=0, i=1,j = len-1; sort(nums.begin(),nums.end()); vector<vector<int>> res; while(k<=len-3) { if(nums[k]+nums[k+1]+nums[k+2]>0)break; if(nums[k]+nums[k+1]+nums[k+2]==0) { vector<int> temp={nums[k],nums[k+1],nums[k+2]}; res.push_back(temp);break;}; //if(nums[k]+nums[i]+nums[j]>0){k++;i=k+1;j=len-1;continue;} if(nums[k]+nums[j-1]+nums[j]<0){k++;i=k+1;j=len-1;continue;} while(i<j) { if(nums[i]+nums[j]==-nums[k]) { vector<int> temp={nums[k],nums[i],nums[j]}; res.push_back(temp); i++;j--; } else if(nums[i]+nums[j]>-nums[k]) { j--; } else { i++; } } k++; i=k+1; j=len-1; } set<vector<int>> r(res.begin(),res.end()); res.assign(r.begin(),r.end()); return res; } };
class Solution { public: int threeSumClosest(vector<int>& nums, int target) { int len = nums.size(); int k=0,i=1,j=len-1; sort(nums.begin(),nums.end()); int res = 1e6; if(nums[0]+nums[1]+nums[2]>target) return nums[0]+nums[1]+nums[2]; if(nums[len-3]+nums[len-2]+nums[len-1]<target) return nums[len-3]+nums[len-2]+nums[len-1]; while(k<=len-3) { if(nums[k]+nums[k+1]+nums[k+2]>target) return abs(res-target)<abs(nums[k]+nums[k+1]+nums[k+2]-target)?res:nums[k]+nums[k+1]+nums[k+2]; while(i<j) { if(nums[k]+nums[i]+nums[j]==target)return target; else if(nums[k]+nums[i]+nums[j]>target) { res = abs(res-target)<abs(nums[k]+nums[i]+nums[j]-target)?res:nums[k]+nums[i]+nums[j];j--; } else { res =abs(res-target)<abs(nums[k]+nums[i]+nums[j]-target)?res:nums[k]+nums[i]+nums[j];i++; } } k++; i=k+1; j=len-1; } return res; } };
怎麼排列出全部的狀況,時最難的地方
string str; str = str + 'm' + "m" + to_string(m)+char(m) + char(m+'0') ;
字符串拼接,加號也行,單雙引號都能拼,整型用to_string
這題能使用遞歸,可是很差理解,這個隊列的思想很是實用,很是像二叉樹層序遍歷的過程!先遍歷第一曾(單個的),每個生成一個組合再放到隊列末尾,而這個彈出,像極了把子節點放入隊列後面,而後將此節點彈出的過程
隊列是有效的一種遍歷手段
class Solution { public: vector<string> letterCombinations(string digits) { vector<string> res;//用於輸出向量 map<char, string> m = { {'2',"abc" },{'3',"def"},{'4',"ghi"},{'5',"jkl"},{'6',"mno"},{'7',"pqrs"},{'8',"tuv"},{'9',"wxyz"} };//映射map哈希表 int size = digits.size();//輸入字符串產長度 queue<string> que;//新建隊列 //先將第一個元素對應的碼錶入隊 for (int j = 0; j < m[digits[0]].size(); j++) { string str; str.push_back(m[digits[0]][j]);//char轉string que.push(str);//string入隊,分別將'a','b','c'入隊列 } string s;//用於存儲隊頭元素 for (int i = 1; i < size; i++)//對剩下的第二個開始進行遍歷,每一都會組成新的字符串放入隊列,而後將此彈出 { int length = que.size();//當前隊列長度,這時的隊列存放的是上一層全部的字符串,以前的都彈掉了,對於裏面的每一個,都須要新的組合進入隊尾,再彈出這個 while (length--)// { for (int j = 0; j < m[digits[i]].size(); j++) { s = que.front(); s = s + m[digits[i]][j];//隊頭元素加上新元素 que.push(s);//入隊 } que.pop();//隊頭出隊 } } while (!que.empty()) { res.push_back(que.front());//隊頭元素存儲至res que.pop();//隊頭出隊 } return res;//返回 } }; 做者:su-ge 連接:https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number/solution/c-dui-lie-jian-dan-shi-xian-yi-dong-by-su-ge/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
套用遞歸模板能夠寫出遞歸
class Solution { private: vector<string> res; string path; int len; public: void dfs(unordered_map<char,string> mymap, int pos, string digits) { if(pos == len) { res.push_back(path); } for(auto i:mymap[digits[pos]]) { path = path+ i; dfs(mymap,pos+1,digits); path.pop_back(); } } vector<string> letterCombinations(string digits) { if(digits.empty())return res; len = digits.size(); unordered_map<char,string> mymap{{'2',"abc"},{'3',"def"},{'4',"ghi"},{'5',"jkl"},{'6',"mno"},{'7',"pqrs"},{'8',"tuv"},{'9',"wxyz"}}; dfs(mymap,0,digits); return res; } };
借鑑以前三數之和的雙指針法,能作可是計算量大
class Solution { public: vector<vector<int>> fourSum(vector<int>& nums, int target) { sort(nums.begin(),nums.end()); int len = nums.size(); int i = 0, j = 1, k1= 2,k2= len-1; set<vector<int> > res; while(i< len-3 ) { while(j<len-2) { while( k1<k2) { int temp = nums[i]+nums[j]+nums[k1]+nums[k2]; if (temp == target) { vector<int> vec ={nums[i], nums[j], nums[k1],nums[k2]}; res.insert(vec); k1++; } else if (temp > target){k2--;} else {k1++;} } j++; k1 = j+1; k2 = len-1; } i++; j=i+1; k1=j+1; k2=len-1; } vector<vector<int> > re; for (auto i :res) { re.push_back(i); } return re; } };
這樣的方法可行,在這個方法上能夠有更優化的辦法,以前有特殊狀況0,因此即判斷第一個指針值的大小和0的關係就好了,可是當target不爲0時不能這樣簡單的判斷,由於0如下的值越加越小,0以上的值越加越大,因此能夠換個稍微複雜的驗證方式,假設頭節點是p,若是p和右邊的三個的和都大於target,就不必了,能夠中止循環,由於還會大於target,若是p和最後三位的加和還小於target的話,那麼就說明這個p不可能組成,直接計算p+1的狀況
指針依次是 p,k,i,j,若是 nums[p] + 3 * nums[p + 1] > target,由於 nums 按升序排列,因此以後的數確定都大於 target ,直接 break; 若是 nums[p] + 3 * nums[-1] < target,那麼當前的 nums[p] 加其他三個數必定小於 target,故 p 直接下一位便可,continue; 做者:z1m 連接:https://leetcode-cn.com/problems/4sum/solution/shuang-zhi-zhen-gao-su-jie-fa-by-ml-zimingmeng/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
第二次寫的加入剪枝的操做
class Solution { public: vector<vector<int>> fourSum(vector<int>& nums, int target) { int len = nums.size(); int k=0,l=1,i=2,j=len-1; sort(nums.begin(),nums.end()); set<vector<int>> res; while(k<=len-4) { if(nums[k]+nums[k+1]+nums[k+2]+nums[k+3]>target)break; if(nums[k]+nums[j-2]+nums[j-1]+nums[j]<target){k++;l=k+1;i=k+2;j=len-1;continue;} while(l<=len-3) { if(nums[k]+nums[l]+nums[l+1]+nums[l+2]>target)break; if(nums[k]+nums[l]+nums[j-1]+nums[j]<target){l++;i=l+1;j=len-1;continue;} while(i<j) { if(nums[k]+nums[l]+nums[i]+nums[j]==target) { vector<int> temp={nums[k],nums[l],nums[i],nums[j]}; res.insert(temp);i++;j--; } else if(nums[k]+nums[l]+nums[i]+nums[j]>target) { j--; } else { i++; } } l++;i=l+1;j=len-1; } k++; l=k+1; i=l+1; j=len-1; } vector<vector<int>> r(res.begin(),res.end()); return r; } };
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: ListNode* removeNthFromEnd(ListNode* head, int n) { int len=0; ListNode* temp = head; while(temp!=nullptr) { len++; temp = temp->next; } if(len==1){return NULL;} else if (len==n){head = head->next;} else { temp =head; for(int i= 0;i<len-n-1;i++) { temp=temp->next; } temp ->next = temp ->next->next; } return head; } };
思想是找到該節點的前一個節點,可是要注意特殊狀況,即刪除的是第一個節點的狀況,這種狀況下會沒有前一個節點
雙指針,當快的前進n時,慢的開始;當快的到結尾時,慢的正好到能夠刪除的那個節點前一個,對於一維方向上的問題,雙指針能夠解決絕大數
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: ListNode* removeNthFromEnd(ListNode* head, int n) { ListNode* dummy = new ListNode(NULL); dummy->next = head; //添加頭節點,便於操做 ListNode* slow=dummy,* fast=dummy; int distance=0; while(fast->next){ if(distance<n){ fast=fast->next; distance++; }else{ fast=fast->next; slow=slow->next; } } slow->next=slow->next->next; return dummy->next; } }; 做者:zgdgod 連接:https://leetcode-cn.com/problems/remove-nth-node-from-end-of-list/solution/c-by-zgdgod-4/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
有效運用了break和continue:一個是當即退出當前循環,一個是結束本次循環;
class Solution { public: bool isValid(string s) { int length = s.size(); if (length % 2 == 1) { return false; } else { map <string ,int> fuhao = {{"(",1}, {")",-1}, {"[",2}, {"]",-2}, {"{",3}, {"}",-3}}; while((s.size()) > 0) { length = s.size(); for(int i = 0; i < s.size()-1; i++) { if ( fuhao[s.substr(i,1)] == - fuhao[s.substr(i+1,1)]) { s.erase(i,2); break; } } if(length == 0) { return true; } else if (length == s.size()) { return false; } else { continue; } } return (s.size() == 0) ? (true) : (false); } } };
思路:對於字符串s
若下一個字符是左括號:則任務數量task+1,且將須要匹配的右括號數字添加至key容器;
若下一個字符是右括號:
一、如果key容器中最後一個數字對應的右括號,則任務數量task-1,移除容器最後一項;
二、若不是key容器中最後一個數字對應的右括號,則直接結束,返回False。
知識點:
1.for(auto i : s)遍歷s中的全部字符、元素,甚至可使用自動類型推斷,一個冒號就解決了,實用性很大!
2.vector.empty()與
if (vector.size() == 0 ){return true}
else {return false} 的效果更簡潔,若是要反着來能夠在前面加一個!
3.對於s中的每個符號,若是是左半邊,就把這個推動容器;若是是右半邊,此時若容器爲空則說明以前沒有左半邊入棧,則false,若是容器不爲空繼續,把容器最後一個左半括號提取出來,若是此右半邊和這個括號不匹配,則false,若是匹配則彈出此左半括號,繼續下去,所有走完後,直接用空就表明是否所有匹配,一語雙關!
class Solution { public: bool isValid(string s) { if(s.size() % 2) return false; vector<char> vecStack;//字符型的容器 char c; //對於s中的每個符號,若是是左半邊,就把這個推動容器;若是是右半邊,此時若容器爲空則說明以前沒有左半邊入棧,則false,若是容器不爲空繼續,把容器最後一個左半括號提取出來,若是此右半邊和這個括號不匹配,則false,若是匹配則彈出此左半括號,繼續下去,所有走完後,直接用空就表明是否所有匹配,一語雙關! for(auto i : s) { if(i == '}' || i == ')' || i== ']') { if (!vecStack.empty()) c = vecStack[vecStack.size()-1]; else return false; if(i == '}' && c != '{') return false; if(i == ')' && c != '(') return false; if(i == ']' && c != '[') return false; vecStack.pop_back(); } else vecStack.push_back(i); } return vecStack.empty(); } }; 做者:wallcwr 連接:https://leetcode-cn.com/problems/valid-parentheses/solution/jing-dian-jian-dan-de-zhan-wen-ti-by-wallcwr/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
本身又寫了一遍,發現一個問題:必須是單引號才行,雙引號就不行——單引號字符,雙引號字符串
class Solution { public: bool isValid(string s) { vector <char> kuohao; if (s.size() % 2 == 1) return false; for (auto str : s) { if ((str == '{') || (str == '[') || (str == '(')) { kuohao.push_back(str); } else { if(kuohao.size() == 0) return false; if (str == '}' && kuohao[kuohao.size()-1] == '{') { kuohao.pop_back(); continue; } else if (str == ']' && kuohao[kuohao.size()-1] == '[') { kuohao.pop_back(); continue; } else if (str == ')' && kuohao[kuohao.size()-1] == '(' ) { kuohao.pop_back(); continue; } else { return false; } } } return kuohao.empty(); } };
在C++中單引號表示字符,雙引號表示字符串。
例如 :在定義一個數組的時候string a [5]={「nihao」,「henhao」,「good」,「en」,「h」};
定義的是一個字符串數組,這是字符串元素要用雙引號。
char b[5]={‘a’,‘b’,‘c’,‘d’,‘e’};
定義的是一個字符數組,元素要用單引號。
要注意元素的輸出不一樣:
int a=10;
cout<<「a」;輸出爲 字符a;這就是上次「s[0]」中不能再編譯s[0]的緣由,可是若是是單引號沒準能編譯
cout<<a;輸出爲10;
cout<<‘a’ ;輸出爲65;
思路正確,但就是代碼有些複雜
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) { ListNode* head = new ListNode(0); ListNode* res = head; ListNode* temp1 = l1; ListNode* temp2 = l2; while(temp1!= nullptr && temp2!= nullptr) { while (temp1 != nullptr && temp1->val <= temp2->val) { ListNode* temp = new ListNode(temp1->val); res->next = temp; res = temp; temp1 = temp1->next; } while (temp1 != nullptr&&temp2 != nullptr && temp1->val > temp2->val) { ListNode* temp = new ListNode(temp2->val); res->next = temp; res = temp; temp2 = temp2->next; } } while (temp1!=nullptr) { ListNode* temp = new ListNode(temp1->val); res->next = temp; res= temp; temp1=temp1->next; } while(temp2!=nullptr) { ListNode* temp = new ListNode(temp2->val); res->next =temp; res= temp; temp2=temp2->next; } return head->next; } };
#include <vector> using namespace std; /** * Definition for singly-linked list. */ struct ListNode//鏈表的結構體 { int val; ListNode *next; ListNode(int x) : val(x), next(NULL) {} }; class Solution { public: ListNode *mergeTwoLists(ListNode *l1, ListNode *l2) { ListNode *result = new ListNode(-1); //啞節點簡化代碼 ListNode *workNode = result; while (l1 != nullptr && l2 != nullptr) //直到有一個爲空指針爲止 { if (l1->val <= l2->val) //若是1的值小於2的值,就考察1的下一個值 { workNode->next = l1; l1 = l1->next; } else//考察2的下一個值 { workNode->next = l2; l2 = l2->next; } workNode = workNode->next; } workNode->next = (l1 != nullptr) ? l1 : l2;//誰不爲空就把誰剩下的加進去 return result->next; } }; 連接:https://leetcode-cn.com/problems/merge-two-sorted-lists/solution/he-bing-liang-ge-you-xu-
借鑑了以前電話號碼題的思路,經過一個隊列作到了層序遍歷,可是因爲產生的括號的有效性不能保證,因此又寫了一個判斷有效性的函數,過程十分複雜
class Solution { public: bool isright(string s) { stack<char> st; for (auto i : s) { if (i == '(') { st.push('('); } else { if (!st.empty() && st.top() == '(') { st.pop(); } else { return false; } } } if (st.empty()) { return true; } else { return false; } } vector<string> generateParenthesis(int n) { if (n == 0) { vector<string> s = { "" }; return s; } else { string s = "("; queue<string> res; res.push(s); for (int i = 0; i < 2 * (n-1); i++) { int j = res.size(); while (j) { string s1 = res.front() + "("; res.push(s1); string s2 = res.front() + ")"; res.push(s2); res.pop(); j--; } } vector<string> re; while (!res.empty()) { string s3 = res.front() + ")"; if (isright(s3)) { re.push_back(s3); res.pop(); } else { res.pop(); } } return re; } } };
有一種能大大提升效率的方法(多用於樹形問題)是剪枝算法,及時判斷是否可行,不可行就捨去這一樹枝
遞歸作法(代碼簡單可是情景必需要知足遞歸條件):
class Solution { public: vector<string> generateParenthesis(int n) { if (n == 0) return{ "" }; if (n == 1) return{ "()" }; vector<string> ans; generateParenthesisIter("(", n-1, n, ans); return ans; } void generateParenthesisIter(string valid, int left_num, int right_num, vector<string>& ans){ if (left_num == 0){ //遞歸終止條件 valid.append(right_num, ')'); ans.push_back(valid); return; } for (int n = left_num; n >= 0; n--){ //本層使用的左括號數量 string str = valid; if (left_num - n > right_num - 1)//剪枝: 無效的分支 return; //剩餘括號中左括號數量大於右括號數量,說明已用的右括號數量大於左括號數量 str.append(n, '('); str.append(1, ')'); generateParenthesisIter(str, left_num - n, right_num - 1, ans); } return; } }; 做者:lcl-17 連接:https://leetcode-cn.com/problems/generate-parentheses/solution/c-shen-du-you-xian-bian-li-0ms-by-lcl-17/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
找到全部括號的組合方式看起來比較複雜。
因此咱們能夠嘗試換個思路:若是「(」對應數字1,「)」對應數字-1呢?
經過觀察咱們能夠發現這樣一個規律:
凡有效的括號組合,轉化成數字,任意前n項和都不小於0!
好比:「()()()」
前1位:1>=0;前2位:1+(-1)=0>=0;前3位:1+(-1)+1=1>=0;
…以此類推,前n位數字和均大於等於0.
又好比:「((()))」
前3位:1+1+1=3>=0;前4位:1+1+1+(-1)=2>=0;前5位:1+1+1+(-1)+(-1)=1>=0;
…依然知足規律。
至此,咱們就能想到這樣一個思路:
1.目標爲n時,能夠轉化爲n個-1和n個1
2.求這串數字的全部排列
3.知足以上規律的就是有效的括號組合
4.最後一步再將數字組合轉化成括號組合
整個過程須要一些小的工具:
1.求全排列的函數:next_permutation
2.數字轉化成括號:容器map
class Solution { public: vector<string> generateParenthesis(int n) { vector<string> result; if(n == 0){return result;} vector<vector<int>> mid; vector<int> temp; for(int i = 0 ; i < n ; i ++) { temp.push_back(-1); //先放全部的-1 } for(int i = 0 ; i < n ; i ++) { temp.push_back(1); //再放全部的+1(這樣的緣由是由於全排列須要從小到大的順序) } while(next_permutation(temp.begin(),temp.end())) //求全排列 { int target = 0; int judg = 1; for(auto i:temp) { target+=i; if(target < 0) { judg = 0;break; //是否知足前n項之和不小於0 } } if(judg == 1){mid.push_back(temp);}//若是不用剪枝,則能夠將數組放入mid } map<int,string> invert; invert.insert(map<int,string>::value_type(1,"(")); //1對應左括號 invert.insert(map<int,string>::value_type(-1,")")); //-1對應右括號 for(auto i:mid) { string s; for(auto j:i) { s += invert[j]; //數字組合轉化成字符串 } result.push_back(s); } return result; } }; 做者:da-lian-mao-ai-chi-yu 連接:https://leetcode-cn.com/problems/generate-parentheses/solution/gua-hao-tai-fu-za-shu-zi-zhuan-hua-ta-by-da-lian-m/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
用這個函數一會兒就省去了找全部可能的排列,在排列以後再轉成括號就好了,因爲只有一種括號,就用1和-1來代替並使用剪枝算法
while(next_permutation(temp.begin(),temp.end())) {}
next_permutation(num,num+n)函數是對數組num中的前n個元素進行全排列,同時並改變num數組的值。
另外,須要強調的是,next_permutation()在使用前須要對欲排列數組按升序排序,不然只能找出該序列以後的全排列數。
這種思路太好了,放到一塊兒再重構,只要掌握好新建鏈表的方法就能夠了,必須用new
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: ListNode* mergeKLists(vector<ListNode*>& lists) { vector<int> v; for(auto head:lists) { while(head) { v.push_back(head->val); head=head->next; } } sort(v.begin(),v.end()); ListNode* head = new ListNode(0); ListNode* res = head; //int i = v.size(); for(int i = 0 ; i<v.size(); i++) { ListNode* temp =new ListNode(v[i]); res->next = temp; res = temp; } return head->next; } };
class Solution { public: int removeDuplicates(vector<int>& nums) { int i =1; int j = 0; int len = nums.size(); if(len==0)return 0; for(i=1; i<len;i++) { if(nums[i]==nums[j])continue; else { nums[++j] = nums[i]; } } return j+1; } };
循環這裏必須加等號
class Solution { public: int removeElement(vector<int>& nums, int val) { int i = 0 ; int j = nums.size()-1; while(i<=j) { while(i<=j && nums[i]!=val) i++; while(i<=j && nums[j]==val)j--; if(i<=j) swap(nums[i],nums[j]); } return i; } };
class Solution { public: void nextPermutation(vector<int>& nums) { if(next_permutation(nums.begin(),nums.end())) { return ; } else { sort(nums.begin(),nums.end()); return ; } } };
採用的是遍歷判斷法,可以剪枝的地方很是有限,因此超時了
class Solution { public: bool is(string s1) { if(s1[0]==')')return false; int len = s1.size(); if(len%2!=0)return false; stack<char> st; for(auto i:s1) { if(i=='(') { st.push(i); } else { if(!st.empty() && st.top()=='(') { st.pop(); } else { return false; } } } return st.empty(); } int longestValidParentheses(string s) { //兩種辦法,第一種雙指針窗口法,當遇到破壞結構時,從中間的地方開始起,可是可能會有一點複雜 //遍歷判斷法,專門呢寫一個函數,第一種的複雜度確定是最低的 if(s.empty())return 0; int max = 0; int len = s.size(); if(len==1)return 0; for(int i = 0 ; i<len ;i++) { for(int j = i+1;j<len;j++) { string s1=s.substr(i,j-i+1); if(is(s1) && j-i+1>max)max = j-i+1; } } return max; } };
class Solution { public: int search(vector<int>& nums, int target) { int right = nums.size(); int left = 0; while(left<right) { int mid = left + (right-left)/2; if(nums[mid]==target){return mid;} else if (nums[mid]>target) { if(target>=nums[0] || nums[mid]<nums[0]){right = mid;} else{left=mid+1;} } else { if(nums[mid]>=nums[0] ||target<nums[0]){left=mid+1;} else{right=mid;} } } return -1; } };
class Solution { public: int search(vector<int>& nums, int target) { int lo = 0, hi = nums.size() - 1; while (lo < hi) { int mid = (lo + hi) / 2; if ((nums[0] > target) ^ (nums[0] > nums[mid]) ^ (target > nums[mid])) lo = mid + 1; else hi = mid; } return lo == hi && nums[lo] == target ? lo : -1; } }; 做者:LukeLee 連接:https://leetcode-cn.com/problems/search-in-rotated-sorted-array/solution/ji-jian-solution-by-lukelee/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
class Solution { public: vector<int> searchRange(vector<int>& nums, int target) { int left = 0, right = nums.size(); while(left<right) { int mid = left + (right-left)/2; if(nums[mid]==target) { int t1 =mid-1, t2 = mid+1; while(t1>=0 && nums[t1]==target)t1--; while(t2<(int)nums.size() && nums[t2]==target)t2++; return {t1+1,t2-1}; } else if(nums[mid]<target) { left = mid +1; } else{right = mid;} } return {-1,-1}; } };
二分法的邊界仍是比較難處理,這道題中當存在相等時就直接返回mid,當不相等的時候須要進行兩值夾逼併返回有值,並且兩個邊界都不能捨去,可是這樣又會可能無限循環,最後還要加個if判斷是否夾逼了
class Solution { public: int searchInsert(vector<int>& nums, int target) { int len = nums.size(); int left=0,right = len-1; if(target<=nums[0])return 0; if(target>nums[len-1])return len; if(target==nums[len-1])return len-1; while(left<right) { int mid = (left+right)/2; if(nums[mid]==target)return mid; else if(nums[mid]>target) { right = mid; } else { left = mid; } if(left==right-1 && nums[left]<target && nums[right]>target )return right; } return right; } };
簡單的寫法
能夠不用夾逼,由於找的時剛剛大於等於這個值的,因此每次讓左邊界加1
class Solution { public: int searchInsert(vector<int>& nums, int target) { int low = 0, high = nums.size() - 1, mid = 0; while(low <= high){ mid = low + (high - low) / 2; if(nums[mid] < target) low = mid + 1; else if(nums[mid] > target) high = mid - 1; else return mid; } return low; } };
比較使用的模板
用來求剛剛大於等於target的第一個值
class Solution { public: int searchInsert(vector<int>& nums, int target) { int len = nums.size(); int left=0,right = len; while(left<right) { int mid = (left+right)/2; if(nums[mid]==target)return mid; else if(nums[mid]<target) { left = mid+1; } else { right = mid; } } return left; } };
class Solution { public: bool isValidSudoku(vector<vector<char>>& board) { //主要考察二維數組用法 //每一列 for(int i= 0 ; i<9 ;i++) { set<char> st; int num = 0; for(int j = 0 ; j < 9; j++) { if(board[i][j]=='.'){continue;} else{st.insert(board[i][j]);num++;} } if(st.size()!=num){return false;} } //每行 for(int i= 0 ; i<9 ;i++) { set<char> st; int num = 0; for(int j = 0 ; j < 9; j++) { if(board[j][i]=='.'){continue;} else{st.insert(board[j][i]);num++;} } if(st.size()!=num){return false;} } for(int c1 = 0 ; c1<9; c1=c1+3) { for(int c2 =0 ; c2<9 ;c2=c2+3) { set<char>st; int num=0; for(int i=0 ; i<3 ;i++) { for(int j = 0; j<3; j++) { if(board[c1+i][c2+j]=='.'){continue;} else{st.insert(board[c1+i][c2+j]);num++;} } } if(st.size()!=num){return false;} } } return true; } };
class Solution { public: bool isValidSudoku(vector<vector<char>>& board) { vector<int> wow(9,0); int mux1; int mux2; int mux3; int box_index; for(int i=0;i<9;i++){ for(int j=0;j<9;j++){ if(board[i][j] == '.'){ continue; } mux1 = 0x01 << (board[i][j] - '1'); mux2 = 0x01 << 9 << (board[i][j] - '1'); mux3 = 0x01 << 9 << 9 << (board[i][j] - '1'); box_index = (i/3) * 3 + j/3; if((wow[i]&mux1) != mux1 && (wow[j]&mux2) != mux2 && (wow[box_index]&mux3) != mux3){ wow[i] = wow[i]|mux1; wow[j] = wow[j]|mux2; wow[box_index] = wow[box_index]|mux3; } else{ return false; } } } return true; } };
這個遍歷三次太複雜了,並且使用set時間也比較長
下面這個方法靈活運用了三個哈希表(二維數組實現)的,單次遍歷,用目的區域(行、列、快)來表示行,用1-9來9個數字出現次數做爲列,列爲10由於直接對應,0不算,給9加一行
class Solution { public: bool isValidSudoku(vector<vector<char>>& board) { int row[9][10] = {0};// 哈希表存儲每一行的每一個數是否出現過,默認初始狀況下,每一行每個數都沒有出現過 // 整個board有9行,第二維的維數10是爲了讓下標有9,和數獨中的數字9對應。 int col[9][10] = {0};// 存儲每一列的每一個數是否出現過,默認初始狀況下,每一列的每個數都沒有出現過 int box[9][10] = {0};// 存儲每個box的每一個數是否出現過,默認初始狀況下,在每一個box中,每一個數都沒有出現過。整個board有9個box。 for(int i=0; i<9; i++){ for(int j = 0; j<9; j++){ // 遍歷到第i行第j列的那個數,咱們要判斷這個數在其所在的行有沒有出現過, // 同時判斷這個數在其所在的列有沒有出現過 // 同時判斷這個數在其所在的box中有沒有出現過 if(board[i][j] == '.') continue; int curNumber = board[i][j]-'0'; if(row[i][curNumber]) return false; if(col[j][curNumber]) return false; if(box[j/3 + (i/3)*3][curNumber]) return false; row[i][curNumber] = 1;// 以前都沒出現過,如今出現了,就給它置爲1,下次再碰見就可以直接返回false了。 col[j][curNumber] = 1; box[j/3 + (i/3)*3][curNumber] = 1; } } return true; } }; 做者:liujin-4 連接:https://leetcode-cn.com/problems/valid-sudoku/solution/36-jiu-an-zhao-cong-zuo-wang-you-cong-shang-wang-x/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
寫了一套很是複雜的算法,原理是依次判斷每一個控制區域,若是有個地方能用三個條件判斷出來則填上,而後再解決其餘的,結果發現代碼在無限循環,緣由是不存在這樣的直接能夠判斷出來的位置,這樣原理上就出錯了
class Solution { public: int num(vector<vector<char>>& board, int k1, int k2) { map<char, int> m = { {'1',0},{'2',0},{'3',0},{'4',0},{'5',0},{'6',0},{'7',0},{'8',0},{'9',0} }; for (int i = 0; i < 9; i++) { if (board[k1][i] != '.') { m[board[k1][i]] = 1; } if (board[i][k2] != '.') { m[board[i][k2]] = 1; } } for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if (board[k1 / 3 + i][k2 / 3 + j] != '.') { m[board[k1 / 3 + i][k2 / 3 + j]] = 1; } } } map<char, int>::iterator iter; int num = 0; int res = 0; for (auto iter = m.begin(); iter != m.end(); iter++) { //int num = 0; if (iter->second == 0) { res = iter->second; num++; } } if (num == 1) { return res; } else { return 0; } } bool is(vector<vector<char>>& board) { for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { if (board[i][j] == '.') { return false; } } } return true; } void solveSudoku(vector<vector<char>>& board) { //想法使用遞歸,終止條件是填滿了,方法是對空着的位置進行遍歷,每個都設置一個哈希,當行列塊又出現的設爲1,當剩最後一個 了時就返回這個表 for (int i = 0; i < 9; i++) { for (int j = 0; j < 9; j++) { if (board[i][j] == '.') { if (num(board, i, j)) { board[i][j] = char(num(board, i, j)+'0' ); } else { continue; } } } } while (!is(board)) { solveSudoku(board); } return; } };
要特別注意將i放入下次dfs的寫法,目的就是爲了下次再也不查找以前的元素,值得學習
class Solution { private: vector<vector<int>> res; vector<int> path; int sum = 0; public: void dfs(vector<vector<int>>& res,vector<int>& candidates,vector<int>& path, int sum,int target ,int j) { if(sum == target) { res.push_back(path);return; } if(sum>target)return; for(int i = j; i<(int)candidates.size() ;i++) { path.push_back(candidates[i]); sum +=candidates[i]; dfs(res, candidates, path, sum, target,i); path.pop_back(); sum -=candidates[i]; } } vector<vector<int>> combinationSum(vector<int>& candidates, int target) { sort(candidates.begin(),candidates.end()); dfs(res,candidates,path,0,target,0); return res; } };
回溯算法:當給定終點時,能夠進行回退
再加上遞歸和剪枝算法
負遞歸算法:
// author:rmokerone #include <iostream> #include <vector> using namespace std; class Solution { private://成員變量 vector<int> candidates; vector<vector<int>> res; vector<int> path; public: void DFS(int start, int target) { if (target == 0) { //遞歸終止條件 res.push_back(path); return; } for (int i = start; i < candidates.size() && target - candidates[i] >= 0; i++) { path.push_back(candidates[i]); DFS(i, target - candidates[i]); path.pop_back(); } } vector<vector<int>> combinationSum(vector<int> &candidates, int target) { std::sort(candidates.begin(), candidates.end()); this->candidates = candidates; DFS(0, target); return res; } }; 做者:liweiwei1419 連接:https://leetcode-cn.com/problems/combination-sum/solution/hui-su-suan-fa-jian-zhi-python-dai-ma-java-dai-m-2/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
正遞歸加剪枝算法:
要特別注意幾點:
1.是用了剪枝是爲了提速
2.除此以外,在深度優先搜索的時候能夠設置從當前元素開始,由於以前的元素在以前的節點已經用過了,因此加了一個start變量用來記錄在can容器中的位置
3.像sum這種的不用每次都計算,直接加個參數就好了,注意全局變量的定義位置,由於遞歸函數要用,因此定義在全局的地方,還能夠在外面定義可是在函數中進行計算,好比siz這個變量,更嚴格點能夠定義在private中
class Solution { public: vector<int> tmp; vector<vector<int>> ans; int siz; void dfs(int now, int sum, int tar, vector<int>& num) { if(sum > tar) return; if(sum == tar) { ans.push_back(tmp); return; } for(int i = now; i < siz; i ++) { tmp.push_back(num[i]); //一層一層往下走 dfs(i, sum + num[i], tar, num); //當大於或者等於時都會返回 tmp.pop_back();//回到上一個分叉節點,從整個樹的最左邊一條開始試 } } vector<vector<int>> combinationSum(vector<int>& candidates, int target) { siz = candidates.size(); dfs(0, 0, target, candidates); return ans; } }; 做者:iswJXkYVv3 連接:https://leetcode-cn.com/problems/combination-sum/solution/c-di-gui-shen-sou-by-iswjxkyvv3/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
class Solution { public: int firstMissingPositive(vector<int>& nums) { //sort(nums.begin(),nums.end()); for(int i = 1;true;i++) { auto temp = find(nums.begin(),nums.end(),i); if (temp==nums.end()){return i;} } } };
還可使用哈希表作,創建一個1到n的哈希表,遍歷過數組後找第一個哈希值爲0的
這是一個創建在一維數組上的問題,理論上這類的全部問題均可以用雙指針來解決,這道題的難點在於找到最大值以後的後半部分怎麼處理,怎麼去找到剩下的部分,這時候須要求最大值,使用max_element函數,可是注意返回的是一個迭代器,用法以下
#include <algorithm> //返回迭代器 auto max = max_element(v.begin(), v.end()); //獲得最大值對應下標 int kk = max-v.begin();
當開始使用這個函數的時候想把裏面全部的索引換成迭代器指針,可是發如今執行起來十分有困難,就是由於迭代器之間不傳遞,好比i和j都是v的迭代器,i++被容許,可是j=i+1就不被容許,因此能不使用迭代器就不使用,當要使用那些返回迭代器的方法時,能夠用他減去v.begin()天然就返回的是下標位置
class Solution { public: int trap(vector<int>& height) { if(height.size()<=2){return 0;} int i = 0; int j = 1; int sum = 0; while (i < height.size() - 2) { while (j<height.size() && height[i] > height[j]) { j++; } if (j == height.size()) { auto m = max_element(height.begin()+i+1, height.end()); j=m-height.begin(); } sum = sum + (j - i - 1)*min(height[i], height[j]); for (int c = i + 1; c < j; c++) { sum = sum - height[c]; } i = j; j = i + 1; } return sum; } };
堆棧的方法,不是很好理解
直觀想法
咱們能夠不用像方法 2 那樣存儲最大高度,而是用棧來跟蹤可能儲水的最長的條形塊。使用棧就能夠在一次遍歷內完成計算。
咱們在遍歷數組時維護一個棧。若是當前的條形塊小於或等於棧頂的條形塊,咱們將條形塊的索引入棧,意思是當前的條形塊被棧中的前一個條形塊界定。若是咱們發現一個條形塊長於棧頂,咱們能夠肯定棧頂的條形塊被當前條形塊和棧的前一個條形塊界定,所以咱們能夠彈出棧頂元素而且累加答案到
\text{ans}ans 。算法
使用棧來存儲條形塊的索引下標。 遍歷數組: 當棧非空且
\text{height}[current]>\text{height}[st.top()]height[current]>height[st.top()]
意味着棧中元素能夠被彈出。彈出棧頂元素 \text{top}top。 計算當前元素和棧頂元素的距離,準備進行填充操做
\text{distance} = \text{current} - \text{st.top}() -
1distance=current−st.top()−1 找出界定高度 \text{bounded_height} =
\min(\text{height[current]}, \text{height[st.top()]}) -
\text{height[top]}bounded_height=min(height[current],height[st.top()])−height[top]
往答案中累加積水量\text{ans} \mathrel{+}= \text{distance} \times
\text{bounded_height}ans+=distance×bounded_height 將當前索引下標入棧 將
\text{current}current 移動到下個位置
做者:LeetCode
連接:https://leetcode-cn.com/problems/trapping-rain-water/solution/jie-yu-shui-by-leetcode/
來源:力扣(LeetCode)
著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
int trap(vector<int>& height) { int ans = 0, current = 0; stack<int> st; while (current < height.size()) { while (!st.empty() && height[current] > height[st.top()]) { int top = st.top(); st.pop(); if (st.empty()) break; int distance = current - st.top() - 1; int bounded_height = min(height[current], height[st.top()]) - height[top]; ans += distance * bounded_height; } st.push(current++); } return ans; } 做者:LeetCode 連接:https://leetcode-cn.com/problems/trapping-rain-water/solution/jie-yu-shui-by-leetcode/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
沒有思路,原來是利用乘法的計算方式來的
class Solution { public: string multiply(string num1, string num2) { int n1=num1.size(); int n2=num2.size(); string res(n1+n2,'0'); for(int i=n2-1;i>=0;i--){ for(int j=n1-1;j>=0;j--){ int temp=(res[i+j+1]-'0')+(num1[j]-'0')*(num2[i]-'0'); res[i+j+1]=temp%10+'0';//當前位 res[i+j]+=temp/10; //前一位加上進位,res[i+j]已經初始化爲'0',加上int類型自動轉化爲char,因此此處不加'0' } } //去除首位'0' for(int i=0;i<n1+n2;i++){ if(res[i]!='0') return res.substr(i); } return "0"; } }; 做者:carryzz 連接:https://leetcode-cn.com/problems/multiply-strings/solution/c-shu-shi-cheng-fa-dai-ma-jian-ji-you-ya-yi-dong-b/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
class Solution { public: bool isMatch(string s, string p) { int n = s.size(), m = p.size(); vector< vector<bool> > dp(n+1, vector<bool>(m+1, false)); dp[0][0] = true; // initialize for (int i = 1; i <= m; ++ i){ if(p[i - 1] == '*' && dp[0][i - 1]) dp[0][i] = dp[0][i - 1]; } for (int i = 1; i <= n; ++ i){ for (int j = 1; j <= m; ++ j){ if (s[i - 1] == p[j - 1] || p[j - 1] == '?'){ dp[i][j] = dp[i - 1][j - 1];// ismatch, move on }else if (p[j - 1] == '*'){ bool zero, mul;// '*' act as zero, '*' act as multiple characters zero = (j < m && s[i - 1] == p[j] && dp[i - 1][j - 1]) || dp[i][j - 1]; mul = dp[i -1][j]; dp[i][j] = zero || mul; } } } return dp[n][m]; } }; 做者:wen-mu-yang 連接:https://leetcode-cn.com/problems/wildcard-matching/solution/c-dong-tai-gui-hua-yu-shuang-zhi-zhen-tan-xin-by-w/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
使用了遞歸算法,不斷更新起點,每更新一次跳到能跳到的最遠的地方,記錄到達終點時的步數,但在過程當中關於什麼時候記錄步數的問題很難。終於調成功後發現這樣是錯誤的,由於有時不須要全跳,尤爲是最後幾個值爲0的狀況,要是早早跳到這裏反而會到不了終點!
class Solution { public: int ans=0; int summm(int start, vector<int> nums) { int temp = max_element(nums.begin() + start +1, nums.begin() + start + nums[start]+1) - nums.begin(); if (start + nums[start] >= temp + nums[temp]) { ans++; return start + nums[start]; } else { ans++; return temp ; } } int jump(vector<int>& nums) { int len = nums.size(); //int ans = 0; if (len == 1) { return 0; } else { int start = 0; //if (start + nums[start] >= len) { return 1; } while (start<len ) { if (start + nums[start] >= len-1) { return ans + 1; } start = summm(start, nums); } return ans; } } };
看了解答,發現這種思路總體是對的,就是追求每一次能走的最大,然而以前的錯誤在於,並非比較start+nums[start]和max[start,start+len(start)]之間的最大值,應該比較的是和區間裏每個值和其值的和即i+len(i),這纔是走的最遠的方法,按照這種方法不斷更新,就能夠找到最後了
即便意思相近可是別人的表達也十分簡介
int jump(vector<int> &nums) { int ans = 0; int start = 0; int end = 1; while (end < nums.size()) { int maxPos = 0; for (int i = start; i < end; i++) { // 能跳到最遠的距離 maxPos = max(maxPos, i + nums[i]); //在遍歷的過程當中不斷更新這個最大值 } start = end; // 下一次起跳點範圍開始的格子 end = maxPos + 1; // 下一次起跳點範圍結束的格子,加一是由於上面的遍歷時左閉右開的 ans++; // 跳躍次數 } return ans; } 做者:ikaruga 連接:https://leetcode-cn.com/problems/jump-game-ii/solution/45-by-ikaruga/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
關於全排列next_permumation有幾點須要說明
1.之氣那要排序
2.只要開始全排列,第一個結果是已經改變以後的數組,而不是原來的
3。參數是首末迭代器
class Solution { public: vector<vector<int>> permute(vector<int>& nums) { vector<vector<int> > res; sort(nums.begin(), nums.end()); res.push_back(nums); while(next_permutation(nums.begin(),nums.end())) { res.push_back(nums); } return res; } };
這是使用隊列層序遍歷的方法寫的,可是因爲不容許重複,每次要使用find判斷,十分複雜
class Solution { public: vector<vector<int>> permute(vector<int>& nums) { vector<vector<int> > res; if (nums.size()==0 || nums.size()==1) { res.push_back(nums); return res; } sort(nums.begin(), nums.end()); queue<vector<int>> q; for(auto i:nums) { vector<int> temp; temp.push_back(i); q.push(temp); } //int i=nums.size()-1; while(q.front().size()!=nums.size()) { for (auto i:nums) { vector<int> temp = q.front(); if(find(temp.begin(),temp.end(),i)==temp.end()) { temp.push_back(i); q.push(temp); } } q.pop(); } while(!q.empty()) { res.push_back(q.front()); q.pop(); } return res; } };
算法模板
遞歸{ 遞歸出口; for(int i = 1;i<=n;i++){ add(i); 進入遞歸; remove(i); } } 做者:windliang 連接:https://leetcode-cn.com/problems/unique-binary-search-trees-ii/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by-2-7/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
void backtrack(路徑,選擇列表) { if(終止條件) { 傳出路徑結果 return; } for i in 選擇列表 { 進入該選擇 back(路徑,選擇列表) 退出該選擇 } }
這是根據該思想寫的代碼,代碼結構簡單,清晰,可是優化的很差
class Solution { private: vector<vector<int> > res; int len; vector<int> path; public: void backtrack(vector<int> &path, vector<int>& nums) { set<int> s(path.begin(),path.end()); if(path.size()==len) { if (s.size()==len) { res.push_back(path); return; } else { return; } } for(auto i : nums) { path.push_back(i); backtrack(path,nums); path.pop_back(); } } vector<vector<int>> permute(vector<int>& nums) { len = nums.size(); if (nums.size()==0 || nums.size()==1) { res.push_back(nums); return res; } sort(nums.begin(), nums.end()); backtrack(path,nums); return res; } };
將圖片沿正對角線和豎軸翻轉一下就能夠了
class Solution { public: void rotate(vector<vector<int>>& matrix) { int n = matrix.size(); int m = matrix[0].size(); // 對於正對角線對稱翻轉 for (int i = 0; i < n; i++) { for (int j = i; j < m; j++) { swap(matrix[i][j], matrix[j][i]); } } // 豎軸鏡像操做 for (int i = 0; i < n; i++) { reverse(matrix[i].begin(), matrix[i].end()); } } }; 做者:happy_yuxuan 連接:https://leetcode-cn.com/problems/rotate-image/solution/leetcode48-fan-zhuan-gui-lu-by-happy_yuxuan/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
class Solution { public: vector<vector<string>> groupAnagrams(vector<string>& strs) { vector<vector<string>> res; vector<pair<multiset<char>,vector<string>>> st; for(auto str:strs) { multiset<char> temp(str.begin(),str.end()); bool b = false; for(int i = 0 ;i<(int)st.size() ;i++) { if(st[i].first==temp) { st[i].second.push_back(str); b = true; break; } } if(!b) { st.push_back(pair<multiset<char>,vector<string>>(temp,{str})); } } for(auto i:st) { vector<string> temp; for(auto j:i.second) { temp.push_back(j); } res.push_back(temp); } return res; } };
更好的辦法:哈希表和數組聯繫,只要引入一個hash表,索引是排序後的單詞,值爲結果vector的下標,循環一遍就行了
class Solution { public: vector<vector<string>> groupAnagrams(vector<string>& strs) { vector<vector<string>> res; int sub=0; //結果vector的下標值 string tmp; //臨時string unordered_map<string,int> work; //判斷排序後單詞是否存在,即字母組成是否一致 for(auto str:strs) { tmp=str; sort(tmp.begin(),tmp.end()); if(work.count(tmp)) { res[work[tmp]].push_back(str); } else { vector<string> vec(1,str); res.push_back(vec); work[tmp]=sub++; } } return res; } }; 做者:rjs 連接:https://leetcode-cn.com/problems/group-anagrams/solution/c-yin-ru-hashbiao-shi-jian-32ms-ji-bai-9948-nei-cu/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
暴力法
class Solution { public: double myPow(double x, int n) { if(x==0)return 0; if(x==1 || n==0) return 1; double res=1; while(n>0) { res=res*x; n--; } while(n<0) { res = res/x; n++; } return res; } };
遞歸法,將指數冪進行二分,十分巧妙
class Solution { public: double myPow(double x, int n) { if (n == 0) { return 1; } if (n == 1) { return x; } if (n == -1) { return 1 / x; } double half = myPow(x, n / 2); double rest = myPow(x, n % 2); return rest * half * half; } }; 做者:frank588 連接:https://leetcode-cn.com/problems/powx-n/solution/qing-xi-jian-dan-de-dan-han-shu-di-gui-wu-lei-xing/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
思路很是清晰,典型的n皇后的問題,稍微麻煩的問題是判斷是否額可以知足條件,並且注意不用在最後才檢查條件。應該在中間進行判斷,只要可以知足條件才繼續下去,這樣到最後也就知足條件了,放一個知足一個,最後就必定知足,這裏一行一行創建的會稍微簡單一點
class Solution { vector<vector<string>> res;//存結果 vector<string> tmp;//存棋盤 public: vector<vector<string>> solveNQueens(int n) { string line(n,'.');//既然一行一行試,那就一行一行存 solveNQueens(line, 0, n);//從第0行開始 return res; } private: //試某一行 void solveNQueens(string& line, int row, int n) { if(tmp.size() == n)//棋盤繪製夠n行存進結果,不用繼續試了 { res.push_back(tmp); return; } for(int i = 0; i < n; ++i)//一格一格,每格都要試 { if(checkAll(row, i, n)) //符合條件了 { line[i] = 'Q'; //就把當前試的這一格放皇后 tmp.push_back(line); //而後把這一行繪製進棋盤 line[i] = '.'; //棋盤的下一行應該是沒有皇后的 solveNQueens(line, row + 1, n);//去試下一行 tmp.pop_back(); //接下來要去試下一格,剛纔繪製進去的那一行刪掉 } } } //暴力檢查條件 bool checkAll(int row, int col, int n) { for(int i = row - 1, j = col - 1; i >= 0 && j >= 0; --i,--j)//左上方 { if(tmp[i][j] == 'Q') return false; } for(int i = row - 1; i >= 0; --i)//正上方 { if(tmp[i][col] == 'Q') return false; } for(int i = row -1, j = col + 1; i >= 0 && j < n; --i, ++j)//右上方 { if(tmp[i][j] == 'Q') return false; } return true; } }; 做者:zynflicker 連接:https://leetcode-cn.com/problems/n-queens/solution/4ms89mhui-su-fa-jian-dan-yi-dong-by-zynflicker/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
很典型的使用貪心算法的例題
class Solution { public: int maxSubArray(vector<int> &nums) { //相似尋找最大最小值的題目,初始值必定要定義成理論上的最小最大值 int result = INT_MIN; int numsSize = int(nums.size()); int sum = 0; for (int i = 0; i < numsSize; i++) { sum += nums[i]; result = max(result, sum); //若是sum < 0,從新開始找子序串 if (sum < 0) { sum = 0; } } return result; } }; 做者:pinku-2 連接:https://leetcode-cn.com/problems/maximum-subarray/solution/zui-da-zi-xu-he-cshi-xian-si-chong-jie-fa-bao-li-f/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
每次跟新一次位置,當最遠達到的地方是0,說明false,代碼用vs會出現莫名其妙的問題,可是作法應該是對的
另外因爲可能出現的請狀況太多了,在邊界的設置上出錯的地方太多了
class Solution { public: bool canJump(vector<int>& nums) { int left = 0; int len = nums.size(); while(1) { if(left>=len-1 || left+nums[left]>=len-1)return true; int max = 0; int temp = 0; for(int i = left ; i<=len-1 &&i<=left+nums[left] ;i++) { if(i+nums[i]>max){max = i+nums[i];temp=i;} } if(max<len-1&& nums[max]==0)return false; left = temp; } return false; } };
一樣意思但更簡潔的代碼
解題思路:
1.若是某一個做爲 起跳點 的格子能夠跳躍的距離是 3,那麼表示後面 3 個格子均可以做爲 起跳點。
2.能夠對每個能做爲 起跳點 的格子都嘗試跳一次,把 能跳到最遠的距離 不斷更新。
3.若是能夠一直跳到最後,就成功了。
做者:ikaruga
連接:https://leetcode-cn.com/problems/jump-game/solution/55-by-ikaruga/
來源:力扣(LeetCode)
著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
bool canJump(vector<int>& nums) { int k = 0; for (int i = 0; i < nums.size(); i++) { if (i > k) return false;//k表示到i的時候,以前全部位置能到達的最大距離,每個都走一下,可是不用考慮怎麼走的,當走到一個位置不能再更新了,就會小於下一個i k = max(k, i + nums[i]); } return true; } 做者:ikaruga 連接:https://leetcode-cn.com/problems/jump-game/solution/55-by-ikaruga/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
一樣的格式,可是改爲max了,並且維護的不是一個樹,而是先放到二維向量中的末尾
class Solution { public: vector<vector<int>> merge(vector<vector<int>>& intervals) { vector<vector<int>> res; if(intervals.empty())return res; sort(intervals.begin(),intervals.end()); res.push_back(intervals[0]); for(int i = 1 ;i< (int)intervals.size() ;i++) { if(intervals[i][0]<= res.back()[1]) { res.back()[1] = max(res.back()[1],intervals[i][1]); } else { res.push_back(intervals[i]); } } return res; } };
使用長度爲1的滑動窗口在滑動,可是沒想到題目仍是會出現後面區間包含前面的狀況,這樣就要從新設計了
看到答案說sort函數排序能夠默認按照第一個第二個這樣排序,能夠排序以後在作,排序以後後面的值確定是大於等於前面的,可是還要考慮後面包含前面的狀況
考點:排序+數組
思路:先對二維數組表示的全部區間的左端點進行升序排序,而後從第二個區間(下標爲1)開始遍歷判斷是否能夠作合併操做:只要前一個區間的右端點大於等於當前區間的左端點,那說明這兩個區間能夠合併,合併後的區間左端點是前一個區間的左端點,只須要更新右端點爲兩個區間右端點的最大值便可。
不然,說明兩個區間沒有重疊,直接更新表示不重複區間的pos值
做者:xing2fan
連接:https://leetcode-cn.com/problems/merge-intervals/solution/qu-jian-he-bing-de-pai-xu-fa-by-xing2fan/
來源:力扣(LeetCode)
著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
class Solution { public: vector<vector<int>> merge(vector<vector<int>>& intervals) { if (!intervals.size()) return {}; //先把區間集合按照左端點從小到大進行排序 sort(intervals.begin(), intervals.end(), less<vector<int>>()); int pos = 0;//pos的值是每一個不重疊的區間 //直接在原區間上作就能夠 //從第二個區間開始去比較 for (int i = 1; i < intervals.size(); ++i) { //兩個區間若能合併,則第一個區間的右端點必定大於等於第二個區間的左端點 //好比[1,3] [2,6] if (intervals[pos][1] >= intervals[i][0]) { //第一個區間的尾部須要更新爲:兩個區間的最大值即[1,6] intervals[pos][1] = max(intervals[pos][1], intervals[i][1]); } else//沒有重疊時 { //[1,6] [2,8]====>區間1就是目前的這個區間 intervals[++pos] = intervals[i]; } } intervals.resize(pos + 1);//把最後的不用的彈出 return intervals; } }; 做者:xing2fan 連接:https://leetcode-cn.com/problems/merge-intervals/solution/qu-jian-he-bing-de-pai-xu-fa-by-xing2fan/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
class Solution { public: vector<vector<int>> merge(vector<vector<int>>& intervals) { if (intervals.empty()) { return intervals; } sort(intervals.begin(), intervals.end()); vector<vector<int>> result{ intervals.front() }; for (auto&& interval : intervals) { if (interval.front() > result.back().back()) { result.emplace_back(move(interval)); } else { result.back().back() = max(interval.back(), result.back().back()); } } return result; } }; 做者:klaxxi 連接:https://leetcode-cn.com/problems/merge-intervals/solution/c-pai-xu-tan-xin-by-klaxxi/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
和上題同樣,先插入再作
class Solution { public: vector<vector<int>> insert(vector<vector<int>>& intervals, vector<int>& newInterval) { vector<vector<int>> res; if(newInterval.empty())return intervals; intervals.push_back(newInterval); sort(intervals.begin(),intervals.end()); res.push_back(intervals[0]); for(int i =1; i< (int)intervals.size() ;i++) { if(intervals[i][0]<= res.back()[1]) { res.back()[1] = max(res.back()[1], intervals[i][1]); } else { res.push_back(intervals[i]); } } return res; } };
在多種題解法中,動態規劃每每是最簡單的一種,而遞歸每每是麻煩也很差寫的一種,因此最好仍是先考慮dp,這道理可想而知dp較爲簡單,以前作的dp都是在一維上,這回是二維上的dp
class Solution { public: int uniquePaths(int m, int n) { vector<vector<int>> board(m,vector<int>(n,0)); for(int i = 0 ; i <m ;i++) { board[i][0]=1; } for(int j = 0; j<n ;j++) { board[0][j]=1; } for(int i = 1; i<m ;i++) { for(int j = 1 ; j<n ;j++) { board[i][j] = board[i-1][j] + board[i][j-1]; } } return board[m-1][n-1]; } };
很典型的動態規劃題目,和上題同樣
class Solution { public: int minPathSum(vector<vector<int>>& grid) { if(grid.empty())return 0; int m = grid.size(); int n = grid[0].size(); vector<vector<int>> board(m,vector<int>(n,0)); board[0][0]=grid[0][0]; for(int i = 1 ; i< n ;i++) { board[0][i] = grid[0][i]+board[0][i-1]; } for(int i = 1 ; i< m ; i++) { board[i][0] = grid[i][0]+board[i-1][0]; } for(int i = 1 ; i < m ;i++) { for(int j = 1 ; j< n; j++) { board[i][j] = grid[i][j] + min( board[i-1][j],board[i][j-1]); } } return board[m-1][n-1]; } };
雖然就是典型的二分法,可是有不少值得講究的地方:
1.mid最好設置爲兩個的和除以2再加1,擴大範圍更好,否則可能會chucuo
2.初始值能夠設置的更小點
3.最後是left和right的變化問題,最後兩個都變化或者至少一個變化,而後返回left就能夠,沒必要要返回mid
class Solution { public: int mySqrt(int x) { if(x==0)return 0; long left = 1; long right = x; while(left<right) { long mid = (left+right)/2+1; if(mid==x/mid)return mid; else if(mid>x/mid) {right = mid-1;} else {left = mid;} } return left; } };
動態規劃
class Solution { public: int climbStairs(int n) { if(n==0)return 0; if(n==1)return 1; if(n==2) return 2; vector<int> dp(n+1,0); dp[0]=0; dp[1]=1; dp[2]=2; for(int i = 3 ; i<n+1; i++) { dp[i] = dp[i-1]+ dp[i-2]; } return dp[n]; } };
class Solution { public: int minDistance(string word1, string word2) { int len1 = word1.size(); int len2 = word2.size(); vector<vector<int>> dp(len1+1, vector<int>(len2+1,0)); for(int i = 0 ;i<=len1; i++) { dp[i][0] = i; } for(int j = 0 ; j<=len2 ;j++) { dp[0][j] = j; } for(int i=1 ;i<=len1 ;i++) { for(int j=1; j<=len2 ;j++) { if(word1[i-1]==word2[j-1]) { dp[i][j] = dp[i-1][j-1] ; } else { dp[i][j] = min(dp[i-1][j-1],min(dp[i-1][j],dp[i][j-1]))+1; } } } return dp[len1][len2]; } };
class Solution { public: void setZeroes(vector<vector<int>>& matrix) { if(matrix.size()==0)return; vector<int> row; vector<int> col; int len1 = matrix.size(); int len2 = matrix[0].size(); for(int i= 0 ;i<len1 ; i++) { for(int j = 0 ; j <len2 ;j++) { if(matrix[i][j]==0) { row.push_back(i); col.push_back(j); } } } for(auto i :row) { for(int j = 0 ; j<len2 ;j++) { matrix[i][j]=0; } } for(auto j:col) { for(int i = 0 ; i<len1 ; i++) { matrix[i][j] = 0; } } return; } };
只有三個數的時候排序能夠如此簡單
class Solution { public: void sortColors(vector<int>& nums) { int left = 0 , right = nums.size()-1; for(int i = left ;i<=right ;i++) { if(nums[i]==0)swap(nums[left++],nums[i]); if(nums[i]==2)swap(nums[right--],nums[i--]); } return; } };
鞏固一下快速排序,要特注意的是先循環j再循環i
class Solution { public: void quicksort(int left,int right, vector<int>& nums) { if(left>=right)return; int i = left; int j = right; while(i<j) { while(i<j && nums[j]>=nums[left])j--; while(i<j && nums[i]<=nums[left])i++; if(i<j) { swap(nums[i],nums[j]); } } swap(nums[left],nums[i]); quicksort(left,i-1,nums); quicksort(i+1,right,nums); } void sortColors(vector<int>& nums) { int len = nums.size(); if(len==0 || len==1)return; quicksort(0,len-1,nums); return; } };
採用了兩個二分法模板作的,注意銜接的時候,要考慮小於第一個個值的狀況
class Solution { public: bool searchMatrix(vector<vector<int>>& matrix, int target) { if(matrix.empty())return false; if(matrix[0].empty())return false; int len = matrix.size(); int left = 0,right = len; while(left<right) { int mid = (left+right)/2; if(matrix[mid][0]==target)return true; else if(matrix[mid][0]<target) { left = mid+1; } else { right = mid; } } int temp = left-1; if(temp<0)return false; len = matrix[0].size(); left = 0;right=len; while(left<right) { int mid = (left+right)/2; if(matrix[temp][mid]==target) return true; else if(matrix[temp][mid]<target) { left=mid+1; } else { right=mid; } } return false; } };
因爲這個pos表明的是初始位置,當進行過一次遍歷後,這個值也要加1才行
class Solution { private: vector<vector<int> > res; vector<int> path; public: void dfs(int n,int pos, int k) { if (path.size()==k) { res.push_back(path); } for(int i = pos; i<= n; i++) { path.push_back(i); dfs(n,pos+1,k); path.pop_back(); pos++; } } vector<vector<int>> combine(int n, int k) { if(n==0||k==0) return res; dfs(n,1,k); return res; } };
其餘解答,道理是同樣的
class Solution { public: vector<vector<int>> combine(int n, int k) { vector<vector<int>> list; vector<int> result; dfs(list,result,n,k,0,-1); return list; } void dfs(vector<vector<int>>& list, vector<int>& result, int n, int k, int pos,int pre){ if(pos == k){ list.push_back(result); return; } if((pos + (n-pre)) <= k)return;//剪枝,添加以後用時節省了2/3 //在當前對不合理的取值進行判斷,結束下一層的遞歸操做。 //若是當前剩餘可操做數字的個數即(n-pre)< k-pos+1(即組合中有待賦值的位置個數),(+1是由於當前pos尚未進行操做),則能夠判斷該條路徑不可能獲得正確的解,再也不向下探尋。 for(int i=pre+1; i<n ; i++){ result.push_back(i+1); pre = i; dfs(list,result,n,k,pos+1,pre); result.pop_back();//回溯 } return; } }; 做者:rouzi 連接:https://leetcode-cn.com/problems/combinations/solution/dfsjian-zhi-by-rouzi/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
適合用遞歸作,可是遞歸併很差寫
下面這個遞歸式return,是在最後(至關於沒有),這個結構比較少見,主要是由於沒有肯定的返回條件,進行一個遍歷,而後在遍歷的過程當中不斷的放入大的容器
class Solution { public: vector<vector<int>> subsets(vector<int>& nums) { vector<vector<int> > res; vector<int> tmp; helper(res,tmp,nums,0); return res; } void helper(vector<vector<int> >& res,vector<int> tmp,vector<int>& nums,int level){ if(tmp.size()<=nums.size()){ res.push_back(tmp); } for(int i=level;i<nums.size();i++){ tmp.push_back(nums[i]); helper(res,tmp,nums,i+1); tmp.pop_back(); } return; } }; 做者:Tangzixia 連接:https://leetcode-cn.com/problems/subsets/solution/liang-chong-fang-fa-qiu-jie-zi-ji-by-tangzixia/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
class Solution { public: vector<vector<int>> ans; vector<int> tmp; void find(int dep, vector<int>& nums) { // 已經處理完最後一位,將目前存儲的集合存入 ans ,並回溯 if(dep <= 0) { ans.push_back(tmp); return; } // 狀況一:集合中有該元素 tmp.push_back(nums[dep - 1]); find(dep - 1, nums); tmp.pop_back(); // 狀況二:集合中無該元素 find(dep - 1, nums); } vector<vector<int>> subsets(vector<int>& nums) { find(nums.size(), nums); return ans; } }; 做者:iswJXkYVv3 連接:https://leetcode-cn.com/problems/subsets/solution/c-0ms-132mb-shen-sou-yong-shi-ji-bai-100mark-by-is/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
因爲代碼太複雜沒有全寫完,可是道理應該是對的,遍歷每一個word中的字符,當找到該字符時就將下一個字符、找到的座標、board輸入到判斷函數中,當全部字符都判斷成功時纔算含有此字符串,道理時對的,但不算是dfs或者回溯方法
class Solution { private: string s1; int h=0; int l = 0; public: //位置必須傳遞,才能和上次有關聯 bool dfs(char s1, string& s, int a , int b , vector<vector<char>>& board) { if(a==0) { if(b==0) { if(board[a+1][b]==s1 || board[a][b+1]==s2){return true;}} } if(b==l-1 ) { if(board[a+1][b]==s1 || board[a][b-1]==s2){return true;}} } else { if(board[a+1][b]==s1 || board[a][b+1]==s2 || board[a][b-1]==s2){return true;}} } for(int j = 0 ; j <b ; j++ ) { if(board[a+1][j]==s1){return true;} } if(a==h-1) { for(int j = 0 ; j <b ; j++ ) { if(board[a-1][j]==s1){return true;} } } if(b==l-1) { for(int i = 0 ; i<a; i++) { if(board[i][]) } } for(auto i :s) { dfs(i,s,) } } bool exist(vector<vector<char>>& board, string word) { h = board.size(); l = board[0].size(); //如何獲取第一次的正確位置,或者進行遍歷尋找 for(int i = 0 ; i< h;i++ ) { for(int j = 0; j<l ; j++) { if (board[i][j]==word[0])//這裏不用判斷 { if(dfs(word[1],word,i,j,board)){return true;} } } } return false; } };
class Solution { public: bool exist(vector<vector<char>>& board, string word) { if(board.size() == 0) return false; for (int i=0;i<board.size();i++){ for(int j=0;j<board[0].size();j++){ if (dfs(board,word,i,j,0)){ return true; } } } return false; } bool dfs(vector<vector<char>>& board, string& word, int i,int j,int length){ if(i>=board.size()||j>=board[0].size()||i<0||j<0||length>=word.size()||word[length]!=board[i][j]){ return false; //出界或者最後一位不相等 } if(length==word.size()-1&&word[length]==board[i][j]){//word不爲空且最後一位和當前位置相等 return true; } char temp=board[i][j]; board[i][j]='0';//這個位置 bool flag=dfs(board,word,i,j+1,length+1)||dfs(board,word,i,j-1,length+1)||dfs(board,word,i+1,j,length+1)||dfs(board,word,i-1,j,length+1); //找四邊的每個點,若是點 board[i][j]=temp; // 標記過的點恢復原狀,以便進行下一次搜索 return flag; } }; 做者:z1m 連接:https://leetcode-cn.com/problems/word-search/solution/tu-jie-di-gui-shen-du-you-xian-sou-suo-by-z1m/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
僞代碼
dfs(i,j,word, board,len) { if(i,j已經出界 || 這個位置和wordlen元素不相等) {返回false} if(這個位置和word最後一維相等) {返回true} } exist() { 遍歷board,並對每個位置執行一次判斷//當每一個位置都輸入進去,直接進入dfs,判斷和第一位是否相等,若是不相等則到下一個位置,若是相等則進入函數主體,判斷四周的四個位置都和下一位相等,true的終止條件是到了最後一位並且與此位置相等。裏面有一點要注意爲了防止出現AEA這種又回去的狀況,在這次搜索中已經相等過的要設置爲‘0’防止回去 }
1.總結:dfs也能夠設置爲有類型的,當設置爲bool型這種特殊的類型時,要寫兩個終止條件,一個是true的,一個是false,false能夠是每次的回退條件,可是true必定是最終的判斷條件,剩下的函數主題就是默認的經過這次驗證
class Solution { public: int removeDuplicates(vector<int>& nums) { int len = nums.size(); int i = 0, j = 0; while(i<len) { int temp = i; while(i<len && nums[i]==nums[temp])i++; if(i-temp==1){nums[j++]=nums[temp];} else if(i-temp>=2){nums[j++]=nums[temp];nums[j++]=nums[temp];} } return j; } };
先去重
class Solution { public: bool search(vector<int>& nums, int target) { if(nums.empty())return false; int j = 0; for(int i =1 ;i<(int)nums.size() ;i++) {if(nums[i]!=nums[j])nums[++j]=nums[i];} vector<int> num; num.assign(nums.begin(),nums.begin()+j+1); if((int)num.size()>1 && num[j]==num[0]){num.pop_back();} int right = num.size(); int left = 0; while(left<right) { int mid = left + (right-left)/2; if(num[mid]==target){return true;} else if (num[mid]>target) { if(target>=num[0] || num[mid]<num[0]){right = mid;} else{left=mid+1;} } else { if(num[mid]>=num[0] ||target<num[0]){left=mid+1;} else{right=mid;} } } return false; } };
class Solution { public: void merge(vector<int>& arr1, int m, vector<int>& arr2, int n) { //int len1 = arr1.size(); //int len2 = arr2.size(); if(m==0) { arr1.assign(arr2.begin(),arr2.end()); return; } if(n==0) return; int i = m - 1; int j = n - 1; int k = m + n - 1; while (i>= 0 && j>= 0) { while (i>= 0 && j>=0 && arr1[i] >= arr2[j]) { arr1[k--] = arr1[i--]; } while (i >= 0 && j >= 0 && arr1[i] <= arr2[j]) { arr1[k--] = arr2[j--]; } } while (i>=0 ) { arr1[k--] = arr1[i--]; } while (j>=0) { arr1[k--] = arr2[j--]; } return ; } };
atoi(str.c_str());
int(char-'0')
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { vector<int> res; public: void dfs(TreeNode* root) { if(!root) return; dfs(root->left); res.push_back(root->val); dfs(root->right); } vector<int> inorderTraversal(TreeNode* root) { if(!root) return res; dfs(root); return res; } };
看不懂爲何這麼作
解法一徹底沒有用到查找二叉樹的性質,暴力嘗試了全部可能從而形成了重複。咱們能夠利用一下查找二叉樹的性質。左子樹的全部值小於根節點,右子樹的全部值大於根節點。
因此若是求 1…n 的全部可能。
咱們只須要把 1 做爲根節點,[ ] 空做爲左子樹,[ 2 … n ] 的全部可能做爲右子樹。
2 做爲根節點,[ 1 ] 做爲左子樹,[ 3…n ] 的全部可能做爲右子樹。
3 做爲根節點,[ 1 2 ] 的全部可能做爲左子樹,[ 4 … n ] 的全部可能做爲右子樹,而後左子樹和右子樹兩兩組合。
4 做爲根節點,[ 1 2 3 ] 的全部可能做爲左子樹,[ 5 … n ] 的全部可能做爲右子樹,而後左子樹和右子樹兩兩組合。
…
n 做爲根節點,[ 1… n ] 的全部可能做爲左子樹,[ ] 做爲右子樹。
至於,[ 2 … n ] 的全部可能以及 [ 4 … n ] 以及其餘狀況的全部可能,能夠利用上邊的方法,把每一個數字做爲根節點,而後把全部可能的左子樹和右子樹組合起來便可。
若是隻有一個數字,那麼全部可能就是一種狀況,把該數字做爲一棵樹。而若是是 [ ],那就返回 null。
做者:windliang
連接:https://leetcode-cn.com/problems/unique-binary-search-trees-ii/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by-2-7/
來源:力扣(LeetCode)
著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
class Solution { public: vector<TreeNode*> helper(int start,int end){ vector<TreeNode*> ret; if(start > end) ret.push_back(nullptr); for(int i=start;i<=end;i++){ vector<TreeNode*> left = helper(start,i-1); vector<TreeNode*> right = helper(i+1,end); for(auto l : left){ for(auto r : right){ TreeNode* root = new TreeNode(i); root -> left = l; root -> right = r; ret.push_back(root); } } } return ret; } vector<TreeNode*> generateTrees(int n) { vector<TreeNode*> ret; if(n == 0) return ret; ret = helper(1,n); return ret; } }; 做者:black-hole 連接:https://leetcode-cn.com/problems/unique-binary-search-trees-ii/solution/c-30xing-dai-ma-di-gui-by-black-hole/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
能正確判斷,可是超時了,由於遞歸的效率比較低;
對於字符串的題,使用遞歸都比較耗時
class Solution { private: int len1; int len2; int len3; public: bool dfs(int i, int j, int k, string& s1, string &s2, string &s3) { if(i==len1 && j==len2 && k==len3) return true; if(i<len1 && j<len2) { if(s2[j]!=s3[k]&&s1[i]==s3[k]) { return dfs(i+1,j,k+1,s1,s2,s3); } if(s1[i]!=s3[k]&&s2[j]==s3[k]) { return dfs(i,j+1,k+1,s1,s2,s3); } if(s1[i]==s3[k]&&s2[j]==s3[k]) { return dfs(i+1,j,k+1,s1,s2,s3)||dfs(i,j+1,k+1,s1,s2,s3); } if(s1[i]!=s3[k]&&s2[j]!=s3[k]) { return false; } } if(i==len1 && j<len2) { if(s2[j]==s3[k]) return dfs(i,j+1,k+1,s1,s2,s3); else{return false;} } if(i<len1 && j==len2) { if(s1[i]==s3[k]) return dfs(i+1,j,k+1,s1,s2,s3); else{return false;} } } bool isInterleave(string s1, string s2, string s3) { len1 = s1.size(); len2 = s2.size(); len3 = s3.size(); if(len1+len2 != len3) return false; if(s1.empty()&&s2.empty()&&s3.empty()) return true; return dfs(0,0,0,s1,s2,s3); } };
寫了一堆不正確的代碼,四層遞歸
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { public: //用來遍歷,得到全部節點下的值 void dfs1(TreeNode* root, vector<int>& res) { if(!root) return; res.push_back(root->val); dfs1(root->left,res); dfs1(root->right,res); } //用來判斷每一個節點做爲根節點時候是否符合標準 bool dfs2(TreeNode* root, vector<int> r1, vector<int> r2) { if(r1.empty() && r2.empty()) return true; if(!r1.empty() && r2.empty()) { for(auto i:r1) { if(i>=root->val)return false; } } if(r1.empty() && !r2.empty()) { for(auto i:r2) { if(i<=root->val)return false; } } for(auto i:r1) { if(i>=root->val)return false; } for(auto i:r2) { if(i<=root->val)return false; } return true; } //對每個進行判斷 bool df3(TreeNode* root) { vector<int> left; dfs1(root->left,left); vector<int> right; dfs1(root->right,right); return dfs2(root,left,right); } //遍歷並返回判斷結果 void df4(TreeNode* root, bool& flag) { if(!root) return; if(!df3(root)) flag = false; isValidBST(root->left); isValidBST(root->right); } bool isValidBST(TreeNode* root) { if(!root) return true; bool flag=true; df4(root,flag); if(flag==true)return true; return false; } };
以前的思路是判斷兩百邊的數組,可是得到數組就須要進行遍歷,十分麻煩,下面的解法目的不是求節點是否在左右節點數組的中間,而是每一個節點和一個區間進行比較,這個區間一開始是無限大,每通過一個節點就將區間範圍縮小,往左節點走時父節點做爲有邊界,往右邊界走時父節點做爲左邊界,這樣能夠不斷的將父節點的區間範圍傳遞下去,十分巧妙
思路:引入上下邊界
對於樹的每一個節點 val ,設其上下邊界 low , high。(用 long 防止 INT_MAX 溢出 )
判斷根結點時,須知足 low < val < high ,不然返回 false
判斷左節點時,僅 上界 變化 ( 新上界爲 high 與 val 較小值。又因 val 必小於 high,故新上界爲 val )
判斷右節點時,僅 下界 變化 ( 同理,新下界爲 val )
做者:penn-10
連接:https://leetcode-cn.com/problems/validate-binary-search-tree/solution/cyu-yan-yan-zheng-er-cha-sou-suo-shu-by-penn-10/
來源:力扣(LeetCode)
著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
bool fun(struct TreeNode* root, long low, long high) { if (root == NULL) return true; long num = root->val; if (num <= low || num >= high) return false; return fun(root->left, low, num) && fun(root->right, num, high); } bool isValidBST(struct TreeNode* root){ return fun(root, LONG_MIN, LONG_MAX); } 做者:penn-10 連接:https://leetcode-cn.com/problems/validate-binary-search-tree/solution/cyu-yan-yan-zheng-er-cha-sou-suo-shu-by-penn-10/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { public: bool isSameTree(TreeNode* p, TreeNode* q) { //一種適用性很強的辦法,都通過層序遍歷,比較最後的結果,但這確定不是最優解法 if(p==NULL && q==NULL)return true; if(p==NULL && q!= NULL)return false; if(p!=NULL && q==NULL)return false; if(p->val != q->val)return false; return isSameTree(p->left,q->left)&&isSameTree(p->right,q->right); } };
易錯點時三個條件不能少,並且注意對稱時左節點的左孩子和右節點的右孩子相比
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { public: bool dfs(TreeNode* root1, TreeNode* root2) { if(root1==NULL && root2==NULL){return true;} if(root1==NULL && root2!=NULL){return false;} if(root1!=NULL && root2==NULL){return false;} if(root1->val!=root2->val){return false;} return dfs(root1->left,root2->right) && dfs(root1->right,root2->left); } bool isSymmetric(TreeNode* root) { if(root==NULL){return true;} return dfs(root->left,root->right); } };
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { private: vector<vector<int> > res; public: void dfs(vector<TreeNode*> cur) { if(cur.size()==0){return;} vector<int> temp; vector<TreeNode*> next; for(auto i:cur) { temp.push_back(i->val); if(i->left!=NULL){next.push_back(i->left);} if(i->right!=NULL){next.push_back(i->right);} } res.push_back(temp); dfs(next); } vector<vector<int>> levelOrder(TreeNode* root) { if(root==NULL){return res;} vector<TreeNode*> cur; cur.push_back(root); dfs(cur); return res; } };
使用隊列的方法
class Solution { public: vector<vector<int>> levelOrder(TreeNode* root) { vector<vector<int>> res; vector<int> level; if(root == nullptr) return res; queue<TreeNode*> que; que.push(root); que.push(nullptr); TreeNode *cur = nullptr; while(que.size()){ cur = que.front(); que.pop(); if(cur){ level.push_back(cur->val); if(cur->left) que.push(cur->left); if(cur->right) que.push(cur->right); }else{ res.push_back(level); level.resize(0); if(que.size() > 0) que.push(nullptr); } } return res; } };
印象中是使用堆棧的,可是直接用一個bool量來標記也很方便
還能夠正常層序遍歷,而後隔一個倒敘一下就能夠,和1007同樣,都是遍歷以後對數組作處理
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { private: vector<vector<int> > res; bool flag; public: void dfs(vector<TreeNode*> cur) { if(cur.size()==0){return;} vector<TreeNode*> next; vector<int> temp; for(auto i:cur) { if(i->left!=NULL){next.push_back(i->left);} if(i->right!=NULL){next.push_back(i->right);} temp.push_back(i->val); } if(!flag){reverse(temp.begin(),temp.end());} flag = !flag; res.push_back(temp); dfs(next); } vector<vector<int>> zigzagLevelOrder(TreeNode* root) { if(root==NULL){return res;} vector<TreeNode*> temp; temp.push_back(root); flag = 1; dfs(temp); return res; } };
有一種很簡單的寫法,不少處會用到
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { public: int maxDepth(TreeNode* root) { if(root==NULL)return 0; return max( maxDepth( root->left) , maxDepth( root->right) )+1; } };
通常最終值放到private處定義,中間每次須要用到的參量放入遞歸的參數列表
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { private: int max; public: void dfs(TreeNode* root, int num) { if(root->left==NULL&&root->right==NULL){return;} if(root->left!=NULL) { num++; if(max<num){max=num;} dfs(root->left,num); num--; } if(root->right!=NULL) { num++; if(max<num){max=num;} dfs(root->right,num); num--; } } int maxDepth(TreeNode* root) { max=0; if(root==NULL){return max;} max=1; dfs(root,1); return max; } };
主要問題是代碼寫的太複雜了,可是思路是正確的,這裏的遞歸函數須要設置成返回節點型,注意這種的最後仍是要吧頭節點返回來,否則第一個遞歸函數就沒有返回值了
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { private: public: TreeNode* dfs(vector<int>& preorder, vector<int>& inorder) { if(preorder.size()==0){return NULL;} if(preorder.size()==1) { TreeNode* temp = new TreeNode(preorder[0]); return temp; } int mid = find(inorder.begin(),inorder.end(),preorder[0])-inorder.begin(); TreeNode* root= new TreeNode(preorder[0]); vector<int> p1(preorder.begin()+1,preorder.begin()+ mid+1); vector<int> q1(inorder.begin() ,inorder.begin()+ mid); root->left = dfs(p1,q1); vector<int> p2(preorder.begin()+mid+1, preorder.end()); vector<int> q2(inorder.begin()+mid+1 ,inorder.end()); root->right =dfs(p2,q2); return root; } TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) { if(preorder.size()==0){return NULL;} TreeNode* root = dfs(preorder,inorder); return root; } };
在遞歸的過程當中傳遞了不少參數,代碼量減小
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { public: TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) { int pos = 0; return buildTree(preorder, pos, inorder, 0, inorder.size()-1); } TreeNode* buildTree(vector<int>& preorder, int& pos, vector<int>& inorder, int left, int right) { if (pos >= preorder.size()) return 0; int i = left; for (i = left; i <= right; ++i) { if (inorder[i] == preorder[pos]) break; } TreeNode* node = new TreeNode(preorder[pos]); if (left <= i-1) node->left = buildTree(preorder, ++pos, inorder, left, i-1); // 左子樹 if (i+1 <= right) node->right = buildTree(preorder, ++pos, inorder, i + 1, right); // 右子樹 return node; } }; 做者:yuexiwen 連接:https://leetcode-cn.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/solution/c-fen-zhi-by-yuexiwen/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { public: TreeNode* dfs(vector<int>& inorder,vector<int>& postorder) { if(inorder.size()==0){return NULL;} if(inorder.size()==1) { TreeNode* temp = new TreeNode(inorder[0]); return temp; } int mid = find(inorder.begin(), inorder.end(), postorder[postorder.size()-1] )- inorder.begin(); TreeNode* root = new TreeNode(inorder[mid]); vector<int> l1(inorder.begin(), inorder.begin()+mid); vector<int> l2(postorder.begin(), postorder.begin()+mid); root->left = dfs(l1, l2); vector<int> r1(inorder.begin()+mid+1, inorder.end()); vector<int> r2(postorder.begin()+mid, postorder.end()-1); root->right = dfs(r1,r2); return root; } TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) { if(postorder.size()==0){return NULL;} TreeNode* root = dfs(inorder,postorder); return root; } };
徹底能夠從上到下遍歷,而後拿一個堆棧存放,或者直接返回reverse的結果?!不是更簡單,看來在使用堆棧的倒敘功能時,用reverse可能更方便
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { private: stack<vector<int>> res; public: void dfs(vector<TreeNode*> cur) { if(cur.size()==0){return;} vector<int> temp; vector<TreeNode*> next; for(auto i:cur) { if(i->left!=NULL){next.push_back(i->left);} if(i->right!=NULL){next.push_back(i->right);} temp.push_back(i->val); } res.push(temp); dfs(next); } vector<vector<int>> levelOrderBottom(TreeNode* root) { vector<TreeNode*> temp; temp.push_back(root); vector<vector<int>> r; if(root==NULL){return r;} dfs(temp); while(!res.empty()) { r.push_back(res.top()); res.pop(); } return r; } };
dfs方法
class Solution { public: TreeNode* sortedArrayToBST(vector<int>& nums) { return bst(nums,0,nums.size()-1); } TreeNode* bst(vector<int>& a,int l,int r){ if(l>r) return NULL; int mid=(r-l)/2+l; TreeNode* root=new TreeNode(a[mid]); root->left=bst(a,l,mid-1); root->right=bst(a,mid+1,r); return root; } }; 做者:dai-52 連接:https://leetcode-cn.com/problems/convert-sorted-array-to-binary-search-tree/solution/cdi-gui-jie-fa-by-dai-52-2/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
轉成數組是一種方法,另一種是使用快慢指針,把中間的找出來,這部分放到dfs最開始的地方
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ /** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { public: TreeNode* dfs(vector<int>& nums, int left, int right) { if(left>right) return NULL; int mid = left+(right-left)/2; auto root = new TreeNode(nums[mid]); root->left = dfs(nums,left,mid-1); root->right = dfs(nums,mid+1,right); return root; } TreeNode* sortedListToBST(ListNode* head) { //其實跟上一題區別不大,只是數組換成了鏈表,能夠把鏈表轉換成數組,而後和上題同樣 vector<int> v; while(head){v.push_back(head->val);head=head->next;} int len = v.size(); return dfs(v,0,len-1); } };
有考慮的方法是寫一個這個節點下面深度的函數,而後遞歸每個節點的左右節點進行判斷,可是以前求最大深度的代碼就寫的很複雜
下面這個求深度的代碼十分簡單,不用進行num的維護每次返回值加1就能夠了,代筆層數,這道題雙遞歸函數十分巧妙
三個條件放再一塊兒判斷會更簡便,不過傳入單個節點會稍微比較難理解,爲何到了葉節點就返回true了
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { public: bool isBalanced(TreeNode* root) { if(!root) return true; int d = abs(depth(root->left)-depth(root->right)); //當前節點的左右子樹的高度差 return (d<=1) && (isBalanced(root->left)) && (isBalanced(root->right)); } // 求解二叉樹深度(104題) int depth(TreeNode* node) { if(node ==NULL) return 0; return max( depth(node->left), depth(node->right) )+1; } }; 做者:fly_sky 連接:https://leetcode-cn.com/problems/balanced-binary-tree/solution/c-ban-ben-dai-ma-jian-ji-by-fly_sky/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
形式基本上是仿照最大深度來的,可是有一點不同,要注意對最小深度的定義,不是出現null就行,而是必須出現葉節點,即自己存在可是左右孩子不存在的,這是惟一的狀況,因爲root可能會存在左右孩子爲null的狀況,因此不能將左右孩子直接輸入,而是將根節點輸入。
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { public: int minnn(TreeNode* root) { if(!root->left && !root->right){return 0;} if(root->left && !root->right){return minnn(root->left)+1;} if(!root->left && root->right){return minnn(root->right)+1;} return min(minnn(root->left),minnn(root->right))+1; } int minDepth(TreeNode* root) { if(!root){return 0;} return minnn(root)+1; } };
要注意幾點:
1.當根節點爲空,其路徑和不存在而不是爲空,0和空在這裏是不同的,以前這個地方就錯過
2.這題的終止條件是到葉節點,因此終結條件(正+負)必須是關於葉節點的
3.不管是最大最小仍是其餘,邏輯關係都體如今dfs的返回方式的邏輯關係上
4.sum這種變化的值能夠做爲參數
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { public: bool dfs(TreeNode* root, int sum) { if(!root->left && !root->right) { if(root->val==sum)return true; return false; } if(!root->left && root->right)return dfs(root->right,sum-root->val); if(!root->right && root->left)return dfs(root->left,sum-root->val); return dfs(root->left,sum-root->val) || dfs(root->right,sum-root->val); } bool hasPathSum(TreeNode* root, int sum) { if(!root)return false; return dfs(root,sum); } };
難點是如何應對何時存節點,何時放節點的問題,這個能夠根據例子倒推一下
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { private: vector<vector<int>> res; vector<int> path; public: void dfs(TreeNode* root,int sum) { if(!root->left && !root->right) { path.push_back(root->val); if(root->val==sum) res.push_back(path); path.pop_back(); return; } if(root->left) { path.push_back(root->val); dfs(root->left,sum-root->val); path.pop_back(); } if(root->right) { path.push_back(root->val); dfs(root->right,sum-root->val); path.pop_back(); } } vector<vector<int>> pathSum(TreeNode* root, int sum) { if(!root)return res; dfs(root,sum); return res; } };
先先序遍歷展開爲一列,而後變成鏈表,先序遍歷的代碼時固定的,就像求最大深度那樣要記住
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { private: vector<int> list; public: //先序遍歷 void dfs(TreeNode* root) { if(!root ) return ; list.push_back(root->val); dfs(root->left); dfs(root->right); } void dfs2(TreeNode* root,int pos, vector<int> list) { if(pos==list.size()) return; TreeNode* temp = new TreeNode(list[pos]); root->left = NULL; root->right = temp; dfs2(temp,pos+1,list); } void flatten(TreeNode* root) { //作一下層序遍歷,求出數組,而後都放到右孩子上 if(!root)return; dfs(root); dfs2(root,1,list); return; } };
關於字符串的題使用遞歸由超時了,可是思路應該時對的:把t中的每一個字符在s中的位置求出來並組成一個二維數組,裏面存放每一個t中字符的位置,而後對這個dfs遞歸計數,在值小於前一個個時須要進行剪枝,這個遞歸和求全部根節點到子節點時的模板很是像
class Solution { public: void dfs(int& num,vector<int>& path, int pos, vector<vector<int>>& board) { if(path.size()==board.size()) { num++; return; } for(int i:board[pos]) { if( pos==0 || (pos>0 && i>path[path.size()-1]) ) { path.push_back(i); dfs(num,path,pos+1,board); path.pop_back(); } } } int numDistinct(string s, string t) { if(s.empty())return 0; int lens = s.size(); int lent = t.size(); vector<vector<int>> board; for(int i = 0 ; i<lent ; i++) { vector<int> temp; for(int j = 0; j<lens ; j++) { if(s[j]==t[i]) { temp.push_back(j); } } board.push_back(temp); } int num = 0; vector<int> path; int pos = 0; dfs(num,path,pos,board); return num; } };
dfs是深度優先遍歷,有前序中序後序,參數爲根節點;bfs是廣度優先遍歷,沒有什麼序之說,在遍歷是後習慣用一個數組存放每一層的全部節點或者節點值,參數爲數組
一般bfs用非遞歸隊列比較多,可是用遞歸也能夠較好的解決一些問題,自由的對結果進行調整,可是隊列很差拿出來
/* // Definition for a Node. class Node { public: int val; Node* left; Node* right; Node* next; Node() : val(0), left(NULL), right(NULL), next(NULL) {} Node(int _val) : val(_val), left(NULL), right(NULL), next(NULL) {} Node(int _val, Node* _left, Node* _right, Node* _next) : val(_val), left(_left), right(_right), next(_next) {} }; */ class Solution { vector<vector<Node*>> res; public: void bfs(vector<Node*> cur) { if(cur.empty())return; vector<Node*> temp; for(auto i:cur) { if(i->left)temp.push_back(i->left); if(i->right)temp.push_back(i->right); } bfs(temp); res.push_back(temp); } Node* connect(Node* root) { if(!root) return NULL; vector<Node*> cur; cur.push_back(root); res.push_back(cur); bfs(cur); for(auto i : res) { if(i.size()==1){continue;} int m = i.size(); for(int j = 0 ; j< m-1; j++) { i[j]->next=i[j+1]; } } return root; } };
和116的代碼一摸同樣
class Solution { public: vector<vector<int>> generate(int numRows) { vector<vector<int>> res; if(numRows==0) return res; vector<int> t = {1}; res.push_back(t); if(numRows==1) return res; t.push_back(1); res.push_back(t); if(numRows==2) return res; numRows -=2; vector<int> cur = {1,1}; while(numRows--) { vector<int> temp; temp.push_back(1); int len = cur.size(); for(int i = 0 ; i<len-1 ; i++) { temp.push_back(cur[i]+cur[i+1]); } temp.push_back(1); cur.assign(temp.begin(),temp.end()); res.push_back(cur); } return res; } };
class Solution { public: vector<int> getRow(int numRows) { vector<int> t = {1}; if(numRows==0) return t; t.push_back(1); if(numRows==1) return t; numRows -=1; vector<int> cur = {1,1}; while(numRows--) { vector<int> temp; temp.push_back(1); int len = cur.size(); for(int i = 0 ; i<len-1 ; i++) { temp.push_back(cur[i]+cur[i+1]); } temp.push_back(1); cur.assign(temp.begin(),temp.end()); } return cur; } };
額外的空間也能夠不使用,可是要修改原數據,不太好,因此就用了額外的 n*sizeof(int)的空間。
line用於記錄n層到n-1層最短的路徑長度,其初始值爲triangle的第n行。其遞歸公式爲:
line[j] = triangle[i][j] + min(line[j], line[j + 1]),i 表示當前自下向上到達第 i 層。
line[j]=triangle[i][j]+min(line[j],line[j+1]),i表示當前自下向上到達第i層。
做者:liushang-leecode
連接:https://leetcode-cn.com/problems/triangle/solution/dong-tai-gui-hua-zi-di-xiang-shang-ke-yi-shi-yong-/
來源:力扣(LeetCode)
著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
class Solution { public: int minimumTotal(vector<vector<int>>& triangle) { int n = triangle.size(); vector<int> line(triangle[n - 1]); for (int i = n - 2; i >= 0; i--) for (int j = 0; j <= i; j++) line[j] = triangle[i][j] + min(line[j], line[j + 1]); return line[0]; } }; 做者:liushang-leecode 連接:https://leetcode-cn.com/problems/triangle/solution/dong-tai-gui-hua-zi-di-xiang-shang-ke-yi-shi-yong-/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
class Solution { public: int maxProfit(vector<int>& prices) { if(prices.empty())return 0; int dp=prices[0],res = 0; for(int i = 1; i< (int)prices.size() ;i++) { dp = min(dp,prices[i]); res = max(res, prices[i]-dp); } return res; } };
一次遍歷法,不是特別好理解
class Solution { public: int maxProfit(vector<int>& prices) { int inf = 1e9; //很大的值 int minprice = inf, maxprofit = 0; for (int price: prices) { maxprofit = max(maxprofit, price - minprice); minprice = min(price, minprice); } return maxprofit; } }; 做者:LeetCode-Solution 連接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/solution/121-mai-mai-gu-piao-de-zui-jia-shi-ji-by-leetcode-/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
class Solution { public: int maxProfit(vector<int>& prices) { int len = prices.size(); if(len==0 || len==1)return 0; int minvalue = prices[0]; int maxvalue = INT_MIN; for(int i = 1 ; i<len ; i++) { minvalue = min(minvalue,prices[i-1]); maxvalue = max(maxvalue,prices[i]-minvalue); } if(maxvalue<0)return 0; return maxvalue; } };
動態規劃法
class Solution { public: int maxProfit(vector<int>& prices) { int n = prices.size(); if (n == 0) return 0; // 邊界條件 int minprice = prices[0]; vector<int> dp (n, 0); //新建一個數組 //在動態變化中維護了最小值,老是能得到這個點以前的最小值 //最後原本不用再和本身比較了,可是題目有一個特殊要求,就是不能小於0,因此再生成這個動態數組的時候,每次都要和0比較,若是小於0就不要了 for (int i = 1; i < n; i++){ minprice = min(minprice, prices[i]); dp[i] = max(dp[i - 1], prices[i] - minprice); } return dp[n - 1]; } }; 做者:z1m 連接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/solution/gu-piao-wen-ti-python3-c-by-z1m/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
class Solution { public: int maxProfit(vector<int>& prices) { int res = 0; for(int i=1; i< (int)prices.size() ;i++) { res += (prices[i]>prices[i-1])?(prices[i]-prices[i-1]):0; } return res; } };
這道題不適合動態規劃,能夠用貪心結合雙指針作
不用非等到上升到最高點纔買,只要上升一次就買一次,低了就不動
class Solution { public: int maxProfit(vector<int>& prices) { //快慢指針標記 int i=0,j=1,sum=0; //判斷vector大小 if(prices.size()<=1) return 0; //兩個指針一直緊挨在一塊兒,只有j的值大於i的值時,就把差值加入sum中 while(j<prices.size()){ if(prices[j]-prices[i]>0) sum+=(prices[j]-prices[i]); i++; j++; } return sum; } }; 做者:zhong-zhong-ban-da-dui-chang 連接:https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/solution/ctan-xin-suan-fa-by-zhong-zhong-ban-da-dui-chang/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
是在上一題的基礎上作的,可是次數再多就不行了,並且這種雙循環方式也超時了,可是思路是對的
class Solution { public: int maxProfit(vector<int>& prices) { if(prices.empty())return 0; int res1 = 0, dp1 = prices[0], res = 0; for(int i =1 ; i< (int)prices.size() ;i++) { dp1 = min(dp1, prices[i]); res1 = max(res1, prices[i]-dp1); int dp2 = prices[i]; int res2 = 0; for(int j = i+1 ; j<(int)prices.size() ;j++) { dp2 = min(dp2, prices[j]); res2 = max(res2, prices[j]-dp2); } res = max(res, res1+res2); } return res; } };
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { public: //一個 根,左,右 的路徑狀況有(根,左+根,根+右,左+根+右) //可是該根做爲其父親的孩子時,其max 應該爲 max(根,左+根,根+右).若是包含左+根+右,則加上其父親就構不成路徑了 int maxPathSum(TreeNode* root) { long long gMax = LLONG_MIN; maxPathSumCore(root, gMax); return gMax; } int maxPathSumCore(TreeNode* root, long long& gMax){ if(root==nullptr) return 0; int curVal = root->val; int leftMax = maxPathSumCore(root->left, gMax); int rightMax = maxPathSumCore(root->right, gMax); long long curMax = max(curVal, max(curVal+leftMax, max(curVal+rightMax, curVal+leftMax+rightMax))); if(curMax>gMax) gMax = curMax; return max(curVal, max(curVal+leftMax, curVal+rightMax)); } }; 做者:lou-ding-_shai-tai-yang 連接:https://leetcode-cn.com/problems/binary-tree-maximum-path-sum/solution/di-gui-dang-qian-jie-dian-de-zui-da-lu-jing-he-dan/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
很簡便的作法,稍難理解
class Solution { public: int longestConsecutive(vector<int>& nums) { if(nums.empty()){ return 0; } unordered_set<int> myset(nums.begin(), nums.end()); int res = 0; for(auto num : nums){ if(myset.count(num-1)==0){ int x = num + 1; while(myset.count(x)){ x ++; } res = max(res, x-num); } } return res; } }; 做者:anyaleung 連接:https://leetcode-cn.com/problems/longest-consecutive-sequence/solution/cdai-ma-li-yong-unordered_set-by-anyaleung/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
int num = 0 ; string s = "sdfsf"; num = std::atoi(s.c_str());
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { private: vector<string> res; string s; public: void dfs(TreeNode* root) { if(!root->left && !root->right) { s = s + to_string(root->val); res.push_back(s); s.pop_back(); } if(root->left) { s = s + to_string(root->val); dfs(root->left); s.pop_back(); } if(root->right) { s = s + to_string(root->val); dfs(root->right); s.pop_back(); } } int sumNumbers(TreeNode* root) { if(!root)return 0; dfs(root); int sum = 0; for(auto i:res) { sum += atoi(i.c_str()); } return sum; } };
本身寫的遞歸十分的複雜,主要問題在於,在判斷的過程當中就把不是本位置的元素給改了,致使路徑上發生變化,看了解答,發現是思路錯了,或者說這道題逆向思惟一下很是好作,就像數學選擇題裏面的舉反例同樣
被圍繞的區間不會存在於邊界上,換句話說,任何邊界上的 ‘O’ 都不會被填充爲 ‘X’。 任何不在邊界上,或不與邊界上的 ‘O’ 相連的 ‘O’ 最終都會被填充爲 ‘X’。若是兩個元素在水平或垂直方向相鄰,則稱它們是「相連」的。
做者:ZZYuting
連接:https://leetcode-cn.com/problems/surrounded-regions/solution/cbeats-9465easy-to-understand-by-zzyuting/
來源:力扣(LeetCode)
著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
/* First, check the four border of the matrix. If there is a element is 'O', alter it and all its nei***or 'O' elements to '1'. Then ,alter all the 'O' to 'X' At last,alter all the '1' to 'O' For example: X X X X X X X X X X X X X X O X -> X X O X -> X X X X X O X X X 1 X X X O X X X O X X X 1 X X X O X X */ class Solution { public: void solve(vector<vector<char>>& board) { if(board.size()==0){ return; } int rows=board.size(),cols=board[0].size(); //對邊界上的進行dfs for(int i=0;i<rows;i++){ dfs(board,i,0); dfs(board,i,cols-1); } for(int j=1;j<cols-1;j++){ dfs(board,0,j); dfs(board,rows-1,j); } //遍歷所有的,1變0,其餘爲X,很巧妙的先把這些量都標出來來了 for(int i=0;i<rows;i++){ for(int j=0;j<cols;j++){ if(board[i][j]=='1'){ board[i][j]='O'; } else{ board[i][j]='X'; } } } } private: void dfs(vector<vector<char>>& board,int i,int j){ //若是既不在邊界上並且又爲X的直接跳過,當此邊界上的點爲0時纔有動做 //若是在邊界上爲O,就直接改爲1 if(i>=0&&i<board.size()&&j>=0&&j<board[0].size()&&board[i][j]=='O'){ board[i][j]='1'; dfs(board,i-1,j); dfs(board,i+1,j); dfs(board,i,j-1); dfs(board,i,j+1); } } }; 做者:ZZYuting 連接:https://leetcode-cn.com/problems/surrounded-regions/solution/cbeats-9465easy-to-understand-by-zzyuting/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
在這裏插入代碼片
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode(int x) : val(x), left(NULL), right(NULL) {} * }; */ class Solution { private: vector<int> res; public: void dfs(TreeNode* root) { if(root==NULL)return; res.push_back(root->val); dfs(root->left); dfs(root->right); } vector<int> preorderTraversal(TreeNode* root) { dfs(root); return res; } };
使用哈希map和雙鏈表:
邏輯
// key 映射到 Node(key, val) HashMap<Integer, Node> map; // Node(k1, v1) <-> Node(k2, v2)... DoubleList cache; int get(int key) { if (key 不存在) { return -1; } else { 將數據 (key, val) 提到開頭; return val; } } void put(int key, int val) { Node x = new Node(key, val); if (key 已存在) { 把舊的數據刪除; 將新節點 x 插入到開頭; } else { if (cache 已滿) { 刪除鏈表的最後一個數據騰位置; 刪除 map 中映射到該數據的鍵; } 將新節點 x 插入到開頭; map 中新建 key 對新節點 x 的映射; } }
較簡化的版本
class LRUCache { public: //刪除、查找、插入的複雜度都O(1) LRUCache(int capacity) { cap=capacity; } int get(int key) { if(hashtable.count(key)==0) return -1; else//更新到表頭 { auto iter = hashtable[key];//找到對應結點地址 cache.splice(cache.begin(),cache,iter);//移動到鏈表頭 return cache.begin()->second;//返回value } } void put(int key, int value) { if(hashtable.count(key)==0) { //若是容量到了,刪除尾部元素,再加上新結點 if(cache.size()==cap) { hashtable.erase(cache.back().first); cache.pop_back(); }//直接添加 cache.push_front(make_pair(key,value)); hashtable[key]=cache.begin(); } else {//若是插入相同key的元素,除了要移動,value也須要更新 auto iter = hashtable[key]; iter->second=value;//更新地址value cache.splice(cache.begin(),cache,iter);//移動到鏈表頭 hashtable[key]=cache.begin(); //更新hashtable的value } } unordered_map<int,list<pair<int,int>>::iterator> hashtable; list<pair<int,int>> cache;//key value int cap=0; }; /** * Your LRUCache object will be instantiated and called as such: * LRUCache* obj = new LRUCache(capacity); * int param_1 = obj->get(key); * obj->put(key,value); */ 做者:tong-guai 連接:https://leetcode-cn.com/problems/lru-cache/solution/ha-xi-shuang-xiang-lian-biao-you-hua-ban-ben-by-to/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
class LRUCache { public: LRUCache(int capacity) { _capacity = capacity; } int get(int key) { if(_map.count(key) != 0) { int ret = (*_map[key]).second; _list.erase(_map[key]); _list.push_front({key, ret}); _map[key] = _list.begin(); return ret; } else return -1; } void put(int key, int value) { if(_map.count(key) == 0) { if(_list.size() == _capacity) { int k = _list.back().first; _map.erase(_map.find(k)); _list.pop_back(); } } else{ _list.erase(_map[key]); _map.erase(_map.find(key)); } _list.push_front({key, value}); _map[key] = _list.begin(); } private: unordered_map<int, list<pair<int, int>>::iterator> _map; list<pair<int, int>> _list; int _capacity; }; /** * Your LRUCache object will be instantiated and called as such: * LRUCache* obj = new LRUCache(capacity); * int param_1 = obj->get(key); * obj->put(key,value); */ 做者:ma-te-zai-wei-ma 連接:https://leetcode-cn.com/problems/lru-cache/solution/lruhuan-cun-ji-zhi-by-ma-te-zai-wei-ma/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
用bool判斷第一個符號而且用一個值儲存結果是很麻煩的事,不如就一直將結果放在堆棧中
class Solution { public: int evalRPN(vector<string>& tokens) { stack<string> st; //bool flag = true;第一次遇到的符號須要特殊處理,用flag來表示是不是第一個 for(auto token:tokens) { if(token=="+") { int t1 =atoi(st.top().c_str()); st.pop(); int t2 = atoi(st.top().c_str()); st.pop(); st.push(to_string(t1+t2)); } else if(token=="-") { int t1 =atoi(st.top().c_str()); st.pop(); int t2 = atoi(st.top().c_str()); st.pop(); st.push(to_string(t2-t1)); } else if(token=="*") { int t1 =atoi(st.top().c_str()); st.pop(); int t2 = atoi(st.top().c_str()); st.pop(); st.push(to_string(t1*t2)); } else if(token=="/") { int t1 =atoi(st.top().c_str()); st.pop(); int t2 = atoi(st.top().c_str()); st.pop(); st.push(to_string(t2/t1)); } else { st.push(token); } } return atoi(st.top().c_str()); } };
解題思路
這題是求數組中子區間的最大乘積,對於乘法,咱們須要注意,負數乘以負數,會變成正數,因此解這題的時候咱們須要維護兩個變量,當前的最大值,以及最小值,最小值可能爲負數,但沒準下一步乘以一個負數,當前的最大值就變成最小值,而最小值則變成最大值了。
class Solution { public: int maxProduct(vector<int>& nums) { int res = INT_MIN , imax = 1, imin =1; for(int i = 0 ; i< (int)nums.size() ;i++) { if(nums[i]<0)swap(imax,imin); imax = max(nums[i],imax*nums[i]); imin = min (nums[i],imin*nums[i]); res = max(res,imax); } return res; } };
class Solution { public: int findMin(vector<int>& nums) { if(nums[0]<=nums.back())return nums[0]; int left = 0 , right = nums.size(); while(left<right) { int mid = left+(right-left)/2; if(nums[mid]>=nums[0]) {left=mid+1;} else {right=mid;} } return nums[left]; } };
使用兩個隊列,可是會超時
class MinStack { private: queue<int> q1; queue<int> q2; public: /** initialize your data structure here. */ MinStack() { } void push(int x) { if(q1.empty()&&q2.empty())q1.push(x); else if(q1.empty())q2.push(x); else if(q2.empty())q1.push(x); } void pop() { if(q1.empty()) { while(q2.size()>1) { q1.push(q2.front()); q2.pop(); } q2.pop(); } else { while(q1.size()>1) { q2.push(q1.front()); q1.pop(); } q1.pop(); } } int top() { int res; if(q1.empty()) { while(q2.size()>1) { q1.push(q2.front()); q2.pop(); } res = q2.front(); q1.push(q2.front()); q2.pop(); } else { while(q1.size()>1) { q2.push(q1.front()); q1.pop(); } res = q1.front(); q2.push(q1.front()); q1.pop(); } return res; } int getMin() { int res = INT_MAX; if(q1.empty()) { while(!q2.empty()) { res = min(res, q2.front()); q1.push(q2.front()); q2.pop(); } } else { while(!q1.empty()) { res = min(res,q1.front()); q2.push(q1.front()); q1.pop(); } } return res; } }; /** * Your MinStack object will be instantiated and called as such: * MinStack* obj = new MinStack(); * obj->push(x); * obj->pop(); * int param_3 = obj->top(); * int param_4 = obj->getMin(); */
一個棧s存放數據,另外一個棧min存放前棧中最小的數據
class MinStack { public: stack<int> s;//數據棧 stack<int> min;//輔助棧 /** initialize your data structure here. */ MinStack() { } void push(int x) { s.push(x); if(min.empty()||x<=min.top()) { min.push(x); } } void pop() { if(s.top()==min.top()) min.pop(); s.pop(); } int top() { return s.top(); } int getMin() { return min.top(); } }; 做者:chenlele 連接:https://leetcode-cn.com/problems/min-stack/solution/zui-xiao-zhan-by-gpe3dbjds1/ 來源:力扣(LeetCode) 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。
直接作是不符合要求的,看到logN應該直接想到使用二分法
class Solution { public: int findPeakElement(vector<int>& nums) { int len =nums.size(); if(len==0 || len ==1) return 0; if(len==2) { int i = nums[0]>nums[1]? 0:1; return i; } for(int i = 1 ; i<len-1 ;i++) { if(nums[i]>nums[i-1] && nums[i]>nums[i+1]) return i; } if(nums[0]>nums[1])return 0; if(nums[len-1]>nums[len-2])return len-1; return INT_MIN; } };
以前作過一道兩數之和的,因此 在這裏面使用了查找法,凡是查找須要遍歷,因此這樣作會超時,更好的辦法是雙指針一個在前一個在後,這裏雙指針法更好的緣由是數組已經排序好了,,能夠有規律的縮小雙指針的範圍
class Solution { public: vector<int> twoSum(vector<int>& numbers, int target) { int len = numbers.size(); for(int i = 0 ;i<len ;i++) { auto iter = find(numbers.begin()+i+1,numbers.end(), target- numbers[i]); if( iter!=numbers.end()) { int temp = iter-numbers.begin(); vector<int> res={i+1,temp+1}; return res; } } return {}; } };
class Solution { public: int majorityElement(vector<int>& nums) { int len = nums.size(); if(len==1) return nums[0]; sort(nums.begin(),nums.end()); return nums[len/2]; } };
一開始想的是把全部數字都變成數組的形式,而後使用sort進行排序,可是會有一個問題,30和3進行比較的話確定會認爲30大而且在前,可是最後303顯然沒有330大,因此這種方法是有問題的
題目解析:根據題目中給的例子來看,主要就是要給給定數組進行排序,可是排序方法不是普通的升序或者降序,由於9要排在最前面,而9既不是數組中最大的也不是最小的,因此咱們要自定義排序方法。對於兩個數字a和b來講,若是將其都轉爲字符串,若是ab > ba,則a排在前面,好比9和34,因爲934>349,因此9排在前面,再好比說30和3,因爲303<330,因此3排在30的前面。按照這種規則對原數組進行排序後,將每一個數字轉化爲字符串再鏈接起來就是最終結果。
————————————————
版權聲明:本文爲CSDN博主「pushup8」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處連接及本聲明。
原文連接:https://blog.csdn.net/pushup8/article/details/85196465
class Solution { public: string largestNumber(vector<int>& nums) { string res = ""; sort(nums.begin(), nums.end(), [](int a, int b){ return to_string(a) + to_string(b) > to_string(b) + to_string(a); }); for(auto num : nums){ res += to_string(num); } return res[0] == '0' ? "0" : res; } };
本身寫的
注意sort函數重寫的四個要素1.static2.inline3.const4.&
class Solution { public: static inline bool func(const string& a, const string& b) { return a+b>b+a; } string largestNumber(vector<int>& nums) { vector<string> st; //st.assign(nums.begin(),nums.end()); for(auto i:nums)st.push_back(to_string(i)); sort(st.begin(),st.end(),func); string res; for(auto i:st)res += i; while( (int)res.size()>1 && res[0]=='0')res = res.substr(1,(int)res.size()-1); return res; } };
class Solution { public: void rotate(vector<int>& nums, int k) { int len = nums.size(); reverse(nums.begin(),nums.end()-k%len