今日のトピック説明は主にハッシュ化の知識点のトピック説明です。
目次
(1) 最初の正の整数が欠落している
リンクは次のとおりです:最初の正の整数がありません
トピックの表示:
トピック分析:
このトピックを分析すると、次の点がわかります。
1. 配列に負の数がある場合、負の数の処理は無視されます。つまり、正の数の場合は、欠落している最初の正の整数のみが判断されます。
2. 長さが n で繰り返し番号がない配列の場合、配列を走査した後、次の 2 つの状況が発生します。
- ① 配列内の要素が 1 と n の間に出現する場合、欠落している数値は n+1 であることがわかります。
- ② 逆に、配列の要素が 1 ~ n の間で欠落している場合、欠落している数は 1 ~ n の間の数になります。このような場合は。ハッシュ テーブルを使用して、各要素が出現したかどうかを判断するだけで済みます。
具体的な方法:
- ステップ 1 : まず、配列に表示される数値を記録するハッシュ テーブルを作成する必要があります。
- ステップ 2 : 配列を最初から走査し、配列内に数値が出現する回数を記録します。
- ステップ 3 : 次に、ハッシュ テーブルにこの数値があるかどうかを確認します。ない場合は、それが配列から欠落している最初の正の整数であることを意味します。
コード表示:
class Solution {
public:
int firstMissingPositive(vector<int>& nums) {
unordered_map<int, int> tmp;
for (auto& e : nums)
{
if (e > 0)
tmp[e]++;
}
for (int i = 1; i <= nums.size() + 1; i++) {
if (tmp[i] == 0)
return i;
}
return 0;
}
};
パフォーマンス分析:
- 時間計算量: O(n)。配列を初めて走査してその数値の出現回数を記録するのは O(n) で、1 から n に走査するのに 2 番目に悪い時間は O(n) であるため、時間計算量は O(n) になります。
- 空間の複雑さ: O(n)。ハッシュ テーブルには、長さが n の一意の要素が n 個記録されるため、空間の複雑さは O(n) になります。
(2) 配列内に 1 回だけ出現する 2 つの数値
リンクは次のとおりです:配列内に 1 回だけ現れる 2 つの数値
トピックの表示:
トピック分析:
まず、この質問の意味は比較的単純です。この質問では、ハッシュを使用することも、使用しないこともでき、どちらの方法でもこの質問を作成できます。次に両方の方法を紹介します。
1. 直接法
具体的な考え:
- 実際、これを直接実行するというアイデアも非常にシンプルです。まず最初に配列をソートします。これは非常に重要なステップです。
- 次に、ソートされた配列の場合、同じ数値が隣り合っている必要があることが分かるので、これを判断に使用できます。
具体的な方法:
- ステップ 1 : 最初は、配列内の要素を並べ替えることです。
- ステップ 2 : 配列を最初からたどって、2 つの連続する数値が異なることを確認します。
- ステップ 3 : 2 つの連続する番号が同じ場合、現在の位置から 2 つ前の位置にジャンプするだけで検索を続行できます。
コード表示:
class Solution {
public:
vector<int> FindNumsAppearOnce(vector<int>& array) {
// write code here
sort(array.begin(),array.end());
vector<int>tmp;
int size=array.size();
for(int i=0; i<size;++i )
{
if(array[i] == array[i+1])
i++;
else
tmp.push_back(array[i]);
}
return tmp;
}
};
2.ハッシュ
具体的な考え:
- 一度しか出現しない数値が 2 つあるため、各数値の出現回数を数え、ハッシュ テーブルを使用してキー値に従ってその頻度値にすばやくアクセスし、トピックの要件を達成します。
具体的な方法:
- ステップ 1 : まず、配列内の要素を並べ替えます。
- ステップ 2 : 配列を最初からスキャンし、ハッシュ テーブルを使用して各数値の頻度を数えます。
- ステップ 3 : 次に、配列を再度走査し、ハッシュ テーブルを比較し、頻度 1 の 2 つの数値を見つけて、戻って次の結果を達成します。
コード表示:
class Solution {
public:
vector<int> FindNumsAppearOnce(vector<int>& array) {
// write code here
sort(array.begin(), array.end());
unordered_map<int, int> tmp;
vector<int> res;
for(auto& e : array)
++tmp[e];
//再次遍历数组
for(int i = 0; i < array.size(); i++)
{
//找到频率为1的两个数
if(tmp[array[i]] == 1)
res.push_back(array[i]);
}
return res;
}
};
パフォーマンス分析:
- 時間計算量: O(n)、n は配列の長さ、配列の各要素は 2 回走査されます。
- 空間計算量: O(n)、ハッシュ テーブルの長さは (n−2)/2 である必要があります。
(3) 直線上にある最も多くの点
リンクは次のとおりです: 149. 直線上のほとんどの点
トピックの表示:
トピック分析:
この問題に対する乱暴な解決策は、2 つの点を任意に引用して直線を列挙することですが、その時間計算量は O(N^3) に達するため、ここではあまり多くを紹介しません。
では、どうすれば最適化できるのでしょうか? アイデアを示すために次の写真を例に挙げてみましょう。
知らせ:
- 傾きを計算する際、浮動小数点に切り替えると精度の問題が発生するため、分数に切り替えて演算します。
具体的な方法:
- ステップ 1 :点の総数が 2 以下の場合、直線を使用してすべての点を直列に接続するだけで、点の総数を直接返すことができます。
- ステップ 2 : 配列を先頭から走査し、中心点を列挙します。
- ステップ 3 : 次に、中心点を使用して他の点との傾きを計算し、最大数を計算します。以上の操作を繰り返すことで実現できます。
コード表示:
class Solution {
public:
int maxPoints(vector<vector<int>>& points) {
int n = points.size();
//当结点数小于2时,直接返回即可
if (n < 2)
return n;
int ans = 0;
//枚举中心店
for (int i = 0; i < n; i++) {
//定义哈希表来统计每个斜率的数量
unordered_map<string, int> tmp;
//定义Count用来来表示最大的数量
int Count = 0;
//枚举剩余点,因为i之前的已经枚举过了,所以才 i+1开始
for (int j = i + 1; j < n; j++) {
//获取两点的坐标
int x1 = points[i][0] ,y1 = points[i][1];
int x2 = points[j][0] ,y2 = points[j][1];
//计算斜率
string key = clac(x1,x2,y1,y2);
tmp[key]++;
//更新Count
Count = max(Count, tmp[key]);
}
//一个中心点完成后更新结果
ans = max(ans, Count+1);
}
return ans;
}
string clac(int x1, int x2, int y1, int y2)
{
//计算横纵坐标的差值,注意记得加绝对值
int index=abs(x1-x2);
int indey=abs(y1-y2);
//最大公约数
int val=gcd(index, indey);
//拼接
string key=to_string (indey / val) + "_" + to_string (index / val);
//斜率为负,拼接一个 -号
if((x1 < x2 && y1 > y2) || (x1 > x2 && y1 < y2))
return "-" + key;
return key;
}
};
パフォーマンス分析:
- 時間計算量: O(n^2*logn)、n は配列の長さで、中心点の列挙には O(N) が必要で、傾きのグループ化にも O(N) が必要で、最大値を得るには O(logN) が必要です。公約数なので、時間計算量は O(n^2logn) です
- 空間複雑度: O(n)。記録にはハッシュ テーブルが必要なので、空間複雑度は O(N) です。
以上でハッシュテーブルに関する3つの質問の説明は終わりです。皆さんのお役に立てれば幸いです。また次回お会いしましょう!!!