While exploring algorithms in Swift, couldn\'t find algorithm for array rotation in swift without using funcs shiftLeft
/ shiftRight
.
C has
This solution rotates the element of time complexity 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
}
you shouldn't use .append(x)
as in the worst case it can be
O(n) and you shouldn't use .remove(at: x)
as its O(n) when you can avoid using those methods As when using them you basically get n + n + n which isn't that great
If anybody lands here after watching the Embracing Algorithms WWDC18 session by David Abrahams, here is one of the implementations of rotate from the swift/test/Prototypes/Algorithms.swift file.
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
}
}
This algorithms depends on reverseUntil(:) defined in the same file
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)
}
}
here is a way to rotate left or right. Just call rotate on your array as shown. This does not mutate the array, if you wish to mutate the array, set the array equal to the rotation.
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"]
Edit update:
Swift 5 or later
extension RangeReplaceableCollection {
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 {
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"
To be complete, the rotation function should support negative (right) rotations and rotating more than the array's size
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]) }
}
}
// a is the array to be left rotated // d is the number of unit for left rotation
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
}
// calling Function
rotLeft(a: [1,2,3,4,5], d: 4)
// OutPut [5, 1, 2, 3, 4]