一次アルゴリズムを完成させるのに半年近くかかりました。. . 私はそれをほとんど忘れてしまいました。. . . .
❤ 2022.2.23 ❤
今日のトピックは:
3 つの数値の合計
私の考え:
3つの数の和なので、まず2つの数を順番に足し合わせてから、合計が0になる数がないか調べればいいのです。
戻り値は2次元配列である必要がある.C言語で2次元配列を動的に配置するのは得意だけど(さすが!)、まだまだ面倒くさい.c++にするかpythonにするか検討中. . . 実際、私はいつも python を使いたいと思っていましたが、怠け者です。. .
最初に、
最初に入力配列をソートすることでこれを行いました。次に、タイトルでトリプルが繰り返されないようにする必要があるため、実装プロセス中にそれらの繰り返し要素を除外し、最終的に答えを見つけました。
int cmp(const void* _a, const void* _b)
{
int a = *(int*)_a, b = *(int*)_b;
return a - b;
}
/**
* Return an array of arrays of size *returnSize.
* The sizes of the arrays are returned as *returnColumnSizes array.
* Note: Both returned array and *columnSizes array must be malloced, assume caller calls free().
*/
int** threeSum1(int* nums, int numsSize, int* returnSize, int** returnColumnSizes) {
int front = 0;
int rear = 0;
int n = 0;
*returnSize = 0;
*returnColumnSizes = (int*)malloc(sizeof(int) * (numsSize / 3 + 1));
int** result = (int**)malloc(sizeof(int*) * (numsSize / 3 + 1));
qsort(nums, numsSize, sizeof(int), cmp);
while (front < numsSize - 2)
{
while (front < numsSize - 2 && nums[front] == nums[front + 1])
{
front++;
rear = front + 1;
}
while (rear < numsSize - 1 && nums[rear] == nums[rear + 1])
{
rear++;
n = rear + 1;
}
while (n < numsSize && nums[n] == nums[n + 1])
n++;
if (nums[front] + nums[rear] + nums[n] == 0)
{
(*returnSize)++;
(*returnColumnSizes)[*returnSize - 1] = 3;
result[*returnSize - 1] = (int*)malloc(sizeof(int) * 3);
result[*returnSize - 1][0] = nums[front];
result[*returnSize - 1][1] = nums[rear];
result[*returnSize - 1][2] = nums[n];
rear++;
n = rear + 1;
}
else
n++;
if (n == numsSize)
{
rear++;
n = rear + 1;
}
if (rear == numsSize - 1)
{
front++;
rear = front + 1;
n = rear + 1;
}
}
return result;
}
しかし、これには問題があります。繰り返される要素が除外されるため、[-1 -1 2] のような回答が除外されるためです。コンパイラは私にパスを与えません。. .
次に、要素の重複排除から回答の重複排除に変更します。
int cmp(const void* _a, const void* _b)
{
int a = *(int*)_a, b = *(int*)_b;
return a - b;
}
int** threeSum(int* nums, int numsSize, int* returnSize, int** returnColumnSizes) {
int front = 0;
int rear = 1;
int n = 2;
bool newArray = true;
*returnSize = 0;
*returnColumnSizes = (int*)malloc(sizeof(int));
int** result = (int**)malloc(sizeof(int*)); //注意:结果可能有各种组合,数量可能比numsSize还多,只能动态分配。。。
qsort(nums, numsSize, sizeof(int), cmp);
while (front < numsSize - 2)
{
if (nums[front] + nums[rear] + nums[n] == 0)
{
for (int i = 0; i < *returnSize; i++)
{
if (nums[front] == result[i][0])
{
if (nums[rear] == result[i][1])
{
newArray = false;
n++;
}
}
}
if (newArray == true)
{
(*returnSize)++;
*returnColumnSizes = (int*)realloc(*returnColumnSizes, sizeof(int) * (*returnSize));
(*returnColumnSizes)[*returnSize - 1] = 3; //注意加小括号,否则优先和[]结合
result = (int**)realloc(result, sizeof(int*) * (*returnSize));
result[*returnSize - 1] = (int*)malloc(sizeof(int) * 3);
result[*returnSize - 1][0] = nums[front];
result[*returnSize - 1][1] = nums[rear];
result[*returnSize - 1][2] = nums[n];
n++;
}
newArray = true;
}
else
n++;
if (n == numsSize)
{
rear++;
n = rear + 1;
}
if (rear == numsSize - 1)
{
front++;
rear = front + 1;
n = rear + 1;
}
}
return result;
}
しかし。. . 驚くことではないが。. . タイムアウトしました。. . 案の定、ブルート フォース クラックは機能しません。
しかし、動的 2 次元配列のメモリ割り当てなど、デバッグ プロセス中にいくつかの問題に遭遇しました。いつ
* を追加し、いつ括弧を追加するかを正確に覚えていません。 、そしてここで結果を判断することはできません.いくつのグループがあるかは、入力配列の長さよりも長くなる可能性があるため、動的割り当ての方が優れています.
まずは答えを見てみましょう
回答を読んで、そう思いました。. . 私は本当に私の考えに似ていたいのですが、私のプログラムはうまくいきません。. .
だから私は答えに従って答えを修正しました。
1 つ目は、入力配列をトラバースし、小さい部分と大きい部分で合計が 0 になる要素を見つけることです。
重複排除戦略は、トラバーサル プロセス中に同じ要素に遭遇したときに結果配列に現在の組み合わせと同じ結果があるかどうかを調べ、ある場合はスキップし、ない場合は増やすことです。
長時間調整した後、結果は問題ないはずですが、それでもタイムアウトします。. .
私のコードはどこが間違っていますか?. .
考えてみると、問題は重複排除にあるのかもしれません。
私のコードのアイデアは、対象の要素よりも小さい間隔と大きい間隔で合計が 0 の要素を探すことです。そのため、同じ要素に遭遇した場合、直接スキップすることはできませんが、結果を取得してから判断し、少なくとも 2 つの結果が両方とも繰り返される場合はスキップします。しかし、答えの考え方によれば、各ターゲット要素の重みは同じであるため、同じ要素に遭遇したときに直接スキップできます。
それでは、コードを変更させてください。
int** threeSum(int* nums, int numsSize, int* returnSize, int** returnColumnSizes) {
int front = 1;
int rear = numsSize - 1;
*returnSize = 0;
*returnColumnSizes = (int*)malloc(sizeof(int));
int** result = (int**)malloc(sizeof(int*)); //注意:结果可能有各种组合,数量可能比numsSize还多,只能动态分配。。。
qsort(nums, numsSize, sizeof(int), cmp);
for (int i = 0; i < numsSize; i++)
{
if (i > 0 && nums[i] == nums[i - 1])
continue;
if (nums[i] > 0)
break;
front = i + 1;
rear = numsSize - 1;
while (front < rear)
{
if (nums[i] + nums[front] + nums[rear] > 0)
{
rear--;
continue;
}
else if (nums[i] + nums[front] + nums[rear] < 0)
{
front++;
continue;
}
else
{
(*returnSize)++;
*returnColumnSizes = (int*)realloc(*returnColumnSizes, sizeof(int) * (*returnSize));
(*returnColumnSizes)[*returnSize - 1] = 3; //注意加小括号,否则优先和[]结合
result = (int**)realloc(result, sizeof(int*) * (*returnSize));
result[*returnSize - 1] = (int*)malloc(sizeof(int) * 3);
result[*returnSize - 1][0] = nums[i];
result[*returnSize - 1][1] = nums[front];
result[*returnSize - 1][2] = nums[rear];
front++;
rear--;
}
while (front < rear && nums[front] == nums[front - 1])
front++;
while (front < rear && nums[rear] == nums[rear + 1])
rear--;
}
}
return result;
}
このコードと答えの考え方は同じです。つまり、配列を順番にトラバースし、トラバースした要素の後に合計がゼロになる要素を見つけます。左ポインタはトラバースされた次の要素から始まり、右ポインタは最後の要素から始まり、トラバースされた要素が 0 より大きくなるか、左ポインタが右ポインタより大きくなるまで続きます。
重複排除に関して、私はこの形を使い始めました.
まず、配列要素をトラバースする重複排除は、重複要素に遭遇したときにスキップすることですが、合計がゼロの2つの要素を探す場合、これは機能しません.それでも最初に判断して合計する場合前の要素が同じでスキップされた場合、左ポインターが指す最初の要素が走査された配列要素と同じ場合、左ポインターが配置されている要素はスキップされるため、ここでの走査は少なくとも 1 回実行する必要があります。値が 0 である 判定後、重複排除操作を実行します。
while ループの最後では、重複排除時に直接 continue を使用できないため、各判定で判定とゼロ操作が実行され、結果が繰り返される可能性があります。そのため、while ループを使用して繰り返しのないポインター項目を見つけてから、ループを続行する必要があります。
しかし、非常に恥ずかしいことに、提出はまだタイムアウトしました。. .
それ****。. .
他に方法がなく、他の人が提出した回答をc言語で読みに行き、変更を続けました。. .
私は他の人の答えを見て、私のものとの違いはおそらく、戻り値を増やすたびに realloc 関数を使用して配列を動的に割り当てることであることがわかりましたが、他の人は最初に十分な配列を直接割り当てます(n * n)ので、私はこちらも変更しました
int** threeSum(int* nums, int numsSize, int* returnSize, int** returnColumnSizes) {
int front = 1;
int rear = numsSize - 1;
*returnSize = 0;
*returnColumnSizes = (int*)malloc(sizeof(int) * numsSize * numsSize);
int** result = (int**)malloc(sizeof(int*) * numsSize * numsSize); //注意:结果可能有各种组合,数量可能比numsSize还多,只能动态分配。。。
qsort(nums, numsSize, sizeof(int), cmp);
for (int i = 0; i < numsSize; i++)
{
if (i > 0 && nums[i] == nums[i - 1])
continue;
if (nums[i] > 0)
break;
front = i + 1;
rear = numsSize - 1;
while (front < rear)
{
if (nums[i] + nums[front] + nums[rear] > 0)
{
rear--;
continue;
}
else if (nums[i] + nums[front] + nums[rear] < 0)
{
front++;
continue;
}
else
{
(*returnSize)++;
//*returnColumnSizes = (int*)realloc(*returnColumnSizes, sizeof(int) * (*returnSize));
(*returnColumnSizes)[*returnSize - 1] = 3; //注意加小括号,否则优先和[]结合
//result = (int**)realloc(result, sizeof(int*) * (*returnSize));
result[*returnSize - 1] = (int*)malloc(sizeof(int) * 3);
result[*returnSize - 1][0] = nums[i];
result[*returnSize - 1][1] = nums[front];
result[*returnSize - 1][2] = nums[rear];
front++;
rear--;
}
while (front < rear && nums[front] == nums[front - 1])
front++;
while (front < rear && nums[rear] == nums[rear + 1])
rear--;
}
}
return result;
}
ようやく合格しました.この質問はほぼ1か月間書かれています.これは中間アルゴリズムの力ですか? !
問題は、なぜ答えを n*n メモリに格納できるのかということです。
忘れて、後で話しましょう。. . 私は疲れている。. .
したがって、この質問に c++ を使用した場合、既に解決している可能性があります。. .
だから私はC ++を練習することにしました
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> result;
sort(nums.begin(), nums.end());
for (int i = 0; i < nums.size(); i++)
{
if (i > 0 && nums[i] == nums[i - 1])
continue;
if (nums[i] > 0)
break;
int front = i + 1;
int rear = nums.size() - 1;
while (front < rear)
{
if (nums[i] + nums[front] + nums[rear] > 0)
{
rear--;
continue;
}
else if (nums[i] + nums[front] + nums[rear] < 0)
{
front++;
continue;
}
else
{
result.push_back({ nums[i], nums[front], nums[rear] });
front++;
rear--;
}
while (front < rear && nums[front] == nums[front - 1])
front++;
while (front < rear && nums[rear] == nums[rear + 1])
rear--;
}
}
return result;
}
};
案の定、
sort() 関数とベクトル 2 次元配列の使用法を簡単に思い出すことができました。