持续更新中....坚持在期末的日子里每日两题。
一、哈希
1.两数之和
(1)题目
给定一个整数数组 nums
和一个整数目标值 target
,请你在该数组中找出 和为目标值 target
的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
(2)解答
我是真的菜哈哈,看到里面有句话 “ 有人相爱,有人夜里开车看海,有人leetcode第一题都做不出来。”
我真的是暴力解出来的,官方提供了一种使用哈希的解法让时间复杂度降下来:
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int, int> hashtable;
for (int i = 0; i < nums.size(); ++i) {
auto it = hashtable.find(target - nums[i]);
if (it != hashtable.end()) {
return {it->second, i};
}
hashtable[nums[i]] = i;
}
return {};
}
};
二、动态规划
70.爬阶梯
(1)题目
假设你正在爬楼梯。需要 n
阶你才能到达楼顶。
每次你可以爬 1
或 2
个台阶。你有多少种不同的方法可以爬到楼顶呢?
(2)解答
这个就是斐波那锲数列,但是用递归的话超时了,所以就要用非递归的方法。
class Solution {
public:
int climbStairs(int n) {
if(n==1) return 1;
if(n==2) return 2;
int sum=0;
int a1=1;
int a2=2;
for(int i=3;i<=n;i++){
sum=a1+a2;
a1=a2;
a2=sum;
}
return sum;
}
};
三、回溯
46.全排列问题
(1)题目
给定一个不含重复数字的数组 nums
,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
示例 1:
输入:nums = [1,2,3] 输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
示例 2:
输入:nums = [0,1] 输出:[[0,1],[1,0]]
示例 3:
输入:nums = [1] 输出:[[1]]
(2)解答
就是很正常的回溯法,不过这个是需要用到递归的,然后要注意每次排列都要换回去,不然乱七八糟的。
class Solution {
public:
vector<vector<int>> permute(vector<int>& nums) {
vector<vector<int>> final_list;
perm(nums,final_list,0,nums.size()-1);
return final_list;
}
void perm(vector<int>& nums,vector<vector<int>>& final_nums,int l,int m){
if(l==m){
vector<int> list_try;
for(int i=0;i<=m;i++){
list_try.push_back(nums[i]);
}
final_nums.push_back(list_try);
}
else{
for(int i=l;i<=m;i++){
swap(nums[l],nums[i]);
perm(nums,final_nums,l+1,m);
swap(nums[l],nums[i]);
}
}
}
};
四、链表
160.相交链表
(1)题目
(2)解答
这里用到了一歌方法来消除链表之间的长度不同:
假设有两个链表A和B,长度分别为L1
和L2
。如果一个链表比另一个长,当两个链表的指针同时从各自的头部出发时,较长链表的指针会先到达末端。为了同步两个链表,使它们可以一起到达相交点,可以采用以下策略:
- 指针
pA
开始时指向链表A的头部,指针pB
指向链表B的头部。 - 当
pA
遍历到链表A的末尾时,将pA
重置到链表B的头部继续遍历;同样,当pB
遍历到链表B的末尾时,将pB
重置到链表A的头部继续遍历。
为什么这样可以消除长度差?
通过上述步骤,每个指针都将遍历长度为L1 + L2
的路径。这样做的关键在于,当两个指针在各自链表的末尾“交换”起点后,它们会以相同的速度前进,并同时到达相交点(如果存在的话)。
- 第一阶段:两个指针分别在自己的链表中前进。
- 第二阶段:指针交换起点后,开始遍历对方的链表。
这种方法的精妙之处在于,当两个指针“交换”后,如果链表相交,那么因为交换起点,两个指针到达相交点的距离是相等的。即使两个链表不相交,指针也会同时到达各自链表的末尾(此时都是null
),从而结束循环。
/**
* 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) {
ListNode *pa=headA;
ListNode *pb=headB;
while (pA != pB) {
// If reaching the end of one list, start from the beginning of the other
pA = pA == NULL ? headB : pA->next;
pB = pB == NULL ? headA : pB->next;
}
return headA0;
}
};
五、二叉树
46.二叉树中序遍历
(1)题目
(2)解答
二叉树遍历用最简单的方法就是递归遍历。只要记住:
前序遍历是根->左->右
中序遍历是左->根->右
后序遍历是左->右->根
那么递归的顺序一目了然。
/**
* 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<int> inorderTraversal(TreeNode* root) {
vector<int> final_answer;
if(root==NULL){
return final_answer;
}
middleOrderTraversal(final_answer,root);
return final_answer;
}
void middleOrderTraversal(vector<int>& v_list,TreeNode* root) {//传递记得加上引用个哈哈
if(root==NULL){
return;
}
middleOrderTraversal(v_list,root->left);
v_list.push_back(root->val);
middleOrderTraversal(v_list,root->right);
}
};
只要换一下下面的代码的顺序就可以了。
然后看到评论区的题解,发现很有意思,就是前中后遍历的代码是一样的:
在树的深度优先遍历中(包括前序、中序、后序遍历),递归方法最为直观易懂,但考虑到效率,我们通常不推荐使用递归。
栈迭代方法虽然提高了效率,但其嵌套循环却非常烧脑,不易理解,容易造成 “一看就懂,一写就废” 的窘况。而且对于不同的遍历顺序(前序、中序、后序),循环结构差异很大,更增加了记忆负担。
因此,我在这里介绍一种 “颜色标记法” (瞎起的名字……),兼具栈迭代方法的高效,又像递归方法一样简洁易懂,更重要的是,这种方法对于前序、中序、后序遍历,能够写出完全一致的代码。
其核心思想如下:
使用颜色标记节点的状态,新节点为白色,已访问的节点为灰色。
如果遇到的节点为白色,则将其标记为灰色,然后将其右子节点、自身、左子节点依次入栈。
如果遇到的节点为灰色,则将节点的值输出。作者:henry
链接:https://leetcode.cn/problems/binary-tree-inorder-traversal/solutions/25220/yan-se-biao-ji-fa-yi-chong-tong-yong-qie-jian-ming/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
class Solution:
def inorderTraversal(self, root: TreeNode) -> List[int]:
WHITE, GRAY = 0, 1
res = []
stack = [(WHITE, root)]
while stack:
color, node = stack.pop()
if node is None: continue
if color == WHITE:
stack.append((WHITE, node.right))
stack.append((GRAY, node))
stack.append((WHITE, node.left))
else:
res.append(node.val)
return res
五、贪心算法
121.买股票的最佳时机
(1)题目
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0
。
示例 1:
输入:[7,1,5,3,6,4] 输出:5 解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。 注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。
示例 2:
输入:prices = [7,6,4,3,1] 输出:0 解释:在这种情况下, 没有交易完成, 所以最大利润为 0。
(2)解答
就是说我又一次的忘记了命名不能够作为变量...我真傻。
#include <bits/stdc++.h>
class Solution {
public:
int maxProfit(vector<int>& prices) {
int min_price=pow(10,4);
int profit=0;
for(int i=0;i<prices.size();i++){
profit=max(profit,prices[i]-min_price);
min_price=min(min_price,prices[i]);
}
return profit;
}
};