# Acwing 语法基础课总结

1 篇文章 0 订阅
1 篇文章 0 订阅


声明:本文章由Escoffier原创,未经允许,禁止转载,欢迎友好交流学习

原第二章很基础所以就没有做知识点,而是穿插在其它章节,敬请谅解



第一章 变量,输入输出,表达式与顺序语句


本章节主要介绍了几种常见问题的输入输出方法,整体较为简单,属于入门级别的问题
个人认为比较重要的点:

主函数

  1. 主函数操作:
    要注意主函数的格式:int main(){},不要忘记(),同时输出程序时{}要相互对应

  2. 常量的定义
    在定义常量的时候我们有固定格式const 数据类型 名称=x;可以加快我们写代码的速度

  3. 注意算式里面的浮点情况
    因为不注意浮点情况会导致精度丢失计算错误,更严重的是整型和浮点型或其他类串了导致错误

  4. 注意数据大小
    比如整型当给出 10^8时要敏感,可能在算式中数量级上升导致错误数据生成

  5. 比较大小问题:
    大于> 小于< 等于==(双等号) 不等于!= abs(a):对a取绝对值

  6. 遇到开方问题
    开方的时候需要引入<cmath>sqrt(算式),注意()个数

  7. 输入输出操作:cin>>x;cout<<x<<endl;
    其中cin不能使用endl进行换行,cout可以直接跟endl进行换行操作

  8. 输入输出格式:
    注意常见的有字符的大小写,空格,浮点数的位数保留
    (注:浮点数位数保留可以使用<cstdio.h>使用c语言的输出方式
    还可以使用<iomanip>头文件和fixed<<setprecision(位数)<<x<<endl;来实现)
    iomanip(input output manipulation)可以提供函数处理数据的显示方式,精度,宽度,对齐方式

  9. 字符串的输入
    主要有两种输入方式:getline(cin,str);和cin>>str;
    getline:
    1.读取整行文本,包括空格,如果想在中途换行(如单词),需要手动操作分别输入空格
    2.空格与换行符:空格会被视为单个字符,
    3.完全接受数据,无用的数据也会输入进去
    cin:
    1.可局部接受,挑选数据进行输入
    2.空格与换行符:空格会被视为分隔符,不会视为字符输入

  10. 字符串工具:字符串比较
    670.动物使用了成员函数strcmp(),注意格式,首先引入头文件<cstring>
    int result=strcmp(str1.c_str(),str2.c_str());//将字符串1和字符串2进行内容的比较
    result==0//两个字符串内容完全相同


数组

  1. 数组的写法
    如果不需要用户输入的话直接int a[]={元素};,如果需要用户输入,考虑for循环还是while循环
    for循环:for(int i=0;i<边界;i++){cin>>a[i];}
    while循环:while(cin>>X){a[x]++;}
  2. 数组的类型
    数组有若干类型包括但不限于:int,char,string使用时直接在前面定义 Eg:double array[]
  3. 数组的条件
    其实可以试一试的,主要就是()里面的条件数:初始值,大小关系,递加关系

布尔表达式

  1. 符号表达式:
    包括&&(且),||(或),!(非)

公式

  1. 平均数=(A的权重*A的平均数+B的权重*B的平均数+...)/所有元素的权重之和
  2. max(a,b)=(a+b+abs(a−b))/2
  3. 给出天数:a=N/365; //用式子表示年 b=(N-365*a)/30 ;//用式子表示月 c=N-365*a-30*b;//用式子表示日

第二讲:判断语句


本章节主要介绍了if-else语句的判断和布尔表达式的结合,操作简单但是流程可能会复杂
个人认为比较重要的点:
  1. 运算
    目前已知的有:四则运算(+,-,*,/),求余运算(%)和位与运算(二进制)(&)
    注意:求余计算浮点数时,小数部分会被删除,谨慎使用
  2. 绝对值处理
    这一章涉及的数据较多,计算减法的时候要注意越位,不确定大小关系可以套用abs()取绝对值
  3. 浮点数据控制
    因为我们输入的是十进制小数,在转换为二进制的时候会有精度变化,在计算浮点数乘法时影响会增大
    所以我们使用*1.0来控制精度浮动,详细见 AcWing 666. 三角形类型
  4. 程序优化:
    虽然没有改变复杂度,但是多个if-else语句的嵌套,可以使用if(条件1&&条件2&&条件n)简化
    熟练应用还可以多层(几层看你心情):Eg:if(((条件a&&条件b)&&(条件c&&条件d))||(条件d)){}
  5. 三元表达式:
    格式为:条件1?操作1:操作2,在小选择上经常用到
    Eg:return l1+(l2?l2:0);表示首先判断l2是否存在,存在就加上l2,否则就加0.

公式

  1. 三角形判断
    double compare=(A+B)*1.0; //定义1:两边之和(用1.0控制到小数点后两位)
    double compare2=abs(A-B)*1.0; //定义2:两边之差(用abs控制绝对值)
  2. 一元二次方程求根公式:
    double x1=(-b+sqrt(b*b*1.0-4.0*a*c))/(2*a); //求根公式
    double x2=(-b-sqrt(b*b*1.0-4.0*a*c))/(2*a); //求根公式
    *

第三章:循环结构


本章节主要介绍了几种循环模式(for循环和while循环)基本功要扎实
个人认为比较重要的点:

主函数

  1. while循环的三种形式:
    1.普遍的while循环:while(循环发生的条件){}
    2.无限循环条件控制终止型:while(1){if条件控制break}
    3.布尔表达式控制的终止型:int flag=0 while(flag==0){中间条件符合if条件句改变flag值使得循环终止}
  2. while的条件:
    比如控制输入的;while(cin>>N,N<=0) or while(N<=0){cin>>N}
    第一个表示读取数据,数据小于等于0继续循环.第二个表示一个常量小于0的话就执行程序即输入N
    就功能来说,第一个是用于挑选用户输入的数字大小,第二个是控制输入,而且N没有变化是错误的死循环
  3. for循环的作用:
    1.控制循环次数(输出,遍历,比较等等)
    2.控制输入(特指数组,和一系列数据结构)
  4. 数据的初始化:
    循环除了死循环的另一个大坑,如果是需要比较或者单字符相关需要初始化,加法就不用初始化了
    具体操作就是在循环开始的时候定义这个变量为0
  5. 旗帜法的运用:
    其实就是一个自创的布尔表达式,通过旗帜(颜色)标记所选数据,达到输出但是不更改原始数据的目的
    主要操作:int flag=0; if(条件成立){int flag=1(flag变色)};在此前还可以加循环遍历所有数据
  6. swap的使用:
    十分方便的工具,要用写上就行了,不换数据,直接换名字,一般在排序或其它情况下使用
    当然也可以自己void一个
        int temp=*a;
        *a=*b;
        *b=temp;
    }
    
  7. sqrt优化:
    最常见的优化方式之一,以后遇到此类题:诸如质数、完全数等定义中带有约数、需要整除,
    优化方面皆可朝开平方处思考(实质上就是ab=ba)
    操作:需要引入c语言数学头文件<cmath>然后对数字使用sqrt(x)即可
  8. 数据类型的转化:
    十分方便的工具,要用按照格式写就行了,格式:static_cast<需要转化的数据类型>a;
  9. 字符串的比对:
    一个经典操作用于比较单个字符:首先定义一个单字符用于输入,然后再定义一个单字符用于储存需要比对的单字符
    最后if条件句进行比较即可:
    Eg:char c;char defined1='I';cin>>c;if(c==defined1){*struction*}具体见:AcWing 718. 实验

公式

  1. 斐波那契数列:

            c=a+b; 
            b=a;
            a=c;
    }
    
  2. 素数:
    10000000内的完全数有且仅有6,28,496,8128,33550336这五个
    其它性质:
    1.所有完全数都是三角形数
    2.目前截止发现的所有完全数都以 6或者28结尾
    3.到现在为止,数学家们一共发现了 48个完全数,且 48个完全数全部是偶数
    4.完全数的约数的倒数之和为调和数
    5.完全数可以表示成连续奇数的立方和
    6.完全数可以表示成 2的连续自然数的次幂之和,且这些自然数的数量必定是素数

  3. 曼哈顿距离(转载自A+B大佬的题解):
    在二维空间中 i,j两点的曼哈顿距离可以表示为
    d(i,j)=|xi−xj|+|yi−yj|(横、纵坐标差值的绝对值之和)
    此题目可认为是使得到中心单元的曼哈顿距离满足一定条件即打印星号。

    曼哈顿距离
    对于满足题目条件的任意菱形,总是会出现相似的结构。菱形与此 n∗n方阵中心的曼哈顿距离不超过 n/2
    的所有单元的集合。因此,打印‘*’的判断条件是abs(i−n/2)+abs(j−n/2)<=n/2


第三章 数组


本章节主要介绍了数组的建立,遍历,选择输出,平面以及多维数组,属于必须掌握的知识
个人认为比较重要的点:

主函数

  1. 数组的建立:一般有两种方式
    第一种直接输入:int array[]={1,2,3,4,5};
    第二种:for循环控制输入:int array[大小];for(int i=0;i<大小;i++){cin>>array[i];}
    注意:类型可以有整型,浮点型,字符串型,单字符型

  2. 多维数组的建立:
    一般使用多个for循环嵌套而成的n维数组

  3. 平方矩阵的建立:
    平方矩阵1:(环套环)
    图形一览:
    1 1 1 1 1
    1 2 2 2 1
    1 2 3 2 1
    1 2 2 2 1
    1 1 1 1 1
    首先我们需要确定方法:曼哈顿距离,欧氏距离,切比雪夫距离中的曼哈顿距离(控制轴间距时候使用)
    在曼哈顿距离的题目里面,中心点都是(n/2,n/2)[偶数]和((n-1)/2,(n-1)/2)[奇数]
    中心点已经确定好了,如何从中心开始距离相等的点输出都一致呢?
    答案就是使用绝对值+一个正方形
    在中心表达式(n+1)/2-max(abs(n/2-i),abs(n/2-j))中,绝对值可以很好的控制右方和下方的数据一致
    而选择max值和正方形能够使上下左右的距离相等的点保持对称性,就可以输出正确答案了
    (但是需要注意的是虽然曼哈顿距离相等的点在正常数学图像里组成的是菱形,但是这里是计算机
    在哈莫曼距离介于n~n+1的位置都会被规划为n,从而其实组成的图形是一个空心环)

    平方矩阵2:(规律矩阵) 图形一览:
     1 2 3 4 5
     2 1 2 3 4
     3 2 1 2 3
     4 3 2 1 2
     5 4 3 2 1
    

    当发现不是环套环就应该看行数和列数的关系了:
    我们可以发现行数和列数都是递增1的,同时组成的图形是关于对角线对称的,所以肯定使用abs()
    通过观察发现规律是:abs(i-j)+1

    平方矩阵3:见: 平方矩阵 III
     1 2 4 8
     2 4 8 16
     4 8 16 32
     8 16 32 64
    

    跟平方矩阵2一样,但是倍数变成了2的n次方,
    核心语句:

    cout<<fixed<<setprecision(0)<<exp2(j)<< ' '; //按照格式输出即可 
    
  4. 蛇形矩阵:
    与其它矩阵不同,其它矩阵是按照顺序填充数组,但是蛇形矩阵是使用方向填充(先右再下再左再上,具体看要求)
    首先定义一个100*100的数组,因为第一排输出的时候字符永远都占一位
    所以先定义右侧距离为c-1,竖直距离为r-1,同时定义输出的数字为k

    初始化四个变量left、right、top、bottom,分别表示矩阵的左边界、右边界、上边界和下边界
    

    在每次循环中,首先从左到右填充上边界。使用一个for循环,从left开始,到right结束,同时top<=bottom。 在每次迭代中,将k的值赋给a[top][i],然后k加1。
    填充完上边界后,将top加1。
    接下来,从上到下填充右边界。使用一个for循环,从top开始,到bottom结束,同时left<=right。 在每次迭代中,将k的值赋给a[i][right],然后k加1。
    填充完右边界后,将right减1。
    然后,从右到左填充下边界。使用一个for循环,从right开始,到left结束,同时top<=bottom。 在每次迭代中,将k的值赋给a[bottom][i],然后k加1。
    最后,从下到上填充左边界。使用一个for循环,从bottom开始,到top结束,同时left<=right。 在每次迭代中,将k的值赋给a[i][left],然后k加1。
    填充完左边界后,将left加1。
    当所有边界都被填充后,退出while循环。

    使用两个嵌套的for循环来打印出矩阵的所有元素。
    外层循环遍历行数(r),内层循环遍历列数(c)。
    在内层循环中,将a[i][j]的值输出,并在每个元素之 间添加一个空格。
    在外层循环结束后,输出一个换行符。

  5. 数学cmath中的科学计数法:
    在我们需要对数据使用e的计算的时候我们可以调用<cmath>头文件,使用e的格式如下
    expn(x)(n表示要乘以e的几次方,x表示被乘以e的数字)

公式

  1. 设二维数组为M[n]n:
    划分方式
    数组的上方部分:j>i&&i+j<11
    数组的右上半部分:i<j
    数组的右方区域:i<j&&i+j>=12
    数组的右下半部分:i+j>11
    数组的下方区域:i+j>=12&&i>j
    数组的左下半部分:j<i
    数组的左半部分:i+j<n-1
    数组的左方区域:j<i&&i+j<=10

第四章 字符串


本章节主要介绍了字符串的输入,截取,识别,输出,多个字符串操作,属于必须掌握的知识
个人认为比较重要的点:

主函数

  1. 字符串的输入
    主要有两种输入方式:getline(cin,str);和cin>>str;
    getline:
    1.读取整行文本,包括空格,如果想在中途换行(如单词),需要手动操作分别输入空格
    2.空格与换行符:空格会被视为单个字符,
    3.完全接受数据,无用的数据也会输入进去
    cin:(可以自动删除空格)
    1.可局部接受,挑选数据进行输入
    2.空格与换行符:空格会被视为分隔符,不会视为字符输入
    (因为单词之间是用单个空格分隔的,所以使用cin就可以输入多个单词(字符串))
  2. 字符串工具大全:
    string s1, s2;
    s1.find(s2);// 在 s1 中查找字符串 s2,找到返回 s2 首字母在字符串中的下标,找不到返回 -1
    s1.replace(pos, len, s2);// 把 s1 中从下标 pos 开始的长度为 len 的子串替换为 s2
    s1.erase(it);// 把 s1 字符串中迭代器 it 处的字符删除
    s1.erase(pos, len);// 把 s1 中从下标 pos 开始的长度为 len 的子串删除
  3. 字符串工具:字符串比较
    第一种是直接比较:string a;if(a=="String"){}
    第二种是成员函数比较:670.动物使用了成员函数strcmp(),注意格式,首先引入头文件<cstring>
    int result=strcmp(str1.c_str(),str2.c_str());//将字符串1和字符串2进行内容的比较
    result==0//两个字符串内容完全相同
    **
  4. 字符串工具:字符串的长度
    直接使用即可,int l=str.size();
  5. 字符串中的ASCII码:
    大小:小写字母的ASCII码反而大一些,大写和小写差距为32,大小写a到z的差距是25.见 AcWing 767. 信息加密
    str[i] - '0':将字符转换为整数。这个技巧适用于处理数字字符。
    str[i] - 'a':将字符转换为小写字母的ASCII码值。这个技巧适用于处理小写字母
    str[i] - 'A':将字符转换为大写字母的ASCII码值。这个技巧适用于处理大写字母
    (str[i] - '0') * 10 + (str[j] - '0'):将两个数字字符转换为整数并计算它们的和。适用于处理含数字的字符串
  6. 字符串中的区间:
    分为数字区间和字母区间:(str[i]>='0'&&str[i]<='9')和(str[i]>='a'&&str[i]<='z')和(str[i]>='A'&&str[i]<='Z')
  7. 字符串的插入:
    首先引入头文件:使用成员函数:函数名字.insert(第几位,插入的语句名字)
    Eg: str.insert(sum+1,substr);
  8. 字符串的查找:(见: AcWing 772. 只出现一次的字符 )
    查找字符串元素性质需要进行查找:1.正向查找 2.逆向查找:
    格式就是:strx,find(str[i])
    Eg:if(str1.find(str1[i])==str1.rfind(str1[i]))设置两个查找器一个从前,一个从后.
  9. 字符串的比较:
    我们用成员函数strcmp可比较两个字符串的大小,首先引入头文件: <string>和<cstring>
    Eg:int result=strcmp(a.c_str(),b.c_str());注意:c_str是固定格式
  10. 字符串的提取(重要!取位可以使用for循环递增):
    使用成员函数substr:
    格式:substr=str.substr(0,l);//第一个substr是修改后的函数名字,第二个是成员函数substr,括号内是提取范围
    注意:分割的子字符串可以一起输出: 见AcWing 78. 左旋转字符串
    return str.substr(n,a-n)+str.substr(0,n);是可行的
  11. getline的分段功能:
    Eg:getline(cin,str,',');
    这是getline()函数的调用,其中cin是标准输入流对象,str是存储输入字符串的变量名,逗号是输入结束的标志。
    getline()函数从输入流中读取字符,直到遇到指定的结束标志为止,并将读取到的字符存储在指定的字符串变量中
    在这个例子中,输入的字符串将包含输入流中的所有字符,直到遇到逗号为止。因此,如果输入是"Hello, World",则只有"Hello"将被存储在str变量中。
  12. reverse和sort函数的调用求解后缀问题:
    使用reverse函数,将字符串str[i]反转。使用sort函数按照字母顺序排序,有一样的前缀会被排列在一起)
    Eg:ab abc bcd ae
    reverse函数的格式:reverse(str[i].begin(),str[i].end());
    sort函数的格式(n为字符串的长度):sort(str, str+n);

第六章 函数


本章节主要介绍了函数的建立,调用,封装(较为综合,包括前面所有可能的知识以及后面会出现的指针)
个人认为比较重要的点:

主函数

  1. 构造函数:
    为什么要构造函数呢?方便后面反复调用不用写太多重复的东西,提高代码可读性
    大体分为两种构造函数:1.有返回值的 2.没有返回值的
    格式:有返回值的:
    int x( 这个括号里面是调用的数据 ){//在程序里面可以定义整型变量result}
    return result;//然后就可以在主函数里面cout返回值了
    格式:没有返回值的:
    void x( 这个括号里面是调用的数据 ){//没有返回值,需要输出的话在这里输出吧}
  2. 构造函数的调用:
    格式的话直接:函数名(附加的数据1,附加的数据2,...);就可以调用了.

    1.构造函数数据的调用
    要注意的是调用方法和构造函数时引入的数据:第一个()是构造函数的,第二个()是调用的,构造函数名为x
    输入整型:x(int number) x(number)
    输入字符串:x(string name) x(name)
    输入浮点数:x(double name) x(name)
    输入单字符:x(cahr character) x('A')
    输入布尔值:x(bool value) x(false)
    输入指针:x(name) x(name)//int *ptr(new int[110])和ptr(new int[110])
    输入数组:1.int a[];x(int *a) x(a)
    2.int a[];x(int a[]) x(a)
    输入二维数组:int a[][];x(int a[][N]) x(a)
    输入字符数组:char a[10];x(char a[]) x(a)
    一个新问题:为什么是a[][100],而不是a[][]?
    当你将二维数组传递给函数时,你实际上是传递了一个指向指针的指针。
    所以,在函数参数中,你需要指定[第二维]的大小。这是因为在函数内部,
    这个参数将被解释为一个指向int的指针,而int指向一个大小为 col 的数组。
    如果你不指定第二维的大小,那么编译器将无法确定你正在引用的是哪个维度,因此会产生错误

    使用&:
    用于操作原函数(发生在不直接输出的题目,而是先对原函数操作再输出)
    例如swap: 见AcWing 811. 交换数值
    不使用&,那么你的swap函数将会交换的是x和y的副本,而不是原始的x和y。这意味着,尽管你在函数内部交换了x和y的值,但原始的x和y值不会被改变。
    使用*:
    用于操作数据结构(跟&一样,但是受用者不一样)(包括数组):
    例如: 见812. 打印数字
  3. 构造函数的递归:
    用法较为简单,直接在构造函数的过程中在调用构造函数即可,就可以自行完成递归,但是注意停止条件

公式

  1. 辗转相除法求最大公因数:
    两个数的最大公约数等于其中较小的数和两个数的差的最大公约数
    eg:(12,16)-->(16,4)-->(4,0)-->输出4,具体做法就是:假设a>b(小于就swap)b=a%b,a不变,b=0时候输出
  2. 辗转相乘法求最小公倍数:
    其实也使用了辗转相除法:两个数的最小公倍数等于两个数的最大公约数乘以两个数:
    eg:(12,18)-->最大公约数G=6->最小公倍数=G*a*b-->输出1080
    具体做法就是: a>b(小于就swap)b=a%b,a不变,b=0时候输出G,result=a*b*G;

算法

  1. 插入排序:
    理论:
    首先for循环遍历从l到r的元素.定义一个变量temp储存这个元素,然后用j标记需要比较的元素的位置,
    while循环实现功能(j大于等于l)[还没到达终点],以及a[j]大于tem[没不到小于tem的元素])
    操作(将当前元素往前移动一位,j--继续往前比较,直到比该元素大,先执行后比,现在的位置就是最终位置)
    程序:
    for(int i=l;i<=r;i++){
    int tem=a[i];
    int j=i-1;
    while(j>=l&&a[j]>tem) {
    a[j+1]=a[j];
    j--;
    }
    a[j+1]=tem;
    }
  2. 跳台阶:类似的还有: 见AcWing 822. 走方格
    随机跳台阶跳上去:递归问题
    构造函数就是先给if条件判断大小,然后分情况:相等输出加一,大于return到上一步,小于就递归
    上程序:
    void myFunction(int x){
    if(x==n){
    res++;
    }
    if(x>n){
    return;
    }else{
    myFunction(x+1);
    myFunction(x+2);
    myFunction(x+n);
    }
    }
  3. 深度搜索: 见AcWing 823. 排列(深度搜索)
    其实跟递归一样,只不过因为元素不能重复使用,所以了一个标记(层数)的问题,所以使用深度搜索树
    方法:
    首先定义一个节点数组储存数字,然后定义一个jugde数组用于判断,同时u表示树的层数
    声明myFunction代入数据层数u,递归有一个习惯先写结果再出过程,如果u==n,也就是到达最后一层,输出即可
    过程就是首先for循环控制遍历所有数据元素,然后if条件句判断该元素有无标记
    如果没有标记则放入节点库,同时标记为使用过(true),通过再次调用函数递归进入下一层再换标记为未使用(false)

第七章 结构体,类,指针,引用


本章节主要介绍了结构体,类,指针,引用,是最常见的简化代码的工具
个人认为比较重要的点:

主函数

  1. 类的建立:(将抽象出的数据,代码封装在一起(类声明中的{}),形成类)
    class Solution {
    public:公有成员(类与外部的接口)
    protected:保护型成员(类的继承)
    private:私有成员(辅助作用的数据或函数)
    };
  2. 类和结构体的区别:(见 CSDN-类和结构体 )
    主要区别是:类是C++中对于[C语言中的结构体]的延伸,同时可以将函数作为成员
    同时类的访问级别默认为private,结构体的访问级别默认为public
  3. 类/结构体的调用:
    首先创造一个类,类里面有各种数据类型的元素(结构体基本一致,除了class变成struct)
    eg:include<iostream>
    class Person{//避免后面变量冲突
    public;//访问级别为public
    string name;//元素1是名字
    int age;//元素2是年龄
    void sayHello(){//构造函数输出
    cout<<"hello,my name is"<<name<<endl;
    }
    }
    };
    int main(){
    Person person1 ;
    person1.name="Escoffier";
    person1.age="19";
    person1.sayHello;
    return 0;
    }
    不难发现,构造类和调用类的方式有些许不同,构造直接数据类型加上名字,调用是说明一次,后面就是名字.变量1;的形式
  4. 单链表的创建
    首先需要知道的是链表中每个链节就是一个封装好的节点里面包括指针域和数据域,所以想要创造一个
    链表,就先需要创造出对应个数的节点,再通过指针将它们连接起来形成有顺序的链条
    eg:(创造一个大小为n的链表)
#include<iostream>

using namespace std;

// 定义一个结构体Node,这个结构体代表了链表中的一个节点
struct Node {
    // 存储节点数据,这是一个整数
    int data;
    // 指向下一个节点的指针
    Node* next;
    // 构造函数,用于初始化数据和下一个节点指针
    Node(int data,Node*next=nullptr):data(data),next(next){}
};	
	int main() {
    // 从用户那里接收一个整数n,表示接下来的链表中有n个节点
    int n;
    cin >> n;
    // 创建链表的第一个节点,这个节点的数据是0,并且没有下一个节点
    Node* head = new Node(0);
    // 用于存储用户输入的数据的变量
    int data;
    // 通过循环n次,每次都从用户那里读取一个数据并创建一个新的节点
    for (int i = 0; i < n; i++) {//创造链表,性质的变化需要讨论空集
        cin >> data;
        Node* node = new Node(data);
        // 如果链表为空(即头节点为nullptr),则将新节点设为头节点
        if (head == nullptr) {
            head = node;
        }
        // 如果链表不为空,则遍历链表找到最后一个节点,并将新节点添加到最后一个节点的后面
        else {
            Node* current = head;
            while (current->next != nullptr) {
                current = current->next;
            }
            current->next = node;
        }
    } 
    // 将头节点的下一个节点设为当前节点,这样我们就可以从第二个节点开始遍历整个链表了
    Node* current = head->next;
    // 通过循环遍历链表,并输出每个节点的数据,直到到达链表的末尾(即current为nullptr)
    while (current != nullptr) {//输出数据
        cout << current->data << " "; // 输出当前节点的数据和一个空格
        current = current->next; // 将当前节点设为其下一个节点,以便在下一次循环中继续输出其下一个节点的数据
    }
    cout << endl; 
    // 释放链表中每个节点的内存,以防止内存泄漏。我们通过将头节点设为其下一个节点的方式逐个删除头节点后面的节点。每次删除一个节点后,都会将当前头节点的下一个节点设为下一个待删除的节点。当我们处理完最后一个节点后,头节点就会被自动释放。   
    while (head != nullptr) {//释放空间
        Node* temp = head; // 保存当前头节点的临时指针,以便我们在删除它后依然能够找到下一个待删除的节点。
        head = head->next; // 将头节点设为其下一个节点,这样我们就可以删除当前头节点了。同时我们将在下次循环中继续处理头节点的下一个节点。 
        delete temp;//释放空间
	}
	return 0;
}
  1. 链表的删除:( 见AcWing 28. 在O(1)时间删除链表结点 )
    在给你node的情况下输出的数据是下一个节点的,节点的next指针也要指向再下一个节点
  2. 链表的合并:( 见AcWing 36. 合并两个排序的链表 )
    合并需要引入虚节点(标准英语是dummy当然你也可以使用其它的):
    ListNode *dummy=new ListNode(-1);ListNode *ptr=dummy;
    同时对链表的性质进行操作的时候都需要检查是否为空:(其实while这不标准,应该if,如果一个为空输出另一个)
    while(l1&&l2){//两个链表斗不为空}
    进行比较和换位:
    if(l1->val <= l2->val){
    ptr->next = l1;
    ptr = ptr->next;
    l1 = l1->next;
    } else {
    ptr->next = l2;
    ptr = ptr->next;
    l2 = l2->next;
    }
    特殊情况:有剩余节点接上去就行了
  3. 链表重复元素的删除: 见AcWing 29. 删除链表中重复的节点(新手友好)
    处理原链表复杂,所以引入头节点用于储存目标链节
    思路就是先来一个总驱动while控制p,然后再来一个while用于控制q:因为输出的链节小于等于原链节
    所以p循环在q循环内,也就是q来开道,发现目标值就让p跳过来记录,但是需要考虑p和q相邻的情况
  4. 双链表共同节点的寻找:
    如果两个链表长度不一致,即使是错位,但是指针的都是由起点开始移动,当移动完一个链表,
    双指针还没有相遇,那么走到头的指针再回到起点headx,再移动直到相遇(因为只有交点能相遇)
    所以思路就是:
    1.创建head A和head B用于保存头节点的地址方便跳转
    2.如果没有找到相同的就循环查找
    3.遇到NULL跳到头节点即可

Oth

  1. 输入输出流stringstream( 见AcWing 87. 把字符串转换成整数 )
    注意:单独在主函数使用的时候需要加上头文件#include <sstream>
    先我们定义一个字符串流s用于转换(关键组成),然后定义一个整型变量(不需要大小),用于定义我们需要提取的类型通过s<<str将str输入到s输入输出流中,就可以输出指定类型了

    eg:stringstream s; int c; s<<str; s>>c; return c; }
    stringstream s;:这一行创建了stringstream对象,名为s。
    s<<str;:这一行将一个字符串str写入到流s中。。
    s>>c;:这一行从流s中读取一个整数并存储在变量c中。这可以看作是向流s询问:“你能告诉我一个整数吗?” 流s会检查它是否“知道”一个整数,如果知道,那么流s就会将这个整数发送给询问者
    return c;:这一行将变量c返回给调用者。因为在上述代码中,我们已经从流s中读取了一个整数并存储在变量c中,所以这个返回值就是那个整数。

第八章 STL,位运算,常用库函数


本章节主要介绍了STL工具(包括向量,栈等),还有二进制位的运算基于类的构造,STL是竞赛最常用的需要掌握
个人认为比较重要的点:

主函数

  1. vector的构造和调用:
    下面讲通过程序介绍如何构造vector和调用vector输出:
    #include <iostream>
    #include <vector>
    using namespace std;
    int main(){
    int n;
    cin>>n;
    //创造向量
    vector<int>data(n);
    //输入vector(已知长度)
    for(int i=0;i<n;++i) {
    cin>>data[i];
    }
    或者
    int value;
    cin>>value;
    data.push_back(value);
    //输出vector
    for(int i=0;i<n;++i) {
    cout<<data[i]<<" ";
    }
    cout<<endl;
    return 0;
    }
    //调用vector的数据
    for(int x:nums)(从名为nums的容器里面读取元素并且赋值给x)

  2. vector内一个元素出现的次数:
    return count(nums.begin(),nums.end(),k);//检测k的出现次数

  3. 栈实现队列: 见AcWing 20. 用两个栈实现队列
    首先要知道的是栈的建立和向量如出一辙,但是输入输出有所不同
    首先输入:cin>>x;s.push(x),输出:cout<<s.top()<<" "<<endl;还要s.pop();
    也就是说输出的时候必须pop删除栈顶元素才能输出后面的内容
    第一题:初始化stack:首先定义容器stack和缓存cache,然后声明MyQueue类函数
    第二题:将x元素放在队列最后面:直接stk.push添加一个x数据即可
    第三题:去掉在队列最前面的元素并且输出那个元素:
    首先为了方便定义一个copy函数引入一个stack a和stack b,通过while循环将a最上方的元素移动到stack b
    然后将这个元素从stack a删除,也就是stack a后来者居上,stack b就变成按顺序排列的了,此时stack a最
    底下的元素就会被放置于stack b的最上方.
    然后再主函数中,首先我们运行copy函数,然后定义整型变量res用于储存cach(也就是stack b)最上方的元素
    储存好以后将这个元素从stack b里面删除,再运行copy函数还原原来的stack a(这个时候删掉的不会出现)
    最后返回值就是之前res储存的元素
    第四题:得到最前面的元素(在stack最底下的元素)
    因为之前使用了main函数,这次我们使用peek避免程序错误(当然随便你),然后运行一次我们上面定义的copy函数.还是定义一个整型变量res用于储存最前面的元素,不用删除,得到结果再按照原来的顺序放好,返回res
    第五题:判断stack是否为空
    布尔表达式判断,empty和pop一样是可以直接调用的
    注意:为什么pop(),empty(),top()后面都有括号? 因为这些都是成员函数,不加括号编译器无法识别导致错误

  4. vector的删除: 见AcWing 53. 最小的k个数 这道题目解释了如何使用成员函数erase删除vector容器里面的元素
    格式:v.erase(v.begin()+k,v.end());就可以删除从k位开始到结尾的所有元素

  5. vector数组的输出:
    vector数组的调用就不用for(int x:nums);了,只需要数组长度就可以随便玩了
    不能通过cout来输出而是:return vector<int>{nums[i],nums[j]}(数组名是nums)

  6. vector的递归(dfs): 见AcWing 51. 数字排列
    在声明向量的时候可以在<>之间寻求嵌套形成二维向量便于数据操作和输出
    例如在本题使用深度搜索:基本上就是数据结构加构造函数或者再加一个主函数输出
    在这道题,首先使用向量的嵌套将给path容器和st容器提供参数,同时对输出初处理
    接着构造函数,先if条件句储存insert的第一种情况,然后return使用for循环进行遍历元素
    如果存在没有排序的就先调用当前的数值形成排列并标记,然后判断下一个元素是否与该元素相等(调用dfs进入下一层),如果相等表示下一层和这一层一样,就可以直接从i+1开始了,如果不一样就从0开始遍历重新生成排序,然后将当前元素设置为未选取状态

  7. 位与运算:
    对于一个数字来说,我们不能像数组一样方便地进行比较,同时要使用数字的二进制,于是我们想到了位与运算.&的意义就是位与运算的标识符
    eg:res+= n>>i&1;在这里n>>i&就是将n编译成二进制数,1的意思是看第i位是否是1,因为i是递增的,所以相当于是检测二进制的所有位.换成4就表示看第i位,第i+1位,第i+2位为1的次数,将结果加到result上

  8. 三元组的输入和输出:
    我们可以使用向量的pair嵌套完成,通过用户cin输入然后pushback进向量组tri,最后使用cout进行三元组的输出,有格式限制:
    首先我们重新定义一个嵌套向量组位tri:
    typedef pair<int,pair<double,string>>tri;
    输入:cin>>a>>b>>s; ans,push_back{a,{b,s}}:
    输出:cout<<ans[i].first<<""fixed<<setprecision(2)<<ans[i].second.first<<""<<ans[i].second.second.c_str()<<endl;
    注意:second.first就是第二个大括号里面的第一位,second.second就是第二个大括号里面的第二位


结语:都看到这里了,不点个赞?爆肝8000余字(瘫)数据结构还是需要先通过基础知识的掌握再进行操作,本复习章节是以题目讲述知识点,如果哪里有遗漏或者错误欢迎指正.


向着下一个目标前进吧:

上一篇:AcWing 862. 三元组排序
https://www.acwing.com/solution/content/213297/

下一篇:数据结构与算法(筹备中)

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值