Anda di halaman 1dari 5

TECHPAROKSHA

The Two Pointer Algorithm


Prateek Gupta

Introduction

The legendary two pointer approach is one such technique which is less of a talk and more
of a discussion on the problems. Binary search is a kind of optimization on the number of
trials taken to reach the optimal position and so is the two pointer technique. The
approach relies on the sequence following one specific property on which our pointers
can move.

Lets try to learn how we apply two pointers and what advantages does they offer us. We
will be discussing a lot of problems that may help you to understand the technique better
and for that, it is highly recommendable that you sit with a pen and a paper.

Motivation Problem: Given a sorted array A, having N integers. You need to find any
pair(i, j) having sum as given number X .

Constraints: Array A contains about 105 integers with each having values around 109 .

Solution: No doubt, we always have an option of iterating all the array values and finding
their corresponding integer Aj having value as X Ai using binary search. But, lets see
how does the two pointer uses the fact that the array is sorted. For this, we keep two
pointers, one at the starting tip of the array and another at the tail. Moving on, we now
sum up the values contained at both the pointers. In case, value is greater than required
sum, we need to shift the right pointer to the left in order to decrease the value by
whatever we can since we don't have any other choice. Similarly, if we encounter the sum
of values less than required sum, that we can shift the left pointer, one unit to the right to
bring us more closer to required sum. We keep moving the pointers unless we encounter
the situation where Al + Ar reaches the required given sum as X .

C++ implementation of above approach

1 #define lli long long


2
3 bool f(lli sum) {
4 int l = 0, r = n - 1; //two pointers
5 while ( l < r ) {
6 if ( A[l] + A[r] == sum ) return 1;
7 else if ( A[l] + A[r] > sum ) r--;
8 else l++;
9 }
10 return 0;
11 }

Overall complexity of the above solution is O(N) .


Motivation Problem: Given two sorted arrays A and B, each having length N and M
respectively. Form a new sorted merged array having values of both the arrays in sorted
format.

Constraints: Array A and B contains about 105 integers, each having value around 109 .

Solution: Since the two arrays are given in sorted order, we can surely do something with
two pointers. Let us go step by step.

1
. We will introduce read-indices l1 , l2 to traverse arrays A and B, respectively.
and another write-index cnt to store position of the first free cell in the resulting
array. Initially all l1, l2 and cnt will be 0

2. Moving on, if both indices are in range (l1 < N and l2 < M ), choose minimum
of (Ai , Bj ) and write it to C[cnt], and increase the respective pointer.

C++ implementation of the above approach

1 #define MAX 100005


2
3 lli C[2*MAX];
4
5 void merge(lli A[], lli B[])
6 {
7 int l1 = 0, l2 = 0, cnt = 0;
8 while ( l1 < n || l2 < m ) {
9 if ( l1 < n && l2 < m ) {
10 if ( A[l1] < B[l2] ) {
11 C[cnt++] = A[l1];
12 l1++;
13 }
14 else if ( A[l1] > B[l2] ) {
15 C[cnt++] = B[l2];
16 l2++;
17 }
18 }
19 else if ( l1 < n ) {
20 C[cnt++] = A[l1];
21 l1++;
22 }
23 else if ( l2 < m ) {
24 C[cnt++] = B[l2];
25 l2++;
26 }
27 }
28 return;
29 }

We basically traversed both the arrays in the sorted order of combined elements and
hence kept inserting the elements into the new array that we needed.

Overall complexity of the solution is O(N + M).

Move on to the next problem if you are sure, you understood the above problems right and
have implemented them yourself. I would like you to think as much as you can in all the
problems and question yourself, Why?. Thinking is a healthy process and good for
exercising your brain to understand the concepts a lot better.

Motivation Problem: Given an array having N integers, find the contiguous subarray
having sum as great as possible, but not greater than M . For details on the statement,
refer the problem link here

Constraints: Array can have atmost 105 elements and each number will be non-negative
and can be as big as 109 .
Solution: As the given array contains positive elements, cumulative sum will indeed be
increasing as you go on from left to right in the array. If you have already read the binary
search tutorial, I am pretty sure you must have found out a way to solve it already.

Solution using binary search:

Store cumulative sum at each index in a separate auxiliary array.

Treat each index as startIndex of the required contiguous subarray, find a


corresponding endIndex such that the following equation holds true.

cum[endIndex] - cum[startIndex-1] <= M and cum[endIndex+1] - cum


[startIndex-1] > M

I leave the implementation for this on the part of the reader to discuss in comments. Now,
let us see how can we solve the problem using the two pointers technique.

We introduce two pointers l, r denoting startIndex and endIndex of our


contiguous subarray, with both of them at the tip of the array.

We now start extending our right pointer r provided sum[l, r] <= M Once, we
reach at such a stage, we don't have any option but to move the left pointer as
well and start decreasing the sum until we arrive at the situation where we can
extend our right pointer again.

As and when we reach the point, where we need to shift the left pointer, we keep
updating our maximum sum we have achieved so far.

C++ implementation of the above approach

1 #include <bits/stdc++.h>
2 #define lli long long
3 #define MAX 1000005
4
5 using namespace std;
6
7 lli A[MAX];
8
9 int main()
10 {
11 int n;
12 lli sum = 0;
13 cin >> n;
14
15 for ( int i = 0; i < n; i++ ) cin >> A[i];
16
17 int l = 0, r = 0;
18 lli ans = 0;
19
20 while ( l < n ) {
21 while ( r < n && sum + A[r] <= M ) {
22 sum += A[r];
23 r++;
24 }
25 ans = max(ans, sum);
26 sum -= A[l];
27 l++;
28 }
29
30 cout << ans << endl;
31 return 0;
32 }

That was a lot of fun, learning one topic in such a less amount of time. Excited still!? Lets see
how much more you can grasp. Moving on to the next problem...

Motivation Problem: Given an array containing N integers, you need to find the length
of the smallest contiguous subarray that contains atleast K distinct elements in it. Output
"1 " if no such subarray exists.
Constraints: Number of elements in the array are around one million with each of them
having value as large as 109 .

Solution: Now, thats where the legendary two pointer technique is the only way to your
rescue. If you are reading this, I am pretty much sure that you have read the tutorial on
STL containers. The STL container you need to know about here is set.

Going by our own traditional way as we approached in the previous problem, we take two
pointers l, r with both at the tip of the array. We keep on shifting the right pointer unless
we have K elements into the set as set will only contains distinct elements and will ignore
the insertion of duplicate elements. As soon as we meet our condition, we now shift the
left pointer unless the size of the set becomes < K . We also update the length of our
minimum contiguous subarray as soon as we are ready to shift the left pointer.

C++ implementation of above approach

1 int l = 0, r = 0, ans = INF;


2 map <int , int > cnt;
3
4 while ( l < n ) {
5 while ( r < n && s.size() < K ) {
6 s.insert(A[r]);
7 cnt[A[r]]++;
8 r++;
9 }
10 if (s.size() >=K) {
11 ans = min(ans, r-l);
12 }
13 if ( cnt[A[l]] == 1 ) s.erase(A[l]);
14 cnt[A[l]]--;
15 l++;
16 }
17 if ( ans == INF ) ans = -1;
18
19 cout << ans << endl;

Great, that was just kind of similar to previous problem. Lets move on to the final problem
and you shall be ready to explore.

Motivation Problem: Given an array having N integers, you need to find out a
subsequence of K integers such that these K integers have the minimum hustle. Hustle
of a sequence is defined as sum of pair-wise absolute differences divided by the number of
pairs. For details on the statement, refer the problem link here

Constraints: Both N and K are less than or equal to 105 and each element has absolute
value of around 109 .

Solution: Since, this will be the last problem, let us try to spend some more time
discussing the solution in detail.

You really don't want this to be the last problem of the discussion, don't you?

Anyway, lets see what the problem demands this time. The number of pairs in the
function wont be of much significance since we will always be dividing by fixed number
of C(K, 2) pairs. So, we can ignore the denominator here and the way function "hustle" is
defined surely tells us that the numerator of the function will be minimum as much as
those K integers will be close to each other. Since, hustle function is nothing but merely
summation of absolute difference of pairs. Why not just sort the numbers and consider
each consecutive contiguous subarray of length K for this? That's it. You are done for
good !

If you still doubt how we have reduced the problem from subsequence of length K to
contiguous substring of length K , try to contradict yourself by removing one element
from contiguous K elements and taking some other element, you will surely realize, what
wrong happened their and how much the absolute difference increased overall. Try to
prove this way to give yourself complete satisfaction about the greedy technique used
over here.

C++ implementation of above approach.

1 #include <bits/stdc++.h>
2 using namespace std;
3
4 #define lli long long
5 #define MAX 1000006
6
7 lli A[MAX],C[MAX];
8
9 int main()
10 {
11 int l = 1, r = 2, st,en,n;
12
13 lli sum,ans;
14
15 cin >> n >> k;
16
17 for ( int i = 1; i <= n; i++ ) cin >> A[i];
18
19 sort(A+1, A+n+1);
20
21 cum[0] = 0;
22 for ( int i = 1; i <= n; i++ ) cum[i] = cum[i-1] + A[i];
23
24 while ( r <= k ) {
25 sum += (A[r]*(r-l) - (cum[r-1] - cum[l-1]));
26 r++;
27 }
28
29 st = 1, en = k, ans = sum;
30
31 while ( r <= n ) {
32 sum -= (cum[r-1] - cum[l] - A[l]*(r-l-1));
33 l++;
34 sum += (A[r]*(r-l) - (cum[r-1] - cum[l-1]));
35
36 if ( ans > sum ) {
37 ans = sum;
38 st = l;
39 en = r;
40 }
41 r++;
42 }
43 return 0;
44 }

Overall complexity of the solution is O(NlogN) + O(N)

Recommended

Visit this link and start solving problems from level 1 onwards.

Read the comments section of this blog at codeforces.com .

I hope you now have a fair idea of the two pointers technique. It is really very handy in the
contests if you master it well enough and for that I believe, you need to solve a lot of
problems as mentioned above. Good Luck !
17,617 views 244 upvotes Written Dec 31, 2015

Anda mungkin juga menyukai