C++ ACM模式的讲解

本文从C++输入输出的具体函数,再到几乎覆盖全部情况的ACM模式写法,最后介绍链表和二叉树的定义和输入输出。

C++常用的输入输出方法

C++的输入输出有很多种方式,既有继承自C语言的,也有其自己独特的。只介绍几个在笔试面试中经常被用到的,足够满足笔试的使用。

输入

首先,在C++语言中,要使用标准的输入,需要包含头文件

cin

cin是C++中, 标准的输入流对象,下面列出cin的两个用法,单独读入,和批量读入

cin的原理,简单来讲,是有一个缓冲区,我们键盘输入的数据,会先存到缓冲区中,用cin可以从缓冲区中读取数据。

注意1:cin可以连续从键盘读入数据
注意2:cin以空格、tab、换行符作为分隔符
注意3:cin从第一个非空格字符开始读取,直到遇到分隔符结束读取

示例:

// 用法1,读入单数据
int num;
cin >> num;
cout << num << endl;  // 输出读入的整数num

// 用法2,批量读入多个数据
vector<int> nums(5);
for(int i = 0; i < nums.size(); i++) {
	cin >> nums[i];
}
// 输出读入的数组
for(int i = 0; i < nums.size(); i++) {
	cout << nums[i] << " ";
}

getline()

从cin的注意中,也可以看出,当我们要求读取的字符串中间存在空格的时候,cin会读取不全整个字符串,这个时候,可以采用getline()函数来解决。

注意1:使用getline()函数的时候,需要包含头文件<string>
注意2:getline()函数会读取一行,读取的字符串包括空格,遇到换行符结束

string s;
getline(cin, s);
// 输出读入的字符串
cout << s << endl;

getchar()

该函数会从缓存区中读出一个字符,经常被用于判断是否换行

char ch;
ch = getchar();
// 输出读入的字符
cout << ch << endl;

输出

同样的,在C++语言中,要使用标准的输出,也需要包含头文件<iostream>

输出这边,主要介绍一个函数,就是用的最多的cout,需要注意的是,如果输出endl对象的时候,会输出一个换行符,类似\n

示例:

string s = "hello, Irray~";
// 看看二者有何不同
cout << "hello, Irray~";
cout << s << endl;

当然,C++中的输入输出函数不止这几个,其他的输入函数包括scanf()cin.get()等等方式,输出函数也有printf(),clog,cerr等方式,要根据具体的使用场景,选择具体的输入输出函数。

但,接下来的案例中,掌握上述三个方法是足够的。不想介绍太多,也是因为,记忆太多方法,容易记混,不如用最简洁的方式实现全部问题。

案例

(1)一维数组

此类输入,每个元素为一个int或者char,有两类常见的案例:

1.固定数目
输入格式:

1 2 3

or

3 1 2 3

解析:
对于第一组,第一行的3为整数的个数,第二行为三个用空格隔开的整数,因此可以采用cin来进行读取

对于第二组,第一行的3为整数的个数,空格后面的数据为三个用空格隔开的整数,因此可以采用cin来进行读取

此类问题,可以先创建一个vector,大小设置为给定值,然后通过for循环来循环输入

答案:

int n;
cin >> n; // 读入3,说明数组的大小是3
vector<int> nums(n); // 创建大小为3的vector<int>
for(int i = 0; i < n; i++) {
    cin >> nums[i];
}
 
// 验证是否读入成功
for(int i = 0; i < nums.size(); i++) {
    cout << nums[i] << " ";
}
cout << endl;

2.不固定数目
输入格式:

1 2 3 4

解析:
输入的数据为四个用空格间隔的整数,没有指定整数个数,因此可以用while循环结合cin来处理该问题。

答案:

vector<int> nums;
int num;
while(cin >> num) {
    nums.push_back(num);
    // 读到换行符,终止循环
    if(getchar() == '\n') {
        break;
    }
}
 
// 验证是否读入成功
for(int i = 0; i < nums.size(); i++) {
    cout << nums[i] << " ";
}
cout << endl;

(2)二维数组

除了一维数组这种最基础的输入外,还会考察二维数组的输入,尤其是在dfs、dp类型的题目中。

二维数组主要有两种方式:

1.常规模式
输入格式:

2 3
1 2 3
1 2 3

解析:
第一行的2,代表数据为2行,3代表数据为3列,因此根据第一行,可以得出,所输入数据为2行3列的二维数组。接下来的6个数字,就是按照空格和换行符分隔开的2x3二维数组,因此用for循环和cin即可处理

答案:

int m; // 接收行数
int n; // 接收列数
 
cin >> m >> n;
 
vector<vector<int>> matrix(m, vector<int>(n));
 
for(int i = 0; i < m; i++) {
    for(int j = 0; j < n; j++) {
        cin >> matrix[i][j];
    }
}
 
// 验证是否读入成功
for(int i = 0; i < m; i++) {
    for(int j = 0; j < n; j++) {
        cout << matrix[i][j] << " ";
    }
    cout << endl;
}

2.每一行数据是逗号隔开的整数
输入格式:
2 3
1,2,3
1,2,3
解析:
第一行的2,代表数据为2行,3代表数据为3列,因此根据第一行,可以得出,所输入数据为2行3列的二维数组。接下来的2行,分别是一个字符串,字符串中用逗号隔开每个整数。这里采用读入字符串的方式,并将读入的字符串进行按逗号分开。

答案:

int m; // 接收行数
int n; // 接收列数
 
cin >> m >> n;
getchar(); // 用于忽略换行符
 
vector<vector<int>> matrix(m);
 
for(int i = 0; i < m; i++) {
    // 读入字符串
    string s;
    getline(cin, s);
     
    // 将读入的字符串按照逗号分隔为vector<int>
    vector<int> vec;
    int p = 0;
    for(int q = 0; q < s.size(); q++) {
        p = q;
        while(s[p] != ',' && p < s.size()) {
            p++;
        }
        string tmp = s.substr(q, p - q);
        vec.push_back(stoi(tmp));
        q = p;
    }
     
    //写入matrix
    matrix[i] = vec;
    vec.clear();
}
 
// 验证是否读入成功
for(int i = 0; i < matrix.size(); i++) {
    for(int j = 0; j < matrix[i].size(); j++) {
        cout << matrix[i][j] << " ";
    }
    cout << endl;
}

(3)字符串

1.单字符串
输入格式:
abc
解析:
用cin读入即可

答案:

string s;
cin >> s;
// 验证是否读入成功
cout << s << endl;

2.给定数目多字符串
输入格式:

3 abc ab a

解析:
第一行的3,代表有3个字符串,后续为用空格隔开的3个字符串,采用for循环和cin读入即可

答案:

int n;
cin >> n; // 读入3,说明字符串数组的大小是3
vector<string> strings(n); // 创建大小为3的vector<string>
for(int i = 0; i < n; i++) {
    cin >> strings[i];
}
 
// 验证是否读入成功
for(int i = 0; i < strings.size(); i++) {
    cout << strings[i] << " ";
}
cout << endl;

3.不给定数目多字符串
输入格式:

abc ab a d

解析:
输入为用空格隔开的若干个字符串。

答案:

vector<string> strings;
string str;
while(cin >> str) {
    strings.push_back(str);
    // 读到换行符,终止循环
    if(getchar() == '\n') {
        break;
    }
}
 
// 验证是否读入成功
for(int i = 0; i < strings.size(); i++) {
    cout << strings[i] << " ";
}
cout << endl;

4.字符串转整数数组
输入格式:

11,22,3,4

解析:
输入为一个完整字符串,字符串内容是按照逗号隔开的一个数组,可以先读入完成字符串,然后根据逗号进行分隔

答案:

vector<int> vec;
 
// 读入字符串
string s;
getline(cin, s);
 
// 将读入的字符串按照逗号分隔为vector<int>
    int p = 0;
    for(int q = 0; q < s.size(); q++) {
        p = q;
        while(s[p] != ',' && p < s.size()) {
            p++;
        }
        string tmp = s.substr(q, p - q);
        vec.push_back(stoi(tmp));
        q = p;
    }
 
// 验证是否读入成功
for(int i = 0; i < vec.size(); i++) {
    cout << vec[i] << " ";
}
cout << endl;

ACM模式练习平台

除了在笔试中实战之外,也可以在牛客平台进行练习:https://ac.nowcoder.com/acm/contest/5652

同时,牛客上也有很多专门的ACM模式算法题。

这里给出一个案例:

解答:

因为比较简单,就直接给出代码了。

#include <iostream>
using namespace std;
 
int main() {
    int a;
    int b;
    while(cin >> a >> b) {
        cout << a + b << endl;
    }
     
    return 0;
}

常见数据结构定义

在ACM模式中,链表、二叉树这些数据结构的定义也需要自己去定义,接下来就给出二者的定义、输入和输出。

这里就直接给出代码了,想必大伙对数据结构都是了如指掌的。

1.链表

#include <iostream>
using namespace std;
 
// 链表定义,并给出两个有参构造函数
struct ListNode
{
    int val;
    ListNode* next;
    ListNode(int _val):val(_val),next(nullptr){}
    ListNode(int _val,ListNode* _next):val(_val),next(_next){}
};
 
int main()
{
 
    // 根据控制台的输入,创建一条单链表
    ListNode* LHead = new ListNode(-1);
    ListNode* pre = LHead;
    ListNode* cur = nullptr;
     
    int num;
    while(cin >> num)
    {
        // 为了简单起见,设置为-1退出,后续可优化,这里只是给出一个例子
        if(num == -1) break;
        cur = new ListNode(num);
        pre->next = cur;
        pre = cur;
    }
     
    cur = LHead->next;
     
    // 输出单链表的value
    while(cur)
    {
        cout << cur->val << " ";
        cur = cur->next;
    }
     
    cout << endl;
     
    return 0;
}

2.二叉树

#include <iostream>
#include <vector>
#include <queue>
 
using namespace std;
 
//定义树节点
struct TreeNode
{
    int val;
    TreeNode* left;
    TreeNode* right;
    TreeNode():val(0),left(nullptr),right(nullptr){}
    TreeNode(int _val):val(_val),left(nullptr),right(nullptr){}
    TreeNode(int _val,TreeNode* _left,TreeNode* _right):val(0),left(_left),right(_right){}
};
 
//根据数组生成树
TreeNode* buildTree(const vector<int>& v)
{
    vector<TreeNode*> vTree(v.size(),nullptr);
    TreeNode* root = nullptr;
    for(int i = 0; i < v.size(); i++)
    {
        TreeNode* node = nullptr;
        if(v[i] != -1)
        {
            node = new TreeNode(v[i]);
        }
        vTree[i] = node;
    }
    root = vTree[0];
    for(int i = 0; 2 * i + 2 < v.size(); i++)
    {
        if(vTree[i] != nullptr)
        {
            vTree[i]->left = vTree[2 * i + 1];
            vTree[i]->right = vTree[2 * i + 2];
        }
    }
    return root;
}
 
//根据二叉树根节点层序遍历并打印
void printBinaryTree(TreeNode* root)
{
    if(root == nullptr) return;
    vector<vector<int>> ans;
    queue<TreeNode*> q;
    q.push(root);
    while(!q.empty())
    {
        int size = q.size();
        vector<int> path;
        for(int i = 0;i<size;i++)
        {
            TreeNode* node = q.front();
            q.pop();
            if(node == nullptr)
            {
                path.push_back(-1);
            }
            else
            {
                path.push_back(node->val);
                q.push(node->left);
                q.push(node->right);
            }
        }
        ans.push_back(path);
    }
     
    for(int i = 0;i<ans.size();i++)
    {
        for(int j = 0;j<ans[i].size();j++)
        {
            cout << ans[i][j] << " ";
        }
        cout << endl;
    }
    return;
}
 
int main()
{
    // 验证
    vector<int> v = {4,1,6,0,2,5,7,-1,-1,-1,3,-1,-1,-1,8};
    TreeNode* root = buildTree(v);
    printBinaryTree(root);
     
    return 0;
}
  • 7
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1 图论 3 1.1 术语 3 1.2 独立集、覆盖集、支配集之间关系 3 1.3 DFS 4 1.3.1 割顶 6 1.3.2 桥 7 1.3.3 强连通分量 7 1.4 最小点基 7 1.5 拓扑排序 7 1.6 欧拉路 8 1.7 哈密顿路(正确?) 9 1.8 Bellman-ford 9 1.9 差分约束系统(用bellman-ford解) 10 1.10 dag最短路径 10 1.11 二分图匹配 11 1.11.1 匈牙利算法 11 1.11.2 KM算法 12 1.12 网络流 15 1.12.1 最大流 15 1.12.2 上下界的网络的最大流 17 1.12.3 上下界的网络的最小流 17 1.12.4 最小费用最大流 18 1.12.5 上下界的网络的最小费用最小流 21 2 数论 21 2.1 最大公约数gcd 21 2.2 最小公倍数lcm 22 2.3 快速幂取模B^LmodP(O(logb)) 22 2.4 Fermat小定理 22 2.5 Rabin-Miller伪素数测试 22 2.6 Pollard-rho 22 2.7 扩展欧几里德算法extended-gcd 24 2.8 欧拉定理 24 2.9 线性同余方程ax≡b(mod n) 24 2.10 中国剩余定理 25 2.11 Discrete Logging(BL == N (mod P)) 26 2.12 N!最后一个不为0的数字 27 2.13 2^14以内的素数 27 3 数据结构 31 3.1 堆(最小堆) 31 3.1.1 删除最小值元素: 31 3.1.2 插入元素和向上调整: 32 3.1.3 堆的建立 32 3.2 并查集 32 3.3 树状数组 33 3.3.1 LOWBIT 33 3.3.2 修改a[p] 33 3.3.3 前缀和A[1]+…+A[p] 34 3.3.4 一个二维树状数组的程序 34 3.4 线段树 35 3.5 字符串 38 3.5.1 字符串哈希 38 3.5.2 KMP算法 40 4 计算几何 41 4.1 直线交点 41 4.2 判断线段相交 41 4.3 三点外接圆圆心 42 4.4 判断点在多边形内 43 4.5 两圆交面积 43 4.6 最小包围圆 44 4.7 经纬度坐标 46 4.8 凸包 46 5 Problem 48 5.1 RMQ-LCA 48 5.1.1 Range Minimum Query(RMQ) 49 5.1.2 Lowest Common Ancestor (LCA) 53 5.1.3 Reduction from LCA to RMQ 56 5.1.4 From RMQ to LCA 57 5.1.5 An algorithm for the restricted RMQ 60 5.1.6 An AC programme 61 5.2 最长公共子序列LCS 64 5.3 最长上升子序列/最长不下降子序列(LIS) 65 5.3.1 O(n^2) 65 5.3.2 O(nlogn) 66 5.4 Joseph问题 67 5.5 0/1背包问题 68 6 组合数学相关 69 6.1 The Number of the Same BST 69 6.2 排列生成 71 6.3 逆序 72 6.3.1 归并排序求逆序 72 7 数值分析 72 7.1 二分法 72 7.2 迭代法(x=f(x)) 73 7.3 牛顿迭代 74 7.4 数值积分 74 7.5 高斯消元 75 8 其它 77

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值