這是在LeetCode中文網上第一次刷題,慢慢來吧,希望堅持一段時間之後能有所收穫。採用的是隨機做題模式,可能序號會有些亂。
845. 數組中最長的山脈- 中等難度
- 題目描述
我們把數組 A 中符合下列屬性的任意連續子數組 B 稱爲 “山脈”:
B.length >= 3
- 存在
0 < i < B.length - 1
使得B[0] < B[1] < ... B[i-1] < B[i] > B[i+1] > ... > B[B.length - 1]
(注意:B 可以是 A 的任意子數組,包括整個數組 A。)
給出一個整數數組
A
,返回最長 “山脈” 的長度。如果不含有 “山脈” 則返回
0
。事例:
輸入:[2,1,4,7,3,2,5]
輸出:5
解釋:最長的山脈是 [1,4,7,3,2],長度時5
輸入:[2,2,2]
輸出:0
解釋:不含山脈
- 解題思路
對數組從前往後遍歷和對數組從後往前遍歷,如果符合山脈要求,就加1,否則直接置爲0,然後再對兩個數組遍歷,符合兩個數組均不爲0的位置就是山峯的位置,然後相加之後再加1,就是最終答案。
- 代碼
class Solution{
public:
int longestMoutain(vector<int>& A){
if(A.size()<3)
return 0;
int left[100010];
int right[100010];
left[0]=0;
for(int i=1;i<A.size();i++){
if(A[i]>A[i-1])
left[i]=left[i-1]+1;
else
left[i]=0;
}
right[A.size()-1]=0;
for(int i=A.size()-2;i>=0;i--){
if(A[i]>A[i+1])
right[i]=right[i+1]+1;
else
right[i]=0;
}
int ans=0;
for(int i=0;i<A.size();i++){
if(left[i]>0 && right[i]>0 && right[i]+left[i]+1>ans)
ans=left[i]+right[i]+1;
}
return ans;
}
};
357. 計算各個位數不同的數字個數- 中等難度
- 題目描述
給定一個非負整數 n,計算各位數字都不同的數字 x 的個數,其中 。
輸入實例:
輸入: 2
輸出: 91
解釋: 答案應爲除去 11,22,33,44,55,66,77,88,99 外,在 [0,100) 區間內的所有數字。
- 解題思路
按照排列組合的思路進行,從最開始的一位,到n位,其實說白了,就是找規律:1,10,10+9 * 9,10+9 * 9 *8
- 代碼
class Solution{ public: int countNumberswitchUniqueDigits(int n){ int res; if(n<=1){ return pow(10,n); }else{ res=10; int temp=9; for(int i=1;i<n;i++) { temp*=(10-i); res +=temp; } return res; } } };
390. 消除遊戲- 中等難度
- 題目描述
給定一個從1 到 n 排序的整數列表。
首先,從左到右,從第一個數字開始,每隔一個數字進行刪除,直到列表的末尾。
第二步,在剩下的數字中,從右到左,從倒數第一個數字開始,每隔一個數字進行刪除,直到列表開頭。
我們不斷重複這兩步,從左到右和從右到左交替進行,直到只剩下一個數字。
返回長度爲 n 的列表中,最後剩下的數字。事例:
輸入 n=9 1 2 3 4 5 6 7 8 9 2 4 6 8 2 6 6 輸出 6
- 解題思路
其實這個題目需要解決的話,思路可以很簡單,就是用for循環來來回回刪除,採用的數據結構可以是鏈表或者其他比較容易刪除的數據結構。但是當數據量比較大的時候就不實用了。
當然這個題目還是很規整,很有規律的,我看了一眼,知道不是用這種暴力的解決辦法,肯定使用比較有技巧的數學公式來解決,但是想了挺久沒有找到一個比較合適的數據公式來解決。
最終還是看了答案,並且看了最後牛逼的證明,感覺也不是很難,高中數列題的水平。但是想在短時間之內做出來也不容易。
- 代碼
Class Solution{ public: int lastRemaining(int n){ if(n==1){ return 1; }else{ /* 單純從左向右剔除數字的結果:f(n) 單純從右向左剔除數字 b(n) f(n)+b(n)=n+1 f(n)=2*b(n/2) f(n)=2*b(n/2)=2*(n/2+1-f(n/2)) */ return 2*(n/2+1-lastRemaining(n/2)); } } };
623. 在二叉樹中增加一行- 中等難度
- 題目描述
給定一個二叉樹,根節點爲第1層,深度爲 1。在其第
d
層追加一行值爲v
的節點。添加規則:給定一個深度值
d
(正整數),針對深度爲d-1
層的每一非空節點N
,爲N
創建兩個值爲v
的左子樹和右子樹。將
N
原先的左子樹,連接爲新節點v
的左子樹;將N
原先的右子樹,連接爲新節點v
的右子樹。如果
d
的值爲 1,深度 d - 1 不存在,則創建一個新的根節點v
,原先的整棵樹將作爲v
的左子樹。
- 解題思路
首先申明一點就是在OJ之類的題目中,遞歸很好用,但是在一般項目之中儘量避免使用遞歸,多用棧或者循環。
這一題兩種解法,一種是比較頭鐵的遍歷,還有一種是遞歸。但是遞歸又非常巧妙,將層數變成flag方便插入。
- 代碼
struct TreeNode{ int val; TreeNode *left; TreeNode *right; TreeNode(int x):val(x),left(NULL),right(NULL){} }; class Solution{ public: TreeNode* addOneRow(TreeNode* root,int v ,int d){ if(!root) return NULL; if(d==1){ //這是新產生的節點作爲根節點,然後原先的二叉樹作爲左子樹 //新節點的建立和節點的插入是需要記住的,其他都是一些思路上的東西 TreeNode* newRoot = new TreeNode(v); newRoot->left=root; return newRoot; } //queue隊列這種方式進行初始化第一次看到,需要注意一下 //二叉樹的遍歷方式是層序遍歷,因此採用的是隊列的方式 /* 這個算法的主體就是一個隊列,然後不斷朝裏面加入新的元素 然後有兩個判斷的過程,一個是d=0還有一個是d=1,只要d>1就不斷往隊列裏面放東西 如果d=1就將新的一層插入 如果d=0就直接返回最終的頭結點,也就是結果 */ queue<TreeNode*>q{{root}}; while(!q.empty()){ if(--d==0)return root; int n=q.size(); for(int i=0;i<n;++i){ auto t= q.front(); q.pop(); if(d==1){ TreeNode *left=t->left; TreeNode *right=t->right; t->left=new TreeNode(v); t->right=new TreeNode(v); t->left->left=left; t->right->right=right; }else{ if(t->left) q.push(t->left); if(t->right) q.push(t->right); } } } return root; } };
上面這種是逐層遍歷的方案,解決問題來不是很難懂,但是不夠牛逼,下面介紹一種牛逼的,遞歸方案
class Solution{ public: TreeNode* addOneRow(TreeNode* root,int v,int d){ /* d變成了一個flag用來指示插入左節點還是右節點 */ if(d==1 || d==0){ TreeNode* newRoot =new TreeNode(v); (d ? newRoot->left : newRoot->right)=root; return newRoot; } if(root && d>1){ root->left =addOneRow(root->left,v,d>2 ?d-1 :1); root->right =addOneRow(root->right,v,d>2 ?d-1 :0); } return root; } };
81. 搜索旋轉排序數組II -中等難度
- 題目描述
假設按照升序排序的數組在預先未知的某個點上進行了旋轉。
( 例如,數組
[0,0,1,2,2,5,6]
可能變爲[2,5,6,0,0,1,2]
)。編寫一個函數來判斷給定的目標值是否存在於數組中。若存在返回
true
,否則返回false
。事例:
輸入: nums = [2,5,6,0,0,1,2], target = 0
輸出: true
- 思路解析
這一題本來是非常簡單的,就是求一個數組裏是否有相應的值,但是有一個要求就是,需要在符合要求的時間複雜度內找到,直接遍歷不合適。因此採用二分法,充分利用數組的特點。
- 代碼
class Solution { public: bool search(vector<int>& A, int target) { // write your code here int low = 0, high = A.size()-1, size = A.size(), mid = 0; if(size <= 0 ) { return 0; } while(low <= high) { mid = low + (high - low) / 2; if(A[mid] == target) { return 1; } if(A[mid] > A[low]) { if(A[low] <= target && target < A[mid]) { high = mid - 1; } else { low = mid + 1; } } else if(A[mid] < A[low]){ if(A[mid] < target && target <= A[high]) { low = mid + 1; } else { high = mid - 1; } } else { low++; } } return 0; } };
104. 二叉樹的最大深度 -簡單難度
- 題目描述
給定一個二叉樹,找出其最大深度。
二叉樹的深度爲根節點到最遠葉子節點的最長路徑上的節點數。
說明: 葉子節點是指沒有子節點的節點。
示例:
給定二叉樹[3,9,20,null,null,15,7]
,返回最大深度:3
- 思路
這一題需要應用遞歸的方法來求,其實就是假裝你已經求到了最終的結果,然後需要將其相加得到最終的結果。
- 代碼
/* struct TreeNode{ int val; TreeNode* right; TreeNode* left; TreeNode(int x):val(x),left(NULL),right(NULL){} }; */ class Solution{ public: int maxDepth(TreeNode* root){ int depth=0; if(root==nullptr) { return 0; }else{ depth++; int sub_tree_max_depth = max(maxDepth(root->right),maxDepth(root->left)); depth + = sub_tree_max_depth; } return depth; } };
75. 顏色分類 – 中等難度
- 題目描述
給定一個包含紅色、白色和藍色,一共 n 個元素的數組,原地對它們進行排序,使得相同顏色的元素相鄰,並按照紅色、白色、藍色順序排列。
此題中,我們使用整數 0、 1 和 2 分別表示紅色、白色和藍色。
注意:
不能使用代碼庫中的排序函數來解決這道題。示例:
輸入:[2,0,2,1,1,0]
輸出:[0,0,1,1,2,2]
進階:
- 一個直觀的解決方案是使用計數排序的兩趟掃描算法。
首先,迭代計算出0、1 和 2 元素的個數,然後按照0、1、2的排序,重寫當前數組。- 你能想出一個僅使用常數空間的一趟掃描算法嗎?
- 思路分析
這一題第一眼看上去好像還挺簡單,但是按照題目要求加上一些限制條件之後就沒那麼簡單了,主要是問題是兩個一個是空間複雜度是一個常數,這就說明不能多擴展新的的空間來提升排序的效率,還有一個就是一趟就能得出結果的方法並其排序算法不能是代碼庫裏面的。
我看到這些限制條件之後,第一個想法就是用計數法來進行排序,新建一個數組(保證空間複雜度是常數),然後將計數的結果都賦給新的數組一般只要一趟(最後做下來好像需要兩趟,還是不夠優化)。
後來看網上大佬的解答,驚爲天人,太有創意了,其中一個大佬的做法是將三個數的排序看成是三個指針,然後依次向後進行遍歷,如果遇到符合條件的就向後++ ,0,1,2分別給定三個優先級。
- 代碼
class Solution{ void sortColors(vector<int>& nums){ int i,j,k=-1; for(int m=0;m<nums.size();m++){ if(nums[m]==0){ nums[++k]=2; nums[++j]=1; nums[++i]=0; }else if(nums[m]==1){ nums[++k]=2; nums[++j]=1; }else{ nums[++k]=2; } } } };
522. 最長特殊序列 II -中等難度
- 題目描述
給定字符串列表,你需要從它們中找出最長的特殊序列。最長特殊序列定義如下:該序列爲某字符串獨有的最長子序列(即不能是其他字符串的子序列)。
子序列可以通過刪去字符串中的某些字符實現,但不能改變剩餘字符的相對順序。空序列爲所有字符串的子序列,任何字符串爲其自身的子序列。
輸入將是一個字符串列表,輸出是最長特殊序列的長度。如果最長特殊序列不存在,返回 -1 事例:
輸入:“aba”,“cbc”,“eae”
輸出:3
提示:
- 所有給定的字符串長度不會超過10
- 給定字符串列表長度將在[2,50]之間
- 解題思路
這道題題目首先沒看懂,後來看網上解答發現其實就是比較一堆字符串中是不是有其中的子字符串,有的話把最長子字符串的長度輸出.
所以首先有一種簡單的方法將所有字符串進行對比,如果發現存在子字符串就保存一個結果,最終獲取最長子字符串的長度。
還有一種思路就是需要將這一堆字符串進行排序,然後進行比對,最終首先比對出的結果,就是最終的結果。
具體的:
首先給字符串按長度排序,然後將長度大的放在前面,這樣找到非共同子序列,直接返回其長度就行。因爲當前找到的肯定是最長的,然後用集合來記錄已經遍歷過的字符串,對於當前遍歷到的字符串,我們和集合中所有的字符串進行對比,看是否是某個字符串的子字符串,說明當前就是最長的非公共字符,如果當前的字符串是集合中某個字符串的子序列就直接break出來。
- 代碼
class Solution{ public: int findLUSlength(vector<string>& strs ){ int res=-1,j=0,n=strs.size(); for(int i=0;i<n;++i){ for(j=0;j<n;++j){ if(i==j) continue; if(checkSub(strs[i],strs[j])) break; } if(j==n) res=max(res,(int)strs[i].size()); } return res; } //判斷sub是否是str的子序列 int checkSub(string sub,string str){ int i=0; for(char c:str){ if(c==subs[i]) ++i; if(i==subs.size()) break; } return i==subs.size(); } };
方法二:
class Solution{ int findLUSlength(vector<string>& strs){ int n=strs.size(); unordered_set<string> s; sort(strs.begin(),strs.end(),[](string a,string b){ if(a.size()==b.size()) return a>b; return a.size() > b.size(); }); for(int i=0;i<n;i++){ if(i==n-1 || strs[i]!=strs[i+1]){ bool found =true; for(auto a:s){ int j=0; for(char c:a){ if(c==strs[i][j]) ++j; if(j==strs[i].size()) break; } if(j==strs[i].size()){ found =false; break; } } if(found) return strs[i].size(); } s.insert(strs[i]); } return -1; } };
95. 不同的二叉搜索樹 II- 中等難度
- 題目描述
給定一個整數 n,生成所有由 1 … n 爲節點所組成的二叉搜索樹。
輸入:3
輸出:
[
[1,null,3,2],
[3,2,null,1],
[3,1,null,null,2],
[2,1,3],
[1,null,2,null,3]
]
- 解題思路:
這一題參考的也是網上的,大家也都是千篇一律,就是用的遞歸,或者有的參考code ganker上的代碼。主題思路就是將
1-n
的數字劃分成兩個部分,,i
作爲根節點,一個是小於i
的數值全部作爲左子樹,一個是大於i
的部分全部作爲右子樹。左子樹的生成通過調用構造函數,右子樹的生成通過調用生成函數,但是有一個比較困惑的地方就是需要進行左右兩個子樹進行循環遍歷,將其接到根結點上。
- 代碼
struct TreeNode{ int val; TreeNode * left; TreeNode * right; TreeNode(int x):val(x),left(NULL),right(NULL){} }; class Solution{ public: vector<TreeNode *>createTrees(int start,int end){ vector<TreeNode* >res; if(start>end) { res.push_back(NULL); return res; } if(start== end){ TreeNode * root = new TreeNode(start); res.push_back(root); return res; } for(int i=start;i<=end;i++){ vector<TreeNode* >left= createTrees(start,i-1); vector<TreeNode* >right= createTrees(i+1,end); for(int j=0;j<left.size();++j){ for(int k=0;k<right.size();++k){ TreeNode *root = new TreeNode(i); root->left= left[j]; root->right= right[k]; res.push_back(root); } } } return res; } vector<TreeNode *> generateTrees(int n){ if(n<1) return (vector<TreeNode *>)NULL; return createTrees(1,n); } };
330 . 按要求補齊數組 -困難
- 題目描述
給定一個已排序的正整數數組 *nums,*和一個正整數 *n 。*從
[1, n]
區間內選取任意個數字補充到 nums 中,使得[1, n]
區間內的任何數字都可以用 nums 中某幾個數字的和來表示。請輸出滿足上述要求的最少需要補充的數字個數。事例:
輸入: nums = [1,3], n = 6
輸出: 1
解釋:
根據 nums 裏現有的組合 [1], [3], [1,3],可以得出 1, 3, 4。
現在如果我們將 2 添加到 nums 中, 組合變爲: [1], [2], [3], [1,3], [2,3], [1,2,3]。
其和可以表示數字 1, 2, 3, 4, 5, 6,能夠覆蓋 [1, 6] 區間裏所有的數。
所以我們最少需要添加一個數字。
- 解題思路
這道題,有些懵逼,主要的想法就是貪心算法,從兩個方面去加和,一個是
nums[i]
,一個是1-n
的數字進行加和,然後只要提供一次1-n
的數字,就統計一次需要補充的數字。
- 代碼
class Solution{ public: int minPatchess(vector<int>& nums, int n){ int i=0,count=0; long long maxsum=0; while(maxsum<n){ if(i<nums.size() && nums[i]<maxsum+1){ maxsum=maxsum+nums[i]; i++; }else{ maxsum+=(maxsum+1); count++; } } return count; } };
331. 驗證二叉樹的前序序列化 - 中等
- 題目描述
序列化二叉樹的一種方法是使用前序遍歷。當我們遇到一個非空節點時,我們可以記錄下這個節點的值。如果它是一個空節點,我們可以使用一個標記值記錄,例如
#
。例如,上面的二叉樹可以被序列化爲字符串
"9,3,4,#,#,1,#,#,2,#,6,#,#"
,其中#
代表一個空節點。給定一串以逗號分隔的序列,驗證它是否是正確的二叉樹的前序序列化。編寫一個在不重構樹的條件下的可行算法。
每個以逗號分隔的字符或爲一個整數或爲一個表示
null
指針的'#'
。你可以認爲輸入格式總是有效的,例如它永遠不會包含兩個連續的逗號,比如
"1,,3"
。事例:
輸入: “9,3,4,#,#,1,#,#,2,#,6,#,#”
輸出: true
- 解題思路
可以採用棧的方法,因爲搜索二叉樹,是右子樹大於根大於左子樹的元素,因此把數組進行壓棧,將根節點壓入棧,往後遍歷,只要遇到的數字比棧頂元素小,就說明是左子樹的節點,繼續壓棧,如果遇到的數字比棧頂元素大,就是右子樹的元素了,然後我們更新最小值,並且刪除棧頂元素,繼續往後遍歷,如果還是大於 ,就繼續刪除棧頂元素,如果棧空了或者當前棧頂元素大於當前值就停止,並且壓入當前值,如果當前值小於最小值就報錯。(資料來源:[LeetCode] Verify Preorder Sequence in Binary Search Tree 驗證二叉搜索樹的先序序列)
還有一個思路是,看規律,因爲開頭不可能是
#
,通過觀察規律是,數組的數目和 數組中的數字個數的關係是 :n=num_n*2 +1
- 代碼:
class Solution{ public: bool verifyPreorder(vector<int> & preorder){ stack<int>s; int min_num = INT_MIN; for(auto node : preorder){ if(node<min_num) return false; while(!s.empty() && node > s.top()){ min_num = s.top(); s.pop(); } s.push(node); } return true; } };
class Solution{ public: bool verifyPreorder(string preorder){ preorder = preorder+","; int count = 1; for(int i=0;i<preorder.size();i++){ if(preorder[i] ==',') continue; if((--count)<0) return false; if(preorder[i-1]!='#') count+=2; } return count==0; } };
101.對稱二叉樹- 簡單
- 題目描述
給定一個二叉樹,檢查是否鏡像對稱
比如二叉樹[1,2,2,3,4,4,3]就是對稱的
多利用迭代和遞歸
- 解題思路
這一題思路上不難,看的很清楚,就是對比對稱位置的數值是否相等,可以用遞歸來做也可以用棧來做,思路也比較好想,但是在實際寫的時候。出現了一些問題,主要還是思路不夠清晰吧。
- 代碼
class Solution{ public: bool isSymmetric(TreeNode * root){ if(!root) return true; } private: bool Dis_symmetric(TreeNode* left, TreeNode * right){ if(!left && !right) return true; if(!left || !right) return false; return (left->val == right->val) && Dis_symmetric(left->left,right->right) && Dis_symmetric(left->right,right->left);1 } };
棧的方法:
class Solution{ public: bool isSymmetric(TreeNode* root){ if(!root) return true; stack<TreeNode *>s; TreeNode *p= root->left,*q = root->right; s.push(p); s.push(q); while(!s.empty()){ p=s.top();s.pop(); q=s.top();s.pop(); if(!p && !q) continue; if(!p || !q) return false; if(p->val != q->val) return false; s.push(p->left);s.push() } } };
728. 自除數 - 簡單
- 題目描述
自除數 是指可以被它包含的每一位數除盡的數。
例如,128 是一個自除數,因爲
128 % 1 == 0
,128 % 2 == 0
,128 % 8 == 0
。還有,自除數不允許包含 0 。
給定上邊界和下邊界數字,輸出一個列表,列表的元素是邊界(含邊界)內所有的自除數。
- 解題思路
這題沒啥特別的,就是直接將數字拆開,然後除掉就行了。
- 代碼
class Solution { public: vector<int> selfDividingNumbers(int left, int right) { vector<int> res; for (int i=left;i<=right;i++) { if (self_div(i)) { res.push_back(i); } } return res; } bool self_div(int n) { int boss = n; while (n!=0) { if (n % 10 !=0 && boss%( n % 10) == 0) { n = n / 10; }else { return false; } } if (n==0) { return true; } } };
856. 括號的分數- 中等
- 題目描述
給定一個平衡括號字符串
S
,按下述規則計算該字符串的分數:
()
得 1 分。AB
得A + B
分,其中 A 和 B 是平衡括號字符串。(A)
得2 * A
分,其中 A 是平衡括號字符串。比如:
輸入: “(()(()))”
輸出:6
- 解題思路
這一題的思路和括號匹配挺像,第一想法就是用棧來作爲匹配的容器,然後第二個想法就是如何將不同的指代特徵表示出來。因此需要給出兩個棧,一個用來存儲括號的下標,還有一個表示現在的計算數值。
思路:
- 維護一個棧和一個數組,棧用來存放’('的位置,數組中用來存放其對應的位置的值
- 把每一個’(‘的位置,也就是其中
s
中的下標,存放在棧中,當來了一個’)‘時,取出棧頂的’(‘的下標,將從棧’(‘的下標開始到’)‘的位置之間的所有value的值相加,存放在value的’)'的位置- 相加過的位置全部置爲0
- 最後相加整個value數組的值即可
- 代碼
class Solution{ int scoreOfParentheses(string s){ int len = s.size(),res=0; stack<int> Hash; vector<int>value(len,0); for(int i=0;i<len;i++){ int sum=0; if(s[i]='(') Hash.push(i); else{ if(int n.Hash.top();n<i;n++){ sum += value[n]; value[n]=0; } Hash.pop(); sum*=2; value[i]= sum==0 ?1:sum; } } for(int n:value){ res+=n; } return res; } };