C言語学習summary_3(最強の再帰分析)

1.順序付けられた配列のバイナリ検索を実装する関数を記述します(配列が順序付けられており、昇順か降順かがわかっている場合)

```c
#include<stdio.h>
#include<windows.h>
int BinSearch(int arr[], int length, int targetNum){
    
    
	int left = 0;
	int right = length - 1;
	int mid = 0;
	while (left <= right){
    
    
		mid = left + (right - left)  >>1;
		//数组元素为降序排列,若为升序,left和right更新方式发生改变
		if (targetNum<arr[mid]){
    
    
			left = mid + 1;
		}
		else if (targetNum>arr[mid]){
    
    
			right = mid - 1;
		}
		else{
    
    
			return mid;
		}
	}
	return -1;//数组中没有此元素
}
int main(){
    
    
	int arr[] = {
    
     99, 88, 77, 66, 55, 44, 33, 22, 11, 1 };
	int length = sizeof(arr) / sizeof(arr[0]);
	int targetNum = 99;
	int ret = BinSearch(arr, length, targetNum);
	if (-1 == ret){
    
    
		printf("没有此元素!");
	}
	else{
    
    
		printf("找到了,元素下标为%d\n", ret);
	}
	system("pause");
	return 0;
}

2. gotoステートメントの使用:
原則として、gotoの使用は、コードのロジックが混乱する原因となるため、可能な限り避ける必要があります。
ただし、gotoステートメントは、一度に2つ以上のループからジャンプするなど、特定の深くネストされた構造の処理を終了するなど、状況によっては引き続き使用できます。LinuxOSソースコードはgotoステートメントを多用します

3.アドレスによる受け渡しと値による受け渡し
1)実パラメーター(実パラメーター):
関数に渡される実パラメーターは実パラメーターと呼ばれ、定数、変数、式、関数などがあります。実際のパラメーターのタイプに関係なく、これらの値を仮パラメーターに渡すことができるように、関数が呼び出されるときにそれらは特定の値を持っている必要があります。
2)仮パラメーター(仮パラメーター):
仮パラメーターは、関数名の後の括弧内の変数を指します。仮パラメーターは、関数が呼び出されている間のみインスタンス化(割り当てられたメモリー単位)されるため、仮パラメーターと呼ばれます。関数呼び出しが完了すると、仮パラメーターは自動的に破棄されます。したがって、仮パラメータは関数でのみ有効です。
具体的に
は、仮パラメータインスタンス化は、関数が呼び出された後、関数本体が実行される前に発生します。
以下をコードと組み合わせて説明します

#include <stdio.h>
#include <windows.h>
void Swap1(int x, int y){
    
    
	int temp = 0;
	temp = x;
	x = y;
	y = temp;
}
void Swap2(int *px, int *py){
    
    
	int temp = 0;
	temp = *px;
	*px = *py;
	*py = temp;
}
int main(){
    
    
	int num1 = 23;
	int num2 = 24;
	/*Swap1(num1, num2);
	printf("Swap1::num1 = %d num2 = %d\n", num1, num2);*/

	Swap2(&num1, &num2);
	printf("Swap2::num1 = %d num2 = %d\n", num1, num2);
	
	system("pause");
	return 0;
}

ここに画像の説明を挿入
ここで、Swap1が関数呼び出しにある場合、xとyには独自のスペースがあり、実際のパラメーターとまったく同じ内容であることがわかります。したがって、次のように考えることができます。仮パラメータのインスタンス化は、実際のパラメータの一時的なコピーと同等です。
関数呼び出し:値による呼び出し、アドレスによる呼び出し。
1)値による呼び出し:関数の仮パラメーターと実パラメーターは異なるメモリーブロックを占有し、仮パラメーターの変更(関数本体で特定の操作を実行するなど)は実際のパラメーターに影響を与えません。
2)アドレスによる呼び出し:次の図を参照してください
ここに画像の説明を挿入
。スワップのパラメータータイプは、ポインター変数であるint *です。アドレスによる呼び出しは、関数の外部で作成された変数のメモリアドレスをに渡すことによって関数を呼び出す方法です。関数パラメーター。
このパラメータ転送方法により、関数は関数外の変数との実際の接続を確立できます。つまり、関数外の変数は関数内で直接操作できます。
ここに画像の説明を挿入
最終結果はこれです。
4. C言語には文字列データ型はありませんが、文字列はあります。文字配列とポインタで表すことができます。

char str[64] = "chenzhihao";
char *str = "chenzhihao";

5.再帰:
アルゴリズムとして、再帰はプログラミング言語で広く使用されています。プロシージャまたは関数には、それ自体を直接または間接的に呼び出す方法があります。通常、大規模で複雑な問題を元の問題と同様の小さな問題に変換します。再帰的戦略では、問題を解決するプロセスで必要な複数の反復操作を表すために必要なプログラムの数が少なく、コードが大幅に削減されます。プログラムが測定されます。再帰についての主な考え方は、大きなものを小さくすることです。tioajian
再帰に必要な2つの条件:
1)制限条件があり、この制限条件が満たされると、再帰は続行されません。
2)各再帰呼び出しの後、この制限にますます近づきます。
再帰の原理を説明する例を見てみましょう(ネットワーク全体で絶対に明確です!見栄えがします!)
最初に質問をしてみましょう:
符号なし整数値を受け取り、その各桁を順番に出力します。例:Enter 1234、Output 1 2 3 4

//用递归实现输入1234,输出1 2 3 4
int PrintInt(int n){
    
    
	if (n > 10){
    
    
		PrintInt(n/10);//方法内调用自己	
	}
	printf("%d ", n %10);

}
在main函数中调用:
int a = 1234;
	PrintInt(a);

まず、いくつかの事前知識を説明します:
C言語の各プログラムはアドレス空間図に対応します
.C言語開発エンジニアの場合、プログラムのメモリ領域の論理分割は次の図に示すようになります:
ここに画像の説明を挿入
図から、Cプログラムは次のようになります。アドレス空間は、コード領域、文字定数領域、グローバルデータ領域、ヒープ、およびスタックで構成できます。上から下へ、上位アドレスから下位アドレスへ。このアドレス空間は、プログラムがメモリにロードされている限り常に存在します。私たちが通常定義するグローバル変数はグローバルデータ領域にあり、プログラムが実行されている限り、グローバル変数のライフサイクルはプログラムまたはプロジェクトと一致しています。このことについては後で話す予定です。再帰アルゴリズムの原理を明確にするためには、スタックを明確にする必要があります。次の図を見てみましょう。
ここに画像の説明を挿入
プログラムの実行中に、プログラムエントリのメイン関数を入力すると、スタックフレームと呼ばれるメモリ領域がスタックメモリに開かれます。スタックフレームでは、関数で定義されたローカル変数は次のとおりです。 int n = 1234;
次に、PrintIntメソッドを呼び出すと、メインスタックフレームの下に別のメモリ領域(PrintIntスタックフレーム)が開きます。次に、プログラムのロジックに従ってPrintInt(123)の実行が開始され、同様に開きます。そのスタックフレーム、そして最後にPrintInt(1); printf( "%d"、n%10);
を実行し、スタックフレームを解放し、その隣にある前の関数PrintInt(12)の実行結果を返し、次に戻ります。同じようにPrintInt(123)、次にPrintInt(1234);それで終わりです。
最終的な実行結果は、
ここに画像の説明を挿入
再帰を使用して階乗およびフィボナッチ数列を実装できることです。
1)nを見つける!
方法1:ループを使用する

int Fact1(int n){
    
    
	int ret = 1;
	for (int i = n; i > 1; i--){
    
    
		ret *= i;
	}
	return ret;
}

方法2:再帰を使用する

int Fact2(int n){
    
    
	if (n <= 2){
    
    
		return n;
	}
	return n*Fact2(n-1);
}

2)フィボナッチ数列のn番目の数を見つけます(n> = 2):(関数呼び出しが繰り返されることが多く、スタックフレームの開発と解放にも時間がかかるため、再帰の使用効率は非常に低くなります)、ループを使用して最適化できます。

int Fib(int n){
    
    
	
	if (n <= 2){
    
    
		return 1;
	}
	/*if (n == 3){
		count++;
	}*/
	return Fib(n - 1) + Fib(n - 2);
}

全体として、複雑な計算問題を段階的に同じロジックに分割するのは再帰ですが、5などの小さな計算問題です。、5に聞いてください!5 4に相当、4!= 4 3!、3!= 3 * 2!、..。だから5!最小のコンピューティングユニット2に分割してください!2!と1!のため それ自体が再帰的な出口です。
これは、最初に述べた再帰問題の2つの必要条件を確認するためだけのものです
。1)制限が満たされると制限があります。再帰は続行されません。
2)各再帰呼び出しの後、この制限にますます近づきます。

おすすめ

転載: blog.csdn.net/CZHLNN/article/details/109150475
おすすめ