JavaScript 二分查找

二分查找的思路很简单,就不叙述了,考的也不是思路,重点是应用。
二分查找不止可以用于有序数组,在特定情况下也可以用于无序数组。


在无序数组的情况下应用

题目要求
给定一个无序数组arr,已知任意相邻的两个元素值都不重复。请返回任意一个局部最小的位置。
所谓局部最小的位置是指:

  • 如果arr[0]<arr[1],那么位置0就是一个局部最小的位置。
  • 如果ar[N-1](也就是arr最右的数)小于arr[N-2],那么位置N-1也是局部最小的位置。
  • 如果位置i既不是最左位置也不是最右位置。那么只要满足arr[i]同时小于它左右两侧的值即(arr[i-1]和arr[i+1]),那么位置i也是一个局部最小的位置。

题目解析
本题是找局部最小,不是找最小值。也就是说,你只要找到某一部分的最小值就行了。根据题目要求我自己来出几组例子:

输入样例 输出样例
[1,2,3,1,2,3] 1
[3,2,3,1,2,0] 0
[6,2,3,-5,2,3] 2(3左边的2)或者-5
[6,4,3,1,0,3] 0

上边第三组样例输出几取决于你的代码。按照我的代码来那就是输出2。因为找的是局部最小。当中间值比两边都大的时候,你返回任意一边就可以了,我是默认返回左边的。

			function min(a) {
				let length = a.length
				if (length === 0) {				//数组空
					return -1
				} else {
					let low = 0
					let high = length - 1
					let mid
					if(low === high || a[0] < a[1]){	
					//数组中只有一个数或者arr[0]<arr[1],a[0]就是局部最小
						return 0	
					}else if (a[length - 1] < a[length - 2]) { 
						return length - 1
					} else {
						while (low <= high) {		//二分法
							mid = Math.floor(low + (high - low) / 2)	//向下取整
							if (a[mid] < a[mid + 1] && a[mid] < a[mid - 1]) {
								return mid
							} else if (a[mid] > a[mid - 1]) {
								high = mid - 1
							} else {
								low = mid + 1
							}
						}
					}
				}
			}

补充:代码里我用的low + (high - low) / 2而不是(low + high)/2因为这样可以防止数组过大时发生溢出。


在有序重复数组中的应用

1

给定一个有序数组arr,再给定一个整数num请在arr中找到num这个数出现的最左边的位置。
题目要求

function find(a, num) {
	let res = -1
	if (a.length === 0) {	//数组为空
		return res
	}
	let right = a.length - 1
	let mid
	let left = 0
	while (left <= right) {
		if (a[0] === num) {		//a[0]就是要找的数
			return res = 0
		} else {
			while (left <= right) {
				mid = Math.floor(left + (right - left) / 2)
				if (a[mid] === num && a[mid] > a[mid - 1]) {
					res = mid
					break		//一定要写,否则死循环
				} else if (a[mid] < num) {
					left = mid + 1
				} else {
					right = mid - 1
				}
			}
			return res	
		}
	}
}

2

给定一个有序数组arr,其中不含有重复元素,请找到满足arr[i]==i条件的最左的位置。如果所有位置上的数都不满足条件,返回-1。

function find(arr) {
				let res = -1
				if (arr.length === 0 || arr[0] > arr[length - 1] || arr[length - 1] < 0) {
				//长度为0 或者 arr[0]>arr[length - 1]数组递增所以不可能出现arr[i]=i
					return res
				} else {
					let left = 0,
						right = arr.length - 1,
						mid = left + (right - left) / 2
					while (left <= right) {
						mid = Math.trunc(left + (right - left) / 2)
						if (arr[mid] < mid) {
							left = ++mid
						} else if (arr[mid] > mid) {
							right = --mid
						} else {
							res = mid
							right = --mid
						}
					}
					return res
				}
			}

循环数组找最值/中位数

1

题目要求:给定一个有序循环数组arr,返回arr中的最小值。有序循环数组是指,有序数组左边任意长度的部分放到右边去,右边的部分拿到左边来。比如数组1,2,3,3,4,是有序循环数组,4,1,2,3,3也是。

输入样例 输出样例
0,1,2,3,-1,-1,-1 -1(res=4)
0 0
2,2,2,2,1,2,2,2 1
function Min(arr) {
	//我这里res仅仅是记录下标而已,你可以不写的。
	let res = -1;
	if (arr.length === 0) {
		return res //数组空。查找失败
	} else if (arr.length === 1 || arr[0] < arr[arr.length - 1]) {
		//数组中一个值 或者 第一个小于最后一个即没翻转过
		return arr[0]
	} else {
		let left = 0,
			right = arr.length - 1,
			mid = 0
		while (left <= right) {
			mid = Math.trunc(left + (right - left) / 2)
			if (left + 1 === right) {
				res = arr[left] > arr[right] ? right : left
				return arr[res] 
			} else { 
				if (arr[left] > arr[mid]) {
					right = mid
				} else if (arr[mid] > arr[right]) {
					left = mid
				} else if (arr[left] === arr[mid] && arr[mid] === arr[right]) {
					let min = arr[0]
					for (let i = 1; i < arr.length; i++) {
					//这是为了应对输入样本3,遍历查找
						if (arr[i] < arr[0]) {
							res = i
							return arr[res]
						}
					}
				}
			}
		}
	}
}

2

还是这个题。如果让你找中位数呢?

function Min(arr) {
				let res = "数组空,无中位数";
				let m = Math.trunc(arr.length/2)
				var num = function(res){
					if (arr.length % 2 !== 0) {
						res = arr[(res + m) % arr.length]
					} else {
						num1 = arr[(res + m) % arr.length]
						num2 = arr[(res + m -1 ) % arr.length]
						res = (num1 + num2) / 2
					}
					return res
				}
				if (arr.length === 0) {
					return res //数组空。查找失败
				} else if (arr.length === 1) { //数组中一个值 
					return arr[0]
				} else if(arr[0] < arr[arr.length - 1]){ //第一个小于最后一个即没翻转过
					return num(0)
				}else {
					let left = 0,
						right = arr.length - 1,
						mid = 0
					while (left <= right) {
						mid = Math.trunc(left + (right - left) / 2)
						if (left + 1 === right) {
							res = num(arr[left] > arr[right] ? right : left)
							return res
						} else {
							if (arr[left] > arr[mid]) {
								right = mid
							} else if (arr[mid] > arr[right]) {
								left = mid
							} else if (arr[left] === arr[mid] && arr[mid] === arr[right]) {
								let min = arr[0]
								for (let i = 1; i < arr.length; i++) {
									//这是为了应对输入样本3,遍历查找
									if (arr[i] < arr[0]) {
										res = num(a[i])
										return arr[res]
									}
								}
							}
						}
					}
				}
			}

应用:更快的求一个整数k的N次方。

如果两个整数相乘并得到结果的时间复杂度为O(1),得到整数k的N次方的过程k*k*…*k复杂度为O(n),请想办法实现时间复杂度为O(logN)的方法。
这个也是用的二分查找的思想,虽然我没看出来。但是不得不说算法博大精深!!!

function calculate(a, b) {
	let arr = []
	//将b转换为2进制表示
	while (b != 0) {
		arr.push(b % 2)
		b = Math.trunc(b / 2)
	}
	console.log(arr)
	let mul = a
	let res 
	if(arr[0]!==0){
		res = a
	}else{
		res = 1
	}
	//b二进制有几位,乘几次就可以了
	for (let i = 1; i < arr.length; i++) {
		mul *= mul
		console.log("mul: "+mul)
		if (arr[i] === 1) {
			res *= mul
			console.log(res)
		}
	}
}

题目来源于牛客网面试算法。

发布了131 篇原创文章 · 获赞 451 · 访问量 54万+

猜你喜欢

转载自blog.csdn.net/qq_36667170/article/details/105277066
今日推荐