C++ vector介绍

🌈一、vector是什么

在这里插入图片描述
vector是一个可以更改大小的数组模版,可以注入不同的数据类型,从而构建成不同数据类型的数组。

既然vector是数组模版,那如果被数据类型char实例化,是否就可以替代string了呢?
不可以,因为string这个类中有一些专门为字符串设计的函数,比如插入一整串数据(vector)、寻找子字符串的起始位置等等。

🌈二、vector的成员函数

☀️1.构造、拷贝构造

在这里插入图片描述

🎈函数介绍:

用于构造一个数组,根据使用的构造函数版本初始化其内容。

  1. 重载1:空容器构造函数(默认构造函数)。
    构造一个没有元素的空容器(空数组)。
    在这里插入图片描述
  2. 重载2:构造一个包含n个元素的容器。每个元素都是val。
    在这里插入图片描述
  3. 重载3:范围构造函数。
    构造一个包含与范围 [第一个,最后一个)一样多的元素的容器,每个元素都以相同的顺序从该范围中的相应元素构造而成。
    在这里插入图片描述
  4. 重载4:拷贝构造函数。
    以相同的顺序构造一个包含x中每个元素的副本的容器。
    在这里插入图片描述

🎈举例使用构造、拷贝构造:

#include<iostream>
#include<vector>
using namespace std;
int main() {
	vector<int> v1;
	vector<int> v2(4, 100);
	vector<int> v3(v2.begin(), v2.end());
	int myInt[] = { 1,2,3,4,5 };
	vector<int> v4(myInt, myInt + 4);
	vector<int> v5(v4);
	for (auto ch : v1) {
		cout << ch << ' ';
	}
	cout << endl;
	for (auto ch : v2) {
		cout << ch << ' ';
	}
	cout << endl;
	for (auto ch : v3) {
		cout << ch << ' ';
	}
	cout << endl;
	for (auto ch : v4) {
		cout << ch << ' ';
	}
	cout << endl;
	for (auto ch : v5) {
		cout << ch << ' ';
	}
	cout << endl;
}

在这里插入图片描述
注:vector不支持流插入和流提取,因为vector的自定义程度很高,可以存放int数据以4个字节为一个单位,还可以存放string之类的自定义类型数据,所以很难知道到底每个单位多大,很难把控流插入和提取。但是可以通过遍历vector数组的方式一个一个地打印其内部的每个元素。

☀️2.析构函数

在这里插入图片描述
当vector数组存储数据是内置类型时,不需要显式写析构函数,编译器会自动回收内置类型数据站用电空间。当存储的数据是自定义类型时,需要显式地写析构函数,在析构函数的内部delete掉new出来的空间。

☀️3.赋值重载函数

在这里插入图片描述
将新内容分配给容器,替换其当前内容,并相应地修改其大小。

🎈举例使用operator=

#include<iostream>
#include<vector>
using namespace std;
int main() {
	vector<int> v1(4, 1);
	vector<int> v2;
	for (auto ch : v2) {
		cout << ch << ' ';
	}
	cout << endl;
	v2 = v1;
	for (auto ch : v2) {
		cout << ch << ' ';
	}
	cout << endl;
}

在这里插入图片描述
注:赋值重载和拷贝构造的区别:
赋值重载是两个已存在对象间的赋值,就像上面的程序中,已经存在了两个对象v1和v2,再给v2赋上v1值;拷贝构造是用一个已经存在的对象创建另一个对象,即这两个对象在目前的程序中只有一个存在,拷贝构造函数运行完成后另一个对象才开始存在。

☀️4.迭代器

迭代器的类型前要加上类名,比如现在的类是vector < i n t > <int> <int>,则此时的迭代器类型为:vector < i n t > <int> <int>::iterator。
返回指向数组头尾的迭代器的函数有:
在这里插入图片描述

☀️5.size、capacity

🎈(1)size:返回有效元素个数

在这里插入图片描述

🎈(2)capacity:返回底层空间个数

在这里插入图片描述

注:有效元素个数和底层容量大小不一定一样。

☀️6.resize 重置数组大小并初始化

在这里插入图片描述

🎈函数介绍:

(1)简要概括功能该函数作用是调整容器的大小,使其包含n个元素,并对所有位置初始化。

(2)关于容器大小和底层容量:

  1. 如果n小于当前容器大小,则内容将减少到其前n个元素,删除超出的元素(并销毁它们)。
  2. 如果n大于当前容器大小,则通过在末尾插入所需数量的元素来扩展内容,以达到n的大小。如果指定了val,则将新元素初始化为val的副本,否则将对其进行值初始化。
  3. 如果n也大于当前容器容量,则自动重新分配所分配的存储空间。
  4. 该函数的底层其实是用insert(插入)和erase(删除)实现的。
  5. 大小改变的同时底层容量可能也会改变。(erase函数大部分时候不会缩减底层容量,但如果碰到缩减有效元素至底层空间的一半这种程度,则可能会出于节省空间而释放掉多余的空间)

(3)关于初始化出来的内容:

  1. 取决于vector < c l a s s T > <class T> <classT>中的数据类型T,初始化的内容就是调用T的默认构造函数而生成的对象,即T()。
  2. 根据之前学到的,只有自定义类型才有默构造函数,比如T为string时,其默认构造会产生一个空字符串,然而T也有可能是内置类型呀,因此为了逻辑闭环,给内置类型也设计了默认构造函数,构造出来的值是确定的,如下:
  3. T是内置类型,则:
    T为int:int()=0
    T为char:char()=0=’\0’(ASCII码值0对应的字符是’\0’,非有效字符)
    T为double:double()=0.0

🎈举例使用resize

#include<iostream>
#include<vector>
using namespace std;
int main() {
	vector<int> v1(10, 1);
	for (auto ch : v1) {
		cout << ch << ' ';
	}
	cout << endl;
	cout << "capacity:" << v1.capacity() << endl;
	v1.resize(15, 2);
	for (auto ch : v1) {
		cout << ch << ' ';
	}
	cout << endl;
	cout << "capacity:" << v1.capacity() << endl;
	v1.resize(3);
	for (auto ch : v1) {
		cout << ch << ' ';
	}
	cout << endl;
	cout << "capacity:" << v1.capacity() << endl;
}

在这里插入图片描述

☀️7.reserve 显示扩容

在这里插入图片描述
在这里插入图片描述

🎈函数介绍:

  1. 请求向量容量至少足以包含n个元素。
  2. 如果n大于当前数组容量,则函数会使容器重新分配其存储,将其容量增加到n(或更大)。
  3. 在所有其他情况下,函数调用不会导致重新分配,数组底层容量也不会受到影响。
  4. 此函数对数组大小没有影响,也不能更改其元素。
  5. 无返回值。如果请求的大小大于最大大小(vector::max_size),则抛出length_error异常。

🎈用capacity验证reserve在vs平台下的的扩容机制

#include<iostream>
#include<vector>
using namespace std;
int main() {
	vector<int> v;
	int size = v.size();
	for (int i = 0;i < 200;i++) {
		if (size == v.capacity()) {
			cout << "new capacity:" << size << endl;
		}
		v.push_back(1);
		size = v.size();
	}
}

在这里插入图片描述
可以看出,在vs平台下,reserve的扩容机制是1.5倍扩容。

不同平台有不同机制,比如Linux系统的g++是2倍扩容:
在这里插入图片描述

☀️8.shrink_to_fit 缩减至合适大小

在这里插入图片描述

🎈函数介绍:

  1. 请求容器减小容量以适应其大小。
  2. 编译器的缩容其实是很不敏感的,常常会有底层空间很大但实际只占用一小部分的情况,用shrink_to_fit函数就能让编译器调整底层容量至一个刚好放得下有效内容的大小,避免浪费空间。
  3. 该请求是不绑定的(即不同平台可能实现机制不同),容器实现可以自由地进行其他优化,并使向量的容量大于其大小。
  4. 这可能会导致重新分配,但对向量大小没有影响,也无法更改其元素。

🎈体验shrink_to_fit的缩容(vs平台下)

#include<iostream>
#include<vector>
using namespace std;
int main() {
	vector<int> v(100, 1);
	cout << "size:" << v.size() << '\n';
	cout << "capacity:" << v.capacity() << '\n';
	v.resize(10);
	cout << "size:" << v.size() << '\n';
	cout << "capacity:" << v.capacity() << '\n';
	v.shrink_to_fit();
	cout << "size:" << v.size() << '\n';
	cout << "capacity:" << v.capacity() << '\n';
}

在这里插入图片描述
可看出,resize后,底层空间仍为100,但实际只占用10个,造成浪费,用shrink_to_fit函数及时调整底层,既不影响数组的内容,又不浪费空间。

☀️9.下标访问操作符重载函数

在这里插入图片描述

🎈函数介绍:

  1. 返回对数组容器中第n个位置处元素的引用。
  2. 类似的成员函数vector::at与此运算符函数具有相同的行为。不同之处在于当越界访问时,at会抛出out_of_range异常,结合try catch语句就不会使程序崩溃,属于很温和的处理方式;而operator[]直接程序崩溃,十分严重。
  3. 有两个重载,分别针对无const修饰和有const修饰的数组。

☀️10.插入相关

🎈(1)push_back 尾插一个元素

在这里插入图片描述

🎈(2)insert 内部插入

在这里插入图片描述
在这里插入图片描述

  1. 重载1:在迭代器指向的位置插入一个元素
    在这里插入图片描述
    返回值:一个迭代器,指向新插入的元素中的第一个。
  2. 重载2:在迭代器指向位置插入n个元素
    在这里插入图片描述
    无返回值。
  3. 重载3:在迭代器指向位置,插入另外两个迭代器间的内容
    在这里插入图片描述
    无返回值。
    注意:
    ①Iterator是指向原vector数组的迭代器,InputIterator是指向其他容器的迭代器,不一定非得是vector容器,还有可能是int数组、string、list等等。
    ②将其他容器中的元素放入vector容器中,会先进行类型转换。比如Iterator指向vector < i n t > <int> <int>数组、InputIterator指向字符串,会先将字符串隐式转换成int,即转换成字符对应的ASCII码值,vector < i n t > <int> <int>数组;如果InputIterator指向double类型数组,会现将double类型的元素隐式转换成int类型,在插入原vector < i n t > <int> <int>数组。
🌟举例使用insert
👻(1)插入一个元素、n个元素(前两个重载)
#include<iostream>
#include<vector>
using namespace std;
int main() {
	vector<int> v1(2, 2);//22
	v1.insert(v1.end(), 1);
	for (auto ch : v1) {
		cout << ch;
	}
	cout << endl;//221
	v1.insert(v1.begin(), 2, 3);
	for (auto ch : v1) {
		cout << ch;
	}
	cout << endl;//33221
}

在这里插入图片描述

👻(2)插入另外两个迭代器之间的内容

①另外两个迭代器也是vector < i n t > <int> <int>::iterator类型:

#include<iostream>
#include<vector>
using namespace std;
int main() {
	vector<int> v1(5, 1);
	vector<int> v2(2, 2);
	v1.insert(v1.begin(),v2.begin(), v2.end());
	for (auto ch : v1) {
		cout << ch;
	}
	cout << endl;//2211111
}

在这里插入图片描述
②另外两个迭代器是list < i n t > <int> <int>::iterator类型:
注意包含头文件#include < l i s t > <list> <list>

#include<iostream>
#include<vector>
#include<list>
using namespace std;
int main() {
	vector<int> v1(5, 1);
	list<int> ls(2,2);
	v1.insert(v1.begin(), ls.begin(), ls.end());
	for (auto ch : v1) {
		cout << ch;
	}
	cout << endl;//2211111
}

在这里插入图片描述
③另外两个迭代器是string::iterator类型:

#include<iostream>
#include<vector>
using namespace std;
int main() {
	vector<int> v1(5, 1);
	string str("22");//字符'2'的ASCII码值为50
	v1.insert(v1.begin(), str.begin(), str.end());
	for (auto ch : v1) {
		cout << ch;
	}
	cout << endl;//不是2211111,而是505011111
}

在这里插入图片描述
注意:string内部的元素是char类型元素,当char为数字字符时,转换为int类型后会变成该字符对应的ASCII码值。对于上面的程序,数字字符’2’的ASCII码值为50(数字字符’0’的ASCII码值为28),转换成int类型的元素就是50,因此打印出来的不是2211111,而是505011111。
④另外两个迭代器是数组指针类型:

#include<iostream>
#include<vector>
using namespace std;
int main() {
	vector<int> v1(5, 1);
	int myInt[] = { 2,2,2 };
	v1.insert(v1.begin(), myInt,myInt+2);
	for (auto ch : v1) {
		cout << ch;
	}
	cout << endl;//2211111
}

在这里插入图片描述

☀️11.删除相关

🎈(1)pop_back 尾删一个元素

在这里插入图片描述
注意:在数组为空时不能尾删:

int main() {
	vector<int> v1;
	v1.pop_back();
}

在这里插入图片描述

🎈(2)erase 删除内部部分元素

在这里插入图片描述
在这里插入图片描述

🌟函数介绍:
  1. 重载1:删除迭代器指向位置的那个元素。
  2. 重载2:删除两个迭代器之间的元素。注意:会删除掉first指向的那个位置的元素,但last指向的那个位置的元素不会删除。
  3. 返回值是一个迭代器,指向那个或哪些被删除的元素紧接着的有效元素。

思考:为何erase要设置返回值,而不是void?
为了防止迭代器失效。(下一个主题详细说)

🎈 (3)clear 清空所有内容

在这里插入图片描述

☀️12.swap 交换两个数组

在这里插入图片描述

🎈举例使用swap

#include<iostream>
#include<vector>
using namespace std;
int main() {
	vector<int> v1(5, 1);
	vector<int> v2(5, 2);
	v1.swap(v2);
	for (auto ch : v1) {
		cout << ch;
	}
	cout << endl;
	for (auto ch : v2) {
		cout << ch;
	}
	cout << endl;
}

在这里插入图片描述

🌈三、特殊函数:find

vector的成员函数中没有叫find的函数,但是find是一个函数模版,可以用于不同的实例化出来的对象。
在这里插入图片描述

☀️函数介绍:

  1. 返回一个迭代器,迭代器指向范围[first,last)中第一个值等于val的元素。如果找不到这样的元素,函数将返回last。
  2. 函数底层使用operator==函数将各个元素与值进行比较。
  3. find函数模版的各参数间的关系:
    find函数有3个参数:前两个是迭代器,分别指向同一块空间的两个位置,限定了进行搜寻的范围;第3个参数是被寻找的那个数据(变量)的引用。迭代器的类型就是,用被寻找的那个数据(变量)的类型,实例化出来的模版的类型。
    (比如数据为int,数据被放进了模版vector中,实例化出来了vector < i n t > <int> <int>,则此时的迭代器类型就是vector < i n t > <int> <int>::iterator)

☀️举例使用在vector < i n t > <int> <int>中使用find

#include<iostream>
#include<vector>
using namespace std;
int main() {
	vector<int> v1;
	for (int i = 0;i < 5;i++) {
		v1.push_back(i);
	}
	vector<int>::iterator it = find(v1.begin(), v1.end(), 4);
	v1.insert(it, 0);
	for (auto ch : v1) {
		cout << ch;
	}
	cout << endl;
}

在这里插入图片描述

注意:常常使用auto来接收迭代器的类型,原因如下:

  1. 因为迭代器类型通常都是类域加Iterator,类域还可能是同数据类型实例化出来的模版,非常长。
  2. 用auto可以接收任何迭代器的类型,由编译器在内部判断,这正符合函数模版的需求,即用同一段代码适用于各种地方。

在这里插入图片描述
常常写成这样:
在这里插入图片描述

思考:为何string不用find函数模版?
因为对于string而言,需要寻找的不仅仅是某一个字符,还有可能是一串子字符串,并且返回的是下标而不是迭代器,因此需要重载多个find函数来满足需求。

🌈四、迭代器失效

  1. 迭代器的底层实际是一个指针,或者对指针的封装。
  2. 迭代器失效的原因有两种,第一种是原先迭代器指向的空间被销毁,一旦继续使用该迭代器,则会引起程序崩溃;第二种是迭代器指向的位置错误。
  3. 使用了插入、扩容函数则可能引起第一种迭代器失效;使用删除相关函数,两种迭代器失效都有可能引起。

☀️1.插入、扩容引起的迭代器失效

  1. 任何有关插入的函数,如resize、reserve、insert、assign、push_back等,都可能会底层变大,从而迭代器指向一段新的更大的空间。
  2. 之前迭代器指向的旧的、小的空间已经被释放掉了。
  3. 如果此时继续使用原来的迭代器,则会因为访问失效空间而引发程序崩溃。

以使用insert函数为例:

#include<vector>
#include<iostream>
using namespace std;
int main()
{
	vector<int> v(10, 1);
	auto it = v.begin();
	cout << *it << endl;
	v.resize(20, 2);
	cout << *it << endl;
}

在这里插入图片描述

🎈解决方法:失效前对迭代器重新赋值。

在插入后的首次使用迭代器前,重新对迭代器赋值,即加上it = v.begin();这句话:

#include<vector>
#include<iostream>
using namespace std;
int main()
{
	vector<int> v(10, 1);
	auto it = v.begin();
	cout << *it << endl;
	v.resize(20, 2);
	it = v.begin();
	cout << *it << endl;
}

在这里插入图片描述

☀️2.删除(erase)引起的迭代器失效

  1. 删除操作有可能引起两种迭代器失效。
  2. 第一种:使用erase函数后,被删除位置会被其他有效数据覆盖,而迭代器指向的位置不会变换,可能迭代器指向位置的数据已不是原先数据,可能指向的位置已经超过了更新后的数组的有效范围。
  3. 第二种:erase函数虽然大部分时候不会缩减底层容量,但有些平台下的特殊时刻,比如删除掉了原来的一半的内容,大幅缩减了对底层空间的占用,平台为了节省空间会缩小底层容量,之前指向后半段空间的迭代器就类似于野指针。
  4. 总体而言,erase操作之后,所有指向原来数组的迭代器所指向的内容已经未知,无法控制。
  5. 为了规避任何可能的危险,规定只要使用了erase函数,则指向原先数组的迭代器统统失效。
  6. 虽然迭代器失效,但是erase函数有返回值,会返回一个指向被删除元素位置的迭代器,可以利用这个返回值,对原先声明好的迭代器重新赋值,这样这个迭代器就又可以用了。

❌错误用法:

#include<vector>
#include<iostream>
using namespace std;
int main()
{
	vector<int> v = { 1,2,3,4,5 };
	vector<int>::iterator it = v.begin();
	v.erase(it);
	it++;
	cout << *it;
}

在这里插入图片描述

🎈解决方法:用erase的返回值重新对迭代器赋值。

#include<vector>
#include<iostream>
using namespace std;
int main()
{
	vector<int> v = { 1,2,3,4,5 };
	vector<int>::iterator it = v.begin();
	it=v.erase(it);
	it++;
	cout << *it;
}

在这里插入图片描述

🌈五、C++动态二维数组

补充:vector类的成员变量是3个迭代器,分别是_start、_finish、_endofstorage。_start指向空间的起始位置、_finish指向空间的结束位置、_endofstorage指向底层空间结束位置。

☀️1.声明数组(数组类型的写法)

  1. 首先,二维数组本质上还是数组,因此用到vector模版;
  2. 其次,二维数组中的每个元素都是一维数组,因此用来实例化的数据类型也是vector类型的。
  3. 如果每个一维数组中存储的是int,则类型表示为:vector<vector < i n t > <int> <int>>
  4. 如果每个一维数组中存储的是char,则类型表示为:vector<vector < c h a r > <char> <char>>

☀️2.初始化数组

分为对外部二维数组的初始化和内部一维数组的初始化。

🎈(1)外部二维数组的初始化

  1. 一定要用resize,不能用reserve。
  2. resize不仅能申请到足够个数的空间,还能初始化。初始化出来的内容就是调用二维数组内部存放的一维数组的类型对应的默认构造函数产生的对象。即调用vector < i n t > <int> <int>的默认构造函数,最终构造出的对象为空数组(_start、_finish、_endofstorage这迭代器类型三个成员变量的值都为nullptr)。
  3. 不用reserve是因为reserve只能申请空间,无法初始化,相当于用完reserve之后,二维数组的每个位置上都放的是随机值,这些随机值无法被识别为一维数组,更无法进行后续的对一维数组初始化工作。
    在这里插入图片描述

🎈(2)内部一维数组的初始化

  1. 如果需要在一维数组开辟空间的同时初始化的话,就用resize函数,比如下面的oj题杨辉三角,必须初始化为0;
  2. 如果不用初始化的话就既可以用resize也可以用reserve。
  3. 注意,如果用resize函数但没有传第二个参数既初始化的值,则系统会调用一维数组储存的数据类型的默认构造函数。
    比如:内部的一维数组类型为vector < i n t > <int> <int>,调用resize时未传第二个参数,给一维数组初始化的内容就是调用int()产生的值,为0。

给内部一维数组初始化为1:
在这里插入图片描述

☀️3.二维数组的遍历

一般的遍历我们用auto for循环,但这本质是将迭代器指向的内容拷贝到用来接收数据的auto类型的变量中。如果也用这种方式遍历二维数组(vector<vector < i n t > <int> <int>>),则需要将vector < i n t > <int> <int>拷贝给用来接收数据的auto类型的变量,这个过程是深拷贝,需要反复地新开辟空间,拷贝构造临时变量,再释放,十分消耗空间和效率。

因此用引用类型的变量来接收数据:

for(const auto& e : vstr)
{
	cout << e << "";
	cout << e[0] << "";
}
cout << endl ;

const可加可不加,加上后保证了字符串不被修改。

☀️4.汉字二维数组

  1. 英文的字符一共也就二百多个,每个字符背后都对应着一个编码(ASCII码值),通过这个编码即可在计算机中表示出来。但对于中国的汉字,有好几万个,该如何在计算机中表示出来呢?
  2. 规定一个汉字由两个英文字符组合而成,因为这种随机组合从而得以产生出好几万种组合方式,每一种组合都代表一个汉字。
  3. 一个汉字代表着一个长度为2的字符串,因此汉字字符串本质上就是二维数组。
  4. 程序中,要打印出(或者访问到)一个完整汉字,就必须连续打印两次(或者连续访问两次),一次只访问一个字符。注意:连续打印表示两次输入间无间隔,无换行符。
    例如访问汉字字符串"(张三)(李四)(王五)"的“张”字、“李”字、“王”字:
#include<vector>
#include<string>
#include<iostream>
using namespace std;
int main()
{
	vector<string> vstr;
	string s1("张三");
	string s2("李四");
	string s3("王五");
	vstr.push_back(s1);
	vstr.push_back(s2);
	vstr.push_back(s3);
	for (auto& e : vstr) {
		cout << "姓名:" << e << '\n';
		cout << "姓:" << e[0] << e[1] << '\n';
	}
	cout << endl;
}

在这里插入图片描述

  1. 如果只打印了一个字符,则无法得到这个汉字。一个汉字由两个字符组成,如果单独打印一个字符,编译器会将这一个字符对应的汉字编码,转换成ASCII编码中的符号,而这个符号就肯定不是汉字了。
    比如将上方程序for循环内的内容改成这样:
    在这里插入图片描述
    打印结果:访问不到汉字
    在这里插入图片描述

  2. 字符可以随机组合,但是这样就如同开盲盒,不知道随机组合的两个字符到底组成的是哪个汉字:

#include<vector>
#include<string>
#include<iostream>
using namespace std;
int main()
{
	vector<string> vstr;
	string s1("张三");
	string s2("李四");
	string s3("王五");
	vstr.push_back(s1);
	vstr.push_back(s2);
	vstr.push_back(s3);
	cout << vstr[0][0] << vstr[0][3] << '\n';
	cout << vstr[0][0] << vstr[1][3] << '\n';
	cout << vstr[0][0] << vstr[2][3] << '\n';
}

在这里插入图片描述

  1. 常见的汉字是两个字符组成的,但这样的组合无法放满所有汉字,因此还有三个字符组成的汉字,但这种汉字多数是不常用的汉字。

🌈oj:找出只出现一次的数字

链接: https://leetcode.cn/problems/single-number/description/
在这里插入图片描述

☀️思路:

遍历一遍数组,将所有元素亦或一下两两相同的数就成0了,最后只剩下那个唯一的不一样的数。

☀️初始代码:

class Solution {
public:
    int singleNumber(vector<int>& nums) {

    }
};

☀️我的提交:

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int i=0;
        for(auto e:nums){
            i^=e;
        }
        return i;
    }
};

🌈oj:用杨辉三角

链接: https://leetcode.cn/problems/pascals-triangle/submissions/510336036
在这里插入图片描述

☀️复习用C语言实现杨辉三角

🎈写法一:定长数组

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<iostream>
using namespace std;
#define N 10
int main()
{
	int num[N][N] = { 0 };
	num[0][0] = 1;
	for (int i = 1;i < N;i++) {
		for (int j = 0;j <= i;j++) {
			num[i][j] += num[i - 1][j];
			if (j - 1 >= 0) {
				num[i][j] += num[i - 1][j - 1];
			}
		}
	}
	for (int i = 0;i < N;i++) {
		for (int j = 0;j <= i;j++) {
			cout << num[i][j] << ' ';
		}
		cout << endl;
	}
}

在这里插入图片描述

🎈写法二:二维变长数组

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<iostream>
using namespace std;
int main()
{
	int size;
	scanf("%d", &size);
	int** num = (int**)calloc(size,sizeof(int*));
	for (int i = 0;i < size;i++) {
		num[i] = (int*)calloc(i+1, sizeof(int*));
		if (i == 0) *(num[i]+i) = 1;
		else {
			int j = 0;
			while (j <= i) {
				if(j<=i-1)
				*(num[i] + j) += *(num[i - 1] + j);
				if (j - 1 >= 0)
				*(num[i] + j) += *(num[i - 1] + (j - 1));
				j++;
			}
		}
	}
	for (int i = 0;i < size;i++) {
		for (int j = 0;j <= i;j++) {
			cout << num[i][j]<<' ';
		}
		cout << endl;
	}
}

在这里插入图片描述

☀️思路:

用二维数组的思想,只不过把二维指针改成
vector < v e c t o r < i n t > > <vector<int>> <vector<int>>

☀️初始代码:

class Solution {
public:
    vector<vector<int>> generate(int numRows) {

    }
};

☀️我的提交

class Solution {
public:
    vector<vector<int>> generate(int numRows) {
        vector<vector<int>> vv;
        vv.resize(numRows);
        for(int i=0;i<numRows;i++){
            vv[i].resize(i+1,0);
            vv[i].front()=vv[i].back()=1;
        }
        for(int i=0;i<numRows;i++){
            for(int j=0;j<vv[i].size();j++){
                if(vv[i][j]==0){
                    vv[i][j]=vv[i-1][j-1]+vv[i-1][j];
                }
            }
        }
        return vv;
    }
};

🎈注意,用resize而不是reserve

  1. 在声明好二维数组(vector<vector < i n t > > <int>> <int>>类型)后,需要给数组开空间并初始化每个空间成为一级指针,而不能只开空间。
  2. 初始化二维数组时,会调用存放的数据类型的默认构造函数,即vector < i n t > <int> <int>的默认构造函数。
  3. vector < i n t > <int> <int>的默认构造函数就是构造空数组的函数,任何数组的名字本质上都是数组指针(一级指针)。
  4. 如果只有空间、不初始化的话,空间内存的随机值无法被当做数组指针(一级指针),就无法进行后续的给一级指针指向的空间初始化并赋值的操作了。
    错误写法:
    在这里插入图片描述

🌈oj:电话号码的字母组合

链接: https://leetcode.cn/problems/letter-combinations-of-a-phone-number/description/
在这里插入图片描述

☀️初始代码:

class Solution {
public:
    vector<string> letterCombinations(string digits) {

    }
};

☀️我的提交:

class Solution {
public:
string numToStr[10]{"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
    void Combine(const string& digits,int di,string cbstr,vector<string>& v){
        if(di==digits.size()){
            v.push_back(cbstr);
            return;
        }
        int num=digits[di]-'0';
        string str=numToStr[num];
        for(int i=0;i<str.size();i++){
            Combine(digits,di+1,cbstr+str[i],v);
        }
    }
    vector<string> letterCombinations(string digits) {
        vector<string> v;
       if(digits.empty())
       return v;
       Combine(digits,0,"",v);
       return v;
    }
};
  • 12
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C++中的vector是一个动态数组,可以根据需要随时调整大小。它是标准库中最常用的容器之一,提供了许多方便的成员函数和操作符重载。 使用vector容器需要包含头文件`<vector>`。 下面是一些vector容器的特点和常用操作: 1. 动态大小:vector可以根据需要动态调整大小,可以在任意位置插入或删除元素。 2. 快速随机访问:vector支持通过索引快速访问元素,时间复杂度为O(1)。 3. 连续存储:vector的元素在内存中是连续存储的,这样可以提高访问效率。 4. 自动内存管理:vector会自动管理内部的动态内存分配和释放,无需手动管理。 5. 范围检查:vector会在访问操作时进行边界检查,确保不越界。 以下是一些常用的vector操作: - `push_back(value)`:在vector末尾添加一个元素。 - `pop_back()`:删除vector末尾的元素。 - `size()`:返回vector中元素的个数。 - `empty()`:判断vector是否为空。 - `clear()`:清空vector中的所有元素。 - `at(index)`:返回指定索引位置的元素,并进行范围检查。 - `front()`:返回第一个元素。 - `back()`:返回最后一个元素。 - `insert(iterator, value)`:在指定位置插入一个元素。 - `erase(iterator)`:删除指定位置的元素。 - `begin()`和`end()`:返回指向vector第一个元素和最后一个元素之后的迭代器,用于循环遍历。 vector容器提供了丰富的功能,并且易于使用,适合在需要动态大小和快速访问的情况下使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值