Java 中右侧较小元素的计数

2024 年 9 月 26 日 | 阅读 16 分钟

给定一个包含数字(可以是负数或正数)的数组或列表 inArr。任务是找出当前元素右侧有多少个较小的元素。以下示例有助于更好地理解。

示例 1

输入

int inArr[] = {7, 4, 9, 1, 3, 5, 0, 6, 2, 8}

输出: outArr[] = {7, 4, 7, 1, 2, 2, 0, 1, 0, 0}

解释: 对于元素 7,右侧的较小数目是 7(4、1、3、5、0、6、2)。对于元素 4,右侧的较小数目是 4(1、3、0、2)。数字 9 右侧的所有元素都比 9 小,其计数为 7。对于元素 1,右侧只有一个 0 比它小。因此,计数为 1。对于 3,右侧只有两个元素(0、2)比它小。对于元素 5,右侧只有 0 和 2 比 5 小。对于 0,没有较小的元素。因此,计数为 0。对于 6,只有 2 比 6 小(计数为 1)。对于元素 2,右侧只有 8,它比 2 大。因此,计数为 0。对于元素 8,右侧没有元素,计数为 0。

示例 2

输入

int inArr[] = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0}

输出: outArr[] = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0}

解释: 所有元素都按严格递减的顺序排列。此外,输入数组中元素的排列方式使得右侧元素的数量等于元素本身的值。例如,元素 1 右侧的元素数量为 1(右侧只有 0)。因此,输入数组本身就是答案。

示例 3

输入

int inArr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}

输出: outArr[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}

解释: 元素按递增顺序排列。因此,如果我们以输入数组的元素为参考,则右侧没有较小的元素。因此,输出数组只包含 0。

示例 4

输入

int inArr[] = {4, 4, 4, 4, 4, 4, 4, 4, 4, 4}

输出: outArr[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}

解释: 输入数组的所有元素都是相同的,即值相同。因此,输入数组中不存在较小的元素。因此,输出数组的所有元素都为零。

方法:使用嵌套循环

在此方法中,我们将嵌套两个 for 循环。外层循环选择输入数组中的一个元素(将其视为当前元素)。内层循环从当前元素的下一个元素开始,一直遍历到末尾。在内层循环中,将内层循环变量指向的元素与当前元素进行比较。如果它较小,则将当前元素的计数加 1;否则,不增加计数。

文件名: CountSmallEle.java

输出

For the input array: 
7 4 9 1 3 5 0 6 2 8 
The count of the smaller element on the right side is: 
7 4 7 1 2 2 0 1 0 0 

For the input array: 
9 8 7 6 5 4 3 2 1 0 
The count of the smaller element on the right side is: 
9 8 7 6 5 4 3 2 1 0 

For the input array: 
0 1 2 3 4 5 6 7 8 9 
The count of the smaller element on the right side is: 
0 0 0 0 0 0 0 0 0 0 

For the input array: 
4 4 4 4 4 4 4 4 4 4 
The count of the smaller element on the right side is: 
0 0 0 0 0 0 0 0 0 0

复杂度分析: 由于程序使用了嵌套循环,因此程序的时间复杂度为 O(N^2)。程序还使用了额外的空间来存储输出。因此,程序的空间复杂度也为 O(N),其中 N 是输入数组中存在的元素总数。

方法:使用带有附加字段的 BST

另一种方法是使用具有附加字段的简单二叉搜索树 (BST)。这两个附加字段是:1) 用于存储节点左侧的元素,2) 用于存储元素的出现次数。我们将从后往前遍历给定数组,并将元素添加到二叉搜索树中。在将元素添加到二叉搜索树时,我们将计算小于当前元素的所有元素的数量。

实施

观察下面的程序。

文件名: CountSmallEle1.java

输出

For the input array: 
7 4 9 1 3 5 0 6 2 8 
The count of the smaller element on the right side is: 
7 4 7 1 2 2 0 1 0 0 

For the input array: 
9 8 7 6 5 4 3 2 1 0 
The count of the smaller element on the right side is: 
9 8 7 6 5 4 3 2 1 0 

For the input array: 
0 1 2 3 4 5 6 7 8 9 
The count of the smaller element on the right side is: 
0 0 0 0 0 0 0 0 0 0 

For the input array: 
4 4 4 4 4 4 4 4 4 4 
The count of the smaller element on the right side is: 
0 0 0 0 0 0 0 0 0 0

复杂度分析: 由于添加的步骤可能需要 O(n) 时间,因此程序的时间复杂度为 O(n^2)。程序还使用辅助数组来存储结果,这使得程序的空间复杂度为 O(n),其中 n 是数组中存在的元素总数。

我们可以进行优化以降低时间复杂度。以下方法展示了这一点。

方法:使用自平衡树

BST 的缺点是它不是自平衡树。因此,BST 可能会变成倾斜树,导致时间复杂度较高。因此,为了进一步优化,需要使用自平衡树来计算答案。

我们将通过从右到左遍历输入数组,逐个将所有元素插入自平衡树。每当插入一个新键时,我们将其与根元素(输入数组的最右侧元素)进行检查或比较。如果新键大于根,则意味着它大于根节点左侧的所有节点。因此,左子树的大小会添加到新插入键的较小数目的计数中。其他节点也遵循相同的方法。

实施

观察下面的程序。

文件名: CountSmallEle1.java

输出

For the input array: 
7 4 9 1 3 5 0 6 2 8 
The count of the smaller element on the right side is: 
7 4 7 1 2 2 0 1 0 0 

For the input array: 
9 8 7 6 5 4 3 2 1 0 
The count of the smaller element on the right side is: 
9 8 7 6 5 4 3 2 1 0 

For the input array: 
0 1 2 3 4 5 6 7 8 9 
The count of the smaller element on the right side is: 
0 0 0 0 0 0 0 0 0 0 

For the input array: 
4 4 4 4 4 4 4 4 4 4 
The count of the smaller element on the right side is: 
0 0 0 0 0 0 0 0 0 0

复杂度分析: 上述程序中使用的二叉树永远不会是倾斜树。因此,程序的时​​间复杂度为 O(n x log(n)),其中 n 是输入数组中存在的节点总数。程序的空间复杂度与上一个程序相同。