1 线段树
1.1 建立线段树
1.2 单点修改
1.3 区间修改
1.4 区间查询
#include <iostream>
#include <cctype>
#include <string>
#include <algorithm>
#include <vector>
#include <map>
#include <unordered_map>
#include <unordered_set>
#include <cstdio>
#include <functional>
using namespace std;
class SegmentTree {
private:
struct Node {
int l, r, sum,lazy;
Node() :l(-1), r(-1), sum(0),lazy(0) {}
};
const int MAX;
vector<Node> tree;
vector<int> data;//index from 1
int n;
public:
SegmentTree(vector<int>&nums) :MAX(4 * nums.size()), tree(vector<Node>(MAX)), data(nums),n(nums.size()) {
data.insert(data.begin(), 0);
build(1, 1, n);
}
//使用子节点更新父节点
void pushUp(int k) {
tree[k].sum = tree[k << 1].sum + tree[k << 1 | 1].sum;
}
//建立线段树,数据和树下标都从1起
void build(int i, int l, int r) {
tree[i].l = l, tree[i].r = r;
//根节点
if (l == r) {
tree[i].sum = data[l];
return;
}
//递归建子树
int mid = l + (r - l)/2;
build(i << 1, l, mid);
build(i << 1 | 1, mid + 1, r);
//使用子节点更新父节点
pushUp(i);
}
//区间查询
int query(int cur,int l, int r) {
int L = tree[cur].l, R = tree[cur].r;
//当前节点区间被目标区间完全包含,作为查询结果的一部分返回
if (L >= l && R <= r)
return tree[cur].sum;
//当前节点与目标区间无交集
if (L > r || R < l)return 0;
//判断当前节点的子树是否与目标区间有交集
int res = 0;
//pushDown
pushDown(cur);
//计算左右子树
if(tree[cur<<1].r>=l)res+=query(cur << 1, l, r);
if(tree[cur<<1|1].l<=r)res+=query(cur << 1 | 1, l, r);
return res;
}
//单点更新 将索引为k的位置改为val 要更新所有包含该点的区间的信息
void change(int k, int val) {
_change(1, k, val);
}
void _change(int cur, int k,int val) {
int L = tree[cur].l, R = tree[cur].r;
//更新根节点
if (L == R) {
tree[cur].sum = val;
return;
}
//更新左右子树
int mid = (L + R) / 2;
if (mid >= k)_change(cur << 1, k, val);
else _change(cur << 1 | 1, k, val);
//更新当前节点的信息
pushUp(cur);
}
//区间更新,指定区间内的每一个位置都+val
void change_segment(int l, int r, int val) {
_change_segment(1, l, r, val);
}
void _change_segment(int cur, int l, int r, int val) {
int L = tree[cur].l, R = tree[cur].r;
//当前区间完全被目标区间包含
if (L >= l && R <= r) {
tree[cur].sum += (R - L + 1)*val;
//lazy表示当前节点已更新,但子树还未更新
tree[cur].lazy += val;
return;
}
int mid = (l + r) / 2;
//pushDown
pushDown(cur);
if (L <= mid)_change_segment(cur << 1, l, r, val);
if (R > mid)_change_segment(cur << 1 | 1, l, r, val);
pushUp(cur);
}
//下推标记
void pushDown(int cur) {
if (tree[cur].lazy) {
//下推标记
tree[cur << 1].lazy += tree[cur].lazy;
tree[cur << 1 | 1].lazy += tree[cur].lazy;
//更新子节点的信息
tree[cur << 1].sum += (tree[cur << 1].r - tree[cur << 1].l)*tree[cur].lazy;
tree[cur << 1 | 1].sum += (tree[cur << 1 | 1].r - tree[cur << 1 | 1].l)*tree[cur].lazy;
//clear
tree[cur].lazy = 0;
}
}
};
int main() {
vector<int> vec = { 1,2,3,4,5 };
SegmentTree ta(vec);
int a, b,c;
char ch;
while (1) {
cin >>ch;
if (ch == 'q') {//query
cin >> a >> b;
cout << ta.query(1, a, b) << endl;
}
else if (ch == 'c') {//change
cin >> a >> b;
ta.change(a, b);
}
else if (ch == 's') {//change_segment
cin >> a >> b >> c;
ta.change_segment(a, b, c);
}
}
system("pause");
}
1.5 树状数组
1.5.1 树状数组模板
class TreeArray{
//index from 1
vector<int> data;
int n;
vector<int> c;
public:
TreeArray(vector<int>&nums):data(nums),n(nums.size()),c(vector<int>(n+1,0)){
data.insert(data.begin(),0);
for(int i=1;i<=n;i++)
add(i,data[i]);
cout<<endl;
}
int getSum(int i){
int copy=i;
int res=0;
while(i>0){
res+=c[i];
i-=lowbit(i);
}
return res;
}
//换值操作:直接修改值
void update(int i,int val){
int add = val - data[i];
data[i]=val;
while(i<=n){
c[i]+=add;
i+=lowbit(i);
}
}
//加值操作:向某一位置加上一个值
void add(int i,int val){
while(i<=n){
c[i]+=val;
i+=lowbit(i);
}
}
int lowbit(int x){
return x&(-x);
}
};
1.5.2 树状数组的应用: 求树组中的逆序对
class Solution {
class TreeArray{
vector<int> tree;
int n;
int lowbit(int x){return x&(-x);}
public:
TreeArray(int _n): n(_n),tree(_n+1){
}
int getSum(int i){
int res=0;
while(i>0){
res+=tree[i];
i-=lowbit(i);
}
return res;
}
void add(int i,int val){
while(i<=n){
tree[i]+=val;
i+=lowbit(i);
}
}
};
public:
int reversePairs(vector<int>& nums) {
int n = nums.size();
// 离散化
vector<int> copy = nums;
sort(copy.begin(),copy.end());
vector<int> index(nums.size(),0);
for(int i=0;i<nums.size();i++)
index[i]=lower_bound(copy.begin(),copy.end(),nums[i])-copy.begin()+1;
// 先放置下标在后面的数
TreeArray treeArray(n);
int res=0;
for(int i=n-1;i>=0;i--){
res+=treeArray.getSum(index[i]-1);
treeArray.add(index[i],1);
}
return res;
}
};
2 二叉搜索树
2.1 建树
2.2 插入节点
2.3 删除节点
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* deleteNode(TreeNode* root, int key) {
if(!root)return NULL;
// root->val != key
if(root->val<key)root->right = deleteNode(root->right,key);
else if(root->val>key)root->left = deleteNode(root->left,key);
else {
if(!root->right)return root->left;
if(!root->left)return root->right;
// find minValue of right subTree
TreeNode * cur = root->right;
while(cur->left)cur=cur->left;
// swap value
root->val = cur->val;
cur->val = key;
// delete again
root->right=deleteNode(root->right,key);
}
return root;
}
};
3 红黑树
4 堆
4.1 大顶堆与升序排序
class MaxHeap{
private:
vector<int> data;
int n;
public:
MaxHeap(vector<int>&arr): data(arr),n(arr.size()){}
void sink(int i){
// 2*n+1, 2*n+2
while(2*i+1<n){
i=2*i+1;
if(i+1<n&&data[i+1]>data[i])++i;
if(data[(i-1)>>1]<data[i])
swap(data[(i-1)>>1],data[i]);
}
}
void makeHeap(){
for(int i=(n-1)/2;i>=0;i--)sink(i);
}
void heapSort(){
while(n>0){
swap(data[0],data[--n]);
sink(0);
}
}
void print(){
for(auto d:data)cout<<d<<" ";
cout<<endl;
}
};
int main() {
vector<int> nums={1,3,5,10,9,7,6,2,8};
MaxHeap heap(nums);
heap.makeHeap();
heap.heapSort();
heap.print();
}
5 哈希、有序哈希
5.1 链地址法
5.2 LRU缓存
class LRUCache {
list<pair<int,int>> data;//<key,value>
unordered_map<int,list<pair<int,int>>::iterator> dict;//<key,pos>
int cap;
public:
LRUCache(int capacity) {
cap=capacity;
}
int get(int key) {
// 不存在
if(!dict.count(key))return -1;
auto pos = dict[key];
pair<int,int> p = *pos;
// 先删除
data.erase(pos);
// 再加入
data.push_front(p);
dict[key]=data.begin();
// return
return p.second;
}
void put(int key, int value) {
// 已存在
if(dict.count(key)){
// 先删除
data.erase(dict[key]);
}
// 不存在,且满了
else if(data.size()==cap){
// 先删除最旧的数据
dict.erase(data.back().first);
data.pop_back();
}
// 加入新的数据
data.push_front({key,value});
dict[key]=data.begin();
}
};
5.3 LFU缓存
class LFUCache {
struct Node{
int key,value,freq;
Node(int k,int v,int f):key(k),value(v),freq(f){}
};
int minFreq=0,cap;
unordered_map<int,list<Node>::iterator> keysDict;
unordered_map<int,list<Node>> freqDict;
public:
LFUCache(int capacity) {
cap=capacity;
}
int get(int key) {
// 不存在
if(!keysDict.count(key)){
return -1;
}
auto whichNode = keysDict[key];
auto node = *whichNode;
// 先删除
auto &whichList=freqDict[node.freq];
whichList.erase(keysDict[key]);
// 再装入
freqDict[node.freq+1].push_front({node.key,node.value,node.freq+1});
keysDict[key]=freqDict[node.freq+1].begin();
// 更新freq
if(whichList.empty()){
freqDict.erase(node.freq);
if(minFreq == node.freq)minFreq+=1;
}
return node.value;
}
void put(int key, int value) {
// cap==0
if(cap==0)return;
Node newNode(key,value,1);
int newMinFreq=1;
// 存在
if(keysDict.count(key)){
auto whichNode = keysDict[key];
auto node = *whichNode;
// 先删除
auto & whichList = freqDict[node.freq];
whichList.erase(whichNode);
newMinFreq = minFreq;
// 更新新的,等待装入
newNode.freq=node.freq+1;
// 更新minFreq
if(whichList.empty()){
freqDict.erase(node.freq);
if(newMinFreq == node.freq)newMinFreq+=1;
}
}
// 满了且不存在
else if(keysDict.size()==cap && !keysDict.count(key)){
auto old = freqDict[minFreq].back();
// 删除最旧的
freqDict[minFreq].pop_back();
keysDict.erase(old.key);
if(freqDict[minFreq].empty())freqDict.erase(minFreq);
}
// 装入
freqDict[newNode.freq].push_front(newNode);
keysDict[key] = freqDict[newNode.freq].begin();
minFreq = newMinFreq;
}
};
/**
* Your LFUCache object will be instantiated and called as such:
* LFUCache* obj = new LFUCache(capacity);
* int param_1 = obj->get(key);
* obj->put(key,value);
*/
6 并查集
6.1 模板
class UnionFind{
private:
vector<int> father;
public:
UnionFind(int n):father(n){
for(int i=0;i<n;i++)father[i]=i;
}
int find(int x){
return x==father[x]?x:father[x]=find(father[x]);
}
void connect(int x,int y){
int fx=find(x),fy=find(y);
father[fx]=fy;
}
bool isConnected(int x,int y){
return find(x)==find(y);
}
static void test(){
cout<<"\n************* UnionFind Test ******************\n";
UnionFind uf(5);
uf.connect(1,3);
uf.connect(3,4);
cout<<uf.isConnected(3,4)<<endl;
}
};
6.2 应用
class Solution {
vector<int> p;
void connect(int i,int j){
int x = find(i);
int y = find(j);
p[y]=x;
}
int find(int i){
if(i!=p[i])p[i]=find(p[i]);
return p[i];
}
public:
int removeStones(vector<vector<int>>& stones) {
p.resize(20000);
for(int i=0;i<20000;i++)
p[i]=i;
for(auto s:stones)
connect(s[0],s[1]+10000);
set<int> dict;
for(auto s:stones)
dict.insert(find(s[0]));
return stones.size()-dict.size();
}
};
7 拓扑排序
7.1 入度排序
class Solution {
public:
bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
vector<vector<int>> adj(numCourses);
vector<int> degree(numCourses,0);
for(auto it:prerequisites){
adj[it[0]].push_back(it[1]);
++degree[it[1]];
}
queue<int> q;
for(int i=0;i<numCourses;i++)
if(degree[i]==0)q.push(i);
while(!q.empty()){
int front=q.front();
q.pop();
numCourses--;
for(auto to:adj[front])
if(--degree[to]==0)q.push(to);
}
return numCourses==0;
}
};
8 背包问题
8.1 0-1背包
将n
个物品放入容量为V
的容器内,每个物品有自己的体积和价值,每个物品只能选择0次或1次,问容器内最多能装下多少价值的物品?
8.1.1 基础的两层循环
class Solution {
public:
int lastStoneWeightII(vector<int>& stones) {
// sum
int sum=0;
for(auto stone:stones)sum+=stone;
int V =sum/2;
// bag
int len = stones.size();
vector<vector<int>> dp(len+1,vector<int>(V+1,0));
for(int i=1;i<=len;i++){
for(int j=0;j<=V;j++){
if(j<stones[i-1])dp[i][j]=dp[i-1][j];
else dp[i][j]=max(dp[i-1][j],dp[i-1][j-stones[i-1]]+stones[i-1]);
}
}
return sum-2*dp[len][V];
}
};
8.1.2 空间优化
leetcode 1049. 最后一块石头的重量II
二维数组优化为一维数组,注意倒着打表;
class Solution {
public:
int lastStoneWeightII(vector<int>& stones) {
// sum
int sum=0;
for(auto stone:stones)sum+=stone;
int V =sum/2;
// bag
vector<int> dp(V+1,0);
for(auto s:stones){
for(int j=V;j>=s;j--)
dp[j]=max(dp[j],dp[j-s]+s);
}
return sum-2*dp[V];
}
};
8.2 无限背包(完全背包)
物品可取无数次。
vector<int> dp(V+1,0);
for(auto item:items){
for(int j=item;j<=V;j++)
dp[j]=max(dp[j],dp[j-item]+item);
}
8.3 多维背包
class Solution {
public:
int findMaxForm(vector<string>& strs, int m, int n) {
vector<vector<int>> dp(m+1,vector<int>(n+1,0));
for(auto &str:strs){
int cnt0 = count(str.begin(),str.end(),'0');
int cnt1= str.size()-cnt0;
for(int j=m;j>=cnt0;j--)
for(int k=n;k>=cnt1;k--)
dp[j][k]=max(dp[j][k],dp[j-cnt0][k-cnt1]+1);
}
return dp[m][n];
}
};
8.4 刚好装满的背包(组合数、物品数量最小数)
class Solution {
const int MAX = 0x3f3f3f3f;
public:
int coinChange(vector<int>& coins, int amount) {
vector<int> dp(amount+1,MAX);//money cnt
dp[0]=0;
for(auto coin:coins)
for(int j=coin;j<=amount;j++)
dp[j]=min(dp[j],dp[j-coin]+1);
return dp[amount]==MAX?-1:dp[amount];
}
};
class Solution {
public:
int change(int amount, vector<int>& coins) {
vector<int> dp(amount+1,0);
dp[0]=1;
for(auto c:coins)
for(int j=c;j<=amount;j++)
dp[j]+=dp[j-c];
return dp[amount];
}
};
class Solution {
public:
int combinationSum4(vector<int>& nums, int target) {
vector<unsigned long long> dp(target+1,0);
dp[0]=1;
for(int j=1;j<=target;j++)
for(auto n:nums)
if(j>=n)dp[j]+=dp[j-n];
return dp[target];
}
};
8.5 多重背包(物品数量有限制)
class Solution {
private:
/* 计算组合数 */
int C(int m, int n) {
if ((n == 0) || (n == m))
return 1;
int ret = 1;
for (int i = 1; i <= n; ++i) {
ret *= (m - i + 1);
ret /= i;
}
return ret;
}
public:
int keyboard(int k, int n) {
vector<long long> dp(n + 1, 0);
/* 按键0次则得出唯一的一种内容: 空 */
dp[0] = 1;
/* 遍历26个字母(26种货物) */
for (int i = 0; i < 26; ++i) {
/* 遍历背包的容量(从大到小) */
for (int j = n; j >= 1; --j) {
/* 放入x个当前字母 */
for (int x = 1; x <= k; ++x) {
if (x > j)
continue;
dp[j] += C(j, x) * dp[j - x];
dp[j] %= 1000000007;
}
}
}
return dp.back();
}
};
9 二叉树的非递归遍历
void Traversal(TreeNode* root) {
stack<TreeNode*> stk;
TreeNode *p = root, *prev = nullptr;
while (p || !stk.empty()) {
while (p) {
// ① 前序遍历
stk.push(p);
p = p->left;
}
p = stk.top();
// if (!p->right || p->right != prev) ② 中序遍历;
if (p->right && p->right != prev) p = p->right;
else {
// ③ 后序遍历
stk.pop();
prev = p;
p = nullptr;
}
}
}
另一种先序方法
class Solution {
public:
vector<int> preorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode*> stk;
if(!root)return res;
stk.push(root);
while(!stk.empty()){
TreeNode * top = stk.top();
stk.pop();
res.push_back(top->val);
if(top->right)stk.push(top->right);
if(top->left)stk.push(top->left);
}
return res;
}
};
另一种后序:改前序为“根右左”然后反转。
class Solution {
public:
vector<int> postorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode*> stk;
if(!root)return res;
stk.push(root);
while(!stk.empty()){
TreeNode * top = stk.top();
stk.pop();
res.push_back(top->val);
if(top->left)stk.push(top->left);
if(top->right)stk.push(top->right);
}
reverse(res.begin(),res.end());
return res;
}
};
另一种中序:能向左走就向左走,没路了就从栈中取一个值并向右走。
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;
stack<TreeNode*> stk;
while(root || !stk.empty()){
if(root){
stk.push(root);
root=root->left;
}
else{
TreeNode * top = stk.top();
stk.pop();
res.push_back(top->val);
root = top->right;
}
}
return res;
}
};
10 二分查找
10.1 正常的二分查找
int search(vector<int>& nums, int target) {
int l=0,r=nums.size()-1;
while(l<=r){
int mid = l + (r-l)/2;
if(nums[mid]==target)return mid;
else if(nums[mid]>target)r=mid-1;
else l=mid+1;
}
return -1;
}
10.2 lower_bound
和upper_bounder
int lower_bound(vector<int> &nums,int target){
if(target>nums[nums.size()-1])return nums.size();
int l=0,r=nums.size()-1;
while(l<r){
int mid = l+(r-l)/2;
if(nums[mid]<target)
l=mid+1;
else
r=mid;
}
return l;
}
int upper_bound(vector<int> &nums,int target){
if(target>=nums[nums.size()-1])return nums.size();
int l=0,r=nums.size()-1;
while(l<r){
int mid = l+(r-l)/2;
if(nums[mid]>target)
r=mid;
else
l=mid+1;
}
return r;
}
10.3 l<r
与l<=r
l<=r
:在循环体内部查找元素;l<r
:在循环体内部排除元素,退出时的唯一元素就是查找结果;
10.4 mid=(l+r)>>1
和 mid=(l+r+1)>>1
- 将区间划分为
[l,mid]
和[mid+1,r]
,使用mid=(l+r)>>1
; - 将区间划分为
[l,mid-1]
和[mid,r]
,使用mid=(l+r+1)>>1
;
10.5 rank()
int rank(vector<int>&nums,int target){
int l=0,r=nums.size()-1;
while(l<=r){
int mid = l+(r-l)/2;
if(nums[mid]==target)return mid;
else if(nums[mid]<target)l=mid+1;
else r=mid-1;
}
return l;
}
10.6 在旋转数组中查找数字
int search(vector<int>& nums, int target) {
int l=0,len=nums.size(),r=len-1;
while(l<=r){
int mid = l+(r-l)/2;
if(target==nums[mid])return mid;
// l--mid is sorted
if(nums[l]<= nums[mid]){
if(target>=nums[l] && nums[mid]>target)r=mid-1;
else l=mid+1;
}
else{ // mid--r is sorted
if(target>nums[mid] && target<=nums[r])l=mid+1;
else r=mid-1;
}
}
return -1;
}
class Solution {
public:
int minArray(vector<int>& numbers) {
if(numbers.empty()) return 0;
int l=0,r=numbers.size()-1;
while(l<r) {
int mid = (l+r)>>1;
if(numbers[r] > numbers[mid]) {
r = mid;
}
else if(numbers[r]<numbers[mid]){
l=mid+1;
}
else r--;
}
return numbers[l];
}
};
11 dp
11.1 区间dp
先讨论[i,i],[i,i+1]
时的简单情况,然后用小区间数据辅助计算大区间的数据,相当于倒推。
class Solution {
public:
int maxCoins(vector<int>& nums) {
nums.insert(nums.begin(),1);
nums.push_back(1);
int len = nums.size();
vector<vector<int>> dp(len,vector<int>(len,0));
for(int l=2;l<len;l++){// length of []
for(int i=0;i+l<len;i++){// start from i,end at i+l
for(int j=i+1;j<i+l;j++)// j BOOMs at last
dp[i][i+l]=max(dp[i][i+l],dp[i][j]+dp[j][i+l]+nums[i]*nums[j]*nums[i+l]);
}
}
return dp[0][len-1];
}
};
12 KMP
看不懂,硬记就行了。
class Solution{
public:
string shortestPalindrome(string s) {
string r_s = s;
reverse(r_s.begin(),r_s.end());
string str = s+"#"+r_s;
int n = str.size();
vector<int> next(n+1,0);
for(int i=0,j=next[0]=-1;i<n;next[++i]=++j){
while(~j && str[i]!=str[j])j=next[j];
}
return r_s.substr(0, r_s.size()-next[n])+s;
}
};
13 Manacher
其本质仍是中心扩展算法,但是做了更多的优化;
class Solution{
public:
string shortestPalindrome(string s) {
string ms = "#";
for(auto ch:s){
ms+=ch;
ms+="#";
}
int len = ms.size();
vector<int> p(len,0);
int maxRight=0,center=0;
int maxLen = 0,start=0;
for(int i=0;i<len;i++){
if(i<maxRight){
int mirror = 2*center - i;
p[i]=min(maxRight-i,p[mirror]);
}
int left = i-(1+p[i]);
int right= i+(1+p[i]);
while(left>=0 && right<len && ms[left--]==ms[right++])++p[i];
if(i+p[i]>maxRight){
maxRight=i+p[i];
center=i;
}
if(p[i]>maxLen){
start = (i-p[i])/2;
if(start==0)
maxLen = p[i];
}
}
string add = s.substr(maxLen);
reverse(add.begin(),add.end());
return add+s;
}
};
14 TrieTree
class Trie {
struct Node{
char val;
bool isEnd;
Node* next[26];
Node(){
for(int i=0;i<26;i++)next[i]=NULL;
}
Node(char val_,bool isEnd_=false):val(val_),isEnd(isEnd_){
for(int i=0;i<26;i++)next[i]=NULL;
}
};
Node root;
public:
/** Initialize your data structure here. */
Trie() {
}
/** Inserts a word into the trie. */
void insert(string word) {
Node * cur = &root;
for(auto ch:word){
if(cur->next[ch-'a']==NULL)cur->next[ch-'a']=new Node(ch);
cur=cur->next[ch-'a'];
}
cur->isEnd=true;
}
/** Returns if the word is inå the trie. */
bool search(string word) {
Node * cur = &root;
for(auto ch:word){
if(cur->next[ch-'a']==NULL)return false;
cur=cur->next[ch-'a'];
}
return cur->isEnd;
}
/** Returns if there is any word in the trie that starts with the given prefix. */
bool startsWith(string prefix) {
Node *cur = &root;
for(auto ch:prefix){
if(cur->next[ch-'a']==NULL)return false;
cur=cur->next[ch-'a'];
}
for(int i=0;i<26;i++)
if(cur->next[i])return true;
return cur->isEnd;
}
};
/**
* Your Trie object will be instantiated and called as such:
* Trie* obj = new Trie();
* obj->insert(word);
* bool param_2 = obj->search(word);
* bool param_3 = obj->startsWith(prefix);
*/
class FileSystem {
class TrieTree{
public:
TrieTree(string name){
this->name = name;
}
// 当前文件(夹)的名字
string name;
unordered_map<string,TrieTree*> dict;
// 文件信息
bool isFile=false;
string content;
};
TrieTree * root;
// 相同的操作,到达某一个路径,不存在则创建
TrieTree* reach(string path){
TrieTree * cur = root;
stringstream ss(path);
string dir;
while(getline(ss,dir,'/')){
if(dir.empty())continue;
if(!cur->dict.count(dir)){
cur->dict[dir]=new TrieTree(dir);
}
cur=cur->dict[dir];
}
return cur;
}
public:
FileSystem() {
root = new TrieTree("");
}
// 释放内存
~FileSystem(){
free(root);
}
void free(TrieTree * cur){
for(auto &it:cur->dict){
free(it.second);
}
delete cur;
}
vector<string> ls(string path) {
vector<string> res;
TrieTree * cur = root;
stringstream ss(path);
string dir;
// 不存在就返回空
while(getline(ss,dir,'/')){
if(dir.empty())continue;
if(cur->dict.count(dir)){
cur=cur->dict[dir];
}
else return {};
}
// 是文件就返回文件名
if(cur->isFile)return {cur->name};
// 文件夹则返回目录下所有内容
for(auto &it:cur->dict)res.push_back(it.first);
sort(res.begin(),res.end());
return res;
}
void mkdir(string path) {
reach(path);
}
void addContentToFile(string filePath, string content) {
TrieTree * cur = reach(filePath);
cur->isFile=true;
cur->content += content;
}
string readContentFromFile(string filePath) {
TrieTree * cur = reach(filePath);
return cur->content;
}
};
/**
* Your FileSystem object will be instantiated and called as such:
* FileSystem* obj = new FileSystem();
* vector<string> param_1 = obj->ls(path);
* obj->mkdir(path);
* obj->addContentToFile(filePath,content);
* string param_4 = obj->readContentFromFile(filePath);
*/
15 回溯
class Solution {
// memthod1
// 借助类变量完成回溯,较为简单明了。数组中的值是正序的。
// 一次递归选择一个值,这个值可能是后面的任意一个值,全部遍历一边。
vector<vector<int>> res;
vector<int> path;
void backtrack(vector<int>&candidates,int target,int pos){
if(target==0){
res.push_back(path);
return;
}
for(int i=pos;i<candidates.size() && target>=candidates[i];i++){
if(i>pos && candidates[i]==candidates[i-1])continue;
// choose
path.push_back(candidates[i]);
backtrack(candidates,target-candidates[i],i+1);
path.pop_back();
}
}
// memthod2
// 直接借助返回值完成回溯,数组中的值是倒序的。
vector<vector<int>> backtrack2(vector<int>&candidates,int target,int pos){
if(target==0)return {{}};
vector<vector<int>> res;
for(int i=pos;i<candidates.size() && target>=candidates[i];i++){
if(i>pos && candidates[i]==candidates[i-1])continue;
vector<vector<int>> subRes = backtrack2(candidates,target-candidates[i],i+1);
for(auto vec:subRes){
vec.push_back(candidates[i]);
res.push_back(vec);
}
}
return res;
}
// method3
// 每个位置选与不选两种情况,因每次只考虑当前位置导致去重较为麻烦,某些不去重的题更实用。
set<vector<int>> dict;
void backtrack3(vector<int> &candidates,int target,int pos){
if(target==0){
if(!dict.count(path))
res.push_back(path);
dict.insert(path);
return;
}
if(pos==candidates.size())return;
// choose pos
if(target>=candidates[pos]){
path.push_back(candidates[pos]);
backtrack3(candidates,target-candidates[pos],pos+1);
path.pop_back();
}
// do not choose
backtrack3(candidates,target,pos+1);
}
public:
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
sort(candidates.begin(),candidates.end());
// method1
//backtrack(candidates,target,0);
//return res;
// method2
//return backtrack2(candidates,target,0);
// method3
backtrack3(candidates,target,0);
return res;
}
};
16. 快排
pivot的选择:
- 选取第一个元素;
- 选取最后一个元素;
- 选取第一个、中间、最后一个元素的中位数:该方法性能一般较好。
- 随机选取数组中某个值:性能较好,但
random()
并不便宜。
16.1 单向扫描法实现partition
int partition1(vector<int> s, int left, int right) {
int pivot = s[left];
int i = left ;
for (int j = left+1; j <= right; j++) {
if (s[j] <= pivot) {
i++;
if (i != j)swap(&s[i], &s[j]);
}
}
swap(&s[left], &s[i]);
return i;
}
16.2 双向扫描法实现partition(哨兵法)
void swap(int *a, int *b) {
int tmp = *a; *a = *b; *b = tmp;
}
int partition2(vector<int> s, int left, int right) {
int pivot = s[left];
int i = left, j = right;
while (i < j) {
while (i<j&&s[j]>pivot)j--;
while (i < j&&s[i] <= pivot)i++;
if (i < j)swap(&s[i], &s[j]);
}
swap(&s[left], &s[i]);
return i;
}
16.3 双向扫描法实现partition(挖坑法)
int partition3(vector<int> s, int left, int right) {
int pivot = s[left];
int i = left, j = right;
while (i < j) {
while (i<j && s[j]>pivot)j--;
if (i < j)s[i++] = s[j];
while (i < j&&s[i] <= pivot)i++;
if (i < j)s[j--] = s[i];
}
s[i] = pivot;
return i;
}
16.4 partition三路切分
假设数组的值仅有0,1,2
三种取值,需要将全部0
放到最左边,全部2
放最右边,全部1
放中间,可通过以下步骤实现:
- 获取数组长度
len
,初始化i=0,low=0,high=len-1
; - 从两端向中间扫描,同时从左向右扫描;
- 可以理解为
low
的左边全是0
,i
指向当前数值,high
的右边全是2
;只要把全部的0
移到左边,全部的2
移到右边,那么剩下的1
就自然全在中间了; - i位置上是
1
,i
右移; - 位置上是
0
,交换i
和low
位置,同时都右移; - i位置上是
2
,交换i
和high
位置,high
左移,但i
不右移,因为交换后i
位置可能是0
,就还需要一次交换; - 完成后
low
指向第一个1
,high
指向第一个2
,如需要确切的划分位置可利用。
#include <iostream>
#include <stack>
#include <string>
#include <vector>
#include <algorithm>
#include <map>
#include <queue>
#include <stdio.h>
#include <list>
#include <set>
using namespace std;
void partitionThreeWay(vector<int> &s, int left, int right) {
int i = left , low = left, high = right;
while (i <= high) {
if (s[i] == 1)i++;
else if (s[i] == 0)swap(&s[i++], &s[low++]);
else swap(&s[i], &s[high--]);
}
//输出内容
for (int t = left; t <= right; t++) {
if (t == low)cout << low << " ";
else if (t == i)cout << i << " ";
else cout << "_ ";
}
cout << endl;
for (int t = left; t <= right; t++) {
cout << s[t] << " ";
}
cout << endl;
}
int main()
{
vector<int> s = { 0,1,2,2,1,0,1,2,0 };
partitionThreeWay(s,0, s.size() - 1);
system("pause");
return 0;
}