题海精华——算法题精选

前言

做了一周的算法题,在原有的算法基础上进行一些深化的题目。主要是将一些做过比较好的题的思路,并且一边做,一边也修改了一些之前发的文档优化解题方式。
类似于错题重错,一错再错,死不悔改,至死方休的记忆加深学习法。并且暴力的方式都pass掉,只讲算法解法

暑假每日亿题

char文字串的操作集合

  • getline,获取此行的语句相比于cin,getline的优点就是在输入字符串的时候能够输入空格。而cin则会在输入空格时自动结束输入。

  • cin.getline(char* s, streamsize n, char delim),这里的参数char* s是输入的字符串变量, n是输入字符串的字符个数(第n个补’\0’), delim是输入终止条件,即遇到delim所代表的字符就终止输入。值得注意的是,在正常使用时 char delim可以省略,c++语言默认为’\0’。

  • getline(cin,str)
    getline(istream& is, string& str, char delim)
    is是标准输入流函数, str是用来存字符的变量名, delim是结束标志,此处作用与cin.getline()里的相同。

  • getline()是string流的函数,只能用于string类型的输入操作。
    cin.getline是std流的函数,用于char*类型的输入操作。

*getchar可以吃掉空格,\n和\0都会被存储,一次吃一个,ch=getchar();

  • scanf会有输入缓存遇到后也会停止读取
  • cin也是会卡掉数据空格或者换行

ASCII

大小顺序: 数大小
48~57为阿拉伯数字0-9,65 ~ 90为26个大写英文字母A-Z,97 ~ 122为26个小写英文字母A-Z。

G巴士计数

一些城市沿着一条笔直的公路建造。
这些城市从左到右编号为 1,2,3…
有 N 个 G 巴士在这条路上运行。
对于每个 G 巴士,我们知道它服务的城市范围:第 i 个 g 巴士为编号为 Ai 和 Bi 之间的城市(包含 Ai 和 Bi)提供服务。
现在给定你一个城市子集 P。
对于 P 中包含的每一座城市,我们需要找出有多少辆 G 巴士为该城市服务。

一开始看题目可以理解为从一条直线里的区间里,拉一堆线,找某点的公共线段数,较多抑或者少。根据一种输出入结果,进一步理解
在样例#1中,有四个 G 巴士。第一个服务城市 15 到 25,第二个服务城市 30 到 35,第三个服务城市 45 到 50,第四个服务城市 10 到 20。
城市 15 由第一个和第四个 G 巴士服务,城市 25 仅由第一个 G 巴士服务,因此答案输出为 2 1。

第一次看到使用桶排序的套层解法,也就是暴力,但这道题目的是让我们做出来差分。
对一段序列{0, 0, 0, 0, 0}, 如果我们想让它第二到第四个元素都加个1, 我们可以让第二个元素+1, 第五个元素-1,得到{0, 1, 0, 0, -1}, 再对它求一遍前缀和, 就可以得到{0, 1, 1, 1, 0}. 这就是差分的基本思想

本题几乎是裸差分问题, 对于每个巴士的服务范围[l, r], 我们在差分数组中令count[l] += 1, count[r+1]-=1即可, 最后将所有的巴士范围都插入到差分数组中后, 求一遍前缀和, 即为所有城市的巴士数量

#include
using namespace std;
const int N = 5e3+10;
int n,m,st[N];
int main()
{
    int T;
    cin>>T;
    for(int cases=1;cases<=T;cases++){//几组数据
        memset(st, 0, sizeof st);
        cin>>n;
        for(int i=0;i<n;i++){
            int l,r;
            cin>>l>>r;
            st[l]++,st[r+1]--;
        }//差分的预处理,某区间想变,也就是做基础变量,为之后的s差分机做预处理。
        for(int i=1;i<N;i++)st[i]+=st[i-1];//差分机
        cin>>m;
        printf("Case #%d: ", cases);
        while (m -- )
        {
            int x;cin>>x;
            printf("%d ", st[x]);//基础的Q & A
        }
        puts("");
    }
    return 0;
}

范围分区(莱纳,你坐呀)

艾伦和芭芭拉一起玩数字游戏。给定前 N 个正整数 1∼N。
首先,由艾伦从中选取一些数字(不能不取),然后,芭芭拉选取剩余所有数字(如果有的话)。
不妨设艾伦选取的所有数字之和为 A,芭芭拉选取的所有数字之和为 B。
现在,给定一个比率 X:Y,要求 A:B 恰好等于 X:Y。
请你给出一种艾伦选取数字的合理方案。

这种方法是数论的解决方法。需要设计的约数解法+贪心
S=sum(1~~N);A+B=S;
A/B=X/Y;gcd(X,Y)=1;A=CX,B=CY;
CX+CY=S;
存在解 S%(X+Y)==0;
把S分为(x / (x+y), y / (x+y) )两部分令A为任一个, 然后往结果数组中填数直到和为A
数学归纳法证明贪心算法的正确性
我们先搞个定义, 定义运用了上述贪心思想的函数 f(N, A), 作用是返回在1~N序列可能的 和为A的 子序列, 当函数f能正确返回我们需要的子序列时我们称函数f是正确的

数学归纳法三部曲

  1. 当n = 1时
    此时序列为[1], 所以A仅可能为1, 1确实是可以填入A且最大的数字.
    因此, 当n = 1时, 算法成立, f(1, A)正确
  2. 假设n = k-1时成立
    假设对任何合理的A, f(k-1, A)始终正确
  3. 当n = k时
    当A < k, 此时k不能被选入的子序列中, 所以问题f(k, A)等价于f(k-1, A), 成立
    当A >= k, 我们可以先把k加入子序列, 加入后在考虑剩下的和, 因此结果为 [k] + f(k-1, A-k), f(k-1, A-k)在2中已假设正确, 且先选的k是能加入子序列中最大的一个数, 因此当A >= k时算法成立

对任意正整数n, 对任意合理的A, f(n, A)都能返回1~n中sum为A的子序列, 因此证明了上述贪心思想是正确的

#include 
using namespace std;
typedef long long LL;
const int N = 5010;
int n, x, y;
int ans[N];
int main(){
    int T;
    cin >> T;
    for (int t = 1; t <= T; t ++)
    {
        cin >> n >> x >> y;
        int sum = 0;
        for (int i = 1; i <= n; i ++) sum += i;//总目的数
        int b = sum / (x + y);//比率是否可以整除
        if (sum % (x + y)) printf("Case #%d: IMPOSSIBLE\n", t);
        else {
            memset(ans, 0, sizeof ans);
            printf("Case #%d: POSSIBLE\n", t);
            int cnt = 0;
            int s = x * b;
            for (int i = n; i >= 1; i --)//贪心倒着拿最大的就行
            {
                if (s >= i) 
                {
                    s -= i;
                    ans[cnt ++] = i;
                }
            }
            cout << cnt << endl;
            for (int i = 0; i < cnt; i ++)
                cout << ans[i] << ' ';
            cout << endl;
        }
    }
}

正方形数组的数目

给定一个非负整数数组 A,如果该数组每对相邻元素之和是一个完全平方数,则称这一数组为正方形数组。
返回 A 的正方形排列的数目。
两个排列 A1 和 A2 不同的充要条件是存在某个索引 i,使得 A1[i]≠A2[i]

dfs全排列,并且要考虑到重复方案也就是等效排列剪枝对于每一种数字,第一个相同数字用了才能用第二个相同数字。对于排序过的数组,如果当前元素与前一个元素相同并且前一个元素还没有用过,那么当前元素也不能使用。

#include
const int N=15;
int n,number[N];
int ans;
bool vst[N];
bool isSqr (int x)
{//判断是否为平方数 
    int SQRT=(int)sqrt(x);//取出平方根,要向下取整 
    return SQRT*SQRT==x;//如果平方根的平方与自己一样,那就是平方数 
} 
void dfs (int lst,int unvst)
{//lst表示目前队列中最后一个元素,unvst表示还未枚举的元素数量 
    if (unvst==0)
    {//没有遍历过的元素全部没有了 
        ans++;//符合要求的答案再增 
        return;//返回 
    }
   for (int iNum=1;iNum<=n;++iNum)
    {//枚举数字 
        if (vst[iNum]) continue;//枚举过了,跳过
        if (number[iNum]==number[iNum-1] and not vst[iNum-1]) continue;
        //如果自己和上一个没被用过的数字一样,不能使用
        if (lst==-1 or isSqr(lst+number[iNum]))
        {//第一个元素或者满足题目要求 
            vst[iNum]=true;//加入队列 
            dfs(number[iNum],unvst-1);//继续深入 
            vst[iNum]=false;//不加入队列,撤销(,继续) 
        } 
    }
}
int main (){
    cin>>n;
    for (int i=1;i<=n;++i)
        cin>>number[i];
    sort(number+1,number+n+1);//排序 
    number[0]=-1;//防止一号判断出错
    dfs(-1,n);//注意要初始化为-1,防止一号判断出错
    cout<<ans<<endl;//输出答案 
    return 0;
}

围圈报数

链表题,最基本的套圈模型
N 个人围成一圈顺序编号,从 1 号开始按 1、2、3 顺序报数,报 3 者退出圈外,其余的人再从 1、2、3 开始报数,报 3 的人再退出圈外,依次类推。主要是适应一下链表的搭建,typedef和直接struct都可以,看清华的数据结构书上的比较套路化,建议使用那本书。

#include
using namespace std;
const int N = 51;
int T;
typedef struct TNode {
    int data;
    bool test;
    struct TNode* next;
    TNode (int x):data(x),test(true),next(NULL){}
}node,*linklist;
int main()
{
    cin >> T;
    while (T--) {
        int n; cin >> n;
        node  *h, *t;
        for (int i = 1; i <= n; i++) {
            if (i == 1) { h = new node(1); t = h;}
            else {
                node* a = new node(i);
                t->next = a;
                t = t->next;
            }
        }
        t->next = h;
        t = h;
        int cnt = 0;
        while (n > 0) {
            if (t->test == true) {
                cnt++;
                if (cnt % 3 == 0) {
                    cout << t->data << " "; n--; t->test = false;
                }
            }
            t = t->next;
        }
        cout << endl;
    }
    return 0;
}

排列与二进制

数论中从 n 个不同元素中取出 m(m<=n)个元素的所有排列的个数,叫做从 n 中取 m 的排列数,记为 p(n,m)。
具体计算方法为 p(n,m)=n(n−1)(n−2)……(n−m+1)=n!/(n−m)!(规定 0!=1)。
在此基础上引入进制表示
当 n 和 m 不是很小时,这个排列数是比较大的数值,比如 p(10,5)=30240。
如果用二进制表示为 p(10,5)=30240=(111011000100000)b,也就是说,最后面有 5 个零。
我们的问题就是,给定一个排列数,算出其二进制表示的后面有多少个连续的零。
数论分析后::二进制末尾有多少个0,就是p(n,m)中有几个2因子
Ans=fun(n,2)−fun(n−m,2)

#include
using namespace std;
const int N = 1e4+10;
int n,m,ans;
int get(int n,int p){
    int s=0;
    while(n)s+=n/=p;
    return s;
}
int main(){
    cin>>n>>m;
    while(n||m){
        ans=get(n,2)-get(n-m,2);
        cout<<ans<<endl;
        cin>>n>>m;
    }
    return 0;
}

LCA 二叉树

给定一个 n 个结点(编号 1∼n)构成的二叉树,其根结点为 1 号点。进行 m 次询问,每次询问两个结点之间的最短路径长度。树中所有边长均为 1。
可以引入一个简单的逻辑,就是两个长度不同的链表找到公共点,会有la+lb与bl+al的循环差,那个点就是公共交集点,LCA也是如此,
当节点x比节点y深,让x向上爬树,变成自己的父节点;
当节点y比节点x深,让y向上爬树,变成自己的父节点。
此时两个节点深度相同,让两个节点同时爬树,直到重合为止,第一次重合的那一个节点就是它们的最近公共祖先。

int LCA (int x,int y)
{//寻找x和y的最近公共祖先 
    while(d[x]>d[y])
        x=p[x];//x更深,让x往上走
    while(d[y]>d[x])
        y=p[y];//y更深,让y往上走
    while(x!=y)//当两个节点不相等时 
        x=p[x],y=p[y];//两个节点深度相同时,同时往上爬树 
    return x;//返回x或y都可以 
}

二叉树的优化点技能+1

using namespace std;

typedef unsigned long long ull;
typedef long long ll;
typedef long double ld;//较高精度的浮点数
typedef pair<int,int> PII;
typedef pair<string,int> PSI;

const int N=1009;

int n,m;
int l[N],r[N],p[N],d[N];
//l[i]表示左儿子、r[i]表示右儿子、p[i]表示父节点、d[i]表示深度

void dfs (int root,int depth)
{//root表示根节点,depth表示目前深度 
    if (root==-1)
        return;//为-1,到底了,返回
    d[root]=depth;//设置当前深度 重新建表确定他们的深度
    dfs(l[root],depth+1);//左儿子深入
    dfs(r[root],depth+1);//右儿子深入 
} 

int LCA (int x,int y)
{//寻找x和y的最近公共祖先 
    while(d[x]>d[y])
        x=p[x];//x更深,让x往上走
    while(d[y]>d[x])
        y=p[y];//y更深,让y往上走
    while(x!=y)//当两个节点不相等时 
        x=p[x],y=p[y];//两个节点深度相同时,同时往上爬树 
    return x;//返回x或y都可以 
}

void input ()
{
    cin>>n>>m;
    for (int i=1;i<=n;++i)
    {
        int leftson,rightson;
        cin>>leftson>>rightson;//输入左右儿子
        l[i]=leftson;
        r[i]=rightson;//设置当前节点的左右儿子
        if (leftson!=-1)
            p[leftson]=i;//如果左儿子不为空,它的父亲设为当前节点
        if (rightson!=-1)
            p[rightson]=i;//如果右儿子不为空,它的父亲设为当前节点 
    }
} 

void query ()
{
    for (int i=1;i<=m;++i)
    {
        int x,y;
        cin>>x>>y;//输入两个询问的节点 
        cout<<d[x]+d[y]-2*d[LCA(x,y)]<<endl;
    }
}

int main ()
{ int T;
    cin>>T;//输入数据组数
    for (int i=1;i<=T;++i)
    {
        input();//输入
        dfs(1,1);//从根节点开始遍历,初始化深度为1或0都没有问题
        query();//问询 
    } 
    return 0;
}

最低票价

特殊数据DP
在一个火车旅行很受欢迎的国度,你提前一年计划了一些火车旅行。
在接下来的一年里,你要旅行的日子将以一个名为 days 的数组给出。
每一项是一个从 1 到 365 的整数。
火车票有三种不同的销售方式:
一张为期一天的通行证售价为 costs[0] 美元;
一张为期七天的通行证售价为 costs[1] 美元;
一张为期三十天的通行证售价为 costs[2] 美元。
通行证允许数天无限制的旅行。
例如,如果我们在第 2 天获得一张为期 7 天的通行证,那么我们可以连着旅行 7 天:第 2 天、第 3 天、第 4 天、第 5 天、第 6 天、第 7 天和第 8 天。
返回你想要完成在给定的列表 days 中列出的每一天的旅行所需要的最低消费。
重点写一下关于日期的操作,DP还是用之前的闫式DP

int get(int i, int cnt)
{
    int day = i;
    while (day >= 1 && days[day] >= days[i] - cnt + 1) day -- ;
    return day;
}

int main()
{
    int n;
    cin >> n;
    for (int i = 1; i <= n; i ++ ) cin >> days[i];
    for (int i = 1; i <= 3; i ++ ) cin >> costs[i];

    for (int i = 1; i <= n; i ++ )
    {
        f[i] = f[i - 1] + costs[1];
        f[i] = min(f[i], f[get(i, 7)] + costs[2]);
        f[i] = min(f[i], f[get(i, 30)] + costs[3]);
    }
    cout << f[n] << endl;

最大连续子序列

同样是DP
题海精华——算法题精选_第1张图片
DP外加双指针

#include
using namespace std;
const int N=1e5+10;
int a[N],f[N];
int main(){
    int n;
    while(cin>>n){
        for(int i=1;i<=n;i++){
            cin>>a[i];
        }
        int sum=-0x3f3f3f3f,l,r;
        for(int i=1,j=1;i<=n;i++){
            f[i]=max(f[i-1],0)+a[i];
            if(f[i-1]<0)j=i;
            if(f[i]>sum)l=j-1,r=i-1,sum=f[i];
        }    
         if(sum<0) puts("0 0 0");
        else cout<<sum<<' '<<l<<' '<<r<<endl;
        
    }
    return 0;
}

二叉树遍历

给前中,求后遍历,题目很简单,主要是在纸上画一下分区间

void dfs (int l1,int r1,int l2,int r2)
{//代表当前状态为前序遍历的[l1,r1]范围,中序遍历的[l2,r2]范围。 
    if (l1>r1)//越界不合法 
        return;

    int root=l2;
    while(in[root]!=pre[l1])
        ++root;
    //找到中序遍历的根

    dfs(l1+1,l1+root-l2,l2,root-1);//左半部分对应的前序、中序遍历 
    dfs(r1-r2+root+1,r1,root+1,r2);//右半部分对应的前序、中序遍历

    cout<<pre[l1];//输出根节点 
} 

放苹果

又是一个DFS全排列+剪枝
把 M 个同样的苹果放在 N 个同样的盘子里,允许有的盘子空着不放,问共有多少种不同的分法?
盘子相对顺序不同,例如 5,1,1 和 1,5,1 算作同一种分法。
于5 1 1 和 1 5 1 是同一种放法,所以我们最好是有顺序的枚举
这里我们从小到大枚举
dfs(i,x) i表示第i个盘子,x表示上一个盘子的苹果
当前苹果从x开始枚举,就可以保证枚举顺序是递增的,当前盘子的苹果数量一定大于等于上一个盘子的苹果数量
我们一共有m个苹果,所以在枚举完前n - 1个盘子后,最后一个盘子的苹果数量就固定了
根据递增的枚举顺序,如果最后一个盘子的苹果数量比上一个盘子的少
那这个方案在之前就一定已经枚举过了 (因为1 1 5 一定比 1 5 1先枚举)

#include 

using namespace std;

int n,m;
int ans,all;

void dfs(int i , int x)
{
    if (i == n)
    {
        if (m - all >= x)   ans ++;
        return ;
    }

    for (int j = x ; j <= m ; j ++)
    {
        all += j;
        dfs(i + 1 , j);
        all -= j;
    }
}

int main()
{
    while(cin >> m >> n)
    {
        ans = all = 0;
        dfs(1,0);
        cout << ans << endl;
    }

    return 0;
}

哈夫曼树

给定 N 个权值作为 N 个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。现在,给定 N 个叶子结点的信息,请你构造哈夫曼树,并输出该树的带权路径长度。永远逃不掉的哈夫曼树,在苹果合并模板题里也出现了,是图论题的钉子户。
可以自己手动写down up delete也可以直接greaterheap

#include
#include
using namespace std;
int main(){

    int n;
    cin>>n;

    priority_queue<int,vector<int>,greater<int> > q;

    for(int i=0;i<n;i++){
        int x;cin>>x;
        q.push(x);
    }
    int res=0;
    while(q.size()>1){
        int t1=q.top();q.pop();
        int t2=q.top();q.pop();
        q.push(t1+t2);
        res+=t1+t2;
    }
    cout<<res<<endl;
    return 0;
}

峰会(模拟)

一共有 N 个首脑参加峰会,编号 1∼N。这些首脑之间存在 M 对两两之间的直接朋友关系。
在划分区域时,我们希望被安排在同一休息区域的首脑们满足,任意两人之间都是直接朋友关系。现在,给定 K 个关于划分休息区域的安排,请你依次判断每个安排是否合理。
细节条件如下
题海精华——算法题精选_第2张图片

#include
using namespace std;
const int N=205;
int n,m,t;
int g[N][N];
int check(){
    int l,a[N];
    cin>>l;
    unordered_set<int>s;
    for(int i=1;i<=l;i++)cin>>a[i],s.insert(a[i]);
    for(int i=1;i<l;i++)
        for(int j=i+1;j<=l;j++)
            if(!g[a[i]][a[j]])
                return -1;
    
    for(int i=1;i<=n;i++){
        if(!s.count(i))
        {
            bool f=true;
            for(auto k:s){
               
                if(!g[i][k])f=false;
            }
            if(f)
                return i;
            
                
        }
 
    }
    return 0;
}
int main(){
    cin>>n>>m;
    while(m--){
        int a,b;
        cin>>a>>b;
        g[a][b]=g[b][a]=1;
    }
    cin>>t;
    for(int i=1;i<=t;i++){
        int ans=check();
        if(~ans){
            if(ans)
                printf("Area %d may invite more people, such as %d.\n",i,ans);
            else
                printf("Area %d is OK.\n",i);
        }else
            printf("Area %d needs help.\n",i);
    }
    return 0;
}

个人认为像是没有先后关系的搭档舞会,也是之前的那道模板题

最长算数

一个算术数组是指至少包含两个整数,且相邻整数之间的差值都相等的整数数组。
例如,[9、10],[3、3、3] 和 [9、7、5、3] 是算术数组,而 [1、3、3、7],[2、1、2],和 [1、2、4] 不是算术数组。
Sarasvati 有一个包含 N 个非负整数的数组,其中的第 i 个整数为 Ai。请帮助她确定最长的连续算术子数组的长度。

本题需要计算最长的连续等差数列的长度
我们可以转化为差分数组 最长连续相等的长度 最后在加1即可
注意差分数组第一项 我们要设置为0 因为我们差分数组的含义在本题是该项与前一项的差
因为第一项没有前一项 我们就不能将其设置为a[1]
差分算法模型

#include
using namespace std;
const int N=2e5+5;

int T,n,a[N],d[N];

int main(){
    cin>>T;
    for(int t=1;t<=T;t++){
        cin>>n;
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            d[i]=a[i]-a[i-1];//记录差分哦
        }
        d[1]=0;//第一项设置为0
        int ans=0,l=1,r=1;
        while(r<=n){//找相邻且相同的最长序列
            if(d[r]!=d[l]){
                ans=max(ans,r-l+1);
                l=r;
            }
            r++;
        }
        ans=max(ans,r-l+1);
        printf("Case #%d: %d\n",t,ans);
    }
    return 0;
}

CCF

正在尝试努力一下CCF认证,看了看之前的历年正题,同时做了当年的考试原题,当初一点也不会,现在回头做出来,发现简单多了,也证明了当时学习有多浮躁。CCF的讲解都烂大街了,直接留下来题目的链接,可以错题重做
题海精华——算法题精选_第3张图片

表达式求值

只能说,这道题考研和刷题或者看书,连着遇到四次了,背过吧

#include
using namespace std;
const int N = 1e5+10;
stack<char> op;
stack<int> num;
void eval(){
    auto b=num.top();num.pop();
    auto a=num.top();num.pop();
    auto c=op.top();op.pop();
    int x;
    if(c=='+')x=a+b;
    else if(c=='-')x=a-b;
    else if(c=='*')x=a*b;
    else x=a/b;
    num.push(x);
}
int main()
{
    string s;cin>>s;
    unordered_map<char,int> p{{'+',1},{'-',1},{'*',2},{'/',2}};
    for(int i=0;i<s.size();i++){
        if(isdigit(s[i])){
            int j=i,x=0;
            while(j<s.size()&&isdigit(s[j])){
                x=x*10+s[j++]-'0';
            }
            num.push(x);
            i=j-1;
        }
        else if (s[i]=='(')op.push(s[i]);
        else if(s[i]==')'){
            while(op.top()!='(')eval();
            op.pop();
        }
        else{
            while(op.size()&&op.top()!='('&&p[op.top()]>=p[s[i]])eval();
            op.push(s[i]);
        }
    }
    while(op.size())eval();
    cout<<num.top()<<endl;
    return 0;
}

重建二叉树

输入一棵树的前序和中序得到重建的二叉树的方式,同样也是上面一道题的解,直接上函数公式

class Solution {
public:
 unordered_map<int,int> pos;
 TreeNode *dfs(vector<int> &pre,vector<int>&in, int a,int b,int x,int y){
     if(a>b)return NULL;
     auto root=new TreeNode(pre[a]);
     int k=pos[root->val];
     root->left=dfs(pre,in,a+1,k+a-x,x,k-1);
     root->right=dfs(pre,in,b-y+k+1,b,k+1,y);
     return root;
 }
    TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
        int n=preorder.size();
        for(int i=0;i<n;i++){
            pos[inorder[i]]=i;
        }
        return dfs(preorder,inorder,0,n-1,0,n-1);
    }
    
};

数组中出现次数超过一半的数字

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。假设数组非空,并且一定存在满足条件的数字。重点看要求
假设要求只能使用O(n)O(n)的时间和额外O(1)O(1)的空间,该怎么做呢?

class Solution {
public:
    int moreThanHalfNum_Solution(vector<int>& nums) {
        int val, cnt = 0;
        for (auto x : nums)
        {
            if (!cnt) val = x, cnt ++ ;     //目标值与其他值刚好配对抵消时,重置计数
            else
            {
                if (x == val) cnt ++ ;
                else cnt -- ;
            }
        } return val;                         //最后剩下的一定是多于半数的目标值
    }
};

Ending

这些题也只是一周左右的一些好玩的题目,也许并不难,但是有一些记忆点。主要是差分,DFS剪枝,模拟,二叉树的操作,其实难点是在CCF里,日常回归吧。

梦里有个女孩在镜湖边,她坠落倒下,涟漪吞没了她,而此时山峦相对。
美的破碎便是悲剧的内核。

你可能感兴趣的:(算法,进阶算法,算法,c++,数据结构)