Arrays array.count和array[0…Bool{var slice:SubSequence=self[…]while!slice.isEmpty{let mid=slice.indexslice.startIndex,offsetBy:slice.count/2如果element==slice[mid]{return true}如果element

Arrays array.count和array[0…Bool{var slice:SubSequence=self[…]while!slice.isEmpty{let mid=slice.indexslice.startIndex,offsetBy:slice.count/2如果element==slice[mid]{return true}如果element,arrays,swift,algorithm,testing,binary-search,Arrays,Swift,Algorithm,Testing,Binary Search,今天我做了一个测试,要求我搜索一个整数数组,问题是: 本练习的目标是检查数据表中是否存在数字 数组 规格: 这些项是按升序排列的整数 该阵列最多可包含100万项 实现函数existsInArray\unumbers:[Int],\uk:Int so 如果k属于数字,则返回true,否则函数 应该返回false 例如: let numbers = [-9, 14, 37, 102] existsInArray(numbers, 102) // returns true existsInArray(

今天我做了一个测试,要求我搜索一个整数数组,问题是:

本练习的目标是检查数据表中是否存在数字 数组

规格:

这些项是按升序排列的整数

该阵列最多可包含100万项

实现函数existsInArray\unumbers:[Int],\uk:Int so 如果k属于数字,则返回true,否则函数 应该返回false

例如:

let numbers = [-9, 14, 37, 102]
existsInArray(numbers, 102) // returns true
existsInArray(numbers, 36) //returns false
注意:尽量节省CPU周期

好的,我给出了我的答案,就是下面的代码,然后等待结果

func existsInArray(_ numbers: [Int], _ k: Int) -> Bool {
    if numbers.isEmpty {
        return false
    }
    let numbersHalfIndex: Int = (numbers.count/2)
    if k == numbers[numbersHalfIndex] {
        return true
    } else if k != numbers[0] && numbers.count == 1 {
        return false
    } else if k <= numbers[numbersHalfIndex] {
        let leftHalfNumbersArray = numbers[0 ..< numbersHalfIndex]
        return existsInArray(Array(leftHalfNumbersArray), k)
    } else if k > numbers[numbersHalfIndex] {
        let rightHalfNumbersArray = numbers[numbersHalfIndex ..< numbers.count]
        return existsInArray(Array(rightHalfNumbersArray), k)
    } else {
        return false
    }
}
结果如下:

//////////地雷//////////

真的

我的时间:

1.200880056591797 s

//////////马丁R//////////

真的

Martin R经过的时间:0.00012993812561035156秒

大约快1000倍

访问number.count不是问题,因为这是数组的O1操作。使用数字[0… 有两种可能的方法可以避免这种情况:

更新当前搜索范围下限和上限的数组索引,而不是创建沿递归传递的数组。 将数组切片向下传递到递归。切片与原始数组共享元素,只要它们没有变异。 第二种方法的演示:

func existsInArray(_ numbers: ArraySlice<Int>, _ k: Int) -> Bool {
    if numbers.isEmpty {
        return false
    }
    let numbersHalfIndex = numbers.startIndex + numbers.count / 2
    if k == numbers[numbersHalfIndex] {
        return true
    } else if k < numbers[numbersHalfIndex] {
        return existsInArray(numbers[..<numbersHalfIndex], k)
    } else {
        return existsInArray(numbers[(numbersHalfIndex + 1)...], k)
    }
}
正如@Leo所建议的,您可以将其实现为一个集合方法,而不是实现两个单独的方法。集合索引不一定是整数,但对于RandomAccessCollection,索引计算保证为O1。您还可以将其推广到任意可比较元素的集合,而不是整数

下面是一个可能的实现:

extension RandomAccessCollection where Element: Comparable {
    /// Returns a Boolean value indicating whether the collection contains the
    /// given element. It is assumed that the collection elements are sorted
    /// in ascending (non-decreasing) order. 
    ///
    /// - Parameter element: The element to find in the collection.
    /// - Returns: `true` if the element was found in the collection; otherwise,
    ///   `false`.
    ///
    /// - Complexity: O(log(*n*)), where *n* is the size of the collection.
    func binarySearch(for element: Element) -> Bool {
        if isEmpty {
            return false
        }
        let midIndex = index(startIndex, offsetBy: count / 2)
        if element == self[midIndex] {
            return true
        } else if element < self[midIndex] {
            return self[..<midIndex].binarySearch(for: element)
        } else {
            return self[index(after: midIndex)...].binarySearch(for: element)
        }
    }
}
或者,使用非递归方法更新搜索范围的索引:

extension RandomAccessCollection where Element: Comparable {
    func binarySearch(for element: Element) -> Bool {
        var lo = startIndex
        var hi = endIndex

        while lo < hi {
            let mid = index(lo, offsetBy: distance(from: lo, to: hi) / 2)
            if element == self[mid] {
                return true
            } else if element < self[mid] {
                hi = mid
            } else {
                lo = index(after: mid)
            }
        }
        return false
    }
}
访问number.count不是问题,因为这是阵列的O1操作。使用数字[0… 有两种可能的方法可以避免这种情况:

更新当前搜索范围下限和上限的数组索引,而不是创建沿递归传递的数组。 将数组切片向下传递到递归。切片与原始数组共享元素,只要它们没有变异。 第二种方法的演示:

func existsInArray(_ numbers: ArraySlice<Int>, _ k: Int) -> Bool {
    if numbers.isEmpty {
        return false
    }
    let numbersHalfIndex = numbers.startIndex + numbers.count / 2
    if k == numbers[numbersHalfIndex] {
        return true
    } else if k < numbers[numbersHalfIndex] {
        return existsInArray(numbers[..<numbersHalfIndex], k)
    } else {
        return existsInArray(numbers[(numbersHalfIndex + 1)...], k)
    }
}
正如@Leo所建议的,您可以将其实现为一个集合方法,而不是实现两个单独的方法。集合索引不一定是整数,但对于RandomAccessCollection,索引计算保证为O1。您还可以将其推广到任意可比较元素的集合,而不是整数

下面是一个可能的实现:

extension RandomAccessCollection where Element: Comparable {
    /// Returns a Boolean value indicating whether the collection contains the
    /// given element. It is assumed that the collection elements are sorted
    /// in ascending (non-decreasing) order. 
    ///
    /// - Parameter element: The element to find in the collection.
    /// - Returns: `true` if the element was found in the collection; otherwise,
    ///   `false`.
    ///
    /// - Complexity: O(log(*n*)), where *n* is the size of the collection.
    func binarySearch(for element: Element) -> Bool {
        if isEmpty {
            return false
        }
        let midIndex = index(startIndex, offsetBy: count / 2)
        if element == self[midIndex] {
            return true
        } else if element < self[midIndex] {
            return self[..<midIndex].binarySearch(for: element)
        } else {
            return self[index(after: midIndex)...].binarySearch(for: element)
        }
    }
}
或者,使用非递归方法更新搜索范围的索引:

extension RandomAccessCollection where Element: Comparable {
    func binarySearch(for element: Element) -> Bool {
        var lo = startIndex
        var hi = endIndex

        while lo < hi {
            let mid = index(lo, offsetBy: distance(from: lo, to: hi) / 2)
            if element == self[mid] {
                return true
            } else if element < self[mid] {
                hi = mid
            } else {
                lo = index(after: mid)
            }
        }
        return false
    }
}

你在操场上测试吗?您是否尝试过使用endIndex而不是count?顺便说一句,您正在使用切片初始化一个新数组将切片转换回使用数组的数组。。。是你在那里做的最慢的事情。如果你在切片机上做了所有的工作会更好你是在操场上测试的吗?您是否尝试过使用endIndex而不是count?顺便说一句,您正在使用切片初始化一个新数组将切片转换回使用数组的数组。。。是你在那里做的最慢的事情。如果你把切片机上的所有工作都做好就更好了,就这样。事实证明,创建新数组的速度太慢,足以触发错误的答案。@DouglasPfeifer:然后您可以定义一个func existsInArray\uuNumbers:[Int]、lowIndex:Int、highIndex:Int\uk:Int->Bool函数。或者不使用递归,类似于中。@LeoDabus:谢谢你的建议,你当然是对的。我的意图主要是解释这个问题,而不是提供最通用的二进制搜索方法。@LeoDabus:这正是我刚才想的:切片在非递归版本中不是更有效吗?func binarySearchfor element:element->Bool{var slice:SubSequence=self[…]while!slice.isEmpty{let mid=slice.indexslice.startIndex,offsetBy:slice.count/2如果element==slice[mid]{return true}如果elementBool函数。或者不使用递归,类似于中。@LeoDabus:谢谢你的建议,你是o
当然是对的。我的意图主要是解释这个问题,而不是提供最通用的二进制搜索方法。@LeoDabus:这正是我刚才想的:切片在非递归版本中不是更有效吗?func binarySearchfor element:element->Bool{var slice:SubSequence=self[…]while!slice.isEmpty{let mid=slice.indexslice.startIndex,offsetBy:slice.count/2如果element==slice[mid]{return true}如果element