二分查找的思路很簡單,就不敘述了,考的也不是思路,重點是應用。
二分查找不止可以用於有序數組,在特定情況下也可以用於無序數組。
在無序數組的情況下應用
題目要求:
給定一個無序數組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)
}
}
}
題目來源於牛客網面試算法。