sz = 1;
c[1] = array[0]; /*at this point, the minimum value of the last element of the size 1 increasing sequence must be array[0]*/
dp[0] = 1;
for( int i = 1; i < len; i++ ) {
if( array[i] < c[1] ) {
c[1] = array[i]; /*you have to update the minimum value right now*/
dp[i] = 1;
}
else if( array[i] > c[sz] ) {
c[sz+1] = array[i];
dp[i] = sz+1;
sz++;
}
else {
int k = binary_search( c, sz, array[i] ); /*you want to find k so that c[k-1]<array[i]<c[k]*/
c[k] = array[i];
dp[i] = k;
}
}
Now the improvement happens at the second loop, basically, you can improve the speed by using binary search. Besides the array dp[], let's have another array c[], c is pretty special, c[i] means: the minimum value of the last element of the longest increasing sequence whose length is i.
sz = 1;
c[1] = array[0]; /*at this point, the minimum value of the last element of the size 1 increasing sequence must be array[0]*/
dp[0] = 1;
for( int i = 1; i < len; i++ ) {
if( array[i] < c[1] ) {
c[1] = array[i]; /*you have to update the minimum value right now*/
dp[i] = 1;
}
else if( array[i] > c[sz] ) {
c[sz+1] = array[i];
dp[i] = sz+1;
sz++;
}
else {
int k = binary_search( c, sz, array[i] ); /*you want to find k so that c[k-1]<array[i]<c[k]*/
c[k] = array[i];
dp[i] = k;
}
}
To account for duplicates one could check, for example, if the number is already in the set. If it is, ignore the number, otherwise carry on using the same method as before. Alternatively, one could reverse the order of the operations: first remove, then insert. The code below implements this behaviour:
The second algorithm could be extended to find the longest increasing subsequence(LIS) itself by maintaining a parent array which contains the position of the previous element of the LIS in the original array.
We need to maintain lists of increasing sequences.
In general, we have set of active lists of varying length. We are adding an element A[i] to these lists. We scan the lists (for end elements) in decreasing order of their length. We will verify the end elements of all the lists to find a list whose end element is smaller than A[i] (floor value).
Our strategy determined by the following conditions, 1. If A[i] is smallest among all end candidates of active lists, we will start new active list of length 1. 2. If A[i] is largest among all end candidates of active lists, we will clone the largest active list, and extend it by A[i]. 3. If A[i] is in between, we will find a list with largest end element that is smaller than A[i]. Clone and extend this list by A[i]. We will discard all other lists of same length as that of this modified list.
Note that at any instance during our construction of active lists, the following condition is maintained.
“end element of smaller list is smaller than end elements of larger lists”.
It will be clear with an example, let us take example from wiki : {0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15}.
A[0] = 0. Case 1. There are no active lists, create one. 0. ----------------------------------------------------------------------------- A[1] = 8. Case 2. Clone and extend. 0. 0, 8. ----------------------------------------------------------------------------- A[2] = 4. Case 3. Clone, extend and discard. 0. 0, 4. 0, 8. Discarded ----------------------------------------------------------------------------- A[3] = 12. Case 2. Clone and extend. 0. 0, 4. 0, 4, 12. ----------------------------------------------------------------------------- A[4] = 2. Case 3. Clone, extend and discard. 0. 0, 2. 0, 4. Discarded. 0, 4, 12. ----------------------------------------------------------------------------- A[5] = 10. Case 3. Clone, extend and discard. 0. 0, 2. 0, 2, 10. 0, 4, 12. Discarded. ----------------------------------------------------------------------------- A[6] = 6. Case 3. Clone, extend and discard. 0. 0, 2. 0, 2, 6. 0, 2, 10. Discarded. ----------------------------------------------------------------------------- A[7] = 14. Case 2. Clone and extend. 0. 0, 2. 0, 2, 6. 0, 2, 6, 14. ----------------------------------------------------------------------------- A[8] = 1. Case 3. Clone, extend and discard. 0. 0, 1. 0, 2. Discarded. 0, 2, 6. 0, 2, 6, 14. ----------------------------------------------------------------------------- A[9] = 9. Case 3. Clone, extend and discard. 0. 0, 1. 0, 2, 6. 0, 2, 6, 9. 0, 2, 6, 14. Discarded. ----------------------------------------------------------------------------- A[10] = 5. Case 3. Clone, extend and discard. 0. 0, 1. 0, 1, 5. 0, 2, 6. Discarded. 0, 2, 6, 9. ----------------------------------------------------------------------------- A[11] = 13. Case 2. Clone and extend. 0. 0, 1. 0, 1, 5. 0, 2, 6, 9. 0, 2, 6, 9, 13. ----------------------------------------------------------------------------- A[12] = 3. Case 3. Clone, extend and discard. 0. 0, 1. 0, 1, 3. 0, 1, 5. Discarded. 0, 2, 6, 9. 0, 2, 6, 9, 13. ----------------------------------------------------------------------------- A[13] = 11. Case 3. Clone, extend and discard. 0. 0, 1. 0, 1, 3. 0, 2, 6, 9. 0, 2, 6, 9, 11. 0, 2, 6, 9, 13. Discarded. ----------------------------------------------------------------------------- A[14] = 7. Case 3. Clone, extend and discard. 0. 0, 1. 0, 1, 3. 0, 1, 3, 7. 0, 2, 6, 9. Discarded. 0, 2, 6, 9, 11. ---------------------------------------------------------------------------- A[15] = 15. Case 2. Clone and extend. 0. 0, 1. 0, 1, 3. 0, 1, 3, 7. 0, 2, 6, 9, 11. 0, 2, 6, 9, 11, 15. <-- LIS List
Also, ensure we have maintained the condition, “end element of smaller list is smaller than end elements of larger lists“. This algorithm is called Patience Sorting. http://en.wikipedia.org/wiki/Patience_sorting
So, pick a suit from deck of cards. Find the longest increasing sub-sequence of cards from the shuffled suit. You will never forget the approach.
//! card piles contain pile of cards, nth pile contains n cards.
int top_card_list[n+1];
for(int i = 0; i <= n; i++) {
top_card_list[i] = -1;
}
现在,top_card_list 包含高度为 n 的卡片堆的顶部卡片。耐心排序将手中的牌放在比它小的最高的顶牌(或相反的牌)之上。进一步的排序说明,请参考维基百科页面耐心排序。
3
* 7 2
-------------------------------------------------------------
Pile of cards above (top card is larger than lower cards)
(note that pile of card represents longest increasing subsequence too !)
You cannot understand, because the code in wikipedia is wrong(I strongly believe so). It is not only wrong but the variables are poorly named. But it allowed me to spend time to understand how it works :D.
Now after I read the patience-sort. I rewrote the algorithm. I also wrote the corrected binary search.
Patience sort is like Insertion sort
Like insertion sort, patience-sort finds appropriate place for the next item by doing binary search. The binary search is done on the card-piles built in sorted order. Let me assign a variable for the card-pile.(I am talking about playing cards because patience is a simplified card game).
//! card piles contain pile of cards, nth pile contains n cards.
int top_card_list[n+1];
for(int i = 0; i <= n; i++) {
top_card_list[i] = -1;
}
Now the top_card_list contains the top-card of the card pile of height n. Patience sort places the card in hand over the highest top-card that is smaller than it(or the opposite). For further sorting note, please refer to wikipedia page for patience sort.
3
* 7 2
-------------------------------------------------------------
Pile of cards above (top card is larger than lower cards)
(note that pile of card represents longest increasing subsequence too !)
Binary search on pile of cards
Now to find a number while we do dynamic programming for longest-increasing subsequence, we run an inner loop which is O(n).
And the inner-loop is there to find the highest-top card that is smaller than our card in hand.
But we know that we can do it by binary search ! (exercise: prove the correctness) In that way we can do that in O(log (number of piles)) time. Now O(number of piles) = O(number of cards)(but number of card is 52, it should be O(1)!, just joking!). So the total application runs in O(n log n) time.
Here is the revised the DP with binary search.
for(int i = 1; i < n; i++) {
pile_height[i] = 1;
const int j = pile_search(top_card_list, arr, pile_len, arr[i]);
if(arr[i] > arr[j]) {
if(pile_height[i] < (pile_height[j]+1)) {
// relaxation
pile_height[i] = pile_height[j]+1;
result = std::max(result,pile_height[i]);
pile_len = std::max(pile_len,pile_height[i]);
}
}
if(-1 == top_card_list[pile_height[i]] || arr[top_card_list[pile_height[i]]] > arr[i]) {
top_card_list[pile_height[i]] = i; // top card on the pile is now i
}
}
Here is the correct pile search below. It is simply a binary search, but it finds the index of the top-card which is smaller than card in hand.
inline static int pile_search(const int*top_card_list, const vector<int>& arr, int pile_len, int strict_upper_limit) {
int start = 1,bound=pile_len;
while(start < bound) {
if(arr[top_card_list[bound]] < strict_upper_limit) {
return top_card_list[bound];
}
int mid = (start+bound)/2 + ((start+bound)&1);
if(arr[top_card_list[mid]] >= strict_upper_limit) {
// go lower
bound = mid-1;
} else {
start = mid;
}
}
return top_card_list[bound];
}
Notice that unlike wikipedia, it returns top_card_list[bound] (my fix). Also notice where the top_card_list[] is updated in the dp. This code is tested for the boundary cases. I hope it helps.
basically it is impossible to not be a strictly increasing subsequence. The proof is by contradiction: Suppose it is not then we have two cases: Case 1) There is some element M[j] that ends two subsequences of length j and j+some number. This is impossible (proof in link)
Case 2) Slightly different that Case 1 but pretty the same reasoning. How can you have a smallest number end two subsequences of two different lengths? it can't be.
发布评论
评论(8)
我们先看一下n^2算法:
现在改进发生在第二个循环,基本上可以通过使用二分查找来提高速度。除了数组dp[]之外,我们还有一个数组c[],c比较特殊,c[i]的意思是:长度为i的最长递增序列的最后一个元素的最小值。
Let's first look at the n^2 algorithm:
Now the improvement happens at the second loop, basically, you can improve the speed by using binary search. Besides the array dp[], let's have another array c[], c is pretty special, c[i] means: the minimum value of the last element of the longest increasing sequence whose length is i.
这是来自编程竞赛搭便车指南 (注意:此实现假设列表中没有重复项):
为了考虑重复项,可以检查例如该数字是否已在集合中。如果是,则忽略该数字,否则继续使用与之前相同的方法。或者,可以颠倒操作顺序:先删除,然后插入。下面的代码实现了这种行为:
第二个算法可以扩展为通过维护一个父数组来找到最长递增子序列(LIS)本身,该父数组包含原始数组中 LIS 的前一个元素的位置。
This is the O(n*lg(n)) solution from The Hitchhiker’s Guide to the Programming Contests (note: this implementation assumes there are no duplicates in the list):
To account for duplicates one could check, for example, if the number is already in the set. If it is, ignore the number, otherwise carry on using the same method as before. Alternatively, one could reverse the order of the operations: first remove, then insert. The code below implements this behaviour:
The second algorithm could be extended to find the longest increasing subsequence(LIS) itself by maintaining a parent array which contains the position of the previous element of the LIS in the original array.
我们需要维护递增序列的列表。
一般来说,我们有一组不同长度的活动列表。我们将元素 A[i] 添加到这些列表中。我们按照列表长度的递减顺序扫描列表(查找末尾元素)。我们将验证所有列表的结束元素,以找到结束元素小于 A[i](下限值)的列表。
我们的策略由以下条件决定,
1. 如果 A[i] 在活动列表的所有最终候选者中最小,我们将开始长度为 1 的新活动列表。
2. 如果 A[i] 在活动列表的所有最终候选者中是最大的,我们将克隆最大的活动列表,并将其扩展 A[i]。
3. 如果A[i]在两者之间,我们将找到一个列表,其最大结束元素小于A[i]。通过 A[i] 克隆并扩展此列表。我们将丢弃与此修改后的列表长度相同的所有其他列表。
请注意,在我们构建活动列表期间的任何情况下,都会保持以下条件。
“较小列表的结束元素小于较大列表的结束元素”。
通过一个例子就可以清楚地看出,让我们以 wiki 为例:
{0、8、4、12、2、10、6、14、1、9、5、13、3、11、7、15}。
A[0] = 0。情况 1。没有活动列表,请创建一个。
0.
--------------------------------------------------------- ------------------------------------------
A[1] = 8. 情况 2. 克隆和扩展。
0.
0, 8.
--------------------------------------------------------- ------------------------------------------
A[2] = 4. 情况 3. 克隆、扩展和丢弃。
0.
0, 4.
0, 8. 丢弃
--------------------------------------------------------- ------------------------------------------
A[3] = 12. 情况 2. 克隆和扩展。
0.
0, 4.
0、4、12。
--------------------------------------------------------- ------------------------------------------
A[4] = 2. 情况 3. 克隆、扩展和丢弃。
0.
0, 2.
0, 4. 已丢弃。
0、4、12。
--------------------------------------------------------- ------------------------------------------
A[5] = 10. 情况 3. 克隆、扩展和丢弃。
0.
0, 2.
0、2、10。
0、4、12。已丢弃。
--------------------------------------------------------- ------------------------------------------
A[6] = 6. 情况 3. 克隆、扩展和丢弃。
0.
0, 2.
0、2、6。
0、2、10。已丢弃。
------------------------------------------------------------ ------------------------------------------
A[7] = 14. 情况 2. 克隆和扩展。
0.
0, 2.
0、2、6。
0、2、6、14。
------------------------------------------------------------ ------------------------------------------
A[8] = 1. 情况 3. 克隆、扩展和丢弃。
0.
0, 1.
0, 2. 丢弃。
0、2、6。
0、2、6、14。
--------------------------------------------------------- ------------------------------------------
A[9] = 9. 情况 3. 克隆、扩展和丢弃。
0.
0, 1.
0、2、6。
0、2、6、9。
0、2、6、14。已丢弃。
--------------------------------------------------------- ------------------------------------------
A[10] = 5. 情况 3. 克隆、扩展和丢弃。
0.
0, 1.
0、1、5。
0、2、6。已丢弃。
0、2、6、9。
--------------------------------------------------------- ------------------------------------------
A[11] = 13. 情况 2. 克隆和扩展。
0.
0, 1.
0、1、5。
0、2、6、9。
0、2、6、9、13。
------------------------------------------------------------ ------------------------------------------
A[12] = 3. 情况 3. 克隆、扩展和丢弃。
0.
0, 1.
0、1、3。
0、1、5。已丢弃。
0、2、6、9。
0、2、6、9、13。
--------------------------------------------------------- ------------------------------------------
A[13] = 11. 情况 3. 克隆、扩展和丢弃。
0.
0, 1.
0、1、3。
0、2、6、9。
0、2、6、9、11。
0、2、6、9、13。已丢弃。
--------------------------------------------------------- ------------------------------------------
A[14] = 7. 情况 3. 克隆、扩展和丢弃。
0.
0, 1.
0、1、3。
0、1、3、7。
0、2、6、9。已丢弃。
0、2、6、9、11。
--------------------------------------------------------- --------------------------------------
A[15] = 15. 情况 2. 克隆和扩展。
0.
0, 1.
0、1、3。
0、1、3、7。
0、2、6、9、11。
0, 2, 6, 9, 11, 15. <-- LIS 列表
此外,确保我们保持条件“较小列表的结束元素小于较大列表的结束元素”列表“。
该算法称为耐心排序。
http://en.wikipedia.org/wiki/Patience_sorting
因此,从甲板上选择一套西装牌。从洗好的花色中找出最长的递增子序列。你永远不会忘记这种方法。
复杂度:O(NlogN)
来源:http://www.geeksforgeeks.org/longest-monotonically-increasing-subsequence-size-n-log-n/
We need to maintain lists of increasing sequences.
In general, we have set of active lists of varying length. We are adding an element A[i] to these lists. We scan the lists (for end elements) in decreasing order of their length. We will verify the end elements of all the lists to find a list whose end element is smaller than A[i] (floor value).
Our strategy determined by the following conditions,
1. If A[i] is smallest among all end candidates of active lists, we will start new active list of length 1.
2. If A[i] is largest among all end candidates of active lists, we will clone the largest active list, and extend it by A[i].
3. If A[i] is in between, we will find a list with largest end element that is smaller than A[i]. Clone and extend this list by A[i]. We will discard all other lists of same length as that of this modified list.
Note that at any instance during our construction of active lists, the following condition is maintained.
“end element of smaller list is smaller than end elements of larger lists”.
It will be clear with an example, let us take example from wiki :
{0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15}.
A[0] = 0. Case 1. There are no active lists, create one.
0.
-----------------------------------------------------------------------------
A[1] = 8. Case 2. Clone and extend.
0.
0, 8.
-----------------------------------------------------------------------------
A[2] = 4. Case 3. Clone, extend and discard.
0.
0, 4.
0, 8. Discarded
-----------------------------------------------------------------------------
A[3] = 12. Case 2. Clone and extend.
0.
0, 4.
0, 4, 12.
-----------------------------------------------------------------------------
A[4] = 2. Case 3. Clone, extend and discard.
0.
0, 2.
0, 4. Discarded.
0, 4, 12.
-----------------------------------------------------------------------------
A[5] = 10. Case 3. Clone, extend and discard.
0.
0, 2.
0, 2, 10.
0, 4, 12. Discarded.
-----------------------------------------------------------------------------
A[6] = 6. Case 3. Clone, extend and discard.
0.
0, 2.
0, 2, 6.
0, 2, 10. Discarded.
-----------------------------------------------------------------------------
A[7] = 14. Case 2. Clone and extend.
0.
0, 2.
0, 2, 6.
0, 2, 6, 14.
-----------------------------------------------------------------------------
A[8] = 1. Case 3. Clone, extend and discard.
0.
0, 1.
0, 2. Discarded.
0, 2, 6.
0, 2, 6, 14.
-----------------------------------------------------------------------------
A[9] = 9. Case 3. Clone, extend and discard.
0.
0, 1.
0, 2, 6.
0, 2, 6, 9.
0, 2, 6, 14. Discarded.
-----------------------------------------------------------------------------
A[10] = 5. Case 3. Clone, extend and discard.
0.
0, 1.
0, 1, 5.
0, 2, 6. Discarded.
0, 2, 6, 9.
-----------------------------------------------------------------------------
A[11] = 13. Case 2. Clone and extend.
0.
0, 1.
0, 1, 5.
0, 2, 6, 9.
0, 2, 6, 9, 13.
-----------------------------------------------------------------------------
A[12] = 3. Case 3. Clone, extend and discard.
0.
0, 1.
0, 1, 3.
0, 1, 5. Discarded.
0, 2, 6, 9.
0, 2, 6, 9, 13.
-----------------------------------------------------------------------------
A[13] = 11. Case 3. Clone, extend and discard.
0.
0, 1.
0, 1, 3.
0, 2, 6, 9.
0, 2, 6, 9, 11.
0, 2, 6, 9, 13. Discarded.
-----------------------------------------------------------------------------
A[14] = 7. Case 3. Clone, extend and discard.
0.
0, 1.
0, 1, 3.
0, 1, 3, 7.
0, 2, 6, 9. Discarded.
0, 2, 6, 9, 11.
----------------------------------------------------------------------------
A[15] = 15. Case 2. Clone and extend.
0.
0, 1.
0, 1, 3.
0, 1, 3, 7.
0, 2, 6, 9, 11.
0, 2, 6, 9, 11, 15. <-- LIS List
Also, ensure we have maintained the condition, “end element of smaller list is smaller than end elements of larger lists“.
This algorithm is called Patience Sorting.
http://en.wikipedia.org/wiki/Patience_sorting
So, pick a suit from deck of cards. Find the longest increasing sub-sequence of cards from the shuffled suit. You will never forget the approach.
Complexity : O(NlogN)
Source: http://www.geeksforgeeks.org/longest-monotonically-increasing-subsequence-size-n-log-n/
我想出了这个
i came up with this
你无法理解,因为维基百科中的代码是错误的(我坚信是这样)。这不仅是错误的,而且变量的命名也很糟糕。但它让我花时间去理解它是如何工作的:D。
现在,在我阅读完耐心排序后。我重写了算法。我还编写了更正的二分搜索。
耐心排序就像插入排序
与插入排序类似,耐心排序通过二分查找为下一项找到合适的位置。二分查找是在按排序顺序构建的卡片堆上完成的。让我为牌堆分配一个变量。(我说的是玩纸牌,因为耐心是一种简化的纸牌游戏)。
现在,
top_card_list
包含高度为n
的卡片堆的顶部卡片。耐心排序将手中的牌放在比它小的最高的顶牌(或相反的牌)之上。进一步的排序说明,请参考维基百科页面耐心排序。在一堆卡片上进行二分搜索
现在,为了在对最长递增子序列进行动态规划时找到一个数字,我们运行一个
O(n)
的内部循环。内循环是为了找到比我们手中的牌小的最高的牌。
但我们知道我们可以通过二分搜索来做到这一点! (练习:证明正确性)这样我们就可以在
O(log (桩数))
时间内完成。现在O(桩数)
=O(牌数)
(但是牌数是52,应该是O(1)!开玩笑!)。因此整个应用程序的运行时间为O(n log n)
。这是修改后的二分查找 DP。
下面是正确的堆搜索。它只是简单的二分查找,但它找到了比手中的牌小的顶牌的索引。
请注意,与维基百科不同,它返回
top_card_list[bound]
(我的修复)。另请注意top_card_list[]
在 dp 中的更新位置。该代码针对边界情况进行了测试。我希望它有帮助。You cannot understand, because the code in wikipedia is wrong(I strongly believe so). It is not only wrong but the variables are poorly named. But it allowed me to spend time to understand how it works :D.
Now after I read the patience-sort. I rewrote the algorithm. I also wrote the corrected binary search.
Patience sort is like Insertion sort
Like insertion sort, patience-sort finds appropriate place for the next item by doing binary search. The binary search is done on the card-piles built in sorted order. Let me assign a variable for the card-pile.(I am talking about playing cards because patience is a simplified card game).
Now the
top_card_list
contains the top-card of the card pile of heightn
. Patience sort places the card in hand over the highest top-card that is smaller than it(or the opposite). For further sorting note, please refer to wikipedia page for patience sort.Binary search on pile of cards
Now to find a number while we do dynamic programming for longest-increasing subsequence, we run an inner loop which is
O(n)
.And the inner-loop is there to find the highest-top card that is smaller than our card in hand.
But we know that we can do it by binary search ! (exercise: prove the correctness) In that way we can do that in
O(log (number of piles))
time. NowO(number of piles)
=O(number of cards)
(but number of card is 52, it should be O(1)!, just joking!). So the total application runs inO(n log n)
time.Here is the revised the DP with binary search.
Here is the correct pile search below. It is simply a binary search, but it finds the index of the top-card which is smaller than card in hand.
Notice that unlike wikipedia, it returns
top_card_list[bound]
(my fix). Also notice where thetop_card_list[]
is updated in the dp. This code is tested for the boundary cases. I hope it helps.这里有一个证明 https://strncat .github.io/jekyll/update/2019/06/25/longest-increasing-subsequence.html
基本上不可能不是严格递增子序列。
证明是通过反证法:假设不是,那么我们有两种情况:
情况 1) 有某个元素 M[j] 结束两个长度为 j 和 j+某个数字的子序列。这是不可能的(链接中的证明)
案例 2)与案例 1 略有不同,但推理几乎相同。如何用最小的数结束两个不同长度的两个子序列?不可能。
There is a proof here https://strncat.github.io/jekyll/update/2019/06/25/longest-increasing-subsequence.html
basically it is impossible to not be a strictly increasing subsequence.
The proof is by contradiction: Suppose it is not then we have two cases:
Case 1) There is some element M[j] that ends two subsequences of length j and j+some number. This is impossible (proof in link)
Case 2) Slightly different that Case 1 but pretty the same reasoning. How can you have a smallest number end two subsequences of two different lengths? it can't be.
算法背后的基本思想是保留给定长度的 LIS 列表,以最小可能元素结尾。构造这样的序列
因为在第一步中您搜索的值小于 X[i],所以新的解决方案(对于
k+1
)将具有最后一个元素大于较短的序列。我希望它会有所帮助。
The base idea behind algorithm is to keep list of LIS of a given length ending with smallest possible element. Constructing such sequence
k
)k+1
lengthBecause in first step you search for smaller value then X[i] the new solution (for
k+1
) will have last element greater then shorter sequence.I hope it will help.
您一定可以查看此视频以获取解释:
https://www.youtube .com/watch?v=nf3YG4CnTbg&feature=youtu.be
我的 nlogn 方法代码是:
You can surely check this video for explanation:
https://www.youtube.com/watch?v=nf3YG4CnTbg&feature=youtu.be
My code for nlogn approch is: