c++leetcode题解

模板:

#include 
#include 
#include 
#include 
#include 
#include 
#include  //cout<
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
#define debug(x) cout << x
// debug1 一维数组
#define debug1(x)               \
    do                          \
    {                           \
        for (auto item : x)     \
        {                       \
            debug(item << " "); \
        }                       \
        debug(endl);            \
    } while (0)
// debug2 二维数组
#define debug2(x)                \
    do                           \
    {                            \
        for (auto i : x)         \
        {                        \
            for (auto j : i)     \
            {                    \
                debug(j << " "); \
            }                    \
            debug(endl);         \
        }                        \
    } while (0)

const int64_t inf = round(pow(10,9))+7;

常用代码段汇总

数字转字符串string、字符串转数字

#include 
//数字转字符串
    //demo: int a = str2num(string("-01.23"));
    template
    T str2num(const string& str)
    {
        stringstream ss;
        T num;
        ss << str;
        ss >> num;
        return num;
    }
    
    //demo: double num=-23.1; string str = num2str(num);
    //也可以指定T的类型,str = num2str(num);
    template
    string num2str(const T& num)
    {
        stringstream ss;
        ss << num;
        string str;
        ss >> str;
        return str;
 }

进制转换

#include 
#include 
//进制转换,输入输出都是string,默认10-->2,支持2,8,10,16进制。
//demo: string str = "01010101"; cout<< m2n(str,2,16) <> a;
    stringstream ss1;
    switch (format_out)
    {
    case 16:
        ss1 << hex << a;
        break;
    case 8:
        ss1 << oct << a;
        break;
    case 2:
    {
        ss1 << bitset<64>(a);
        string ret = ss1.str();
        for (int i = 0; i < ret.size(); ++i)
        {
            if (ret[i] != '0')
                return ret.substr(i);        
        }
        return "0"; //如果全是0,返回0
    }
    break;
    default:
        ss1 << a;
    }
    return ss1.str();
}

容器元素左移n位

#include 
#include 
#include 
#include 
#include 
using namespace std;

//容器元素环形左移n位
template //T string, vector, list...
void rotaten(T& x, int n)
{
    auto it = x.begin();
    advance(it, n);//it步进n位
    rotate(x.begin(), it, x.end());
}
int main()
{
   
    vector a ={0,1,2,3,4,5};
    list nums(a.begin(),a.end());
    rotaten(nums, 1);
    for(auto num : nums)
    {
        cout<

求组合数C(m,n)

//求组合数m个数中选n个,m>=n
uint64_t comb(int m, int n) //计算c(m,n)
{
    if (n == 0)
        return 1;
    if (n == m)
        return 1;
    uint64_t up = 1, low = 1;
    for (int i = 1; i <= n; i++)
        low = low * i;
    for (int i = m - n + 1; i <= m; i++)
        up *= i;
    return up / low;
}

求排列数

//求排列数
uint64_t perm(int m, int n) //计算A(m,n)
{
    if (n == 0)
        return 1;
    uint64_t ret = 1;
    for(int i=m-n+1;i<=m;++i)
    {
        ret *= i;
    }
    return ret;
}

求阶乘

//阶乘
uint64_t fact(int n)
{
    if(n<1) return 1;// 0! = 1
    uint64_t ret = 1;
    for(int i=2;i<=n;++i)
    {
        ret *= i;
    }
    return ret;
}

求整数幂,次方

uint64_t a = round(pow(m,n));//round必须要,否则可能少1,可能多1,m^n uint64_t 上限是10^18 ~ 10^19

求三个元素的最大最小值

//求三个元素的最大值
template // int, string, char 
T max3(T a, T b ,T c)
{
    return max(max(a,b),c);
}
//求三个元素的最小值
template 
T min3(T a, T b, T c)
{
    return min(min(a,b),c);
}

从一个数组中选取m个数字的所有组合,不考虑顺序

#include 
#include 
#include 
#include 
#include 
using namespace std;

template
void choose_num(vector& data,int low,int high,int m,vector &run,int p,vector> &res) 
{
    if(m==0)
        res.push_back(run);
    else
    {
       for(int i=low;i<=high-m;i++)//后面的要保留m-1个不可以选
       {
           run[p]=data[i];
           choose_num(data,i+1,high,m-1,run,p+1,res);
       }
    }
}
//在data序列中选取m个的所有组合.C(n,m)种
template
void choose_num(vector& data, int m, vector> &ret)
{
    if(data.empty() || m==0 || data.size() run(m);
    choose_num(data,0,data.size(),m,run, 0, ret) ;
}

int main()
{
    vector data ={"a","b","c","d","e"};//最好把data排好序
    vector > ret;
    choose_num(data, 2, ret);
    for(auto num_list:ret)
    {
        for(auto num:num_list)
        {
            cout<

一个数组中的所有排列

#include 
#include 
#include 
using namespace std;

//求一个序列的所有排列,n个数的排列是n!种
template //T :int,string...
void get_all_permutation(vector& data, vector< vector >& ret)
{
    int l = data.size();
    if(l == 0 ) return;
    vector index(l);
    vector run(l);
    for(int i=0;i &data, vector &ret)
{
    int l = data.size();
    if (l == 0)
        return;
    vector index(l);
    for (int i = 0; i < l; ++i)
    {
        index[i] = i;
    }
    do
    {
        string run;
        for (int i = 0; i < l; ++i)
        {
            run += data[index[i]];
        }
        ret.push_back(run);

    } while (next_permutation(index.begin(), index.end()));
}

int main()
{
    vector data = {"a","b","c"};
    vector> ret;
    get_all_permutation(data, ret);
    for(auto num_list:ret)
    {
        for(auto num:num_list)
        {
            cout<

一个vector序列中选取m个元素的所有排列,考虑顺序

#include 
#include 
#include 
using namespace std;

template//  choosed前面加& 就变成求组合
void choose_permutation(vector& data,vector choosed,int m,vector& run,int p,vector> &res)
{
    if(m==0)
        res.push_back(run);
    else
    {
       int per=-1;//存上一个选择过的
       for(int i=0;i-1) choosed[per]=0;//保证每一层循环只选择了一个数
               run[p]=data[i];
               choosed[i]=1;
               per=i;
               choose_permutation(data,choosed,m-1,run,p+1,res);
           }
       }
    }
}
//从data序列中选取m个元素的所有排列
template 
void get_all_permutation(vector& data, int m, vector>& ret)
{
    if(data.size() choosed(data.size(), 0);//下标都没有取过, 0没有选过,可选, -1 不可选
    vector run(data.size());
    choose_permutation(data, choosed, m, run, 0,ret);
}

int main()
{
    vector data = {"a","b","c"};
    vector> ret;
    get_all_permutation(data, 2, ret);
    for(auto num_list:ret)
    {
        for(auto num:num_list)
        {
            cout<

分割字符串放到容器里面

#include 
#include 
#include
#include
#include 
using namespace std;

//以char c分割字符串, 放到容器中,支持push_back即可
template //T: vector list,deque,
void part_string(const string& str, const char c, T& str_list)
{
    stringstream ss;
    ss< str_list;
    part_string(str,'/',str_list);
    for(auto s:str_list)
    {
        cout<

删除字符中的某个指定连续重复项,只保留一项

//删除指定连续重复项,string 中连续的字符串只保留一个,O(n^2),支持把多个字符当做整体
void erase_duplicated(string& str, const string& c)//一定加上const
{
    if(str.empty()) return;
    string tmp = c+c;
    uint32_t i;
    while((i = str.find(tmp))

判断子集

#include 
#include 
#include 
using namespace std;
//判断集合cs是否是集合str的子集,不考虑顺序,vector换成list也可以
template
bool is_sub(const vector& str, const vector& cs)
{
    if(str.size() ma;
    for(auto c:cs)
    {
        ma[c]++;
    }
    for(auto c:str)
    {
        ma[c]--;
    }
    for(auto it=ma.begin();it!=ma.end();++it)
    {
        if(it->second>0)
        {
            return false;
        }
    }
    return true;
}
//判断string cs 是否是str的子集,不考虑顺序,单独实现
bool is_sub(string& str, const string& cs)
{
    if(str.size() ma;
    for(auto c:cs)
    {
        ma[c]++;
    }
    for(auto c:str)
    {
        ma[c]--;
    }
    for(auto it=ma.begin();it!=ma.end();++it)
    {
        if(it->second>0)
        {
            return false;
        }
    }
    return true;
}

int main()
{
    vector a={1,2,3,3,4,5};
    vector b={1,2,3,6};
    string s1 ("hello");
    string s2="leo";
    cout<

无向图相关

Floyd算法求最短距离矩阵 O(n^3)

//const int inf =99999;
//n个节点,由节点对得到邻接矩阵表示的图, dis对应node_pair的距离,adj_dis初始化为空
template  //T int, float等用于表示距离
void get_adj_dis(const vector> &node_pair, const vector &dis, const int& n, vector> &adj_dis)
{
    adj_dis.resize(n);
    for(int i=0;ii 的距离是0
    }
    for(int i=0;i //T: int, float表示距离
void floyd(vector> &min_dis)
{
    for (int k = 0; k < min_dis.size(); ++k) //这一层一定要放最外面
        for (int i = 0; i < min_dis.size(); ++i) 
            for (int j = 0; j < min_dis.size(); ++j)
                if(i!=j && min_dis[i][k] + min_dis[k][j]< min_dis[i][j])
                    min_dis[i][j] = min_dis[i][k] + min_dis[k][j];   
}

int main()
{

    const int inf = 999999;
    int n = 6;
    vector> node_pair =  {{0, 3}, {4, 3}, {2, 3}, {1, 3}, {5, 4}};//{{1, 0}, {1, 2}, {1, 3}};
    vector dis(node_pair.size(), 1);
    vector> adj_dis;
    
    get_adj_dis(node_pair, dis, n,adj_dis);
    vector> min_dis = adj_dis; 
    floyd(min_dis);
    debug2(node_pair);debug(endl);
    debug2(adj_dis);debug(endl);
    debug2(min_dis);debug(endl);
}


Floyd算法求最短距离矩阵以及所有最短路径 O(n^4)

不要用如果需要所有可以调用dijkstra

//const int inf =99999;
//n个节点,由节点对得到邻接矩阵表示的图, dis对应node_pair的距离,adj_dis初始化为空
template  //T int, float等用于表示距离
void get_adj_dis(const vector> &node_pair, const vector &dis, const int& n, vector> &adj_dis)
{
    adj_dis.resize(n);
    for(int i=0;ii 的距离是0
    }
    for(int i=0;i
void  floyd_path(const vector> &adj_dis, vector>& min_dis,  vector>>& path)
{
    const int n = adj_dis.size();//节点数
    path.resize(n);
    for(int i=0;i min_dis[k][j] + adj_dis[j][i]) //求源点到节点的最短路径,利用现有的adj_dis 矩阵
                {
                    min_dis[k][i] = min_dis[k][j] + adj_dis[j][i];
                    path[k][i].clear(); //保存并更新路径
                    path[k][i].insert(path[k][i].end(), path[k][j].begin(), path[k][j].end());
                    path[k][i].push_back(i);
                }
            }
            for (int m = 0; m < i; m++) //更新节点最短路径
            {
                for (int j = 0; j < n; j++)
                {
                    if (min_dis[k][m] > min_dis[k][j] + adj_dis[j][m])
                    {
                        min_dis[k][m] = min_dis[k][j] + adj_dis[j][m];
                        path[k][m].clear(); //保存并更新路径
                        path[k][m].insert(path[k][m].end(), path[k][j].begin(), path[k][j].end());
                        path[k][m].push_back(m);
                    }
                }
            }
        }
    }
}

    int main()
{
    int n = 6;
    vector> node_pair =  {{0, 3}, {2, 3}, {4, 3}, {1, 3}, {5, 4}};//{{1, 0}, {1, 2}, {1, 3}};
    vector pair_dis(node_pair.size(), 1);

    vector> adj_dis;
    get_adj_dis(node_pair, pair_dis, n,adj_dis);
    debug2(adj_dis);debug(endl);

    vector>> path;
    vector> min_dis;
    floyd_path(adj_dis, min_dis,  path );
    debug2(min_dis);
    debug(endl);
    for(auto item:path)
    {
         debug2(item);
         debug(endl);
    }
   
}


dijkstra算法求出发点到其它点的最短距离和路径 O(n^2)

const int inf = 99999;

//n个节点,由节点对得到邻接矩阵表示的图, dis对应node_pair的距离,adj_dis初始化为空
template  //T int, float等用于表示距离
void get_adj_dis(const vector> &node_pair, const vector &dis, const int &n, vector> &adj_dis)
{
    adj_dis.resize(n);
    for (int i = 0; i < n; ++i)
    {
        adj_dis[i].resize(n);
        adj_dis[i][i] = 0; //i-->i 的距离是0
    }
    for (int i = 0; i < n; ++i)
    {
        for (int j = 0; j < n; ++j)
        {
            if (i != j)
            {
                adj_dis[i][j] = inf;
            }
        }
    }
    for (int i = 0; i < node_pair.size(); ++i)
    {
        adj_dis[node_pair[i][0]][node_pair[i][1]] = dis[i];
        adj_dis[node_pair[i][1]][node_pair[i][0]] = dis[i];
    }
}
//dijstra算法求最短距离矩阵O(n^2),输入adj_dis,初始邻接矩阵。start 起始点
//输出start-->其它点 path,和最短距离 dis。多个相等最短路径只能返回一条
//若无可达路径,对应dis=inf,path为一个end
template  //T: int, uint 表示距离,float可能会有问题
void dijkstra_path(vector> &adj_dis, const int &start,  vector> &path, vector& dis)
{
    const int n = adj_dis.size();
    dis.resize(n);//起点到其它点的距离
    fill(dis.begin(),dis.end(), inf);
    dis[start] = 0;//到自己是0
    vector visited(n,false);//节点是否访问过
    vector pre(n);//每个点的前驱
    for(int i=0;i st;
        int tmp = i;
        st.push(i);
        while(start != tmp && tmp != pre[tmp])//如果tmp==pre[tmp] 说明无可达路径
        {
            tmp = pre[tmp];
            st.push(tmp);
        }
        while(!st.empty())
        {
            path[i].push_back(st.top());
            st.pop();
        }
    }
}

int main()
{
    int n = 6;
    vector> node_pair = { {4, 3}, {2, 3}, {1, 3}, {5, 4}}; //{{1, 0}, {1, 2}, {1, 3}};
    vector dis(node_pair.size(), 1);
    vector> adj_dis;
    get_adj_dis(node_pair, dis, n, adj_dis);
    debug2(adj_dis);
    int start = 4, end = n-1;
    vector> path;
    vector path_dis;
    dijkstra_path(adj_dis,start,path, path_dis  );
    debug2(path);
    debug(endl);
    debug1(path_dis);
}


dijkstra算法求start到end的最短距离和路径 O(n^2)

const int inf = 99999;

//n个节点,由节点对得到邻接矩阵表示的图, dis对应node_pair的距离,adj_dis初始化为空
template  //T int, float等用于表示距离
void get_adj_dis(const vector> &node_pair, const vector &dis, const int &n, vector> &adj_dis)
{
    adj_dis.resize(n);
    for (int i = 0; i < n; ++i)
    {
        adj_dis[i].resize(n);
        adj_dis[i][i] = 0; //i-->i 的距离是0
    }
    for (int i = 0; i < n; ++i)
    {
        for (int j = 0; j < n; ++j)
        {
            if (i != j)
            {
                adj_dis[i][j] = inf;
            }
        }
    }
    for (int i = 0; i < node_pair.size(); ++i)
    {
        adj_dis[node_pair[i][0]][node_pair[i][1]] = dis[i];
        adj_dis[node_pair[i][1]][node_pair[i][0]] = dis[i];
    }
}
//dijstra算法求最短距离矩阵O(n^2),输入adj_dis,初始邻接矩阵。start 起始点, end 终点
//输出start-->end 的 path,和最短距离 dis。path和dis初始化为空。多个相等最短路径只能返回一条。
//如果路径不可达,那么返回inf,path返回1个end.
template  //T: int, uint 表示距离
int dijkstra_path(vector> &adj_dis, const int &start, const int &end, vector &path, vector &dis)
{
    const int n = adj_dis.size();
    dis.resize(n); //起点到其它点的距离
    fill(dis.begin(), dis.end(), inf);
    dis[start] = 0;                 //到自己是0
    vector visited(n, false); //节点是否访问过
    vector pre(n);             //每个点的前驱
    for (int i = 0; i < n; ++i)
        pre[i] = i; //初始状态设每个点的前驱为自身
    for (int i = 0; i < n; ++i)
    {
        int u = -1;
        T min = inf;
        //先找到start到其它点最短的距离的点 u
        for (int j = 0; j < n; ++j)
        {
            if (visited[j] == false && dis[j] <= min) //这里<=min是必要的
            {
                u = j;
                min = dis[j];
            }
        }
        if (u == -1)
            break;
        visited[u] = true;
        //更新路径
        for (int v = 0; v < n; ++v)
        {
            if (visited[v] == false && adj_dis[u][v] != inf)
            {
                if (dis[u] + adj_dis[u][v] < dis[v]) //d(start, u)+d(u,v) < d(start,v)更新
                {
                    dis[v] = dis[u] + adj_dis[u][v];
                    pre[v] = u; //更新前驱节点
                }
            }
        }
    }
    //start --->  end 的路径,O(n)
    {
        if (end == start)
        {
            path.push_back(end);
            path.push_back(end);
        }
        else
        {
            stack st;
            int tmp = end;
            st.push(tmp); //
            while (start != tmp && tmp!=pre[tmp])
            {
                tmp = pre[tmp];
                st.push(tmp);
            }
            while (!st.empty())
            {
                path.push_back(st.top());
                st.pop();
            }
        }
    }
    return dis[end];//返回start--->end 的最短距离
}

int main()
{
    int n = 6;
    vector> node_pair =  {{4, 3}, {2, 3}, {0, 3}, {5, 4}}; //{{1, 0}, {1, 2}, {1, 3}};
    vector dis(node_pair.size(), 1);
    vector> adj_dis;
    get_adj_dis(node_pair, dis, n, adj_dis);
    debug2(adj_dis);
    int start = 5, end = 1;
    vector path;
    vector path_dis;
    int length = dijkstra_path(adj_dis, start, end, path, path_dis);
    debug1(path);
    debug(endl);
    debug1(path_dis);
    cout << length;
}


邻接矩阵转化为邻接表

//邻接关系矩阵转化为邻接表,>1表示有连接
void adj_matrix2graph(const vector> &adj_matrix, vector> &graph)
{
    int n = adj_matrix.size();
    graph.resize(n);
    for (int i = 0; i < adj_matrix.size(); ++i)
    {
        for (int j = 0; j < adj_matrix[i].size(); ++j)
        {
            if (i != j && adj_matrix[i][j] != 0) //不可以是自己
                graph[i].push_back(j);
        }
    }
}

邻接表的子块数目

//求邻接表的子块数目 BFS, 不同的子块可以通过visited获取。graph邻接表
int num_part(const vector> &graph)
{
    int ret = 0;
    int n = graph.size(); //节点数
    vector visited(n, 0); //是否访问过,大于0表示访问过,不同的数字代表不同的子块
    for (int i = 0; i < n; ++i)
    {
        queue que;
        if (!visited[i])
        {
            ret++;
            que.push(i);
        }
        //BFS
        while (!que.empty())
        {
            int head = que.front();
            visited[head] = ret;
            que.pop();
            for (auto node : graph[head])
            {
                if (!visited[node])
                {
                    que.push(node);
                }
            }
        }
    }
    debug1(visited);
    return ret;
}

邻接表的最大深度,最长关系链

//给定邻接表,出发点start,求从start出发的最长链。O(n^2)
int node_depth(const vector> &graph, const int &start, vector visited)
{
    //debug("start = " << start << endl);
    visited[start] = true;
    if (graph[start].empty())
        return 1; //无邻接点,长度为1
    int ret = 1;
    for (auto node : graph[start])
    {
        if (visited[node])
            continue;
        ret = max(ret, node_depth(graph, node, visited) + 1);
    }
    return ret;
}

//给定邻接表,求其中的最长链,最大深度,O(n^3)
int max_depth(const vector> &graph)
{
    int ret = 0;
    vector visited(graph.size(), false);
    for (int i = 0; i < graph.size(); ++i)
    {
        ret = max(ret, node_depth(graph, i, visited));
        debug("i= " << i << "  node_depth= " << node_depth(graph, i, visited) << endl);
    }
    return ret;
}

邻接表最大深度与路径

/*    0
      |
  3---1---2
      |
      4
      |
  5
*/


#include 
#include 
#include 
#include 

using namespace std;
#define debug(x) cout << x
// debug1 一维数组
#define debug1(x)               \
    do                          \
    {                           \
        for (auto item : x)     \
        {                       \
            debug(item << " "); \
        }                       \
        debug(endl);            \
    } while (0)
// debug2 二维数组
#define debug2(x)                \
    do                           \
    {                            \
        for (auto i : x)         \
        {                        \
            for (auto j : i)     \
            {                    \
                debug(j << " "); \
            }                    \
            debug(endl);         \
        }                        \
    } while (0)

//给定邻接表,出发点start,求从start出发的最长链。O(n^2),next用于求路径
int node_depth(const vector> &graph, const int &start, vector visited, vector &next)
{
    //debug("start = " << start << endl);
    visited[start] = true;
    if (graph[start].empty())
        return 1; //无邻接点,长度为1
    int ret = 1;
    for (auto node : graph[start])
    {
        if (visited[node])
            continue;
        int tmp = node_depth(graph, node, visited, next) + 1;
        if (tmp > ret)
        {
            ret = tmp;
            next[start] = node;
        }
    }
    return ret;
}

//给定邻接表,求其中的最长链,最大深度,O(n^3),并求出所有最长路径
int max_depth(const vector> &graph, vector> &path)
{
    int ret = 0;
    vector visited(graph.size(), false);
    vector next(graph.size());
    for (int i = 0; i < next.size(); ++i)
        next[i] = i;
    for (int i = 0; i < graph.size(); ++i)
    {
        int tmp = node_depth(graph, i, visited, next);
        if (tmp > ret)
            ret = tmp;
        // debug("i= " << i << "  node_depth= " << tmp << endl);
        // debug("next = ");
        // debug1(next);
        //由next得到路径
        int k = i;
        vector path_run(1, k);
        while (next[k] != k)
        {
            k = next[k];
            path_run.push_back(k);
        }
        path.push_back(path_run);
        for (int i = 0; i < next.size(); ++i)
            next[i] = i;
    }

    return ret;
}

int main()
{
    vector> graph = {
        {1},
        {0, 2, 3, 4},
        {1},
        {1},
        {1, 5},
        {4}};
    // debug2(graph);
    vector> path;
    cout << max_depth(graph, path) << endl;
    debug2(path);
}

并查集相关

用类实现方便一些,主要包括 init, find , join三个方法,可以直接套用

#include 
#include 

using namespace std;

class UnionSet //并查集模板
{
public:
    int n ;//节点数量
    vector pre;//前驱节点
    //其它信息
    int need_other_num;//

    void init()
    {
        pre.resize(n);
        for (int i = 0; i < n; ++i)
            pre[i] = i;
    }
    int find(const int &node)//找最深前驱
    {
        int x = node;
        while (pre[x] != x)
        {
            x = pre[x];
        }
        //路径压缩
        { 
            int i = node, j = 0;
            while (i != x)
            {
                j = pre[i];
                pre[i] = x;
                i = j;
            }
        }
        return x;
    }
    //合并
    void join(int a, int b)
    {
        int a_root = find(a);
        int b_root = find(b);
        if(a_root != b_root)
        {
            pre[a_root] = b_root;
            //其它操作
            need_other_num--;
        }
    }
    //其它个性化操作
    void func(vector>& node_pair)
    {
        init();
        if(node_pair.size()<2) 
            return;
        for(auto item: node_pair)
        {
            int& a = item[0];
            int& b = item[1];
            join(a,b);
        }
        return ;
    }
};

int main()
{
    int n = 4;
    vector> node_pair = {{1,3},{4,3}};
    UnionSet uset;
    uset.n = n;
    uset.need_other_num = n-1;
    uset.func(node_pair);
    cout<

小知识点

二分查找写mid=left+(right-left)/2;不要写+,可能溢出。

[外链图片转存失败(img-3gGaEwA7-1568281841740)(c++刷题笔记.assets/1568278186645.png)]

遇到与或非的运算务必加括号,优先级比比较还低。

1.2 二维vector初始化

vectorM(3,vector(2,0));//3行2列,初始为0

1.3 vector排序汇总

https://www.cnblogs.com/grandyang/p/4843528.html

多维vector默认对第一列进行升序。默认逆序可以用反向迭代器完成。

sort(nums.rbegin(),nums.rend());//降序排序

自己重写cmp,前后的大小关系就是序列的大小关系。

bool cmp(vector a,vector b){
    return a[0]

1.4 vector去重

unique把重复的放到末尾,返回的是重复项的第一个迭代器。用unique之前一定要sort

sort(nums.begin(),nums.end());
nums.erase(unique(nums.begin(),nums.end()),nums.end());

2、排列组合问题

https://www.doc88.com/p-3337642361283.html

加法原理、乘法原理、组合公式、排列公式

方法:

针对不同元素:

1、住店法:客是指数

2、相邻问题捆绑法

3、相离问题插空法

 

针对相同元素放不同盒子:

1隔板法

5个相同的球放在3个不同的盒子,每份至少一个,有C(4,2)  种

题目

放鸡蛋

m个相同鸡蛋放到n个相同的碗里,有多少种不同的方法,碗可以空。
测试,coun(7,3)=8;  coun(3,2)=2; coun(2,2)=2;
f[m][n - 1]相当于第一盘子中为0,只用将数分成n - 1份即可。因为0不会大于任何数,相当于f[m][n - 1]中的方案前面加一个为0的盘子,而且不违背f的定义。所以f[m][n - 1]一定是f[m][n]的方案的一部分,即含有0的方案数。
f[m - n][n]相当于在每个盘子中加一个数1。因为每个盘子中加一个数1不会影响f[m][n - 1]中的方案的可行性,也不会影响f的定义。所以f[m - n][n]一定是f[m][n]的方案的一部分,即不含有0的方案数。

int coun(int m, int n)
{
    if(m<0 || n<=0)//不合法输入
        return 0;
    if (m == 0 || n == 1)
        return 1;
    if (n > m)
        return coun(m, m);
    else
        return coun(m, n - 1) + coun(m - n, n);//递推关系
}

修改为碗不可以空,则先在每个碗里面放1个鸡蛋,转化为上述问题。

LeetCode295 数据流中的中位数

思路:利用最大堆和最小堆,保证最大堆中元素个数>=最小堆元素个数,且最多只能多一个。heap_max的元素值<=heap_min的元素,故加入元素时,从最大堆中换出最大的加到heap_min中。

class MedianFinder {
    private:
    priority_queue heap_max;
    priority_queue,greater> heap_min;
public:
    /** initialize your data structure here. */
   MedianFinder()
   {
   }
  

    void addNum(int num) {
        heap_max.push(num);
        heap_min.push(heap_max.top());
        heap_max.pop();
        
        while(heap_max.size()heap_min.size())
        {
            res=heap_max.top();   
        }
        return res;
    }
};

灯泡开关LeetCode319

初始时有 n 个灯泡关闭。 第 1 轮,你打开所有的灯泡。 第 2 轮,每两个灯泡你关闭一次。 第 3 轮,每三个灯泡切换一次开关(如果关闭则开启,如果开启则关闭)。第 i 轮,每 i 个灯泡切换一次开关。 对于第 n 轮,你只切换最后一个灯泡的开关。 找出 n 轮后有多少个亮着的灯泡。

示例:

输入: 3
输出: 1 
解释: 
初始时, 灯泡状态 [关闭, 关闭, 关闭].
第一轮后, 灯泡状态 [开启, 开启, 开启].
第二轮后, 灯泡状态 [开启, 关闭, 开启].
第三轮后, 灯泡状态 [开启, 关闭, 关闭]. 

你应该返回 1,因为只有一个灯泡还亮着。

用数组模拟,结论:下标从1开始算,完全平方数位置对应的灯泡最后是开的

等价于求不大于n的完全平方数的个数,答案就是(int)sqrt(n)

模拟的函数

void trans(int &a)
    {
        if(a==0) a=1;
        else a=0;
        return;
    }
int bulbSwitch(int n) {
        if(n<1) return 0;
        vector num(n,0);
        for(int i=1;i<=n;i++)
        {
            for(int j=0;j

在LR字符串中交换相邻字符 LeetCode777

在一个由 'L' , 'R''X' 三个字符组成的字符串(例如"RXXLRXRXL")中进行移动操作。一次移动操作指用一个"LX"替换一个"XL",或者用一个"XR"替换一个"RX"。现给定起始字符串start和结束字符串end,请编写代码,当且仅当存在一系列移动操作使得start可以转换成end时, 返回True。

示例 :

输入: start = "RXXLRXRXL", end = "XRLXXRRLX"
输出: True
解释:
我们可以通过以下几步将start转换成end:
RXXLRXRXL ->
XRXLRXRXL ->
XRLXRXRXL ->
XRLXXRRXL ->
XRLXXRRLX

https://blog.csdn.net/fuxuemingzhu/article/details/83538485#_39

思路:L只能向左移动,R只能向右移动,并且LR位置不可以互换

那么只需要,start的所有L的索引对应地大于end的

bool canTransform(string s1, string s2) {
        if(s1.size()!=s2.size()) return false;
        vector indexL1,indexL2,indexR1,indexR2;
        string str1,str2;
        for(int i=0;iindexR2[i]) return false;
       }
        return true;
    }


恢复二叉搜索树

二叉搜索树中的两个节点被错误地交换。

请在不改变其结构的情况下,恢复这棵树。

示例 1:

输入: [1,3,null,null,2]

   1
  /
 3
  \
   2

输出: [3,1,null,null,2]

   3
  /
 1
  \
   2
示例 2:

输入: [3,1,4,null,null,2]

  3
 / \
1   4
   /
  2

输出: [2,1,4,null,null,3]

  2
 / \
1   4
   /
  3


思路1:中序遍历,存到一个数组中,再中序遍历写入树

思路2:只用常数空间

https://blog.csdn.net/ssdirector/article/details/78669447

class Solution {
public:
    vector num;
    int t=0;
    void recoverTree(TreeNode* root) {
        mid_search(root);
        sort(num.begin(),num.end());
        mid_write( root);
    }
    void mid_search(TreeNode* root)
    {
        if(root!=NULL)
        {
            mid_search(root->left);
            num.push_back(root->val);
            mid_search(root->right);
        }
        return;
    }
    void mid_write(TreeNode* root)
    {
       
        if(root!=NULL)
        {
            mid_write(root->left);
            root->val=num[t++];
            mid_write(root->right);
        }
  
        return;
    }
};

路径总和 LeetCode113

给定一个二叉树和一个目标和,找到所有从根节点到叶子节点路径总和等于给定目标和的路径。

说明: 叶子节点是指没有子节点的节点。

示例:
给定如下二叉树,以及目标和 sum = 22,

              5
             / \
            4   8
           /   / \
          11  13  4
         /  \    / \
        7    2  5   1
返回:

[
   [5,4,11,2],
   [5,8,4,5]
]

思路:DFS,递归

class Solution {
    vector> res;
public:
    vector> pathSum(TreeNode* root, int sum) {
        vector p;
        get_path(root,sum,p);
        return res;
    }
    void get_path(TreeNode* root,int sum,vector p)
    {
        if (root==NULL) return;
        p.push_back(root->val);
        if(root->left==NULL && root->right==NULL && sum==root->val)//去掉sum==root->val可以得到所有待选路径
            res.push_back(p);
        get_path(root->left, sum - root->val, p);
        get_path(root->right, sum - root->val, p);
    }
};

从n个数中选m个的所有组合,不考虑顺序

思路:DFS,递归实现,从data[]中选择放到arr[]中。先选第一个数,有m种,用循环。再选第二个数时,已经是子问题了,相当于从刚刚选择的数之后data[]中选m-1个数放到arr[]中,递归。当待选数目为0时,函数出口。注意点:因为不需要顺序,所以子问题选择时只需要在选择的数之后进行选择。

//在[low,high)的n个数之间选m个数放到run,函数调用一次就把一个数放到run[p]位置。再把run放到res。因为需要递归,所以引用传递,或者把run和res放到全局变量
//res需要初始化,长度是m。
void choose_num(vector data,int low,int high,int m,vector &run,int p,vector> &res) 
{
    if(m==0)
        res.push_back(run);
    else
    {
       for(int i=low;i<=high-m;i++)//后面的要保留m-1个不可以选
       {
           run[p]=data[i];
           choose_num(data,i+1,high,m-1,run,p+1,res);
       }
    }
}

int main()
{
    int m = 3;
    vector a={1,2,3,4,5,6,7,8};
    vector b(m);
    vector> ret;
    choose_num(a,0,a.size(),m,b,0,ret);
    for(auto num_list:ret)
    {
        for(auto num:num_list)
        {
            cout<

从n个数中选m个的所有排列

思路1

可以参考上面的思路,由于考虑顺序,所以用转存数组的方式实现,这种写法一定要预先知道run的长度,用位置来赋值,不可clear。用STL的next_permutation()更好。

void choose_num(vector data,int m,vector &run,int p,vector> &res)
{
    if(m==0)
        res.push_back(run);
    else
    {
        vector  new_data;
       for(int i=0;i

思路2

用标记数组,只不过要对每一层做一下处理

void choose_num(vector &data,vector choosed,int m,vector &run,int p,vector> &res)
{
    if(m==0)
        res.push_back(run);
    else
    {
       int per=-1;//存上一个选择过的
       for(int i=0;i-1) choosed[per]=0;//保证每一层循环只选择了一个数
               run[p]=data[i];
               choosed[i]=1;
               per=i;
               choose_num(data,choosed,m-1,run,p+1,res);
           }
       }
    }
}

从数组中找出出现次数大于1/2n的元素,

或者1/3n的元素,LeetCode 169,LeetCode 229

思路1:摩尔投票法.

1/2,概率算法不推荐,每次选两个不一样的,然后删除,最后剩下的两个,1/3就选三个不同的。具体实现不需要真的删除,只需要用count,一样就加1,不一样就减一,等于0就换下一个数。

对于1/3,https://blog.csdn.net/fuqiuai/article/details/82989138

class Solution {
public:
    vector majorityElement(vector& nums) {
        if(nums.size()==0) return {};
        vector res;
        int count1=0,count2=0;
        int num1=0,num2=0;//初始值可以随便选一个
//得到两个次数最多的数
        for(int i=0;inums.size()/3) res.push_back(num1);
        if(count2>nums.size()/3) res.push_back(num2);
        return res;
    }
};


思路2:用hash,unordered_multiset

unordered_multiset的头文件是unordered_set
int majorityElement(vector& nums) {
        unordered_multiset my_hash;
        for(int i=0;i=nums.size()/2)
            {
                if(my_hash.count(nums[i])>=(nums.size()+1)/2) return nums[i];
            }
        }
        return INT_MIN;//没找到
    }

飞地数量

给出一个二维数组 A,每个单元格为 0(代表海)或 1(代表陆地)。

移动是指在陆地上从一个地方走到另一个地方(朝四个方向之一)或离开网格的边界。

返回网格中无法在任意次数的移动中离开网格边界的陆地单元格的数量。

示例 1:

输入:[[0,0,0,0],[1,0,1,0],[0,1,1,0],[0,0,0,0]]
输出:3
解释: 
有三个 1 被 0 包围。一个 1 没有被包围,因为它在边界上。
示例 2:

输入:[[0,1,1,0],[0,0,1,0],[0,0,1,0],[0,0,0,0]]
输出:0
解释:
所有 1 都在边界上或可以到达边界。
 

提示:

1 <= A.length <= 500
1 <= A[i].length <= 500
0 <= A[i][j] <= 1
所有行的大小都相同
LeetCode1020

思路1:最简单的DFS

,从等于1 的地出发,往四周移动,如果可以到达边界,就返回false。LeetCode超时。DFS一定要注意是否需要标记已经走过的,走过的不可以往回!!!

class Solution {
public:
    int numEnclaves(vector>& A) {
        int bx=A.size();
        int count=0;
        if(bx==0) return 0;
        int by=A[0].size();
        for(int i=1;i> A,int bx,int by,int x,int y)
    {
       
        if(x<0||y<0||x>=bx||y>=by) return false;
        if(A[x][y]==1)
        {
            
            A[x][y]=0;//当前的不可以再返回了
            return (dfs(A,bx,by,x-1,y)&&
            dfs(A,bx,by,x,y-1)&&
            dfs(A,bx,by,x+1,y)&&
            dfs(A,bx,by,x,y+1));//有一个到达了边界就false
        }
        return true;
        
    }
};


思路2:反向思考

不是飞地的陆地一定可以通过边界1连到,所以从边界1出发把所有连到的1都变为0,剩下的1就是飞地个数。

class Solution {
public:
    int numEnclaves(vector>& A) {
        int bx=A.size();
        if(bx==0) return 0;
        int count=0;
        int by=A[0].size();
        for(int j=0;j> &A,int bx,int by,int x,int y)
    {
       
        if(x<0||y<0||x>=bx||y>=by) return ;
        if(A[x][y]==1)
        {
            
            A[x][y]=0;//当前的不可以再返回了
            dfs(A,bx,by,x-1,y);
            dfs(A,bx,by,x,y-1);
            dfs(A,bx,by,x+1,y);
            dfs(A,bx,by,x,y+1);
        }
        return ;
        
    }
};


思路3、找连通块,

https://blog.csdn.net/hh66__66hh/article/details/88929370

格雷编码

思路1:记住格雷编码


        **关键是搞清楚格雷编码的生成过程, G(i) = i ^ (i/2);**

        **如 n = 3: 共有pow(2,3)个**

        **G(0) = 000,** 

        **G(1) = 1 ^ 0 = 001 ^ 000 = 001**

       **G(2) = 2 ^ 1 = 010 ^ 001 = 011** 

       **G(3) = 3 ^ 1 = 011 ^ 001 = 010**

        **G(4) = 4 ^ 2 = 100 ^ 010 = 110**

        **G(5) = 5 ^ 2 = 101 ^ 010 = 111**

        **G(6) = 6 ^ 3 = 110 ^ 011 = 101**

        **G(7) = 7 ^ 3 = 111 ^ 011 = 100**

class Solution {
public:
    vector<int> grayCode(int n) {
        vector<int> res;
        if(n==0) return {0};
        for(int i=0;i<pow(2,n);i++)
            res.push_back(i^(i/2));
        return res;
    }
};

合并k个有序链表

思路1:

用一个自定义仿函数的小顶堆来实现,注意细节,每一轮出一个节点,进一个节点。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
#include
class Solution {
public:
    ListNode* mergeKLists(vector& lists) {
        priority_queue,cmp> heap;//小堆
        for(int i=0;inext!=NULL) heap.push(heap.top()->next);
        heap.pop();
        ListNode* tail=head;
        while(heap.size()>0)
        {
            tail->next=heap.top();
            if(heap.top()->next!=NULL) heap.push(heap.top()->next);
            heap.pop();
            tail=tail->next;
        }
        return head;
    }
    struct cmp{
    bool operator() (ListNode* node1,ListNode* node2)
    {
        return (node1->val)>(node2->val);//表示大的先出
    }
    };
};


课程表LeetCode207,拓扑排序AOV网问题

现在你总共有 n 门课需要选,记为 0 到 n-1。

在选修某些课程之前需要一些先修课程。 例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示他们: [0,1]

给定课程总量以及它们的先决条件,判断是否可能完成所有课程的学习?

示例 1:

输入: 2, [[1,0]] 
输出: true
解释: 总共有 2 门课程。学习课程 1 之前,你需要完成课程 0。所以这是可能的。
示例 2:

输入: 2, [[1,0],[0,1]]
输出: false
解释: 总共有 2 门课程。学习课程 1 之前,你需要先完成课程 0;并且学习课程 0 之前,你还应先完成课程 1。这是不可能的。
说明:

输入的先决条件是由边缘列表表示的图形,而不是邻接矩阵。详情请参见图的表示法。
你可以假定输入的先决条件中没有重复的边。
提示:

这个问题相当于查找一个循环是否存在于有向图中。如果存在循环,则不存在拓扑排序,因此不可能选取所有课程进行学习。
通过 DFS 进行拓扑排序 - 一个关于Coursera的精彩视频教程(21分钟),介绍拓扑排序的基本概念。
拓扑排序也可以通过 BFS 完成。

思路:

分三步,统计各个节点的入度。将入度为0的节点放入stack或者queue,当queue非空,则遍历节点关系。

class Solution {
public:
    bool canFinish(int numCourses, vector>& p) {
        if(p.empty()) return true;
        vector in(numCourses,0);
        for(int i=0;i st;
        for(int i=0;i

矩阵中的最长路径LeetCode329

给定一个整数矩阵,找出最长递增路径的长度。

对于每个单元格,你可以往上,下,左,右四个方向移动。 你不能在对角线方向上移动或移动到边界外(即不允许环绕)。

示例 1:

输入: nums = 
[
  [9,9,4],
  [6,6,8],
  [2,1,1]
] 
输出: 4 
解释: 最长递增路径为 [1, 2, 6, 9]。
示例 2:

输入: nums = 
[
  [3,4,5],
  [3,2,6],
  [2,2,1]
] 
输出: 4 
解释: 最长递增路径是 [3, 4, 5, 6]。注意不允许在对角线方向上移动。

思路1:

DFS求某个点开始的最长路径,然后遍历每个点调用dfs, 会超时

class Solution {
public:
    int longestIncreasingPath(vector>& matrix) {
        int bx=matrix.size();
        if(bx==0) return 0;
        int by=matrix[0].size();
        if(bx==0||by==0) return 0;
        int res=0,run=0;
       for(int i=0;ires) res=run;
            }
        }

      //  res=dfs(matrix,bx,by,2,1);
        return res;
    }
   
    
    int dfs(vector> & matrix,int &bx,int &by,int &i,int &j)
    {
        vector> direction={{0,1},{1,0},{0,-1},{-1,0}};
        vector can_walk(4,1);
        int x,y;
        for(int k=0;k<4;k++)
        {
            x=i+direction[k][0];
            y=j+direction[k][1];
            if(x<0||y<0||x>=bx||y>=by||matrix[x][y]<=matrix[i][j]) 
                can_walk[k]=0;//一定要标记走过的,不然就死循环
        }
        if(can_walk[0]+can_walk[1]+can_walk[2]+can_walk[3]==0)
            return 1;
        int res=0,run=0;
        for(int k=0;k<4;k++)
        {
            x=i+direction[k][0];
            y=j+direction[k][1];
            if(can_walk[k])
            {
                 run= dfs(matrix,bx,by,x,y);
                 if(run>res) res=run;

            }
            
        }
        res++;
        return res;
        
    }
};


思路2 记忆化

稍微修改一下就可以,用一个dp数组,避免重复计算,记忆化

class Solution {
public:
    int longestIncreasingPath(vector>& matrix) {
        int bx=matrix.size();
        if(bx==0) return 0;
        int by=matrix[0].size();
        if(bx==0||by==0) return 0;
        int res=0,run=0;
        vector> dp(bx,vector(by,0));
       for(int i=0;ires) res=run;
            }
        }
      //  res=dfs(matrix,bx,by,2,1);
        return res;
    }
   
    
    int dfs(vector> & matrix,vector> &dp,int bx,int by,int i,int j)
    {
        if(dp[i][j]>0) return dp[i][j];
        vector> direction={{0,1},{1,0},{0,-1},{-1,0}};
        vector can_walk(4,1);
        int x,y;
        for(int k=0;k<4;k++)
        {
            x=i+direction[k][0];
            y=j+direction[k][1];
            if(x<0||y<0||x>=bx||y>=by||matrix[x][y]<=matrix[i][j])
                can_walk[k]=0; 
        }
        if(can_walk[0]+can_walk[1]+can_walk[2]+can_walk[3]==0)
        {
            dp[i][j]=1;
            return 1;
        }

        int res=0,run=0;
        for(int k=0;k<4;k++)
        {
            x=i+direction[k][0];
            y=j+direction[k][1];
            if(can_walk[k])
            {
                 run= dfs(matrix,dp,bx,by,x,y);
                 if(run>res) res=run;
               // cout<<"yi"<<' ';
            }

        }
        res++;
        dp[i][j]=res;
        return res;

    }
};

思路3 dfs的另一种写法

int dfs(vector> &matrix, vector> &dp, int bx, int by, int x, int y)
{
    if (dp[x][y] > 0)
        return dp[x][y];
    vector> direction = {{0, 1}, {1, 0}, {-1, 0}, {0, -1}};

    int run = 0, res = 0, new_x, new_y;
    //如果下面四个循环中if都不成立,那么说明该点无路可走,所以就是1,0+
    for (int i = 0; i < 4; i++)
    {
        new_x = x + direction[i][0];
        new_y = y + direction[i][1];
        if (new_x >= 0 && new_y >= 0 && new_x < bx && new_y < by &&
            matrix[new_x][new_y] > matrix[x][y])
        {
            run = dfs(matrix, dp, bx, by, new_x, new_y);
            if (run > res)
                res = run;
        }
    }
    dp[x][y] = res + 1;
    return dp[x][y];
}

思路4 分开写

//提交发现分开写效率高很多,可能是因为条件判断简单了很多,但是容易乱
int dfs(vector> &m, vector> &dp, int row, int col, int i, int j)
{
    if (dp[i][j] > 0)
        return dp[i][j];
    int left = 1, right = 1, up = 1, down = 1;
    if (i > 0 && m[i - 1][j] < m[i][j]) //向上搜索,只可能出现i-1越界,下面同理
        up = 1 + dfs(m, dp, row, col, i - 1, j);
    if (i < row - 1 && m[i + 1][j] < m[i][j])
        down = 1 + dfs(m, dp, row, col, i + 1, j);
    if (j > 0 && m[i][j - 1] < m[i][j])
        left = 1 + dfs(m, dp, row, col, i, j - 1);
    if (j < col - 1 && m[i][j + 1] < m[i][j])
        right = 1 + dfs(m, dp, row, col, i, j + 1);
    dp[i][j] = max(max(up, down), max(left, right)); //记忆化搜索
    return dp[i][j];
}

LRU缓存机制

LeetCode146

运用你所掌握的数据结构,设计和实现一个  LRU (最近最少使用) 缓存机制。它应该支持以下操作: 获取数据 get 和 写入数据 put 。

获取数据 get(key) - 如果密钥 (key) 存在于缓存中,则获取密钥的值(总是正数),否则返回 -1。
写入数据 put(key, value) - 如果密钥不存在,则写入其数据值。当缓存容量达到上限时,它应该在写入新数据之前删除最近最少使用的数据值,从而为新的数据值留出空间。

进阶:

你是否可以在 O(1) 时间复杂度内完成这两种操作?

示例:

LRUCache cache = new LRUCache( 2 /* 缓存容量 */ );

cache.put(1, 1);
cache.put(2, 2);
cache.get(1);       // 返回  1
cache.put(3, 3);    // 该操作会使得密钥 2 作废
cache.get(2);       // 返回 -1 (未找到)
cache.put(4, 4);    // 该操作会使得密钥 1 作废
cache.get(1);       // 返回 -1 (未找到)
cache.get(3);       // 返回  3
cache.get(4);       // 返回  4

思路1 用list和map来实现

class LRUCache
{
public:
    LRUCache(int capacity) : _cap(capacity) {}

    // O(1)
    // hash 查找,如果找到了,就把 list 中的节点接下来移到头部
    int get(int key)
    {
        auto it = _m.find(key);
        if (it == _m.end())
            return -1;
        int val = it->second->second;
        _list.erase(it->second);
        _list.push_front(make_pair(key, val));
        _m[key] = _list.begin();
        return it->second->second;
    }

    // O(1)
    // 先查找旧 key 是否存在,如果存在,将节点移动到首部。
    // 如果不存在,插入新节点。
    // 如果容量超限,删除最脏的节点。
    void put(int key, int value)
    {
        auto it = _m.find(key);
        if (it != _m.end())
        {
            _list.erase(it->second);
        }

        _list.push_front(make_pair(key, value));
        _m[key] = _list.begin();

        if (_list.size() > _cap)
        {
            int key = _list.back().first;
            _m.erase(key);
            _list.pop_back();
        }
    }

private:
    unordered_map>::iterator> _m;
    // 新节点或刚访问的节点插入表头,因为表头指针可以通过 begin 很方便的获取到。
    list> _list;
    int _cap;
};

/**
 * Your LRUCache object will be instantiated and called as such:
 * LRUCache* obj = new LRUCache(capacity);
 * int param_1 = obj->get(key);
 * obj->put(key,value);
 */

最长连续序列

LeetCode128

给定一个未排序的整数数组,找出最长连续序列的长度。

要求算法的时间复杂度为 O(n)。

示例:

输入: [100, 4, 200, 1, 3, 2]
输出: 4
解释: 最长连续序列是 [1, 2, 3, 4]。它的长度为 4。

思路:对复杂度有要求,用hash,先将数组存到hash中,再依次遍历数组,如果有比它小1的数存在,说明它不是序列中最小的数,则跳过。记录每个最长序列。

class Solution
{
public:
    int longestConsecutive(vector &nums)
    {
        if (nums.empty())
            return 0;
        unordered_set my_set;
        for (int i = 0; i < nums.size(); i++)
        {
            my_set.insert(nums[i]);
        }

        int run = 0, count = 0, res = 0;
        for (int i = 0; i < nums.size(); i++)
        {
            if (my_set.count(nums[i] - 1) > 0)
                continue;
            run = nums[i];
            count = 0;
            while (my_set.count(run) > 0)
            {
                run++;
                count++;
            }
            if (count > res)
                res = count;
        }
        return res;
    }
};


串联所有单词的子串

leet30

给定一个字符串 s 和一些长度相同的单词 words。找出 s 中恰好可以由 words 中所有单词串联形成的子串的起始位置。

注意子串要与 words 中的单词完全匹配,中间不能有其他字符,但不需要考虑 words 中单词串联的顺序。

示例 1:

输入:
  s = "barfoothefoobarman",
  words = ["foo","bar"]
输出:[0,9]
解释:
从索引 0 和 9 开始的子串分别是 "barfoor" 和 "foobar" 。
输出的顺序不重要, [9,0] 也是有效答案。
示例 2:

输入:
  s = "wordgoodgoodgoodbestword",
  words = ["word","good","best","word"]
输出:[]

思路:

用map存word,记录各个单词的个数,然后遍历s, 每次取S子串,看看是不是在map中,在的话就将他的数量减1. 如果一次遍历中,能够遍历完所有单词,说明可以。

https://www.cnblogs.com/ariel-dreamland/p/9134212.html

class Solution
{
public:
    vector findSubstring(string s, vector &words)
    {
        if (words.empty())
            return {};
        vector res;
        unordered_map map_words;
        for (int i = 0; i < words.size(); i++)
        {
            map_words[words[i]]++; //默认初始化0,所以直接加
        }
        unordered_map map_words2 = map_words;

        int num_words = words.size();
        int l_words = words[0].size();
        if (s.size() < num_words * l_words)
            return {};
        for (int i = 0; i < s.size() - num_words * l_words + 1; i++)
        {
            map_words = map_words2;
            int j = 0;
            for (j = 0; j < num_words; j++)
            {
                string tmp = s.substr(i + j * l_words, l_words);
                // cout<<"num of "< 0 && map_words[tmp] > 0)
                    map_words[tmp]--; //存在tmp并且其个数大于0
                else
                    break; //不存在或者个数小于等于0,就说明不是
            }
            //  cout<<"j "<

思路2:单词序列不考虑顺序相同即可。把s中字符串按照单词长度分成一个单词数组,然后遍历,如果和map相同,则是。

三维形体的面积

LeetCode892

在 N * N 的网格上,我们放置一些 1 * 1 * 1  的立方体。

每个值 v = grid[i][j] 表示 v 个正方体叠放在对应单元格 (i, j) 上。

请你返回最终形体的表面积。

示例 1:

输入:[[2]]
输出:10
示例 2:

输入:[[1,2],[3,4]]
输出:34
示例 3:

输入:[[1,0],[0,2]]
输出:16
示例 4:

输入:[[1,1,1],[1,0,1],[1,1,1]]
输出:32
示例 5:

输入:[[2,2,2],[2,1,2],[2,2,2]]
输出:46

思路:遍历单元格,面积等于自身堆叠面积-四周重叠部分

class Solution
{
public:
    int surfaceArea(vector> &grid)
    {
        int bx = grid.size();
        if (bx == 0)
            return 0;
        int by = grid[0].size();
        int s = 0;
        for (int i = 0; i < bx; i++)
            for (int j = 0; j < by; j++)
            {

                if (grid[i][j] == 0)
                    continue; //如果是0就跳过
                int tmp = grid[i][j] * 4 + 2;
                if (i - 1 >= 0)
                    tmp = tmp - min(grid[i - 1][j], grid[i][j]);
                if (j - 1 >= 0)
                    tmp -= min(grid[i][j - 1], grid[i][j]);
                if (i + 1 < bx)
                    tmp -= min(grid[i + 1][j], grid[i][j]);
                if (j + 1 < by)
                    tmp -= min(grid[i][j + 1], grid[i][j]);
                s += tmp;
            }
        return s;
    }
};


最小矩形面积

LeetCode963

给定在 xy 平面上的一组点,确定由这些点组成的任何矩形的最小面积,其中矩形的边不一定平行于 x 轴和 y 轴。

如果没有任何矩形,就返回 0。

示例 1:
输入:[[1,2],[2,1],[1,0],[0,1]]
输出:2.00000
解释:最小面积的矩形出现在 [1,2],[2,1],[1,0],[0,1] 处,面积为 2。

示例 2:
输入:[[0,1],[2,1],[1,1],[1,0],[2,0]]
输出:1.00000
解释:最小面积的矩形出现在 [1,0],[1,1],[2,1],[2,0] 处,面积为 1。

思路:不要排斥暴力求解,暴力求解,把点放在set中,注意unordered_set不支持pair;遍历三个点的所有组合情况,先判断三个点能否组成正方形*,用向量。再求出第四个点,如果在set中,求面积。否则任意一个条件不满足直接continue,这样实际上降低了复杂度。

class Solution
{
public:
    double minAreaFreeRect(vector> &point)
    {
        int num_points = point.size();
        if (num_points < 4)
            return 0;
        set> point_set;
        for (int i = 0; i < num_points; i++)
        {
            point_set.insert(make_pair(point[i][0], point[i][1]));
        }
        double s = INT_MAX;
        double runs = 0;
        for (int i = 0; i < num_points - 2; i++)
        {
            for (int j = i + 1; j < num_points - 1; j++)
            {
                for (int k = j + 1; k < num_points; k++)
                {
                    //计算向量
                    pair point4;//待求的第四个点
                    //第四个点有三种情况,以其中一点作为顶点的两个向量正交
                    if ((point[i][0] - point[j][0]) * (point[i][0] - point[k][0]) + (point[i][1] - point[j][1]) * (point[i][1] - point[k][1]) == 0)
                    {
                        point4.first = point[j][0] + point[k][0] - point[i][0];
                        point4.second = point[j][1] + point[k][1] - point[i][1];//第四个点的坐标
                        if (point_set.find(point4) == point_set.end())
                            continue;//第四个点不存在
                        runs = s_square(point[i][0], point[j][0], point[k][0],
                                        point[i][1], point[j][1], point[k][1]);

                        if (runs < s)
                            s = runs;
                    }
                    else if ((point[j][0] - point[i][0]) * (point[j][0] - point[k][0]) + (point[j][1] - point[i][1]) * (point[j][1] - point[k][1]) == 0)
                    {
                        point4.first = point[i][0] + point[k][0] - point[j][0];
                        point4.second = point[i][1] + point[k][1] - point[j][1];
                        if (point_set.find(point4) == point_set.end())
                            continue;
                        runs = s_square(point[j][0], point[i][0], point[k][0],
                                        point[j][1], point[i][1], point[k][1]);

                        if (runs < s)
                            s = runs;
                    }
                    else if ((point[k][0] - point[i][0]) * (point[k][0] - point[j][0]) + (point[k][1] - point[i][1]) * (point[k][1] - point[j][1]) == 0)
                    {
                        point4.first = point[j][0] + point[i][0] - point[k][0];
                        point4.second = point[j][1] + point[i][1] - point[k][1];
                        if (point_set.find(point4) == point_set.end())
                            continue;
                        runs = s_square(point[k][0], point[j][0], point[i][0],
                                        point[k][1], point[j][1], point[i][1]);

                        if (runs < s)
                            s = runs;
                    }
                    else
                        continue;
                }
            }
        }
        if (s == INT_MAX)
            return 0;
        return s;
    }
    //由矩形的三个点坐标计算面积,以A作为顶点
    double s_square(int xa, int xb, int xc, int ya, int yb, int yc)
    {
        return (double)sqrt(1.0 * (xb - xa) * (xb - xa) + 1.0 * (yb - ya) * (yb - ya)) *
               (double)sqrt(1.0 * (xc - xa) * (xc - xa) + 1.0 * (yc - ya) * (yc - ya));
    }
};


最小栅栏,凸包问题

LeetCode587

在一个二维的花园中,有一些用 (x, y) 坐标表示的树。由于安装费用十分昂贵,你的任务是先用最短的绳子围起所有的树。只有当所有的树都被绳子包围时,花园才能围好栅栏。你需要找到正好位于栅栏边界上的树的坐标。

 

示例 1:

输入: [[1,1],[2,2],[2,0],[2,4],[3,3],[4,2]]
输出: [[1,1],[2,0],[4,2],[3,3],[2,4]]
解释:

示例 2:

输入: [[1,2],[2,2],[4,2]]
输出: [[1,2],[2,2],[4,2]]
解释:

即使树都在一条直线上,你也需要先用绳子包围它们。
 

注意:

所有的树应当被围在一起。你不能剪断绳子来包围树或者把树分成一组以上。
输入的整数在 0 到 100 之间。
花园至少有一棵树。
所有树的坐标都是不同的。
输入的点没有顺序。输出顺序也没有要求。

https://blog.csdn.net/liqinzhe11/article/details/79802986

思路1:暴力求解:

遍历两个的所有组合,如果剩下的全在线的一侧,那么这两个就是边界点。

思路2:边界扫描,

从一个点出发,用栈deque实现。O(nlogn),一直找在同一侧的点,两个方向都找一遍即可。

class Solution
{
public:
    vector> outerTrees(vector> &points)
    {
        sort(points.begin(), points.end(), cmp);
        if (points.size() < 4)
            return points;
        vector> res;
        vector stx(points.size()); //数组模拟栈,效率太低
        vector sty(points.size());
        int p = 0; //个数
        for (int i = 0; i < points.size(); ++i)
        {

            while (p >= 2 && is_right(stx[p - 2], sty[p - 2], stx[p - 1], sty[p - 1], points[i][0], points[i][1]))
            {
                p--;
            }
            stx[p] = points[i][0];
            sty[p] = points[i][1];
            ++p;
        }

        for (int i = 0; i < p; i++)
        {
            res.push_back({stx[i], sty[i]});
        }
        //反向遍历
        p = 0;
        for (int i = points.size() - 1; i >= 0; --i)
        {
            while (p >= 2 && is_right(stx[p - 2], sty[p - 2], stx[p - 1], sty[p - 1], points[i][0], points[i][1]))
            {
                p--;
            }
            stx[p] = points[i][0];
            sty[p] = points[i][1];
            ++p;
        }

        for (int i = 0; i < p; i++)
        {
            res.push_back({stx[i], sty[i]});
        }
        sort(res.begin(), res.end(), cmp);
        res.erase(unique(res.begin(), res.end(), equ), res.end()); //去重

        return res;
    }
    static bool cmp(vector a, vector b)
    {
        return a[0] < b[0] || (a[0] == b[0] && a[1] < b[1]);
    }
    static bool equ(vector a, vector b)
    {
        return a[0] == b[0] && a[1] == b[1];
    }
    bool is_right(int ax, int ay, int bx, int by, int cx, int cy)
    {
        int res = (bx - ax) * (cy - ay) - (cx - ax) * (by - ay);
        return res < 0; //向量叉积判断是否在左边
    }
};


搜索旋转排序数组||

LeetCode81

假设按照升序排序的数组在预先未知的某个点上进行了旋转。

( 例如,数组 [0,0,1,2,2,5,6] 可能变为 [2,5,6,0,0,1,2] )。

编写一个函数来判断给定的目标值是否存在于数组中。若存在返回 true,否则返回 false。

示例 1:

输入: nums = [2,5,6,0,0,1,2], target = 0
输出: true
示例 2:

输入: nums = [2,5,6,0,0,1,2], target = 3
输出: false

思路:先求出分界点,注意几种特殊情况,

class Solution
{
public:
    bool search(vector &nums, int target)
    {
        int l_nums = nums.size();
        if (l_nums == 0)
            return false;
        if (l_nums == 1)
            return nums[0] == target;
        if (l_nums == 2)
            return nums[0] == target || nums[1] == target;
        int left = 0, right = l_nums - 1;
        //寻找分界点
        int part = 0;
        while (left < right - 1)
        {
            int mid = (left + right) / 2;
            if (nums[mid] < nums[left])
            {
                right = mid;
            }
            else if (nums[mid] > nums[left])
                left = mid;
            else
            {
                if (nums[mid] == nums[right] && nums[left + 1] >= nums[left])
                    left++;
                else if (nums[mid] == nums[right] && nums[left + 1] < nums[left])
                {
                    part = left + 1;
                    break;
                }
                else
                    left = mid;
            }
            part = right;
        }
        int pivot = part; //分界点
        //cout<

最长有效括号

给定一个只包含 '('')' 的字符串,找出最长的包含有效括号的子串的长度。

示例 1:

输入: "(()"
输出: 2
解释: 最长有效括号子串为 "()"
示例 2:

输入: ")()())"
输出: 4
解释: 最长有效括号子串为 "()()"

测试例子:NULL, ((), (())((), ()), ()()(((), ())((()()(())

思路1:

这题首先应该想到暴力求解,成对出现的首先想到两两相消,用栈。一个函数用于判断一组字符串是否是有效括号,另一个用于穷举可能出现的括号段。会超时

class Solution
{
public:
    bool ok(string &s)//用栈来判断是否是有效括号
    {
        if (s.size() == 0)
            return false;
        stack st;
        for (int i = 0; i < s.size(); i++)
        {
            if (s[i] == '(')
                st.push(s[i]);
            else if (s[i] == ')' && st.size() > 0)
                st.pop();
            else if (s[i] == ')' && st.size() == 0)
                return false;
        }
        if (st.empty())
            return true;
        return false;
    }

    int longestValidParentheses(string &s)
    {

        if (s.size() < 2)
            return 0;
        int res = 0, run = 0;
        for (int i = 0; i < s.size() - 1; i++)
        {

            if (s[i] == ')')
                continue;
            for (int j = i + 1; j < s.size(); j++)
            {
                if (s[j] == '(')
                    continue;
                string tmp = s.substr(i, j - i + 1);
                if (ok(tmp))
                    run = j - i + 1;
                if (run > res)
                    res = run;
            }
        }
        return res;
    }
};

思路2:https://www.cnblogs.com/coldyan/p/5921560.html

思路3:https://www.cnblogs.com/zpfbuaa/p/6531746.html

O(n)解法,这种思路可以提供一个判断有效括号对的新方案。从右往左相当于把字符串镜像后从左往右,所以也可以写一个求镜像的函数。

代码:

class Solution
{
public:
    int longestValidParentheses(string s)
    {

        if (s.size() < 2)
            return 0;
        int num1 = 0, num2 = 0, max1 = 0, max2 = 0;
        int left = 0, right = 0;
        for (int i = 0; i < s.size(); i++)
        {
            if (s[i] == '(')
                left++;
            else
                right++;
            if (right == left) //此时一定是一对有效括号
                num1 = left * 2;
            if (num1 > max1)
                max1 = num1; //从左到右数最大长度
            else if (right > left)//右括号多了就不可能是有效括号了
            {
                left = 0;//个数重置为零
                right = 0;
            }
        }
//反向遍历
        left = 0;
        right = 0;
        for (int i = s.size() - 1; i >= 0; i--)
        {
            if (s[i] == ')')
                right++;
            else
                left++;
            if (right == left)
                num2 = left * 2;
            if (num2 > max2)
                max2 = num2;
            else if (right < left)
            {
                left = 0;
                right = 0;
            }
        }
        return max(max1, max2);
    }
};


通配符匹配

LeetCode44

给定一个字符串 (s) 和一个字符模式 (p) ,实现一个支持 '?' 和 '*' 的通配符匹配。

'?' 可以匹配任何单个字符。
'*' 可以匹配任意字符串(包括空字符串)。
两个字符串完全匹配才算匹配成功。

说明:

s 可能为空,且只包含从 a-z 的小写字母。
p 可能为空,且只包含从 a-z 的小写字母,以及字符 ? 和 *。
示例 1:

输入:
s = "aa"
p = "a"
输出: false
解释: "a" 无法匹配 "aa" 整个字符串。
示例 2:

输入:
s = "aa"
p = "*"
输出: true
解释: '*' 可以匹配任意字符串。

测试例子:
s=””  p=”a” false
s=”acd”   p=”” false
s=”acd”  p=”*ac?” true

思路1:函数递归

可以参考正则表达式匹配的思路。但是超时了一点点。

https://www.cnblogs.com/grandyang/p/4461713.html

class Solution
{
public:
    bool isMatch(string s, string p)
    {
        if (p.size() == 0)
            return s.empty();
        if (s.empty())
        {
            for (int i = 0; i < p.size(); i++)
            {
                if (p[i] != '*')
                    return false;
            }
            return true;
        }
        if (p.size() == 1)
        {
            if (p[0] == '*')
                return true;
            else if (p[0] == '?' && s.size() == 1)
                return true;
            else if (p[0] == s[0] && s.size() == 1)
                return true;
        }
        //递归开始
        if (s[0] == p[0] || p[0] == '?')
        {
            if (isMatch(s.substr(1), p.substr(1)))
                return true;
        }
        if (p[0] == '*')
        {
            for (int i = 0; i < s.size(); i++)
            {
                if (isMatch(s.substr(i), p.substr(1)))
                    return true; //主要是这一句在循环里面,很容易超时
            }
        }
        return false;
    }
};

思路2:https://blog.csdn.net/qq_41231926/article/details/82732623

状态定义: dp(x,y)表示 s[0,x) 和 p[0,y) 字符串是否匹配
状态转移关系
(1) if p[y] == '?'  则 dp[x][y] = dp[x-1][y-1]
(2) if p[y] == s[x],  则 dp[x][y] = dp[x-1][y-1]
(3) if p[y] == '*',  则 dp[x][y] = dp[x][y-1] || dp[x-1][y]

3的推导可以用数列求和来做,做差法求数列和。

dp(x,y)表示从0开始数,s的前x个和p的前y个是否匹配。一定注意初始值。

class Solution
{
public:
    bool isMatch(string s, string p)
    {
        int slen = s.size();
        int plen = p.size();
        vector> dp(slen + 1, vector(plen + 1, false));
        dp[0][0] = true;
        for (int i = 1; i <= plen; ++i)
        {
            if (p[i - 1] == '*')
                dp[0][i] = dp[0][i - 1];
            //当s为空时的情况
        }

        for (int i = 1; i <= slen; i++)
        {
            for (int j = 1; j <= plen; j++)
            {
                if (s[i - 1] == p[j - 1] || p[j - 1] == '?')
                    dp[i][j] = dp[i - 1][j - 1];
                else if (p[j - 1] == '*')
                {
                    dp[i][j] = dp[i][j - 1] || dp[i - 1][j];
                }
            }
        }
        return dp[slen][plen];
    }
};

猜数字大小||

LeetCode375

我们正在玩一个猜数游戏,游戏规则如下:

我从 1 到 n 之间选择一个数字,你来猜我选了哪个数字。

每次你猜错了,我都会告诉你,我选的数字比你的大了或者小了。

然而,当你猜了数字 x 并且猜错了的时候,你需要支付金额为 x 的现金。直到你猜到我选的数字,你才算赢得了这个游戏。

示例:

n = 10, 我选择了8.

第一轮: 你猜我选择的数字是5,我会告诉你,我的数字更大一些,然后你需要支付5块。
第二轮: 你猜是7,我告诉你,我的数字更大一些,你支付7块。
第三轮: 你猜是9,我告诉你,我的数字更小一些,你支付9块。

游戏结束。8 就是我选的数字。

你最终要支付 5 + 7 + 9 = 21 块钱。
给定 n ≥ 1,计算你至少需要拥有多少现金才能确保你能赢得这个游戏。


思路:极小化极大

用递归可以解决,加入dp 进行记忆化优化

class Solution
{
public:
    int getMoneyAmount(int n)
    {
        if (n == 1)
            return 0;
        if (n == 2)
            return 1;
        vector> dp(n + 2, vector(n + 2, 0));
        //初始化,一定要这步,否则函数的循环会覆盖它,或者dp初始值是一个很大的数
        for (int i = 1; i <= n - 2; i++)
        {
            dp[i][i] = i;
            dp[i][i + 1] = i;
            dp[i][i + 2] = i + 1;
        }
        get_money(1, n, dp);
        return dp[1][n];
    }
    //[low,high]区间至少需要多少钱才可以猜中
    int get_money(int low, int high, vector> &dp)
    {
        if (dp[low][high] != 0)
            return dp[low][high]; //记忆化
        if (low > high)
            return 0;

        if (low == high)
            dp[low][high] = low;
        if (low == high - 1)
            dp[low][high] = low;
        if (low == high - 2)
            dp[low][high] = low;
        int res = 10000;
        for (int i = low; i <= high; i++)
        {
            //猜i,则需要付出至少tmp的钱
            int tmp = i + max(get_money(low, i - 1, dp), get_money(i + 1, high, dp));
            if (tmp < res)
                res = tmp;
        }
        dp[low][high] = res;
        return res;
    }
};	

思路2:用dp数组

和上面的复杂度差不多

//矩阵链乘法

//相似的题目有 887鸡蛋掉落 312戳气球 132分割回文串

class Solution
{
public:
    int getMoneyAmount(int n)
    {
        vector> dp(n + 1, vector(n + 1, INT_MAX));
        for (int i = 1; i <= n; i++)
            dp[i][i] = 0;
        for (int l = 1; l <= n; l++)
        {
            for (int i = 1; i + l <= n; i++)
            {
                int j = i + l;
                if (i == j - 1)
                {
                    dp[i][j] = i;
                    cout << i << " " << j << " " << dp[i][j] << endl;
                    continue;
                }
                for (int k = i + 1; k < j; k++)
                {
                    dp[i][j] = min(dp[i][j], max(dp[i][k - 1], dp[k + 1][j]) + k);
                }
            }
        }
        return dp[1][n];
    }
};

三数之和

给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。

注意:答案中不可以包含重复的三元组。

例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],

满足要求的三元组集合为:
[
  [-1, 0, 1],
  [-1, -1, 2]
]

思路:双指针法

指针对撞法。.将数组排序 2.定义三个指针,i,j,k。遍历i,那么这个问题就可以转化为在i之后的数组中寻找nums[j]+nums[k]=-nums[i]这个问题,也就将三数之和问题转变为二数之和—(可以使用双指针)

class Solution
{
public:
    vector> threeSum(vector &nums)
    {
        vector> res;
        sort(nums.begin(), nums.end());
        if (nums.empty() || nums.back() < 0 || nums.front() > 0)
            return {};
        for (int k = 0; k < nums.size(); ++k)
        {
            if (nums[k] > 0)
                break;
            if (k > 0 && nums[k] == nums[k - 1])
                continue; //去重
            int target = 0 - nums[k];
            int i = k + 1, j = nums.size() - 1;
            while (i < j)
            {
                if (nums[i] + nums[j] == target)
                {
                    res.push_back({nums[k], nums[i], nums[j]});
                    while (i < j && nums[i] == nums[i + 1])
                        ++i;
                    while (i < j && nums[j] == nums[j - 1])
                        --j;
                    ++i;
                    --j;
                }
                else if (nums[i] + nums[j] < target)
                    ++i;
                else
                    --j;
            }
        }
        return res;
    }
};

下一个排列

LeetCode35

next_permutation实现,

思路:其实就是从数组倒着查找,找到nums[i] 比nums[i+1]小的时候,就将nums[i]跟nums[i+1]到nums[nums.length - 1]当中找到一个最小的比nums[i]大的元素交换。交换后,再把nums[i+1]到nums[nums.length-1]排序,就ok了,[0,5,4,3,2,1],下一个是[1,0,2,3,4,5]

接雨水

LeetCode42

[外链图片转存失败(img-5lGlBjuh-1568281841747)(c++刷题笔记.assets/1568280868388.png)]

给定 n 个非负整数表示每个宽度为 1 的柱子的高度图,计算按此排列的柱子,下雨之后能接多少雨水。

 
上面是由数组 [0,1,0,2,1,0,1,3,2,1,2,1] 表示的高度图,在这种情况下,可以接 6 个单位的雨水(蓝色部分表示雨水)。 感谢 Marcos 贡献此图。

示例:

输入: [0,1,0,2,1,0,1,3,2,1,2,1]
输出: 6

思路:先找出最大的索引,然后从左到右,从右到左遍历

class Solution
{
public:
    int trap(vector &height)
    {
        if (height.size() < 3)
            return 0;
        int max_index = 0;
        for (int i = 0; i < height.size(); i++)
            if (height[i] > height[max_index])
                max_index = i;
        //left
        int h2 = 0; //局部最大点
        int res = 0;
        for (int i = 0; i <= max_index; i++)
        {
            if (height[i] > h2)
                h2 = height[i];
            else
                res += h2 - height[i];
        }

        //right
        h2 = 0;
        for (int i = height.size() - 1; i >= max_index; i--)
        {
            if (height[i] > h2)
                h2 = height[i];
            else
                res += h2 - height[i];
        }
        return res;
    }
};

写一个不可以被继承的类

思路:可以参考单例模式,c++11的final关键字,直接加上就可以

class Base
{
private:
    Base() {}
    ~Base() {}
    friend class B;
};
class B : public virtual Base //虚继承,
{
} ;


虚拟继承的作用,是为了多继承而存在,Java中就无法多继承。引申:虚函数的作用是为了实现多态,基类某一个成员函数声明为虚函数,创建虚函数表,派生类中的同名函数也会自动生成虚函数表。静态成员函数和构造函数不能声明为虚函数。因为静态成员函数不属于类生成的对象,所以没有this,没有const, 静态成员函数仅可以调用静态数据成员,全局变量,自身参数,或者其他静态成员函数。静态数据成员虽然当做类的成员,但是它在内存中仅有一份拷贝。
静态成员函数不能加const的原因,首先明白void f() const {} 意义是函数不改变类的数据成员。而static成员函数根本无法访问类的普通成员,所以不需要加const,也不能加const。
类的大小问题:不存在继承关系,普通成员对齐加上虚函数表。普通继承,基类成员+派生类成员+虚函数表一份。普通多继承,各个基类成员+虚函数表一份。虚继承:如果自己有虚函数,还要加上自己的虚函数表,和基类的虚函数表,这时候有两张虚函数表。

跳跃游戏2

LeetCode45

给定一个非负整数数组,你最初位于数组的第一个位置。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

你的目标是使用最少的跳跃次数到达数组的最后一个位置。

示例:

输入: [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
     从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。

思路:dp超时,用贪心,记录当前能到达的最大步数。

#include 
#include 
using namespace std;
#define debug(x) cout< &nums)
{
    if (nums.size() < 2)
        return 0;
    if (nums.size() == 2)
        return 1;
    int max_reach = 0, res = 0, p = 0, pwalk = 0;
    for (int i = 0; i < nums.size(); i++)
    {
        if (nums[i] + i > max_reach)//每遍历一个点,就判断从这里走可以最远走多远
        {
            max_reach = nums[i] + i;
            pwalk = i;
        }
        if (i == p)//当走到了上一步的最远路径时,这一步很重要
        {
            res++;
            p = max_reach;
            debug(pwalk<<"--->");//记录了实际的路线
        }

        if (p == nums.size() - 1)
            return res;
        if (p >= nums.size())//走过了也算到达了末尾
            return res;
    }
    return res;
}

int main()
{
    vector nums = {2,3,1,1,4};
    cout<

N皇后问题

n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

[外链图片转存失败(img-FjoMwgTs-1568281841748)(c++刷题笔记.assets/1568281061434.png)]

上图为 8 皇后问题的一种解法。

给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。

每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。

示例:

输入: 4
输出: [
 [".Q..",  // 解法 1
  "...Q",
  "Q...",
  "..Q."],

 ["..Q.",  // 解法 2
  "Q...",
  "...Q",
  ".Q.."]
]
解释: 4 皇后问题存在两个不同的解法。

一旦选定了一个位置,那么该位置所在行、列以及两条对角线都不可以再选。显然用dfs

需要注意保存mark,再用一个new_mark

class Solution
{
public:
    static vector> solveNQueens(int n)
    {
        vector> res;
        if (n < 1)
            return res;
        vector> mark(n, vector(n, 0));//初始化mark表示都没有被标记过
        vector run(n, -1);
        vector> choose;
        dfs(mark, n, run, 0, choose);
        for (int i = 0; i < choose.size(); i++)
        {
            vector s1;
            for (int j = 0; j < n; j++)
            {
                string str;
                for (int k = 0; k < n; k++)
                    str = str + '.';
                str[choose[i][j]] = 'Q';
                s1.push_back(str);
            }
            res.push_back(s1);
            //  cout<> &mark, int n, vector &run, int p, vector> &choose)
    {

        vector> new_mark = mark;
        if (p >= n)
        {
            choose.push_back(run);
            //cout<<"find"<= 0 && j < n; p1--, j++)
                    new_mark[p1][j] = 1;
                for (int p1 = p, j = i; p1 >= 0 && j >= 0; p1--, j--)
                    new_mark[p1][j] = 1;
                for (int p1 = p, j = i; p1 < n && j >= 0; p1++, j--)
                    new_mark[p1][j] = 1;
                dfs(new_mark, n, run, p + 1, choose);
            }
        }
    }
};


最长公共子串

查找两个字符串a,b中的最长公共子串。若有多个,输出在较短串中最先出现的那个。

思路:dp[i][j]表示a[0,i)和b[0,j)的最长公共子串长度,左闭右开

(1) i ==0 or j ==0  则 , dp[i][j] = 0
(2) A[i]==B[j]   则, dp[i][j] = dp[i-1][j-1] + 1
(3) A[i] != B[j]  则, dp[i][j] = 0
#include 
#include 
#include 
using namespace std;
#define debug(x) cout << x //cout<<__LINE__<<"|"<> dp(len1 + 1, vector(len2 + 1, 0));
    for (i = 1; i <= len1; i++)
    {
        for (j = 1; j <= len2; j++)
        {
            //只有在最后一个字符相等的时候,子串长度才可能更长,所以不用关注不相等的情况,递推关系第三条不对,但可以忽略
            if (str1[i - 1] == str2[j - 1])
                dp[i][j] = dp[i - 1][j - 1] + 1;
            if (dp[i][j] > max) //改成 >= 就是最后出现的
            {
                max = dp[i][j];
                start = i - max;
            }
            Sleep(100);
            debug(dp[i][j] << " ");
        }
        debug(endl);
    }
    debug(str1.substr(start, max) << endl);
    return str1.substr(start, max).size(); //substr就是最长的那个
}

int main()
{
    string s1 = "hello,hahha";
    string s2 = "llobhufds";
    cout << get_long(s1, s2) << endl;
}

正则表达式匹配

LeetCode10

给你一个字符串 s 和一个字符规律 p,请你来实现一个支持 '.''*' 的正则表达式匹配。

'.' 匹配任意单个字符
'*' 匹配零个或多个前面的那一个元素
所谓匹配,是要涵盖 整个 字符串 s的,而不是部分字符串。

说明:

s 可能为空,且只包含从 a-z 的小写字母。
p 可能为空,且只包含从 a-z 的小写字母,以及字符 . 和 *。
示例 1:

输入:
s = "aa"
p = "a"
输出: false
解释: "a" 无法匹配 "aa" 整个字符串。

递推关系:dp[i][j]表示s[0,i)和p[0,j)是否匹配

思路1:递归

class Solution
{
public:
    bool isMatch(string s, string p)
    {
        if (s.empty())
        {
            if (p.empty() || p[1] == '*' && p.size() == 2)
                return true;

            else if (p.empty() || p[1] == '*')
                return isMatch(s, p.substr(2, p.size()));
            else
                return false;
        }
        if (p.empty())
            return false;

        if (p[1] == '*')
        {
            if (p[0] == s[0] || p[0] == '.')
                return isMatch(s.substr(1, s.size()), p) || isMatch(s, p.substr(2, p.size()));
            else
                return isMatch(s, p.substr(2, p.size()));
        }
        if (p[0] == s[0] || p[0] == '.')
            return isMatch(s.substr(1, s.size()), p.substr(1, p.size()));
        return false;
    }
};


思路2:dp, 仿照通配符的方法,首先处理dp[0][]的情况,然后分情况dp

class Solution
{
public:
    bool isMatch(string s, string p)
    {
        int slen = s.size(), plen = p.size();
        //dp[i][j]表示s[0,i)和p[0,j)是否匹配
        vector> dp(slen + 1, vector(plen + 1, false));
        dp[0][0] = true;
        //处理初始值,s.empty(),p非空时,只有p全是*才可以匹配
        if (plen > 1)
            for (int i = 2; i <= plen; i++)
                if (p[i - 1] == '*')
                    dp[0][i] = dp[0][i - 2];//-2是因为*是前面的字符重复了任意次
        //dp开始
        for (int i = 1; i <= slen; i++)
        {
            for (int j = 1; j <= plen; j++)
            {
                if (p[j - 1] == '.' || p[j - 1] == s[i - 1])
                    dp[i][j] = dp[i - 1][j - 1];
                if (p[j - 1] == '*' && j > 1)
                {
                    if (p[j - 2] == '.' || p[j - 2] == s[i - 1])
                        dp[i][j] = dp[i - 1][j] || dp[i][j - 2];
                    else
                        dp[i][j] = dp[i][j - 2];
                }
            }
        }
        return dp[slen][plen];
    }
};

最大子序和

LeetCode53

给定一个整数数组 nums ,找到一个具有最大和的连续子数组(子数组最少包含一个元素),返回其最大和。

示例:

输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
进阶:

如果你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。


思路1:

O(n)解法,并求出子序,遍历,sum记录和,res记录结果

#include 
#include 
#include 
using namespace std;
#define debug(x) cout<<__LINE__<<"|"< &nums)
{
    if (nums.size() == 0)
        return 0;
    if (nums.size() == 1)
        return nums[0];
    int sum = nums[0], res = nums[0];
    int start = 0, end = 0;
    for (int i = 1; i < nums.size(); i++)
    {
        int start_pre = start, end_pre = end;//记录上一次的区间[start,end]都是闭
        if (sum > 0)//如果sum<=0, 那么说明sum对应的区间可以去掉了,只有大于0时候才有可能继续增加
        {
            sum = sum + nums[i];
            end = i;//新增了一个数,无论新增的数是否大于0,都暂时先加上,sum比以前的大才更新区间
        }
        else
        {
            sum = nums[i];
            start = i;
            end = i;
        }

        if (sum > res)
            res = sum;
        else     //还是以前的大
        {
            start = start_pre;
            end = end_pre;
        }
        debug(start<<' '< nums = {2,-2,3,-1,5};
    cout<

思路2:

用从sum表示从数列某一项开始到i位置的最大和。那么存在dp关系

sum[i]=max(sum[i-1],nums[i]);

最后在这些sum中选最大的那个,其实就是上面的思路。

不同的路径

leetcode62

一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。

机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。

问总共有多少条不同的路径?

思路1:组合数学

机器人一共走了m+n-2步,所以在这些步中选n-1步向下C((m+n-2),(m-1))。


//求组合数
long long comb(int m, int n) //计算c(m,n)
{
    if (n == 0)
        return 1;
    if (n == m)
        return 1;
    long long up = 1, low = 1;
    for (int i = 1; i <= n; i++)
        low = low * i;
    for (int i = m - n + 1; i <= m; i++)
        up *= i;
    return up / low;
}

int uniquePaths(int m, int n)
{
    return comb(m + n - 2, n - 1);
}


思路2:dp

dp[i][j]=dp[i-1][j]+dp[i][j-1]//只能从上边和左边过来

大家一起数二叉树

https://www.cnblogs.com/zufezzt/p/8120203.html
某一天,Zzq正在上数据结构课。老师在讲台上面讲着二叉树,zzq在下面发着呆。
突然zzq想到一个问题:对于一个n个节点,m个叶子的二叉树,有多少种形态呐?你能告诉他吗?
对于第一组样例的解释

 
输入描述:
每一组输入一行,两个正整数n,m(n<=50)意义如题目
输出描述:
每一行输出一个数,表示相应询问的答案取模1000000007
示例1
输入
4 2
10 5
输出
6
252
备注:
a取模b等于a%b,即a除以b的余数

dp[i][j] 表示有i个节点,j个叶子节点的不同二叉树的形态。

对于dp[i][j],我们可以枚举根节点左子 树的节点个数x和叶子节点个数y,将dp[x][y]∗dp[i−1−x][j−y]累加就可以得到dp[i][j]了。

const long long mod = 1e9 + 7;
long long cou(int m, int n)
{

    vector> dp(m + 1, vector(n + 1, 0));
    dp[0][0] = 1;
    dp[1][1] = 1;
    if (m < 2 || n < 1)
        return dp[m][n];
    for (int i = 2; i <= m; i++)
    {
        for (int j = 1; j <= i; j++)
        {
            for (int x = 0; x <= i - 1; x++)
                for (int y = 0; y <= x; y++)
                {
                    if (j - y < 0)
                        continue;
                    long long sum = (dp[x][y] * dp[i - 1 - x][j - y]) % mod;
                    dp[i][j] = (dp[i][j] + sum) % mod;
                }
        }
    }
    return dp[m][n];
}

黑白矩阵

一个黑白矩阵是指,每个棋子上下左右值都一样,且和自己不一样,输入一个m*n的矩阵,求最少改变多少个数字就可以使得它是黑白矩阵。

思路:

最后只有两个值,分别遍历,用hash保存最多的那个元素,总数-最多的就是要改变的。对两个值交换一下顺序,最后比较一下取小的。

#include 
#include 
#include 
using namespace std;

int trans1(vector> &D, int m, int n)
{

    int res = 0;
    unordered_multiset odd;
    int max1 = 0, num1 = 0, max_num1 = 0;
    for (int i = 0; i < m; i++)
    {
        if (i % 2 >= n)
            continue;
        for (int j = i % 2; j < n; j = j + 2)
        {
            odd.insert(D[i][j]);
            if (odd.count(D[i][j]) > max1)
            {
                max1 = odd.count(D[i][j]);
                max_num1 = D[i][j];
            }
            num1++; //hei
        }
    }
    res += num1 - max1;
    unordered_multiset even;
    int max2 = 0, num2 = 0, max_num2 = 0;
    for (int i = 0; i < m; i++)
    {
        if (1 - i % 2 >= n)
            continue;
        for (int j = 1 - i % 2; j < n; j = j + 2)
        {
            even.insert(D[i][j]);
            if (even.count(D[i][j]) > max2 && D[i][j] != max_num1)
            {
                max2 = even.count(D[i][j]);
                max_num2 = D[i][j];
            }
            num2++;
        }
    }
    res += num2 - max2;
    return res;
}
int trans2(vector> &D, int m, int n)
{

    int res = 0;

    unordered_multiset even;
    int max2 = 0, num2 = 0, max_num2 = 0;
    for (int i = 0; i < m; i++)
    {
        if (1 - i % 2 >= n)
            continue;
        for (int j = 1 - i % 2; j < n; j = j + 2)
        {
            even.insert(D[i][j]);
            if (even.count(D[i][j]) > max2)
            {
                max2 = even.count(D[i][j]);
                max_num2 = D[i][j];
            }
            num2++;
        }
    }
    res += num2 - max2;

    unordered_multiset odd;
    int max1 = 0, num1 = 0, max_num1 = 0;
    for (int i = 0; i < m; i++)
    {
        if (i % 2 >= n)
            continue;
        for (int j = i % 2; j < n; j = j + 2)
        {
            odd.insert(D[i][j]);
            if (odd.count(D[i][j]) > max1 && D[i][j] != max_num2)
            {
                max1 = odd.count(D[i][j]);
                max_num1 = D[i][j];
            }
            num1++; //hei
        }
    }
    res += num1 - max1;
    return res;
}
int main()
{
    int m, n;
    cin >> m >> n;
    vector> D(m, vector(n, 0));
    for (int i = 0; i < m; i++)
        for (int j = 0; j < n; j++)
            cin >> D[i][j];
    int res1 = trans1(D, m, n);
    int res2 = trans2(D, m, n);
    int res = min(res1, res2);
    cout << res;
}

你可能感兴趣的:(C++)