Rotate List

excerpt: This covers the building blocks such as left shift in array and using modulo operator for modulo arithmetic tags: left-shift-array-element modulo-operator two-pointers-moving-in-opposite-direction modulo-arithmetic swap

Write a function that rotates a list by k elements. For example [1,2,3,4,5,6] rotated by two becomes [3,4,5,6,1,2]. Try solving this without creating a copy of the list. How many swap or move operations do you need?

Given: [1,2,3,4,5,6]

One rotation:  [2,3,4,5,6,1]
Two rotations: [3,4,5,6,1,2]

Brute Force Implementation

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
def rotate(list, k)
  size = list.size - 1
  k.times do
    previous = list[0]  

    size.downto(0) do |j|
      list[j], previous = previous, list[j]
    end
  end
  
  list
end

list = [1,2,3,4,5,6] 
k = 2

p rotate(list, k)

If the value of k is greater than the size of the list, there is no point in shifting that many times as we need to shift it only the number of times that is less than the size of the list. We can use modulo operator to reduce the value of k so that it does not exceed the list size.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
def rotate(list, k)
  size = list.size - 1
  
	# Use modulo operator to minimize rotations
	k = k % size
	
  k.times do
    previous = list[0]  

    size.downto(0) do |j|
      list[j], previous = previous, list[j]
    end
  end
  
  list
end

If you want to shift the elements to the right:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
def rotate(list, k)
  k.times do
    previous = list[-1]  

    for j in (0..list.size - 1)
      list[j], previous = previous, list[j]
    end
  end
  
  list
end

list = [1,2,3,4,5,6] 
k = 2

p rotate(list, k)

Task Breakdown

Original List : 1 2 3 4 5 6 7 After reversing all numbers : 7 6 5 4 3 2 1 After reversing first k numbers : 5 6 7 4 3 2 1 After reversing last n-k numbers : 5 6 7 1 2 3 4 –> Result

1
2
3
4
5
6
7
def reverse(a, i, j)  
  while i < j
    a[i], a[j] = a[j], a[i]
    i += 1
    j -= 1
  end  
end
1
2
3
4
5
6
7
8
9
def rotate(nums, k)
  reverse(nums)
end

list = [1,2,3,4,5,6,7] 
k = 2

rotate(list, k)
p list

After reversing all numbers : 7 6 5 4 3 2 1

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
def rotate(nums, k)
  reverse(nums, 0, nums.size - 1)
  reverse(nums, 0, k-1)
end

list = [1,2,3,4,5,6,7] 
k = 3

rotate(list, k)
p list

After reversing first k numbers : 5 6 7 4 3 2 1

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
def rotate(nums, k)
  reverse(nums, 0, nums.size - 1)
  reverse(nums, 0, k-1)
  reverse(nums, nums.size-1-k, nums.size-1)
end

list = [1,2,3,4,5,6,7] 
k = 3

rotate(list, k)
p list

After reversing last n-k numbers : 5 6 7 1 2 3 4 –> Final Result

Working Code

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
def reverse(a, i, j)  
  while i < j
    a[i], a[j] = a[j], a[i]
    i += 1
    j -= 1
  end  
end

def rotate(nums, k)
  # Avoid unnecessary rotation
  k = k % nums.size
  reverse(nums, 0, nums.size - 1)
  reverse(nums, 0, k-1)
  
	# Fix bug in the index for this call:
  reverse(nums, k, nums.size-1)
end