【C++第九课 - vector】vector介绍、vector使用,vector的底层实现、杨辉三角、全排列、只出现一次的数字

目录

  • 一、vector的介绍
  • 二、vector的使用
      • 1、vector的构造函数
      • 2、vector的插入和三种遍历方式
      • 3、开空间
      • 4、insert
      • 5、find
      • 6、erase
      • 补充
  • 三、vector的底层实现
      • 1、成员变量
      • 2、构造函数
      • 3、push_back
      • 4、访问方式
      • 5、pop_back
      • 6、insert - pos位置插入x
      • 7、resize
      • 8、拷贝构造
      • 9、赋值
      • 10、erase
  • 四、迭代器失效
      • 1、insert在pos位置插入时,发生扩容,pos这个迭代器就失效了
      • 2、erase进行删除的时候会发生迭代器失效
  • 五、vector和string的优势
  • 四、算法题
      • 1、电话号码的字母组合 -- 全排列问题
      • 2、只出现一次的数字
      • 3、杨辉三角
      • 4、删除排序数组中的重复项
      • 5、数组中出现次数超过一半的数字

上次课回顾
windows和Linux下默认的string类型的大小
(1)windows
按理来说,大小应该是12,但是运行的结果却又28
在这里插入图片描述
在这里插入图片描述
多了个_Buf数组,这个数组大小是16,可以存15个字符
所以s1和s2的大小不是12,而是18
在这里插入图片描述
对于_Buf这个字符数组,如果string的_str的大小小于等于15就存在_Buf里面,如果大于15则_Buf这个字符数组就废弃重新开辟空间。目的:防止频繁开辟小空间
在这里插入图片描述
(2)Linux
Linux默认64位,默认release版本
上述代码放到Linux里面,大小就是八字节,也就是string里面就放了一个指针
在这里插入图片描述
在这里插入图片描述
浅拷贝问题
(1)析构两次 -> 引用计数
当最后一个对象删除时,才会释放这段空间
在这里插入图片描述
(2)一个修改会影响另一个 -> 写时拷贝(也有缺陷)

一、vector的介绍

就是顺序表

string类是一个保存字符的动态数组,由于其中有一个接口c_str,转化成c语言的字符串,要以\0结尾,所以string类最后会有一个\0.
string支持+=
string支持比较大小(通过ascii码)
vector是一个保存T类型的动态数组,vector也是保存字符的动态数组,但是,不会以\0结尾,不保存\0.
vector不支持+=
vector不支持比较大小(也可以通过ascii码比较,但意义不大)
在这里插入图片描述

在这里插入图片描述

二、vector的使用

1、vector的构造函数

在这里插入图片描述
(1)全缺省构造(无参的)

vector<int> v1;

(2)n个value的构造

vector<int> v2(5, 1);

在这里插入图片描述

(3)迭代区间的构造

	vector<int> v1;
	v1.push_back(1);
	v1.push_back(2);
	v1.push_back(3);
	v1.push_back(5);
	vector<int> v3(v1.begin(), v1.end());

在这里插入图片描述

2、vector的插入和三种遍历方式

(1)、下标

#include<iostream>
#include<vector>
using namespace std;

int main()
{
	vector<int> v1;
	v1.push_back(1);
	v1.push_back(2);
	v1.push_back(3);
	v1.push_back(4);
	v1.push_back(5);

	for (int i = 0; i < v1.size(); i++)
	{
		cout << v1[i];
	}
	cout << endl;


	return 0;
}

(2)、迭代器

#include<iostream>
#include<vector>
using namespace std;

int main()
{
	vector<int> v1;
	v1.push_back(1);
	v1.push_back(2);
	v1.push_back(3);
	v1.push_back(4);
	v1.push_back(5);

	vector<int>::iterator t = v1.begin();
	while (t != v1.end())
	{
		cout << *t;
		++t;
	}
	cout << endl;

	return 0;
}

(3)、范围for

#include<iostream>
#include<vector>
using namespace std;

int main()
{
	vector<int> v1;
	v1.push_back(1);
	v1.push_back(2);
	v1.push_back(3);
	v1.push_back(4);
	v1.push_back(5);

	for (auto e : v1)
	{
		cout << e;
	}
	cout << endl;

	return 0;
}

3、开空间

(1)、开空间reserve

void test2()
{
	vector<int> v1;
	int sz = v1.capacity();
	for (int i = 0; i < 100; i++)
	{
		v1.push_back(i);
		if (sz != v1.capacity())
		{
			cout << "capacity: " << v1.capacity() << endl;
			sz = v1.capacity();
		}
	}
}

int main()
{
	test2();
	return 0;
}

在这里插入图片描述
1.5倍扩容,Linux一般是二倍扩容
减少频繁扩容的方式,使用reserve,而不使用resize,因为resize不仅扩容还会初始化那么再插入的时候还需要扩容
在这里插入图片描述

(2)、开空间加初始化resize

vector的resize不缩容,缩容用Shrink_to_fit

在这里插入图片描述

void test2()
{
	vector<int> v1;
	v1.reserve(100);
	int sz = v1.capacity();
	for (int i = 0; i < 100; i++)
	{
		v1.push_back(i);
		if (sz != v1.capacity())
		{
			cout << "capacity: " << v1.capacity() << endl;
			sz = v1.capacity();
		}
	}
	v1.resize(10);
	cout << "capacity: " << v1.capacity() << endl;
	cout << "size: " << v1.size() << endl;
}

在这里插入图片描述

4、insert

在这里插入图片描述
(1)在某一位置插入val

void test3()
{
	vector<int> v1;
	v1.push_back(1);
	v1.push_back(2);
	v1.push_back(3);
	v1.push_back(5);
	for (auto e : v1)
	{
		cout << e << " ";
	}
	cout << endl;

	v1.insert(v1.begin(), 10);
	for (auto e: v1)
	{
		cout << e <<" ";
	}
	cout << endl;
}

在这里插入图片描述
2、在某一位置插入n个val

void test3()
{
	vector<int> v1;
	v1.push_back(1);
	v1.push_back(2);
	v1.push_back(3);
	v1.push_back(5);
	for (auto e : v1)
	{
		cout << e << " ";
	}
	cout << endl;

	v1.insert(v1.begin(), 2,10);
	for (auto e: v1)
	{
		cout << e <<" ";
	}
	cout << endl;
}

在这里插入图片描述
(3)在某一位置插入一个迭代区间

	v1.insert(v1.begin(), v2.begin(), v2.end());
	cout << "v1:";
	for (auto e : v1)
	{
		cout << e << " ";
	}
	cout << endl;

在这里插入图片描述

5、find

vector没有find,用的是算法里面的find

string为何要自己写find
(1)string查找的更为复杂,不仅要查一个字符还有可能查一个字串
(2)string返回下标更合适

在这里插入图片描述

void test4()
{
	vector<int> v1;
	v1.push_back(1);
	v1.push_back(2);
	v1.push_back(3);
	v1.push_back(4);
	for (auto e : v1)
	{
		cout << e << " ";
	}
	cout << endl;

	v1.insert(find(v1.begin(), v1.end(), 2), 20);

	for (auto e : v1)
	{
		cout << e << " ";
	}
	cout << endl;

}

在这里插入图片描述

6、erase

补充

vector不支持流插入和流提取
因为vector打印时每个之间用什么隔开自己决定
string如果没有特殊需求一般打印的时候都是连着

三、vector的底层实现

相比于之前string的直接定义_a、_size、_capacity,这里的vector是间接定义的
在这里插入图片描述

1、成员变量

vector不使用下标,只使用迭代器进行访问、插入等

private:
		iterator start;
		iterator finish;
		iterator end_of_storage;

2、构造函数

3、push_back

namespace zyh
{
	template<class T>
	class vector
	{
	public:
		typedef T* iterator;
		typedef const T* const_iterator;
		vector()
			:start(nullptr)
			,finish(nullptr)
			,end_of_storage(nullptr)
		{}
		size_t size()
		{
			return finish - start;
		}
		size_t capacity()
		{
			return end_of_storage - start;
		}
		void reverse(size_t n)
		{
			if (n > capacity())
			{
				size_t oldsize = size();
				T* tmp = new T[n];
				if (start)
				{
					memcpy(tmp, start, old * sizeof(T));
				}
				delete[] start;
				start = tmp;
				finish = start + oldsize;
				end_of_storage = start + n;
			}
		}
		void push_back(const T& x)
		{
			if (finish == end_of_storage)
			{
				size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;
				reverse(newcapacity);
			}
			*finish = x;
			finish++;
		}
		iterator begin()
		{
			return start;
		}
		iterator end()
		{
			return finish;
		}
		const_iterator begin() const
		{
			return start;
		}
		const_iterator end() const
		{
			return finish;
		}
	private:
		iterator start;
		iterator finish;
		iterator end_of_storage;

4、访问方式

迭代器

iterator begin()
		{
			return start;
		}
		iterator end()
		{
			return finish;
		}
		const_iterator begin() const
		{
			return start;
		}
		const_iterator end() const
		{
			return finish;
		}
		vector<int>::iterator it1 = v1.begin();
		while (it1 != v1.end())
		{
			std::cout << *it1 << " ";
			it1++;
		}
		std::cout << std::endl;

范围for

		v1.push_back(5);
		for (auto v : v1)
		{
			std::cout << v << " ";
		}
		std::cout << std::endl;

[]

		T& operator[](size_t pos)
		{
			assert(pos < size());
			return start[pos];
			//return *(start + pos);
		}
		v1.push_back(6);
		for (int i = 0; i < v1.size(); i++)
		{
			std::cout << v1[i] << " ";
		}
		std::cout << std::endl;

5、pop_back

		void pop_back()
		{
			assert(size() > 0);
			--finish;
		}

6、insert - pos位置插入x

		void insert(iterator pos,const T& x)
		{
			assert(pos >= start && pos <= finish);
			if (finish == end_of_storage)
			{
				reverse(capacity() == 0 ? 4 : capacity() * 2);
			}
			memmove(pos + 1, pos, sizeof(T) * (finish - pos));
			++finish;
			*pos = x;
		}

问题:扩容时,pos迭代器失效
在这里插入图片描述

		void insert(iterator pos,const T& x)
		{
			assert(pos >= start && pos <= finish);
			int len = pos - start;
			if (finish == end_of_storage)
			{
				reverse(capacity() == 0 ? 4 : capacity() * 2);
				pos = start + len;
			}
			memmove(pos + 1, pos, sizeof(T) * (finish - pos));
			++finish;
			*pos = x;
		}

7、resize

capacity和size都要变
在这里插入图片描述

T()是个匿名对象,因为要给一个缺省值,又因为T是不确定的,因此使用匿名对象
在模板里面无论是内置类型还是自定义类型都是可以初始化的

		void resize(size_t n, T val = T())
		{
			if (n <= size())
				finish = start + n;
			else if (n > size() && n <= capacity())
			{
				while (finish != start + n)
				{
					*finish = val;
					++finish;
				}
			}
			else
			{
				reverse(n);
				while (finish != start + n)
				{
					*finish = val;
					++finish;
				}
			}
		}

8、拷贝构造

		//拷贝构造
		vector(const vector<T>& v)
		{
			reverse(v.capacity());
			for (const auto& i : v)
			{
				push_back(i);
			}
		}

1、因为vconst的,所以在使用范围for进行遍历的时候注意也要是const auto

9、赋值

下面这样写是不对的,全局的swap两个变量都不能有const的
在这里插入图片描述
在这里插入图片描述

		vector<T>& operator=(vector<T> v)
		{
			//swap(v);
			std::swap(start, v.start);
			std::swap(finish, v.finish);
			std::swap(end_of_storage, v.end_of_storage);
			return *this;
		}

10、erase

		void erase(iterator pos)
		{
			assert(pos < finish);
			assert(pos >= start);
			iterator it = pos + 1;
			while (it < finish)
			{
				*(it - 1) = *it;
				++it;
			}
			finish--;
		}

四、迭代器失效

insert和erase形参pos都可能会失效
原则是insert和erase过的迭代器不要使用

1、insert在pos位置插入时,发生扩容,pos这个迭代器就失效了

2、erase进行删除的时候会发生迭代器失效

erase有一个返回值,返回的是刚刚删除元素的下一个位置

删除所以元素中的偶数

五、vector和string的优势

1、尾插和尾删
2、随机访问

四、算法题

1、电话号码的字母组合 – 全排列问题

class Solution {
public:
    string num2str[10] = {"","","abc","def","ghi","jkl","mno","pqrs","tuv", "wxyz"};
    void combinstr(string& digits, int dinum, string str, vector<string>& v)
    {
        if(dinum == digits.size())
        {
            v.push_back(str);
            return;
        }
        int num = digits[dinum] - '0';
        string numstr = num2str[num];
        for(int i = 0; i < numstr.size(); i++)
        {
            combinstr(digits, dinum + 1, str + numstr[i], v);
        }
        

    }
    vector<string> letterCombinations(string digits) {
        vector<string> v;
        if(digits.size() == 0)
            return v;
        combinstr(digits, 0, "", v);
        return v;
    }
};

在这里插入图片描述

2、只出现一次的数字

对于此题的解法:就是异或^
0和任意数a异或还是a:0^a = a
任意数a和其自身异或为0:a^a = 0;

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int res = 0;
        for(int i = 0; i < nums.size(); i++)
        {
            res = res ^ nums[i];
        }
        return res;
    }
};

3、杨辉三角

&& :逻辑与,两个结果都为真时才为真
|| :逻辑或,两个结果有一个为真则为真

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);
            for(int j = 0; j < i+1; j++)
            {
                if(j == 0 || j == i || i == 0)
                {
                    vv[i][j] = 1;
                }
                else
                {
                    vv[i][j] = vv[i-1][j-1] + vv[i-1][j];
                }
            }
        }
        return vv;
    }
};

改进:
if(j == 0 || j == i || i == 0) { vv[i][j] = 1; }
对于这行代码可以使用vector里面的frontback

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);
            vv[i].front() = vv[i].back() = 1;
            for(int j = 1; j < i; j++)
            {
                vv[i][j] = vv[i-1][j-1] + vv[i-1][j];
            }
        }
        return vv;
    }
};

也可以把所有的位置都初始化为0vv[i].resize(i+1, 0);
在对v[i][j]进行赋值的时候可以直接判断只给0的位置,因为1的位置已经赋值了

4、删除排序数组中的重复项

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

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        if(nums.size() == 0)
            return 0;
        if(nums.size() == 1)
            return 1;
        size_t i = 1;
        size_t j = 0;
        int len = 1;
        while(i < nums.size())
        {
            if(nums[i] == nums[j])
            {
                i++;
            }
            else
            {
                nums[len] = nums[i];
                len++;
                j = i;
                i++;
            }
        }
        return len;
    }
};

对于排序的序列去重使用双指针

5、数组中出现次数超过一半的数字

在这里插入图片描述

使用两个指针遍历数组,当两个指针指向的内容不同时就删除,最后留下的就是多个一样的数或一个数

#include <cstddef>
class Solution {
public:
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param numbers int整型vector 
     * @return int整型
     */
    int MoreThanHalfNum_Solution(vector<int>& numbers) {
        // write code here
        size_t len = numbers.size();
        if(len == 1)
            return numbers[0];
        auto i = numbers.begin();
        auto j = numbers.end()-1;
        while(i != j)
        {
            if(*i != *j)
            {
                numbers.erase(j);
                numbers.erase(i);
                j = numbers.end() - 1;
                i = numbers.begin();
            }
            else
            {
                i++;
            }

        }
        return numbers[0];
    }
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/692804.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【第13章】SpringBoot实战篇之项目部署

文章目录 前言一、准备1. 引入插件2. 打包3. 启动4. 后台启动 二、跳过测试模块三、外置配置文件1.引入插件2.忽略配置文件3. 外置配置文件 总结 前言 项目部署需要把项目部署到Linux服务器上&#xff0c;SpringBoot项目通过Maven打包即可快速生成可运行Jar包程序。 一、准备 …

每日一题——Python实现PAT乙级1042 字符统计(举一反三+思想解读+逐步优化)

一个认为一切根源都是“自己不够强”的INTJ 个人主页&#xff1a;用哲学编程-CSDN博客专栏&#xff1a;每日一题——举一反三Python编程学习Python内置函数 Python-3.12.0文档解读 目录 我的写法 优点 缺点和改进建议 时间复杂度分析 空间复杂度分析 改进后的代码 我…

【Androi】安卓发展历程详解

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 目录 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌…

轻NAS玩客云使用Docker部署小雅并挂载到AList详细流程分享

文章目录 前言1. 本地部署AList2. AList挂载网盘3. 部署小雅alist3.1 Token获取3.2 部署小雅3.3 挂载小雅alist到AList中 4. Cpolar内网穿透安装5. 创建公网地址6. 配置固定公网地址 前言 本文主要介绍如何在安装了CasaOS的玩客云主机中部署小雅AList&#xff0c;并在AList中挂…

YOLOv8_obb的训练、验证、预测及导出[旋转目标检测实践篇]

1.旋转目标检测数据集划分和配置 从上面得到的images和labels数据还不能够直接训练,需要按照一定的比例划分训练集和验证集,并按照下面的结构来存放数据,划分代码如下所示,该部分内容和YOLOv8的训练、验证、预测及导出[目标检测实践篇]_yolov8训练测试验证-CSDN博客是重复的…

LNMP与动静态网站介绍

Nginx发展 Nginx nginx http server Nginx是俄罗斯人 Igor Sysoev(伊戈尔.塞索耶夫)开发的一款高性能的HTTP和反向代理服务器。 Nginx以高效的epoll.kqueue,eventport作为网络IO模型&#xff0c;在高并发场景下&#xff0c;Nginx能够轻松支持5w并发连接数的响应&#xff0c;并…

Redis单线程运行与CPU多核心的关系

Redis单线程运行与CPU多核心的关系 Redis作为一种高性能的内存数据库&#xff0c;以其单线程的运行模式而闻名。在高并发的场景下&#xff0c;单线程模型有助于简化开发和避免竞争条件。然而&#xff0c;随着多核CPU的普及&#xff0c;人们不禁要问&#xff0c;Redis的单线程模…

FJSP:烟花算法(FWA)求解柔性作业车间调度问题(FJSP),提供MATLAB代码

一、烟花算法介绍 参考文献&#xff1a; Tan, Y. and Y. Zhu. Fireworks Algorithm for Optimization. in Advances in Swarm Intelligence. 2010. Berlin, Heidelberg: Springer Berlin Heidelberg. 二、烟花算法求解FJSP 2.1FJSP模型介绍 柔性作业车间调度问题(Flexible …

医疗器械网络安全风险管理的基本步骤

医疗器械网络安全风险管理是一个复杂的过程&#xff0c;涉及到多个环节和步骤。以下是一些基本的步骤和关键点&#xff1a; 风险识别&#xff1a;首先需要对医疗器械的软件、网络连接和通信协议等进行漏洞分析&#xff0c;识别潜在的安全漏洞和弱点。这可能涉及对设备的渗透测…

LLVM Cpu0 新后端7 第一部分 DAG调试 dot文件 Machine Pass

想好好熟悉一下llvm开发一个新后端都要干什么&#xff0c;于是参考了老师的系列文章&#xff1a; LLVM 后端实践笔记 代码在这里&#xff08;还没来得及准备&#xff0c;先用网盘暂存一下&#xff09;&#xff1a; 链接: https://pan.baidu.com/s/1V_tZkt9uvxo5bnUufhMQ_Q?…

Nagios的安装和使用

*实验* *nagios安装和使用* Nagios 是一个监视系统运行状态和网络信息的监视系统。Nagios 能监视所指定的本地或远程主机以及服务&#xff0c;同时提供异常通知功能等. Nagios 可运行在 Linux/Unix 平台之上&#xff0c;同时提供一个可选的基于浏览器的 WEB 界面以方便系统管…

【Linux系统编程】进程地址空间

目录 前言 进程虚拟地址空间的引入 进程地址空间的概念 进一步理解进程地址空间 为什么需要进程地址空间&#xff1f; 系统层面理解malloc/new内存申请 前言 首先&#xff0c;在我们学习C语言的时候一定会见过如下这张图。&#xff08;没见过也没关系&#xff0c;接下来…

stm32最小系统焊接调试总结

stm32最小系统打板后,接下来开始焊接元器件,焊接元器件可以参考立创EDA焊接辅助工具。 图1 焊接辅助助手 焊接准备工具有,焊台,放大镜,元器件,镊子,焊锡膏,锡丝及万用表等。调节焊台温度到350-400摄氏度。焊接顺序是先焊接USB typec接口,5V电源,ldo,ch340,stm32芯片…

【Python】一文向您详细介绍 __str__ 的作用和用法

【Python】一文向您详细介绍 str 的作用和用法 下滑即可查看博客内容 &#x1f308; 欢迎莅临我的个人主页 &#x1f448;这里是我静心耕耘深度学习领域、真诚分享知识与智慧的小天地&#xff01;&#x1f387; &#x1f393; 博主简介&#xff1a;985高校的普通本硕&…

Linux -- 正则表达式基础

提示&#xff1a;制作不易&#xff0c;可以点个关注和收藏哦。 前言 虽然我们这一节的标题是正则表达式&#xff0c;但实际这一节实验只是介绍grep&#xff0c;sed&#xff0c;awk这三个命令&#xff0c;而正则表达式作为这三个命令的一种使用方式&#xff08;命令输出中可以包…

一个简单的threejs盒剖切功能

支持六面方向拖拽、反向、切面填充. 代码&#xff1a; import * as THREE from three import { MouseHandler } from src/renderers/input/mouse import {mergeGeometries} from three/examples/jsm/utils/BufferGeometryUtils import {BaseHandle} from ./base import {HANDL…

MathType7永久破解免费版下载最新2024

“数学公式”作为学术和科普写作中不可或缺的一环&#xff0c;一直困扰着很多作者。 在Word等文本编辑器中&#xff0c;虽然提供了插入公式的功能&#xff0c;但使用起来却并不友好&#xff0c;不仅效率低下&#xff0c;而且在调整格式时也会遇到各种问题。而MathType公式编辑器…

【Python机器学习】PCA——特征提取(2)

上一篇写过了用单一最近邻分类器训练后的精度只有0.22. 现在用PCA。想要度量人脸的相似度&#xff0c;计算原始像素空间中的距离是一种相当糟糕的方法。用像素表示来比较两张图像时&#xff0c;我们比较的是每个像素的灰度值与另一张图像对应位置的像素灰度值。这种表示与人们…

flask实现抽奖程序(一)

后端代码E:\LearningProject\lottery\app.py from flask import Flask, render_template import randomapp Flask(__name__)employees [赵一, 钱二, 孙三, 李四, 周五, 吴六, 郑七, 王八]app.route(/) def hello_world():return render_template(index.html, employeesemplo…

Centos7系统禁用Nouveau内核驱动程序【笔记】

在CentOS系统中,Nouveau是开源的NVIDIA显卡驱动程序,但它与NVIDIA的官方驱动程序NVIDIA Proprietary Driver存在兼容性问题。 如果你想要禁用Nouveau并使用NVIDIA官方驱动,可以按照以下步骤操作: 1、创建一个黑名单文件以禁用Nouveau驱动。 echo blacklist nouveau | su…