二分查找
二分查找是一种高效的查找算法,但是要求我们前提数据必须是有序的。
实现思路:说白了就是和中间的数比较,如果比中间数小,就和左半部分中间数继续比较,如果比中间数大、就和右半部分中间数继续比较,直到找到为止。
循环二分查找
public static int binarySearch(int[] arr, int key) { //定义左指针和右指针 int left = 0; int right = arr.length - 1; //当查找的值比最小的还小、最大的还大,直接退出 if (key < arr[left] || key > arr[right]) { return -1; } //当左指针小于等于右指针一直进行循环 while (left <= right) { //定义中间值 int mid = (left + right) / 2; //如果中间值小于查找当值,那么左指针右移 if (arr[mid] < key) { left = mid + 1; } else if (arr[mid] > key) { right = mid - 1; } else { return mid; } } return -1; }
递归二分查找
public static int binarySearch(int[] a, int key, int left, int right) { //设置递归退出的条件:当查找当值比最小值小,最大值大,或者左指针大于右指针当时候退出 if (key < a[left] || key > a[right] || left > right) return -1; //定义中间值 int mid = (left + right) / 2; //递归搜索 if (a[mid] < key) { return binarySearch(a, key, mid + 1, right); } else if (a[mid] > key) { return binarySearch(a, key, left, mid - 1); } else { return mid; } }
二维矩阵(第74题)
题目要求:
编写一个高效的算法来判断 m x n 矩阵中,是否存在一个目标值。该矩阵具有如下特性:
每行中的整数从左到右按升序排列。
每行的第一个整数大于前一行的最后一个整数。
示例1 :
输入:matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 3 输出:true
示例2 :
输入:matrix = [[1,3,5,7],[10,11,16,20],[23,30,34,60]], target = 13 输出:false
实现思路:
这题我们把二维数组转换为一维数组,然后进行二分查找来做。
找到一维数组下标和二维数组下标的关系是关键
二维数组下标第一位是:一维数组下标/列数
二维数组下标第二位是:一维数组下标%列数
public static boolean searchMatrix(int[][] matrix, int target) { if (matrix.length == 0) { return false; } int row = matrix.length; int col = matrix[0].length; int left = 0; int right = row * col - 1; while (left <= right) { int mid = (left + right) / 2; if (target < matrix[mid / col][mid % col]) { right = mid - 1; } else if (target > matrix[mid / col][mid % col]) { left = mid + 1; } else { return true; } } return false; }
寻找重复数(第287题)
题目要求:给定一个包含 n + 1 个整数的数组 nums ,其数字都在 [1, n] 范围内(包括 1 和 n),可知至少存在一个重复的整数。
假设 nums 只有 一个重复的整数 ,返回 这个重复的数 。
你设计的解决方案必须 不修改 数组 nums 且只用常量级 O(1) 的额外空间。
示例2:
输入:nums = [1,3,4,2,2] 输出:2
HashMap
public static int findDuplicate(int[] nums) { HashMap<Integer, Integer> map = new HashMap<>(); for (int num : nums) { if (map.containsKey(num)) { return num; } else { map.put(num, 1); } } return -1; }
相邻元素
public static int findDuplicate(int[] nums) { Arrays.sort(nums); for (int i = 0; i < nums.length - 1; i++) { if (nums[i] == nums[i + 1]) { return nums[i]; } } return -1; }
快慢指针(转换为环形链表)
由于以上两种方法很常见,并且效率也是一般,这题我们使用链表的思想解决。
这样的话此题就变成了找环形链表的出口,我们分为两步骤来寻找出口。
步骤一:寻找链表中的环形链表
定义两个指针,分别是快指针和慢指针
指针走两步,慢指针走一步
当快慢指针相遇当时候,必定会在环中
步骤二:寻找环形链表上的入口节点
定义两个指针,让before指向链表最开始,让after指向相交点
当两个指针相遇,那么就是环形链表当入口点
public static int findDuplicate(int[] nums) { int fast = 0; int low = 0; /** * 第一阶段:寻找链表中的环 * 定义两个指针,分别是快指针和慢指针 * 快指针走两步,慢指针走一步 * 当快慢指针相遇当时候,必定会在环中 */ do { low = nums[low]; fast = nums[nums[fast]]; } while (fast != low); /** * 第二阶段:寻找环形链表上的入口节点 * 定义两个指针,让before指向链表最开始,让after指向相交点 * 当两个指针相遇,那么就是环形链表当入口点 */ int before = 0; int after = low; while (before != after) { before = nums[before]; after = nums[after]; } return before; }