免責事項:この記事はブロガーオリジナル記事ですが、許可ブロガーなく再生してはなりません。https://blog.csdn.net/lin1109221208/article/details/90902919
1、説明:
配列に従って昇順は、以前は未知点に回転したと仮定する。例えば:配列[0、1、2、4、5、6、7]になることができる[4、5、6、7、0、1、2]。
標的配列、そのインデックスリターンの存在は、それ以外の場合は-1を返す場合、与えられた目標値を検索します。
あなたは、アルゴリズムの時間計算量O(LOGN)レベルでなければならない、配列内の要素のない繰り返しが存在しないと仮定することができます。
実施例1:入力:NUMS = [4、5、6、7、0、1、2]、目標= 0
出力:4
実施例2:入力:NUMS = [4、5、6、7、0、1、2]、ターゲット= 3
出力:-1
2、アルゴリズム
はっきりタイトル時間複雑として、これだけのバイナリ検索を使用します
1)直接二分法、2点の位置を決定します
func search3(_ nums: [Int], _ target: Int) -> Int {
//直接用二分法,判断二分点-----超出时间限制
let n = nums.count
if n==0 {
return -1
}
var left = 0
var right = n-1
while left <= right {
let mid = left+(right-left)/2
//如果中间值小于第一个数,目标值也小于第一个数,取中间值
let num = (nums[mid]<nums[0]) == (target<nums[0]) ? nums[mid] : target<nums[0] ? Int.min : Int.max
if num < target {
//中间值小于目标值,在中间值的右边查找
left = mid+1
}else if num > target {
//中间值大于目标值,在中间值的左边查找
right = mid
}else {
//中间值等于目标值,返回中间值下标
return mid
}
}
// print(left, right)
return -1
}
2)を直接二分法2を使用する方法を
func search2(_ nums: [Int], _ target: Int) -> Int {
//直接用二分法,判断二分点
/*
1、直接等于target
2、在左半边递增区域
1)target 在 left 和 mid 之间
2)不在之间
3、在右边的递增区域、
1)target在mid 和 right 之间
2)不在之间
*/
let n = nums.count
if n==0 {
return -1
}
var left = 0
var right = n-1
while left < right {
let mid = left+(right-left)/2
if nums[mid] == target {
return mid
}else if nums[mid] >= nums[left]{//二分点在左半边递增区域
if nums[left] <= target && target < nums[mid] {
//target位于 left 和 mid 之间
right = mid-1
}else{
left = mid+1
}
}else if nums[mid] < nums[right]{
//二分点在右边的递增区域
if nums[mid] < target && target <= nums[right] {
left = mid+1
}else{
right = mid-1
}
}
}
// print(left, right)
return nums[left] == target ? left : -1
}
3)二分探索法、第1の分割点を見つけ、その後、目標値を探し
func search1(_ nums: [Int], _ target: Int) -> Int {
//二分查找法:先找到数组中最小的数 即分割点
let n = nums.count
if n==0 {
return -1
}
var left = 0
var right = n-1
while left < right {
let mid = left + (right-left)/2
if nums[mid] > nums[right] {
//如果中间的数大于右边的数,从mid的右边查找
left = mid+1
}else{
right = mid
}
}
//分割点下标
let split_t = left
left = 0
right = n-1
//判断分割点在target的左边还是右边
if nums[split_t] <= target && target <= nums[right] {
//分割点在target的右边
left = split_t
}else{
//分割点在target的左边
right = split_t
}
while left <= right {
let mid = left + (right-left)/2
if nums[mid] == target {
//中间值等于目标值
return mid
}else if nums[mid] > target {
//中间值大于目标值,右边下标左移一位
right = mid - 1
}else{
//反之,左边下标右移一位
left = mid+1
}
}
return -1
}
4)バイナリ検索:配列添字回転の最小数を検索すなわち、サブ機能3
func search(_ nums: [Int], _ target: Int) -> Int {
//二分查找法:先找到数组中最小的数 即发生旋转的下标
let n = nums.count
if n==0 {
return -1
}
if n==1 {
return nums[0]==target ? 0 : -1
}
let rotate_index = find_rotate_index(nums, 0, n-1)
//如果target是最小的值
if nums[rotate_index] == target {
return rotate_index
}
//如果数组并没有旋转,需要查找整个数组
if rotate_index == 0 {
return search(nums, target, 0, n-1)
}
if target < nums[0] {
//如果目标值小于第一个数,在旋转下标的右边查找
return search(nums, target, rotate_index, n-1)
}else{
return search(nums, target, 0, rotate_index)
}
}
//找到旋转的下标
private func find_rotate_index(_ nums : [Int], _ left: Int, _ right : Int)->Int{
var left = left
var right = right
if nums[left] < nums[right] {
//数组未发生旋转
return 0
}
//二分查找
while left <= right {
var pivot = (left+right)/2
if nums[pivot] > nums[pivot+1] {
//基准数>基准后一位,往右边查找
return pivot+1
}else{
//如果基准数小于左边左边的数,则右下标左移一位,反之,左下标右移一位
if nums[pivot]<nums[left]{
right = pivot-1
}else{
left = pivot+1
}
}
}
return 0
}
//根据下标二分查找
private func search(_ nums : [Int], _ target : Int, _ left: Int, _ right : Int)->Int{
var left = left
var right = right
while left <= right {
var pivot = (left+right)/2
if nums[pivot]==target {
return pivot
}else{
if target < nums[pivot] {
right = pivot-1
}else{
left = pivot+1
}
}
}
return -1
}
5)試運転
调用:
var num3 = [4, 5, 6, 7, 0, 1, 2]
print(search(num3, 0), search(num3, 3))
print(search1(num3, 0), search1(num3, 3))
print(search2(num3, 0), search2(num3, 3))
print(search3(num3, 0), search3(num3, 3))
运行结果:
4 -1
4 -1
4 -1
4 -1