Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added 287_find_the_duplicate_number/a.out
Binary file not shown.
Binary file added 287_find_the_duplicate_number/image.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
163 changes: 163 additions & 0 deletions 287_find_the_duplicate_number/memo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
# 287. find-the-duplicate-number

https://leetcode.com/problems/find-the-duplicate-number/description/

## Comments
09:32
空間計算量がO(1)であることがめっちゃだるそう。配列の値をそのまま使う。。。?
2分探索ならうまくいくのかなぁ。
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

この問題を見て真っ先に二分探索を着想するのはすごいですね!自分は思いつかなかったです。

時間計算量はO(nlogn)になって実行可能ではありそう。
空間計算量はO(1)になる。

具体的な二分探索方法
1-n/2の数が、n/2 + 1個あるなら、前半側の数字に重複がある。
逆もしかり。

1 - n/4の数が、n/4 + 1個あるなら、前半側の数字に重複がある。
を繰り返す。

のかなぁ。
再帰でやる?itereationでやる?
iterationでやると、while文の中で配列の値を使うことになるので、ちょっとだるい。
再帰でやるなら、回数を継続していきたい。どうやって引数に渡すのか。
whileで回すほうが楽か。

### step1
```c
// 1. 配列の値をそのまま使う
int findDuplicate(int* nums, int numsSize)
{
int i;
int first;

i = 0;
first = 0;
while (i < numsSize)
{
if (nums[i] < numsSize / 2)
first++;
else
first--;
i++;
}
if (first > 0)
{
// 前半側に重複がある
return findDuplicate(nums, numsSize / 2);
}
else
{
// 後半側に重複がある
return findDuplicate(nums + numsSize / 2, numsSize - numsSize / 2);
}
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

再帰の場合に気になるのはどんな条件の時に終了して再帰を抜けてくか、です。ぱっとこのコードを見たときにもこれ、再帰の終了条件はどこだ?と迷いました。

```
*
うーんnumSizeは固定にしたい。。。
Static変数にして、再帰のたびにnumsSizeを渡すのはどうだろう。

### step2
```c
// 1. 配列の値をそのまま使う
int findDuplicate(int* nums, int numsSize)
{
int i;
int first;
static int numSize_copy = numsSize;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

まだstatic変数を使う意図をつかめていないのですが、static変数は普通の変数とは違い静的領域に場所が確保されます。なのでそれを再帰で使う場合、何度も何度も同じ関数を再帰している間に、その再帰全体から自由にアクセスできるある種のグローバル変数がほしいって感じでしょうか?


i = 0;
first = 0;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

firstという命名の意図が若干分かりづらい気はします。

while (i < numsSize_copy)
{
if (nums[i] < numsSize / 2)
first++;
else
first--;
i++;
}
if (first > 0)
{
// 前半側に重複がある
return findDuplicate(nums, numsSize / 2);
}
else
{
// 後半側に重複がある
return findDuplicate(nums + numsSize / 2, numsSize - numsSize / 2);
}
}
```
*
いけんちゃうかなぁ。main関数を作成して、テストしてみよう。

```c
#include <stdio.h>

int main(void)
{
int nums[] = {1, 2, 3, 3};
int numsSize = 4;

printf("%d\n", findDuplicate(nums, numsSize));
return (0);
}
```
うごかんなぁ。
セグフォがでる。
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

再帰したときに nums が変わっているので、numsSize_copy までループを回しても動きませんね。

static変数を仮引数で初期化するのはだめらしい。
なんでなんやろう。
一旦固定値でやろう。
やったけど無理そう。セグフォがでる。
indexとかめんどくさいなぁ。やっぱwhileでやるほうが楽そう。
while文に変更しよう。
### step3
```c

int findDuplicate(int *nums, int numsSize)
{
int left;
int mid;
int right;
int i;
int count;

left = 0;
right = numsSize - 1;
while (left < right)
{
mid = (left + right) / 2;
count = 0;
i = 0;
while (i < numsSize)
{
if (nums[i] <= mid)
count++;
i++;
}
if (count > mid)
right = mid;
else
left = mid + 1;
}
return (left);
}

#include <stdio.h>

int main(void)
{
int nums[] = {1, 2, 4, 5, 3, 2};
int numsSize = 6;

printf("%d\n", findDuplicate(nums, numsSize));
return (0);
}

```
めっちゃきれいに実装できた。
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

はい、このコードを、自然言語で説明してくれませんか。
そして、コードとその自然言語の「距離」が十分近ければ、読みやすいコードであると私は思います。

mainのテスト何個か試しても行けた
行けてそう!!
![result](image.png)
いけた!!
なんかめちゃ早の人たちいる。
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

フロイドのうさぎとかめといわれるアルゴリズムを応用した方法がありますね。
いや、正直、これ見たことあるんですが、別にそこができても点数がつかないタイプの問題だと思います。

この配列を「ゲームブック」だと思いましょう。
{1, 2, 4, 5, 3, 2}
これは、0ページを開くと「1ページに行け」と書いてあり、4ページを開くと「3ページに行け」と書いてあった、と解釈します。

まず、0ページ目に戻ってくることはありません。
次に、このゲームブックは終わりません。どのページもどこかに行くからです。
そして、ゲームブックは有限です。

そうすると、0から開始して、このゲームブックを進めていくと、あるところから循環が始まるということです。その循環の開始点は0ではないことから、「(循環開始の)ページに行け」と書かれたページが少なくとも2つあったことを示しています。

これはフロイドのアルゴリズムで解けます。

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

何なんやろ。
51 changes: 51 additions & 0 deletions 287_find_the_duplicate_number/step1.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* step1.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kjikuhar <kjikuhar@student.42tokyo.jp> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/06/02 09:47:39 by kjikuhar #+# #+# */
/* Updated: 2025/06/02 09:55:16 by kjikuhar ### ########.fr */
/* */
/* ************************************************************************** */

// 1. 配列の値をそのまま使う
int findDuplicate(int* nums, int numsSize)
{
int i;
int first;
static int numsSize_copy = 4;

i = 0;
first = 0;
while (i < numsSize_copy)
{
if (nums[i] < numsSize / 2)
first++;
else
first--;
i++;
}
if (first > 0)
{
// 前半側に重複がある
return findDuplicate(nums, numsSize / 2);
}
else
{
// 後半側に重複がある
return findDuplicate(nums, numsSize - numsSize / 2);
}
}

#include <stdio.h>

int main(void)
{
int nums[] = {1, 2, 3, 3};
int numsSize = 4;

printf("%d\n", findDuplicate(nums, numsSize));
return (0);
}
52 changes: 52 additions & 0 deletions 287_find_the_duplicate_number/step2.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* step2.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kjikuhar <kjikuhar@student.42tokyo.jp> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/06/02 09:47:39 by kjikuhar #+# #+# */
/* Updated: 2025/06/02 14:19:49 by kjikuhar ### ########.fr */
/* */
/* ************************************************************************** */


int findDuplicate(int *nums, int numsSize)
{
int left;
int mid;
int right;
int i;
int count;

left = 0;
right = numsSize - 1;
while (left < right)
{
mid = (left + right) / 2;
count = 0;
i = 0;
while (i < numsSize)
{
if (nums[i] <= mid)
count++;
i++;
}
if (count > mid)
right = mid;
else
left = mid + 1;
}
return (left);
}

#include <stdio.h>

int main(void)
{
int nums[] = {1, 2, 4, 5, 3, 2};
int numsSize = 6;

printf("%d\n", findDuplicate(nums, numsSize));
return (0);
}
51 changes: 51 additions & 0 deletions 287_find_the_duplicate_number/step3.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/* ************************************************************************** */
/* */
/* ::: :::::::: */
/* step3.c :+: :+: :+: */
/* +:+ +:+ +:+ */
/* By: kjikuhar <kjikuhar@student.42tokyo.jp> +#+ +:+ +#+ */
/* +#+#+#+#+#+ +#+ */
/* Created: 2025/06/02 15:04:50 by kjikuhar #+# #+# */
/* Updated: 2025/06/02 15:16:38 by kjikuhar ### ########.fr */
/* */
/* ************************************************************************** */

// Floyd's Toroise & Hare algorithm
// Cycle detection

int findDuplicate(int* nums, int n)
{
int tortoize;
int hare;

tortoize = 0;
hare = 0;
while (1)
{
tortoize = nums[tortoize];
hare = nums[nums[hare]];
if (tortoize == hare)
break ;
}
tortoize = 0;
while (1)
{
tortoize = nums[tortoize];
hare = nums[hare];
if (tortoize == hare)
break ;
}
return (hare);
}


#include <stdio.h>

int main(void)
{
int nums[] = {3, 1, 3, 4, 2};
int numsSize = 5;

printf("%d\n", findDuplicate(nums, numsSize));
return (0);
}
12 changes: 6 additions & 6 deletions create.sh
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ git checkout -b "$DIR_NAME"
mkdir -p "$DIR_NAME"

# Create empty files under the directory
touch "$DIR_NAME/step1.py"
touch "$DIR_NAME/step2.py"
touch "$DIR_NAME/step3.py"
touch "$DIR_NAME/step1.c"
touch "$DIR_NAME/step2.c"
touch "$DIR_NAME/step3.c"
touch "$DIR_NAME/memo.md"

# Populate memo.md using a heredoc
Expand All @@ -57,15 +57,15 @@ $URL

### step1

*
*

### step2

*
*

### step3

*
*
EOF

echo "Directory '$DIR_NAME' created with template files: step1.py, step2.py, step3.py, memo.md"
Empty file modified prcreate.sh
100644 → 100755
Empty file.