打家劫舍
class Solution {
public:
int rob(vector<int>& nums) {
int n = nums.size();
if(n == 1){
return nums[0];
}
vector<int> dp(n, 0);
dp[0] = nums[0];//有一间房可以偷
//有两间房可以偷
if(nums[1] > nums[0]){
dp[1] = nums[1];
}else{
dp[1] = nums[0];
}
for (int i = 2; i < n; i++) {
dp[i] = max(dp[i-1], dp[i-2]+nums[i]);
}
return dp[n-1];
}
};
买股票的最佳时机
一次遍历
class Solution {
public:
int maxProfit(vector<int>& prices) {
int minPrice = INT_MAX, sum = 0;
for(int price:prices){
sum = max(sum, price-minPrice);
minPrice = min(minPrice,price);
}
return sum;
}
};
买股票的最佳时机二
给一个整数数组prices,其中prices[i]表示第i天价格。
动态规划
dp[i][0]:表示第i天交易完后手里没有股票的最大利润,dp[i][1]表示第i天交易完后手里持有一只的最大利润。
class Solution {
public:
int maxProfit(vector<int>& prices) {
int n = prices.size();
if(n == 1){
return 0;
}
vector<vector<int>> dp(n, vector<int>(2,0));
dp[0][0] = 0;
dp[0][1] = -prices[0];
for(int i=1; i<n; i++){
//dp[i][0],第i-1天就没有,或者第i天卖了
dp[i][0] = max(dp[i-1][0], dp[i-1][1]+prices[i]);
//dp[i][1],第i-1天没有,第i天买了
dp[i][1] = max(dp[i-1][0]-prices[i], dp[i-1][1]);
}
return max(dp[n-1][0], dp[n-1][1]);
}
};
跳跃游戏
求到达nums[n-1]的最小跳跃次数。
class Solution {
public:
int jump(vector<int>& nums) {
int n = nums.size();
vector<int> dp(n, INT_MAX);
dp[0] = 0;
for(int i=0; i<n; i++){
for(int j=i+1; j<=i+nums[i] && j<=n-1; j++){
dp[j] = min(dp[j], dp[i]+1);
}
}
return dp[n-1];
}
};
H指数
数组citations表示研究者第i篇论文被引用的次数,计算并返回研究者的h指数。
至少发表h篇论文,至少有h篇论文被引用次数大于等于h。
class Solution {
public:
int hIndex(vector<int>& citations) {
int n = citations.size();
int h = n;
sort(citations.begin(), citations.end());
while(h){
int count = 0;
for(int i=n-1; i>=0; i--){
if(citations[i] >= h){
count++;
}
}
if(count >= h){
return h;
}
h--;
}
return 0;
}
};
O(1)时间插入、删除和获取随机元素
实现RandomizedSet类
变长数组+哈希表
这道题要求实现一个类,满足插入、删除和获取随机元素操作的平均时间复杂度O(1)。
变长数组和哈希表结合,变长数组中存储元素,哈希表中记录每个元素在变长数组中的下标。
class RandomizedSet {
private:
vector<int> nums;
unordered_map<int, int> indices;
public:
RandomizedSet() {
srand((unsigned)time(NULL));
}
bool insert(int val) {
if(indices.count(val)){
return false;
}
nums.push_back(val);
indices[val] = nums.size()-1;
return true;
}
bool remove(int val) {
if(indices.count(val)){
int index = indices[val];
int last = nums.back();
nums[index] = last;
indices[last] = index;
nums.pop_back();
indices.erase(val);
return true;
}
return false;
}
int getRandom() {
int randomIndex = rand()%nums.size();
return nums[randomIndex];
}
};
/**
* Your RandomizedSet object will be instantiated and called as such:
* RandomizedSet* obj = new RandomizedSet();
* bool param_1 = obj->insert(val);
* bool param_2 = obj->remove(val);
* int param_3 = obj->getRandom();
*/
除自身以外数组的乘积
要求不使用除法,并且在O(n)时间复杂度内完成
左右乘积列表
我们不必将所有数字的乘积除以给定索引处的数字得到相应的答案,而是利用索引左侧所有数字的乘积和右侧所有数字的乘积相乘得到答案。
- 初始化两个空数组L和R,L[i]表示左侧乘积,R[i]表示右侧乘积
class Solution {
public:
vector<int> productExceptSelf(vector<int>& nums) {
int n = nums.size();
vector<int> res(n,1);
vector<int> left(n);
vector<int> right(n);
left[0] = 1;
right[n-1] = 1;
for(int i=1; i<n; i++){
left[i] = left[i-1] * nums[i-1];
}
for(int i=n-2; i>=0; i--){
right[i] = right[i+1] *nums[i+1];
}
for(int i=0; i<n; i++){
res[i] = left[i]*right[i];
}
return res;
}
};
罗马数字转整数
class Solution {
private:
unordered_map <char,int> map1 = {
{'I',1},
{'V',5},
{'X',10},
{'L',50},
{'C',100},
{'D',500},
{'M',1000}
};
public:
int romanToInt(string s) {
int sum = 0;
for(int i=0; i<s.size(); i++){
char c = s[i];
if(i < s.size()-1 && map1[c] < map1[s[i+1]]){
sum -= map1[c];
}else{
sum += map1[c];
}
}
return sum;
}
};
两数之和二——输入有序数组
给你一个下标从1开始的整数数组numbers,该数组已按非递减顺序排列。
左右指针
class Solution {
public:
vector<int> twoSum(vector<int>& numbers, int target) {
int left = 0;
int right = numbers.size()-1;
while(left < right){
if(numbers[left] + numbers[right] == target){
return {left+1, right+1};
}else if(numbers[left] + numbers[right] > target){
right--;
}else{
left++;
}
}
return {1,2};
}
};
长度最小的子数组
给定一个含有n个正整数的数组和一个正整数target。
找出该数组中满足其总和大于等于target的长度最小的子数组,并返回长度。如果不存在符合条件的子数组,返回0。
滑动窗口
定义两个指针start和end分别表示子数组(滑动窗口的开始位置和结束位置),维护变量sum存储子数组中的元素和。
初始状态下,start和end都指向下标0,sum的值为0。
每一轮迭代,将nums[end]加到sum,如果sum≥s,则更新子数组的最小长度(此时子数组的长度是end-start+1)
给定一个含有n个正整数的数组和一个正整数target。
找出总和大于等于target的最小子数组的长度,如果没有,返回0。
class Solution{
public:
int minSubArrayLen(int target, vector<int>& nums){
int n = nums.size();
int start = 0;
int end = 0;
int sum = 0;
int ans = INT_MAX;
while(end < n){
sum += nums[end];
while(sum >= target){
ans = min(ans, end-start+1);
sum -= nums[start];
start++;
}
end++;
}
return ans==INT_MAX ? 0:ans;
}
};
无重复字符的最长子串
class Solution {
public:
int lengthOfLongestSubstring(string s) {
unordered_set<char> set1;
int start = 0;
int end = 0;
int n = s.size();
int ans = 0;
while(end < n){
//如果最后一个字符不在已有的字符串中,则添加
if(!set1.count(s[end])){
set1.insert(s[end]);
ans = max(ans, end-start+1);
end++;
}else{
while(set1.count(s[end])){
set1.erase(s[start]);
start++;
}
}
}
return ans;
}
};
单词规律
给定一种规律pattern和一个字符串s,判断s是否遵循相同的规律。
class Solution{
public:
bool wordPattern(string pattern, string s){
unordered_map<char, string> chToStr;
unordered_map<string, char> strToCh;
vector<string> strVector;
istringstream iss(s);
string word;
while(iss >> word){
strVector.push_back(word);
}
//检查长度
if(pattern.size() != strVector.size()){
return false;
}
for(int i=0; i<pattern.size(); i++){
char ch = pattern[i];
string str = strVector[i];
//字符到字符串映射
if(chToStr.count(ch)){
if(chToStr[ch] != str){
return false;
}
}else{
chToStr[ch] = str;
}
//字符串到字符映射
if(strToCh.count(str)){
if(strToCh[str] != ch){
return false;
}
}else{
strToCh[str] = ch;
}
}
return true;
}
};
插入区间
给一个无重叠的,按照区间起始端点排序的区间列表intervals。
class Solution {
public:
vector<vector<int>> insert(vector<vector<int>>& intervals, vector<int>& newInterval) {
vector<vector<int>> res;
int n = intervals.size();
int i;
//前半段
for(i=0; i<n; i++){
if(intervals[i][1] < newInterval[0]){
res.push_back(intervals[i]);
}else{
break;
}
}
//中间
for(i; i<n; i++){
if(intervals[i][0] <= newInterval[1]){
newInterval[0] = min(intervals[i][0], newInterval[0]);
newInterval[1] = max(intervals[i][1], newInterval[1]);
}else{
break;
}
}
res.push_back(newInterval);
//尾部
for(i; i<n; i++){
res.push_back(intervals[i]);
}
return res;
}
};