第1节、时间复杂度 先从选择排序看起
选择排序:从第1位到第N位。每一位分别与之后的所有数比较。比较小的树放在第i位。
计算时间复杂度:
每一个数分别看了后面的数:N次、N-1次、N-2次···1次。
每个数分别比较了后面的数:N次、N-1次、N-2次···1次。
每个数都交换了一次:1次。
所以可以得出时间复杂度:
每个数看了: N+N-1+N-2+···+1。
每一个数比较了: N+N-1+N-2+···+1。
总共交换了N次。
全部相加可以得到: (N+1)*N + N 。
它是一个: 最高次项为二次的方程 aN^2+bN+c。
得到时间复复杂度为 O(N^2) 读作:big o N^2。
第2节、额外空间复杂度(选择排序) 这里我们也可以从选择排序看起
下面我们给出选择排序的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 namespace LeetCode_Learn { public class Part01_SelectionSort { public static int [] Sort (int [] arr ) { if (arr == null ) throw new ArgumentNullException("arr不可为空!" ); if (arr.Length < 2 ) return arr; for (int i = 0 ; i < arr.Length; i++) { int minIndex = i; for (int j = i + 1 ; j < arr.Length; j++) { minIndex = arr[j] < arr[minIndex] ? j : minIndex; } Swap(arr, i, minIndex); } return arr; } private static void Swap (int [] arr, int index1, int index2 ) { int temp = arr[index1]; arr[index1] = arr[index2]; arr[index2] = temp; } } }
在遍历每个数字的时候,我们会声明一个temp作为最小的最小的index。每次去每次循环完之后会释放掉这个temp,所以这里我们只用到了开辟了一个变量。也就是额外空间复杂度为O(1) 。
第3节、冒泡排序 冒泡排序:遍历每个数,其左边边的数做以下操作: 如果右边的数大于左边的数则交换。
下面给出代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 namespace LeetCode_Learn { public class Part02_BubbleSort { public static int [] Sort (int [] arr ) { if (arr == null ) throw new ArgumentNullException("arr不可为空!" ); if (arr.Length < 2 ) return arr; for (int i = arr.Length - 1 ; i >= 0 ; i--) { for (int j = 0 ; j < i; j++) { if (arr[j] > arr[j + 1 ]) { Swap(arr, j, j + 1 ); } } } return arr; } private static void Swap (int [] arr, int index1, int index2 ) { arr[index1] = arr[index1] ^ arr[index2]; arr[index2] = arr[index1] ^ arr[index2]; arr[index1] = arr[index1] ^ arr[index2]; } } }
这里用到异或运算进行。进行交换操作。
第4节、异或运算 下面给出异或运算的一些规律:
0^N = N。
N^N = 0。
符合交换律和结合律。
异或运算与顺序无关,可以调换顺序。
注意事项:如果要进行用异或运算进行交换操作,请注意a和b指向的内存空间不能为同一个。因为N^N=0。
下面给出一道异或运算的练习题:
给出一个整形数组 int[] arr
第1问:这个数组中仅有一种数出现奇数次,其他数都是偶数次。求这个基数是多少?
第2问:这个数组中仅有二种数出现奇数次。其他数都是都是偶数次。求这两个奇数分别是多少?
第1题,题解:
定义eor = 0,用eor 异或每一个数得到的数一定是出现奇数次的数。因为出现偶数次的数异或为0。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 namespace LeetCode_Learn { public class Part03_GetOneOddNumber { public static int Get (int [] arr ) { if (arr == null ) throw new ArgumentNullException("arr不可为空!" ); if (arr.Length < 2 ) return arr[0 ]; int eor = 0 ; for (int i = 0 ; i < arr.Length; i++) { eor ^= arr[i]; } return eor; } } }
第2题,题解:
同上题用eor异或每一个数。得到的eor为 a异或b。 a和b是那两个出现奇数次的数。然后我们在定义一个变量_eor = 0。因为a和b不为同一个数,所以 eor不等于0。所以我们一定可以在eor中找到一位为1。这里我们用一个算法来找到最右侧的1:
算法为 rightOne = eor & (~eor+1) 。
a和b在rightOne这一位,一定有一位是1,有一位是0。这里我们将数组分为两种类型。种是对应位为1,一种是对应位为0。a和b肯定只占一种类型。然后我们遍历对应位为1的数。用_eor异或这些数。我们一定可以得到a或者b。然后我们再用得到的这个数,来异或eor。这样我们就得到了这两个数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 namespace LeetCode_Learn { public class Part04_GetTwoOddNumber { public static int [] Get (int [] arr ) { if (arr == null ) throw new ArgumentNullException("arr不可为空!" ); if (arr.Length < 2 ) return arr; int eor = 0 ; for (int i = 0 ; i < arr.Length; i++) { eor ^= arr[i]; } int _eor = 0 ; int rightOne = eor & (~eor + 1 ); for (int i = 0 ; i < arr.Length; i++) { if ((arr[i] & rightOne) == 0 ) { _eor ^= arr[i]; } } int a = eor ^ _eor; int b = eor ^ a; return new int [] { a, b }; } } }
第5节、插入排序 遍历每个数使其左边的数变成成有序态。遍历左边的数如果左边的时候大于右边,那就交换。
插入排序的时间复杂度。最差复杂度为O(N^2)。最好是复杂度为O(N)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public class Part05_InsertSort { public static int [] Sort (int [] arr ) { if (arr == null ) throw new ArgumentNullException("arr不可为空!" ); if (arr.Length < 2 ) return arr; for (int i = 1 ; i < arr.Length; i++) { for (int j = i - 1 ; j >= 0 ; j--) { if (arr[j] > arr[j + 1 ]) { Swap(arr, j, j + 1 ); } } } return arr; } private static void Swap (int [] arr, int index1, int index2 ) { arr[index1] = arr[index1] ^ arr[index2]; arr[index2] = arr[index1] ^ arr[index2]; arr[index1] = arr[index1] ^ arr[index2]; } }
第6节、二分法
有序数组中的二分法。在有序数组中的一个数是否存在?时间复杂度为big,log2为底的N次方。
有序数组中找大于等于某个数最左侧的位置。与上题相同,不过要二分到最左侧的位置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public class Part06_BSOne { public static int Get (int [] arr, int num ) { if (arr == null ) throw new ArgumentNullException("arr不可为空!" ); if (arr.Length < 2 ) return arr[0 ]; return binaryFindNum(arr,0 , arr.Length - 1 ,num); } public static int binaryFindNum (int [] arr, int left, int right, int num ) { if (left > right) return -1 ; int mid = (left + right) / 2 ; int midVal = arr[mid]; if (num > midVal) return binaryFindNum(arr, mid + 1 , right, num); else if (num < midVal) return binaryFindNum(arr, left, mid - 1 , num); else return mid; } }
局部最小问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public class Part06_BSOneLeftNear { public static int Get (int [] arr, int num ) { if (arr == null ) throw new ArgumentNullException("arr不可为空!" ); if (arr.Length < 2 ) return arr[0 ]; return binary(arr, 0 , arr.Length - 1 , num); } public static int binary (int [] arr, int left, int right, int num ) { if (left > right) return -1 ; int mid = (left + right) / 2 ; int midVal = arr[mid]; if (mid - 1 >= 0 && midVal >= num && arr[mid - 1 ] >= num) return binary(arr, left, mid - 1 , num); else if (mid - 1 >= 0 && midVal >= num && arr[mid - 1 ] < num) return mid; else return binary(arr, mid + 1 , right, num); } }
第7节、对数器 实现一个简单的C#对数器,实现排序算法对比
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 public class Compare { public static void DO (int number, Action<int []> CustomSort, int length, int min, int max ) { if (number == 0 ) return ; for (int i = 0 ; i < number; i++) { DO(CustomSort, length, min, max); } } static bool DO (Action<int []> CustomSort, int length, int min, int max ) { int [] arr = new int [length]; RandomArr(arr, length, min, max); int [] arr1 = new int [length]; Array.Copy(arr, arr1, length); int [] arr2 = new int [length]; Array.Copy(arr, arr2, length); Array.Sort(arr1); CustomSort(arr2); if (!Do(arr1, arr2, length)) { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine($"错误样本{string .Join(", " , arr)} " ); Console.WriteLine($"错误样本排序: {string .Join(", " , arr2)} " ); Console.WriteLine("\n" ); return false ; } else { Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine($"正确样本{string .Join(", " , arr)} " ); Console.WriteLine($"正确样本排序{string .Join(", " , arr2)} " ); Console.WriteLine("\n" ); }; Console.ForegroundColor = ConsoleColor.White; return true ; } static bool Do (int [] arr1, int [] arr2, int length ) { bool right = true ; for (int i = 0 ; i < length; i++) { if (arr1[i] == arr2[i]) { } else { right = false ; }; } return right; } static void RandomArr (int [] arr, int length, int min, int max ) { Random rand = new Random(); for (int i = 0 ; i < length; i++) { arr[i] = rand.Next(min, max); } } }