c++中 int, long long, double 等数据类型的长度及范围整理
你最多能定义的局部数组大小(栈上)大约是:
最大个数 ≈ 栈大小 / sizeof(int)
≈ 8 * 1024 * 1024 / 4
≈ 2 * 1024 * 1024 = 2,097,152 个 int
为了稳妥,建议不要超过:
const int N = 2e6; // 即 2,000,000
int a[N]; // 栈上安全范围内
const int N = 1e6;
long long a[N]; // ✅ 一般栈上没问题
在算法竞赛中,我们常常需要用到设置一个常量用来代表“无穷大”。
比如对于int类型的数,有的人会采用INT_MAX,即0x7fffffff作为无穷大。但是以INT_MAX为无穷大常常面临一个问题,即加一个其他的数会溢出。
而这种情况在动态规划,或者其他一些递推的算法中常常出现,很有可能导致算法出问题。
所以在算法竞赛中,我们常采用0x3f3f3f3f来作为无穷大。0x3f3f3f3f主要有如下好处:
0x3f3f3f3f的十进制为1061109567,和INT_MAX一个数量级,即109数量级,而一般场合下的数据都是小于109的。
0x3f3f3f3f * 2 = 2122219134,无穷大相加依然不会溢出。
可以使用memset(array, 0x3f, sizeof(array))来为数组设初值为0x3f3f3f3f,因为这个数的每个字节都是0x3f。
哈希表的定义
(1)哈希表的作用
哈希表就是在关键字和存储位置之间建立对应关系,使得元素的查找可以以O(1)的效率进行, 其中关键字和存储位置之间是通过散列函数建立关系,记为:
(2) 常见的散列函数
1)线性定址法:直接取关键字的某个线性函数作为存储地址,散列函数为:
2)除留余数法:将关键字对某一小于散列表长度的数p取余的结果作为存储地址,散列函数为:
3)平方取中法:对关键字取平方,然后将得到结果的中间几位作为存储地址;
4)折叠法:将关键字分割为几部分,然后将这几部分的叠加和作为存储地址。
(3) 地址冲突解决方法
通过以上方法构建的哈希表在理想的情况下能够做到一个关键字对应一个地址,但是实际情况是会有冲突发生,也就是散列函数会将多个关键字映射到同一个地址上。以下是一些解决冲突的方法:
1)开放地址法:
线性探测法:当发生冲突时,就顺序查看下一个存储位置,如果位置为空闲状态,就将该关键字存储在该位置上,如果还是发生冲突,就依次往后查看,当查看到存储空间的末尾时还是找不到空位置,就返回从头开始查看;
#include <iostream>
#include <cstdio>
#include <fstream>
#include <iomanip>
#include <algorithm>
#include <cmath>
#include <deque>
#include <vector>
#include <queue>
#include <string>
#include <cstring>
#include <map>
#include <stack>
#include<list>
#include <set>
#include <ctime>
#include<unordered_map>
#include <bitset>
#include<random>
#include<regex>
#include <chrono>
#include<unordered_map>
#include<unordered_set>
using namespace std;
typedef long long ll;
#define pr pair<double,int>
#define tp tuple<int,int,int>
const int N = 2e5 + 3,INF=0x3f3f3f3f;
const int mod = 100003;//最好是质数
ll a[N],s[N];
ll n, m, k;
ll e[N];
ll ne[N];
int h[N];
ll head,idx;
int find(ll x)
{
ll k = (x % N + N) % N;
while(h[k]!=x&&h[k]!=INF)
{
k++;
if(k==N) k=0;
}
return k;
}
void solve()
{
ll n;
cin >> n;
memset(h,0x3f,sizeof(h));
while(n--)
{
char c;
ll x;
cin >> c >> x;
ll p=find(x);
if(c=='I') h[p]=x;
else {
if(h[p]!=INF) cout << "Yes\n";
else cout << "No\n";
}
}
}
int main()
{
cin.tie(0);
cout.tie(0);
ios::sync_with_stdio(false);//提高cin、cout的输入输出效率
solve();
}
memset(h, 0x3f, sizeof(h)) 实际做了什么?
它将数组 h 中每个字节都设置为 0x3f(即二进制的 00111111),对于 int 或 long long 而言,效果如下:
对于 int 类型(4 字节),0x3f3f3f3f ≈ 1.06e9
对于 long long 类型(8 字节),变成 0x3f3f3f3f3f3f3f3f ≈ 4.56e18
这通常被用作**“无穷大”**,但又小于 INT_MAX,适合比较和加减运算,不会溢出。
2)拉链法
将具有相同存储地址的关键字链成一单链表, m个存储地址就设m个单链表,然后用一个数组将m个单链表的表头指针存储起来,形成一个动态的结构,假设散列函数为 Hash(key) = key %13,其拉链存储结构为:
#include <iostream>
#include <cstdio>
#include <fstream>
#include <iomanip>
#include <algorithm>
#include <cmath>
#include <deque>
#include <vector>
#include <queue>
#include <string>
#include <cstring>
#include <map>
#include <stack>
#include<list>
#include <set>
#include <ctime>
#include<unordered_map>
#include <bitset>
#include<random>
#include<regex>
#include <chrono>
#include<unordered_map>
#include<unordered_set>
using namespace std;
typedef long long ll;
#define pr pair<double,int>
#define tp tuple<int,int,int>
const int N = 2e5 + 3;
const int mod = 100003;//最好是质数
ll a[N],s[N];
ll n, m, k;
ll e[N];
ll ne[N];
ll h[N];
ll head,idx;
void insert(ll x)
{
ll k = (x % N+ N) % N;//先加一个在取模,防止负数
e[idx] = x;
ne[idx] = h[k];
h[k] = idx++;
}
bool find(ll x)
{
ll k = (x % N + N) % N;
for(int i = h[k]; i != -1; i = ne[i])
{
if(e[i] == x)
return true;
}
return false;
}
void solve()
{
ll n;
cin >> n;
memset(h,-1,sizeof(h));
while(n--)
{
char c;
ll x;
cin >> c >> x;
if(c=='I') insert(x);
else {
if(find(x)) cout << "Yes\n";
else cout << "No\n";
}
}
}
int main()
{
cin.tie(0);
cout.tie(0);
ios::sync_with_stdio(false);//提高cin、cout的输入输出效率
solve();
}
383. 赎金信
AC
class Solution {
public:
bool canConstruct(string ransomNote, string magazine) {
const int N=1e5;
int a[N];
memset(a,0,sizeof(a));
for(int i=0;i<magazine.size();i++)
{
a[magazine[i]]++;
}
for(int i=0;i<ransomNote.size();i++)
{
a[ransomNote[i]]--;
if(a[ransomNote[i]]<0) return false;
}
return true;
}
};
第454题.四数相加II
AC
关键是把n4次方的时间复杂度减到n平方
class Solution {
public:
int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<int>& nums3, vector<int>& nums4) {
int ans=0;
unordered_map<int,int> m;
for(int i=0;i<nums1.size();i++)
{
for(int j=0;j<nums2.size();j++)
{
m[nums1[i]+nums2[j]]++;
}
}
for(int k=0;k<nums3.size();k++)
{
for(int p=0;p<nums4.size();p++)
{
int x=-(nums3[k]+nums4[p]);
if(m.find(x)!=m.end()) ans+=m[x];
}
}
return ans;
}
};
第15题. 三数之和
AC
set去重:
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
set<vector<int>> s;
vector<vector<int>> ans;
sort(nums.begin(),nums.end());
for(int i=0;i<nums.size();i++)
{
int l=i+1,r=nums.size()-1;
while(l<r)
{
int k=nums[i]+nums[l]+nums[r];
if(k<0) l++;
if(k>0) r--;
if(k==0) {
s.insert({nums[i],nums[l],nums[r]});
l++;
r--;
};
}
}
for(auto it=s.begin();it!=s.end();it++)
ans.push_back(*it);
return ans;
}
};
剪枝去重:
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> s;
sort(nums.begin(),nums.end());
for(int i=0;i<nums.size();i++)
{
if(nums[i]>0) break;
int l=i+1,r=nums.size()-1;
if(i>0&&nums[i]==nums[i-1]) continue;//去重
while(l<r)
{
int k=nums[i]+nums[l]+nums[r];
if(k<0) l++;
if(k>0) r--;
if(k==0) {
s.push_back({nums[i],nums[l],nums[r]});
while (l < r && nums[l] == nums[l + 1]) l++;//去重
while (l < r && nums[r] == nums[r - 1]) r--;//去重
l++;
r--;
};
}
}
return s;
}
};
第18题. 四数之和
AC
用set来去重 时间会用的更多 但也能通过
我已经long long定义k了为什么还要强制转换:
尽管你已经将 k 定义为 long long,但 加法操作中依然可能存在溢出,因为 nums[i]、nums[j]、nums[l] 和 nums[r] 本身是 int 类型,而在 int 类型的操作过程中,它们的和会先被计算为 int,然后如果超出了 int 的范围,才会出现溢出。因此,为了避免溢出,必须先将参与计算的操作数转换为 long long 类型。
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
set<vector<int>> s;
vector<vector<int>> ans;
sort(nums.begin(),nums.end());
for(int i=0;i<nums.size();i++)
{
for(int j=i+1;j<nums.size();j++)
{
int l=j+1,r=nums.size()-1;
while(l<r)
{
long long k=(long long)nums[i]+nums[j]+nums[l]+nums[r];
if(k==target){
s.insert({nums[i],nums[j],nums[l],nums[r]});
l++;
r--;
}
else if(k>target) r--;
else l++;
}
}
}
for(auto it=s.begin();it!=s.end();it++)
ans.push_back(*it);
return ans;
}
};
第二种方法:
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> ans;
sort(nums.begin(),nums.end());
for(int i=0;i<nums.size();i++)
{
if(i>0&&nums[i]==nums[i-1]) continue;
for(int j=i+1;j<nums.size();j++)
{
if(j>i+1&&nums[j]==nums[j-1]) continue;
int l=j+1,r=nums.size()-1;
while(l<r)
{
long long k=(long long)nums[i]+nums[j]+nums[l]+nums[r];
if(k==target){
ans.push_back({nums[i],nums[j],nums[l],nums[r]});
while(l<r&&nums[r]==nums[r-1]) r--;
while(l<r&&nums[l]==nums[l+1]) l++;
l++;
r--;
}
else if(k>target) r--;
else l++;
}
}
}
return ans;
}
};