数组 和 字符串
注:博客内容只限于个人学习。图片和内容可能整合了多渠道的信息来源,如侵联系可删。
归并排序 分治思想
分: 不断将数组从中点位置划分开(即二分法),将整个数组的排序问题转化为子数组的排序问题;
治: 划分到子数组长度为 1 时,开始向上合并,不断将 较短排序数组 合并为 较长排序数组,直至合并至原数组时完成排序;
参考
https://leetcode-cn.com/problems/shu-zu-zhong-de-ni-xu-dui-lcof/solution/jian-zhi-offer-51-shu-zu-zhong-de-ni-xu-pvn2h/
/*
merge_sort() 归并排序与逆序对统计:
1.终止条件: 当 l≥r 时,代表子数组长度为 1 ,此时终止划分;
2.递归划分: 计算数组中点 m ,递归划分左子数组 merge_sort(l, m) 和右子数组 merge_sort(m + 1, r) ;
3.合并与逆序对统计:
1.暂存数组 nums 闭区间 [i, r] 内的元素至辅助数组 tmp ;
2.循环合并:设置双指针 i , j 分别指向左 / 右子数组的首元素;
* 当 i = m + 1 时: 代表左子数组已合并完,因此添加右子数组当前元素tmp[j] ,并执行 j = j + 1 ;
* 否则,当 j = r + 1 时: 代表右子数组已合并完,因此添加左子数组当前 元素 tmp[i] ,并执行 i = i + 1 ;
* 否则,当tmp[i]≤tmp[j] 时: 添加左子数组当前元素 tmp[i] ,并执行 i=i+1;
* 否则(即 tmp[i]>tmp[j])时: 添加右子数组当前元素 tmp[j] ,并执行 j=j+1 ;此时构成m−i+1 个「逆 序对」,统计添加至 res ;
4.返回值: 返回直至目前的逆序对总数 res;
reversePairs() 主函数:
1.初始化: 辅助数组 tmp ,用于合并阶段暂存元素;
2.返回值: 执行归并排序 merge_sort() ,并返回逆序对总数即可
*/
class Solution {
public:
int reversePairs(vector<int>& nums) {
vector<int> tmp(nums.size());
return mergeSort(0, nums.size() - 1, nums, tmp);
}
private:
int mergeSort(int l, int r, vector<int>& nums, vector<int>& tmp) {
// 终止条件
if (l >= r) return 0;
// 递归划分
int m = l + (r-l)>> 2;
int res = mergeSort(l, m, nums, tmp) + mergeSort(m + 1, r, nums, tmp);
// 合并阶段
int i = l, j = m + 1;
for (int k = l; k <= r; k++)
tmp[k] = nums[k];
for (int k = l; k <= r; k++) {//填补tmp数组对应位置
if (i == m + 1)
nums[k] = tmp[j++];
else if (j == r + 1 || tmp[i] <= tmp[j])
nums[k] = tmp[i++];
else {
nums[k] = tmp[j++];
res += m - i + 1; // 统计逆序对
}
}
return res;
}
};
数组题常见套路
1.双指针:双指针灵活擦操作,可以起到降低一个等级复杂度的作用
2.滑动窗口:重要性质是窗口的左边界和右边界永远只能向右移动,而不能向左移动。这是为了保证滑动窗口的时间复杂度是 O(n)。如果左右边界向左移动的话,这叫做“回溯”,算法的时间复杂度就可能不止 O(n)。(又可以分为固定窗口,可变窗口)
在滑动窗口类型的问题中都会有两个指针,一个用于「延伸」现有窗口的 r 指针,和一个用于「收缩」窗口的 l 指针。在任意时刻,只有一个指针运动,而另一个保持静止
3.根据数组中数据操作的需求可以选取适当数据结构,如栈,堆,队列等
需要保留和丢弃相邻的元素:栈
//c++上下取整数
#include <math.h> double floor(double x );
float floorf(float x);
long double floorl(long double x);
double floor(double x);S
double ceil(double x);
使用floor函数。floor(x)返回的是小于或等于x的最大整数。
如: floor(10.5) == 10 floor(-10.5) == -11
使用ceil函数。ceil(x)返回的是大于x的最小整数。
如: ceil(10.5) == 11 ceil(-10.5) ==-10
floor()是向负无穷大舍入,floor(-10.5) == -11;
ceil()是向正无穷大舍入,ceil(-10.5) == -10
滑动窗口例题:
904. 水果成篮
76. 最小覆盖子串
滑动窗口总结:https://mp.weixin.qq.com/s?__biz=MzAxODQxMDM0Mw==&mid=2247485141&idx=1&sn=0e4583ad935e76e9a3f6793792e60734&scene=21#wechat_redirect
C++ STL中Map的按Value排序
#include<bits/stdc++.h>
using namespace std;
typedef pair<string, int> PAIR;
bool cmp_by_value(const PAIR& l, const PAIR& r) {
return l.second < r.second;
}
int main() {
map<string, int> name_score_map;
name_score_map["LiMin"] = 90;
name_score_map["ZiLinMi"] = 79;
name_score_map["BoB"] = 92;
name_score_map.insert(make_pair("BoB",99));//不会插入或者更新
name_score_map.insert(make_pair("Albert",86));
//把map中元素转存到vector中,以pair的形式
vector<PAIR> name_score_vec(name_score_map.begin(), name_score_map.end());
sort(name_score_vec.begin(), name_score_vec.end(), cmp_by_value);
for (auto iter: name_score_vec) {//这里的ater不是迭代器,是指代元素,要注意
cout << iter.first << endl;//注意:pair不能以元素遍历形式取出,只能以.first或者.second取key或者value。(map迭代器则用->,如下)
// for (auto iter=name_score_map.begin();iter!=name_score_map.end();iter++) {
// cout << iter->second<< endl;
}
return 0;
}
//技巧:也可以把key和value颠倒组成pair,然后存进vector,这样不用写cmp
map set pair 好题:(2034. 股票价格波动)
收获:
-
map赋值的两种方式:map[key]=value ,如果已经有该key,会覆盖原始值 ,map.insert(pair<int,int>(2,4))形式则不会插入也不会修改
-
灵活处理map,set和pair。核心:pair数据类型,map和set是容器,且这两个容器都会自动排序(map根据key。set如果存的是pair类型,则会根具pair第一个元素)
-
//map的erase()可以这样写: auto iter = m.find(1);//找到要删除的迭代器 if(iter!=m.end()) m.erase(iter); //set的erase(),括号中是set中的元素
-
//set常用操作: 除了insert(),erase(),size(),empty()常用: rbegin():返回指向最后一个(注意,是已排好序的最后一个)元素的迭代器 find(val):在set容器中查找值为val的元素,如果成功找到,则返回指向该元素的双向迭代器;反之,则返回和end() 方法一样的迭代器 count(val) 在当前 set 容器中,查找值为 val 的元素的个数,并返回。注意,由于 set 容器中各元素的值是唯一的,因此该函数的返回值最大为 1
-
set,map和vector容器互相转换的问题: //vector和set可以自由转换 vec = { 1, 2, 3, 4, 8, 9, 3, 2, 1, 0, 4, 8 }; set<int> st(vec.begin(), vec.begin()+3);两者互相截取迭代器区间即可 //vector和map转换 map.insert(pair<int,int>(1,2));//pair类型赋值 vector<pair<int,int>> vec(map.begin(),map.end());//map类型转进vec,类型为pair
字符串拼接可以直接用‘+’
//substr用法
#include<string>
#include<iostream>
using namespace std;
int main()
{
string s("12345asdf");
string a = s.substr(0,5); //获得字符串s中从第0位开始的长度为5的字符串
cout << a << endl;
//substr越界问题:
https://blog.csdn.net/saiolive/article/details/53102298?spm=1001.2101.3001.6650.2&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-2.no_search_link&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7Edefault-2.no_search_link
string 对象支持互相直接赋值
未完待续…