Title Description
The title from Leetcode algorithm 315 exam questions, specific topics as follows:
Given an nums
array of integers, as required to return a counts
new array. The array counts
has the properties: counts[i]
The value of nums[i]
the right side is smaller than the nums[i]
number of elements.
Examples given:
Input: [5,2,6,1]
Output: [2,1,1,0]
Explanation:
right side 5 has two smaller elements (1 and 2)
only one of the more right 2 small elements (1).
6 right has a smaller elements (1)
on the right side has a smaller elements 0.
Most likely to think of problem-solving ideas
The subject looks very easy to read and very easy to start, but on the difficulty setting is Hard , in fact, the biggest problem is the complexity of the problem, the basic idea is to traverse the array input, comparing each element and its element after using a counter to record and added to the output array, the complexity of such an algorithm is clearly $ O (n ^ 2) $ , code is as follows:
1 |
class : |
But to do so it will be submitted directly to TLE, and therefore need to find other new ideas.
Reverse problem
Observe this problem will find that you can essentially consider this issue as a reverse problem, reverse problem issues described as follows:
Given a number $ a_1, a_2, cdots, a_n $, find the number of ordered pairs $ (i, j) $ such that $ i <j $ but $ a_i> a_j $, value $ n-$ herein can be achieved $ 10 ^ 6 $
The problem is similar, violent direct solving method can be used to loop through the complexity is $ O (n ^ 2) $, is clearly time out, and therefore need to consider other solution here is to learn merge sort method.
Merge sort
Thought merge sort shown below:
The core idea is to divide and conquer the idea to get in a disorderly array, we divide them binary, recursive partitioning to each interior is only one number, in reverse "and" operation carried out at all the data up orderly consolidation, ultimately orderly array. The figure just to the split that step operation. About algorithmic complexity of merge sort is also very easy to calculate, binary data is first split, the algorithm complexity be $ O (log N) $, and the complexity of the sorting process is $ O (N) $, so this complexity of the algorithm is $ O (Nlog N) $. Python code is implemented is given below:
1 |
class : DEF merge_sort (Self, the nums) : IF len (the nums) == . 1 : return the nums MID = len (the nums) // 2 left the nums = [: MID] right = the nums [MID:] left_list self.merge_sort = ( Self, left) right_list = self.merge_sort (Self, right) Result = [] # for storing the sorted array finished # define two pointers are used for the left and right sides of the array left_pointer = right_pointer = 0 the while left_pointer <len (left_list) and right_pointer <len (right_list): IF left_list [left_pointer] <right_list [right_pointer]: |
Back to the reverse of the problem
Reference method merge sort, reverse solving the problem can be divided into three steps:
- Partitioning problem: the original sequence into two subsequences of equal length as
- Recursive procedure: Statistics about to reverse subsequence
- After the reverse order of the sequence of statistics about the merger: merger
A map can more clearly understand the relationship between reverse and merge sort of:
When result
when placed one element, another array of data arrays in which it will actually constitutes its reverse pair. The reverse may be calculated on the number of reverse when the number of arrays can also be used behind a row calculation using the previous column array elements.
Calculating the code is given below in reverse order:
1 |
# Recursively implementation of Merge Sort and Inversion Pairs counts |
回到出发点Leetcode第315题
刚刚我们已经通过归并排序成功求解了给定一个数组其中的逆序对的个数,现在回到第315题,这个问题虽然不是直接求逆序对,但是很大程度上思想是类似的,因此我们只需要做一些简单的修改就可以实现CountSmaller
的要求。
这道题目与逆序对求解最大的不同在于逆序对只要求计数,而这道题目要求的是输出每个元素的右边小于它的元素数量,是更加具体的求解。思想上可以考虑为:left数组中的元素出列时,计算right数组中已经有了多少个元素出列,这么考虑的原因在于那些right数组中已经出列的元素都是小于当前考虑的left数组中的元素。
这里我们需要使用到一个索引数组的概念,原因是基于归并排序的过程元素的位置会发生变动,而我们希望的是得到原始位置下的结果,因此需要使用一个索引来记录元素的初识位置。
下面给出实现的代码:
1 |
class : |
其他的一些方法
实际上个人认为上面的这种基于归并排序求逆序对的算法理解起来还是很难得,我花了很多很多的时间才理解算法的整个过程,可能过不几天就把这个算法的具体流程给忘了哈哈哈。另外的话在这一道题的Leetcode的交流区还发现了一些大神给出的一些更加简单且易懂的算法。
基于二分查找的方法
1 |
class : |
这个名为“望舒”的大神提供了一种很好的方法,思路基本如下:从给定数组的最后一个开始遍历,使用二分查找法将其插入一个新的数组,这个新的数组会是有序的,那么此时遍历到的那个数字在新数组中的坐标就是原来数组中其右边所有比它小的数字的个数。通过查找插入的位置来得到该数后面比其小的数字的数量。这个算法的复杂度应该是$O(N^2)$。
二叉搜索树(BST)
这里简要给出一下Binary Search Tree的说明,搜索树也被称为排序树,该树构建的核心在于所有的左子树的值都小于根结点的值,所有的右子树的值都大于根节点的值。
给出二叉搜索树的简单实现代码:
1 |
class BTNode: |
回到原来的题目,使用BST如何计算右侧更小的数据的个数呢?
Mainly based on the idea: For the calculation of the right side is smaller than the current number of rules, from the perspective of the need to maintain a first binary considerations, we reverse traverse a given nums
array, this array using a binary sort tree structure, for example (in the test sample is given as an example [5,2,6,1]
) Some more images:
First, why should insert in reverse order?
After inserting the reverse we found that for any node
nums[i]
for which data is to the right of the binary tree have been entered, when it is inserted into the BST, the insertion process is completed, calculation of the number of data is smaller than the right side thereof will get the results.countSmaller = root.count + 1
Meaning?When the next node
nums[i]
is added BST, adding if theroot
right subtree, thenroot
all of the values of the left subtree are less than the number, along withroot
itself is less thannums[i]
the calculation process becomes very easy.