21. 合并两个有序链表
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
/**
* 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) {
ListNode* preHead = new ListNode(-1);
ListNode* prev = preHead;
while(list1 != nullptr && list2 != nullptr){
if(list1->val < list2->val){
prev->next=list1;
list1=list1->next;
}
else{
prev->next=list2;
list2=list2->next;
}
prev = prev->next;
}
prev->next = list1==nullptr?list2:list1;
return preHead->next;
}
};
递归
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
if (l1 == NULL) {
return l2;
}
if (l2 == NULL) {
return l1;
}
if (l1->val <= l2->val) {
l1->next = mergeTwoLists(l1->next, l2);
return l1;
}
l2->next = mergeTwoLists(l1, l2->next);
return l2;
}
};
二分 35. 搜索插入位置
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。
示例 1:
输入: nums = [1,3,5,6], target = 5
输出: 2
示例 2:
输入: nums = [1,3,5,6], target = 2
输出: 1
示例 3:
输入: nums = [1,3,5,6], target = 7
输出: 4
返回第一个大于等于target
的下标,或者nums.size()
- 因为要找的是 大于等于
target
的,所以if(nums[mid]<target) l=mid+1;
(mid之前的都小于target,所以下次的范围直接从mid+1开始) - 因为题目还可能出现
nums.size()
,所以r的初始化为nums.size()
class Solution {
public:
int searchInsert(vector<int>& nums, int target) {
int l=0,r=nums.size();
int mid;
while(l<r){
mid=(l+r)>>1;
if(nums[mid]>=target) r=mid;
else if(nums[mid]<target) l=mid+1;
}
return l;
}
};
详细二分题解力扣 对二分查找不是套模板并往里面填空,需要仔细分析题意
22. 括号生成
数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
示例 1:
输入:n = 3
输出:[“((()))”,“(()())”,“(())()”,“()(())”,“()()()”]
示例 2:
输入:n = 1
输出:[“()”]
class Solution {
public:
vector<string> generateParenthesis(int n) {
//dp[i]:i对括号组成的集合
//思路:符合条件的左边肯定是左括号,后面一定有与之匹配的右括号
//"("+ ... + ")"+...
if(n==0) return {};
if(n==1) return {"()"};
vector<vector<string>> dp(n+1);
dp[0]={""};
dp[1]={"()"};
for(int i=2;i<=n;i++)
for(int j=0;j<i;j++)//i个括号
for(string p:dp[j])//i个括号又分成了j+(i-j-1)+1
for(string q:dp[i-j-1]){
string str="("+p+")"+q;
dp[i].push_back(str);
}
return dp[n];
}
};
27. 移除元素
模板
给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素,并返回移除后数组的新长度。
不要使用额外的数组空间,你必须仅使用 O(1) 额外空间并 原地 修改输入数组。
元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
[l, -1]
的元素都是等于target
的
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
if(nums.size()==0) return 0;
if(nums.size()==1){
if(nums[0]==val) return 0;
else return 1;
}
//双指针
int l=0,r=nums.size()-1;
while(l<=r){
if(nums[r]!=val && nums[l]==val)
swap(nums[l],nums[r]);
if(nums[r]==val) r--;
if(nums[l]!=val) l++;
}
return l;
}
};
72. 编辑距离
给你两个单词 word1 和 word2, 请返回将 word1 转换成 word2 所使用的最少操作数 。
你可以对一个单词进行如下三种操作:
插入一个字符
删除一个字符
替换一个字符
class Solution {
public:
int minDistance(string word1, string word2) {
//dp[i][j]: s1的前i个变为s2的前j个需要的最小操作数
int n1=word1.size(),n2=word2.size();
vector<vector<int>> dp(n1+1,vector<int>(n2+1,0));
for(int i=0;i<=n2;i++)
dp[0][i]=i;
for(int i=0;i<=n1;i++)
dp[i][0]=i;//dp[0]是没有用的
for(int i=1;i<=n1;i++){
for(int j=1;j<=n2;j++){
if(word1[i-1]!=word2[j-1])//注意下标和dp[i][j]里的ij之间的区别
dp[i][j]=min(dp[i-1][j]+1,min(dp[i-1][j-1]+1,dp[i][j-1]+1));
else
dp[i][j]=dp[i-1][j-1];
}
}
return dp[n1][n2];
}
};
139. 单词拆分
给你一个字符串 s 和一个字符串列表 wordDict 作为字典。请你判断是否可以利用字典中出现的单词拼接出 s 。
注意:不要求字典中出现的单词全部都使用,并且字典中的单词可以重复使用。
示例 1:
输入: s = “leetcode”, wordDict = [“leet”, “code”]
输出: true
解释: 返回 true 因为 “leetcode” 可以由 “leet” 和 “code” 拼接成。
示例 2:
输入: s = “applepenapple”, wordDict = [“apple”, “pen”]
输出: true
解释: 返回 true 因为 “applepenapple” 可以由 “apple” “pen” “apple” 拼接成。
注意,你可以重复使用字典中的单词。
class Solution {
public:
bool wordBreak(string s, vector<string>& wordDict) {
unordered_set<string> p;
for(string tmp: wordDict){
p.insert(tmp);
}
//dp[i]:s的前i个字符是否能被空格拆分成若干个字典中出现的单词
int n=s.size();
vector<bool> dp(n+1);
dp[0]=true;
for(int i=1;i<=n;i++){
for(int j=0;j<i;j++){
dp[i]=dp[j] && (p.find(s.substr(j,i-j))!=p.end());
if(dp[i]) break;
}
}
return dp[n];
}
};