for 用來遍歷,while 也用來遍歷;看是功能相同的,但如果功能相同,為啥不剔除一個呢;(而且幾乎所有編程語言都有這兩個循環語句)
因為他們有不一樣的地方呀,(感覺好像廢話,我們還是具體看代碼吧)
相同的地方;
public static void TestLoop1() { int loopTimes = 4; int i; for(i = 0; i < loopTimes; i++) { //0 //1 //2 //3 //最后一次進來是 index=3; //出去的時候,index 還是3 } //(累加=>停止循環的時候變成了4 int index = 0; while (index < loopTimes) { //0 //1 //2 //3 //最后一次進來 index=3; index++; //累加 然后出去,index=4; 然后不滿足條件;退出循環; } //眨眼一看,上面的寫法沒什么區別; BUT i 的疊加是在 {} 外部進行的, index 的疊加是在 {} 內部進行的; }
上面的寫法,在開發中會遇到不同的問題,比如:
//眨眼一看,上面的寫法沒什么區別; BUT i 的疊加是在 {} 外部進行的, index 的疊加是在 {} 內部進行的; //這樣會出現什么問題呢; Console.WriteLine("我們來遍歷一次數組,就知道了"); Console.WriteLine("for......."); var arr = new int[] {1,2,3,4}; int len = arr.Length; for(int j = 0; j < len; j++) { Console.WriteLine(arr[j]); //這個數絕對安全的; } Console.WriteLine("while....."); int k = 0; while (k < len) { //k++; 如果k++放在前面,無法比那里k=0 這個數組不說,還會發生數組越界的異常; Console.WriteLine(arr[k]); k++; } Console.WriteLine("finall k index;"+k); //最后這個index 會變成4 所以這樣比那里數組是不安全的; //你以為區別就這一點嗎? 太年輕了,接着看;
不同的地方;
static void ForVSWhile() { var arr=new []{ 1,2,4,5,6}; int len = arr.Length; int target = 4; for (int i = 0; i < len; i++) { if (arr[i] == target) { break; } } //這個就相當於我們的while; int index = 0; while (arr[index] == target) //找到我們想要的元素后,就立馬停止; { index++; } }
不過,你也可以把他們寫來一樣,不過,感覺好別扭;
var arr=new []{ 1,2,4,5,6}; int len = arr.Length; int target = 4; for (int i = 0; i < len && arr[i] == target; i++) { }
我們再來看一個容易出錯的地方;
/// <summary> /// 找出數組中小於target的值; /// 這個問題,我在快速排序的時候遇到的一個死循環; /// </summary> public static void TestLoop2() { var arr = new int[] { 1,2,1}; int len = arr.Length; int target = 2; List<int> list = new List<int>(3); List<int> list1 = new List<int>(3); // 常規寫法; for(int j = len-1; j>=0; j--) { if (arr[j] < target) { list.Add(arr[j]); } } Console.WriteLine("while寫法的結果--------------:"); //index=2; int k = len-1; //因為 arr[1]=2 不滿足條件,然后就退出了循環;並沒有遍歷我們的整個數組; //並沒有把我們的數組遍歷完; while (arr[k] < target) { list1.Add(arr[k]); k--; } }
其實,對這個問題的總結,來自我們的快速排序時,遇到的一個問題;
static void TestLoop() { //在快速排序的過程中遇到一個問題; var arr = new[] { 5, 4,1 }; int len = arr.Length; int high = len - 1; int low = 0; int middle = arr[1]; while (low < high) { while (arr[high] > middle && low<high) { high--; } while (arr[low] < middle && low < high) { low++; } } //高地址 middle=4 high=2; arr[2]=1 找到了小於 middle 的值; 這時候,我們的high=2; //底地址 middle=4 low=0; arr[0]=5;找到了大於 middle 的值; 這時候 我們的low=0; // 這個時候,進行交換 arr[2] 和 arr [0] 數組變了 1 4 5 // 但是 關鍵來了!!!!!!!!!!!!!!! // 進行尋址之后 我們的 high 和 low 沒有 進行 遞減 和 遞增的操作, high還是2 low 還是 0 //這樣就形成了我們的死循環;窩草; //這樣就被迫,我去思考一些基礎性的問題;之前也遇到過 如 ++i 和 i++; 如 for 循環 和while 循環的區別; }
然后,這里我們再來復習一個非常簡單的額問題;i++; 和 ++i;
i++; 先進行賦值,然后再進行累加;
我們來看一個以此產生的死循環;
/// <summary> /// 如果這樣寫,tad的就變成了死循環哈 /// </summary> /// <param name="n"></param> static void TestRecursion(int n) { if (n > 0) { Console.WriteLine(n); TestRecursion(n--); //先將n進行函數賦值進行運算,然后再n--;如果這樣搞就是死循環啊; } }
fix(這樣就好了)
/// <summary> /// 如果這樣寫,tad的就變成了死循環哈 /// </summary> /// <param name="n"></param> static void TestRecursion2(int n) { if (n > 0) { Console.WriteLine(n); TestRecursion2(--n); //先將n進行函數賦值進行運算,然后再n--;如果這樣搞就是死循環啊; } }
然后,我們再來看一個取值的問題;
static void TestWhile() { var arr = new[] {1,2,3,4}; int len = arr.Length; var newArr = new int[len]; int index = 0; var newArr1 = new int[len]; int index1 = 0; for (int i = 0; i < len; i++) { if (arr[i] % 2 == 0) { newArr[++index] = arr[i]; //從結果中可以看出,++i 是先累加,在賦值 newArr1[index1++] = arr[i]; //從結果中可以看出,i++ 是先賦值,在累加 } } //然后我們來看這兩個的區別; Console.WriteLine("++index"); foreach (int i in newArr) { Console.WriteLine(i); } Console.WriteLine("index++"); foreach (int i in newArr1) { Console.WriteLine(i); } }
結果:
然后,我們看看,在while 中應用,主要是是在我們的快速排序中;也就是修復我們前面那個快速排序中的死循環;
static void TestLoop() { //在快速排序的過程中遇到一個問題; var arr = new[] { 5, 4,1 }; int len = arr.Length; int high = len - 1; int low = 0; //這里寫個重負代碼,主要為了,看得清楚一點; high = high + 1; low = low - 1; int middle = arr[1]; while (low < high) { while (arr[--high] > middle && low<high) //先盡心一次自減。再取值; { int xx = 0; } while (arr[++low] < middle && low < high) ////先盡心一次自加。再取值; //這樣,保證,我們每一次遍歷,都在前進;就想我們的for循環;知道low 遇到我們的i { int xx = 0; } }
然后,我們再來看一個具體的實例優化;找到元素應該插入的位置;如:target =2; 在 0 1 中,應該插入的位置就是2;如果用for來實現;則會出現如下的代碼;
/// <summary> /// 找到元素應該在的正確元素; /// 考慮下面三種情況; /// 1.數組長度就是1; /// 2.找完了,都沒找到; /// 找到,應該插入的位置;。。。shit優化; /// </summary> public static int FindRightPostion(int [] arr,int target) { int len = arr.Length; int position =0; //如果沒有找到,就應該在第一個位置;最后一個步,卻加了1,這樣。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。、 bool isFind = false; //正向尋找; for(int i = 0; i < len; i++) { //最后一次沒有進來;對,i就是我們的待插入的位置;不過,最后一次的條件沒有進來; if (arr[i]<target) //最后一個找到,肯定是不滿足條件的的; { position = i; //不滿足條件的值,沒有進入這個賦值;所以,最后合適的位置就是;position+1 isFind = true; } else { break; } } if (isFind == false) return position; else return position+1; } /// <summary> /// 這種方式也不錯滴啊; /// </summary> /// <param name="arr"></param> /// <param name="target"></param> /// <returns></returns> public static int FindRightPostion2(int[] arr, int target) { int len = arr.Length; int position = 0; for (int i = 1; i < len; i++) { if (arr[i] < target) { position = i; } else { break; } } return position == 0 ? 0 : position + 1; }
然后,如果用我們的while 來實現,這樣的方式,代碼,好了很多;(這樣,就完美多了)
/// <summary> /// 這樣寫,比較合適,出來的時候,pointer 恰好;執向的是下一個位置; /// </summary> /// <param name="arr"></param> /// <param name="target"></param> /// <returns></returns> public static int FindeRightPostion(int [] arr,int target) { int len = arr.Length; int pointer = 0; while (pointer < len && arr[pointer] < target) { pointer++; } return pointer; }
總結:for 循環,更多用於一個完整的遍歷; while 更多是帶有終止條件的遍歷;