C++基础总结

从C到C++的体验

  1. C++语言以cpp作为文件结尾。
  2. C++函数必须有返回值。
  3. C++是经典OO(Object Oriented,面向对象)思想编程语言,同时有结构体和类的概念,而C语言是面向过程的,据我所知只有结构体。
  4. 数据类型上与c的差异,最明显的是C++有布尔型:bool,返回值只有true和false。C语言中仅以0和非零代表真假。
  5. 对于一个初级ACMer来说,C++带给我最直观的感受就是,有很多现成的类库可以直接调用,再也不用手写快排什么的?,而且C++的sort底层由红黑树来维护,十分稳定,远优于手写的快排。
  6. C++的STL十分方便,堆栈队列双端队列优先队列各种方便,还有集合set,映射map,不定长数组vector,大整数类biginteger,手写体验好不逊色于Java,某种层面讲速度还优于Java。
  7. C++有输入输出流,底层原理上比较难理解,但是因为不用记C语言中printf、scanf的占位符,所以用起来感觉,一时用一时爽,一直用一直爽。但是输入输出流会比printf和scanf比起来慢不少。有的算法题会明确写着输入量过大请使用较快的输入方式,充满了对C++的不友好,所以我在巨佬的博客上查到了这个 → std::ios::sync_with_stdio(false);原理比较难搞懂,但是作用是关闭输入输出流的同步,提高输入输出效率,写上这句话基本上和printf和scanf一样快了,输入几十万次好像也是0ms,比较感人。

  8. C++引入了命名空间的概念,命名空间:实际上就是一个由程序设计者命名的内存区域,程序设计者可以根据需要指定一些有名字的空间域,把一些全局实体分别放在各个命名空间中,从而与其他全局实体分隔开来。就是为了解决同名冲突,看来相比较于C,C++更适合于做工程。

STL总结

​​​​​STL是指C++的标准模板库(Standard Template Library)。涉及很多容器。

首先介绍一个c++比较常用的函数→sort(begin,end,cmp)。这个函数就是快速排序,但是比快排要稳定的多,底层是用红黑树来维护的,时间复杂度稳定在O(nlogn)。这个函数可以用给数组排序:sort(a,a+n);也可以给vector进行排序:sort(v.begin,v.end);其中这个函数第三个参数是比较函数,默认是升序排列,可以根据题目的要求自己定义比较函数,甚至可以比较结构体,例如:

    bool less_int(int a,int b){

        return b<a;

    }
    sort(a,a+n,less_int());

上面这个less_int的意思是认为b<a是a<b,所以实现的效果是降序排列。同理可以比较结构体:

struct point{
    int x,y;
};//点的结构体,包含x坐标和y坐标
vector<point> vec(10);
bool greater_point(point a,point b)
{
    return a.x <= b.x;
}
sort(vec.begin,vec.end,greater_point());

意思是把每个点按x坐标的大小升序排列,所以sort的功能十分强大。

不定长数组::vector

这是C++的一个容器,直观来讲是一个不定长的数组,因为可以通过resize()函数来改变vector的长度,push_back()来向数组末端添加元素,用pop_back()来删除最后一个元素,感觉有点像python的list。

    (1)a.assign(b.begin(), b.begin()+3); //b为向量,将b的0~2个元素构成的向量赋给a
    (2)a.assign(4,2); //是a只含4个元素,且每个元素为2
    (3)a.back(); //返回a的最后一个元素
    (4)a.front(); //返回a的第一个元素
    (5)a[i]; //返回a的第i个元素,当且仅当a[i]存在2013-12-07
    (6)a.clear(); //清空a中的元素
    (7)a.empty(); //判断a是否为空,空则返回ture,不空则返回false
    (8)a.pop_back(); //删除a向量的最后一个元素
    (9)a.erase(a.begin()+1,a.begin()+3); //删除a中第1个(从第0个算起)到第2个元素,也就是说删除的元素从a.begin()+1算起(包括它)一直到a.begin()+         3(不包括它)
    (10)a.push_back(5); //在a的最后一个向量后插入一个元素,其值为5
    (11)a.insert(a.begin()+1,5); //在a的第1个元素(从第0个算起)的位置插入数值5,如a为1,2,3,4,插入元素后为1,5,2,3,4
    (12)a.insert(a.begin()+1,3,5); //在a的第1个元素(从第0个算起)的位置插入3个数,其值都为5
    (13)a.insert(a.begin()+1,b+3,b+6); //b为数组,在a的第1个元素(从第0个算起)的位置插入b的第3个元素到第5个元素(不包括b+6),如b为1,2,3,4,5,9,8,插入元素后为1,4,5,9,2,3,4,5,9,8
    (14)a.size(); //返回a中元素的个数;
    (15)a.capacity(); //返回a在内存中总共可以容纳的元素个数
    (16)a.resize(10); //将a的现有元素个数调至10个,多则删,少则补,其值随机
    (17)a.resize(10,2); //将a的现有元素个数调至10个,多则删,少则补,其值为2
    (18)a.reserve(100); //将a的容量(capacity)扩充至100,也就是说现在测试a.capacity();的时候返回值是100.这种操作只有在需要给a添加大量数据的时候才         显得有意义,因为这将避免内存多次容量扩充操作(当a的容量不足时电脑会自动扩容,当然这必然降低性能) 
    (19)a.swap(b); //b为向量,将a中的元素和b中的元素进行整体性交换
    (20)a==b; //b为向量,向量的比较操作还有!=,>=,<=,>,<

常用的几个方法:

1.重新设置长度 v.resize(int n);

#include<vector>

vector<int> v;
v.resize(10);//将vector的长度重新设为10

2.在尾部添加元素v.push_back();

#include<vector>

vector<int> v;
v.push_back(1);//在尾部添加一个1

3.取容器首元素 v.front()

#include<vector>

vector<int> v(10,1);//创建一个容量为10,每个原素初始值均为1的容器

int p = v.front();//取出vector中第一个元素的值,但是不从中删除该元素

4获取vector的大小

#include<vector>
vector<int> v(10,0);//创建一个vector,容量为10,所有元素初始化为0
int sizen = v.size();//获取vector长度

5.插入元素v.insert()

#include<vector>
vector<int> v(10,0);//初始化
vector<int> b(10,1);//初始化
a.insert(a.begin()+1,5); //在a的第1个元素(从第0个算起)的位置插入数值5,如a为1,2,3,4,插入元素后为1,5,2,3,4
v.insert(v.begin()+1,3,5); //在a的第1个元素(从第0个算起)的位置插入3个数,其值都为5
v.insert(v.begin()+1,b+3,b+6); //b为数组,在a的第1个元素(从第0个算起)的位置插入b的第3个元素到第5个元素(不包括b+6),如b为1,2,3,4,5,9,8,插入元素后为1,4,5,9,2,3,4,5,9,8

6.清空与判空v.clear()和v.empty()

#include<vector>
vector<int> v(10,1);
if(v.empty())//此时输出1
    cout<<NULL;
else
    cout<<1;
v.clear();//清空操作
if(v.empty())//此时输出NULL
    cout<<NULL;
else
    cout<<1;

下面是遍历vector的方法

方法零,对C念念不舍的童鞋们习惯的写法:

void ShowVec(const vector<int>& valList)
{
    int count = valList.size();
    for (int i = 0; i < count;i++)
    {
        cout << valList[i] << endl;
    }
}

 

或者

void ShowVec(const vector<int>& valList)
{
    int count = valList.size();
    for (int i = 0; i < count;i++)
    {
        cout << valList.at(i) << endl;
    }
}

 

方法一,大家喜闻乐见的for循环迭代器输出,(注意,此处使用了C++11中新增的标准库容器的cbegin函数)

 

void ShowVec(const vector<int>& valList)
{
    for (vector<int>::const_iterator iter = valList.cbegin(); iter != valList.cend(); iter++)
    {
        cout << (*iter) << endl;
    }
}

 

或者使用c++新增的语义auto,与上面差不多,不过能少打几个字:

 

void ShowVec(const vector<int>& valList)
{
    for (auto iter = valList.cbegin(); iter != valList.cend(); iter++)
    {
        cout << (*iter) << endl;
    }
}

 

 

方法二,for_each加函数:

 

template<typename T>
void printer(const T& val)
{
    cout << val << endl;
}
void ShowVec(const vector<int>& valList)
{
    for_each(valList.cbegin(), valList.cend(), printer<int>);
}

 

 

方法三,for_each加仿函数:

 

 
template<typename T>
struct functor
{
    void operator()(const T& obj)
    {
        cout << obj << endl;
    }
};
void ShowVec(const vector<int>& valList)
{
    for_each(valList.cbegin(), valList.cend(), functor<int>());
}

 

方法四,for_each加Lambda函数:(注意:lambda为c++11中新增的语义,实则是一个匿名函数)

void ShowVec(const vector<int>& valList)
{
    for_each(valList.cbegin(), valList.cend(), [](const int& val)->void{cout << val << endl; });
}

方法五,for区间遍历:(注意,for区间遍历是c++11新增的语法,用于迭代遍历数据列表)

for (auto val : valList)
{
    cout << val << endl;
}

 

etc.

ps:我就是那个对C语言念念不忘的童鞋。。

集合::set

集合与映射也是两个常见的容器。set就类似于数学上或者离散结构里面的集合,里面的元素是有序且唯一的,所以set存储元素自动排序,并且里面提供查找方法,查找应该使用的二分查找,时间复杂度O(logn)。和sort一样自定义类型也可以构造set,同样必须定义小于运算符,就是比较函数。

#include <iostream>
#include <set> 
 
using namespace std;
 
struct Node{
	int id,value;
	Node(){}
	Node(int _id,int _value):id(_id),value(_value){}
	bool operator < (const Node& b)const {
		if(id == b.id)return false;//去重环节 
		//排序环节(这里是按value降序,id升序) 
		if(value != b.value)return value > b.value; 
		else return id < b.id;
	}
};
 
int main(){
	
	set<Node> S;
	S.insert(Node(1,2));
	S.insert(Node(2,3));
	S.insert(Node(2,4));//因为id重复插入失败 
	S.insert(Node(3,4));
	S.insert(Node(4,4));
	set<Node>::iterator it;
	for(it=S.begin() ; it!=S.end() ; ++it)printf("id:%d value:%d\n",(*it).id,(*it).value);
	
	return 0;
}

其中iterator是C++中的迭代器,也是C++和C比较大的不同之处之一,类似于指针。

映射::map

map就是从key到value的映射。他重载了[]运算符,通过索引key就可以找到value。例如:

#include<map>
map<string,int> month_name;//表示月份名到月份号的映射
month_name["July"] = 7;//可以通过类似数组的赋值方式进行赋值

map和set都支持insert、find、count和remove操作,并且可以按照从小到大的顺序遍历元素。

栈::stack

栈是STL中3中特殊的数据结构之一。栈的一个比较重要的特性是后进先出,很多经典算法是用了栈后进先出的特性来实现的,例如dfs和后缀表达式的运算。

栈的基本用法:

#include<iostream>
#include<stack>
int main()
{
    stack<int> s;//创建一个栈
    s.push(1);//将元素压入栈顶
    s.top();//获取栈顶元素
    s.pop();//弹出栈顶元素
    return 0;
}

我目前最常用的只有这些,后面会陆续补充;

队列::queue

队列是一种与栈相对应的数据结构,也是STL中三个特殊的数据结构之一。它性质与栈相对应,栈是先进后出,后进先出;而队列是先进先出,后进后出。

队列的基本用法:

#include<iostream>
#include<queue>
int main()
{
    queue<int> q;//创建一个队列
    q.push(1);//将元素入对尾
    q.front();//获取队首元素,此处与栈不同,栈是top()
    q.pop();//弹出队首元素
    return 0;
}

优先队列::priority_queue

优先队列和队列都包含在<queue>这个头文件中。优先队列也是一种抽象数据类型,有些像队列,但是先出队的不是先进队的元素,而是优先级较高的元素,类似于插队。优先队列和队列的定义方式大同小异,但是访问队首元素从queue的front()变成了top(),push变成了push_back(),和vector一样。

priority_queue的模板申明带3个参数:priority_queue<Type, Container, Functional>,其中Type 为数据类型,Container为保存数据的容器,Functional 为元素比较方式,类似于sort和set的比较函数。

双端队列::deque

这个东西我还不太会。。

 

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值