初始vector类(上篇)

目录

一.序言

二.vector类常用接口说明

1.vector类的常见构造

1.构造空的vector对象

2.用n个相同元素初始化vector

3.用迭代器构造

4.拷贝构造

2.vecotr类的迭代器

3.vector类的空间管理

4.vector类的增删查改

三.有关vector类的题目

1.只出现一次的数字

2.杨辉三角 

3.删除有序数组中的重复项 

4.数组中出现超过一半的数字

5.只出现一次的数字(II) 

6.只出现一次的数字(III)

7.电话号码的数字组合


一.序言

本质来讲,vector类就是我们之前数据结构所实现的顺序表.

和数组不同,它的大小是可以自动进行调节,而不是固定不变,并且由容器来管理内存,和数组,

String一样,都可以通过[ ]高效进行访问数据.

我们同样可以去cplusplus.com网站查看有关vector类更为详细的官方介绍

有了string类的基础,可以说上手vector类的成本是很低的,并且由于vector类是属于STL库的,相

比于string类来说,重复繁杂的接口反而减少了很多. 

注意:在使用vector对象的时候,同样需要包含#include头文件以及using namespace std(直接展开或间接展开);

后面的程序可能有部分省略了这部分代码.

二.vector类常用接口说明

1.vector类的常见构造

1.构造空的vector对象

 

由于是模板实现,所以vector类创建的对象,可以是任意类型(其实就是顺序表中所存元素可以是任

意类型),只需要创建对象的时候,显示指定即可.

vector <int> first;

2.用n个相同元素初始化vector

//用10个1,给vector类初始化
vector <int> second(10, 1);

3.用迭代器构造

注意是左闭右开区间,按照对象迭代器的顺序进行构造.

vector <int> third(second.begin(), second.end());

4.拷贝构造

利用另一个对象进行拷贝构造对象,顺序保持不变.

比如可以利用上面的third对象创建fourth对象.

vector <int> fourth(third);

2.vecotr类的迭代器

和String类几乎完全相同,vector类也提供begin,end等等迭代器,同时也支持函数重载,所以

cbegin,cend等等函数其实不常用. 

和String类似,vector类的迭代器,我们依旧可以把它看作是一个指针,然后对它进行解引用,移

动等等操作.

比如说下面的代码,就可以输出vector对象相应的值.

vector <int> v(10,1);
vector <int>::iterator it = v.begin();
while (it != v.end())
{
	cout << *it;
	it++;
}
cout << endl;

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

当然如果要反向进行遍历,调用rbegin,rend搭配使用也是可以的.

vector <int> v;
for (size_t i = 0; i < 10; ++i)
	v.push_back(i);
cout << "my vector contains:";
auto rit = v.rbegin();
while (rit != v.rend())
{
	cout << " " << * rit ;
	rit++;
}
cout << endl;

 

3.vector类的空间管理

和String类类似,vector类也提供了类似的接口.

在用法和功能上上,和String类几乎完全一样.

这里只简单挑reserve接口进行简单讲解.

reserve接口也和String的类似,当调整的空间小于当前空前容量,其实没有任何变化,如果需要空

间大于当前空间容量,才进行扩容,我们同样可以用之前类似的小程序进行验证.

    size_t sz = 0;
	vector <int> v;
	for (size_t i = 0; i < sz; ++i) 
		v.push_back(i);
	sz = v.capacity();
	cout << "making v grow:\n";
	for (int i = 0; i < 100; i++)
	{
		v.push_back(i);
		if (sz != v.capacity())
		{
			sz = v.capacity();
			cout << "capacity changed: " << sz << endl;
		}
	}


	vector<int> bar;
	sz = bar.capacity();
	bar.reserve(100);   // this is the only difference with v above
	cout << "making bar grow:\n";
	for (int i = 0; i < 100; ++i) {
		bar.push_back(i);
		if (sz != bar.capacity()) 
		{
			sz = bar.capacity();
			cout << "capacity changed: " << sz << '\n';
		}
	}

如果可以提前知道大概需要多少空间,提前将容量设置好,避免边插入边扩容效率低

PS:在vs下空间大致按照1.5倍进行扩容,而Linux下大致按照2倍扩容.

4.vector类的增删查改

具体用法和String类的接口类似,这里不再过多讲解. 

值得注意的是,我们注意到vector类本身并没有提供find接口,那对于insert,erase等等接口来

说,那找pos位置,进行插入,删除应该如何操作呢?

答案是库里面算法有专门find接口提供.

虽然我们可以看到,它的实现其实还是逐一进行遍历,但至少给我们编写程序提供便捷.

//C++11提供的使用列表进行初始化
vector <int>v{ 1,2,3,4 };
auto pos = find(v.begin(), v.end(), 3);
if (pos != v.end())
{   
    //在pos位置前插入100
	v.insert(pos, 100);
}

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

//重新返回迭代器,否则会报错
pos = find(v.begin(), v.end(), 3);
// 删除pos位置的数据
v.erase(pos);

it = v.begin();
while (it != v.end()) {
	cout << *it << " ";
	++it;
}
cout << endl;

三.有关vector类的题目

1.只出现一次的数字

136. 只出现一次的数字 - 力扣(LeetCode)

严格来说,其实这和vector类的运用关系不多,主要涉及vector的遍历,关键思想反而是异或的运

用.

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

2.杨辉三角 

118. 杨辉三角 - 力扣(LeetCode)

class Solution {
public:
    vector<vector<int>> generate(int numRows) {
         //构建一个vector类,并且每个元素类型为vector<int>
         vector <vector<int>> vv;
         vv.resize(numRows,vector<int>());
         //初始化,并且给每个vector的开头或者结尾赋上相应的值1
         for (size_t i = 0;i < numRows;++i)
         {
             vv[i].resize(i + 1,0);
             vv[i][0] = vv[i][vv[i].size() - 1] = 1;
         }
         //遍历二维数组每一个元素,并求解相应位置的值
         for (size_t i = 0;i < vv.size();++i)
         {
             for (size_t j = 0;j < vv[i].size();++j)
             {
                 if (vv[i][j] == 0)
                    vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1];
             }
         }
         return vv;
    }
};

 有了C++的vector类,二维数组在我们这里,得到大大的简化,最起码的一点是,我们再也不用像

之前一样,和二级指针打交道.

虽然这道题用C也同样可以实现,但在可读性上,明显C++的优势开始发挥出来.

int** generate(int numRows, int* returnSize, int** returnColumnSizes){
    //每行有多少个元素,需要一个一维数组返回,所以传进来二级指针
    *returnColumnSizes = (int*)malloc(sizeof(int)*numRows);
    //记录数组行数
    *returnSize = numRows;
    //创建新矩阵,每个元素都是一个一级指针,指向一个一维数组
    int** anw = (int**)malloc(sizeof(int*)*numRows);
    for (int i = 0;i < numRows;++i)
    {   
        //对应每一行的列数,记录下来
        (*returnColumnSizes)[i] = i + 1;
        //每一行分配一个一维数组,对应长度为行数+1
        anw[i] = (int*)malloc(sizeof(int)*(i + 1));
        //一维数组对应首尾赋值为1
        anw[i][0] = anw[i][i] = 1;
        //其余位置,由上一行左方与上一行右方相加得到
        for (int j = 1;j < i;++j)
        {
            anw[i][j] = anw[i - 1][j - 1] + anw[i - 1][j];
        }
    }
    return anw;
}

3.删除有序数组中的重复项 

26. 删除有序数组中的重复项 - 力扣(LeetCode)

严格来说,这道题,也不是主要考察vector类的知识,其本质考查的是快慢指针的知识,设立slow

慢指针,始终指向新数组的最后一个位置,fast指针在前面找不同元素,和快排的快慢指针实现倒

是非常相近.

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        if (nums.size() < 1)  return 0;
        int slow = 0;
        for (int i = 1; i < nums.size(); i++)
        {
            if (nums[slow] != nums[i])
            {
                nums[++slow] = nums[i];
            }
        }
        return (slow + 1);
    }
};

4.数组中出现超过一半的数字

数组中出现次数超过一半的数字_牛客题霸_牛客网 (nowcoder.com)

众数也就是出现次数最多的数字,换句话说,就是数量最多的数字,假如比拼人数的话,其它所有

数字都比不过它.

因此我们可以假定第一个数字是众数,并且给一个计数器cnt,初始为1,如果遇到相同的数字,

那计数器增加,否则减少,一旦减为0,则这个数字至少在现在不可能是我们的众数,我们进行相

应调整,那存活到最后的数,就是我们要求的众数.

class Solution {
public:
    int MoreThanHalfNum_Solution(vector<int> numbers) {
        size_t mode = numbers[0];
        size_t cnt = 1;
        for (size_t i = 1;i < numbers.size();++i)
        {
            //如果和当前假定众数相同,则计数+1
            if (numbers[i] == mode)   cnt++;
            //不同,则计数-1
            else  cnt--;
            //由于保证有结果,所以众数计数绝对最后不为0
            if (cnt == 0)
            {
                mode = numbers[i];
                cnt = 1;   
            }
        }
        return mode;
    }
};

5.只出现一次的数字(II) 

260. 只出现一次的数字 III - 力扣(LeetCode)

题目要求不能开额外空间,也就是限定哈希表不能使用,同时线性时间复杂度,也就是不能进行排

序算法.

一种较为简单的思路,就是将每一个数字,都看成二进制的形式,那对于一个数字来说,每一位不

是1就是0,对于出现3个相同数字来说,该位至少会出现3个1或者3个0以上,如果对所有数的相同

位统计1的个数,再取余,得到剩余的1,必定是只出现1的数所提供的.

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int anw = 0;
        //一个int类型的数字总共有32位
        for (size_t i = 0;i < 32;++i)
        {   
            //记录每一位数字总共出现多少个1
            int cnt = 0;
            //遍历nums数组中的每一个数,统计每一位对应的1的个数有多少
            for (auto e:nums)
                cnt += ((e >> i) & 1);
            //假如有多出来的1,说明该1必是从仅出现1次数字第i位所得到的
            if (cnt % 3 == 1)
               anw |= (1 << i);
        }
        return anw;
    }
};

还有另外一种思路,不过需要一定的数电知识.

同样将每一个数字,都看成二进制的形式,求解不同的那个数字,我们可以将它看做解决三个相同

的数,怎么运算使其等于0.那剩下的那个数字就是唯一的数字.

对于这三个数的每一位来说,会有以下的转换表达式.

用0,1,2分别代表现在积累的1的个数,当遇到当前位是0,不会改变状态,当遇到当前位是1,则改

变状态.

但我们知道,单纯的0,1是无法表示三个状态,,因此我们用两位表示这三种状态.(00,01,10),11

状态舍弃不用.

那上面的状态图就可以表示为这样一种形式.

运用卡诺图,分别化简,得到新的ai,bi的表达式.

具体如何用卡诺图化简得到表达式,可以参考这篇文章.

(62条消息) 逻辑函数常用的描述方法及相互间的转化_逻辑函数的五种表示方法_·present·的博客-CSDN博客

 

将最后得到的结果,转换成程序即可.

PS:

1.当我们遍历完数组中的所有元素后,每一位要么是00,要么是01,所以结果返回b即可.

2.a.b需要同时更新,所以需要用临时变量辅助.

class Solution {
public:
    int singleNumber(vector<int>& nums) {
        int a = 0,b = 0;
        for (auto e:nums)
        {
            int new_a = (~a & b & e) | (a & ~b & ~e);
            int new_b = ~a & (b ^ e);
            a = new_a;
            b = new_b;
        }
        return b;
    }
};

6.只出现一次的数字(III)

260. 只出现一次的数字 III - 力扣(LeetCode)

这道题难度有点大,不太好想,需要做过第一道题后,才可能会有一点思路.

关键是如何分别将只出现一次的数字分到两个组,具体步骤,可以看代码理解.

PS:条件判断需要注意加括号,有符号优先级问题存在

class Solution {
public:
    vector<int> singleNumber(vector<int>& nums) {
        //求出两个只出现1次的数的异或结果ret
        int ret = 0;
        for (auto e : nums)
        {
            ret^=e;
        }
        //由于两个只出现一次的数不同,则ret必定有一位是1
        int index = 0;
        while (((ret >> index) & 1) == 0)
        {
            index++;
        }
        //遍历数组,该位为1的分到一组,异或;反之该位为0的分到一组,异或
        int num1 = 0,num2 = 0;
        for (auto e :nums)
        {
            if (((e >> index) & 1) == 1)
            {
                num1 ^= e;
            }
            else
            {
                num2 ^= e;
            }
        }
        vector<int> anw;
        anw.push_back(num1);
        anw.push_back(num2);
        return anw;
    }
};

7.电话号码的数字组合

17. 电话号码的字母组合 - 力扣(LeetCode)

这道题本身难度有点大,主要有几个难点:

第一,按键数字字数是不确定的,因此用循环来实现难度会比较大,至少难以确定循环多少层.

第二,返回类型是一个vector<string>的自定义类型变量,对于初学vector类的人来说,有时候比较

难理解其内部的实际结构是什么.

比较简单的一种想法是用递归去实现,由于题目提供的letterCombinations函数是用来返回答案

的,因此递归函数,我们要单独进行实现.

如何设置参数呢?设置多少个参数呢?这都是我们需要考虑的.

首先,肯定要传引用的vector<string>对象,还有层数level

其次,每一次递归,我们还需要一个String接收遍历得到的字符

最后,还有digits数组,不然我们怎么得到相应数字对应得字母串呢?

递归终止条件比较好确定,当层数和digits字母串大小相同的时候,比如说“23”,那此时层数已经为

2,就不需要再往下递归了,可以把String压入我们的vector<string>对象中.

class Solution {
    //数字有相应的字母串,0和1不对应任何字母串
    string numtoStr[10] = {"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
public:
    void Combinations(string CombineStr,size_t level,string digits,vector<string>&vv)
    {    
         //假设层数已经达到相应的字符长度,递归停止
         if (level == digits.size())
         {
             vv.push_back(CombineStr);
             return;
         }
         //找出当前层所对应的数字
         int num = digits[level] - '0';
         //取出其对应的字符串
         string str = numtoStr[num];
         //递归遍历该字符串
         for (auto ch: str)
         {
             Combinations(CombineStr + ch,level + 1,digits,vv);
         }
    }
    vector<string> letterCombinations(string digits) {
        vector<string> vv;
        if (digits.size() == 0)  return vv;
        Combinations("",0,digits,vv);
        return vv;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值