本文主要爲
LeetCode
刷題學習筆記。
核心要點
集合
集合裏的元素類型不一定相同。
集合裏的元素沒有順序。
列表
是一種數據項構成的有限序列,即按照一定的線性順序,排列而成的數據項的集合。
列表最常見的表現形式有數組和鏈表,而棧和隊列則是兩種特殊類型的列表。
數組
數組與列表的區別在於索引。索引是數組中的一個重要概念,列表中沒有索引這個概念。在大多數編程語言中,索引是從0
算起的。
數組中元素在內存中也是連續存儲的。
數組中有基本的四種操作:
- 讀取元素
nums[i] // 讀取第數組中的第i個元素。
- 查找元素
s1.find(s2,position) //在S1中查找子串S2, position示從從這個位置開始的字符串中找指定元素,默認爲0。
//返回值爲目標字符的位置,當沒有找到目標字符時返回npos。
- 插入元素
s1.insert(2,"123") // 下標爲2處插入“123”。
- 刪除元素
s1.erase(1,3) // 刪除從1開始,長度爲3的子串
除此之外還有:
s1.append(s2,1,3) // 添加s2下標從1開始,長度爲3的字符串到s1。
s1.swap(s2) // 交換s1,s2.
s1.assign(4,"K") // s1賦值爲“KKKK"
編程實例
一維數組
合併區間
給出一個區間的集合,請合併所有重疊的區間。
示例 1:
輸入: [[1,3],[2,6],[8,10],[15,18]]
輸出: [[1,6],[8,10],[15,18]]
解釋: 區間 [1,3] 和 [2,6] 重疊, 將它們合併爲 [1,6].
示例 2:
輸入: [[1,4],[4,5]]
輸出: [[1,5]]
解釋: 區間 [1,4] 和 [4,5] 可被視爲重疊區間。
- 實現思想
思想也就很簡單,首先按照二維數組裏面的一維數組的第一個元素排序,這個時候比較上一個一維數組的末尾元素(比如[1,3]
中的3
)與下一個一維數組的第一個元素(比如[2,6]
中的2
)大小,然後判斷是否要合併。
- C++實現
class Solution {
public:
static bool cmp(vector<int> num1, vector<int> num2){
return num1[0]<num2[0];
}
vector<vector<int>> merge(vector<vector<int>>& intervals) {
if(intervals.size()<2) return intervals;
sort(intervals.begin(), intervals.end(),cmp);
int n = intervals.size();
vector<vector<int>> ans;
ans.push_back(intervals[0]);
for(int i=1; i<n; ++i){
if(ans.back()[1] >= intervals[i][0]){
ans.back()[1] = max(ans.back()[1], intervals[i][1]);
} else {
ans.push_back(intervals[i]);
}
}
return ans;
}
};
這裏要注意:
static bool cmp(vector<int> num1, vector<int> num2){
return num1[0]<num2[0];
之所以要加static
的原因是:使用了函數指針,而函數指針所指函數須得是靜態纔行。如果不加的話,報錯如下:
fatal error: reference to non-static member function must be called
二維數組
旋轉圖像
給定一個 n × n 的二維矩陣表示一個圖像。
將圖像順時針旋轉 90 度。
說明:
你必須在原地旋轉圖像,這意味着你需要直接修改輸入的二維矩陣。請不要使用另一個矩陣來旋轉圖像。
示例 1:
給定 matrix =
[
[1,2,3],
[4,5,6],
[7,8,9]
],
原地旋轉輸入矩陣,使其變爲:
[
[7,4,1],
[8,5,2],
[9,6,3]
]
示例 2:
給定 matrix =
[
[ 5, 1, 9,11],
[ 2, 4, 8,10],
[13, 3, 6, 7],
[15,14,12,16]
],
原地旋轉輸入矩陣,使其變爲:
[
[15,13, 2, 5],
[14, 3, 4, 1],
[12, 6, 8, 9],
[16, 7,10,11]
]
- 思路
我們首先需要對其最外層循環,將每個數字旋轉90度,然後進入內層循環,所以我們要循環的外層數就是N/2(for(int r=0; r<n/2;++r)
);當確定了循環的層數之後,我們需要進入內層循環,內層循環的起始位置s=r
,內層循環的終止位置從右邊往左邊看就是N-1-r
。
接下來就是需要確定各個元素的位置了,剛開始起始位置的元素座標是(r,i)
。因爲是按照順時針旋轉90度,所以逆時針旋轉的座標爲(end-(i-s), r)
,(end, end-(i-s))
, (i, end)
。
- C++實現
class Solution {
public:
void rotate(vector<vector<int>>& matrix) {
int n = matrix.size();
for(int r=0; r<n/2;++r){
int s = r;
int end = n-1-r;
for(int i=s; i<end; i++){
int temp = matrix[r][i];
matrix[r][i] = matrix[end-(i-s)][r];
matrix[end-(i-s)][r] = matrix[end][end-(i-s)];
matrix[end][end-(i-s)] = matrix[i][end];
matrix[i][end] = temp;
}
}
}
};
對角線遍歷
給定一個含有 M x N 個元素的矩陣(M 行,N 列),請以對角線遍歷的順序返回這個矩陣中的所有元素,對角線遍歷如下圖所示。
示例:
輸入:
[
[ 1, 2, 3 ],
[ 4, 5, 6 ],
[ 7, 8, 9 ]
]
輸出: [1,2,4,7,5,3,6,8,9]
解釋:
說明:
給定矩陣中的元素總數不會超過 100000 。
- 思路解析
我們可以知道每一條斜線上的座標之和爲一個固定的值,從0
到m+n
。如果我們以這個和作爲大循環遍歷的話(for(int s=0; s<=m+n;s++)
),我們只需要去確定行或者列中的一個,我們就可以通過總和s減去行或者列得到另一個。以行爲例,如果我們知道行座標r
的範圍,那麼s-r
就是縱座標的值。
那現在的問題就是如何確定行座標的值。
當s
小於行座標的總數時,行座標的最大值爲s
,否者爲m = matrix.size()-1
。程序裏面表示爲int max_r = min(s, m);
,(對應第0列邊界)。
當s
很大時,大到被最後一列限制的時候,此時行座標的最小值爲s-n
,否者爲0。程序裏面表示爲int min_r = max(0, s-n)
,(對應第最後一列邊界)。
- C++實現
class Solution {
public:
vector<int> findDiagonalOrder(vector<vector<int>>& matrix) {
vector<int> ans;
if (matrix.empty()) return ans;
int m = matrix.size()-1;
int n = matrix[0].size()-1;
for(int s=0; s<=m+n;s++){
int min_r = max(0, s-n);
int max_r = min(s, m);
if(s%2==1){
for(int r=min_r; r<=max_r; ++r){
ans.push_back(matrix[r][s-r]);
}
} else {
for(int r=max_r; r>=min_r; --r){
ans.push_back(matrix[r][s-r]);
}
}
}
return ans;
}
};
字符串簡介
最長公共前綴
編寫一個函數來查找字符串數組中的最長公共前綴。
如果不存在公共前綴,返回空字符串 “”。
示例 1:
輸入: ["flower","flow","flight"]
輸出: "fl"
示例 2:
輸入: ["dog","racecar","car"]
輸出: ""
解釋: 輸入不存在公共前綴。
說明:
所有輸入只包含小寫字母 a-z
。
- 解題思路
先從字符串數組中取出第一個字符strs[0]
,之後從前往後取出子字符,for(int i=0;i<=strs[0].length();++i)
,sub_tem = strs[0].substr(0, i);
,拿到的子字符與剩餘字符一一對比,如果能找到且返回的座標是0,也就是從頭開始就匹配到了,那就說明這個字符滿足要求,否者循環就沒必要進行下去,已經不滿足要求了。
如果到最後一個都能找到的話,就說明當前這個子字串是最長公共前綴。
- C++實現
class Solution {
public:
string longestCommonPrefix(vector<string>& strs) {
int n = strs.size();
string ans = "";
if(n==0) return ans;
if(n==1) return strs[0];
string sub_tem;
for(int i=0;i<=strs[0].length();++i){
sub_tem = strs[0].substr(0, i);
for(int j=1;j< strs.size();++j){
if(strs[j].find(sub_tem)!=0) break;
if(j==strs.size()-1) ans.swap(sub_tem);
}
}
return ans;
}
};
翻轉字符串裏的單詞
給定一個字符串,逐個翻轉字符串中的每個單詞。
示例 1:
輸入: "the sky is blue"
輸出: "blue is sky the"
示例 2:
輸入: " hello world! "
輸出: "world! hello"
解釋: 輸入字符串可以在前面或者後面包含多餘的空格,但是反轉後的字符不能包括。
示例 3:
輸入: "a good example"
輸出: "example good a"
解釋: 如果兩個單詞間有多餘的空格,將反轉後單詞間的空格減少到只含一個。
說明:
無空格字符構成一個單詞。
輸入字符串可以在前面或者後面包含多餘的空格,但是反轉後的字符不能包括。
如果兩個單詞間有多餘的空格,將反轉後單詞間的空格減少到只含一個。
進階:
請選用 語言的用戶嘗試使用 額外空間複雜度的原地解法。
雙指針
移除元素
給你一個數組nums
和一個值 val
,你需要 原地 移除所有數值等於val
的元素,並返回移除後數組的新長度。
不要使用額外的數組空間,你必須僅使用 額外空間並 原地 修改輸入數組。
元素的順序可以改變。你不需要考慮數組中超出新長度後面的元素。
示例 1:
給定 nums = [3,2,2,3], val = 3,
函數應該返回新的長度 2, 並且 nums 中的前兩個元素均爲 2。
你不需要考慮數組中超出新長度後面的元素。
示例 2:
給定 nums = [0,1,2,2,3,0,4,2], val = 2,
函數應該返回新的長度 5, 並且 nums 中的前五個元素爲 0, 1, 3, 0, 4。
注意這五個元素可爲任意順序。
你不需要考慮數組中超出新長度後面的元素。
- 解題思路
設定兩個快慢指針slow
和fast
,slow
指針從0
開始,fast
指針隨着循環從0
到n
(數組長度)。當遇到非val
值時,fast
元素賦值到slow
,並且++slow
,爲下一個賦值做準備。當循環結束,slow
的值就是非val
值的個數。
- C++實現
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int n = nums.size();
int slow=0;
for(int fast=0;fast<n;++fast){
if(nums[fast]!=val){
nums[slow]=nums[fast];
++slow;
}
}
return slow;
}
};