交錢做網(wǎng)站對(duì)方拿了錢不做該怎么辦網(wǎng)站維護(hù)工程師
疊甲:以下文章主要是依靠我的實(shí)際編碼學(xué)習(xí)中總結(jié)出來(lái)的經(jīng)驗(yàn)之談,求邏輯自洽,不能百分百保證正確,有錯(cuò)誤、未定義、不合適的內(nèi)容請(qǐng)盡情指出!
文章目錄
- 1.何為控制語(yǔ)句
- 2.控制語(yǔ)句-分支語(yǔ)句
- 2.1.if
- 2.2.switch
- 3.控制語(yǔ)句-循環(huán)語(yǔ)句
- 3.1.while
- 3.2.do...while
- 3.3.for
- 4.控制語(yǔ)句-轉(zhuǎn)向語(yǔ)句
- 4.1.break
- 4.2.continue
- 4.3.goto
- 5.一些簡(jiǎn)單練習(xí)
- 5.1.倒計(jì)時(shí)關(guān)機(jī)程序
- 5.2.判斷一個(gè)數(shù)是否為素?cái)?shù)
- 5.3.計(jì)算 1!+2!+...+n! 的值
- 5.4.求解兩個(gè)數(shù)的最小公倍數(shù)和最大公因數(shù)
- 5.4.1.公倍數(shù)和公因數(shù)概念
- 5.4.2.樸素求得最小公倍數(shù)
- 5.4.3.樸素求得最大公約數(shù)
- 5.4.4.LCM 和 GCD 的關(guān)系
- 5.4.5.使用輾轉(zhuǎn)相除法解決
- 5.5.使用循環(huán)來(lái)清空緩沖區(qū)
- 5.6.簡(jiǎn)易二分法查找算法
1.何為控制語(yǔ)句
一份 C
代碼由多種語(yǔ)句組合而成, C
的語(yǔ)句主要分為下面幾種:
- 表達(dá)式語(yǔ)句
- 函數(shù)調(diào)用語(yǔ)句
- 控制語(yǔ)句
- 復(fù)合語(yǔ)句
- 空語(yǔ)句
我這里主要給您提及控制語(yǔ)句的相關(guān)語(yǔ)法,其他的語(yǔ)句的相關(guān)概念您可以簡(jiǎn)單使用 google/bing/chatGPT
查詢一下…
C
語(yǔ)言是結(jié)構(gòu)化的程序設(shè)計(jì)語(yǔ)言,而這些結(jié)構(gòu)實(shí)際上是對(duì)現(xiàn)實(shí)生活中的一種抽象,基本所有要做的事情都會(huì)包含“順序、選擇、循環(huán)”結(jié)構(gòu)。
因此映射到 C
語(yǔ)言中就對(duì)應(yīng)了三種控制語(yǔ)句:
- 分支語(yǔ)句(
if、switch
) - 循環(huán)語(yǔ)句(
do...while、while、for
) - 跳轉(zhuǎn)語(yǔ)句(
break、goto、continue、return
)
控制語(yǔ)句可以按照某種要求,跳轉(zhuǎn)到對(duì)應(yīng)的代碼中進(jìn)行執(zhí)行。
在分支語(yǔ)句和循環(huán)語(yǔ)句中,依靠表達(dá)式的真假(零表示真,非零為假)來(lái)跳轉(zhuǎn)代碼決定執(zhí)行哪些語(yǔ)句。但跳轉(zhuǎn)語(yǔ)句則無(wú)需在意真假,是無(wú)條件的跳轉(zhuǎn)代碼。
另外,多種控制語(yǔ)句直接也可以互相嵌套使用,構(gòu)成復(fù)雜的代碼…
2.控制語(yǔ)句-分支語(yǔ)句
2.1.if
if
語(yǔ)句會(huì)分為單分支、雙分支、多分支的情況,一個(gè) if
語(yǔ)句可以被視為一個(gè)整體,經(jīng)過(guò)邏輯判斷后,只能選擇執(zhí)行其中一個(gè)分支的代碼。
而所謂的邏輯判斷就是指判斷表達(dá)式內(nèi)的真假,if
語(yǔ)句一旦遇到:
- 表達(dá)式的整體結(jié)果判定為假的時(shí)候,就會(huì)跳過(guò)跟在后面的代碼塊的所有內(nèi)容,繼續(xù)判斷下一個(gè)表達(dá)式的真假
- 表達(dá)式的整體結(jié)果判定為真的時(shí)候,就會(huì)立刻執(zhí)行后面跟著的代碼塊的內(nèi)容,執(zhí)行完后就拋棄其他分支的代碼,也就執(zhí)行完一條
if
語(yǔ)句
而表達(dá)式的真和假根據(jù) 零表示真,非零為假 來(lái)判斷,例如:
- 對(duì)于代碼
int num = 1; num + 3;
中num + 3
的整體結(jié)果就是真 - 對(duì)于代瑪
int num = 3; 3 - num
中3 - num
的整體結(jié)果就是假 - 對(duì)于代碼
int x = 9; int y; y = x;
中y = x
的整體結(jié)果就是真
//單分支語(yǔ)句的語(yǔ)法形式
if (表達(dá)式)
{語(yǔ)句0;
}
//雙分支語(yǔ)句的語(yǔ)法形式
if (表達(dá)式)
{語(yǔ)句1;
}
else
{語(yǔ)法2;
}
//多分支語(yǔ)句的語(yǔ)法形式
if (表達(dá)式1)
{語(yǔ)句1;
}
else if (表達(dá)式2)
{語(yǔ)句2;
}
else if (表達(dá)式3)
{語(yǔ)句3;
}
//... 其他的 if 語(yǔ)句
else if (表達(dá)式n-1)
{語(yǔ)句n-1;
}
else
{ 語(yǔ)句n;
}
這里舉一個(gè)簡(jiǎn)單的例子:當(dāng)用戶輸入一個(gè)數(shù)字,程序可以通過(guò) if
語(yǔ)句檢查該數(shù)字是否為正數(shù)、負(fù)數(shù)、零,并給出相應(yīng)的提示信息。
//if 語(yǔ)句的使用例子
#include <stdio.h>
int main()
{int num;printf("請(qǐng)輸入一個(gè)整數(shù): ");scanf("%d", &num);if (num > 0){printf("這是一個(gè)正數(shù)\n");}else if (num < 0){printf("這是一個(gè)負(fù)數(shù)\n");}else{printf("這是零\n");}return 0;
}
寫雙分支語(yǔ)句、多分支語(yǔ)句時(shí),如果分支內(nèi)只需要執(zhí)行一條語(yǔ)句,則可以省略代碼塊 {}
,但是語(yǔ)句特別多的時(shí)候最好是要養(yǎng)成加上代碼塊 {}
的習(xí)慣。
//省略代碼塊
#include <stdio.h>
int main()
{int num;printf("請(qǐng)輸入一個(gè)整數(shù): ");scanf("%d", &num);if (num > 0)printf("這是一個(gè)正數(shù)\n");else if (num < 0)printf("這是一個(gè)負(fù)數(shù)\n");elseprintf("這是零\n");return 0;
}
補(bǔ)充:分支語(yǔ)句的書(shū)寫習(xí)慣對(duì)比
//兩種 if 書(shū)寫習(xí)慣的對(duì)比 //代碼1 if (表達(dá)式) {//代碼塊1 } else {//代碼塊2 }//代碼2 if(表達(dá)式) {//代碼塊1 } else {//代碼塊2 }
代碼1
和代碼2
是寫的較多兩種形式,第一種強(qiáng)調(diào)是if
以及內(nèi)容是一個(gè)整體、第二種強(qiáng)調(diào)語(yǔ)句的“塊”狀,兩種寫法都可以,憑您的美感而言。
警告:關(guān)于
==
的判斷寫法分支語(yǔ)句判斷相等時(shí)最常見(jiàn)的錯(cuò)誤就是將
==
寫作=
,漏寫一個(gè)等號(hào)帶來(lái)的困擾有時(shí)是災(zāi)難性的(好在現(xiàn)代的編譯器在很多情況下都會(huì)警告這一做法,不過(guò)您依然不能太過(guò)依賴這點(diǎn))。//有錯(cuò)誤的代碼 int num = 0; if(num = 0) //這里原意是判斷一個(gè)數(shù)是否為 0,但這里變成了賦值語(yǔ)句,把 num 又賦值為 0,導(dǎo)致表達(dá)式的判斷結(jié)果為 0,也就是假,后續(xù)代碼塊將不會(huì)被執(zhí)行... {printf("hehe\n"); }
而關(guān)于這種寫法有一種典型的檢錯(cuò)方法,就是把變量和常量判斷反過(guò)來(lái)寫。
//兩種 == 的判斷寫法 //代碼 1 int num = 1; if(num == 0) {printf("hehe\n"); }
//代碼 2 int num = 1; if(0 == num) //這里把變量和常量反過(guò)來(lái)寫了 {printf("hehe\n"); }
代碼2
的寫法會(huì)比較好一點(diǎn),自帶檢錯(cuò)功能。假設(shè)num == 0
錯(cuò)寫成了num = 0
,那么意義就不同了(相當(dāng)于0
賦值給了num
),編譯器不一定會(huì)報(bào)錯(cuò),但是如果將0 == num
寫成0 = num
,那么編譯器就會(huì)報(bào)錯(cuò),因?yàn)闊o(wú)法使用變量賦值給一個(gè)常量。不過(guò)這種方法也只能規(guī)避常量和變量的情況,無(wú)法規(guī)避變量和變量之間比較的情況。
警告:
if
和else
的就近對(duì)應(yīng)原則(懸空else
)//有問(wèn)題的代碼 #include <stdio.h> int main() {int num1 = 0;int num2 = 1;if(num1 == 1) //第一個(gè) ifif(num2 == 1) //第二個(gè) ifprintf("hello");else //這個(gè) else 故意不進(jìn)行縮進(jìn)printf("limou");return 0; }
實(shí)際上第二個(gè)
if
才是和else
對(duì)應(yīng)的(這里是故意不縮進(jìn)的),這里發(fā)現(xiàn)程序結(jié)果是什么都不打印。這就是沒(méi)有加上“代碼塊”的后果,有可能造成和您內(nèi)心所需不同的結(jié)果。但是如果加上代碼塊就會(huì)情況就好了一些。//沒(méi)問(wèn)題的代碼 #include <stdio.h> int main() {int num1 = 0;int num2 = 1;if(num1 == 1) //第一個(gè) if{if(num2 == 1) //第二個(gè) if{printf("hello");}}else{printf("limou");}return 0; }
這樣代碼就會(huì)在第一個(gè)表達(dá)式判斷為假時(shí),執(zhí)行
printf("limou");
從而打印"limou"
。因此加上代碼塊,一是增加代碼可讀性,二是減少邏輯錯(cuò)誤。我個(gè)人推薦,就算是單分支語(yǔ)句也最好加上“代碼塊”,不要太過(guò)于省事。
2.2.switch
switch
語(yǔ)句的運(yùn)行和 if
語(yǔ)句有些類似。
//switch 的語(yǔ)法形式
switch(整型表達(dá)式)
{case 值 1:語(yǔ)句 1;break;case 值 2:語(yǔ)句 2;break;case 值 3:語(yǔ)句 3;break;//...還可以添加更多個(gè) case 語(yǔ)句case 值 n-1:語(yǔ)句 n-1;break;case 值 n:語(yǔ)句 n;break;default:語(yǔ)句;break;
}
- 當(dāng)
整型表達(dá)式
的計(jì)算值和case
后的值相匹配時(shí),就會(huì)執(zhí)行case
后續(xù)的代碼,直到遇到break
時(shí)才結(jié)束switch
語(yǔ)句 - 其中
default
子句是默認(rèn)選項(xiàng),當(dāng)整型表達(dá)式
的結(jié)果均不滿足值1~值n
時(shí),就會(huì)執(zhí)行default
后的語(yǔ)句 - 每條
switch
只能有一條default
子句,default
放在其他case
之前之后都可以,并沒(méi)有順序要求 - 最后一個(gè)
default
的break
不寫也沒(méi)有問(wèn)題,當(dāng)然最好還是加上為好,維護(hù)代碼的時(shí)候可以減少bug
的產(chǎn)生
case
后面的多條語(yǔ)句可以不加大括號(hào),當(dāng)然最好還是寫上,邏輯會(huì)更加清晰一些…
補(bǔ)充:
if
和switch
最大的差別在于if
可以范圍判斷,switch
是枚舉判斷。
注意一個(gè)上面細(xì)節(jié) 直到遇到 break 時(shí)才結(jié)束 switch 語(yǔ)句,這句話很值得琢磨,如果匹配到對(duì)應(yīng)的 case
選項(xiàng)后沒(méi)有遇到 break
怎么辦?可以看看下面的的代碼,當(dāng) b = 1 或 2
時(shí)都是輸出 "hello 你好 default"
,當(dāng) b = 3 或 4
時(shí)都是輸出 "你好 default"
//缺少 break 的 switch 語(yǔ)句
#include <stdio.h>
int main()
{int num = 0;scanf("%d", &num);switch (num){case 1:case 2:printf("hello ");case 3:case 4:printf("你好 ");default:printf("default");}return 0;
}
因此,可以利用這一特性來(lái)達(dá)到多種選擇對(duì)應(yīng)同一個(gè)結(jié)果的目的。
//多個(gè) case 標(biāo)簽指向相同的語(yǔ)句塊
#include <stdio.h>int main()
{int choice;//提示用戶輸入選擇printf("請(qǐng)選擇選項(xiàng)(1、2、3): ");scanf("%d", &choice);//使用switch語(yǔ)句根據(jù)用戶的選擇執(zhí)行相應(yīng)的操作switch(choice){case 1:case 2:case 3:{printf("您選擇了選項(xiàng) 1 或 2 或 3\n");break;}case 4:{printf("您選擇了選項(xiàng) 4\n");break;}default:{printf("無(wú)效的選項(xiàng)\n");break;}}return 0;
}
警告:在一個(gè)
case
結(jié)束時(shí),忘記寫break
語(yǔ)句也會(huì)引發(fā)一些邏輯錯(cuò)誤,這種錯(cuò)誤只有通過(guò)調(diào)試解決,編譯器不會(huì)做任何警告或報(bào)錯(cuò)。
警告:
switch
后必須是整形變量表達(dá)式,case
也必須是整形常量表達(dá)式,不然無(wú)法運(yùn)行。
3.控制語(yǔ)句-循環(huán)語(yǔ)句
3.1.while
//while 語(yǔ)法形式
while (表達(dá)式)
{//代碼
}
通過(guò) while
語(yǔ)句可以在表達(dá)式為真時(shí),重復(fù)執(zhí)行代碼塊內(nèi)部的代碼。
//打印出 0 到 9 的平方結(jié)果
#include <stdio.h>
int main()
{int i = 0;while (i < 10){printf("%d * %d = %d\n", i, i, i * i);i++;}return 0;
}
3.2.do…while
//do...while 語(yǔ)法形式
do {//代碼
} while (表達(dá)式);
do...while
的顯著特點(diǎn)就是,至少會(huì)執(zhí)行一次循環(huán)體里的語(yǔ)句,使用場(chǎng)景少見(jiàn),但也不是沒(méi)有。
//打印出 0 到 9 的平方結(jié)果(和上面代碼是等價(jià)的)
#include <stdio.h>
int main()
{int i = 0;do{printf("%d * %d = %d\n", i, i, i * i);i++;} while(i < 10);return 0;
}
警告:
while()
后面是有;
的,不要遺漏…
3.3.for
//for 語(yǔ)法形式
for (初始迭代變量; 判斷迭代變量; 更新迭代變量)
{語(yǔ)句;
}
for
語(yǔ)句的執(zhí)行順序如下:
- 初始迭代變量:在循環(huán)開(kāi)始時(shí),初始化迭代變量。這是在循環(huán)的第一次迭代之前執(zhí)行的。通常用來(lái)初始化計(jì)數(shù)器或設(shè)置初始條件。
- 判斷迭代變量:在每次迭代之前,判斷迭代變量的值是否滿足循環(huán)條件。如果條件為真(非零),則繼續(xù)執(zhí)行循環(huán)體;如果條件為假(零),則跳出循環(huán),執(zhí)行循環(huán)后面的代碼。
- 執(zhí)行循環(huán)體:如果循環(huán)條件為真,則執(zhí)行循環(huán)體中的語(yǔ)句。循環(huán)體可以是單個(gè)語(yǔ)句或語(yǔ)句塊,可以包含任意合法的C語(yǔ)句。
- 更新迭代變量:在每次迭代結(jié)束后,更新迭代變量的值。這一步通常用來(lái)遞增或遞減迭代變量,以控制循環(huán)的迭代次數(shù)或改變循環(huán)條件。
- 再次判斷迭代變量:在更新迭代變量之后,再次判斷迭代變量的值是否滿足循環(huán)條件。如果條件為真,則繼續(xù)執(zhí)行下一次迭代,跳轉(zhuǎn)步驟
2
;如果條件為假,則退出循環(huán),執(zhí)行循環(huán)后面的代碼。 - 退出循環(huán):當(dāng)循環(huán)條件不滿足時(shí),退出循環(huán),執(zhí)行循環(huán)后面的代碼。
實(shí)際上 for
語(yǔ)句是 while
在實(shí)踐經(jīng)驗(yàn)中總結(jié)出來(lái)的語(yǔ)法,兩者完全可以互相轉(zhuǎn)換,寫出等價(jià)的循環(huán)代碼。
//while 循環(huán)的代碼
#include <stdio.h>
int main()
{printf("while 循環(huán): \n");int i = 0; //初始迭代變量while (i < 5) //{printf("%d ", i); //需要循環(huán)的代碼i++; //更新迭代變量}printf("\n");return 0;
}
//for 循環(huán)
#include <stdio.h>
int main()
{printf("for 循環(huán):\n");for (int i = 0; i < 5; i++) //這里就包含了 初始迭代變量; 判斷迭代變量; 更新迭代變量{printf("%d ", i); //需要循環(huán)的代碼}printf("\n");return 0;
}
上述的 while
和 for
代碼是完全等價(jià)的,而 for
循環(huán)語(yǔ)句從形式上比 while
有優(yōu)勢(shì),修改賦值、條件、執(zhí)行語(yǔ)句一目了然。
這里有一個(gè)細(xì)節(jié),i < 5
并沒(méi)有寫成 i <= 4
,兩者實(shí)際都正確,但是一般習(xí)慣寫成左閉右開(kāi)區(qū)間,這樣做可以提高可讀性。尤其是在遍歷數(shù)組的時(shí)候。
//使用左閉右開(kāi)的范圍
#include <stdio.h>
int main()
{int arr[5] = { 1, 2, 3, 4, 5 };for (int i = 0; i < 5; i++) //這里填 5 和數(shù)組大小是一樣的,程序員一看就知道這個(gè)循環(huán)會(huì)遍歷 5 次,比寫 4 會(huì)多一層意義{printf("%d ", i);}return 0;
}
還有一點(diǎn)比較有趣的是 for
循環(huán)里的三個(gè)表達(dá)式是“表達(dá)式”,因此還可以寫更長(zhǎng)的代碼放入里面。
//內(nèi)部表達(dá)式更長(zhǎng)的 for 代碼
#include <iostream>int main()
{//在初始表達(dá)式中使用多個(gè)變量并初始化,并且在迭代表達(dá)式中對(duì)兩個(gè)變量進(jìn)行迭代for (int i = 0, j = 10; i < 5; ++i, --j){std::cout << "i: " << i << ", j: " << j << std::endl;}//在判斷表達(dá)式中使用復(fù)雜的邏輯判斷int sum = 0;for (int i = 0; i < 10 && sum < 100; ++i){sum += i;std::cout << "i: " << i << ", sum: " << sum << std::endl;}return 0;
}
補(bǔ)充:之前我提過(guò),給變量命名最好不要使用單個(gè)字母,而是應(yīng)該給予有意義的英文單詞或英文詞組。但是這里有一個(gè)小例外,由于循環(huán)語(yǔ)句中的迭代變量
i、j、z
已經(jīng)被大家熟用,因此迭代變量不多、且主要目的僅僅是作簡(jiǎn)單遍歷時(shí),使用這幾個(gè)單字符也沒(méi)有什么不好的(因?yàn)榇蠹叶贾姥h(huán)中的i、j、z
是迭代變量,無(wú)需再用其他的英文名做提示。
4.控制語(yǔ)句-轉(zhuǎn)向語(yǔ)句
4.1.break
break
在 C
語(yǔ)言中有兩種作用:
- 跳出循環(huán)體,只要在循環(huán)體內(nèi)部執(zhí)行了
break
語(yǔ)句就不再執(zhí)行循環(huán)體的代碼(這種情況更加常見(jiàn)) - 跳出
switch
選項(xiàng)
//使用 break 語(yǔ)句
#include <stdio.h>
int main()
{int i = 0;while (i < 10){printf("%d\n", i);if(5 == i) //循環(huán)打印到 5 的時(shí)候{break; //直接跳出循環(huán), 這樣的話就不會(huì)打印 5 后面的數(shù)字了}i++;}return 0;
}
警告:
break
在多重循環(huán)中只會(huì)終止一層循環(huán)。//使用 break 跳轉(zhuǎn)多次循環(huán)的代碼 #include <stdio.h> int main() {int matrix[3][3] = {{1, 2, 3},{4, 5, 6},{7, 8, 9}};int target = 5;int found = 0;int row, col;for (row = 0; row < 3; row++){for (col = 0; col < 3; col++){if (matrix[row][col] == target){found = 1;break; //終止內(nèi)部循環(huán)}}if (found) {break; //終止外部循環(huán)}}if (found){printf("找到了目標(biāo)值 %d,位置是 (%d, %d)\n", target, row, col);}else{printf("未找到目標(biāo)值 %d\n", target);}return 0; }
4.2.continue
continue
則是跳出本次循環(huán),直接進(jìn)入下一次的循環(huán)。
//使用 continue 語(yǔ)句
#include <stdio.h>
int main()
{int i = 0;while (i < 10){printf("%d\n", i);if (5 == i) //如果 i 等于 5,則跳過(guò)當(dāng)前循環(huán)迭代,直接執(zhí)行下一次循環(huán)迭代{continue;}i++;}return 0;
}
4.3.goto
goto
是一種利用標(biāo)記來(lái)無(wú)條件跳轉(zhuǎn)的跳轉(zhuǎn)語(yǔ)句,由于沒(méi)有任何條件,容易被濫用,導(dǎo)致代碼邏輯一團(tuán)糟,應(yīng)該盡可能少用。
//使用 goto 語(yǔ)句
#include <stdio.h>
int main()
{goto A_LABEL; //提前跳轉(zhuǎn)了printf("haha\n"); //這部分代碼因?yàn)樘D(zhuǎn)而被忽略了A_LABEL:printf("hehe\n"); //因此只會(huì)打印 hehe, 而不會(huì)打印 hahareturn 0;
}
盡量少用 goto
,但是在多層循環(huán)中如果想實(shí)現(xiàn)跳出多層循環(huán),單純靠 break
有些困難(除非多寫幾個(gè) break
,不然它只能從內(nèi)層循環(huán)退回上一層循環(huán)),這種情況就可以考慮使用 goto
。
//多重循環(huán)下的 goto 代碼
for(...) {for(...) {for(...) {if(disaster) //假設(shè)能夠進(jìn)入這個(gè) if 語(yǔ)句的代碼就說(shuō)明出錯(cuò)了goto error; //直接跳出最外層, 不要再執(zhí)行下去了, 避免出現(xiàn)其他更加意外的地方}}//...
}error://處理錯(cuò)誤情況
5.一些簡(jiǎn)單練習(xí)
這里我簡(jiǎn)單帶你解決一些實(shí)際問(wèn)題,以便讓您思考程序的編寫過(guò)程和適應(yīng)多種語(yǔ)句的組合嵌套。
5.1.倒計(jì)時(shí)關(guān)機(jī)程序
需求:程序運(yùn)行后,倒計(jì)時(shí)
60
秒后開(kāi)始關(guān)機(jī),如果輸入"NO"
則停止關(guān)機(jī)。
首先需要知道怎么在命令行使用命令來(lái)對(duì)計(jì)算機(jī)進(jìn)行關(guān)機(jī)和取消關(guān)機(jī),這里以 Windows11
的電腦為例。使用 [win+r]
輸入 cmd
后回車,得到命令行窗口,輸入以下指令進(jìn)行關(guān)機(jī)。
上述就是使用命令行來(lái)操作電腦,使用命令行可以替代很多鼠標(biāo)能做的操作,而且還可以做一些鼠標(biāo)無(wú)法操作的,早期的程序員在計(jì)算機(jī)中工作是不一定需要用到鼠標(biāo)的,程序員可以利用鍵盤輸入不同的指令來(lái)對(duì)計(jì)算機(jī)進(jìn)行操作。
因此,只要我們知道對(duì)應(yīng)的關(guān)機(jī)指令,還有在 C
語(yǔ)言中通過(guò)某個(gè)庫(kù)函數(shù)來(lái)調(diào)用對(duì)應(yīng)的關(guān)機(jī)指令就行,而這個(gè)庫(kù)函數(shù)在 C 語(yǔ)言里面就是 systen()
,該函數(shù)接受一個(gè) char*
指針指向的字符串,可以執(zhí)行字符串對(duì)應(yīng)的指令,最終結(jié)果在 cmd
中執(zhí)行的效果相同。
//關(guān)機(jī)程序
#include <stdio.h>
#include <string.h> //strcmp() 庫(kù)函數(shù)對(duì)應(yīng)的頭文件
#include <stdlib.h> //system() 庫(kù)函數(shù)對(duì)應(yīng)的頭文件int main()
{char arr[20] = { 0 };system("shutdown -s -t 60");while (1) //一直死循環(huán)就行{printf("您的電腦將在 60s 后關(guān)機(jī),輸入 “NO” 則取消關(guān)機(jī): ");scanf("%s", arr); //獲取輸入if (strcmp(arr, "NO") == 0) //比較用戶輸入的字符串是否為 "NO"{//是則取消關(guān)機(jī)printf("取消關(guān)機(jī)\n");system("shutdown -a");break;}else{//不是則給與提示printf("錯(cuò)誤指令\n");}}return 0;
}
運(yùn)行程序后就會(huì)進(jìn)入關(guān)機(jī)狀態(tài),只有輸入 "NO"
才能取消關(guān)機(jī)。
補(bǔ)充:這里再補(bǔ)充一個(gè)小點(diǎn),如果您希望把程序分享給其他人,則可以在程序項(xiàng)目所在文件路徑下尋找
.exe
文件,然后把這個(gè)文件發(fā)送給別人即可…
發(fā)送給對(duì)方后,直接雙擊這個(gè)文件就可以運(yùn)行您編寫的代碼,當(dāng)然,請(qǐng)您不要惡意整蠱別人…
5.2.判斷一個(gè)數(shù)是否為素?cái)?shù)
需求:用戶給定一個(gè)數(shù)
number
,判斷該數(shù)是不是素?cái)?shù)。
最簡(jiǎn)單的思路是,如果 number
能夠被 [2, number)
中任意一個(gè)數(shù)整除,就說(shuō)明不是素?cái)?shù),也就是所謂的試除法
//試除法(除所有的數(shù))
#include <stdio.h>
#include <stdbool.h>//判斷一個(gè)數(shù)是否為素?cái)?shù)
int IsPrime(int number)
{//小于等于 1 的數(shù)一定不是素?cái)?shù)if (number <= 1){return 0;}//能被整除則不是素?cái)?shù)for (int i = 2; i < number; i++){if (number % i == 0){return 0;}}//代碼走到這里就說(shuō)明 number 一定是素?cái)?shù)return 1;
}int main()
{for (int num = 0; num < 100; num++){if (IsPrime(num)){printf("%d is a prime number.\n", num);}else{printf("%d is not a prime number.\n", num);}}return 0;
}
而如果我們仔細(xì)分析,就會(huì)發(fā)現(xiàn),如果 number
能被整除,就一定有一對(duì)數(shù)都可以對(duì) number
進(jìn)行整除操作。例如,6
可以被 2 或 3
整除。因此如果采用試除法的話,如果一個(gè)數(shù)不是素?cái)?shù),就一定會(huì)在遍歷除法中經(jīng)歷至少兩次整除,并且對(duì)應(yīng)的兩個(gè)因子是一小一大的。我們只需要其中判斷一次,取得一小的因子就行,因此只需要遍歷 number/2
次。
//試除法(除一半的數(shù))
#include <stdio.h>
#include <stdbool.h>//判斷一個(gè)數(shù)是否為素?cái)?shù)
int IsPrime(int number)
{//小于等于 1 的數(shù)一定不是素?cái)?shù)if (number <= 1){return 0;}//能被整除則不是素?cái)?shù)for (int i = 2; i <= number / 2; i++){if (number % i == 0){return 0;}}//代碼走到這里就說(shuō)明 number 一定是素?cái)?shù)return 1;
}int main()
{for (int num = 0; num < 100; num++){if (IsPrime(num)){printf("%d is a prime number.\n", num);}else{printf("%d is not a prime number.\n", num);}}return 0;
}
還有一種更絕的辦法,可以少遍歷更多的次數(shù) number=a*b
中,a
或 b
中至少有一個(gè)數(shù)小于等于 sqrt(number)
,則(2, sqrt(i)]
之間不能把 number
整除的數(shù)為質(zhì)數(shù)。例如 100
的因數(shù)有:1和100, 2和50, 4和25, 5和20, 10和10
??闯鰜?lái)沒(méi)有?成對(duì)的因數(shù)中,其中一個(gè)必然小于等于 100
的開(kāi)平方,另一個(gè)大于等于 100
的開(kāi)平方)。
//試除法(除開(kāi)平方個(gè)數(shù))
#include <stdio.h>
#include <stdbool.h>
#include <math.h>//判斷一個(gè)數(shù)是否為素?cái)?shù)
int IsPrime(int number)
{//小于等于 1 的數(shù)一定不是素?cái)?shù)if (number <= 1){return 0;}//能被整除則不是素?cái)?shù)for (int i = 2; i <= sqrt(number); i++){if (number % i == 0){return 0;}}//代碼走到這里就說(shuō)明 number 一定是素?cái)?shù)return 1;
}int main()
{for (int num = 0; num < 100; num++){if (IsPrime(num)){printf("%d is a prime number.\n", num);}else{printf("%d is not a prime number.\n", num);}}return 0;
}
再做一個(gè)優(yōu)化,由于只有 2
一個(gè)偶質(zhì)數(shù),因此如果一個(gè)數(shù)是偶數(shù),就直接從源頭上干掉一半的數(shù)據(jù)。
//試除法(利用偶素?cái)?shù)特性)
#include <stdio.h>
#include <stdbool.h>
#include <math.h>//判斷一個(gè)數(shù)是否為素?cái)?shù)
int IsPrime(int number)
{//小于等于 1 的數(shù)一定不是素?cái)?shù)if (number <= 1){return 0;}//偶數(shù)中是 2 的直接判斷為素?cái)?shù), 否則直接判斷為非素?cái)?shù)if (number % 2 == 0){if (number == 2){return 1;}else{return 0;}}//能被整除則不是素?cái)?shù)for (int i = 2; i <= sqrt(number); i++){if (number % i == 0){return 0;}}//代碼走到這里就說(shuō)明 number 一定是素?cái)?shù)return 1;
}int main()
{for (int num = 0; num < 100; num++){if (IsPrime(num)){printf("%d is a prime number.\n", num);}else{printf("%d is not a prime number.\n", num);}}return 0;
}
補(bǔ)充:如果可以的話,您可以查詢一些其他的質(zhì)數(shù)篩選法。
5.3.計(jì)算 1!+2!+…+n! 的值
需求:編寫代碼,根據(jù)用戶輸入的
n
值,計(jì)算1!+2!+...+n!
的值
//雙循環(huán)
#include <stdio.h>
int main()
{int ret = 1;int sum = 0;//輸入最大的 nint maxN = 0;scanf("%d", &maxN);for (int i = 1; i <= maxN; i++) //計(jì)算從 1! 到 n! 中每一項(xiàng) i!(其中 i∈[1, n]){ret = 1; //每次加入循環(huán)先把 ret 都置為 0for (int j = 1; j <= i; j++) //計(jì)算 i! 的值{ret *= j;}//i! 的最終值最后都存儲(chǔ)在 ret 中sum += ret; //sum}printf("%d\n", sum);return 0;
}
//單循環(huán)
#include <stdio.h>
int main()
{int n = 0;int ret = 1;int sum = 0;int manN = 0;scanf("%d", &manN);for (int j = 1; j <= manN; j++){ret *= j;sum += ret; //這里循環(huán)第一次就可以計(jì)算到 1! 循環(huán)第二次可以計(jì)算 1!+2! ... 一直循環(huán)第 manN 次可以計(jì)算 1!+2!+...+n!}printf("%d\n", sum);return 0;
}
單循環(huán)的方式比雙循環(huán)更加節(jié)約計(jì)算成本。
5.4.求解兩個(gè)數(shù)的最小公倍數(shù)和最大公因數(shù)
需求:根據(jù)用戶給定的兩個(gè)數(shù),得到最小公倍數(shù)(
Least Common Multiple
)和最大公因數(shù)(Greatest Common Divisor
)。
5.4.1.公倍數(shù)和公因數(shù)概念
什么是公倍數(shù)?直接舉一個(gè)例子:
- 數(shù)
12
倍數(shù)為12, 24, 36, 48, ...
- 數(shù)
18
倍數(shù)為18, 36, 54, 72, ...
所以 12
和 18
的公倍數(shù)是 36, ...
。
什么是公因數(shù)?直接舉一個(gè)例子:
- 數(shù)
12
因數(shù)為1, 2, 3, 4, 6, 12
- 數(shù)
18
因數(shù)為1, 2, 3, 6, 9, 18
所以 12
和 18
的公因數(shù)是 1, 2, 3, 6
。
5.4.2.樸素求得最小公倍數(shù)
最樸素的想法就是按照定義出發(fā),先得到 12
和 18
的一眾倍數(shù),然后檢查是否有相同的數(shù)。而當(dāng) 12
出現(xiàn) 12*18=216
,18
出現(xiàn) 18*12=216
這個(gè)倍數(shù)時(shí),該數(shù)一定是兩數(shù)的一個(gè)可以被確定的公倍數(shù)。
而如果 12*i, (i∈[1, 18))
和 18*j(j∈[1, 12))
的一眾倍數(shù)中,沒(méi)有相等的公倍數(shù),那么 216
就一定是最小的公倍數(shù)。
因此可以確定公倍數(shù)的范圍在 [1, 216]
之間。
抽象出來(lái)的話,對(duì)于 number1>0
和 number2>0
來(lái)說(shuō),公倍數(shù)的范圍為 [1, number1*number2]
。
因此我們可以先讓 number1
和 number2
分別列出各自的倍數(shù),直到列舉到 number1*number2
,如果兩列倍數(shù)中沒(méi)有找到公倍數(shù) targer
,則最小公倍數(shù)是 number1*number2
,否則最小公倍數(shù)就是 target
。
或者另外一個(gè)角度來(lái)說(shuō),最小公倍數(shù)只存在于區(qū)間 [1, number1*number2]
中,如果 number1
和 number2
能夠被該區(qū)間中某一個(gè)數(shù)整除,該數(shù)就是公倍數(shù),而如果從最小的數(shù)開(kāi)始嘗試能否整除,找到的第一個(gè)能被整除的數(shù)就是最小公倍數(shù)。
//求解兩個(gè)數(shù)的最小公倍數(shù)(樸素解法)
#include <stdio.h>//找到兩個(gè)數(shù)的最小公倍數(shù)
int FindLCM(int num1, int num2) //Least Common Multiple 即 LCM
{int maxMultiple = num1 * num2;//從 [1, maxMultiple] 中逐個(gè)檢查直到找到最小公倍數(shù)for (int i = 1; i <= maxMultiple; ++i){//找到同時(shí)是兩個(gè)數(shù)倍數(shù)的數(shù)if (i % num1 == 0 && i % num2 == 0){return i; //返回最小公倍數(shù)}}return maxMultiple; //如果沒(méi)有找到, 則返回兩個(gè)數(shù)的乘積
}int main()
{int num1 = 0, num2 = 0;//從用戶輸入獲取兩個(gè)數(shù)printf("請(qǐng)輸入兩個(gè)正整數(shù): ");scanf("%d %d", &num1, &num2);//調(diào)用函數(shù)找到最小公倍數(shù)并打印結(jié)果printf("最小公倍數(shù)是: %d\n", FindLCM(num1, num2));return 0;
}
5.4.3.樸素求得最大公約數(shù)
最樸素的想法就是從定義出發(fā),12
和 18
中如果有公因數(shù),則分別列出各自的因數(shù)列表,此時(shí)一定會(huì)發(fā)現(xiàn),兩列的因數(shù)中最大那個(gè)數(shù)就是自己這個(gè)數(shù)本身,例如 1, 2, 3, 4, 6, 12
都是 12
的因數(shù),最大就是 12
自己本身。
而求解公因數(shù)就是求 {1, 2, 3, 4, 6, 12}
和 {1, 2, 3, 6, 9, 18}
兩個(gè)集合的并集,因此兩數(shù)的最大公因數(shù)一定要處于 [1, 12]
中。
只需要倒過(guò)來(lái)遍歷 [1, 12]
中每一個(gè),將每一個(gè)數(shù)都拿去給 18
和 12
除即可。
直到兩個(gè)數(shù)都可以被整除,就找到最大公因數(shù)。
抽象出來(lái)的話,對(duì)于 number1
和 number2
,找到 minNumber=min(number1, number2)
,確定最大公因數(shù)就在 [1, minNumber]
中,遍歷其中每一個(gè) target
,直到 number1%target == 0 && numebr2%target == 0
,則 target
就是最大公因數(shù)。
//求解兩個(gè)數(shù)的最大公因數(shù)(樸素解法)
#include <stdio.h>int Min(int num1, int num2)
{if (num1 >= num2){return num2;}else //num1 < num2{return num1;}
}int FindGCD(int num1, int num2)
{int minNum = Min(num1, num2);for (int target = minNum; target >= 1; target--){if (num1 % target == 0 && num2 % target == 0)return target;}return -1; //盡管這個(gè) return -1 不太可能會(huì)被執(zhí)行, 但是我為了編譯器不進(jìn)行警告, 還是加了這個(gè)返回 -1 就表示出錯(cuò)的返回值
}int main()
{int num1 = 0, num2 = 0;//從用戶輸入獲取兩個(gè)數(shù)printf("請(qǐng)輸入兩個(gè)正整數(shù): ");scanf("%d %d", &num1, &num2);//調(diào)用函數(shù)找到最小公倍數(shù)并打印結(jié)果printf("最大公約數(shù)是: %d\n", FindGCD(num1, num2));return 0;
}
5.4.4.LCM 和 GCD 的關(guān)系
用最大公約數(shù)(GCD
)和最小公倍數(shù)(LCM
)的關(guān)系可以來(lái)互相求解,根據(jù)以下公式:
L C M ( a , b ) = a × b G C D ( a , b ) LCM(a, b) = \frac{{a \times b}}{{GCD(a, b)}} LCM(a,b)=GCD(a,b)a×b?
只要知道兩個(gè)數(shù)的最小公倍數(shù),就可以知道最大公約數(shù)(因此缺陷也就是需要先知道兩個(gè)數(shù)的最小公倍數(shù))。反過(guò)來(lái),只要知道兩個(gè)數(shù)的最大公約數(shù),就可以知道最小公倍數(shù)。
//求解兩個(gè)數(shù)的最大公因數(shù)(關(guān)系解法)
#include <stdio.h>//找到兩個(gè)數(shù)的最小公倍數(shù)
int FindLCM(int num1, int num2) //Least Common Multiple 即 LCM
{int maxMultiple = num1 * num2;//從 [1, maxMultiple] 中逐個(gè)檢查直到找到最小公倍數(shù)for (int i = 1; i <= maxMultiple; ++i){//找到同時(shí)是兩個(gè)數(shù)倍數(shù)的數(shù)if (i % num1 == 0 && i % num2 == 0){return i; //返回最小公倍數(shù)}}return maxMultiple; //如果沒(méi)有找到, 則返回兩個(gè)數(shù)的乘積
}//找到兩個(gè)數(shù)的最大公約數(shù)
int FindGCD(int num1, int num2)
{return (num1 * num2) / FindLCM(num1, num2);
}int main()
{int num1 = 0, num2 = 0;//從用戶輸入獲取兩個(gè)數(shù)printf("請(qǐng)輸入兩個(gè)正整數(shù): ");scanf("%d %d", &num1, &num2);//調(diào)用函數(shù)找到最小公倍數(shù)并打印結(jié)果printf("最大公約數(shù)是: %d\n", FindGCD(num1, num2));return 0;
}
補(bǔ)充:關(guān)于對(duì)應(yīng)的數(shù)論證明,待補(bǔ)充…
5.4.5.使用輾轉(zhuǎn)相除法解決
輾轉(zhuǎn)相除法是求解最大公因數(shù)的一種高效方法,來(lái)源于 《幾何原本》 中古希臘人的智慧,這里簡(jiǎn)單提及一下使用過(guò)程。
假設(shè)有數(shù) 12
和 30
,求兩個(gè)數(shù)的最大公因數(shù),則直觀來(lái)看如下圖:
轉(zhuǎn)化為代碼如下,注意下面代碼很精妙。
//求解兩個(gè)數(shù)的最小公倍數(shù)和最大公因數(shù)(輾轉(zhuǎn)相除法)
#include <stdio.h>//計(jì)算最大公因數(shù)
int FindGCD(int num1, int num2) //算法可以保證 num1 在計(jì)算過(guò)程中為較大值
{while (num2 != 0) //使用輾轉(zhuǎn)相除法來(lái)計(jì)算{int temp = num2; //緩存num2 = num1 % num2; //計(jì)算邊num1 = temp; //計(jì)算邊}return num1;
}//計(jì)算最小公倍數(shù)
int FindLCM(int num1, int num2)
{return (num1 * num2) / FindGCD(num1, num2); //利用 LCM 和 GCD 的關(guān)系來(lái)通過(guò)最大公因數(shù)計(jì)算最小公倍數(shù)
}int main()
{int num1 = 0, num2 = 0;//從用戶輸入獲取兩個(gè)數(shù)printf("請(qǐng)輸入兩個(gè)正整數(shù): ");scanf("%d %d", &num1, &num2);//求出最小公倍數(shù)和最大公約數(shù)printf("最小公倍數(shù)是: %d\n", FindLCM(num1, num2));printf("最大公因數(shù)是: %d\n", FindGCD(num1, num2));return 0;
}
5.5.使用循環(huán)來(lái)清空緩沖區(qū)
需求:清空緩沖區(qū),避免意外程序出現(xiàn)意外的結(jié)果。
這里還需要給您介紹一些連續(xù)使用循環(huán)語(yǔ)句來(lái)不斷獲取多次輸入的代碼,還會(huì)給您介紹新的輸入輸出函數(shù)。首先介紹一對(duì)輸入輸出函數(shù) getchar()
和 putchar()
,他們的作用是獲取一個(gè)字符、輸出一個(gè)字符。
//使用 getchar() 和 putchar()
#include <stdio.h>
int main()
{int ch = 0; //注意 getchar() 的返回值是 int,getchar()讀取成功返回 ASCII 碼值,故用整型接受沒(méi)有問(wèn)題(讀取失敗返回的是 EOF,EOF 的值是 -1,所以用 int 接受 getchar 的返回值會(huì)更好)ch = getchar();putchar(ch);return 0;
}
然后再給您科普一下緩沖區(qū)的概念,緩沖區(qū)是計(jì)算機(jī)內(nèi)存中的一塊空間區(qū)域,用于臨時(shí)存儲(chǔ)數(shù)據(jù),以便在程序執(zhí)行過(guò)程中進(jìn)行讀取和寫入操作。在 C
語(yǔ)言中,scanf()
和 printf()
這兩個(gè)函數(shù)都使用了緩沖區(qū)(輸入緩沖區(qū)和輸出緩沖區(qū))。
scanf()
函數(shù)用于從標(biāo)準(zhǔn)輸入(簡(jiǎn)單理解為鍵盤)讀取輸入,并根據(jù)指定的格式將輸入數(shù)據(jù)存儲(chǔ)到變量中。在 scanf()
函數(shù)中,用戶輸入的數(shù)據(jù)首先被存儲(chǔ)在輸入緩沖區(qū)中,然后 scanf()
再根據(jù)格式指示符從緩沖區(qū)中讀取數(shù)據(jù)。
scanf()
函數(shù)在第一次遇到空白字符(空格、制表符、換行符)時(shí)會(huì)停止從輸入緩沖區(qū)中讀取輸入,直到遇到非空白字符。而如果再次讀取到空白字符就會(huì)停止讀取,并且把空白字符放回輸入緩沖區(qū)中。- 如果
scanf()
函數(shù)后面還有其他輸入函數(shù)(比如getchar()
),它可能會(huì)讀取到之前輸入的換行符或其他殘留在輸入緩沖區(qū)中的字符,因此我們就需要將緩沖區(qū)中的字符清除,避免誤讀。
考慮沒(méi)有清理緩沖區(qū)的代碼:
//未清理緩沖區(qū)
#include <stdio.h>int main()
{char ch = 0;printf("請(qǐng)輸入一個(gè)字符: ");scanf("%c", &ch); //輸入非空白字符后輸入 \n printf("請(qǐng)確認(rèn)輸入(Y/N): ");char flag = getchar();if ('Y' == flag){printf("確認(rèn)成功\n");}else{printf("確認(rèn)失敗\n");}return 0;
}
上述代碼中如果輸入字符后回車,還沒(méi)輸入 Y/N
進(jìn)行確認(rèn),就提前顯示出“確認(rèn)失敗”,這是因?yàn)?scanf()
讀取完字符后,再次遇到了空白字符,把空白字符留在了緩沖區(qū)中。而使用 getchar()
時(shí),就會(huì)被該函數(shù)讀取走,因此 \n != 'Y'
于是輸出“確認(rèn)失敗”。
可以簡(jiǎn)單驗(yàn)證一下,在使用 scanf()
輸入字符后,使用不同的空白字符結(jié)束輸入,看看 getchar()
是不是發(fā)生了誤讀。
//未清理緩沖區(qū), 并且驗(yàn)證 getchar() 是否發(fā)生了誤讀
#include <stdio.h>int main()
{char ch = 0;printf("請(qǐng)輸入一個(gè)字符: ");scanf("%c", &ch); //輸入非空白字符后輸入 '\n' 或者 ' ' 或者 '\t' 結(jié)束輸入試試 printf("請(qǐng)確認(rèn)輸入(Y/N): ");char flag = getchar();if ('\n' == flag){printf("意外讀取到'\\n'\n");}else if ('\t' == flag){printf("意外讀取到'\\t'\n");}else if (' ' == flag){printf("意外讀取到' '\n");}if ('Y' == flag){printf("確認(rèn)成功\n");}else{printf("確認(rèn)失敗\n");}return 0;
}
如果我們希望清理輸入緩沖區(qū)的代碼,我們可以利用控制臺(tái)輸入最后一個(gè)輸入字符始終是 \n
這一點(diǎn),來(lái)使用循環(huán)清空其他空白字符。
//封裝清理緩沖區(qū)的 Empty()
#include <stdio.h>void Empty(void)
{char ch = 0;do{ch = getchar();} while (ch != '\n'); //在控制臺(tái)中 scanf() 輸入結(jié)束后最后一次輸入一定是換行符, 因此不用考慮其他空白字符, 除了 '\n' 其它滯留在緩沖區(qū)中的字符都會(huì)陷入循環(huán)而被讀走, 避免以下后續(xù)輸入//但是如果是文件讀取, 清理情況就有些不同, 這就會(huì)涉及到文件 IO 的知識(shí)了, 以后我再來(lái)帶您重新回顧這個(gè)知識(shí)(除了使用鍵盤輸入, 實(shí)際上還可以使用文件來(lái)輸入)...
}
//有清理緩沖區(qū)
#include <stdio.h>void Empty(void)
{int ch = 0;while ( ((ch = getchar()) != EOF) ){;}
}int main()
{char ch = 0;printf("請(qǐng)輸入一個(gè)字符\n");scanf("%c", &ch);printf("請(qǐng)確認(rèn)輸入(Y/N)\n");Empty(); //調(diào)用函數(shù)來(lái)清理緩沖區(qū)char flag = getchar();if ('Y' == flag){printf("確認(rèn)成功\n");}else{printf("確認(rèn)失敗\n");}return 0;
}
我可以帶您觀察以下調(diào)試過(guò)程。
補(bǔ)充:這里我給您介紹的重點(diǎn)是清空輸入緩沖區(qū),而實(shí)際上有些時(shí)候需要清空輸出緩沖區(qū),這個(gè)以后結(jié)合
fflush()
進(jìn)行展開(kāi)。
警告:小心寫出死循環(huán)的代碼,仔細(xì)分析好循環(huán)條件后再編寫循環(huán)代碼。
警告:
for
內(nèi)部三個(gè)表達(dá)式是可以進(jìn)行一定的省略的,但是最好不要隨便亂省略,比如下面的代碼://不省略表達(dá)式 int main() {int i = 0;for(i = 0; i < 3 ; i++){for(j = 0; j < 3 ; j++){printf("hello\n");}} //打印九次 helloreturn 0; }
如果修改后:
//省略表達(dá)式 int main() {int i = 0;int j = 0;for(; i < 3 ; i++){for(; j < 3 ; j++){printf("hello\n");}} //只會(huì)打印三次 hello,而不是 9 次,原因在于 j 沒(méi)有被重新初始化為 0return 0; }
5.6.簡(jiǎn)易二分法查找算法
題目:給定任意一個(gè)有序的整型數(shù)組(例如
int arr[] = { 1, 4, 9, 10}
),在其中查找是否存在數(shù)target
。
最簡(jiǎn)單粗暴的方法就是遍歷查找,遇到一個(gè)數(shù)組元素就和 target
進(jìn)行比較,一旦相等就說(shuō)明存在。
//遍歷查找
#include <stdio.h>
int main()
{//輸入需要查詢的數(shù)字int target = 0;scanf("%d", &target);//遍歷查找 target 是否存在與于 arr 數(shù)組中int arr[] = { -2, 1, 3, 4, 6, 8, 9 };for (int i = 0; i < sizeof(arr)/sizeof(int); i++){//如果數(shù)組中某個(gè)數(shù)和 target 相等就說(shuō)明 arr 中存在和 target 相等的數(shù)字if(arr[i] == target)printf("target == %d 存在", arr[i]);}return 0;
}
但這樣做的效率比較低,我們來(lái)了解一種叫做二分查找的思想,這種思想實(shí)際上就是高中數(shù)學(xué)中的二分求解法,考慮一種小游戲,游戲的目的是猜對(duì)數(shù)字,假設(shè) B
提前在一個(gè)數(shù)組中準(zhǔn)備好了一個(gè)數(shù)字,由 A
來(lái)進(jìn)行猜測(cè)。A
向 B
提供數(shù)字,而 B
只能說(shuō)“數(shù)大了”、“數(shù)小了”、“是這個(gè)數(shù)”,則 A
會(huì)根據(jù) B
的回答調(diào)整自己的答案,直到猜對(duì)。
假設(shè)被猜的數(shù)字的范圍是 1~10
,B
準(zhǔn)備好數(shù)字 7
,A
只知道 B
準(zhǔn)備的數(shù)最小是 1
,最大是 10
,并且還知道所有的數(shù)從左到右是遞增的,至于中間是什么數(shù)一概不知:
A
此時(shí)知道答案一定在[1, 10]
范圍中,直接問(wèn)B
,5
是不是答案,B
回答“數(shù)小了”A
此時(shí)知道答案一定在[6, 10]
范圍中,直接問(wèn)B
,8
是不是答案,B
回答“數(shù)大了”A
此時(shí)知道答案一定在[6, 7]
范圍中,直接問(wèn)B
,7
是不是答案,B
回答“是這個(gè)數(shù)”
到這里 A
一直取范圍的中間值,只用了 3
次就找到列表中,如果遍歷數(shù)組的話,可能需要更多的查詢次數(shù)。
//二分查找
int main()
{int arr [] = { -2, 1, 3, 4, 6, 8, 9 }; //有序數(shù)組int i = 0;int k = 0;scanf("%d", &k);int sz = sizeof(arr) / sizeof(arr [0]);//計(jì)算數(shù)組的大小int left = 0;//左下標(biāo)int right = sz - 1;//右下標(biāo)int flag = 0;int mid = 0;while (left <= right){//mid = (left + right) / 2;//中間下標(biāo)(但是有點(diǎn)小問(wèn)題, 加起來(lái)容易溢出)mid = left + (right - left) / 2;//中間下標(biāo)(這樣求平均值比較好)if (arr [mid] < k){left = mid + 1;}else if (arr [mid] > k){right = mid - 1;}else{printf("找到了,下標(biāo)是 %d\n", mid);flag = 1;break;}}if (0 == flag){printf("沒(méi)有找到\n");}return 0;
}