1.複雑さの分析
まず第一に、データ構造とアルゴリズムの本質は「高速」と「節約」の問題を解決することであることを明確にする必要があります。アルゴリズムの品質を説明するには、複雑さの分析が必要です。複雑さの分析は、次の2つのタイプに分けることができます。
-
時間の複雑さ
-
スペースの複雑さ
時間の複雑さはアルゴリズムを記述する速度であり、空間の複雑さはアルゴリズムを記述することの節約です。一般的に言って、複雑さは時間の複雑さです。結局のところ、現代のコンピューターのストレージスペースはもはやそれほどタイトではありません。時間の複雑さは私たちの主要な研究内容です。
第二O
に、表現の非常に複雑な
まず、コードの一部を見て、1~n
累積合計を探します。
int demo(int n) {
int i;
int sum = 0;
for(i=1; i<n; i++) {
sum += i;
}
return sum;
}
次に、このコードの実行時間を見積もります(以下は時間の複雑さの例であり、スペースの複雑さについては最後に説明します)。
CPU
観点からは、コードのすべての行は、データの読み取り-操作-データの書き込みと同様の操作を実行します。ここで、計算の都合上、コードの各行の実行時間は同じであると仮定し、at
は1行に必要な時間をn
表し、データサイズの大きさをT(n)
表し、合計実行時間コードを示します。
では、このコードの合計実行時間はどれくらいですか?数えてみよう。
まず、本体には関数5
ステートメントがあり、1、2、5
ステートメントは合計3
回実行され3*t
ます。必要な時間はです。最初の3、4
ステートメントは毎回実行されn
、必要な時間は2*n*t
です。これら2つのコードセグメントの実行時間を加算すると、このコードに必要な合計時間が得られます。
T(n)=(2 n + 3)t T(n)=(2n + 3)t T (n )=(2 n+3 )t
ルールは、上記の式により求めることができるT(n)
ようにn
大きく大きく、それはますます小さくなります。だから、T(n)
とn
比例し、数学的表記を書き込むことができます。
T(n)= O(f(n))T(n)= O(f(n)) T (n )=O (f (n ))
これはf(n)
実行し、コードセグメントに必要な時間であり、O
示し比例関係を。T(n)
f(n)
式から、コードセグメントの実行に必要な時間は、T(n)= O(2 n + 3)T(n)= O(2n + 3)として表すことができます。T (n )=O (2 n+3 )。これは非常にO
時間計算量の表記です。大きなO
時間計算量は、実際にはリアルタイムを示す特定のコードを実行するのではなく、トレンドデータスケールの増加の変化に伴うコード実行時間を実行します。したがって、時間計算量と呼ばれる時間の漸進的複雑さとしても知られています。
実際にはO(2 n + 3)O(2n + 3)O (2 n+3 )これは最終的な時間計算量を表すものではありません。実際の複雑さの分析では、式の定数、係数、および低次数は通常無視されます。これらの3つの部分は成長傾向に影響を与えないため(時間計算量は実際には漸進的な時間計算量であることを忘れないでください!)、最大の大きさを記録するだけで済みます。時間計算量の最終的な表現はO(n)O(n)です。O (n )。
第三に、複雑さの分析方法
1.最大注文
int demo(int n) {
int i;
int sum = 0;
for(i=1; i<n; i++) {
sum += i;
}
return sum;
}
アルゴリズムまたはコードの時間計算量を分析するときは、ループの実行時間が最も長いコードのみに注目してください。
2.加算のルール
int demo(int n) {
int i;
int sum = 0;
for(i=1; i<n; i++) {
sum += i;
}
for(i=1; i<n; i++) {
int j;
for (j=1; j<n; j++)
sum += i;
}
return sum;
}
コードにさまざまなレベルの時間計算量がある場合、合計時間計算量は、最大の大きさのコードの時間計算量に等しくなります。
3.掛け算の法則
int demo(int n) {
int i;
int sum = 0;
for(i=1; i<n; i++) {
int j;
for (j=1; j<n; j++)
sum += i;
}
return sum;
}
入れ子、関数呼び出し、再帰などの場合は、各部分を乗算するだけで済みます。
第四に、複雑さの大きさ
-
一定の順序:O(1)O(1)O (1 )
-
対数順:O(logn)O(\ log n)O (lo gn )
-
線形順序:O(n)O(n)O (n )
-
線形対数次数:O(nlogn)O(n \ log n)O (nlo gn )
-
二乗次数:O(n 2)O(n ^ 2)O (n2)
-
キュービックオーダー:O(n 3)O(n ^ 3)O (n3)
-
k
電力順序:O(nk)O(n ^ k)O (nk) -
指数次数:O(2 n)O(2 ^ n)O (2n )
-
階乗の順序:O(n!)O(n!)O (n !)
上記の異なる大きさは、多項式の大きさと非多項式の大きさの2つのカテゴリに分類できます。それらの中で、2つの非多項式の大きさだけがあります:O(2 n)O(2 ^ n)O (2n)およびO(n!)O(n!)O (n- !)、非多項式NP
問題としても知られています。
通常の状況では、一般的な複雑さはO(1)O(1)のみです。O (1 )、O(logn)O(\ log n)O (lo gn )、O(n)O(n)O (n )、O(nlogn)O(n \ log n)O (nlo gn )、O(n 2)O(n ^ 2)O (n2)これら5つの場合、一般的に使用される分析方法には、最大次数、加算の規則、および乗算の規則が含まれます。これらが習得されている限り、基本的に大きな問題はありません。
5、時間計算量
時間計算量を分析しましたが、それでも少し小さな問題があります。たとえば、n
配列の長さインデックスの要素を見つけたい場合です。順番にトラバースする場合、理想的な状況は、最初のものが探しているものであるため、時間計算量はO(1)
;最後のものが必要なデータを見つけた場合、その時間計算量はO(n)
です。
さまざまな状況でコードの複雑さの一部が現れる時間の同じ桁違いを解決するには、コードの時間の複雑さをより正確かつ包括的に説明するために、時間の複雑さの分類をさらに洗練する必要があります。4
コンセプトについて紹介しました。
1.ベストケースの時間計算量
最も理想的な場合のコード実行の時間計算量。
2.最悪の場合の時間計算量
最悪の場合のコード実行の時間計算量。
3.平均ケース時間の複雑さ
上記の2つの最良のケースと最悪のケースは小さな確率のイベントであり、平均的なケースの時間計算量はアルゴリズムの時間計算量を最もよく表しています。平均的なケースの時間計算量には分析の確率を導入する必要があるため、加重平均時間計算量とも呼ばれます。
4.償却時間の複雑さ
通常の状況では、コードは実行中に低レベルの複雑さであり、非常にまれなケースで高レベルの複雑さが表示されます。これにより、高レベルの複雑さを各低レベルの複雑さに均等に分散できます。この分析では、アイデアの償却分析。
実際、時間の複雑さを知る必要があるだけです。これらの4つの方法はすべて、時間計算量のいくつかの特殊なケースを補足するものであり、それを研究するために多くの労力を費やす必要はありません。おそらく、このタイプの時間計算量分類があることをご存知でしょう。脳障害のあるインタビュアーがいるこれについて質問したい場合は、自分で情報や調査を見つけることができます。ここでは説明しません。
6、スペースの複雑さ
前に説明したように、時間計算量は漸進的な時間計算量であり、アルゴリズムの実行時間とデータのスケールの間の成長関係を表します。次に、スペースの複雑さはプログレッシブスペースの複雑さであり、アルゴリズムのストレージスペースとデータスケールの間の成長関係を表します。
コードの一部を見て、新しい配列を定義し、割り当て後に出力をトラバースします。
void demo(int n) {
int i;
int data[n];
for(i=0; i<n; i++) {
data[i] = i * i;
}
for(i=0; i<n; i++) {
printf("%d\n", data[i]);
}
}
時間計算量分析では1
、ステートメントの本体の関数は一定の順序であり、無視します。最初の2
ステートメントは配列型のサイズn
を適用するint
ため、コードの複雑さの空間全体はO(n)O(n)です。O (n )。