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)
		}
	}
}

題目來源於牛客網面試算法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章