跳转至

C C++

1680 个字 838 行代码 预计阅读时间 19 分钟

  • 以新的语法和示例代码呈现
  • 包括但不限于 acwing 的笔记内容

1 getline() 相关

cin 的几种读取方法和读取逻辑

方法一 ( 常用 )

C++
#include <iostream>
#include <cstring>

using namespace std;

int main()
{
    string a;//string 是 C++ 标准库提供的一个类,代表动态长度的字符串。
    getline(cin, a);
    cout << a.size() << endl;
    return 0;
}
方法二:
C++
#include <iostream>
#include <cstring>

using namespace std;

int main()
{
    char a[105];
    cin.get(a, 105);//需要注意cin.get()不会把换行符取出删除,影响下一次读入!
    cout << strlen(a) << endl;
    return 0;
}
方法三:
C++
#include <iostream>
#include <cstring>

using namespace std;

int main()
{
    char a[105];
    cin.getline(a, 105);//需要注意cin.getline()会把换行符取出删除,不影响下一次读入!
    cout << strlen(a) << endl;
    return 0;
}
  • getline(cin, a);:从标准输入(键盘)读取一行文本,并将其存储到 a 字符串中。getline C++ 标准库中的一个函数,允许读取包含空格的整行文本。cin 表示从键盘输入数据。不存储最后的换行符,且会把换行符从标准输入流中丢弃。且 getline() 不能直接用于 char 数组。

2 for(auto c : a) 相关

C++
#include <iostream>

using namespace std;

int main()
{
    string a;
    getline(cin,a);

    string b;
    for(auto c : a) b = b + c + ' ';
    cout << b << endl;
    b.pop_back();
    return 0;
}
  • for(auto c : a): 遍历字符串 a 中的每一个字符,将每个字符赋值给 c

  • b.pop_back();: 删除最后一个字符

  • for (auto c : a)for (char &c : a)的区别:前者修改 C 不会影响数组 a,后者会影响到数组 a.

  • auto C++ 中的一种类型推断机制,允许编译器根据表达式的类型自动推断变量的类型。使用 auto 可以减少代码中的显式类型声明,增加代码的可维护性和可读性。

3 insert()

C++
#include <iostream>
using namespace std;

int main()
{
    string str,substr;
    while(cin >> str >> substr)
    {
        int index = 0;
        for(int i = 0;i < str.size();i++ )
        {
            if(str[i] > str[index])
            {
                index = i;
            }
        }
        str.insert(index + 1,substr);
        cout << str <<endl;
    }


    return 0;
}

4 find() & rfind ()

  • a.find(b): 返回字符串 a 在字符串 b 中第一次出现的位置。假如 a 不在 b 中,返回string::npos
  • a.rfind(b): 返回字符串 a 在字符串 b 中最后一次出现的位置。假如 a 不在 b 中,返回string::npos

string::npos是一个常量,在 C++ 中是一个无符号整数类型的最大值

C++
#include <iostream>

using namespace std;

int main()
{
    string a;
    while (cin >> a)
    {
        for (auto c : a)
        {
            if(a.find(c) ==a.rfind(c))
            {
                cout << c;
                return 0;
            }
        }
    }
    cout << "no";
    return 0;
}

5 stringstream ssin()

分割字符串形成字符串流

C++
#include <iostream>
#include <sstream>
using namespace std;

int main()
{
    string a,b,c;

    getline(cin,a);
    cin >> b >> c;

    stringstream ssin(a);
    string str;
    while(ssin >> str )
    {
        if(str == b)
        cout << c;
        else cout << str;
        cout << ' ';
    }
    return 0;
}

6 str.back() 相关

  • str.back(): 返回字符串的最后一个字符。
  • str.pop_back(): 删除字符串的最后一个字符。
    C++
    #include <iostream>
    #include <sstream>
    using namespace std;
    int main()
    {
        string str,res;
        while(cin >> str)
        {
            if (str.back() == '.') str.pop_back();
            if (str.size() > res.size())
            {
                res = str;
            }
        }
        cout << res << endl;
    
        return 0;
    }
    

7 swap()

  • swap(a,b): 交换 a b 的值。其中 a,b 可以是任意同类型的变量。 PS:包含在#include <algorithm>中 示例:
    C++
    #include <iostream>
    #include <algorithm>
    using namespace std;
    int main()
    {
        int a = 1, b = 2;
        swap(a, b);
        cout << a << " " << b << endl;
        return 0;
    }
    
    输出结果:
    Text Only
    2 1
    

8 substr()

  • a.substr(b): 返回字符串 a 从第 a 个字符开始,长度为 b 的子串。
    C++
    for(int i = 0;i < a.size();i++)
        {
            a = a.substr(1) + a[0];//a.substr(1)表示从第1个字符开始,长度为a.size()-1的子串,a[0]表示第0个字符
        }
    
  • a.substr(b,c): 返回字符串 a 从第 b 个字符开始,长度为 c 的子串。 实现字符串循环左移
    C++
    #include <iostream>
    
    using namespace std;
    
    int main()
    {
        int n;
        while(cin >> n, n != 0)
        {
            string str[200];
            int index = 0;
            int min = 201;
            for (int i = 0; i < n; ++i) // 修改第 9 行
            {
                cin >> str[i]; // 修改第 11 行
                if(str[i].size() < min)
                {
                    min = str[i].size();
                    index = i;
                }
            }
            int i;
            string res;
            for(i = 1; i <= min; i++)
            {
                string tmp = str[index].substr(str[index].size() - i, i);
                cout << tmp << ' ' << endl;
                int j;
                for(j = 0; j < n; j++) // 修改第 20 行
                {
                    string back = str[j].substr(str[j].size() - i, i);
                    if(back != tmp)
                    {
                        break;
                    }
                }
                if(j == n)
                {
                    res = tmp;
                }
                else
                {
                    break; // 添加提前退出逻辑
                }
            }
            cout << res << endl;
        }
        return 0;
    }
    

9 #include

  • #include<bits/stdc++.h>: 包含 C++ 标准库的所有头文件。常用于竞赛。
    C++
    #include<bits/stdc++.h>
    using namespace std;
    int n;
    string a[209];
    int main(){
        while(cin>>n){
            if(n==0) return 0;
            for(int i=0;i<n;++i) cin>>a[i],reverse(a[i].begin(),a[i].end());
            sort(a,a+n);
            string v="";
            for(int i=0;i<a[0].length();++i){
                if(a[0][i]==a[n-1][i]) v=a[0][i]+v;
                else break;
            }
            cout<<v<<"\n";
        }
    }
    

10 reverse()

  • reverse(a.begin(),a.end()): 反转字符串 a
    C++
    #include <iostream>
    #include <algorithm>
    using namespace std;
    int main()
    {
        string a = "hello";
        reverse(a.begin(),a.end());
        cout << a << endl;
        return 0;
    }
    
    输出结果:
    Text Only
    olleh
    

11 sort()

  • C++ ,sort 函数的用法是 sort(begin,end,cmp),其中 begin 是要排序的范围的起始迭代器,end 是范围的结束迭代器(不包括该位置的元素),cmp 是比较方法,如果不自定义则默认按照字典序从小到大排序。如果想要降序排列一些基本类型可以在 cmp 的位置greater<type>() , 如果需要更复杂的排序方式则需要自定义返回类型为bool型的 cmp 函数。

示例:

C++
#include <iostream>
#include <algorithm>
using namespace std;
struct tmp
{
    int a;
    double b;
    string str;
}arr[10000];
int cmp(const tmp x,const tmp y)
{
    return x.a < y.a;
}
int main()
{
    int n;
    cin >> n;
   for(int i = 0; i < n;i++)
   {
       cin >> arr[i].a >> arr[i].b >> arr[i].str;
   }
   sort(arr,arr + n,cmp);
   for(int i = 0;i < n;i++)
   {
       printf("%d %.2f %s\n", arr[i].a, arr[i].b, arr[i].str.c_str());
   }

    return 0;
}
  • c_str(): C++ std::string 类的一个成员函数,用于返回一个指向字符串内容的常量字符指针(const char*。这个指针指向一个以 null 结尾的 C 风格字符串,通常用于与 C 风格的字符串函数进行交互。

12 unordered_set

  • unordered_set<int> a: 定义一个无序集合 a,其中元素类型为 int
  • a.insert(b): b 插入到 a 中。
  • a.count(b): 返回 b a 中的个数。
  • a.find(b): 返回 b a 中的迭代器。
  • a.erase(b): 删除 a 中的 b
C++
int get_unique_count(int a[], int n)
 {
    unordered_set<int> unique_nums;
    for (int i = 0; i < n; i++) unique_nums.insert(a[i]);//遍历数组,将每个元素插入集合中。集合会自动去重。
    return unique_nums.size();
}

13 结构体内部的构造函数

C++
struct Node
{
    int a,b;
    Node(int _a,int _b):a(_a),b(_b){}//把_a赋值给a,把_b赋值给b
};

14 链表

  • new:new C++ 中用于动态内存分配的关键字。它允许你在堆上分配内存,并返回指向该内存的指针。 Eg:

    C++
     int* p = new int; // 在堆上分配一个整数
      *p = 10;          // 给这个整数赋值
    
    
    int* arr = new int[5]; // 在堆上分配一个整数数组,大小为5
    for (int i = 0; i < 5; ++i) {
        arr[i] = i;        // 初始化数组
    } 
    
    C++
    #include <iostream>
    using namespace std;
    
    // 定义链表节点结构体
    struct Node
    {
        int val;        // 节点存储的值
        Node* next;     // 指向下一个节点的指针
    
        // 构造函数,初始化节点
        Node(int _val) : val(_val), next(NULL) {}
    };
    
    int main()
    {
        // 创建三个节点
        auto p = new Node(1);  // 创建值为1的节点
        auto q = new Node(2);  // 创建值为2的节点
        auto o = new Node(3);  // 创建值为3的节点
    
        // 连接节点,构建链表
        p->next = q;    // 将节点1指向节点2
        q->next = o;    // 将节点2指向节点3
        Node* head = p;//初始化链表的头节点
    
        //链表的遍历
        for(Node* i = head;i != NULL; i = -> next)
        {
            cout << i->val <<endl;
        }
        return 0;
    }
    
  • 最终的链表结构

    Text Only
    p        q        o
    [1] ---> [2] ---> [3] ---> NULL
    
  • 在链表中添加节点( 一般添加在最前面 )

    C++
    Node* u = new Node(4);
    u->next = head;
    head = u;
    
  • 节点的删除:只要遍历不到需要删除的点就可以,至于有没有真正删除并不重要。

    C++
    head->next = head->next->next;相当于跳过了某一个节点
    
  • 链表归并:

输入两个递增排序的链表,合并这两个链表并使新链表中的结点仍然是按照递增排序的。

二路归并:

C++
/**
* Definition for singly-linked list.
* struct ListNode {
*     int val;
*     ListNode *next;
*     ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
    ListNode* merge(ListNode* l1, ListNode* l2) {
        auto dummy = new ListNode(-1),tail = dummy;//初始化一个虚拟节点
        while(l1&&l2)
        {
            if(l1->val < l2->val)
            {
                tail->next = l1;
                l1 = l1->next;
            }
            else
            {
            tail->next = l2;
            l2 = l2->next;
            }
            tail = tail->next;
        }
        if(l1 == NULL) tail->next = l2;
        if(l2 == NULL) tail->next = l1;
        return dummy->next;
    }
};
  • 链表反转:

定义一个函数,输入一个链表的头结点,反转该链表并输出反转后链表的头结点。
样例:

输入 :1->2->3->4->5->NULL
输出:5->4->3->2->1->NULL

1

C++
/**
* Definition for singly-linked list.
* struct ListNode {
*     int val;
*     ListNode *next;
*     ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if(!head || !head->next) return head;
    auto p = head, q = head->next;
    while (q)
    {
        auto o = q -> next;
        q->next = p;
        p = q;
        q = o;
    }
    head->next = NULL;
    return p;

    }
};
2(递归
C++
/**
* Definition for singly-linked list.
* struct ListNode {
*     int val;
*     ListNode *next;
*     ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if(!head || !head->next) return head;
        auto tail = reverseList(head->next);
        head->next->next = head;
        head->next = NULL;
        return tail;

    }
};      
  • 找公共节点:

找两个链表的公共节点

C++
/**
* Definition for singly-linked list.
* struct ListNode {
*     int val;
*     ListNode *next;
*     ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
    ListNode *findFirstCommonNode(ListNode *headA, ListNode *headB) {
        auto p = headA;
        auto q = headB;
        while(p != q)
        {
            if(p) p = p->next;
            else p = headB;
            if(q) q = q ->next;
            else q = headA;
        }
        return p;

    }
};
  • 删除链表中重复的节点 :

删除链表中的重复节点

样例 1
输入:1->2->3->3->4->4->5
输出:1->2->5

样例 2
输入:1->1->1->2->3
输出:2->3

C++
/**
* Definition for singly-linked list.
* struct ListNode {
*     int val;
*     ListNode *next;
*     ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
    ListNode* deleteDuplication(ListNode* head) {
        auto dummy = new ListNode(-1);
        dummy->next = head;
        auto p = dummy;
        while (p->next) {
        auto q = p->next;
        while(q->next && p->next->val == q->next->val) q = q->next;
        if(q == p->next) p = p->next;
        else p->next = q->next;
        }

      return dummy->next;
    }
};

15 & 对象

示例:

C++
#include <iostream>

using namespace std;

class Box
{
   public:
      double length;   // 长度
      double breadth;  // 宽度
      double height;   // 高度
      // 成员函数声明
      double get(void);
      void set( double len, double bre, double hei );
};
// 成员函数定义
double Box::get(void)
{
    return length * breadth * height;
}

void Box::set( double len, double bre, double hei)
{
    length = len;
    breadth = bre;
    height = hei;
}
int main( )
{
   Box Box1;        // 声明 Box1,类型为 Box
   Box Box2;        // 声明 Box2,类型为 Box
   Box Box3;        // 声明 Box3,类型为 Box
   double volume = 0.0;     // 用于存储体积

   // box 1 详述
   Box1.height = 5.0; 
   Box1.length = 6.0; 
   Box1.breadth = 7.0;

   // box 2 详述
   Box2.height = 10.0;
   Box2.length = 12.0;
   Box2.breadth = 13.0;

   // box 1 的体积
   volume = Box1.height * Box1.length * Box1.breadth;
   cout << "Box1 的体积:" << volume <<endl;

   // box 2 的体积
   volume = Box2.height * Box2.length * Box2.breadth;
   cout << "Box2 的体积:" << volume <<endl;


   // box 3 详述
   Box3.set(16.0, 8.0, 12.0); 
   volume = Box3.get(); 
   cout << "Box3 的体积:" << volume <<endl;
   return 0;
}

16 STL

multiset 库中一个非常有用的类型,它可以看成一个序列,插入一个数,删除一个数都能够在 O(logn) 的时间内完成,而且他能时刻保证序列中的数是有序的,而且序列中可以存在重复的数。

C++
class Solution {
public:
    int getNumberOfK(vector<int>& nums , int k) {
        multiset<int> s;

        for(int x : nums) s.insert(x);

        return s.count(k);
    }
};

C++
#include <string>
#include <iostream>
#include <set>
using namespace std;
void main(){
    intx;
    scanf("%ld",&x);
    multiset<int>h;          //建立一个multiset类型,变量名是h,h序列里面存的是int类型,初始h为空
    while(x!=0){
        h.insert(x);         //将x插入h中
        scanf("%ld",&x);
    }    
    while(!h.empty()){       // 序列非空 h.empty()==true时 表示h已经空了
        __typeof(h.begin()) c=h.begin();
                            //c指向h序列中第一个元素的地址,第一个元素是最小的元素
        printf("%ld ",*c);   //将地址c存的数据输出
        h.erase(c);          //从h序列中将c指向的元素删除
    }
}
更多multiset相关

17 重载函数和重载运算符

  • 重载函数 :在同一个作用域内,可以声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数、类型或者顺序)必须不同。您不能仅通过返回类型的不同来重载函数。下面的实例中,同名函数 print () 被用于输出不同的数据类型:
C++
#include <iostream>
using namespace std;

class printData
{
   public:
      void print (int i) {
        cout << "整数为: " << i << endl;
      }

      void print (double  f) {
        cout << "浮点数为: " << f << endl;
      }

      void print (char c[]) {
        cout << "字符串为: " << c << endl;
      }
};

int main (void)
{
   printData pd;

   // 输出整数
   pd.print (5);
   // 输出浮点数
   pd.print (500.263);
   // 输出字符串
   char c[] = "Hello C++";
   pd.print (c);

   return 0;
}
  • 关于重载函数的更详细内容可以看这篇文章
  • 重载运算符
    重载运算符的基本语法: operator 待重载运算符 (参数){函数体}

示例:

C++
#include <iostream>
using namespace std;

class Box
{
   public:

      double getVolume (void)
      {
         return length * breadth * height;
      }
      void setLength ( double len )
      {
          length = len;
      }

      void setBreadth ( double bre )
      {
          breadth = bre;
      }

      void setHeight ( double hei )
      {
          height = hei;
      }
      // 重载 + 运算符,用于把两个 Box 对象相加
      Box operator+(const Box& b)
      {
         Box box;
         box. length = this->length + b.length;
         box. breadth = this->breadth + b.breadth;
         box. height = this->height + b.height;
         return box;
      }
   private:
      double length;      // 长度
      double breadth;     // 宽度
      double height;      // 高度
};
// 程序的主函数
int main ( )
{
   Box Box1;                // 声明 Box1,类型为 Box
   Box Box2;                // 声明 Box2,类型为 Box
   Box Box3;                // 声明 Box3,类型为 Box
   double volume = 0.0;     // 把体积存储在该变量中

   // Box1 详述
   Box1.setLength (6.0); 
   Box1.setBreadth (7.0); 
   Box1.setHeight (5.0);

   // Box2 详述
   Box2.setLength (12.0); 
   Box2.setBreadth (13.0); 
   Box2.setHeight (10.0);

   // Box1 的体积
   volume = Box1.getVolume ();
   cout << "Volume of Box1 : " << volume <<endl;

   // Box2 的体积
   volume = Box2.getVolume ();
   cout << "Volume of Box2 : " << volume <<endl;

   // 把两个对象相加,得到 Box3
   Box3 = Box1 + Box2;

   // Box3 的体积
   volume = Box3.getVolume ();
   cout << "Volume of Box3 : " << volume <<endl;

   return 0;
}
  • 友元重载和类内成员重载的两个示例:
    • 友元重载是为了让函数能访问类内的 private protected 成员,如果类内全都是 public 成员,友元重载就没有必要
C++
#include <iostream>
#include <string>

using namespace std;
class MM
{
public:
    MM() {}
    MM(string NAME, int AGE) : name(NAME), age(AGE) {}
    void print()
    {
        cout << name << ":" << age << endl;
    }
    void printdata()
    {
        MM::print(); // 直接通过类名调用静态成员函数
        this->print(); // 通过对象调用成员函数
    }
    string name;
    int age;
    friend MM operator+(MM object1, MM object2);// 友元重载
    MM operator-(MM object1)// 类的成员函数方式重载;
    {
        return MM(this->name, this->age - object1.age);
    }
    //重载函数的返回值类型:重载函数的返回值,是由重载的运算符表达式的最终结果决定。

};
MM operator+(MM object1, MM object2)
{
    return MM(object1.name + object2.name, object1.age + object2.age);
}
int main()
{
    MM girl2("小刚", 20);
    MM girl1("小美", 18);
    MM object1; //一定要有一个无参的构造函数
    object1 = girl1 + girl2;//需要重载运算符
    MM object2 = operator+(girl1, girl2);//运算符重载的实质是函数调用
    MM object3 = girl2 - girl1;
    MM object4 = girl2.operator-(girl1);
    object1.print();
    object2.print();
    object3.print();
    object4.print();


}

返回结果:

Text Only
小美:38
小美小刚:38
小刚:2
小刚:2

C++
#include <iostream>

class Point {
public:
    Point(int x = 0, int y = 0) : x(x), y(y) {}

    // 重载 << 运算符,用于输出 Point 对象
    friend std::ostream& operator<<(std::ostream& out, const Point& point) {
        out << "(" << point.x << ", " << point.y << ")";
        return out;
    }

    // 重载 >> 运算符,用于输入 Point 对象
    friend std::istream& operator>>(std::istream& in, Point& point) {
        std::cout << "Enter x: ";
        in >> point.x;
        std::cout << "Enter y: ";
        in >> point.y;
        return in;
    }

private:
    int x, y;
};

int main() {
    Point p1;
    std::cout << "Input a point:" << std::endl;
    std::cin >> p1;  // 使用重载的 >> 运算符输入点的坐标

    std::cout << "You entered: " << p1 << std::endl;  // 使用重载的 << 运算符输出点的坐标

    return 0;
}
输入:
Text Only
Enter x: 1
Enter y: 2
输出:
Text Only
You entered: (1, 2)

特殊重载

  • 前置 ++ 和后置 ++

    C++
    #include <iostream>
    using namespace std;
    class Num
    {
    public:
        Num(int iNum = 0) : iNum(iNum) {}
        void print()
        {
            cout << iNum << endl;
        }
        Num operator++(int)//后置的++和--要用int标识
        {
            return(this->iNum++);
        }
        Num operator++()
        {
            return(++this->iNum);
        }
    protected:
        int iNum;//限制在类内访问;
    };
    int main()
    {
        Num a(1);
        Num b = a++;
        Num c = ++a;
        b.print();
        a.print();
        c.print();
        return 0;
    }
    
  • 流运算符重载

    • 流对象:ostream: 输出流, cout 就是 ostream 类的对象,istream: 输入流 cin 就是 istream 类的对象
    • 流重载,必须采用的引用的方式,必须采用友元的方式。

示例:

C++
#include <iostream>
#include <string>

using namespace std;
class MM
{
public:
    MM(string name = "", int age = 0) :name(name), age(age) {}  
    friend ostream& operator<<(ostream& , MM&);
    friend istream& operator>>(istream& , MM&);
protected:
    string name;
    int age;
};


class girl // 这种方法避免了使用友元函数,但是实现程序和友元函数差不多
{
public:
    girl(string name = "", int age = 0) :name(name), age(age) {}
    void output(ostream& out)
    {
        out << this->name << ":" << this->age;
    }
    void input(istream& in)
    {
        in >> this->name >> this->age;
    }
protected:
    string name;
    int age;
};
ostream& operator<<(ostream& out, MM& mm)
{
    out << mm.name << '\t' << mm.age;
    return out;
}
istream& operator>>(istream& in, MM& mm)
{
    in >> mm.name >> mm.age;
    return in;
}
ostream& operator<<(ostream& out, girl& girl)
{
    girl.output(out);
    return out;
}
istream& operator>>(istream& in, girl& girl)
{
    girl.input(in);
    return in;
}
int main()
{
    MM mm;
    cin >> mm; //cin:istream
    cout << mm;//cout:ostream
    girl girl;
    cin >> girl;
    cout << girl;
    return 0;
}
  • 后缀重载和文本重载
  • PS:后缀重载和文本重载的返回值类型必须为 unsigned long longconst char*
  • PS:后缀重载和文本重载的参数类型必须为 unsigned long longconst char*

示例:

C++
#include <iostream>

using namespace std;
unsigned long long operator""_h(unsigned long long num)
{
    return num * 60 * 60;
}
int main()
{
    int num = 1_h;
    cout << num;
    return 0;
}
  • 隐式转换:
    • C++ 中,隐式转换是指在表达式中,编译器自动将一种数据类型转换为另一种数据类型,而不需要显式地进行类型转换。
    • 格式:operator 目标类型() {return 目标类型数据;} 示例:
      C++
      #include <iostream>
      
      using namespace std;
      class MM
      {
      public:
          MM(string name = "", int age = 0) : name(name), age(age) {};
          operator int()
          {
              return this->age;
          }
      private:
          int age;
          string name;
      };
      int main()
      {
          MM girl("小美", 18);
          int sum = girl;
          cout << sum;
          return 0;
      }
      
      输出结果:
      Text Only
      18