算法是重要的。但是算法毕竟还需要依赖于一门语言实现,它并不纯是理论,并不能跟“数学竞赛”等同。它具有一定的工程性,明白一类算法的思想,并不一定能写出这类算法的程序,而且,对算法理论和程序精通的人,把这种精髓用到实际工程中,也是需要学习锻炼的。所以说,竞赛之人需要酌其轻重,不然可能会适得其反。
所以很多人会觉得,算法除了在ACM竞赛中用到,平时几乎接触不到,把算法看做是一门“不食人间烟火”的东西。认同这种观点的人,并不少有。而产生这种观点的原因,可能就在于算法的“门槛”之高,算法的“内容”之难。
其实不然。上述仅仅是从狭义上面说的。在广义上面,ACM算法只是算法中的一小部分。还有其他的包括机器算法、人工智能算法、大数据算法等等。可以说,算法是计算机科学的核心内容。没学过算法,没掌握过算法的程序员,不能说他十分有能力。只懂得招式,而没有内力,是成不了武林高手的。
1、调试的大体思想
我们在培训第一讲里面说到了怎样用 codeblocks 的 debug 板块对我们的程序进行调试,通过一行一行的操作,我们可以通过顶栏的 debug - debug windows - watches 反馈窗格看到我们程序运行中各个变量的状态。
但是很多时候调试的过程并不如人意。本来去寻找错误之处就是一件 麻烦的事情 ,而我们的代码不会一直只是一二十行,我们定义和需要考虑的变量也会渐趋于复杂。我们很多时候思考出的,想要快速获取我们想要的 数值 和 过程,慢慢的调试会显得 冗长 而 不清晰 。所以在这次培训里推荐大家用直接输出中间变量的方法去直接获取自己想要什么,这更有助于理清自己的思路,有效率地“修复”错误代码。
2、举个栗子
就比如这一道简单的小题目,我们如果在写的时候出现了问题,就可以用某一种方式去输出自己想要的信息:
#include
#include
using namespace std;
int main()
{
int s;
while(cin>>s)
{
char num[8];
getchar();
char read;
int i = 0;
while((read = getchar()) != '\n')
{
num[i] = read;
i++;
}
if(s == 0 && num[0] == 48)break;
char show[2*s+3][i*(s+2)+i-1] = {};
for(int j=1; j<=i; j++)
{
int temp = num[j-1] - 48;
for(int k=0; k<s; k++)
{
if(temp == 4 || temp == 5 || temp == 6
|| temp == 8 || temp == 9 || temp == 0)
{
//cout<<"左上:"<<"show["<
show[k+1][(j-1)*(s+3)] = '|';//左上
}
if(temp == 2 || temp == 6
|| temp == 8 || temp == 0)
{
//cout<<"左下:"<<"show["<
show[k+s+2][(j-1)*(s+3)] = '|';//左下
}
if(temp != 5 && temp != 6)
{
//cout<<"右上:"<<"show["<
show[k+1][s+1+(j-1)*(s+3)] = '|';//右上
}
if(temp != 2)
{
//cout<<"右下:"<<"show["<
show[k+s+2][s+1+(j-1)*(s+3)] = '|';//右下
}
if(temp != 1 && temp != 4)
{
//cout<<"上:"<<"show["<<0<<"]["<
show[0][1+k+(j-1)*(s+3)] = '-';//上
}
if(temp != 1 && temp != 7 && temp != 0)
{
//cout<<"中:"<<"show["<
show[s+1][1+k+(j-1)*(s+3)] = '-';//中
}
if(temp != 1 && temp != 4 && temp != 7)
{
//cout<<"下:"<<"show["<<2*s+2<<"]["<
show[2*s+2][1+k+(j-1)*(s+3)] = '-';//下
}
}
}
for(int j=0; j<(2*s+3); j++)
{
for(int k=0; k<(i*(s+2)+i-1); k++)
{
cout<<show[j][k];
}
cout<<endl;
}
}
return 0;
}
比如说这里就用的是枚举的方式去观察每一个数字相对应的输出是否和想法中一致(调试代码如注释行),从而对整个程序进行调试和掌控。
培训的时候本来没有准备讲重载函数的,但是函数的重载是一个很基础的板块,而因为这个大家基础不是很牢,我就没有继续讲函数模板的知识了,但是这样就存在一个遗憾,因为STL模板库就是建立在其之上的,所以之后给大家也只是很简单的介绍了两个容器。因为学习要循序渐进,知其然亦要知其所以然,所以就不拓展更多的东西了,希望大家能消化消化这次的内容。
重载有csdn写的很成熟了,我这里就偷个懒。
https://blog.csdn.net/zhanghow/article/details/53588458 这篇主要是写遇到不同数据类型的函数的时候,是怎样实现重载的,讲得比较透彻,可以去看一眼(这篇里内容很少很简单)
1、不定长数组 vector < >
对于vector的声明大体有下面几种方式:
vector<int> v1; //创建空的数组容器v1
vector<int> v2(v1); //新建数组容器v2并将数组v1的值拷贝给v2
vector<int> v3(k); //创建长度为k的容器v3
vector<int> v4(m,n);//创建长度为m的,初始值都为n的容器v4
为了能更好的理解各个成员函数的使用,下面就是对上面各个函数的很简单的应用:
#include
#include
using namespace std;
vector <int> c;
int main()
{
cout<<"初始数据:"<<endl;
for(int i=0; i<10; i++)
{
c.push_back(i);
}
// for(int i=0; i<10; i++)
// {
// int temp;
// cin>>temp;
// c.push_back(temp);
// }
for(std::vector<int>::iterator i = c.begin(); i != c.end(); i++)
{
cout<<*i<<" ";
}
cout<<endl;
cout<<"size of c is:"<<c.size()<<endl;
cout<<"删了一个:"<<endl;
c.pop_back();
for(std::vector<int>::iterator i = c.begin(); i != c.end(); i++)
{
cout<<*i<<" ";
}
cout<<endl;
cout<<"size of c is:"<<c.size()<<endl;
cout<<"头数据为:"<<endl;
cout<<c.front()<<endl;
cout<<"尾数据为:"<<endl;
cout<<c.back()<<endl;
cout<<"清除操作:"<<endl;
c.clear();
cout<<"size of c is:"<<c.size()<<endl;
return 0;
}
上面的程序里还有一点要说的就是迭代器iterator。迭代器类似于C语言里面的指针类型,在这里它提供了对数组元素进行快速随机访问以及在序列尾部进行快速插入和删除操作的功能。
2、映射 map
map的功能:
首先也是头文件#include
通常来说,定义一个新的map容器就直接用
map<int, string> your_function_name;
定义map里对应的key值和value值的数据类型不是指定的,可以自行定义。
为了更好的理解,给一个小栗子。
输入数据包含了很多种不同的颜色(e.g : red, blue, orange)每个颜色可能有一个或者多个,想让你统计记录每种颜色各有多少个。
代码如下:
#include
#include
#include
#include
using namespace std;
int main()
{
map<string,int> word_count;
string word;
while(cin >> word)
{
++word_count[word]; 、
}
map<string, int>::iterator iter;
for(iter = word_count.begin(); iter != word_count.end(); iter++)
{
cout << "[" << iter->first << "] = " << iter->second << endl;
}
return 0;
}
仔细看完之后你会发现这要比不用键值对来得容易得多。
在这里没有对大家提很高的要求,主要是让大家熟悉一下这种数据结构类型。以后碰到的时候心里不会出现畏惧的感觉==
1、树是什么
在计算机科学中,树是一种抽象数据类型的数据结构,用来模拟具有树状结构性质的数据集合。它是由n(n>0)个有限节点组成一个具有层次关系的集合。把它叫做“树”是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。
它具有以下的特点:
P.S : 每一个节点的子枝数目不超过2的树我们就叫它作二叉树。
2、建树的基本操作:
构造一个节点类:
class node
{
public://声明公有成员,如果不声明,会默认作private
char data; //结点值
node *lchild, *rchild; //左孩子,右孩子结点
//node *parent; //父结点,起到可以回溯的作用,开始学树的时候也可以不用
};//类声明最后要有分号哦
用递归的思想建树:
void CreateBiTree(node *&T)
{
//这里和培训的时候说的有一些不一样,主要是数组在这里不是很好描述,
//就直接在建立每个节点的时候读入该节点的值。
//假设“#”是代表“空”。
char ch;
cin >> ch;
if (ch == '#')
T = NULL;
else
{
T = new node();
T->data = ch;
CreateBiTree(T->lchild);
CreateBiTree(T->rchild);
}