今天同事估计闲得蛋疼,突然开始回忆以前面试过程中被面到过的一些面试问题,有逻辑的,有算法的,然后来考我思路,标题对应的算法就是他碰到的面试算法题之一。
拿到题目的第一个感觉,就是Linq,被Linq带坏了,这种考算法的题目直接来Linq你都逗谁呢,整理一下思路,恩,题目只要求左侧奇数,右侧偶数,并未要求两侧的整数还要分别排序,那算法思路就这么定下来了,按索引从低向高循环,如果遇到偶数,则在循环内进行反向循环,即从高位向低位循环找奇数来实现两者位置交替,算法实现如下:
static void SetLeftSingleAndRightDoubleOne(int[] arr) { int num = 0; var prevIndex = arr.Length - 1; for (var i = 0; i <= prevIndex; i++) { if (arr[i] % 2 == 0) {//偶数 for (var j = prevIndex; j > i; j--) { num++; if (arr[j] % 2 != 0) { //奇数 var b = arr[i]; prevIndex = j - 1;//当前位置已与偶数更换过位置,所以设置值的索引可以减一 arr[i] = arr[j]; arr[j] = b; break; } } } else { num++; //奇数 continue; } } //Console.WriteLine("ONE RUN NUMS:" + num); }
咋看算法没问题,时间复杂度应当为f(n)=n-1,但实际一运行,却发现偶尔会出现循环次数高于数组长度的现象,这是为什么呢?因为问题出在反向循环上,如果反向循环到i为止,都未能找到奇数,那也就不会有prevIndex的赋值,那外层的循环也就会继续下去,然后就会出现循环次数高于数组长度的情况,修正代码,增加判断停止标志位:
static void SetLeftSingleAndRightDoubleOne(int[] arr) { int num = 0; var prevIndex = arr.Length - 1; for (var i = 0; i <= prevIndex; i++) { if (arr[i] % 2 == 0) {//偶数 bool stop = false;//停止标志位 for (var j = prevIndex; j > i; j--) { num++; if (arr[j] % 2 != 0) { //奇数 var b = arr[i]; prevIndex = j - 1;//当前位置已与偶数更换过位置,所以设置值的索引可以减一 arr[i] = arr[j]; arr[j] = b; break; } stop = j == i + 1;//多了一步赋值 } if (stop)//虽然多了一步判断,但循环次数有减少 {//因为反向遍历已经遍历到了i的前一位,所以可以停止遍历了 break; } } else { num++; //奇数 continue; } } //Console.WriteLine("ONE RUN NUMS:" + num); }
OK,算法已经实现,然后得到同事的实现,大体思路一致,都是左右遍历交替,具体如下:
static void SetLeftSingleAndRightDoubleTwo(int[] arr) { int left = 0; int right = arr.Length - 1; int num = 0; while (left < right) { num++; bool leftDouble = arr[left] % 2 == 0;//判断左侧是否是偶数 bool rightSingle = arr[right] % 2 != 0;//判断右侧是不是奇数 if (!leftDouble) { left++; } if (!rightSingle) { right--; } if (leftDouble && rightSingle) { int t = arr[left]; arr[left] = arr[right]; arr[right] = t; left++; right--; } } //Console.WriteLine("TWO RUN NUMS:" + num); }
与实现一相比,每次循环都会低位高位找两个位置,并判断是否能进行替换,因为每次循环都判断高低两个位置,所以时间复杂度上要低于实现一
最后就是具体的测试代码,为测试结果更为精准,每个实现都执行10次
Stopwatch watch= new Stopwatch(); for (var i = 0; i < 10; i++) { Console.WriteLine("*******************************"); Console.WriteLine("Run the " + (i + 1) + "st time"); var tmp = Enumerable.Range(1, 50/*00000*/).OrderBy(_ => Guid.NewGuid()).Skip(30).ToArray(); //arr = Enumerable.Repeat<int>(1, 50).ToArray(); //arr = new int[50]; var arr1 = tmp.ToArray(); var arr2 = tmp.ToArray(); watch.Reset(); watch.Start(); SetLeftSingleAndRightDoubleOne(arr1); watch.Stop(); Console.WriteLine("ONE MS:" + watch.ElapsedMilliseconds); //Console.WriteLine(string.Join(",", arr1)); watch.Reset(); watch.Start(); SetLeftSingleAndRightDoubleTwo(arr2); watch.Stop(); Console.WriteLine("TWO MS:" + watch.ElapsedMilliseconds); //Console.WriteLine(string.Join(",", arr2)); }
最后分别按正确性和执行时间截了两张图,正确性方面两者一致,执行时间方面实现二要略优于实现一
正确性截图
执行时间截图
版权声明:本文为博主原创文章,未经博主允许不得转载。
时间: 2024-10-25 15:16:43