算法入门---java语言实现的二分查找小结

来源:互联网 发布:js生成uuid时间 编辑:程序博客网 时间:2024/06/07 19:39
  1. package com.zy.serch;
  2. public class BinarySerch {
  3. /**
  4. * 二分查找。
  5. * 前提:数据源是有序的。
  6. * 核心思想:还是先确定要写的算法的区间,此处我们是按照src[left...right],左闭右闭来查找的。
  7. * 首先我们找到中间的索引mid,此时数据源就成了src[left...mid-1] [mid] [mid+1...right]
  8. * 这样src[left...mid-1] < src[mid],src[mid+1...right] > src[mid].此时比较我们要查找
  9. * 的数和mid索引所在的值:
  10. * 等于: 找到直接返回
  11. * 小于: 此时就应该在src[left...mid-1]据需执行相应的操作,
  12. * 注意不需要再比较mid了,那么就是把right的索引做相应的处理。
  13. * 大于: 此时就应该子啊src[mid+1...right]据需执行相应的操作,此次就需要对left索引做处理。
  14. *
  15. * 最终当left索引大于等于right索引的时候就不需要在进行查找了,此时仍没有找到的时候就返回-1,或其它的来标记未找到。
  16. *
  17. * 二分查找,当有相等的时候,那么按照查找规则,找到就会返回,不一定是前面的或者后面的。
  18. * @param src 要进行查找的数据源
  19. * @param value 要查找的值
  20. * @return 如果数据存在返回找到的索引,否则返回-1;
  21. *
  22. */
  23. public int binarySerch(int[] src,int value){
  24. //老思想,确定自己的区间,定义需要维护的变量。
  25. int len = src.length;
  26. int left = 0;
  27. int right = len-1;//由于我此处设计的是按照前闭后闭来看的。
  28. //注意小于等于都符合我们区间控制,不操作等于的时候,最后只剩一个元素正好使我们
  29. //要查找的就会遗漏
  30. while(left <= right){
  31. //
  32. //int mid = (left+right)/2; 这种写法容易发生溢出,当两个大的int型的数相加时候范围是有可能超过int的
  33. //所以用下面的写法。
  34. int mid = left +(right-left)/2;
  35. if(value == src[mid]){
  36. return mid;
  37. }else if(value > src[mid]){
  38. //此时要查找的应该在右边部分所以
  39. left = mid+1;
  40. }else if(value < src[mid]){
  41. //此时要查找的应该在左边部分所以
  42. right = mid-1;
  43. }
  44. }
  45. return -1;
  46. }
  47. /**
  48. * 用递归的方法实现二分查找,核心思想是一样的。
  49. * 不过递归实现时,考虑问题的角度的变,现在想每次的问题都是在一段区间内
  50. * 从左向右找,然后递归的从左向右找,所以这个参数肯定也得传入左右的索引,
  51. * 只不过这个索引会越来越小,直到不满足递归条件然后退出。
  52. * @param src 数据源
  53. * @param left 查找的区间的左索引
  54. * @param right 查找的区间的右索引
  55. * @param value 要查找的值
  56. * @return 如果数据存在返回找到的索引,否则返回-1;
  57. */
  58. public int binarySerch(int[] src,int left,int right,int value){
  59. //递归退出条件
  60. if(left > right){
  61. return -1;
  62. }
  63. int mid = left+(right-left)/2;
  64. if(value == src[mid]){
  65. return mid;
  66. }
  67. if(value > src[mid]){
  68. left = left+1;
  69. //下面应该直接进入到另一个函数进行查询,进入以后这个函数就执行完了,然后
  70. //自动销毁了,然后就去那个函数中接着执行,所以就是一层一层的,递归的设计思想
  71. //直到某个函数中找到了,不再调用此处了,或者某个函数不满足递归条件了直接退出。
  72. return binarySerch(src, left, right, value);
  73. }else{
  74. right = mid-1;
  75. return binarySerch(src, left, right, value);
  76. }
  77. //return -1;这就不该有了,现在理解的还不到位。
  78. }
  79. }
  递归实现的二分查找可能性能上会略差,不过都是logn的时间复杂度。慕课网上的图
 

二分查找实现的floor操作(慕课网的)
  1. /**
  2. * 理解的还不够透彻
  3. * 对value向下取数核心思想:
  4. * 从src[left...right],中开始查找.先计算mid
  5. * 当src[mid]大于或者等于value的时候, 让right的索引减一,因为我们要找小于或者等于的。
  6. * 当src[mid]小于value的时候,那么直接把left放在在mid的索引(不放在mid+1怕大于right)
  7. * 就这样在left < right的条件下一只进行次操作,那么最后出来的时候就是我们要找的最大的小于value的值的索引。
  8. *
  9. * @param src 数据源
  10. * @param value 进行floor操作的值。
  11. * @return 返回在数组中找到的value的floor值:
  12. * 1)、找到该value的时候直接返回。
  13. * 2)、没有找到的时候返回比value小的最大值的索引,如果这个索引最大值有多个,那么返回最大索引(从
  14. * 右往左一步步减少)
  15. * 3)、如果整个数组的最小值都比value大,那么不存在value的floor值返回-1.
  16. */
  17. public int floor(int[] src, int value){
  18. int len = src.length;
  19. //左闭右闭的区间找,
  20. int left = 0;
  21. int right = len-1;
  22. //如果最小的元素都比当前的元素小那么就返回-1;
  23. if(src[0] > value){
  24. return -1;
  25. }
  26. //等于时就证明我们已经走到了我们的条件的索引
  27. while(left < right){
  28. //使用mid = left+(right-left)/2会死循环
  29. //因为假设我们一直查找到了两个数相邻的时候,设为A 、B
  30. //我们要查找的数value等于B, 此时A < B 在数组中有序相邻排列。
  31. //left索引、righ索引分别对应于A和B。那么mid =(如上) 永远是等于left所在的索引
  32. //而此时left索引对应的元素小于value,需要把mid的值再赋值给left,再进行求mid,如此进入了死循环
  33. //因为right索引不会动。
  34. //所以我们使用int mid = left+(right-left)/2,这种求mid的方法。
  35. //这种求mid的方法奇数时候没什么影响,在偶数的是后会向上取一个数如:
  36. // 0 1 2 3 的时候会取 2,而我们以前会取1.但是在奇数的时候如
  37. //0 1 2 3 4 5 依然是取3没什么影响。
  38. //综上:当我们使用mid = left+(right-left)/2时候,再出现如上两个数相邻的状况,我们会取到
  39. //right的数,而right的索引无论是在大于还是等于value的时候都会对mid进行减1.这样while的判断
  40. //条件left < right就不符合了,会退出循环。
  41. //mid = left+(right-left)/2 案例例如:
  42. //int[] src = {5 ,23 ,29 ,30 ,33,33 ,35 ,39 ,53 ,56 ,70 ,74 ,81 ,82 ,87 ,87 };
  43. //第一次:0+(15-0)/2 = 7
  44. //src[7] = 39 < value;left = 7
  45. //第二次:7+(15-7)/2 = 11.
  46. //src[11] = 74 > value;right = 10
  47. //第三次:7+(10-7)/2 = 8;
  48. //src[8] = 53 < value;left = 8
  49. //第四次:8+(10-8)/2 = 9;
  50. //src[9] = 56 < value;left = 9
  51. //第五次:9+(10-9)/2 = 9;
  52. //src[9] = 56 < value;left = 9
  53. //接下来一直无限循环。
  54. int mid = left+(right-left+1)/2;
  55. if(src[mid] >= value){
  56. //大于等于的时候mid减一,往前面走一个
  57. right = mid - 1;
  58. }else{
  59. //当小于的时候此处直接把left设置为mid,大范围减少查找的量
  60. left = mid;
  61. }
  62. }
  63. System.out.println("left = "+left+",right = "+right);
  64. //此处出来的时候left是等于right的
  65. //此时由于等于的时候我们队right进行了减1.那么有可能后一个数是等于的所以执行如下操作
  66. if( left + 1 < len && src[left+1] == value )
  67. return left + 1;
  68. // 否则, 该索引即为返回值
  69. return left;
  70. }
二分查找实现的ceil(慕课网上的)
  1. /**
  2. * 理解的也不够透彻
  3. * 对value向上取数核心思想:
  4. * 从src[left...right],中开始查找.先计算mid.
  5. * 当src[mid]小于或者等于value的时候, 让left的索引加1。
  6. * 当src[mid]大于value的时候,那么直接把right放在mid的索引。
  7. * 就这样在left < right的条件下一直进行次操作,那么最后出来的时候就是我们要找的最大的小于value的值的索引。
  8. * @param src 数据源.
  9. * @param value 进行ceil操作的值.
  10. * @return 返回在数组中找到的value的ceil值:
  11. * 1)、找到该value的时候直接返回。
  12. * 2)、没有找到的时候返回比value大的小大值的索引,如果这个索引最小值有多个,那么返回最小索引(从左
  13. * 往右一步步减少)
  14. * 3)、如果整个数组的最大值都比value小,那么不存在value的floor值返回-1.
  15. */
  16. public int ceil(int[] src, int value){
  17. int len = src.length;
  18. //左闭右闭的区间找,
  19. int left = 0;
  20. int right = len-1;
  21. //如果最小的元素都比当前的元素小那么就返回-1;
  22. if(src[right] < value){
  23. return -1;
  24. }
  25. while( left < right ){
  26. //此处动的是左边索引,所以正好不会出现死循环。
  27. int mid = left + (right-left)/2;
  28. if( src[mid] <= value )
  29. left = mid + 1;
  30. else
  31. right = mid;
  32. }
  33. if( right - 1 >= 0 && src[right - 1] == value )
  34. return right - 1;
  35. //该索引即为返回值
  36. return right;
  37. }

0 0