查找
查找的算法主要有几种:二分查找,插入查找,斐波那契查找。注:所有的查找算法都是在有序数组中
二分查找:顾名思义,先从中间值开始查找,然后不断逼近,类似高中的二分法
二分查找完整代码如下:
public static int BinarySearch(int[] arr, int left, int right, int value) { if (left == right) { return -1; } int mid = (left + right) / 2; int midVal = arr[mid]; if (value > midVal) { return BinarySearch(arr, mid + 1, right, value); } else if (value < midVal) { return BinarySearch(arr, left, mid - 1, value); } else { return mid; } }
二分查找还有个改进算法,可以找到重复的元素,代码如下:
public static List<int> BinarySearch2(int[] arr, int left, int right, int value) { if (left == right) { return new List<int>(); } int mid = (left + right) / 2; int midVal = arr[mid]; if (value > midVal) { return BinarySearch2(arr, mid + 1, right, value); } else if (value < midVal) { return BinarySearch2(arr, left, mid - 1, value); } else { List<int> list = new List<int>(); list.Add(mid); int temp = mid - 1; while (true) { //因为二分查找为有序数组 所以在arr[temp]!=value时之间跳出循环 if (temp < 0 || arr[temp] != value) { break; } list.Add(temp); temp -= 1; } temp = mid + 1; while (true) { if (temp > arr.Length - 1 || arr[temp] != value) { break; } list.Add(temp); temp += 1; } return list; } }
插入查找也可以认为是二分查找的改进,其改进之处在于插入查找插入点不再是中点,而是随着查找值改变(自适应查找)
插入查找适用于均匀分布的数组,原因很简单 其本质是等比例缩放法 value/mid=arr[右]-arr[左]/arr.Length
插入查找完整代码如下:
1 public static int InsertSearch(int[] arr, int left, int right, int value) 2 { 3 if (left > right || value < arr[0] || value > arr[arr.Length - 1]) 4 { 5 return -1; 6 } 7 int mid = left + (right - left) * (value - arr[left]) / (arr[right] - arr[left]); 8 int midVal = arr[mid]; 9 if (value > midVal) 10 { 11 return InsertSearch(arr, mid + 1, right, value); 12 } 13 else if (value < midVal) 14 { 15 return InsertSearch(arr, left, mid - 1, value); 16 } 17 else 18 { 19 return mid; 20 } 21 }
斐波那契查找本质也和二分查找类似,其插入点是接近黄金分割点(虽然不知道有什么意义,但人家这么搞跟着搞就对了^^)
完整代码如下:
1 //构建斐波那契数列 2 public static int[] fib() 3 { 4 int[] f = new int[20]; 5 f[0] = 1; 6 f[1] = 1; 7 for (int i = 2; i < f.Length; i++) 8 { 9 f[i] = f[i - 1] + f[i - 2]; 10 } 11 return f; 12 } 13 14 public static int fibSearch(int[] a, int key) 15 { 16 int low = 0; 17 int high = a.Length - 1; 18 int k = 0;//表示斐波那契分割数值的下标 19 int mid = 0;//存放mid值 20 int[] f = fib(); 21 //获取到斐波那契分割数值的下标 22 while (high > f[k] - 1) 23 { 24 k++; 25 } 26 //因为k值可能大于a长度 构建一个数组temp 将a的值全部放入temp 不足用0补 27 int[] temp = new int[f[k]]; 28 a.CopyTo(temp, 0); 29 //实际中用a最后的元素填充temp的0部分 30 for (int i = high + 1; i < temp.Length; i++) 31 { 32 temp[i] = a[high]; 33 } 34 //用while循环处理 35 while (low <= high) 36 { 37 mid = low + f[k - 1] - 1; 38 //如果要找到的值在左边 39 if (key < temp[mid]) 40 { 41 high = mid - 1; 42 //f[k]=f[k-1]+f[k-2] 左边数组为f[k-1] 该数组斐波那契为f[k-1]=f[k-2]+f[k-3] 43 k--; 44 } 45 else if (key > temp[mid])//要找的值在右边 46 { 47 low = mid + 1; 48 k -= 2; 49 } 50 else //要找的值就是mid 因为是在temp中找的 可能长度大于a.length 所以返回小一点的 51 { 52 if (mid <= high)//没有超出a 53 { 54 return mid; 55 } 56 else//超出了a 57 { 58 return high; 59 } 60 } 61 } 62 //找不到 返回-1 63 return -1; 64 }