|
| 1 | +# Reverse Part of a Linked List via Recusion |
| 2 | + |
| 3 | +It's easy to reverse a single linked list using iteration, however it's kind of difficult to come up with a recursive solution. Furthermore, if only part of a linked list needs reversed, can you nail it with **recursion**? |
| 4 | + |
| 5 | +If you haven't known how to **recursively reverse a single linked list**, no worry, we will start right here and guide you step by step to a deeper level. |
| 6 | + |
| 7 | +```java |
| 8 | +// node structure for a single linked list |
| 9 | +public class ListNode { |
| 10 | + int val; |
| 11 | + ListNode next; |
| 12 | + ListNode(int x) { val = x; } |
| 13 | +} |
| 14 | +``` |
| 15 | +<br> |
| 16 | + |
| 17 | +To reverse part of a linked list means we only reverse elements in a specific interval and leave others untouched. |
| 18 | + |
| 19 | + |
| 20 | + |
| 21 | +Note: **Index starts from 1**. Two loops needed if solve via iteration: use one for-loop to find the mth element, and then use another for-loop to reverse elements between m and n. While in recursive solution, no loop at all. |
| 22 | + |
| 23 | +Though iterative solution looks simple, you have to be careful with the details. On the contrary, recursive solution is quite elegant. Let's start reversing a whole single linked list in the recursive way. |
| 24 | + |
| 25 | +### 1. Recursively reverse a whole single Linked List |
| 26 | + |
| 27 | +You may have already known the solution below. |
| 28 | + |
| 29 | +```java |
| 30 | +ListNode reverse(ListNode head) { |
| 31 | + if (head.next == null) return head; |
| 32 | + ListNode last = reverse(head.next); |
| 33 | + head.next.next = head; |
| 34 | + head.next = null; |
| 35 | + return last; |
| 36 | +} |
| 37 | +``` |
| 38 | +Do you feel lost in trying to understand code above? Well, you are not the only one. This algorithm is often used to show how clever and elegant recursion can be. Let's dig into the code together. |
| 39 | + |
| 40 | +For recursion, **the most important thing is to clarify the definition of the recursive function**. Specifically, we define `reverse` as follows: |
| 41 | + |
| 42 | +**Input a node `head`, we will reverse the list starting from `head`, and return the new head node.** |
| 43 | + |
| 44 | +After clarifying the definition, we look back at the problem. For example, we want to reverse the list below: |
| 45 | + |
| 46 | + |
| 47 | + |
| 48 | +So after calling `reverse(head)`, recursion happens: |
| 49 | + |
| 50 | +```java |
| 51 | +ListNode last = reverse(head.next); |
| 52 | +``` |
| 53 | +Did you just step into the messy details in recursion? Oops, it's a wrong way, step back now! Focus on the recursion definition (which tells you what it does) to understand how recursive code works the wonder. |
| 54 | + |
| 55 | + |
| 56 | + |
| 57 | +After executing `reverse(head.next)`, the whole linked list becomes this: |
| 58 | + |
| 59 | + |
| 60 | + |
| 61 | +According to the definition of the recursive function, `reverse` needs to return the new head node, so we use variable `last` to mark it. |
| 62 | + |
| 63 | +Let's continue cracking the next piece of code: |
| 64 | + |
| 65 | +```java |
| 66 | +head.next.next = head; |
| 67 | +``` |
| 68 | + |
| 69 | + |
| 70 | + |
| 71 | +Last work to do: |
| 72 | + |
| 73 | +```java |
| 74 | +head.next = null; |
| 75 | +return last; |
| 76 | +``` |
| 77 | + |
| 78 | + |
| 79 | + |
| 80 | +The whole linked list is successfully reversed now. Amazing, isn't it? |
| 81 | + |
| 82 | +Last but not the least, there are two things in recursion you need to pay attention to: |
| 83 | + |
| 84 | +1. Recursion needs a base case. |
| 85 | + |
| 86 | + ```java |
| 87 | + if(head.next == null) return head; |
| 88 | + ``` |
| 89 | + |
| 90 | + which means when there is only one node, after reversion, the head is still itself. |
| 91 | +2. After reversion, the new head is `last`, and the former `head` becomes the last node, don't forget to point its tail to null. |
| 92 | + |
| 93 | + ```java |
| 94 | + head.next = null; |
| 95 | + ``` |
| 96 | + |
| 97 | +After understanding above, now we can proceed further, the problem below is actually an extend to the above solution. |
| 98 | + |
| 99 | +### 2. Reverse first N nodes |
| 100 | + |
| 101 | +This time we will implement a funtion below: |
| 102 | + |
| 103 | +```java |
| 104 | +// reverse first n nodes in a linked list (n <= length of the list) |
| 105 | +ListNode reverseN(ListNode head, int n) |
| 106 | +``` |
| 107 | +Take below as an example, call `reverseN(head, 3)`: |
| 108 | + |
| 109 | + |
| 110 | + |
| 111 | +The idea is similar to reversing the whole linked list, only a few modifications needed: |
| 112 | + |
| 113 | +```java |
| 114 | +ListNode successor = null; // successor node |
| 115 | + |
| 116 | +// reverse n nodes starting from head, and return new head |
| 117 | +ListNode reverseN(ListNode head, int n) { |
| 118 | + if (n == 1) { |
| 119 | + // mark the (n + 1)th node |
| 120 | + successor = head.next; |
| 121 | + return head; |
| 122 | + } |
| 123 | + // starts from head.next, revers the first n - 1 nodes |
| 124 | + ListNode last = reverseN(head.next, n - 1); |
| 125 | + |
| 126 | + head.next.next = head; |
| 127 | + // link the new head to successor |
| 128 | + head.next = successor; |
| 129 | + return last; |
| 130 | +} |
| 131 | +``` |
| 132 | + |
| 133 | +Main differences: |
| 134 | + |
| 135 | +1. Base case `n == 1`, if reverse only one element, then new head is itself, meanwhile **remember to mark the successor node**. |
| 136 | +2. In previouse solution, we set `head.next` directly to null, because after reversing the whole list, head becoms the last node. But now `head` may not be the last node after reversion, so we need mark `successor` (the (n+1)th node), and link it to `head` after reversion. |
| 137 | + |
| 138 | + |
| 139 | + |
| 140 | +OK, now we are pretty close to reversing part of the linked list. |
| 141 | + |
| 142 | +### 3. Reverse part of a linked list |
| 143 | + |
| 144 | +Given an interval `[m,n]` (index starts from 1), only reverse elements in this section. |
| 145 | + |
| 146 | +```java |
| 147 | +ListNode reverseBetween(ListNode head, int m, int n) |
| 148 | +``` |
| 149 | + |
| 150 | +First, if `m == 1`, it is equal to reversing the first `n` elements as we discussed just now. |
| 151 | + |
| 152 | + |
| 153 | +```java |
| 154 | +ListNode reverseBetween(ListNode head, int m, int n) { |
| 155 | + // base case |
| 156 | + if (m == 1) { |
| 157 | + // equals to reversing the first n nodes |
| 158 | + return reverseN(head, n); |
| 159 | + } |
| 160 | + // ... |
| 161 | +} |
| 162 | +``` |
| 163 | +What if `m != 1`? If we take the index of the `head` as 1, then we need to reverse from the `mth` element. And what if we take the index of the `head.next` as 1? Then compared to `head.next`, the reverse section should start from `(m-1)th` element. And what about `head.next.next` ... |
| 164 | + |
| 165 | +Different from iteration, this is how we think in the recursive way, so our code should be: |
| 166 | + |
| 167 | +```java |
| 168 | +ListNode reverseBetween(ListNode head, int m, int n) { |
| 169 | + // base case |
| 170 | + if (m == 1) { |
| 171 | + return reverseN(head, n); |
| 172 | + } |
| 173 | + head.next = reverseBetween(head.next, m - 1, n - 1); |
| 174 | + return head; |
| 175 | +} |
| 176 | +``` |
| 177 | +Finally, we solved the problem we have talked about at the very beginning, happy ending! |
| 178 | + |
| 179 | +### 4. Summary |
| 180 | + |
| 181 | +Compared to iteration, it is a little bit difficult to understand recursion, the tricks are: never bury yourself in the details, just focus on its clear definition, thus to gain a quick understanding of how it works and what it outputs. |
| 182 | + |
| 183 | +For time complexity, iteration is O(1) while recursion is always O(N). For space complexity, iteration needs O(N) while recursion needs stack. Overall, iteration has a better performance. Solutions in this article provides you a good way to learn recursion. |
| 184 | + |
| 185 | +**Mission**: Stick to original high quality articles, and make algorithms easy to understand. Welcome to subscribe my Wechat public account `ID:labuladong` for latest articles. |
| 186 | + |
| 187 | + |
0 commit comments