本文主利用我们的vector来解决一些OJ题
前三个题目很类似,分别为
- 一个数字只出现一次,其他数字都出现两次
- 两个数字只出现一次,其他数字都出现两次
- 一个数字只出现一次,其他数字都出现三次
文章目录
- 1、[一个只出现一次的数字,其他数字都出现两次](https://leetcode-cn.com/problems/single-number/submissions/)
- 2、[两个只出现一次的数字,其他数字都出现两次](https://leetcode.cn/problems/single-number-iii/)
- 3、[一个只出现一次的数字,其他数字都出现三次](https://leetcode-cn.com/problems/single-number-ii/)
- 4、[删除数组中的重复项](https://leetcode.cn/problems/remove-duplicates-from-sorted-array/)
- 5、[杨辉三角](https://leetcode.cn/problems/pascals-triangle/)
- 6、[电话号码字母组合](https://leetcode.cn/problems/letter-combinations-of-a-phone-number/submissions/)
- 7、[数组中出现次数超过一半的数字](https://www.nowcoder.com/practice/e8a1b01a2df14cb2b228b30ee6a92163?tpId=13&&tqId=11181&rp=1&ru=/activity/oj&qru=/ta/coding-interviews/question-ranking)
1、一个只出现一次的数字,其他数字都出现两次
思路:这个其实很简单,我们使用异或的思想即可。因为两个相同的数异或的结果为0,任何数和0异或都等于他本身。
class Solution {
public:
int singleNumber(vector<int>& nums)
{
int ret = 0;
for(auto e:nums) //遍历数组,将所有数字异或
{
ret^=e;
}
return ret;
}
};
2、两个只出现一次的数字,其他数字都出现两次
思路1:我们使用排序思想。
- 首先将数组的数组进行排序sort()
- 定义一个数组ret存储要返回的这个两个数。
- 然后遍历数组,如果当前的数字和后一个相等,那么就跳两步,如果不行等就将这个数字放在ret里面。向后跳一步继续寻找。
注意:这个循环的截至条件为数组元素的最后一个,为了防止越界。 - 出了循环判断i是否在最后一个位置,如果是,那最后一个元素也是只出现一次的。因为我们使用的是当前数字和后面一个比较的,如果不相等只会向后跳一步。
class Solution {
public:
vector<int> singleNumber(vector<int>& nums)
{
sort(nums.begin(),nums.end());
vector<int> ret;
int i = 0;
//防止越界
for(;i < nums.size() - 1;)
{
if(nums[i] == nums[i+1])
{
i+=2;
}
else
{
ret.push_back(nums[i]);
i+=1;
}
}
//将最后一个元素插入
if(i == nums.size() - 1)
{
ret.push_back(nums[i]);
}
return ret;
}
};
思路2:使用异或思想
- 遍历
nums
,通过异或操作,得到结果s = a ^ b
(a, b
为恰好只出现一次的元素) - 保留s的最后为1的那位保留,其余位置设置为0 ,存入在
k
里面 (如:k = 0000 0100
) - 把
k
和nums
中的每一个元素进行&
操作,可以把nums
中所有元素,划分为两组(如:一组0000 0000
, 二组:0000 0100
,这样a , b
被划分到不同组中,在对每一个组进行异或,即可提取出a 和 b
了)
注意:这个赋值给k时候需要加一个判断条件。因为与运算的结果可能负数的最大值,最高位是符号位
class Solution {
public:
vector<int> singleNumber(vector<int>& nums)
{
int s = 0;
for(int e : nums)
{
s ^= e;
}
int k = (s == INT_MIN ? s :s & (-s)); //因为s是两个单身狗^后的结果,一定有一个为1另外一个为0,使用s&(-s)可以使得这个位为1,其他位为0
//将k与原数组进行&操作,将其分为两组,这样,两个单身狗就被分到两个组了
vector<int> RetNums(2,0);
for(int e : nums)
{
if(k & e)
{
RetNums[0] ^= e;
}
else
{
RetNums[1] ^= e;
}
}
return RetNums;
}
};
3、一个只出现一次的数字,其他数字都出现三次
思路:这个也是用排序的思想,和上面类似,无非是条两步还是跳三步的区别
- 首先将数组进行排序sort()
- 遍历数组,将当前位置和后面一个位置进行比较,如果相等则向后跳三步,否则返回这个位置的值。
注意:这个需要再加一个判断条件,当i在最后一个位置时候也直接返回。因为每次都跳三步,都是跳到下一个不同值元素的位置。当在结尾时候,说明最后一个就是单身狗
//1:先排序
//2:然后遍历数组,如果是最后一个元素,或者当前位置的元素和后面一个不相等,那么就返回当前位置的值,否则,i+=3
//3:如果为空,则返回最后一个
class Solution {
public:
int singleNumber(vector<int>& nums)
{
sort(nums.begin(),nums.end());
for(int i = 0;i < nums.size();i+=3)
{
if(i == nums.size() - 1 || nums[i] != nums[i+1])
{
return nums[i];
}
}
//return nums[nums.size() - 1];
return 0;
}
};
4、删除数组中的重复项
思路:因为是原地修改,我们使用双指针,一个用来遍历数字,一个用来修改数组
- 定义两个数,src=dest=0
- 遍历数组,如果src和dest位置的值相等就将src++,不相等我们才修改数组。(先将dest++,再将src位置的值赋值给dest位置的值。然后src++)
- 最后数组的长度就是dest+1
class Solution {
public:
int removeDuplicates(vector<int>& nums)
{
size_t src = 0;
size_t dest = 0;
for(size_t i = 0;i < nums.size();++i)
{
if(nums[src] == nums[dest])
{
++src;
}
else
{
++dest;
nums[dest] = nums[src];
++src;
}
}
return dest+1;
}
};
5、杨辉三角
思路:使用vector<vector< int >>
最外层的vector其实相当于存的是一堆数组指针,指向的是每个一维数组。里面的vector是一维数组,里面存的是数字。
class Solution {
public:
vector<vector<int>> generate(int numRows)
{
//开空间初始化
vector<vector<int>> vv;
vv.resize(numRows,vector<int>()); //里面初始化为匿名的对象
for(size_t i = 0;i < vv.size();++i)
{
vv[i].resize(i+1,0);
vv[i][0] = 1;
vv[i][vv[i].size() - 1] = 1;
}
for(size_t i = 0;i < vv.size();++i)
{
//遍历每一个vector<int>赋值
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;
}
};
6、电话号码字母组合
思路:其实就是使用多叉树深度遍历的思想。代码不难,逻辑有点麻烦
图解:
//1:首先定义一个数组,数组里面每个元素数string类(每个数字对应的字符串)
//2:定义一个vector<string> vv,这个对象是一个数组,里面每个元素是一个string,最后我们将所有的组合串尾插到这个数组中
//定义一个组合函数,参数分别为 //输入的数字串,第几个数字,组合的字母串,返回的那个string类型的数组
class Solution {
public:
//数字串
string _numberStr[10] = {"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
//输入的数字串,第几个数字,组合的字母串,string类型的数组
void Combination(const string& digits,size_t di,string CombineStr,vector<string>& Strv)
{
if(di == digits.size())
{
Strv.push_back(CombineStr); //将组合串尾插数组
return;
}
//1:首先取数字
size_t num = digits[di] - '0';
//2:找到数字对应的字串
string str = _numberStr[num];
//3:遍历字串,然后组合并且递归下去
for(auto ch : str)
{
Combination(digits,di + 1,CombineStr + ch,Strv); //递归下一层
}
}
vector<string> letterCombinations(string digits)
{
vector<string> Strv; //string 类型的数组
if(digits.size() == 0)
{
return Strv;
}
Combination(digits,0,"",Strv);
return Strv;
}
};
7、数组中出现次数超过一半的数字
方法1:
思路:这个很简单,我们先把数组排序,然后遍历数组,当前位置和后;一个不相等就++i,相等就静茹循环计数,然后判断。
//思路:
//1:先排序
//2:然后遍历数组,这个注意要防止越界,如果前一个元素和后一个不相等,那么++i
//如果前一个和后一个相等,那么进入循环,我们来记计数。这个也要注意防止越界,结束后判断一下是不是大于长度的一般,大于就返回当前位置的值,否则将count置为1
class Solution {
public:
int MoreThanHalfNum_Solution(vector<int> numbers)
{
if(numbers.size() == 0)
return 0;
//先排序
std::sort(numbers.begin(),numbers.end());
size_t len = numbers.size();//数组长度
size_t i = 0;
size_t count = 1;//相同元素个数
while(i < len - 1)
{
if(numbers[i] != numbers[i+1])
{
++i;
}
else
{
while(i < len - 1 && numbers[i] == numbers[i+1])
{
++count;
++i;
}
if(count > len/2)
return numbers[i];
}
count = 1;
}
return numbers[i];
}
};
方法2:
1:先排序,题目说一定有解,那么一定是中间那个数字(因为这个数组超过了数组长度的一半)。
2:然后遍历数组,统计他的次数,最后判断一下他的次数是不是大于一般,大于则返回这个数,否则返回0。
注:其实第二部都是多余的,有解,那么就是中间这个数,直接返回就行了
//思路:
//1:先排序
//2:如果有解,那么一定是中间的数字
class Solution {
public:
int MoreThanHalfNum_Solution(vector<int> numbers)
{
//先排序
std::sort(numbers.begin(),numbers.end());
//超过一般的那个数字
int middle = numbers[numbers.size()/2];
//这里其实直接返回就行了,因为一定会有解,那就是中间这个数字了!!
//return numbers[numbers.size()/2]
int count = 0;
size_t i = 0;
while(i < numbers.size())
{
if(numbers[i] = middle)
++count;
++i;
}
return (count > numbers.size()/2) ? middle : numbers[0];
}
};