【问题标题】:Rotate Array in Swift在 Swift 中旋转数组
【发布时间】:2023-03-18 16:04:01
【问题描述】:

在 Swift 中探索算法时,如果不使用 funcs 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--;
    }
}

我正在努力将其转换为 swift:

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 相比如何在 swift 中工作。

【问题讨论】:

  • 你的意思是要创建一个变异方法?
  • 只是一个数组旋转的方法。
  • 现在我明白mutating 的真正含义了。

标签: swift algorithm


【解决方案1】:

编辑更新:

Swift 5 或更高版本

extension RangeReplaceableCollection {
    func rotatingLeft(positions: Int) -> SubSequence {
        let index = self.index(startIndex, offsetBy: positions, limitedBy: endIndex) ?? endIndex
        return self[index...] + self[..<index]
    }
    mutating func rotateLeft(positions: Int) {
        let index = self.index(startIndex, offsetBy: positions, limitedBy: endIndex) ?? endIndex
        let slice = self[..<index]
        removeSubrange(..<index)
        insert(contentsOf: slice, at: endIndex)
    }
}

extension RangeReplaceableCollection {
    func rotatingRight(positions: Int) -> SubSequence {
        let index = self.index(endIndex, offsetBy: -positions, limitedBy: startIndex) ?? startIndex
        return self[index...] + self[..<index]
    }
    mutating func rotateRight(positions: Int) {
        let index = self.index(endIndex, offsetBy: -positions, limitedBy: startIndex) ?? startIndex
        let slice = self[index...]
        removeSubrange(index...)
        insert(contentsOf: slice, at: startIndex)
    }
}

var test = [1,2,3,4,5,6,7,8,9,10]
test.rotateLeft(positions: 3)   // [4, 5, 6, 7, 8, 9, 10, 1, 2, 3]

var test2 = "1234567890"
test2.rotateRight(positions: 3)   // "8901234567"

【讨论】:

  • 什么是(大小??计数)?那是“非此即彼”运算符吗?
  • 它是 nil 合并运算符,如果您不传递大小,它将传递数组计数。此外,您应该确保传递的大小不大于数组计数
  • 如果参数“positions”超过数组大小怎么办?
  • 不错的解决方案,占用 O(1) 空间,对吧?如果removeSubrange() 可以接受Int 参数那就更好了。因为从index(endIndex, offsetBy: -positions, limitedBy: startIndex) 获取索引对我来说看起来很奇怪。
  • @ChuckZHB 这是通用的,允许您使用任何类型的集合(如字符串)。您还可以添加另一个方法并将索引限制为 Int。 extension RangeReplaceableCollection where Index == Int {mutating func rotateLeft(positions: Int) {let index = Swift.min(positions, endIndex)let slice = self[..&lt;index]removeSubrange(..&lt;index)insert(contentsOf: slice, at: endIndex)}}
【解决方案2】:

我们可以使用切片

func rotLeft(a: [Int], d: Int) -> [Int] {
    let slice1 = a[..<d]
    let slice2 = a[d...]
    return Array(slice2) + Array(slice1)
}

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

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

【讨论】:

  • 如果d的值超过了count怎么办?此解决方案无法处理所有场景。
【解决方案3】:

既然我们在 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()
    }
}

【讨论】:

    【解决方案4】:

    为了完整,旋转函数应该支持负(右)旋转和旋转超过数组的大小

    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]) }
        }
    }
    

    【讨论】:

      【解决方案5】:

      // 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]

      【讨论】:

      • 这是我解决问题陈述的第一个答案。但是如果您尝试提交代码,它会在hackerrank 上超时
      【解决方案6】:

      这个解旋转时间复杂度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 这不是那么好

      【讨论】:

      • newloc 使用这个表达式可以是负数,你会在尝试访问这个索引时得到一个异常。
      【解决方案7】:

      如果有人在观看David AbrahamsEmbracing Algorithms 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】:

        你需要考虑这样的场景——

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

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

            func rotateLeft(array:[Int],numberOfRotation:Int) -> [Int]
            {
             let offset = numberOfRotation % array.count
             let tempResult = array[offset...] + array[..<offset]
             return Array(tempResult)
            }
        

        【讨论】:

          【解决方案9】:

          我们可以使用 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]
          

          【讨论】:

            【解决方案10】:

            这是一种向左或向右旋转的方法。如图所示,只需在您的阵列上调用旋转即可。这不会改变数组,如果你想改变数组,请将数组设置为等于旋转。

            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"]
            
            

            【讨论】:

              【解决方案11】:

              方法一:

              func rotate(_ nums: inout [Int], _ k: Int) {
                  nums.enumerated().forEach { nums[ (k + $0)  % nums.count] = $1 }
              }
              

              方法二:

              func rotLeft(a: [Int], d: Int) -> [Int] {
                  var a = a
                  
                  reverse(&a, 0, d)
                  reverse(&a, d, a.count)
                  reverse(&a, 0, a.count)
                  return a
              }
              
              func reverse(_ a: inout [Int], _ s: Int, _ r: Int) {
                  var r = r, s = s
                  
                  while s < r {
                      a.swapAt(s, r - 1)
                      s += 1
                      r -= 1
                  }
              }
              

              【讨论】:

                猜你喜欢
                • 2015-07-17
                • 1970-01-01
                • 2019-12-24
                • 2015-05-12
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2022-01-04
                • 1970-01-01
                相关资源
                最近更新 更多