在Swift中旋转数组

时间:2017-05-03 23:34:25

标签: swift algorithm

在Swift中探索算法时,如果不使用func shiftLeft / shiftRight,就无法在swift中找到阵列旋转算法。

C有这个优雅的算法,时间复杂度为O(N):

/* Function to left rotate arr[] of size n by d */
void leftRotate(int arr[], int d, int n)
{
    rvereseArray(arr, 0, d-1);
    rvereseArray(arr, d, n-1);
    rvereseArray(arr, 0, n-1);
}

/*Function to reverse arr[] from index start to end*/
void rvereseArray(int arr[], int start, int end)
{
    int temp;
    while (start < end)
    {
        temp = arr[start];
        arr[start] = arr[end];
        arr[end] = temp;
        start++;
        end--;
    }
}

我正在努力将其转化为快速:

func rotate(array:[Int], positions:Int, arSize:Int) {

    var a = array
    var p = positions
    var s = arSize

    reverseArray(array: a, start: 0, end: p-1)
    reverseArray(array: a, start: p, end: s-1)
    reverseArray(array: a, start: 0, end: s-1)
}

func reverseArray(array: [Int], start:Int, end:Int) {

    var a = array
    var s = start
    var e = end
    var temp = 0
    while s < e {
        temp = a[s]
        a[s] = a[e]
        a[e] = temp
        s += 1
        e -= 1
    }
} 

据我所知,对于swift,我们需要指定返回类型。 如何在不增加空间(内存)复杂性的情况下配置它们? (也就是说,没有创建新的临时数组)

这个问题与其他问题不同,因为它关于returns如何在快速工作中与C相比。

10 个答案:

答案 0 :(得分:5)

你可以扩展数组,你需要让你的方法变异。 BTW无需使用临时对象,可以使用Swift swap方法。另一个认为你应该确保参数中传递的索引在数组的有效范围内,为方法添加一个guard语句。试试这样:

extension Array {
    mutating func rotate(positions: Int, size: Int? = nil) {
        guard positions < count && (size ?? 0) <= count else {
            print("invalid input1")
            return
        }
        reversed(start: 0, end: positions - 1)
        reversed(start: positions, end: (size ?? count) - 1)
        reversed(start: 0, end: (size ?? count) - 1)
    }
    mutating func reversed(start: Int, end: Int) {
        guard start >= 0 && end < count && start < end else {
            return
        }
        var start = start
        var end = end
        while start < end, start != end {
            swap(&self[start], &self[end])
            start += 1
            end -= 1
        }
    }
}
var test = [1,2,3,4,5,6,7,8,9,10]
test.rotate(positions: 3)   // [4, 5, 6, 7, 8, 9, 10, 1, 2, 3]

答案 1 :(得分:4)

为什么在Swift标准库中已经有一个反向函数? 我的解决方案(源自Leo Dabus'):

extension Array {
    mutating func rotate(positions: Int, size: Int? = nil) {
        let size = size ?? count
        guard positions < count && size <= count else { return }

        self[0..<positions].reverse()
        self[positions..<size].reverse()
        self[0..<size].reverse()
    }
}

答案 2 :(得分:3)

我们可以使用Slice

func rotLeft(a: [Int], d: Int) -> [Int] {

var slice1 = a[..<d]
var slice2 = a[d...]
var array = Array(slice2) + Array(slice1)

return array

}

print(rotLeft(a:[1,2,3,4,5], d:4))

//prints [5,1,2,3,4]

答案 3 :(得分:1)

要完成,旋转功能应支持负(右)旋转并旋转超过数组的大小

extension Array 
{
    mutating func rotateLeft(by rotations:Int) 
    { 
       // rotation irrelevant when less than 2 elements
       if count < 2 { return }  

       // effective left rotation for negative and > count
       let rotations = (rotations%count + count) % count 

       // no use rotating by zero
       if rotations == 0 { return } 

       // rotate
       (1..<count).reduce(0)
       { let i = ($0.0+rotations)%count; swap(&self[$0.0],&self[i]); return i }
    }

    mutating func reverse()
    {
       (0..<count/2).forEach{ swap(&self[$0],&self[count-$0-1]) }
    }
}

答案 4 :(得分:0)

// a是要旋转的数组 // d是向左旋转的单位数

func rotLeft(a: [Int], d: Int) -> [Int] {
    var a = a
    for index in 0...(d - 1) {
       a.append(a[0])
       a.remove(at: 0)
     }
    return a
 }

//调用函数

rotLeft(a: [1,2,3,4,5], d: 4)

//输出 [5,1,2,3,4]

答案 5 :(得分:0)

此解决方案旋转时间复杂度为O(n)的元素

func rotLeft(a: [Int], d: Int) -> [Int] {
   var arr = a
   var size = arr.count - 1
   for i in 0...size  {
     let newloc = (i + (arr.count - d)) % arr.count
     arr[newloc] = a[i]
   }
   return arr
}

您不应该使用.append(x),因为在最坏的情况下 O(n),当您可以避免使用这些方法时,不应使用.remove(at: x)作为其O(n),因为使用它们时,您基本上会得到n + n + n,这并不是很好

答案 6 :(得分:0)

我们可以使用Array的dropFirst()和dropLast()函数来做到这一点。

func rotateLeft(arrToRotate: inout [Int], positions: Int){
  if arrToRotate.count == 0 || positions == 0 || positions > arrToRotate.count{
      print("invalid")
      return
  }
  arrToRotate = arrToRotate.dropFirst(positions) + arrToRotate.dropLast(arrToRotate.count-positions)
}

var numbers : [Int] = [1, 2, 3, 4, 5]
rotateLeft(arrToRotate: &numbers, positions:2)
print(numbers)  //prints [3, 4, 5, 1, 2]

答案 7 :(得分:0)

如果在观看了Embracing AlgorithmsDavid Abrahams WWDC18会话后有人落在这里,这就是从swift/test/Prototypes/Algorithms.swift文件开始轮播的实现之一。

extension MutableCollection where Self: BidirectionalCollection {
    /// Rotates the elements of the collection so that the element
    /// at `middle` ends up first.
    ///
    /// - Returns: The new index of the element that was first
    ///   pre-rotation.
    /// **- Complexity: O(*n*)**
    @discardableResult
    public mutating func rotate(shiftingToStart middle: Index) -> Index {
        self[..<middle].reverse()
        self[middle...].reverse()
        let (p, q) = _reverseUntil(middle)
        self[p..<q].reverse()
        return middle == p ? q : p
        }
    }

此算法取决于在同一文件中定义的 reverseUntil(:)

extension MutableCollection where Self: BidirectionalCollection {

/// Reverses the elements of the collection, moving from each end until
/// `limit` is reached from either direction. The returned indices are the
/// start and end of the range of unreversed elements.
///
///     Input:
///     [a b c d e f g h i j k l m n o p]
///             ^
///           limit
///     Output:
///     [p o n m e f g h i j k l d c b a]
///             ^               ^
///             f               l
///
/// - Postcondition: For returned indices `(f, l)`:
///   `f == limit || l == limit`
@inline(__always)
@discardableResult
internal mutating func _reverseUntil(_ limit: Index) -> (Index, Index) {
    var f = startIndex
    var l = endIndex
    while f != limit && l != limit {
        formIndex(before: &l)
        swapAt(f, l)
        formIndex(after: &f)
    }
    return (f, l)
}
}
    

答案 8 :(得分:0)

这是向左或向右旋转的方法。只需在数组上调用rotate,如图所示。这不会改变数组,如果要改变数组,请将数组设置为等于旋转。

extension Array {
    func rotate(moveRight: Bool, numOfRotations: Int) -> Array<Element>{
        var arr = self
        var i = 0
        while i < numOfRotations {
            if moveRight {
                arr.insert(arr.remove(at: arr.count - 1), at: 0)
            } else {
                arr.append(arr.remove(at: 0))
            }
            i += 1
        }
        return arr
    }
}

var arr = ["a","b","c","d","e"]

print(arr.rotate(moveRight: true, numOfRotations: 2))
// ["d", "e", "a", "b", "c"]
print(arr)
// ["a", "b", "c", "d", "e"]
arr = arr.rotate(moveRight: true, numOfRotations: 2)
print(arr)
// ["d", "e", "a", "b", "c"]

答案 9 :(得分:0)

您需要考虑以下情况-

旋转数可以等于/大于您需要旋转的数组大小。

要处理这种情况,请使用模运算符查找实际的转数,因为您会发现将数组旋转等于其大小的数字会导致在同一数组中旋转。

public class Duplicates {

    public static void main(String[] args) {

        List<Blog> blogs = new ArrayList<>();
        blogs.add(new Blog("a", "a", "a", "a"));
        blogs.add(new Blog("b", "b", "b", "b"));
        blogs.add(new Blog("a", "a", "a", "a"));    // duplicate
        blogs.add(new Blog("a", "a", "b", "b"));
        blogs.add(new Blog("a", "b", "b", "b"));
        blogs.add(new Blog("a", "a", "b", "b"));    // duplicate

        List<Blog> blogsWithoutDuplicates = getListWithoutDuplicates(blogs, 
                Blog::getTitle, Blog::getAuthor, Blog::getUrl, Blog::getDescription);
        System.out.println(blogsWithoutDuplicates); // [a a a a, b b b b, a a b b, a b b b]
        
        removeDuplicatesFromList(blogs, 
                Blog::getTitle, Blog::getAuthor, Blog::getUrl, Blog::getDescription);
        System.out.println(blogs);                  // [a a a a, b b b b, a a b b, a b b b]
    }

    private static class Blog {
        private String title;
        private String author;
        private String url;
        private String description;

        public Blog(String title, String author, String url, String description) {
            this.title = title;
            this.author = author;
            this.url = url;
            this.description = description;
        }

        public String getTitle() {
            return title;
        }

        public String getAuthor() {
            return author;
        }

        public String getUrl() {
            return url;
        }

        public String getDescription() {
            return description;
        }

        @Override
        public String toString() {
            return String.join(" ", title, author, url, description);
        }
    }
}