leetcode刷题笔记Day1
1、两数之和
题目描述:
给定一个整数数组
nums
和一个整数目标值target
,请你在该数组中找出 和为目标值target
的那 两个 整数,并返回它们的数组下标。你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
示例:
输入:nums = [2,7,11,15], target = 9
输出:[0,1]
解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。
解法一:暴力求解
思路:
1、遍历每个元素A,寻找合适的数字B组成target;
2、数字B可以从元素A往后的元素中寻找;
代码:
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
vector<int> v;
for (int i = 0; i < nums.size()-1;i++) {
for (int j = i + 1; j < nums.size(); j++) {
if (nums[j] + nums[i] == target) {
v.push_back(i);
v.push_back(j);
}
}
}
return v;
}
};
结果:
解法二、哈希查找
思路:
1、解法一的局限性在于寻找元素B费时,所以为降低空间复杂度,我们需要做的就是缩短寻找元素B的时间,简单来讲就是选择时间复杂度最低的元素查找算法。
2、查找算法中,时间复杂度最小的是哈希查找,当然,也可以选择其他查找算法,只要时间复杂度低于要求的O(n2)
3、利用HashMap实现
代码:
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
vector<int> v;
unordered_map<int, int> hash;
for (int i = 0; i < nums.size(); i++) {
auto it = hash.find(target - nums[i]);
if (it != hash.end()) {
//先插入it->second的原因是最初始哈希表中是空的,当我们找不到元素后,会将该元素直接插入到哈希表中,这就导致了元素B位置是位于元素A前面
v.push_back(it->second);
v.push_back(i);
return v;
}
//将数组中元素的值作为key值,是因为map<key,value>查找是根据key来查找的,而不是value,我们是需要按值查找的,因此将元素的值作为key,元素下标作为value
hash[nums[i]] = i;
}
return {};
}
};
结果:
相关容器介绍
vector
定义:
表示可变大小数组的序列容器,可以通过下标来访问元素,和数组十分类似
用法:
//包含头文件
#include<vector>
/***************************************************************************************/
//构造
vector<datatype> name;//空vector
vector(int nSize);//创建一个vector,元素个数为nSize
vector(int nSize,const t& t);//创建一个vector,元素个数为nSize,且值均为t
vector(const vector&);//复制构造函数
vector(begin,end);//复制[begin,end)区间内另一个数组的元素到vector中
/***************************************************************************************/
//增加
name.push_back(data);//向量尾部增加一个元素X
terator insert(iterator it,const T& x);//向量中迭代器指向元素前增加一个元素x
iterator insert(iterator it,int n,const T& x);//向量中迭代器指向元素前增加n个相同的元素x
iterator insert(iterator it,const_iterator first,const_iterator last);//向量中迭代器指向元素前插入另一个相同类型向量的[first,last)间的数据
/***************************************************************************************/
//插入,insert不支持根据下标去插入
vector<int> v(10, 10);// 用10个10来初始化vector
v.insert(v.begin() + 3, 11);// 在第三个位置插入11
vector<int> v(10, 10);
v.insert(v.begin() + 2, 5, 2);// 在第二个位置往后连续插入5个2
/***************************************************************************************/
//删除,erase不支持根据下标去删除
vector<int> v(10, 10);
v.erase(v.begin()+3);// 删除第3个位置的值
vector<int> v(10, 10);
v.erase(v.begin() + 2, v.begin() + 6);// 删除第2个位置到第6个位置的所有值
void pop_back();//删除向量中最后一个元素
void clear();//清空向量中所有元素
/***************************************************************************************/
//遍历
reference at(int pos);//返回pos位置元素的引用
reference front();//返回首元素的引用
reference back();//返回尾元素的引用
iterator begin();//返回向量头指针,指向第一个元素
iterator end();//返回向量尾指针,指向向量最后一个元素的下一个位置
reverse_iterator rbegin();//反向迭代器,指向最后一个元素
reverse_iterator rend();//反向迭代器,指向第一个元素之前的位置
//直接像数组一样,name[i]来表示
/***************************************************************************************/
//大小
int size() const:返回向量中元素的个数
/***************************************************************************************/
//交换
void swap(vector&);//交换两个同类型向量的数据
/***************************************************************************************/
//排序
#include <algorithm>
sort(name.begin(),name.end());//从小到大
reverse(obj.begin(),obj.end());//从大到小
/***************************************************************************************/
//定义二维数组
/*resize 改变当前使用数据的大小,如果它比当前使用的大,者填充默认值
reserve 改变当前vecotr所分配空间的大小*/
/*方法一*/
int N=5, M=6;
vector<vector<int> > obj(N); //定义二维动态数组大小5行
for(int i =0; i< obj.size(); i++)//动态二维数组为5行6列,值全为0
{
obj[i].resize(M);
}
/*方法二*/
int N=5, M=6;
vector<vector<int> > obj(N, vector<int>(M)); //定义二维动态数组5行6列
Map
定义:
map是STL的一个关联容器,以键值对存储的数据,其类型可以自己定义,每个关键字在map中只能出现一次,关键字不能修改,值可以修改;
用法:
//包含头文件
#include<map>
/***************************************************************************************/
//定义
map<int,string> my_map;
/***************************************************************************************/
//插入
/*第一种*/
my_map.insert(pair<int,string>(1,"first"));//插入pair数据
/*第二种*/
my_map.insert(map<int,string>::value_type(1,"first"));//插入value_type数据
/*第三种*/
my_map[1]="first";//数组直接赋值
/***************************************************************************************/
//查找
my_map.count(1);//用count函数判断关键字是否存在,没办法定位元素位置
my_map.find(1);//用find函数定位元素位置
/*通过map对象的方法获取的iterator数据类型是一个std::pair对象,包括两个数据iterator->first和iterator->second,分别代表关键字和value值*/
/***************************************************************************************/
//删除
map<int, string>::iterator it;
it = my_map.find(1);
my_map.erase(it); //用关键字删除单个元素
my_map.erase( my_map.begin(), my_map.end() ); //整体删除
/***************************************************************************************/
//排序
/*map中元素是自动按key升序排序(从小到大)的;
按照value排序时,想直接使用sort函数是做不到的,sort函数只支持数组、vector、list、queue等的排序,无法对map排序,那么就需要把map放在vector中,再对vector进行排序。*/
vector< pair<string,int> > vec(ma.begin(),ma.end());
sort(vec.begin(),vec.end(),cmp);
HashMap
定义:
C++中实现哈希表可以用unordered_map来实现
用法:
//包含头文件
#include <unordered_map>
//定义:
//和map基本一致,只是换为unordered_map而已
哈希查找
按照关键字为每一个元素"分类",然后将这个元素存储在相应"类"所对应的地方。
哈希表是一个在时间和空间上做出权衡的经典例子。如果没有内存限制,那么可以直接将键作为数组的索引。那么所有的查找时间复杂度为O(1);如果没有时间限制,那么我们可以使用无序数组并进行顺序查找,这样只需要很少的内存。哈希表使用了适度的时间和空间来在这两个极端之间找到了平衡。只需要调整哈希函数算法即可在时间和空间上做出取舍。
2、回文数
题目描述:
给你一个整数
x
,如果x
是一个回文整数,返回true
;否则,返回false
。回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数。
示例:
输入:x = -121
输出:false
解释:从左向右读, 为 -121 。 从右向左读, 为 121- 。因此它不是一个回文数。
解法一、转字符串
思路:
1、将数字转化为字符串,然后对比下标为 i 的元素和下标为 length-1-i 的元素是否一样,若一样,则是回文数,否则,就是非回文数。
代码:
class Solution {
public:
bool isPalindrome(int x) {
string str = to_string(x);
int len = str.length();
for (int i = 0; i < len/2; i++) {
if (str[i] == str[len - 1 - i]) {
continue;
}
else {
return false;
}
}
return true;
}
};
结果:
解法二、数字处理
思路:
1、解法一内存占比大,是因为我们需要另外的空间来存储整数转化后的字符串,为了降低内存,我们可以直接用数字处理,不转化字符串。
2、考虑特殊情况:
- 负数一定不是回文数;
- 非0整数,个数位为0也一定不是回文数;
3、在除上述两种情况下,如何利用数字来判断回文数?
不论是数字长度是奇数还是偶数,只要后半部分的数字倒装后与前半部分数字相等,那就是回文数。 例如:12321,后半部分数字倒装后为12,前半部分为12,则为回文数。
&如何获取后半部分倒装后的数字以及前半部分数字?
除10取余法 . 1、后半部分数字:获取每一位数字,逐步除10取余数; . 2、后半部分倒装:逐步乘10相加; . 3、前半部分数字:在第一步中每次除10剩余部分就是前半部分数字; . 4、结束循环条件:前半部分数字位数小于或等于后半部分位数,但是这种位数判断需要另外的代码,费时费空间,所以我们可大致看为数字大小,前半部分数字小于后半部分数字,循环结束。
代码:
class Solution {
public:
bool isPalindrome(int x) {
if (x < 0) {
return false;//负数
}
else if (x == 0) {
return true;
}
else {
if (x % 10 == 0) {
return false;//末尾为0的数
}
else {
int late = 0;
while (x > late) {
late = late * 10 + x % 10;//后半部分数
x /= 10;//前半部分数字
}
if (x == late) {
return true;//整数为偶位数
}
else if(x==late/10){
return true;//整数为奇位数
}
else {
return false;
}
}
}
}
};
结果:
注意:还不如第一种方法,但是我去看了官方题解,以及在评论中也试了别的代码,发现原理大致就这两种,且结果似乎都是这样,暂时找不到什么能够降低内存的方法。
相关知识
数值与string相互转换
数值转string:
to_string()函数
用法:
//包含头文件
#include<string>
string转数值:
用法:
//包含头文件
#include<stdlib.h>
//string转int
string love="77";
int ilove=atoi(love.c_str());
//string转float或double
string love="77.77";
float fLove=atof(love.c_str());
double dLove=atof(love.c_str());
相关知识
数值与string相互转换
数值转string:
to_string()函数
用法:
//包含头文件
#include<string>
string转数值:
用法:
//包含头文件
#include<stdlib.h>
//string转int
string love="77";
int ilove=atoi(love.c_str());
//string转float或double
string love="77.77";
float fLove=atof(love.c_str());
double dLove=atof(love.c_str());