Problem Statement
Given a singly linked list, rotate the list to the right by k
places, where k
is a non-negative integer.
Examples
Example 1:
Input: 1 -> 2 -> 3 -> 4 -> 5, k = 2
Output: 4 -> 5 -> 1 -> 2 -> 3
Explanation: Rotate the list to the right by 2 places. The last two nodes (4, 5) are moved to the front.
List after 1 shift to right: 5->1->2->3->4
List after 2 shift to right: 4->5->1->2->3
Example 2:
Input: 0 -> 1 -> 2, k = 4
Output: 2 -> 0 -> 1
Explanation: Rotate the list to the right by 4 places. Since k is greater than the length of the list, it effectively rotates by k % length (4 % 3 = 1).
Example 3:
Input: 1, k = 3
Output: 1
Explanation: The list has only one node, so rotating it by any number of places results in the same list.
Different Approaches
1️⃣ Brute Force Approach
Intuition:
For each k step, move the last element of a linked list to the front. This shifts all elements from one position to the right, with the last element wrapping around to become the first.
Approach:
- Move the last element to first for each k.
- For each k, find the last element from the list. Move it to the first.
Code:
#include <bits/stdc++.h>
using namespace std;
// Definition of singly linked list
struct ListNode {
int val;
ListNode *next;
ListNode() {
val = 0;
next = NULL;
}
ListNode(int data1) {
val = data1;
next = NULL;
}
ListNode(int data1, ListNode *next1) {
val = data1;
next = next1;
}
};
class Solution {
public:
// Function to rotate the list by k steps
ListNode* rotateRight(ListNode* head, int k) {
// Base case: if list is empty or has only one node
if (head == NULL || head->next == NULL) return head;
// Perform rotation k times
for (int i = 0; i < k; i++) {
ListNode* temp = head;
// Find the second last node
while (temp->next->next != NULL)
temp = temp->next;
// Get the last node
ListNode* end = temp->next;
// Break the link between
// second last and last node
temp->next = NULL;
// Make the last node
// as new head
end->next = head;
head = end;
}
return head;
}
};
// Utility function to insert node at the end of the list
void insertNode(ListNode* &head, int val) {
ListNode* newNode = new ListNode(val);
if (head == NULL) {
head = newNode;
return;
}
ListNode* temp = head;
while (temp->next != NULL) temp = temp->next;
temp->next = newNode;
}
// Utility function to print list
void printList(ListNode* head) {
while (head != NULL) {
cout << head->val;
if (head->next != NULL) cout << "->";
head = head->next;
}
cout << endl;
}
int main() {
ListNode* head = NULL;
// Inserting nodes
insertNode(head, 1);
insertNode(head, 2);
insertNode(head, 3);
insertNode(head, 4);
insertNode(head, 5);
cout << "Original list: ";
printList(head);
int k = 2;
Solution solution;
// Calling function for rotating right by k times
ListNode* newHead = solution.rotateRight(head, k);
cout << "After " << k << " iterations: ";
// List after rotating nodes
printList(newHead);
return 0;
}
Complexity Analysis:
- Time Complexity:
O(NxK)
because for K times, we are iterating through the entire list to get the last element and move it to the first position. Here, N represents the number of nodes in the linked list, and K is the number of steps by which the list has to be rotated. - Space Complexity:
O(1)
because no extra data structure is required for computation.
2️⃣ Optimal Approach
Intuition
To rotate a linked list to the right by k
places, you can think of it as moving the last k
nodes to the front. Here’s a step-by-step approach to solve the problem:
- Determine the Length of the List:
Calculate the length of the linked list. This helps in finding the actual number of rotations needed whenk
is larger than the length. - Calculate Effective Rotations:
Ifk
is greater than the length of the list (n
), rotatingn
times orn + n
times results in the same list. So, the effective number of rotations needed isk % n
. - Find the New End and New Head:
To perform the rotation:- Find the new end of the list, which is the
(n - k)
-th node from the beginning. - The node right after this node will be the new head of the rotated list.
- Find the new end of the list, which is the
- Rotate the List:
Break the link between the new end and the new head, and connect the original last node to the original head to complete the rotation.
Code:
#include <iostream>
struct ListNode {
int val;
ListNode* next;
ListNode(int x) : val(x), next(nullptr) {}
};
ListNode* rotateRight(ListNode* head, int k) {
if (!head || k == 0) return head;
// Calculate the length of the linked list
int length = 1;
ListNode* tail = head;
while (tail->next) {
tail = tail->next;
length++;
}
// Calculate the effective rotations needed
k = k % length;
if (k == 0) return head; // No rotation needed
// Find the new end of the list
ListNode* new_end = head;
for (int i = 1; i < length - k; i++) {
new_end = new_end->next;
}
// The node after new_end will be the new head
ListNode* new_head = new_end->next;
// Rotate the list
new_end->next = nullptr;
tail->next = head;
return new_head;
}
// Utility function to print the linked list
void printList(ListNode* head) {
while (head) {
std::cout << head->val << " -> ";
head = head->next;
}
std::cout << "nullptr" << std::endl;
}
int main() {
// Example Usage
ListNode* head = new ListNode(1);
head->next = new ListNode(2);
head->next->next = new ListNode(3);
head->next->next->next = new ListNode(4);
head->next->next->next->next = new ListNode(5);
std::cout << "Original list: ";
printList(head); // Output: 1 -> 2 -> 3 -> 4 -> 5 -> nullptr
int k = 2;
head = rotateRight(head, k);
std::cout << "Rotated list by " << k << ": ";
printList(head); // Output: 4 -> 5 -> 1 -> 2 -> 3 -> nullptr
return 0;
}
Complexity Analysis
- Time Complexity:
O(N)
: The function traverses the linked list twice, once to calculate its length and again to find the new end and rotate the list.
- Space Complexity:
O(1)
: The function uses a constant amount of extra space for pointers.
Same Idea Another Implementation:
Intuition
For every k that is a multiple of the length of the list, the linked list returns to its original state after rotation. By using brute force with k as a multiple of the list's length, we notice this pattern. This insight suggests that for k greater than the length of the list, we only need to rotate the list by k % length of the list, thus optimizing our time complexity.
Approach:
- Calculate the length of the list.
- Connect the last node to the first node, converting it to a circular linked list.
- Iterate to cut the link of the last node and start a node of k%length of the list rotated list.
Code:
class Solution {
public:
ListNode* rotateRight(ListNode* head, int k) {
if (head == nullptr || head->next == nullptr || k == 0) {
return head; // Handle edge cases
}
// Step 1: Find the length of the list
int length = 1; // Initialize to 1 to account for the head node
ListNode* temp = head;
while (temp->next != nullptr) {
length++;
temp = temp->next;
}
// Step 2: Update k to avoid unnecessary rotations
k = k % length;
if (k == 0) {
return head; // No rotation needed
}
// Step 3: Connect the tail to the head to form a circular list
temp->next = head;
// Step 4: Find the new tail (length - k - 1 steps from the head)
int stepsToNewTail = length - k;
ListNode* newTail = head;
for (int i = 1; i < stepsToNewTail; i++) {
newTail = newTail->next;
}
// Step 5: Set the new head and break the circular connection
ListNode* newHead = newTail->next;
newTail->next = nullptr;
return newHead;
}
};
Complexity Analysis:
- Time Complexity:
O(n)
- One pass to find the length and another to adjust pointers.
- Space Complexity:
O(1)
- No additional space is used.