Two Pointers
- Array Elements In-Placed Removal
Let’s take a look at a easy problem on Leetcode 27. Remove Element. We will demonstrate how to remove an element from an array without allocating extra space for another array.
1 | func removeElement(nums []int, val int) int { |
Golang Playbook: https://play.golang.org/p/bhOG7VWIE0-
- Squares of a Sorted Array
Here is another problem on Leetcode 977. Squares of a Sorted Array. The straight forward solution will be calculate the squares of the given array with an O(n) loop and then use fast sort to get a result O(
1 | func sortedSquares(nums []int) []int { |
Golang Playbook: https://play.golang.org/p/h_rFxkg42a4
- Sliding window
Generally speaking, a sliding window is a sub-list than runs over an underlying collection. This technique shows how a nested for loop in some array related problems can be converted to a single for loop to reduce the time complexity. Here is a Leetcode problem 209. Minimum Size Subarray Sum. We can easily solve it with two nested loops with O(n2) time complexity. Let’s see how we can optimize it with two pointers sliding window.
1 | func minSubArrayLen(target int, nums []int) int { |
Golang Playbook: https://play.golang.org/p/EjMpnZJvBaW
Since all elements will be visited by the sliding window at most twice (entering or exiting), so the time complexity is O(n) instead of O(n2)
- Reverse a Linked List
It’s easy to use an additional Linked List to reverse a Linked List. But we can use two pointers to reduce the space complexity down to O(1). Let’s check a LeetCode problem 206. Reverse Linked List
1 | /** |
We can also solve it with a recursive solution
1 | func helper(pre, node *ListNode) *ListNode { |
- Remove the last Nth node from a Linked List
The two pointers method can also help us to remove the last Nth node from a Linked List within one iteration. Let’s check LeetCode problem 19. Remove Nth Node From End of List. Another tip for Linked List node removal, with the help of a dummy node pointing to the head node, it will simplify the process.
1 | func removeNthFromEnd(head *ListNode, n int) *ListNode { |
- Swap Nodes in Pairs
24. Swap Nodes in Pairs is a similar problem similar to #4 and #5 above. We can quickly solve it with dummy node and two pointers.
1 | func swapPairs(head *ListNode) *ListNode { |
Tip:
We can come up with a template here, whenever the solution expects a *ListNode
as return value, we should consider using dummy node pointing to the head node.
- Intersection of Two Linked Lists
160. Intersection of Two Linked Lists The key point to solve this problem is to understand the meaning of the intersected Linked List. Based on the examples and description, after the intersected nodes, the two Linked Lists share the same tail nodes. So we just need to find the shorter one’s length N and compare the last N nodes of the given two Linked List.
1 | func getIntersectionNode(headA, headB *ListNode) *ListNode { |
- Detect Circles I
A classical usage of two pointers is to detect if a Linked List contains a circle. We can have a faster and a slower pointer. If there’s a circle, the faster pointer should meet the slower one before it reaches to the end of the Linked List. The faster pointer’s moving speed can be set to two times of the slower one’s. 141. Linked List Cycle
1 | func hasCycle(head *ListNode) bool { |
Tip:
If we make the faster pointer pointing to the head node, the slower pointer pointing to the next node of the head node, and the speed of faster one is 2 times of the slower one. They will meet when we make the first move.
- Detect Circle’s Starting Node
Continue to the circle detection problem, how to find the starting node of the circle? Let’s check 142. Linked List Cycle II. Given the starting node of the circle A, let’s define below variables:
- x : the distance between the head node and A
- y : the distance between A and the met node P
- z : the distance between the met node P and A
Since faster node’s speed is two times of the slower one. We can get (x + y) * 2 = n * (y + z) + (x + y)
=> x = n * (y + z) - y
=> x = (n - 1) * (y + z) + z
. It means after those two pointers meet at P, if the slower pointer continues moving to A, the moved distance will be the same length if we move a new pointer from head to A. More information you can get from (https://github.com/youngyangyang04/leetcode-master/blob/master/problems/0142.%E7%8E%AF%E5%BD%A2%E9%93%BE%E8%A1%A8II.md)
1 | func detectCycle(head *ListNode) *ListNode { |
- 3 Sum
For LeetCode questtion 15. 3Sum, the brute-force solution’s time complexity is at least O(n^3). We can convert it to a two sum problem in side of a loop. We can compare the adjacent elements to remove the duplicated solution.
1 | import "sort" |
- 4 Sum
Similar to 3 Sum, we can solve 18. 4Sum with two pointers.
1 | import "sort" |
We can reverse a string with O(1) extra space by using two pointers method.
1 | func reverseString(s []byte) { |
Note, since the string in Go is immutable, if we need to manipulate a string’s contenet, we need to convert it to a byte slice first.
1 | func reverseStr(s string, k int) string { |
Besides the brute-force solution, we can solve it with O(1) space if the string is mutable.
1 | func replaceSpace(s string) string { |
15.151. Reverse Words in a String
A quickly solution will be we split the string with regexp to a list, then construct a new string by traversing the list from the end to the beginning. But it will take O(n) space. Let’s solve it with a in-place method (assume the string is mutable).
a. Use two pointers (fast/slow) to trim the spaces at the beginning, in this step, the fast pointer will stop at the first non-space character.
b. Start shifting the slice to left if the fast pointer and its previous element are space. So the slow and the fast pointers both moving, and the slower one will stop at the last non-space character or the only space left in the new string (if there are more than one adjacent spaces at the end of the original string)
c. Remove the spaces at the end
d. Reverse the whole new string
e. Reverse the words one by one
1 | func reverseWords(s string) string { |
- (Swap string’s left and right sides)[https://leetcode-cn.com/problems/zuo-xuan-zhuan-zi-fu-chuan-lcof/]
We can simply reconstruct the string with O(n) space. but if we want to use O(1) space if the string is mutable, two pointers reverse can help us to make it done.
a. Reverse the first n elements
b. Reverse the elements after n
c. Reverse the whole slice
1 | func reverseLeftWords(s string, n int) string { |
1 | func removeDuplicates(s string) string { |