在Go(Golang)中搜索经过排序和透视的数组的方法

95 阅读2分钟

概述

我们有一个已排序的输入数组,但在某一索引处被枢轴化。例如,考虑以下数组

[1, 3, 5, 7, 9]

它在索引3处被旋转和枢轴化了。

[5, 7, 9, 1, 3]

给定一个目标元素,目标是在**O(logn)**时间内找到该排序和旋转的数组中目标元素的索引。如果目标元素在给定的数组中不存在,它应该返回-1。

例如

Input: [5, 7, 9, 1, 3]
Target Element: 7
Output: 2

Input: [5, 7, 9, 1, 3]
Target Element: 8
Output: -1

以下是策略

  • 在给定的输入数组中使用二进制搜索,并进行一些修改,找到枢纽索引

  • 如果目标元素小于数组的起始元素,那么从枢轴端到数组的末端进行二进制搜索

  • 如果目标元素大于数组的起始元素,则从起始点到支点-1索引进行二进制搜索。

以下是寻找支点索引的策略

  • 做一个二进制搜索。对于每一个mid元素,检查midmid+1 是否是支点

  • 如果mid的值小于输入数组开始时的值,则在mid的左边进行搜索

  • 如果mid的值大于输入数组的起始值,则在mid的右边搜索。

程序

以下是相同的程序。

package main

import "fmt"

func main() {
	output := search([]int{4, 5, 6, 7, 0, 1, 2}, 0)
	fmt.Println(output)

	output = search([]int{4, 5, 6, 7, 0, 1, 2}, 1)
	fmt.Println(output)

	output = search([]int{4, 5, 6, 7, 0, 1, 2}, 2)
	fmt.Println(output)

	output = search([]int{4, 5, 6, 7, 0, 1, 2}, 4)
	fmt.Println(output)

	output = search([]int{4, 5, 6, 7, 0, 1, 2}, 5)
	fmt.Println(output)

	output = search([]int{4, 5, 6, 7, 0, 1, 2}, 6)
	fmt.Println(output)

	output = search([]int{4, 5, 6, 7, 0, 1, 2}, 7)
	fmt.Println(output)

	output = search([]int{4, 5, 6, 7, 0, 1, 2}, 3)
	fmt.Println(output)

	output = search([]int{1, 2}, 3)
	fmt.Println(output)
}

func search(nums []int, target int) int {
	pivot := findPivot(nums)

	if pivot == -1 {
		return binarySearch(nums, 0, len(nums)-1, target)
	}

	if target < nums[0] {
		return binarySearch(nums, pivot, len(nums)-1, target)
	}

	return binarySearch(nums, 0, pivot-1, target)
}

func findPivot(nums []int) int {
	return findPivotUtil(nums, 0, len(nums)-1)
}

func findPivotUtil(nums []int, start, end int) int {
	if start > end {
		return -1
	}

	mid := (start + end) / 2

	if mid+1 <= end && nums[mid] > nums[mid+1] {
		return mid + 1
	}

	if mid-1 >= start && nums[mid] < nums[mid-1] {
		return mid
	}

	if nums[mid] < nums[start] {
		return findPivotUtil(nums, start, mid-1)
	}

	return findPivotUtil(nums, mid+1, end)

}

func binarySearch(nums []int, start, end, target int) int {
	if start > end {
		return -1
	}

	mid := (start + end) / 2

	if nums[mid] == target {
		return mid
	}

	if target < nums[mid] {
		return binarySearch(nums, start, mid-1, target)
	} else {
		return binarySearch(nums, mid+1, end, target)
	}

}

输出

4
5
6
0
1
2
3
-1
-1