21. 合并两个有序链表
1. 题目
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的
合并两个有序链表
2. 代码
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 == nullptr) return list2;
if (list2 == nullptr) return list1;
if (list1->val < list2->val) {
list1->next = mergeTwoLists(list1->next, list2);
return list1;
} else {
list2->next = mergeTwoLists(list2->next, list1);
return list2;
}
}
};
206. 反转链表
1. 题目
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表
输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]
2. 代码
ListNode* reverseList(ListNode* head) {
//双指针
// ListNode* cur = head;
// ListNode* pre = nullptr;
// while (cur != nullptr) {
// ListNode* tmp = cur->next;
// cur->next = pre;
// pre = cur;
// cur = tmp;
// }
// return pre;
// 递归
if (head == NULL || head->next == NULL) {
//直到当前节点的下一个节点为空时返回当前节点
//由于5没有下一个节点了,所以此处返回节点5
return head;
}
//递归传入下一个节点,目的是为了到达最后一个节点
ListNode* node = reverseList(head->next);
head->next->next = head;
head->next = NULL;
return node;
}
3. 题解
双指针:
- 将每个结点的后一个结点放置在当前结点的前一个结点
递归:
(题解来自)
- 第一轮出栈,head为5,head.next为空,返回5
- 第二轮出栈,head为4,head.next为5,执行head.next.next=head也就是5.next=4,
把当前节点的子节点的子节点指向当前节点
此时链表为1->2->3->4<->5,由于4与5互相指向,所以此处要断开4.next=null
此时链表为1->2->3->4<-5
返回节点5 - 第三轮出栈,head为3,head.next为4,执行head.next.next=head也就是4.next=3,
此时链表为1->2->3<->4<-5,由于3与4互相指向,所以此处要断开3.next=null
此时链表为1->2->3<-4<-5
返回节点5 - 第四轮出栈,head为2,head.next为3,执行head.next.next=head也就是3.next=2,
此时链表为1->2<->3<-4<-5,由于2与3互相指向,所以此处要断开2.next=null
此时链表为1->2<-3<-4<-5
返回节点5 - 第五轮出栈,head为1,head.next为2,执行head.next.next=head也就是2.next=1,
此时链表为1<->2<-3<-4<-5,由于1与2互相指向,所以此处要断开1.next=null
此时链表为1<-2<-3<-4<-5
返回节点5 - 出栈完成,最终头节点5->4->3-2->1
77. 组合
1. 题目
给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。
你可以按 任何顺序 返回答案
组合
2. 代码
class Solution {
public:
vector<int> temp;
vector<vector<int>> ans;
void dfs(int cur, int n, int k) {
// 剪枝:temp 长度加上区间 [cur, n] 的长度小于 k,不可能构造出长度为 k 的 temp
if (temp.size() + (n - cur + 1) < k) {
return;
}
// 记录合法的答案
if (temp.size() == k) {
ans.push_back(temp);
return;
}
// 考虑选择当前位置
temp.push_back(cur);
dfs(cur + 1, n, k);
temp.pop_back();
// 考虑不选择当前位置
dfs(cur + 1, n, k);
}
vector<vector<int>> combine(int n, int k) {
dfs(1, n, k);
return ans;
}
};
3. 题解
- 需要找到一个长度为 n 的序列 的所有子序列,当前位置是cur,原序列总长度为 n, 原序列的每个位置在答案序列种的状态有被选中和不被选中两种,我们用 temp 数组存放已经被选出的数字;
- 在进入 dfs(cur, n, k)之前[1,cur−1] 位置的状态是确定的,而 [cur,n] 内位置的状态是不确定的,dfs(cur,n,k) 需要确定cur 位置的状态,然后求解子问题 dfs(cur+1,n,k)。
- 对于cur 位置,我们需要考虑[cur] 取或者不取,如果取,我们需要把 [cur] 放入一个临时的答案数组中temp,再执行dfs(cur+1,n,k),执行结束后需要对temp 进行回溯;
temp.push_back(cur);
dfs(cur + 1, n, k);
- 如果不取,则直接执行dfs(cur+1,n,k)。在整个递归调用的过程中,cur 是从小到大递增的,当 cur 增加到 n + 1的时候,记录答案并终止递归
temp.pop_back();
// 考虑不选择当前位置
dfs(cur + 1, n, k);
- 如果当前temp 的大小为 s,未确定状态的区间 [cur,n] 的长度为 t,如果 s + t < k,那么即使 t 个都被选中,也不可能构造出一个长度为 k 的序列,故这种情况就没有必要继续向下递归,做一个剪枝,
if (temp.size() + (n - cur + 1) < k) {
return;
}
46. 全排列
1. 题目
给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案
全排列
2. 代码
class Solution {
public:
int n;
vector <int> res;
vector <vector <int>> ans;
vector <bool> used;
void dfs (vector<int>& nums, int k) {
if (k == n) {
ans.push_back (res);
return ;
}
for (int i = 0;i < n;i++) {
if (!used[i]) {
used[i] = true;//为下一次递归做准备
res[k] = nums[i];
dfs (nums,k+1);
used[i] = false;
}
}
}
vector<vector<int>> permute(vector<int>& nums) {
n = nums.size ();
used.resize (n);
res.resize (n);
dfs (nums,0);
return ans;
}
};
3. 题解
- 数组 nums所有的排列都有一个特点,排列中数据不重复, 且排列中包含数组中所有数据
- k代表当前位置, k == n 代表遍历结束, 获得一个排列结果
- 循环遍历数据,当前遍历数据若没有被使用,加入res中,并将当前位置标识为被使用过
递归判断k+1,直到当前数组中数据全被使用,即k==n,此时获取到一个排列结果 - 在每次dfs return之后,used[i] = false; 当前数据标识成未被使用,为下一次循环做准备
784. 字母大小写全排列
1. 题目
给定一个字符串S,通过将字符串S中的每个字母转变大小写,我们可以获得一个新的字符串。返回所有可能得到的字符串集合
示例:
输入:S = "a1b2"
输出:["a1b2", "a1B2", "A1b2", "A1B2"]
输入:S = "3z4"
输出:["3z4", "3Z4"]
输入:S = "12345"
输出:["12345"]
2. 代码
class Solution {
public:
vector<string> ret;
string str;
void dfs (const string s, int cur, int n) {
if (cur == n) {
ret.push_back(str); // string遍历完成存入ret
return;
}
// 针对字母类型字符,有两种情况,当前字符和其对应的大/小字母,针对其他字符,只有当前字符一种情况
if(s[cur] >= 65 && s[cur] <= 90) { // 如果当前是大写字母,存入对应的小写字母
str[cur] = s[cur] + 32;
dfs(s, cur+1, n);
} else if(s[cur] >= 97 && s[cur] <= 122) { // 如果当前是小写字母,存入对应的大写字母
str[cur] = s[cur] - 32;
dfs(s, cur+1, n);
}
str[cur] = s[cur]; // 存入当前字符
dfs(s, cur+1, n);
}
vector<string> letterCasePermutation(string s) {
int length = s.length();
str.resize(length);
dfs(s,0,length);
return ret;
}
};
3. 题解
- 针对字母类型字符,有两种情况,当前字符和其对应的大/小字母, 如果是大写则转换为小写, 如果是小写则转换为大写
- 针对其他字符,只有当前字符一种情况, 不进行转换