LeetCode进34-二分查找

657 阅读4分钟

闲聊

在开算法博客至今,曾经有读者在评论中质疑过博客中大厂面试题的真实性,在之前某一篇知乎评论中我曾经回复过并且列出了某些依据,现在看来觉得完全没必要。在此声明一下,所有算法题都是通过个人精心整理查阅,有些是源于一些开源社区,有些是来自某些开发者面试经验(面经),还有些则是从某些技术网站获取...这些算法题选的题的依据是个人判断理解一些对与算法学习进阶比较有代表性的典型问题。后续类似面试题的真实性问题将不再回复,最后再次声明本人开算法博客的初衷是帮助有需要的人,有任何技术方面的问题欢迎交流,非诚勿扰~

概要

二分查找也称折半查找,是典型的分治思想的应用,是一种效率较高的查找方法。折半查找要求线性表必须采用顺序存储结构,而且表中元素按关键字有序排列。之前的一篇博客LeetCode进阶4-分而治之也介绍过分治法,本篇进一步介绍其思想应用的经典算法—二分查找。

34. Find First and Last Position of Element in Sorted Array (Medium)

Given an array of integers nums sorted in ascending order, find the starting and ending position of a given target value.

Your algorithm's runtime complexity must be in the order of O(log n).

If the target is not found in the array, return [-1, -1].

Example 1:

Input: nums = [5,7,7,8,8,10], target = 8 Output: [3,4] Example 2:

Input: nums = [5,7,7,8,8,10], target = 6 Output: [-1,-1]

34. 在排序数组中查找元素的第一个和最后一个位置(中等)

给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。

你的算法时间复杂度必须是 O(log n) 级别。

如果数组中不存在目标值,返回 [-1, -1]。

示例 1:

输入: nums = [5,7,7,8,8,10], target = 8 输出: [3,4] 示例 2:

输入: nums = [5,7,7,8,8,10], target = 6 输出: [-1,-1]

  • 分类:二分查找(Binary Search)

分析&&思路

按照正常的二分查找思路,循环找到匹配目标数字的下标,继续将右指针变小查找到第一个目标数字。用新的循环从找到的第一个目标数字下标从左往右查找直到最后一个目标数字。(具体的查找思路见伪代码)

伪代码

1、相关声明
   1.1、声明搜索用的左右指针,初始左指针下标0,右指针下标数组末位nums.length-1;
   1.2、获取数组中间下标mid = (l + r) / 2;
   1.3、声明创建结果数组,初始化赋值-1;
2、循环二分查找,直到左指针大于右指针查找结束
   2.1、若中间位置数字小于目标数字,说明目标数字在mid右边,将左指针l右移赋值为mid+1;
   2.2、若中间位置数字大于目标数字,说明目标数字在mid左边,将右指针r左移赋值为mid-1;
   2.3、若中间位置数字等于目标数字:
      2.3.1、将mid作为第一次出现位置存入数组;
      2.3.2、右指针r左移赋值为mid-1,继续查找相等的数字直到找到第一次出现的位置;
   2.4、更新mid;
3、从第一次出现的位置开始,循环往右查找最后一次出现的位置:
   3.1、声明pr指针初始赋值为第一次出现位置下标;
   3.2、当二分查找已找到目标数字时
      3.2.1、循环直到数组越界或者数组当前元素不等于目标元素时结束:
        3.2.1.1、更新最后一次出现位置下标;
        3.2.1.2、更新之后往右移动指针;
4、最终返回已存储位置的结果数组;
          

编码实践

	public int[] searchRange(int[] nums, int target) {
		int l = 0, r = nums.length - 1;
		int mid = (l + r) / 2;
		int res[] = { -1, -1 };
		while (l <= r) {
			if (nums[mid] < target) {
				l = mid + 1;
			} else if (nums[mid] > target) {
				r = mid - 1;
			} else {
				res[0] = mid;
				r = mid - 1;
			}
			mid = (l + r) / 2;
		}
		int pr = res[0];
		if (pr != -1) {
			while (pr <= nums.length - 1 && target == nums[pr]) {
				res[1] = pr++;
			}
		}
		return res;
	}

1.jpg

扩展延伸

本题实际在头条笔试题中也曾经出现过

有序数组二分查找,返回查找元素最后一次出现的位置,若不存在则返回-1

感兴趣可以结合上面思路思考,下面是题解参考代码:

	public static int searchRange(int[] nums, int target) {
		int l = 0, r = nums.length - 1;
		int mid = (l + r) / 2;
		int res = -1;
		while (l <= r) {
			if (nums[mid] < target) {
				l = mid + 1;
			} else if (nums[mid] > target) {
				r = mid - 1;
			} else {
				res = mid;
				l = mid + 1;
			}
			mid = (l + r) / 2;
		}

		return res;
	}

Alt

关注订阅号 获取更多干货~