配列がソートされているという事実を利用して、アルゴリズムを改善できるかどうかを確認するのは興味深いことです。最初に頭に浮かぶのは、これです。入力配列内のすべての数値が一意であることがわかっている場合、配列内の要素の範囲について[i, j]
、実際に調べなくても、その範囲内の要素が連続しているかどうかをすぐに判断できます。範囲を介して。この関係が成り立つ場合
array[j] - array[i] == j - i
そうすれば、その範囲の要素は連続しているとすぐに言うことができます。この基準は、明らかに、配列がソートされており、数値が繰り返されないという事実を使用しています。
ここで、その基準を利用するアルゴリズムを開発する必要があります。考えられる再帰的アプローチの1つは次のとおりです。
- 再帰ステップの入力は要素の範囲です
[i, j]
。最初は[0, n-1]
、配列全体です。
- 上記の基準を範囲に適用します
[i, j]
。範囲が連続していることが判明した場合、それをさらに細分化する必要はありません。範囲を出力に送信します(詳細については、以下を参照してください)。
[i, m]
それ以外の場合(範囲が連続していない場合)、2つの等しい部分とに分割し[m+1, j]
ます。
- 下部(
[i, m]
)でアルゴリズムを再帰的に呼び出し、次に上部()でアルゴリズムを呼び出し[m+1, j]
ます。
上記のアルゴリズムは、左から最初のアプローチを使用して、配列のバイナリパーティションとパーティションツリーの再帰下降を実行します。これは、このアルゴリズムが左から右の順序で連続する要素を持つ隣接するサブ範囲を見つけることを意味します。あなたがする必要があるのは、隣接するサブレンジを一緒に結合することです。手順2で「出力に送信」されたサブ範囲を受信した[i, j]
場合、それらが実際に連続している場合は、以前に受信したサブ範囲と連結する必要があります。または、連続していない場合は、新しい範囲を開始する必要があります。その間ずっと、これまでに見つかった「最長の連続範囲」を追跡しています。
それでおしまい。
このアルゴリズムの利点は、連続する要素のサブレンジを、これらのサブレンジの内部を見ることなく「早期に」検出することです。明らかに、最悪の場合のパフォーマンス(連続するサブ範囲がまったくない場合)はまだO(n)
です。最良の場合、入力配列全体が連続している場合、このアルゴリズムはそれを即座に検出します。(私はまだこのアルゴリズムの意味のあるO推定に取り組んでいます。)
このアルゴリズムの使いやすさは、一意性の要件によって損なわれます。あなたの場合、それが「与えられた」ものであるかどうかはわかりません。
とにかく、これが可能なC++実装です
typedef std::vector<int> vint;
typedef std::pair<vint::size_type, vint::size_type> range;
class longest_sequence
{
public:
const range& operator ()(const vint &v)
{
current = max = range(0, 0);
process_subrange(v, 0, v.size() - 1);
check_record();
return max;
}
private:
range current, max;
void process_subrange(const vint &v, vint::size_type i, vint::size_type j);
void check_record();
};
void longest_sequence::process_subrange(const vint &v,
vint::size_type i, vint::size_type j)
{
assert(i <= j && v[i] <= v[j]);
assert(i == 0 || i == current.second + 1);
if (v[j] - v[i] == j - i)
{ // Consecutive subrange found
assert(v[current.second] <= v[i]);
if (i == 0 || v[i] == v[current.second] + 1)
// Append to the current range
current.second = j;
else
{ // Range finished
// Check against the record
check_record();
// Start a new range
current = range(i, j);
}
}
else
{ // Subdivision and recursive calls
assert(i < j);
vint::size_type m = (i + j) / 2;
process_subrange(v, i, m);
process_subrange(v, m + 1, j);
}
}
void longest_sequence::check_record()
{
assert(current.second >= current.first);
if (current.second - current.first > max.second - max.first)
// We have a new record
max = current;
}
int main()
{
int a[] = { 1, 3, 4, 5, 6, 8, 9 };
std::vector<int> v(a, a + sizeof a / sizeof *a);
range r = longest_sequence()(v);
return 0;
}