二分查找

二分查找

二分查找是一种极致快速的查找方式,时间复杂度仅为:O(log(n))

二分查找对数据有很高的要求,包含以下三点:

  • 必须为数组结构
    二分查找其实也可以依赖链表结构,不过如果使用链表接口、那么时间复杂度就会退化到O(n),把整个数据遍历一遍进行查找的效率是一样的、甚至更低,链表不适用于二分查找。
  • 数据必须有序
    二分查找依赖于数据的有序、因为二分法中的数据对比、取决的数据的顺序。如果数据无序,那么需要先排序
  • 数据量太大或太小都不适合二分查找
    如果数据量特别小、个位数或者就几十个数据,那么可以直接遍历数据进行查找;
    如果数据量特别大,几百兆或者几个G,由于二分查找依赖于数据,需要连续空间,那么系统可能提供不了连续的几个G的空间

基本的二分查找算法实现

public class Bsearch {

    @Test
    public void testSearch() {
        // 二分查找,必须有序
        int[] data = new int[]{19, 55, 67, 23, 27, 8, 11, 33, 45, 98};

        // 1、快排
        quickSort(data, 0, data.length - 1);
        System.out.println(Arrays.toString(data));

        // 2、二分查找
        int value = 23;
        System.out.println(value + " : 在数组data中的index为 : " + bSearch(data, 0, data.length - 1, value));
    }

    /**
     * 二分查找
     */
    private int bSearch(int[] data, int low, int high, int value) {
        if (low > high) {
            return -1;
        }

        int mid = low + ((high - low) >> 1);
        if (value == data[mid]) {
            return mid;
        } else if (value > data[mid]) {
            return bSearch(data, mid + 1, high, value);
        } else {
            return bSearch(data, low, mid - 1, value);
        }
    }

    /**
     * 快排逻辑
     */
    private void quickSort(int[] data, int p, int r) {
        if (p >= r) {
            return;
        }

        int q = partition(data, p, r);
        quickSort(data, p, q - 1);
        quickSort(data, q + 1, r);
    }

    private int partition(int[] data, int p, int r) {
        int i = p;

        int povit = data[r];
        for (int j = p; j < r; j++) {
            if (data[j] < povit) {
                if (i != j) {
                    int tmp = data[i];
                    data[i] = data[j];
                    data[j] = tmp;
                }
                i++;
            }
        }

        int tmp = data[r];
        data[r] = data[i];
        data[i] = tmp;
        return i;
    }

}

使用二分查找计算正整数的平方根

public class Sqrt {

    @Test
    public void test() {
        int data = 8;
        System.out.println(mySqrt(data));
    }

    private int mySqrt(int x) {
        if (x < 2) {
            return x;
        } else {
            return sqrt(x, 0, x);
        }
    }

    private int sqrt(int data, int low, int high) {
        if(low > high){
            return high;
        }

        int mid = low + ((high - low) >> 1);
        if (mid > data / mid) {
            return sqrt(data, low, mid - 1);
        } else if (mid < data / mid) {
            return sqrt(data, mid + 1, high);
        } else {
            return mid;
        }
    }
}

二分查找的变体

查找第一个指定元素的位置

如果数组中存在多个重复的指定元素、请返回第一个指定元素的index

private int bSearch(int[] data, int low, int high, int value) {
    if (low > high) {
        return -1;
    }

    int mid = low + ((high - low) >> 1);
    if (value == data[mid]) {
        if (data[mid - 1] != value) {
            return mid;
        } else {
            return bSearch(data, low, mid - 1, value);
        }
    } else if (value > data[mid]) {
        return bSearch(data, mid + 1, high, value);
    } else {
        return bSearch(data, low, mid - 1, value);
    }
}

查找最后一个指定元素的位置

private int bSearch(int[] data, int low, int high, int value) {
if (low > high) {
      return -1;
  }

  int mid = low + ((high - low) >> 1);
  if (value == data[mid]) {
      if (data[mid + 1] != value) {
          return mid;
      } else {
          return bSearch(data, mid + 1, high, value);
      }
  } else if (value > data[mid]) {
      return bSearch(data, mid + 1, high, value);
  } else {
      return bSearch(data, low, mid - 1, value);
  }
}

查找第一个大于等于给定值的元素位置

public class FirstLarge {

    @Test
    public void test() {
        int[] data = new int[]{2, 5, 8, 11, 16};
        int value = 11;
        System.out.println(firstLarge(data, value, 0, data.length - 1));
    }

    private int firstLarge(int[] data, int value, int low, int high) {
        if (low > high) {
            return low;
        }

        int mid = low + ((high - low) >> 1);
        if (value > data[mid]) {
            return firstLarge(data, value, mid + 1, high);
        } else if (value < data[mid]) {
            return firstLarge(data, value, low, mid - 1);
        } else {
            return mid;
        }
    }
}

查找最后一个小于等于给定值的元素位置

public class LastSmall {

    @Test
    public void test() {
        int[] data = new int[]{2, 6, 9, 11, 16};
        int value = 10;
        System.out.println(lastSmall(data, value, 0, data.length - 1));
    }

    private int lastSmall(int[] data, int value, int low, int high) {
        if (low > high) {
            return high;
        }

        int mid = low + ((high - low) >> 1);
        if (value > data[mid]) {
            return lastSmall(data, value, mid + 1, high);
        } else if (value < data[mid]) {
            return lastSmall(data, value, low, mid - 1);
        } else {
            return mid;
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值