文章目录
- 3 链表
- 3.1 【链表】删除链表中的节点
- 3.2 【双指针】删除链表的倒数第 N 个结点
- 3.3 【链表】反转链表
- 3.4 【链表】合并两个有序链表
- 3.5 【链表】回文链表
- 3.6 【双指针】环形链表
- 4 树
- 4.1 【递归】二叉树的最大深度
- 4.2 【递归】验证二叉搜索树
- 4.3 【递归】对称二叉树
- 4.4 【BFS】二叉树的层序遍历
- 4.5 【分治】将有序数组转换为二叉搜索树
- 5 排序和搜索
- 5.1 【排序】合并两个有序数组
- 5.2 【二分】第一个错误的版本
- 6 动态规划
- 6.1 【动态规划】爬楼梯
- 6.2 【动态规划】买卖股票的最佳时机
- 6.3 【动态规划】最大子数组和
- 6.4 【动态规划】打家劫舍
3 链表
3.1 【链表】删除链表中的节点
https://leetcode.cn/problems/delete-node-in-a-linked-list/
给出的就是要删除的那个节点,直接前后移动就行了。
/*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode(int x) : val(x), next(NULL) {}* };*/
class Solution {
public:void deleteNode(ListNode* node) {node->val = node->next->val;node->next = node->next->next;}
};
3.2 【双指针】删除链表的倒数第 N 个结点
https://leetcode.cn/problems/remove-nth-node-from-end-of-list/
利用双指针left和right,首先让right遍历n个节点,再让两个指针同时遍历,当right遍历到链表结尾时,left所指的就是倒数第n个节点,正常删除即可。
/*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}* ListNode(int x) : val(x), next(nullptr) {}* ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {
public:ListNode* removeNthFromEnd(ListNode* head, int n) {ListNode* left = head;ListNode* right = head;for(int i=0;i<n;i++){right = right->next;}if(right == nullptr) return head->next;while(right->next!=nullptr){left = left->next;right = right->next;}left->next = left->next->next;return head;}
};
3.3 【链表】反转链表
https://leetcode.cn/problems/reverse-linked-list/
使用头插法解决,后续考虑一下递归和栈。
/*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}* ListNode(int x) : val(x), next(nullptr) {}* ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {
public:ListNode* reverseList(ListNode* head) {ListNode* ans = nullptr;ListNode* x = head;while(x){ListNode* p = x;x = x->next;p->next = ans;ans = p;}return ans;}
};
3.4 【链表】合并两个有序链表
https://leetcode.cn/problems/merge-two-sorted-lists/
常规做法,挨个比较大小,后续考虑如何用递归完成。
/*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}* ListNode(int x) : val(x), next(nullptr) {}* ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {
public:ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {if(!list1) return list2;if(!list2) return list1;ListNode* ans = new ListNode();ListNode* tmp = ans;while(list1 && list2){if(list1->val <= list2->val){tmp->next = list1;list1 = list1->next;}else{tmp->next = list2;list2 = list2->next;}tmp = tmp->next;}if(!list1) tmp->next = list2;if(!list2) tmp->next = list1;return ans->next;}
};
3.5 【链表】回文链表
https://leetcode.cn/problems/palindrome-linked-list/
首先将链表进行反转,然后按照链表长度的一半逐一进行比较即可,这里要注意赋值的问题,一开始我是想直接把head赋值给一个空链表,后面发现指针这个东西都是指向同一个地址的,所以其中一个的结构变了另一个也会跟着变,后来就改用数组来存head里面原来正序的数字了,后期可以考虑一下如何用双指针(快慢指针)和栈怎么解决。
/*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}* ListNode(int x) : val(x), next(nullptr) {}* ListNode(int x, ListNode *next) : val(x), next(next) {}* };*/
class Solution {
public:bool isPalindrome(ListNode* head) {ListNode* res = nullptr;vector<int> num;int length = 0;while(head){num.push_back(head->val);ListNode *temp = head;head = head->next;temp->next = res;res = temp;length++;}int i = 0;while(res && i*2<=length-1){if(num[i] != res->val) return false;res = res->next;i++;}return true;}
};
3.6 【双指针】环形链表
https://leetcode.cn/problems/linked-list-cycle/
定义快慢指针,快指针一次走两步,慢指针一次走一步,如果存在环,则两个指针总会相遇,否则不存在环。
/*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode(int x) : val(x), next(NULL) {}* };*/
class Solution {
public:bool hasCycle(ListNode *head) {ListNode *fast = head;ListNode *slow = head;while(fast && fast->next){fast = fast->next->next;slow = slow->next;if(fast == slow) return true;}return false;}
};
4 树
4.1 【递归】二叉树的最大深度
https://leetcode.cn/problems/maximum-depth-of-binary-tree/
递归解决,分别遍历左右子树,找出其中的最大值作为树的最大深度。
/*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
class Solution {
public:int maxDepth(TreeNode* root) {if(root == NULL) return 0;else{int left_depth = maxDepth(root->left);int right_depth = maxDepth(root->right);return max(left_depth,right_depth)+1;}}
};
4.2 【递归】验证二叉搜索树
https://leetcode.cn/problems/validate-binary-search-tree/
递归实现,首先要明白二叉搜索树的判定条件,一是左子树<root<右子树,二是下面的左子树的值要比当前结点小,而右子树的值要比当前结点大,也就是不能只在一个小的子树上满足这个条件,往上回溯几代之后这个条件要依然满足才行。
/*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
class Solution {
public:bool isValidBST(TreeNode* root) {return isValidBST(root , NULL , NULL);}bool isValidBST(TreeNode* root, TreeNode* min, TreeNode* max) {if(root == NULL) return true;else{if(min != NULL && root->val <= min->val) return false;if(max != NULL && root->val >= max->val) return false;}return isValidBST(root->left, min, root) && isValidBST(root->right, root, max);}
};
4.3 【递归】对称二叉树
https://leetcode.cn/problems/symmetric-tree/
以下是递归解法,详见代码,之后需要考虑一下非递归如何实现。
/*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
class Solution {
public:bool isSymmetric(TreeNode* root) {//如果root为空,则返回trueif(root == NULL) return true;else return isSymmetric(root->left, root->right);}bool isSymmetric(TreeNode* left, TreeNode* right) {//如果左右子树均为空,则返回trueif(left == NULL && right == NULL) return true;//如果只有一个子树为空,或者两个子树均不为空但值不相等,则返回falseelse if(left == NULL || right == NULL || left->val != right->val) return false;//比较左子树的右子树和右子树的左子树是否对称、左子树的左子树和右子树的右子树是否对称else return isSymmetric(left->right, right->left) && isSymmetric(left->left, right->right);}
};
4.4 【BFS】二叉树的层序遍历
https://leetcode.cn/problems/binary-tree-level-order-traversal/
BFS解题,按照树的每一层进行遍历,首先定义队列,如果当前节点不为空,则加入队列,之后分别遍历该节点的左右子树,依次重复上述操作,直到最后队列元素为空。
/*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
class Solution {
public:vector<vector<int>> levelOrder(TreeNode* root) {vector<vector<int>> ans;if(root == NULL) return ans;queue<TreeNode*> tree_node;tree_node.push(root);while(!tree_node.empty()){int n = tree_node.size();vector<int> subtree;for(int i=0;i<n;i++){TreeNode* temp = tree_node.front();tree_node.pop();subtree.push_back(temp->val);if(temp->left != NULL) tree_node.push(temp->left);if(temp->right != NULL) tree_node.push(temp->right);}ans.push_back(subtree);}return ans;}
};
4.5 【分治】将有序数组转换为二叉搜索树
https://leetcode.cn/problems/convert-sorted-array-to-binary-search-tree/
分治解题,将构造树的过程分为两部分,首先从数组的中间元素开始,左边的数为左子树,右边的数为右子树,依次往后递推。
/*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}* };*/
class Solution {
public:TreeNode* sortedArrayToBST(vector<int>& nums) {if(nums.size()==0) return NULL;else return sortedArrayToBST(nums, 0, nums.size()-1);}TreeNode* sortedArrayToBST(vector<int>& nums, int start, int end) {if(start > end) return NULL;else{int mid = (start + end) / 2;TreeNode* root = new TreeNode(nums[mid]);root->left = sortedArrayToBST(nums, start, mid-1);root->right = sortedArrayToBST(nums, mid+1, end);return root;}}
};
5 排序和搜索
5.1 【排序】合并两个有序数组
https://leetcode.cn/problems/merge-sorted-array/
直接把nums2的值赋值到nums1后面,然后对nums1进行排序即可。
class Solution {
public:void merge(vector<int>& nums1, int m, vector<int>& nums2, int n) {for(int i=0;i<n;i++){nums1[m+i] = nums2[i];}sort(nums1.begin(),nums1.end());}
};
5.2 【二分】第一个错误的版本
https://leetcode.cn/problems/first-bad-version/
二分法解题,注意遍历大小是从1到n,不要越界了。
// The API isBadVersion is defined for you.
// bool isBadVersion(int version);class Solution {
public:int firstBadVersion(int n) {int left = 1 , right = n;while(left<right){int mid = left + (right - left) / 2;if(!isBadVersion(mid)) left = mid + 1;else right = mid;}return left;}
};
6 动态规划
6.1 【动态规划】爬楼梯
https://leetcode.cn/problems/climbing-stairs/
爬一楼需要1次,爬二楼需要2次,爬三楼需要3次,这个3次可以看做是从一楼+2或者二楼+1
也就是f(n)=f(n-1)+f(n-2)
class Solution {
public:int climbStairs(int n) {if(n==1) return 1;if(n==2) return 2;int ans[n];ans[0] = 1;ans[1] = 2;for(int i=2;i<n;i++){ans[i] = ans[i-1] + ans[i-2];}return ans[n-1];}
};
6.2 【动态规划】买卖股票的最佳时机
https://leetcode.cn/problems/best-time-to-buy-and-sell-stock/
动态规划解题,首先定义数组dp[i][2]
- dp[i][0]:表示第i+1天结束的时候,如果手里没有持有股票,最大的利润是多少
- dp[i][1]:表示第i+1天结束的时候,如果手里持有股票,最大的利润是多少
在这里我们分别对上述两个进行计算:
- dp[i][0]
这个考虑到两种情况,一是第i天没有持有股票,所以与第i天未持有股票的利润一样,二是第i天持有股票,但是决定在第i+1天卖掉,所以利润为第i天持有股票的利润+第i+1天该股票的价格,两个取最大值即可 - dp[i][1]
这个也考虑到两种情况,一是第i天持有股票,所以与第i天持有股票的利润一样,二是第i天未持有股票,但是决定在第i天买入,所以利润为负值,两个取最大值即可
//原始代码
class Solution {
public:int maxProfit(vector<int>& prices) {int length = prices.size();if(length==1) return 0;int dp[length][2];dp[0][0] = 0;dp[0][1] = -prices[0];for(int i=1;i<length;i++){dp[i][0] = max(dp[i-1][0],dp[i-1][1] + prices[i]);dp[i][1] = max(dp[i-1][1],-prices[i]);}return dp[length-1][0];}
};//优化后的代码---用两个变量代替数组
class Solution {
public:int maxProfit(vector<int>& prices) {int length = prices.size();if(length==1) return 0;int stock_unhold = 0;int stock_hold = -prices[0];for(int i=1;i<length;i++){stock_unhold = max(stock_unhold,stock_hold + prices[i]);stock_hold = max(stock_hold,-prices[i]);}return stock_unhold;}
};
6.3 【动态规划】最大子数组和
https://leetcode.cn/problems/maximum-subarray/
利用动态规划,首先定义数组dp[i],表示终点下标为i的序列的最大子数组和,主要考虑以下两种情况:
- 如果dp[i-1]大于0,则继续向前增加下标为i的数值,作为dp[i]的子数组和
- 如果dp[i-1]小于0,则从这里开始停止,重新计算子数组和,赋值为0后再加入下标为i的数值,作为dp[i]的子数组和
最后的结果就是数组中最大的那个值
//原始
class Solution {
public:int maxSubArray(vector<int>& nums) {int length = nums.size();if(length==1) return nums[0];int dp[length];int ans = nums[0];dp[0] = nums[0];for(int i=1;i<length;i++){dp[i] = max(dp[i-1],0) + nums[i];if(dp[i] > ans) ans = dp[i];}return ans;}
};//优化代码---用变量代替数组
class Solution {
public:int maxSubArray(vector<int>& nums) {int length = nums.size();if(length==1) return nums[0];int dps = nums[0];int ans = nums[0];for(int i=1;i<length;i++){dps = max(dps,0) + nums[i];if(dps > ans) ans = dps;}return ans;}
};
6.4 【动态规划】打家劫舍
https://leetcode.cn/problems/house-robber/
利用动态规划,首先定义数组dp[i][2],状态转移如下所示:
- dp[i][0]表示第i家不被偷,则前一家可能被偷,也可能没有被偷,取最大值即可;
- dp[i][1]表示第i家被偷,则前一家一定没有被偷,所以为dp[i-1][0]+nums[i]
最后在第i家的时候取被偷和未被偷之间的最大值即可
class Solution {
public:int rob(vector<int>& nums) {int length = nums.size();if(length==1) return nums[0];int dp[length][2];dp[0][0] = 0;dp[0][1] = nums[0];for(int i=1;i<length;i++){dp[i][0] = max(dp[i-1][0],dp[i-1][1]);dp[i][1] = dp[i-1][0] + nums[i];}if(dp[length-1][0] > dp[length-1][1]) return dp[length-1][0];else return dp[length-1][1];}
};