接觸C#語言只有短短几天時間,想要寫出什麼高大上的深入性研究文章,估計也是滿篇的猜想和一些沒有邏輯的推斷。截至目前而言,從語言入門知識(大多數程式員的入門儀式——輸出“Hello,world!”)、資料和資料類型、資料運算、程式功能實現流程和迴圈、數組和集合這些分塊的小知識到將所學知識串聯應用,並沒有承受太大的困難, 像是在用描述的語言來求解應用題,再通過代碼將這些描述的語句表達出來。
記得開班時做了三道趣味題。第一題是主人運送草、羊和狼過河,狼吃羊,羊吃草,主人看管情況不會發生任何事,一次只能運送一樣過河,這道題給我的感覺是用來引導學員思維方式,順便觀察學員是否有一個清晰的思路來分析和解答問題,找到關鍵點就是羊只能單獨存在或者隨主人一起,這樣就能很好的解決問題。第二題是三個和尚和三個怪物過河,只有一隻空船,船一次可以運兩人,每當出現怪物數量大於和尚數量,遊戲就結束了,這道題也是考驗思考問題的方式,但是還要注意怪物和和尚運輸流程的正確性,必須保證和尚安全的先全部到達對岸,圍繞這個中心來解決問題。第三題印象比較深刻,做完後,旁邊的同學說可以27s,然後我就進行了反覆的嘗試(只能是29s)。要求是利用一盞只能亮30s的燈引導5人過橋,一次只能兩人持燈通過,5人所需時間分別為1s、3s、6s、8s、12s當然,這道題要抓住重點,就是盡量將1s小孩用來反方向運燈,而且必須將12s的老人和8s的胖子一趟完成通過,在多次嘗試中,我發現只要保證1s的小孩返回兩趟,3s小孩返回一趟,耗時最長的兩位一次通過,不管其餘怎樣排序設定,都不會影響到最後的結果。
回顧一下這個有意義的開課儀式,再回到C#的世界中,其實兩者有很多的共同之處,現在就寫一下不同代碼的同樣實現效果這一回事!
先舉一個簡單的例子,在求水仙花數時,需要將一個百位元的每一位的數值進行求解,答案中給出的代碼是這樣的:
int i = 100;while (i < 1000){ int a = i / 100 % 10; int b = i / 10 % 10; int c = i % 10; if (a * a * a + b * b * b + c * c * c == i) { Console.WriteLine(i); } i++;}
而我在解答時使用了兩種不同的方法,第一種是:
int i = 100;while (i < 1000){ int a = i /100; int b = i % 10 / 10; int c = i % 10; if (a * a * a + b * b * b + c * c * c == i) { Console.WriteLine(i); } i++;}
第二種方法是:
int i = 100;while (i < 1000){ int a = i / 100; int b = (i - a * 100) / 10; int c = i -a * 100 - b * 10; if (a * a * a + b * b * b + c * c * c == i) { Console.WriteLine(i); } i++;}
以上均是正確的、可實現的代碼,代碼之所以存在差異,就是在運算時,對問題分析和思考的角度不同。第一種是將所求位元後面的內容拋棄,再將剩餘的數對10取餘,因為切除後剩餘的數的個位總是對應所求位元的值。第二種也是將這個數拆分,將這個數對所求位元的10的倍數求餘,將所求位元之前的數全部拋棄,得到的是的第一位總是所求位元的值,再用除法就能得到想要的值。第三種就是個死辦法,多餘的全減掉再除就可以了。總而言之,不同的思路、不同的解題方法,並不會影響代碼的實現,但是選擇簡短、優雅的代碼,能夠提高整個代碼的美感,這一點還是需要注意。就自己感悟而言,肯定是優先選擇自己能夠理解的代碼,這樣用起來才會得心應手,同時可要擴大自己的見識,多想想不同的思路的實現方式。
但是,說到這裡,也許有人會有質疑,上面就是一道數學題,和編碼的思路有什麼關係。那麼再來看一下我和其餘人不同的實現思路,這個問題是關於建立一個數組並賦值後,讓使用者輸入一個要尋找的數字,判斷該數字在數組中是否存在。
int[] nums = { 4, 8, 12, 333, -9, 1 };bool isFind = false;for (int i = 0; i < nums.Length; i++){ if (nums[i] == n) { isFind = true; break; }}if (isFind){ Console.WriteLine("數組中存在該數");}else{ Console.WriteLine("數組中不存在該數");}
我思考的方法是:
int[] nums = { 4, 8, 12, 333, -9, 1 };Console.Write("請輸入需要尋找的數字:");int input2 = int.Parse(Console.ReadLine());for (int i = 0; i < 5; i++) { if (nums[i] == input2) Console.WriteLine("在數組中尋找到該數值,該數為數組中的第" + (i + 1) + "項!"); if(i==4&&nums[i]!=input2) Console.WriteLine("未在數組中找到對應項!"); }
第一種代碼是通過定義一個bool類型資料isFind,如果找到,就改變isFind的資料,然後通過isFind的資料完成實現。而我在思考時,是想如果沒有找到,那麼迴圈完成後迴圈次數就會達到最大值,但是此時最後一位元與輸入的數相同,兩個輸出對應條件都能滿足,所以,排查到最後並且最後一位的值也不等,才能滿足輸出未找到結果。通過這樣的分析,就寫出了這兩段代碼。這就是不同思路採用不同代碼來實現相同功能的方式。
關於不同代碼實現相同功能,還有一個最經典的例子,是不能不提的,那就是數組和集合的排序,下面介紹三種思路:交換排序、冒泡排序和選擇排序。
交換排序中心思想是從第一個數組項開始,固定nums[i],依次第i+1個後面的資料進行比較,如果有比num[i]小的值,就對其進行交換。
for( int i = 0; i < arrays.Length - 1; i++){ for(int j = i+1; j < arrays.Length; j++) { if(arrays[i]>arrays[j]) { int temp=arrays[i]; arrays[i]=arrays[j]; arrays[j]=temp; } }}
冒泡排序是將最大的數沉到底部,先將最後一個位置固定,再從第一個數開始比較,每遇到一個大的數,這個數就與後一位交換,就像氣泡一樣,這個變動的尋找中的值越滾越大,直到最後一位。這時,再確定倒數第二位,再次進行替換。(第二個for迴圈中,每次迴圈,nums[j]的值總是逐漸層大。)實現代碼如下:
for(int i = nums.Length - 1; i > 0; i--){ for(int j = 0; j < i; j++) { if( nums[j] > nums[j+1] ) { int temp = nums[j]; nums[j] = nums[j+1]; nums[j+1] = temp; } }}
選擇排序從第一個數開始,先假設第一個數為最小的數,將其與後面每一個數進行比較,如果遇到小的,就記錄這個數的下標,迴圈完成後,記錄的下標對應的數一定是資料群組的最小值,此時替換最小值到第一位。後面依次迴圈,完成排序。
for(int i = 0; i < nums.Length - 1; i++){ int index = 1; for(int j = i+1; j < nums.Length; j++) { if(nums[j])<nums[index]) { index=j; } } int temp = nums[i]; nums[i] = nums[index]; nums[index] = temp;}
有上面三種排序方法可以看出,只要能夠實現功能,思路和代碼並不重要。只要能找到解決問題的關鍵點,並圍繞關鍵點弄懂解決問題的方法,根據方法確定流程,再完成代碼的編寫,這樣想要達到功能的實現並不難。不過為了整個代碼的便於查看和修改,在使用這些代碼時,在能夠理解代碼書寫的思路前提下,盡量使用結構優良,語句簡潔的語句。當然,如果一些方法難以理解,最好還是使用自己理解的代碼書寫,便於自己完成查看和修改,如果必要,注釋也是必不可少。
總而言之,多觀察別人的思路,多看多想多開拓,總是沒有壞處。畢竟是編程,難以理解或者使用不熟練,解決的方法還是多練多敲,沒有其他的捷徑。