(一)两数之和
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int, int> map; // <nums[i], i>
for(int i = 0; i < nums.size(); i ++){
auto iter = map.find(target - nums[i]);
if(iter != map.end()) return {iter -> second, i};
else map.insert(pair<int, int>(nums[i], i));
}
return {};
}
};
(二)字母异位词分组
class Solution {
public:
vector<vector<string>> groupAnagrams(vector<string>& strs) {
unordered_map<string, vector<string>> result;
for(int i = 0; i < strs.size(); i ++){
string tempstr = strs[i];
sort(tempstr.begin(), tempstr.end());
result[tempstr].push_back(strs[i]);
}
vector<vector<string>> out;
for(const auto& pair: result){
out.push_back(pair.second);
}
return out;
}
};
(三)最长连续序列
class Solution {
public:
int longestConsecutive(vector<int>& nums) {
if(nums.size() == 0) return 0;
sort(nums.begin(), nums.end());
int result = 1;
int slow = 0;
int flag = 0;
for(int i = 1; i < nums.size(); i ++){
int count = 1; // 每一次进入新的一组连续序列,都重置count
// 统计累计的连续长度以及其中重复的次数
while(i < nums.size() && (nums[i] == nums[i - 1] + 1 || nums[i] == nums[i - 1])){
count ++;
if(nums[i] == nums[i - 1]) flag ++;
i ++;
}
// 记录下当前最长的连续序列
result = max(result, count - flag);
// 进入下一个序列的统计,flag清0,slow表示下一个序列的开始位置
flag = 0;
slow = i;
}
return result;
}
};
(四)移动零
class Solution {
public:
void moveZeroes(vector<int>& nums) {
int slow = 0;
for(int i = 0; i < nums.size(); i ++){
if(nums[i] != 0){
nums[slow] = nums[i];
slow ++;
}
}
for(int i = slow; i < nums.size(); i ++){
nums[i] = 0;
}
}
};
(五)盛最多水的容器
class Solution {
public:
int maxArea(vector<int>& height) {
// 因为两条线相差越远,水的宽度越大,所以从两边向中间试探
// 因为水高度是由最短的边决定的,所以移动最短边,向中间找到一条比他高的边,计算面积是否更大
int left = 0;
int right = height.size() - 1;
int result = 0;
while(left != right){
// 这里是线,所以宽度不需要减1
result = max(result, (right - left) * min(height[left], height[right]));
if(height[left] < height[right]){
left ++;
}else{
right --;
}
}
return result;
}
};
(六)三数之和
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> result;
sort(nums.begin(), nums.end());
for(int i = 0; i < nums.size(); i ++){
if(i > 0 && nums[i] == nums[i - 1]) continue;
int left = i + 1;
int right = nums.size() - 1;
while(left < right){
if(nums[i] + nums[left] + nums[right] < 0) left ++;
else if(nums[i] + nums[left] + nums[right] > 0) right --;
else{
result.push_back({nums[i], nums[left], nums[right]});
while(left < right && nums[left] == nums[left + 1]) left ++;
while(left < right && nums[right] == nums[right - 1]) right --;
left ++;
right --;
}
}
}
return result;
}
};
(七)接雨水
class Solution {
public:
int trap(vector<int>& height) {
stack<int> st;
st.push(0);
int result = 0;
for(int i = 1; i < height.size(); i ++){
if(height[i] < height[st.top()]) st.push(i);
else if(height[i] == height[st.top()]){
st.pop();
st.push(i);
}else{
while(!st.empty() && height[i] > height[st.top()]){
int mid = st.top();
st.pop();
// 这里pop出去后要判断栈内是否还有元素,就是mid左边是否有比他高的元素
if(!st.empty()){
int h = min(height[i], height[st.top()]) - height[mid];
int w = i - st.top() - 1;
result += h * w;
}
}
st.push(i);
}
}
return result;
}
};
(八)无重复字符的最长子串
class Solution {
public:
int lengthOfLongestSubstring(string s) {
int slow = 0;
int result = 0;
unordered_set<char> set; // 因为不确定长度,所以用set
for(int i = 0; i < s.size(); i ++){
if(set.find(s[i]) == set.end()){
set.insert(s[i]);
result = max(result, i - slow + 1);
}
else{ // 之前已经存在该字符,将left左移,并删除对应的元素,直到set中不存在当前字符
while(set.find(s[i]) != set.end()){
// unordered_set删除元素,erase
set.erase(s[slow]);
slow ++;
}
// 将当前字符插入set中
set.insert(s[i]);
}
}
return result;
}
};
(九)找到字符串中所有字母异位词
class Solution {
public:
vector<int> findAnagrams(string s, string p) {
if(p.size() > s.size()) return {};
vector<int> result;
vector<int> hashs(26);
vector<int> hashp(26);
for(int i = 0; i < p.size(); i ++){
hashp[p[i] - 'a'] ++;
}
int left = 0;
for(int i = 0; i < s.size(); i ++){
hashs[s[i] - 'a'] ++;
if(i >= p.size() - 1){
// 如果vector里面的元素类型是简单类型(内置类型),可以直接使用“==”或者“!=”进行比较
if(hashp == hashs) result.push_back(left);
hashs[s[left] - 'a'] --;
left ++;
}
}
return result;
}
};
(十)和为 K 的子数组
class Solution {
public:
int subarraySum(vector<int>& nums, int k) {
// 前缀和
// 因为是连续非空序列,确定其元素总和,pre[j] - pre[i]
// 用map记录下前面所有的前缀和,直接查找pre[j] - k
// 出现的次数不止一次,用map记录某个前缀和出现的次数
unordered_map<int, int> map;
vector<int> pre(nums.size(), 0);
int result = 0;
int sum = 0;
map[0] = 1; // 从0开始的连续数组和为k,那么pre[-1] = 0;
for(int i = 0; i < nums.size(); i ++){
sum += nums[i]; // 记录当前前缀和
if(map.find(sum - k) != map.end()) result += map[sum - k];
map[sum] ++;
}
return result;
}
};
(十一)滑动窗口最大值
class Solution {
public:
deque<int> que;
void push(int value){
// 将队尾所有比当前元素小的都清理掉
while(!que.empty() && value > que.back()){
que.pop_back();
}
que.push_back(value);
}
void pop(int value){
if(!que.empty() && que.front() == value){
que.pop_front();
}
}
int getMaxValue(){
return que.front();
}
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
vector<int> result;
for(int i = 0; i < k; i ++){
push(nums[i]);
}
result.push_back(getMaxValue());
for(int i = k; i < nums.size(); i ++){
push(nums[i]);
pop(nums[i - k]);
result.push_back(getMaxValue());
}
return result;
}
};
(十二)最小覆盖子串
class Solution {
public:
string minWindow(string s, string t) {
unordered_map<char, int> smap;
unordered_map<char, int> tmap;
for(int i = 0; i < t.size(); i ++){
tmap[t[i]] ++;
}
int left = 0;
int count = 0;
int minLenth = INT_MAX;
string res;
for(int i = 0; i < s.size(); i ++){
smap[s[i]] ++;
// 记录s中有效的增加
if(tmap.find(s[i]) != tmap.end() && smap[s[i]] <= tmap[s[i]]) count ++;
// 有效字母等于t的长度
if(count == t.size()){
// 缩减左指针,直到找到最短的子串
while(left < i && (tmap.find(s[left]) == tmap.end() || smap[s[left]] > tmap[s[left]])){
// 注意这里左指针右移的时候要将map中对应的value减小
smap[s[left]] --;
left ++;
}
if(i - left + 1 < minLenth){
// 更新最短子串长度
minLenth = i - left + 1;
// substr(开始下表,子串长度)
res = s.substr(left, i - left + 1);
}
// 跳出当前找到的子串(去掉第一个字母),注意要改变相应的count、map值
smap[s[left]] --;
left ++;
count --;
}
}
return res;
}
};
(十三)最大子数组和
贪心法
class Solution {
public:
int maxSubArray(vector<int>& nums) {
int count = 0;
int result = INT_MIN;
for(int i = 0; i < nums.size(); i ++){
count += nums[i];
result = max(result, count);
if(count < 0) count = 0;
}
return result;
}
};
动态规划法
class Solution {
public:
int maxSubArray(vector<int>& nums) {
if(nums.size() == 1) return nums[0];
vector<int> dp(nums.size());
dp[0] = nums[0];
int result = nums[0];
for(int i = 1; i < nums.size(); i ++){
dp[i] = max(nums[i], dp[i - 1] + nums[i]);
result = max(result, dp[i]);
}
return result;
}
};
(十四)合并区间
class Solution {
public:
static bool cmp(const vector<int>& a, const vector<int>& b){
return a[0] < b[0];
}
vector<vector<int>> merge(vector<vector<int>>& intervals) {
sort(intervals.begin(), intervals.end(), cmp);
vector<vector<int>> result;
result.push_back(intervals[0]);
for(int i = 1; i < intervals.size(); i ++){
if(intervals[i][0] > result[result.size() - 1][1]){
result.push_back(intervals[i]);
}else{
result[result.size() - 1][1] = max(result[result.size() - 1][1], intervals[i][1]);
}
}
return result;
}
};
(十五)轮转数组
class Solution {
public:
void rotate(vector<int>& nums, int k) {
k = k % nums.size();
reverse(nums.begin(), nums.end());
reverse(nums.begin(), nums.begin() + k);
reverse(nums.begin() + k, nums.end());
}
};
(十六)除自身以外数组的乘积
class Solution {
public:
vector<int> productExceptSelf(vector<int>& nums) {
vector<int> preffix(nums.size());
vector<int> suffix(nums.size());
vector<int> result(nums.size());
preffix[0] = nums[0];
suffix[nums.size() - 1] = nums[nums.size() - 1];
// 前缀和的思想,再同样思想求一个后缀和,除自身以外的乘积就是这两部分的乘积
for(int i = 1; i < nums.size(); i ++){
preffix[i] = preffix[i - 1] * nums[i];
}
for(int j = nums.size() - 2; j >= 0; j --){
suffix[j] = suffix[j + 1] * nums[j];
}
result[0] = suffix[1];
result[nums.size() - 1] = preffix[nums.size() - 2];
for(int i = 1; i < nums.size() - 1; i ++){
result[i] = preffix[i - 1] * suffix[i + 1];
}
return result;
}
};
(十七)缺失的第一个正数
class Solution {
public:
int firstMissingPositive(vector<int>& nums) {
for(int i = 0; i < nums.size(); i ++){
// 将nums[i] 交换到它正确的位置
// 交换时注意nums[i]是否符合交换范围,要将其交换到下表为nums[i] - 1处
// 就要nums[i] - 1 >= 0 并且 < nums.size()
// 同时为了放置交换出现死循环,要求交换位置的数和原来的数不相同
while(nums[i] != i + 1 && nums[i] > 0 && nums[i] <= nums.size() && nums[i] != nums[nums[i] - 1]){
swap(nums[i], nums[nums[i] - 1]);
}
}
for(int i = 0; i < nums.size(); i ++){
if(nums[i] != i + 1) return i + 1;
}
return nums.size() + 1;
}
};
(十八)矩阵置零
class Solution {
public:
void setZeroes(vector<vector<int>>& matrix) {
int xx, yy;
int flag = 0; // 记录是否找到了为0的元素
// xx,yy记录第一个为0的元素所在的行和列,这这一行和列来标记整个矩阵中0所在的行和列
for(int i = 0; i < matrix.size(); i ++){
for(int j = 0; j < matrix[0].size(); j ++){
if(matrix[i][j] == 0){
xx = i;
yy = j;
flag = 1;
break;
}
}
}
// 根本不存在为0的元素,下面的都不要做
if(flag == 0) return;
// 用xx行和yy列来记录其余行列是否应该清零
for(int i = 0; i < matrix.size(); i ++){
for(int j = 0; j < matrix[0].size(); j ++){
if(matrix[i][j] == 0){
matrix[xx][j] = 0;
matrix[i][yy] = 0;
}
}
}
// 检查记录元素所在的行和列,(除了记录元素外)
// 这里要跳过记录元素,因为如果将其对应的行列全部置0了,之前做的标记就都没用了
// 行中某一元素为0,将其对应的列全部置0
// 列中某一元素为0,将其对应的行全部置0
for(int j = 0; j < matrix[0].size(); j ++){
if(j == yy) continue;
if(matrix[xx][j] == 0){
for(int i = 0; i < matrix.size(); i ++){
matrix[i][j] = 0;
}
}
}
for(int i = 0; i < matrix.size(); i ++){
if(i == xx) continue;
if(matrix[i][yy] == 0){
for(int j = 0; j < matrix[0].size(); j ++){
matrix[i][j] = 0;
}
}
}
// 最后处理标记元素的行列,全部置0
for(int i = 0; i < matrix.size(); i ++){
matrix[i][yy] = 0;
}
for(int j = 0; j < matrix[0].size(); j ++){
matrix[xx][j] = 0;
}
}
};
(十九)螺旋矩阵
(1)套用螺旋矩阵写法,但是这里循环结束的条件、最后没有遍历到的元素都和螺旋举证不一样,细节处理比较麻烦
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
int m = matrix.size();
int n = matrix[0].size();
vector<int> result;
if(m == 1) return matrix[0];
if(n == 1){
for(int i = 0; i < m; i ++){
result.push_back(matrix[i][0]);
}
return result;
}
int size = min(m, n)/2;
int offset = 1;
int i, j;
int starty = 0;
int startx = 0;
int count = 0;
while(size --){
for(j = starty; j < n - offset; j ++){
result.push_back(matrix[startx][j]);
count ++;
}
for(i = startx; i < m - offset; i ++){
result.push_back(matrix[i][j]);
count ++;
}
for(; j > starty; j --){
result.push_back(matrix[i][j]);
count ++;
}
for(; i > startx; i --){
result.push_back(matrix[i][j]);
count ++;
}
starty ++;
startx ++;
offset ++;
}
if(m == n && m % 2 != 0) {
result.push_back(matrix[n/2][n/2]);
}
if(n > m && count < m * n){
for(j = j + 1; j <= n - offset; j ++){
result.push_back(matrix[m/2][j]);
}
}
if(m > n && count < m * n){
for(i = i + 1; i <= m - offset; i ++){
result.push_back(matrix[i][n/2]);
}
}
return result;
}
};
(2) 另外一种写法,比较灵活,每次遍历完一行/一列,动态判断是否需要结束,同时改变边界值。
class Solution {
public:
vector<int> spiralOrder(vector<vector<int>>& matrix) {
if(matrix.empty() || matrix[0].empty()) return {};
vector<int> res;
int m = matrix.size(), n = matrix[0].size();
// 确定上下左右四条边的位置
int up = 0, down = m - 1, left = 0, right = n - 1;
while (true)
{
for (int i = left; i <= right; i++) res.push_back(matrix[up][i]);
if (++up > down) break;
for (int i = up; i <= down; i++) res.push_back(matrix[i][right]);
if (--right < left) break;
for (int i = right; i >= left; i--) res.push_back(matrix[down][i]);
if (--down < up) break;
for (int i = down; i >= up; i--) res.push_back(matrix[i][left]);
if (++left > right) break;
}
return res;
}
};
(二十)旋转图像
class Solution {
public:
void rotate(vector<vector<int>>& matrix) {
// 先沿着左对角线反转,在逐行逆序
for(int i = 0; i < matrix.size(); i ++){
for(int j = i; j < matrix[0].size(); j ++){
swap(matrix[i][j], matrix[j][i]);
}
}
for(int i = 0; i < matrix.size(); i ++){
reverse(matrix[i].begin(), matrix[i].end());
}
}
};
(二十一)搜索二维矩阵 II
class Solution {
public:
bool searchMatrix(vector<vector<int>>& matrix, int target) {
// 搜索法,找一个初始元素,根据target和初始元素的大小关系,移动位置,直到找到为止
// 就是要选择一个点,使得横向和纵向移动,martix能够有不同的变化
// 左上角:往右往下移动,martix值都变大,无法区分,不可用
// 右上角:往左martix变小,往下martix变大,可区分,可用
// 左下角:往右martix变大,往上martix变小,可区分,可用
// 右下角:往左往上移动,martix都变小,不可区分,不可用
int i = 0;
int j = matrix[0].size() - 1;
while(i < matrix.size() && j >= 0){
if(matrix[i][j] == target) return true;
else if(matrix[i][j] < target) i ++;
else if(matrix[i][j] > target) j --;
}
return false;
}
};
(二十二)相交链表
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
int sizeA = 0;
int sizeB = 0;
ListNode* curA = headA;
ListNode* curB = headB;
while(curA){
curA = curA -> next;
sizeA ++;
}
while(curB){
curB = curB -> next;
sizeB ++;
}
curA = headA;
curB = headB;
// 确保curA和sizeA对应的较长的链表
if(sizeB > sizeA){
swap(curA, curB);
swap(sizeA, sizeB);
}
int between = sizeA - sizeB;
while(between --){
curA = curA -> next;
}
while(curA){
if(curA == curB) return curA;
curA = curA -> next;
curB = curB -> next;
}
return NULL;
}
};
(二十三)反转链表
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* first = head;
ListNode* second = NULL;
while(first != NULL){
ListNode* temp = first -> next;
first -> next = second;
second = first;
first = temp;
}
head = second;
return head;
}
};
(二十四)回文链表
class Solution {
public:
bool isPalindrome(ListNode* head) {
ListNode* cur = head;
vector<int> vec;
while(cur){
vec.push_back(cur -> val);
cur = cur -> next;
}
vector<int> temp = vec;
reverse(vec.begin(), vec.end());
return temp == vec;
}
};
(二十五)环形链表
class Solution {
public:
bool hasCycle(ListNode *head) {
ListNode* fast = head;
ListNode* slow = head;
while(fast != NULL && fast -> next != NULL){
fast = fast -> next -> next;
slow = slow -> next;
if(fast == slow) return true;
}
return false;
}
};
(二十六)环形链表 II
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode* fast = head;
ListNode* slow = head;
while(fast != NULL && fast -> next != NULL){
fast = fast -> next -> next;
slow = slow -> next;
if(fast == slow){
ListNode* first = head;
ListNode* second = fast;
// x = (n - 1)(y + z) + z
while(first != second){
first = first -> next;
second = second -> next;
}
return first;
}
}
return NULL;
}
};
(二十七)合并两个有序链表
class Solution {
public:
ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
ListNode* cur1 = list1;
ListNode* cur2 = list2;
ListNode* newHead = new ListNode();
ListNode* pre = newHead;
while(cur1 != NULL && cur2 != NULL){
if(cur1 -> val < cur2 -> val){
pre -> next = cur1;
pre = cur1;
cur1 = cur1 -> next;
}else{
pre -> next = cur2;
pre = cur2;
cur2 = cur2 -> next;
}
}
if(cur1 != NULL){
pre -> next = cur1;
}
if(cur2 != NULL){
pre -> next = cur2;
}
return newHead -> next;
}
};
(二十八)两数相加
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode* cur1 = l1;
ListNode* cur2 = l2;
ListNode* preHead = new ListNode();
ListNode* cur = preHead;
int addNum = 0;
while(cur1 != NULL && cur2 != NULL){
int temp = cur1 -> val + cur2 -> val + addNum;
cur -> next = new ListNode(temp % 10);
cur = cur -> next;
addNum = temp / 10;
cur1 = cur1 -> next;
cur2 = cur2 -> next;
}
while(cur1 != NULL){
int temp = cur1 -> val + addNum;
cur -> next = new ListNode(temp % 10);
addNum = temp / 10;
cur = cur -> next;
cur1 = cur1 -> next;
}
while(cur2 != NULL){
int temp = cur2 -> val + addNum;
cur -> next = new ListNode(temp % 10);
addNum = temp / 10;
cur = cur -> next;
cur2 = cur2 -> next;
}
if(addNum != 0){
cur -> next = new ListNode(addNum);
}
return preHead -> next;
}
};
(二十九)删除链表的倒数第 N 个结点
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
// 使用preHead,使得链表删除第一个节点和删除其余节点是一样的
ListNode* preHead = new ListNode();
preHead -> next = head;
ListNode* fast = preHead;
ListNode* slow = preHead;
while(n --){
fast = fast -> next;
}
// 删除一个节点要知道该节点前一个节点,因此fast要多走一步
fast = fast -> next;
while(fast != NULL){
fast = fast -> next;
slow = slow -> next;
}
slow -> next = slow -> next -> next;
return preHead -> next;
}
};
(三十)两两交换链表中的节点
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode* preHead = new ListNode();
preHead -> next = head;
ListNode* fast = preHead -> next;
ListNode* slow = preHead;
// 这里必须保证有两个节点才能交换
while(fast != NULL && fast -> next != NULL){
ListNode* temp = fast -> next -> next;
slow -> next = fast -> next;
fast -> next -> next = fast;
fast -> next = temp;
slow = fast;
fast = temp;
}
return preHead -> next;
}
};
(三十一)K 个一组翻转链表
方法一:
class Solution {
public:
ListNode* reverseKGroup(ListNode* head, int k) {
ListNode* cur = head;
int count = 0;
while(cur){
cur = cur -> next;
count ++;
}
int num = count / k;
ListNode* preHead = new ListNode();
preHead -> next = head;
ListNode* temp1 = preHead;
ListNode* temp2 = preHead;
ListNode* temp3 = preHead;
while(num --){
for(int i = 0; i < k; i ++){
temp1 = temp1 -> next;
}
temp2 = temp1 -> next;
ListNode* fast = temp3 -> next;
ListNode* slow = temp2;
while(fast != temp2){
ListNode* temp = fast -> next;
fast -> next = slow;
slow = fast;
fast = temp;
}
ListNode* temp4 = temp3 -> next;
temp3 -> next = temp1;
temp1 = temp4;
temp2 = temp4;
temp3 = temp4;
}
return preHead -> next;
}
};
方法二:
每次都将cur后面一个节点提到这一组节点的最前面(pre→next位置),同时由于位置的交换cur节点也在不断向后,因此每次要交换的都是cur后面的一个节点,即next节点。
class Solution {
public ListNode reverseKGroup(ListNode head, int k) {
ListNode dummy = new ListNode(0), prev = dummy, curr = head, next;
dummy.next = head;
int length = 0;
while(head != null) {
length++;
head = head.next;
}
head = dummy.next;
for(int i = 0; i < length / k; i++) {
for(int j = 0; j < k - 1; j++) {
next = curr.next;
curr.next = next.next;
next.next = prev.next;
prev.next = next;
}
prev = curr;
curr = prev.next;
}
return dummy.next;
}
}
(三十二)随机链表的复制
class Solution {
public:
Node* copyRandomList(Node* head) {
// 用map存储Node指针,将原链表节点的指针(key)和新创建的节点的指针(value)一一对应起来
unordered_map<Node*, Node*> mp;
for(Node* node = head; node; node = node -> next){
mp[node] = new Node(node -> val);
}
for(Node* node = head; node; node = node -> next){
mp[node] -> next = mp[node -> next];
mp[node] -> random = mp[node -> random];
}
return mp[head];
}
};
(三十三)排序链表
方法一:归并
class Solution {
public ListNode sortList(ListNode head) {
return mergeSort(head);
}
// 归并排序
private ListNode mergeSort(ListNode head){
// 如果没有结点/只有一个结点,无需排序,直接返回
if (head==null||head.next==null) return head;
// 快慢指针找出中位点
// 这里找到的是中位点的前一个,因为之后要将中位点置为NULL,而中位点的指向必须保留,所以用slow记录其前一个,这样即使slow -> next置为NULL,也能根据slow 回溯时找到之前的中位点
ListNode slowp=head,fastp=head.next,next,l,r;
while (fastp!=null&&fastp.next!=null){
slowp=slowp.next;
fastp=fastp.next.next;
}
// 对右半部分进行归并排序
// 先对有半部分排序,然后将其头结点置为NULL,这样左半部分就是独立的排序列表
r=mergeSort(slowp.next);
// 链表判断结束的标志:末尾节点.next==null
slowp.next=null;
// 对左半部分进行归并排序
l=mergeSort(head);
return mergeList(l,r);
}
// 合并链表
private ListNode mergeList(ListNode l,ListNode r){
// 临时头节点
ListNode tmpHead=new ListNode(-1);
ListNode p=tmpHead;
while (l!=null&&r!=null){
if (l.val<r.val){
p.next=l;
l=l.next;
}else {
p.next=r;
r=r.next;
}
p=p.next;
}
p.next=l==null?r:l;
return tmpHead.next;
}
}
class Solution {
public:
ListNode* sortList(ListNode* head) {
if(head == NULL || head -> next == NULL) return head;
ListNode* slow = head;
ListNode* fast = head -> next -> next;
while(fast != NULL && fast -> next != NULL){
slow = slow -> next;
fast = fast -> next -> next;
}
// 先对后半部分排序
ListNode* r = sortList(slow -> next);
// 再对前半部分排序
slow -> next = NULL;
ListNode* l = sortList(head);
return mergeList(l, r);
}
ListNode* mergeList(ListNode* left, ListNode* right){
ListNode* preHead = new ListNode();
ListNode* cur = preHead;
// 将两支分支合并
while(left != NULL && right != NULL){
if(left -> val < right -> val){
cur -> next = left;
left = left -> next;
}else{
cur -> next = right;
right = right -> next;
}
cur = cur -> next;
}
// 剩下的部分
if(left != NULL) cur -> next = left;
else cur -> next = right;
return preHead -> next;
}
};
方法二:快排
class Solution {
public ListNode sortList(ListNode head) {
if(head==null||head.next==null) return head;
// 没有条件,创造条件。自己添加头节点,最后返回时去掉即可。
ListNode newHead=new ListNode(-1);
newHead.next=head;
return quickSort(newHead,null);
}
// 带头结点的链表快速排序
private ListNode quickSort(ListNode head,ListNode end){
if (head==end||head.next==end||head.next.next==end) return head;
// 将小于划分点的值存储在临时链表中
ListNode tmpHead=new ListNode(-1);
// partition为划分点,p为链表指针,tp为临时链表指针
ListNode partition=head.next,p=partition,tp=tmpHead;
// 将小于划分点的结点放到临时链表中
while (p.next!=end){
if (p.next.val<partition.val){
tp.next=p.next;
tp=tp.next;
p.next=p.next.next;
}else {
p=p.next;
}
}
// 合并临时链表和原链表,将原链表接到临时链表后面即可
tp.next=head.next;
// 将临时链表插回原链表,注意是插回!(不做这一步在对右半部分处理时就断链了)
head.next=tmpHead.next;
quickSort(head,partition);
quickSort(partition,end);
// 题目要求不带头节点,返回结果时去除
return head.next;
}
}
(三十四)合并 K 个升序链表
法一:暴力解法,逐个合并
class Solution {
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
ListNode* list = NULL;
for(int i = 0; i < lists.size(); i ++){
list = mergeList(list, lists[i]);
}
return list;
}
ListNode* mergeList(ListNode* listA, ListNode* listB){
ListNode* preHead = new ListNode();
ListNode* cur = preHead;
while(listA != NULL && listB != NULL){
if(listA -> val < listB -> val){
cur -> next = listA;
listA = listA -> next;
}else{
cur -> next = listB;
listB = listB -> next;
}
cur = cur -> next;
}
if(listA != NULL){
cur -> next = listA;
}else if(listB != NULL){
cur -> next = listB;
}
return preHead -> next;
}
};
法二:优先队列法(比较巧妙)
用容量为K的最小堆优先队列,把链表的头结点都放进去,然后出队当前优先队列中最小的,挂上链表,,然后让出队的那个节点的下一个入队,再出队当前优先队列中最小的,直到优先队列为空。
class Solution {
private:
struct cmp{
bool operator()(const ListNode* node1, const ListNode* node2) {
return node1 -> val > node2 -> val; //小的优先级高 ,从小到大排,这里是按照val的大小构造的小顶堆
}
};
public:
ListNode* mergeKLists(vector<ListNode*>& lists) {
if(lists.size() == 0) return NULL;
ListNode* preHead = new ListNode();
ListNode* cur = preHead;
priority_queue<ListNode*, vector<ListNode*>, cmp> pq;
for(ListNode* list: lists){
if(list == NULL) continue;
pq.push(list);
}
while(!pq.empty()){
ListNode* topNode = pq.top();
pq.pop();
cur -> next = topNode;
cur = cur -> next;
if(topNode -> next != NULL){
pq.push(topNode -> next);
}
}
return preHead -> next;
}
};
(三十五)LRU 缓存
哈希表+双向链表(这里自己实现了一个简单的双向链表)
// 用双向链表存储所有节点,头部为最新的,尾部为旧的等待被替换的
struct DListNode{
int key;
int value;
DListNode* pre;
DListNode* next;
DListNode(): key(0),value(0), pre(nullptr), next(nullptr){}
DListNode(int k, int v): key(k), value(v), pre(nullptr), next(nullptr){}
};
class LRUCache {
private:
// 哈希表中直接存储指针,这样可以直接通过Key找到节点在链表中的位置
unordered_map<int, DListNode*> cache;
DListNode* head;
DListNode* tail;
int size; // 当前长度
int capacity; //最大容量
public:
// 使用成员初始化列表来初始化所有成员变量,确保成员变量在构造函数体执行之前就已经被赋予了正确的初始值。
LRUCache(int capacity): capacity(capacity), size(0) {
// 使用伪头部和伪尾部节点
head = new DListNode();
tail = new DListNode();
head -> next = tail;
tail -> pre = head;
}
int get(int key) {
if(!cache.count(key)){
return -1;
}
// 如果key存在,先通过哈希表定位,再更新节点位置,移到头部
DListNode* node = cache[key];
moveToHead(node);
return node -> value;
}
void put(int key, int value) {
if(!cache.count(key)){
// 如果key不存在,创建一个新的节点
DListNode* node = new DListNode(key, value);
// 添加进哈希表
cache[key] = node;
// 新节点添加至链表头部
addToHead(node);
++size;
// 如果超出容量,删除双向链表的尾部节点
if(size > capacity){
DListNode* removed = removeTail();
// 删除哈希表中对应项
cache.erase(removed -> key);
// 删除节点,防止内存泄漏
delete removed;
--size;
}
}
// 如果 key 存在,先通过哈希表定位,再修改 value,并移到头部
else{
DListNode* node = cache[key];
node -> value = value;
moveToHead(node);
}
}
void addToHead(DListNode* node){
node -> pre = head;
node -> next = head -> next;
head -> next -> pre = node;
head -> next = node;
}
void removeNode(DListNode* node){
node -> pre -> next = node -> next;
node -> next -> pre = node -> pre;
}
void moveToHead(DListNode* node){
removeNode(node);
addToHead(node);
}
DListNode* removeTail(){
DListNode* node = tail -> pre;
removeNode(node);
return node;
}
};
(三十六)二叉树的中序遍历
(1)法一:递归法
class Solution {
public:
vector<int> result;
void traversal(TreeNode* root, vector<int>& vec){
if(root == NULL) return;
traversal(root -> left, vec);
vec.push_back(root -> val);
traversal(root -> right, vec);
}
vector<int> inorderTraversal(TreeNode* root) {
vector<int> result;
traversal(root, result);
return result;
}
};
(2)法二:迭代法
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> result;
stack<TreeNode*> st;
TreeNode* cur = root;
while(cur != NULL || !st.empty()){
if(cur != NULL){
st.push(cur);
cur = cur -> left;
}else{
cur = st.top();
st.pop();
result.push_back(cur -> val);
cur = cur -> right;
}
}
return result;
}
};
(三十七)二叉树的最大深度
(1)法一:递归法
class Solution {
public:
int maxDepth(TreeNode* root) {
if(root == NULL) return 0;
int leftDepth = maxDepth(root -> left);
int rightDepth = maxDepth(root -> right);
return max(leftDepth, rightDepth) + 1;
}
};
(2)层序比那里
class Solution {
public:
int maxDepth(TreeNode* root) {
if(root == NULL) return 0;
int height = 0;
queue<TreeNode*> que;
que.push(root);
while(!que.empty()){
height ++;
int size = que.size();
while(size --){
TreeNode* cur = que.front();
que.pop();
if(cur -> left) que.push(cur -> left);
if(cur -> right) que.push(cur -> right);
}
}
return height;
}
};
(三十八)翻转二叉树
class Solution {
public:
TreeNode* invertTree(TreeNode* root) {
if(root == NULL) return root;
root -> left = invertTree(root -> left);
root -> right = invertTree(root -> right);
swap(root -> left, root -> right);
return root;
}
};
(三十九)对称二叉树
两边子树的遍历顺序不一样,但是要同时遍历
左子树:左-右-中
右子树:右-左-中
对于整个树而言,遍历顺序为:左-右-中
class Solution {
public:
bool isSymmetric(TreeNode* root) {
return compare(root -> left, root -> right);
}
bool compare(TreeNode* left, TreeNode* right){
if(left == NULL && right == NULL) return true;
else if(left == NULL && right != NULL) return false;
else if(left != NULL && right == NULL) return false;
else if(left -> val != right -> val) return false;
// 如果left和right都不为NULL且值相等,还要判断他们的outside和inside是否相等
bool outside = compare(left -> left, right -> right);
bool inside = compare(left -> right, right -> left);
return outside && inside;
}
};
(四十)二叉树的直径
class Solution {
public:
int maxLength = 0;
int getHeight(TreeNode* root){
if(root == NULL) return 0;
int leftHeight = getHeight(root -> left);
int rightHeight = getHeight(root -> right);
// 对每个节点计算其高度的同时,刷新当前的最长路径(最长路径可能以任意一个节点为根节点)
maxLength = max(maxLength, leftHeight + rightHeight);
return max(leftHeight, rightHeight) + 1;
}
int diameterOfBinaryTree(TreeNode* root) {
getHeight(root);
return maxLength;
}
};
(四十一)二叉树的层序遍历
class Solution {
public:
vector<vector<int>> levelOrder(TreeNode* root) {
vector<vector<int>> result;
queue<TreeNode*> que;
if(root != NULL) que.push(root);
while(!que.empty()){
int size = que.size();
vector<int> path;
while(size --){
TreeNode* topNode = que.front();
path.push_back(topNode -> val);
que.pop();
// 将当前pop出来的节点的左右孩子入队列
if(topNode -> left) que.push(root -> left);
if(topNode -> right) que.push(root -> right);
}
result.push_back(path);
}
return result;
}
};
(四十二)将有序数组转换为二叉搜索树
class Solution {
public:
TreeNode* traversal(vector<int>& nums, int left, int right){
if(left > right) return NULL;
int mid = (left + right) / 2;
TreeNode* root = new TreeNode(nums[mid]);
root -> left = traversal(nums, left, mid - 1);
root -> right = traversal(nums, mid + 1, right);
return root;
}
TreeNode* sortedArrayToBST(vector<int>& nums) {
TreeNode* root = traversal(nums, 0, nums.size() - 1);
return root;
}
};
(四十三)验证二叉搜索树
(1)中序遍历整理转化为数组进行判断
class Solution {
public:
void traversal(TreeNode* root, vector<int>& vec){
if(root == NULL) return;
traversal(root -> left, vec);
vec.push_back(root -> val);
traversal(root -> right, vec);
}
bool isValidBST(TreeNode* root) {
vector<int> vec;
traversal(root, vec);
for(int i = 1; i < vec.size(); i ++){
if(vec[i] <= vec[i - 1]) return false;
}
return true;
}
};
(2)一边中序遍历一边比较
class Solution {
public:
// 按照左中右遍历,当前节点的值一定是大于Pre的值
TreeNode* pre = NULL;
bool isValidBST(TreeNode* root) {
if(root == NULL) return true;
bool left = isValidBST(root -> left); // 左
// 跳过第一个节点,依次将后一个节点与前一个节点的值作比较
if(pre != NULL && pre -> val >= root -> val) return false; // 中
pre = root;
bool right = isValidBST(root -> right);// 右
// 左子树和右子树都为二叉搜索树
return left && right;
}
};
(四十四)二叉搜索树中第 K 小的元素
class Solution {
public:
void traversal(TreeNode* root, vector<int>& vec){
if(root == NULL) return;
traversal(root -> left, vec);
vec.push_back(root -> val);
traversal(root -> right, vec);
}
int kthSmallest(TreeNode* root, int k) {
vector<int> vec;
traversal(root, vec);
return vec[k - 1];
}
};
(四十五)二叉树的右视图
class Solution {
public:
vector<int> rightSideView(TreeNode* root) {
vector<int> vec;
queue<TreeNode*> que;
if(root == NULL) return vec;
que.push(root);
// 层序遍历,取每一层最后的节点的值
while(!que.empty()){
int size = que.size();
while(size --){
TreeNode* topNode = que.front();
que.pop();
if(size == 0) vec.push_back(topNode -> val);
if(topNode -> left) que.push(topNode -> left);
if(topNode -> right) que.push(topNode -> right);
}
}
return vec;
}
};
(四十六)二叉树展开为链表
(1)迭代法(自顶向下)
class Solution {
public:
void flatten(TreeNode* root) {
// 一边遍历一边解构,不断将左子树移到右边,右子树接到左子树后面
while(root != NULL){
// 如果当前节点没有左子树,则直接跳到下一个节点
if(root -> left == NULL){
root = root -> right;
}else{
TreeNode* temp = root -> left;
// 找到左子树的先序遍历到的最后一个节点(即最右边的节点)
while(temp -> right != NULL){
temp = temp -> right;
}
// 将左子树整个移到根节点右边,原来的右子树移到左子树最后一个节点的右子树上
temp -> right = root -> right;
root -> right = root -> left;
// 注意要将左子树置为空
root -> left = NULL;
root = root -> right;
}
}
}
};
(2)递归法(自底向上)
利用后序遍历,依次遍历 6 5 4 3 2 1,然后每遍历一个节点就将当前节点的右指针更新为上一个节点。相应的左孩子也要置为 null
,同样的也不用担心左孩子丢失,因为是后序遍历,左孩子已经遍历过了。
遍历到 5,把 5 的右指针指向 6。6 <- 5 4 3 2 1。
遍历到 4,把 4 的右指针指向 5。6 <- 5 <- 4 3 2 1。
链接:https://leetcode.cn/problems/flatten-binary-tree-to-linked-list/solutions/17274/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by–26/
class Solution {
public:
TreeNode* pre = NULL;
void flatten(TreeNode* root) {
if(root == NULL) return;
flatten(root -> right);
flatten(root -> left);
root -> right = pre;
pre = root;
root -> left = NULL;
}
};
(四十七)从前序与中序遍历序列构造二叉树
class Solution {
public:
TreeNode* traversal(vector<int>& preorder, vector<int>& inorder, int preLeft, int preRight, int inLeft, int inRight){
if(preLeft > preRight || inLeft > inRight) return NULL; // 终止条件
TreeNode* root = new TreeNode(preorder[preLeft]); // 中
int inRootId = inLeft;
while(inRootId <= inRight && inorder[inRootId] != preorder[preLeft]) inRootId ++;
int subLeftLen = inRootId - inLeft;
root -> left = traversal(preorder, inorder, preLeft + 1, preLeft + subLeftLen, inLeft, inRootId - 1); // 左
root -> right = traversal(preorder, inorder, preLeft + subLeftLen + 1, preRight, inRootId + 1, inRight); // 右
return root;
}
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
return traversal(preorder, inorder, 0, preorder.size() - 1, 0, inorder.size() - 1);
}
};
(四十八)路径总和 III
(1)双重递归
class Solution {
public:
int result = 0;
void getPath(TreeNode* root, long long target){
if(root == NULL) return;
if(target == root -> val) result ++;
getPath(root -> left, target - root -> val);
getPath(root -> right, target - root -> val);
}
int pathSum(TreeNode* root, int targetSum) {
if(root == NULL) return 0;
// 以左子树为根节点可能的路径总数
int left = pathSum(root -> left, targetSum);
// 以右子树为根节点可能的路径总数
int right = pathSum(root -> right, targetSum);
// 以当前节点为根节点可能的路径总数
getPath(root, targetSum);
return result;
}
};
(2)前缀和+递归
class Solution {
public:
// 前缀和表示从根节点到当前节点(包括当前节点)前面所有节点之和。
unordered_map<long, int> prefix;
int dfs(TreeNode* root, long long cur, int target){
if(root == NULL) return 0;
int res = 0;
cur += root -> val; // 当前节点的前缀和
if(prefix.count(cur - target) != 0){
res = prefix[cur - target];
}
prefix[cur] ++;
res += dfs(root -> left, cur, target);
res += dfs(root -> right, cur, target);
// 退出某个节点时将其cur值从哈希表中删除
prefix[cur] --;
// 这行可以省略,因为cur作为参数压栈了,所以其每个节点对应的值被保存了下来
cur -= root -> val;
return res;
}
int pathSum(TreeNode* root, int targetSum) {
// 注意:空路径cur为0
prefix[0] = 1;
return dfs(root, 0, targetSum);
}
};
(四十九)二叉树的最近公共祖先
class Solution {
public:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
if(root == NULL) return NULL;
if(root == p || root == q) return root;
TreeNode* left = lowestCommonAncestor(root -> left, p, q);
TreeNode* right = lowestCommonAncestor(root -> right, p, q);
if(!left && !right) return NULL;
if(left && !right) return left;
if(left && right) return root;
if(!left && right) return right;
return NULL;
}
};
(五十)二叉树中的最大路径和
class Solution {
public:
int maxTemp = INT_MIN;
int getMax(TreeNode* root){
if(root == NULL) return 0;
// 如果其子树最大路径和为负数,这样对父节点的贡献为0,舍弃该子树
int left = max(0, getMax(root -> left));
int right = max(0, getMax(root -> right));
// 最大路径包含root,左子树、右子树的情况,无法递归(递归了就不是一条路径了),这里用一个temp记录下来最大的
maxTemp = max(maxTemp, root -> val + left + right);
return max(left, right) + root -> val;
}
int maxPathSum(TreeNode* root) {
getMax(root);
// 对于最后的root,当然是要返回包含root、左子树、右子树的最大路径长度,所以最终返回的是maxTemp
// maxTemp保存的是以任意节点为根节点的最长路径
return maxTemp;
}
};
(五十一)岛屿数量
(1)深搜
class Solution {
public:
int dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1};
void dfs(vector<vector<char>>& grid, vector<vector<bool>>& visited, int x, int y){
for(int i = 0; i < 4; i ++){
int nextx = x + dir[i][0];
int nexty = y + dir[i][1];
if(nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size()) continue;
if(grid[nextx][nexty] == '1' && !visited[nextx][nexty]){
visited[nextx][nexty] = true;
dfs(grid, visited, nextx, nexty);
}
}
}
int numIslands(vector<vector<char>>& grid) {
int n = grid.size();
int m = grid[0].size();
vector<vector<bool>> visited(n, vector<bool>(m, false));
int count = 0;
for(int i = 0; i < n; i ++){
for(int j = 0; j < m; j ++){
if(grid[i][j] == '1' && !visited[i][j]){
count ++;
visited[i][j] = true;
dfs(grid, visited, i, j);
}
}
}
return count;
}
};
(2)广搜
class Solution {
public:
int dir[4][2] = {0, 1, 1, 0, -1, 0, 0, -1};
void bfs(vector<vector<char>>& grid, vector<vector<bool>>& visited, int x, int y){
queue<pair<int, int>> que;
que.push({x, y});
visited[x][y] = true;
while(!que.empty()){
pair<int, int> temp = que.front();
que.pop();
int tempx = temp.first;
int tempy = temp.second;
for(int i = 0; i < 4; i ++){
int nextx = tempx + dir[i][0];
int nexty = tempy + dir[i][1];
if(nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size()) continue;
if(grid[nextx][nexty] == '1' && !visited[nextx][nexty]){
visited[nextx][nexty] = true;
que.push({nextx, nexty});
}
}
}
}
int numIslands(vector<vector<char>>& grid) {
int n = grid.size();
int m = grid[0].size();
vector<vector<bool>> visited(n, vector<bool>(m, false));
int count = 0;
for(int i = 0; i < n; i ++){
for(int j = 0; j < m; j ++){
if(grid[i][j] == '1' && !visited[i][j]){
count ++;
bfs(grid, visited, i, j);
}
}
}
return count;
}
};
(五十二)腐烂的橘子
class Solution {
public:
// 深搜
int sum = 0; // 统计当前一共有多少烂橘子(包括原来就烂的和后来烂掉的)
int dir[4][2] = {1, 0, 0, 1, -1, 0, 0, -1};
int bfs(vector<vector<int>>& grid, queue<pair<int, int>> que){
int minute = 0;
while(!que.empty()){
// 类比二叉树层序遍历,记录当前层的橘子数目
int size = que.size();
sum += size;
minute ++; // 每一层都使得时间+1
while(size --){
pair<int, int> temp = que.front();
que.pop();
int tempx = temp.first;
int tempy = temp.second;
for(int i = 0; i < 4; i ++){
int nextx = tempx + dir[i][0];
int nexty = tempy + dir[i][1];
if(nextx < 0 || nextx >= grid.size() || nexty < 0 || nexty >= grid[0].size()) continue;
if(grid[nextx][nexty] == 1){
grid[nextx][nexty] = 2;
que.push({nextx, nexty});
}
}
}
}
return minute - 1; //因为第一层是已经腐烂的橘子,不算时间,所以时间=层数-1
}
int orangesRotting(vector<vector<int>>& grid) {
int m = grid.size();
int n = grid[0].size();
int total = 0; // 统计一共多少橘子,判断最后橘子有没有烂完
int fresh = 0; // 新鲜的橘子数目
for(int i = 0; i < m; i ++){
for(int j = 0; j < n; j ++){
if(grid[i][j]) total ++;
if(grid[i][j] == 1) fresh ++;
}
}
if(fresh == 0) return 0; // 如果原来就没有新鲜的橘子,所需时间为0
// 因为所有腐烂的橘子同时扩散到周围的橘子,所以将所有腐烂的橘子视为同一层
queue<pair<int, int>> que;
for(int i = 0; i < m; i ++){
for(int j = 0; j < n; j ++){
if(grid[i][j] == 2){
que.push({i, j});
}
}
}
int result = bfs(grid, que);
if(sum != total) return -1;
return result;
}
};