C++之vector用法及二维数组
Part Ⅰ vector用法目录介绍用法头文件vector声明及初始化vector基本操作1.容量2.修改3.迭代器4.元素的访问算法特别注意:建立二维数组vector的元素是结构体介绍vector是表示可变大小数组的序列容器。简单地说,vector是一个能够存放任意类型的动态数组,能够增加和压缩数据。就像数组一样,vecto...
Part Ⅰ vector用法
目录
介绍
- vector是表示可变大小数组的序列容器。简单地说,vector是一个能够存放任意类型的动态数组,能够增加和压缩数据。
- 就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。
- 本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。
- vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。
- 因此,vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增长。
- 与其它动态序列容器相比(deques, lists and forward_lists), vector在访问元素的时候更加高效,在末尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起lists和forward_lists统一的迭代器和引用更好。
用法
头文件
#include<vector>
vector声明及初始化
vector<int> vec; //声明一个int型向量
vector<int> vec(5); //声明一个初始大小为5的int向量
vector<int> vec(10, 1); //声明一个初始大小为10且值都是1的向量
vector<int> vec(tmp); //声明并用tmp向量初始化vec向量
vector<int> tmp(vec.begin(), vec.begin() + 3); //用向量vec的第0个到第2个值初始化tmp
int arr[5] = {1, 2, 3, 4, 5};
vector<int> vec(arr, arr + 5); //将arr数组的元素用于初始化vec向量
//说明:当然不包括arr[4]元素,末尾指针都是指结束元素的下一个元素,
//这个主要是为了和vec.end()指针统一。
vector<int> vec(&arr[1], &arr[4]); //将arr[1]~arr[4]范围内的元素作为vec的初始值
"//说明:当然不包括arr[4]元素,末尾指针都是指结束元素的下一个元素,"
#include<vector>
#include <iostream>
using namespace std;
int main()
{
int arr[5] = {1, 2, 3, 4, 5};
vector<int> vec(arr, arr + 5);
cout << "vec.size():" << vec.size() << endl << endl;
vector<int>::iterator it;
int i;
for (i=0,it = vec.begin(); it != vec.end(); it++,i++)
cout << "arr[" << i << "]元素:" << *it << endl;
return 0;
}
g++输出如下
[code]
vec.size():5
arr[0]元素:1
arr[1]元素:2
arr[2]元素:3
arr[3]元素:4
arr[4]元素:5
[/code]
vector基本操作
1.容量
- 向量大小: vec.size();
- 向量最大容量: vec.max_size();
- 更改向量大小: vec.resize();
- 向量真实大小: vec.capacity();
- 向量判空: vec.empty();
- 减少向量大小到满足元素所占存储空间的大小: vec.shrink_to_fit(); //shrink_to_fit
2.修改
- 多个元素赋值: vec.assign(); //类似于初始化时用数组进行赋值https://blog.csdn.net/xlm289348/article/details/8166820
- 末尾添加元素: vec.push_back();
- 末尾删除元素: vec.pop_back();
- 任意位置插入元素: vec.insert();
- 任意位置删除元素: vec.erase();
- 交换两个向量的元素: vec.swap();https://blog.csdn.net/sunmenggmail/article/details/8605538
- 清空向量元素: vec.clear();
vector<int>test;//建立一个vector test.push_back(1); test.push_back(2);//把1和2压入vector,这样test[0]就是1,test[1]就是2 vec.insert(vec.begin()+i,a);//在第i+1个元素前面插入a vec.erase(vec.begin()+2);//删除第3个元素 vec.erase(vec.begin()+i,vec.end()+j);删除区间[i,j-1];区间从0开始
assign函数:
函数原型:
void assign(const_iterator first,const_iterator last);
void assign(size_type n,const T& x = T());
功能:
将区间[first,last)的元素赋值到当前的vector容器中,或者赋n个值为x的元素到vector容器中,这个容器会清除掉vector容器中以前的内容。
实例:v2.assign(v1.begin(), v1.end());
3.迭代器
- 开始指针:vec.begin();
- 末尾指针:vec.end(); //指向最后一个元素的下一个位置
- 指向常量的开始指针: vec.cbegin(); //意思就是不能通过这个指针来修改所指的内容,但还是可以通过其他方式修改的,而且指针也是可以移动的。
- 指向常量的末尾指针: vec.cend();
//使用迭代器访问元素. vector<int>::iterator it; for(it=vec.begin();it!=vec.end();it++) cout<<*it<<endl;
4.元素的访问
- 下标访问: vec[1]; //并不会检查是否越界,下标是从0开始的
- at方法访问: vec.at(1); //以上两者的区别就是at会检查是否越界,是则抛出out of range异常
- 访问第一个元素: vec.front();
- 访问最后一个元素: vec.back();
- 返回一个指针: int* p = vec.data(); //可行的原因在于vector在内存中就是一个连续存储的数组,所以可以返回一个指针指向这个数组。这是是C++11的特性。
算法
- 遍历元素
vector<float> vecClass; int nSize = vecClass.size(); //打印vecClass,方法一: for(int i=0;i<nSize;i++) { cout<<vecClass[i]<<" "; } cout<<endl; //需要注意的是:以方法一进行输出时,数组的下表必须保证是整数。 //打印vecClass,方法二: for(int i=0;i<nSize;i++) { cout<<vecClass.at(i)<<" "; } cout<<endl; //打印vecClass,方法三:输出某一指定的数值时不方便 vector<int>::iterator it; for (it = vec.begin(); it != vec.end(); it++) cout << *it << endl; //或者 for (size_t i = 0; i < vec.size(); i++) { cout << vec.at(i) << endl; }
<algorithm>与<vector>的结合使用
#include <iostream>
#include <algorithm>
#include <vector>
int main()
{
using namespace std;
vector<int> vec;
vec.push_back(1);
vec.push_back(2);
vec.push_back(3);
vec.push_back(4);
vec.push_back(5);
vec.push_back(6);
vector<int>::iterator it = find(vec.begin(), vec.end(), 6);
if (it != vec.end())
cout<<*it<<endl;
else
cout<<"can not find"<<endl;
return 0;
}
- 元素翻转
#include <algorithm> reverse(vec.begin(), vec.end());//将元素翻转,即逆序序列
- 元素排序
#include <algorithm> sort(vec.begin(), vec.end()); //采用的是从小到大的排序 //如果想从大到小排序,可以采用上面反转函数,也可以采用下面方法: bool Comp(const int& a, const int& b) { return a > b; } sort(vec.begin(), vec.end(), Comp);
特别注意:
1.如果你要表示的向量长度较长(需要为向量内部保存很多数),容易导致内存泄漏,而且效率会很低。
2.Vector作为函数的参数或者返回值时,需要注意它的写法:double Distance(vector<int>&a, vector<int>&b) //其中的“&”绝对不能少!
一维数组a[5]中&a和a区别
#include "stdio.h" int main() { int a[5]={1,2,3,4,5}; int *ptr=(int *)(&a+1); printf("%d,%d",*(a+1),*(ptr-1)); return 0; }
答案:2,5
区别:a代表数组a[5]的首地址,&a代表数组整体的地址,虽然他们的地址数是一样的,但是含义不一样的。
注意 a 是表示数组a[5] 的。
*(a + 1) 和 *(&a + 1)区别见见下图。*(a + 1) 这个好理解,但*(&a + 1)理解起来别扭,把&a看成表示数组整体的长度,加一表示数组整体长度后移(见图红色的方框,到了a[5]位置,其实数组只有到a[4],所以相当与数组整体后移了)。
建立二维数组
vector<vector<int> > array(m); //这个m一定不能少
//初始化一个m*n的二维数组
for(int i=0;i<m;i++) {
array[i].resize(n);
}
array[0].size(); //指第一行的列
//第一种方法
#include<iostream>
#include<vector>
using namespace std;
int main() {
vector<vector<int> > array(4);
for(int i=0;i<4;i++) {
array[i].resize(5);
}
//输出二维数组的行和列
cout<<"Row:"<<array.size()<<" Column:"<<array[0].size()<<endl;
//输出二维数组
cout<<"["<<endl;
for(int j=0;j<array.size();j++) {
cout<<"["<<" ";
for(int k=0;k<array[0].size();k++) {
cout<<array[j][k]<<" ";
}
cout<<"]"<<endl;
}
cout<<"]"<<endl;
return 0;
}
//第二种方法
#include <vector>
#include <iostream>
using namespace std;
int main(){
int out[3][2] = { 1, 2,3, 4,5, 6 };
vector <int*> v1;
v1.push_back(out[0]);
v1.push_back(out[1]);
v1.push_back(out[2]);
cout << v1[0][0] << endl;//1
cout << v1[0][1] << endl;//2
cout << v1[1][0] << endl;//3
cout << v1[1][1] << endl;//4
cout << v1[2][0] << endl;//5
cout << v1[2][1] << endl;//6
return 0;
}
vector的元素是结构体
vector的元素不仅仅可以是int,double,string,还可以是结构体,但是要注意:结构体要定义为全局的,否则会出错。
#include<stdio.h>
#include<algorithm>
#include<vector>
#include<iostream>
using namespace std;
typedef struct rect{
int id;
int length;
int width;
//对于向量元素是结构体的,可在结构体内部定义比较函数,下面按照id,length,width升序排序。
bool operator< (const rect &a) const
{
if(id!=a.id)
return id<a.id;
else
{
if(length!=a.length)
return length<a.length;
else
return width<a.width;
}
}
}Rect;
int main(){
vector<Rect> vec;
Rect rect;
rect.id=1;
rect.length=2;
rect.width=3;
vec.push_back(rect);
vector<Rect>::iterator it=vec.begin();
cout<<(*it).id<<' '<<(*it).length<<' '<<(*it).width<<endl;
return 0;
}
转载地址:https://www.cnblogs.com/zhonghuasong/p/5975979.html
转载地址:https://blog.csdn.net/duan19920101/article/details/50617190/
Part Ⅱ 二维数组
目录
二维数组的初始化
形式:
数据类型 数组名[整常量表达式][ 整常量表达式]={ 初始化数据 };
初始化方式:
⑴ 分行进行初始化
int a[2][3]={{1,2,3},{4,5,6}};
相当于执行如下语句:
int a[2][3];
a[0][0]=1;a[0][1]=2;a[0][2]=3;a[1][0]=4;a[1][1]=5;a[1][2]=6;
注意,初始化的数据个数不能超过数组元素的个数,否则出错。
⑵ 不分行的初始化
int a[2][3]={ 1,2,3,4,5,6};
相当于执行如下语句:
a[0][0]=1; a[0][1]=2;a[0][2]=3;a[1][0]=4;a[1][1]=5;a[1][2]=6;
⑶ 为部分数组元素初始化
static int a[2][3]={{1,2},{4}}; //第一行只有2个初值,按顺序分别赋给a[0][0]和a[0][1];第二行的初值4赋给a[1][0]。由于存储类型是static,故其它数组元素的初值为0。
注意:
1.可以省略第一维的定义,但不能省略第二维的定义。系统根据初始化的数据个数和第2维的长度可以确定第一维的长度。
int a[ ][3]={ 1,2,3,4,5,6};
a数组的第一维的定义被省略,初始化数据共6个,第二维的长度为3,即每行3个数,所以a数组的第一维是2。
2.一般,省略第一维的定义时,第一维的大小按如下规则确定:
初值个数能被第二维整除,所得的商就是第一维的大小;若不能整除,则第一维的大小为商再加1。
例如,int a[ ][3]={ 1,2,3,4};等价于:int a[2][3]={ 1,2,3,4};
若分行初始化,也可以省略第一维的定义。
下列的数组定义中有两对{ },已经表示a数组有两行。static int a[ ][3]={{1,2},{4}};
二维数组作为函数参数
规定:如果将二维数组作为参数传递给函数,那么在函数的参数声明中必须指明数组的列数,数组的行数没有太大关系,可以指定也可以不指定。因为函数调用时传递的是一个指针,它指向由行向量够成的一维数组。
正确写法:
二维数组作为函数参数正确写法如下所示:
void Func(int array[3][10]);
void Func(int array[ ][10]);
因为数组的行数无关紧要,所以还可以写成如下形式:
void Func(int (*array)[10]); 注意*array需要用括号括起来
因为[]的优先级比*的优先级高,故*array必须用括号括起来,这种形式的声明参数是一个指针,它指向具有10个元素的一维数组。否则变成了void Func(int *array[10]);这时候参数相当于是声明了一个数组,该数组有10个元素,其中每个元素都是一个指向整型对象的指针。
错误写法:
1.void Func(int array[ ][ ]);//如果在形参中不说明列数,则系统无法决定应为多少行多 少列
2.void Func(int array[3][ ]);//实参数组维数可以大于形参数组,例如形参数组定义为:void Func(int array[3][10]); 而实参数组定义为:int array[5][10];
指针写法:
如果我们省略了第二维或者更高维的大小,编译器将不知道如何正确的寻址。但是我们在编写程序的时候却需要用到各个维数都不固定的二维数组作为参数,如何解决呢?
编译器虽然不能识别,但是我们完全可以不把它当作一个二维数组,而是把它当作一个普通的指针,再另外加上两个参数指明各个维数,然后我们为二维数组手工寻址,这样就达到了将二维数组作为函数的参数传递的目的,根据这个思想,我们可以把维数固定的参数变为维数随即的参数。
例如:void Func(int array[3][10]);
void Func(int array[][10]);
变为:void Func(int **array, int m, int n);
在转变后的函数中,array[i][j]这样的式子是不对的(不信,大家可以试一下),因为编译器不能正确的为它寻址,所以我们需要模仿编译器的行为把array[i][j]这样的式子手工转变为:*((int*)array + n*i + j);
在调用这样的函数的时候,需要注意一下,如下面的例子:int a[3][3] = {{1,1,1},{2,2,2},{3,3,3}};Func(a, 3, 3);
根据不同编译器不同的设置,可能出现warning 或者error,可以进行强制转换如下调用: Func((int**)a, 3, 3);
转载地址:https://blog.csdn.net/chenshijun0101/article/details/6640377
更多推荐
所有评论(0)