[ソードフィンガーオファー]-配列内の繰り返し数

トピックの要件:

長さnの配列のすべての数値は、0からn-1の範囲です。配列内のいくつかの数字が繰り返されていますが、繰り返し数がわかりません。各数字が何回繰り返されるかわかりません。配列内で重複する番号を見つけてください。たとえば、長さ7の入力配列が{2,3,1,0,2,5,3}の場合、対応する出力は最初に繰り返される数値2です。

解決策:

方法1:
配列を最初に並べ替え、並べ替えられた配列から重複する数値を見つけるのは簡単な作業ですが、時間の複雑さO(nlogn)は非常に高くなります

方法2:
長さnの配列のすべての数値は0〜n-1の範囲にあるため、繰り返される数値がない場合、配列がソートされた後、添え字iの位置に数値iが表示されます。ただし、番号が繰り返されているため、場所によっては複数の番号があり、場所によっては番号がない場合があります。配列{2、3、1、0、2、5、3}などの
メソッドを説明する具体的な例を見てみましょう
最初の数は2です!=添え字0なので、2を添え字2の位置にスワップします。
ここに画像の説明を挿入
i ++、比較を続行、2番目の数値3!=添え字1、したがって3を添え字3の位置に
ここに画像の説明を挿入
i ++に交換し、比較を続行、3番目の数値2 ==添え字2、交換しない。
i ++、比較を続け、4番目の数値3 ==下付き3、交換しない。
I ++、5番目の数値2を比較し続けます!=下付き4、数値[4]を比較==数値[数値[4]]。そのため、重複する値
コードの実装が見つかりました:

bool duplicate(int number[], int length, int* duplicate)
{
	if (number == nullptr || length <= 0)
	{
		return false;
	}
	for (int i = 0; i < length; i++)
	{
		if (number[i]<0 || number[i]>length - 1)
		{
			return false;
		}
	}
	for (int i = 0; i < length; i++)
	{
		while (i != number[i])
		{
			if (number[i] == number[number[i]])
			{
				*duplicate = number[i];
				return true;
			}
			int tmp = number[i];
			number[i] = number[tmp];
			number[tmp] = tmp;
		}
	}
	return false;
}	

トピックの改善:

上記の質問に基づいて入力できない配列

解決策:

方法1:
長さn + 1の補助配列を作成し、元の配列の各数値を1つずつ補助配列にコピーし、値mの数値をmでマークされた位置にコピーします。しかし、そうすることのスペースの複雑さはO(n)です。だから私たちは別の方法を思いついた

方法2:それ
はまだ配列{2,3,5,4,3,2,6,7}などの特定の配列で説明されています。8つの数字があります。中央の4は配列を2つの部分に分割し、1つのセクションは1〜4で、もう1つのセクションは5〜7です。1〜4
ここに画像の説明を挿入
の数値は後者よりも大きいため、繰り返し数は1〜4の間でなければなりません。
次に、真ん中の2を2つのグループに分けます。1-2、3-4、3-4の
ここに画像の説明を挿入
数字は前者よりも大きいので、繰り返し数は3-4の間でなければなりません。
次に、これらの2つの数値が配列に現れる回数を数えます。

コードは次のように実装されます。

int getcount(int* number, int length, int start, int end)
{
	if (number == nullptr)
		return 0;
	int count = 0;
	for (int i = 0; i < length; i++)
	{
		if (number[i] >= start && number[i] <= end)
		{
			count++;
		}
	}
	return count;
}
int getduplicate(int* number, int length)
{
	if (number == nullptr || length <= 0)
		return -1;
	int end = length - 1;//7
	int start = 1;//1
	while (end >= start)
	{
		int middle = ((end - start) >> 1) + start;//4
		int count = getcount(number, length, start, middle);
		if (end == start)
		{
			if (count > 1)
				return start;
			break;
		}
		if (count > (middle - start + 1))//5>4 所以在1-4里面再划分
			end = middle;
		else
			start = middle;
	}
	return -1;
}

この方法はバイナリ検索に似ており、関数getcountはO(logn)回呼び出され、毎回O(n)時間を必要とするため、時間の複雑度はO(nlogn)ですが、空間の複雑度はO(1)です。これは、時間対空間アルゴリズムです。

公開された98元の記事 ウォンの賞賛9 ビュー3660

おすすめ

転載: blog.csdn.net/qq_43412060/article/details/105254115