Finding Second Minimum element in an Array

How would you find minimum element in an array (non-repeating random numbers) ? Well, the solution is obvious, you pick first element and compare with the next and keep the lower of the two. Do this again by comparing result of first comparison with the third element. Repeat this process for the complete array and you got the answer. So, in all for n elements you would need to do n-1 comparisons to find the minimum element (Figure 1). 


Ok, the solution for finding smallest element was obvious. What if we need to find the next smallest element in an array? Well, one simple solution would be to compare the first two to select minimum and second minimum. Then pick the next element, compare with the minimum (selected in previous step) and replace it if the next element if lower. If not, compare with the second min and replace if necessary. Repeat this over the array and we have the second minimum. This algorithm would require 2n -3 comparisons (one comparison for first two elements + 2 comparisons for remaining elements of array. So 1 + 2 (n -2) = 2n -3.

What about finding 3rd minimum? The above logic would require 3 + 3(n-3) or 3n -6 comparisons.

The next question is can we do better than 2n -3 for finding second element (likewise for 3rd element and so forth). Can we not just sort the array and then just pick the 2nd value off the sorted array? Well, unfortunately that would not help either as best sorting algorithm will take nlog(n) time which is definitely more than what it would take for finding first log(n) elements using above method anyway. Well, the good news is that, yes there is a way to do better than 2n comparisons, and that is what this article is about - finding 2nd minimum in an array in efficient manner.

To solve problem of this nature we can take clue from a specialized form of problem set that can be best solved by technique called Dynamic Programming. My previous post about finding Critical Path is another example where dynamic programming technique was used.

Tournament Algorithm 

For solving problem of finding minimum element in an array, we will use what can be termed tournament method. Imagine a tennis tournament with 'n' players. Each player is paired with another player and the winner advance to next round and loser goes home. So, after first round the field is reduced to half. So, to find the winner of the tournament a total log(n) rounds will be played, where n is the field size (Figure 2). 

Finding Min Using Tournament Method

For our problem, in the first iteration we compare adjacent elements of the array and the lower of two values are selected to form another array half the size (half + 1 for odd number of elements). The process is repeated till we find the minimum value. The number of comparisons needed to find the minimum is still the same, as calculated below:

Total comparisons: n (1/2 + 1/4 + … ) = n

(This above is convergent geometric series which has generalized solution of form (a/1 – r), or for our case it would be ½ (1 – ½); which equates to value 1)

During the process of finding minimum value, the generated arrays (during successive) iterations are saved to form a two-dimensional array (recursion tree) with first row being the input array and subsequent rows as generated from above iterations to form reverse tree (top row with input array and the minimum element at the bottom – root element).

The reason why we went the tournament way (as opposed to serial comparison) is that we can leverage the reverse tree to find the second minimum value with log(n) comparisons (asymptotic), hence the solution represents marked improvements over 2n (ignoring constant) comparisons as required by trivial method.

Here is the logic to find second minimum value.

The logic is that at some point the minimum element (root element) must have knocked out the second minimum element. So, by backtracking from the root element (minimum) all the way to the top and checking all the adjacent elements to the root element (for each row) we can find the second minimum. The key point to note is that at any level (above root level), the adjacent elements can be obtained by the index of root element at this level. Therefore, you don't need to scan complete sub-array (of recursion tree at any level). The adjacent elements for n-1 level is obtained as follows:
Adjacent Left (n-1 level) : 2 * (root index for nth level)
Adjacent Right(n-1 level): 2 * (root index for nth level) + 1

Therefore, for each row of the recursion tree, you just need to perform two comparisons to find the root element index to obtain the adjacent elements of row above. Refer to Figure 3 below. One of the elements marked in green box must be second minimum.

Finding 2nd Minimum using optimized back-tracking

So, how many comparisons we make using this method. Let’s calculate (ignoring constants): 

Comparisons for finding minimum: n 
Comparisons for finding 2nd minimum: log(n) 
Total: n + log(n)

Thus this method offers marked improvement over crude method.

Code 

Here is complete program:

001/**
002 * Copyright (c) 2010-2020 Malkit S. Bhasin. All rights reserved.
003 *
004 * All source code and material on this Blog site is the copyright of Malkit S.
005 * Bhasin, 2010 and is protected under copyright laws of the United States. This
006 * source code may not be hosted on any other site without my express, prior,
007 * written permission. Application to host any of the material elsewhere can be
008 * made by contacting me at mbhasin at gmail dot com
009 *
010 * I have made every effort and taken great care in making sure that the source
011 * code and other content included on my web site is technically accurate, but I
012 * disclaim any and all responsibility for any loss, damage or destruction of
013 * data or any other property which may arise from relying on it. I will in no
014 * case be liable for any monetary damages arising from such loss, damage or
015 * destruction.
016 *
017 * I further grant you ("Licensee") a non-exclusive, royalty free, license to
018 * use, modify and redistribute this software in source and binary code form,
019 * provided that i) this copyright notice and license appear on all copies of
020 * the software;
021 *
022 * As with any code, ensure to test this code in a development environment
023 * before attempting to run it in production.
024 *
025 * @author Malkit S. Bhasin
026 *
027 */
028public class SecondMinimum {
029 
030    public static int getSecondMinimumNonOptimized(int[] values) {
031        int min = -1, secondMin = -1;
032        int firstValue = values[0];
033        int secondValue = values[1];
034        if (firstValue < secondValue) {
035            min = firstValue;
036            secondMin = secondValue;
037        else {
038            min = secondValue;
039            secondMin = firstValue;
040        }
041        int nextElement = -1;
042        for (int i = 2; i < values.length; i++) {
043            nextElement = values[i];
044            if (nextElement < min) {
045                secondMin = min;
046                min = nextElement;
047            else if (nextElement < secondMin) {
048                secondMin = nextElement;
049            }
050        }
051        return secondMin;
052    }
053 
054    /**
055     * Takes an input array and generated a two-dimensional array whose rows are
056     * generated by comparing adjacent elements and selecting minimum of two
057     * elements. Thus the output is inverse triangle (root at bottom)
058     *
059     * @param values
060     * @return
061     */
062    public static int[][] getOutputTree(int[] values) {
063        Integer size = new Integer(values.length);
064        double treeDepth = Math.log(size.doubleValue()) / Math.log(2);
065        int intTreeDepth = getIntValue(Math.ceil(treeDepth)) + 1;
066        int[][] outputTree = new int[intTreeDepth][];
067 
068        // first row is the input
069        outputTree[0] = values;
070        printRow(outputTree[0]);
071 
072        int[] currentRow = values;
073        int[] nextRow = null;
074        for (int i = 1; i < intTreeDepth; i++) {
075            nextRow = getNextRow(currentRow);
076            outputTree[i] = nextRow;
077            currentRow = nextRow;
078            printRow(outputTree[i]);
079        }
080        return outputTree;
081    }
082 
083    /**
084     * Compares adjacent elements (starting from index 0), and construct a new
085     * array with elements that are smaller of the adjacent elements.
086     *
087     * For even sized input, the resulting array is half the size, for odd size
088     * array, it is half + 1.
089     *
090     * @param values
091     * @return
092     */
093    private static int[] getNextRow(int[] values) {
094        int rowSize = getNextRowSize(values);
095        int[] row = new int[rowSize];
096        int i = 0;
097        for (int j = 0; j < values.length; j++) {
098            if (j == (values.length - 1)) {
099                // this is the case where there are odd number of elements
100                // in the array. Hence the last loop will have only one element.
101                row[i++] = values[j];
102            else {
103                row[i++] = getMin(values[j], values[++j]);
104            }
105        }
106        return row;
107    }
108 
109    /**
110     * The logic for finding second minimum is as follows:
111     *
112     * Starting from root element (which is minimum element), find the lower of
113     * two adjacent element one row above. One of the two element must be root
114     * element. If the root element is left adjacent, the root index (for one
115     * row above) is two times the root index of any row. For right-adjacent, it
116     * is two times plus one. Select the other element (of two adjacent
117     * elements) as second minimum.
118     *
119     * Then move to one row further up and find elements adjacent to lowest
120     * element, again, one of the element must be root element (again, depending
121     * upon the fact that it is left or right adjacent, you can derive the root
122     * index for this row). Compare the other element with the second least
123     * selected in previous step, select the lower of the two and update the
124     * second lowest with this value.
125     *
126     * Continue this till you exhaust all the rows of the tree.
127     *
128     * @param values
129     * @return
130     */
131    public static int getSecondMinimum(int[][] tree, int rootElement) {
132        int adjacentleft = -1, adjacentRight = -1;
133        int secondLeast = Integer.MAX_VALUE;
134        int rootIndex = 0;
135        int[] rowAbove = null;
136        // we have to scan in reverse order
137        for (int i = tree.length - 1; i > 0; i--) {
138            // one row above
139            rowAbove = tree[i - 1];
140            adjacentleft = rowAbove[rootIndex * 2];
141 
142            // the root element could be the last element carried from row above
143            // because of odd number of elements in array, you need to do
144            // following
145            // check. if you don't, this case will blow {8, 4, 5, 6, 1, 2}
146            if (rowAbove.length >= ((rootIndex * 2 1) + 1)) {
147                adjacentRight = rowAbove[rootIndex * 2 1];
148            else {
149                adjacentRight = -1;
150            }
151 
152            // if there is no right adjacent value, then adjacent left must be
153            // root
154            // continue the loop.
155            if (adjacentRight == -1) {
156                // just checking for error condition
157                if (adjacentleft != rootElement) {
158                    throw new RuntimeException("This is error condition. Since there "
159                            " is only one adjacent element (last element), "
160                            " it must be root element");
161                else {
162                    rootIndex = rootIndex * 2;
163                    continue;
164                }
165            }
166 
167            // one of the adjacent number must be root (min value).
168            // Get the other number and compared with second min so far
169            if (adjacentleft == rootElement && adjacentRight != rootElement) {
170                secondLeast = getMin(secondLeast, adjacentRight);
171                rootIndex = rootIndex * 2;
172            else if (adjacentleft != rootElement && adjacentRight == rootElement) {
173                secondLeast = getMin(secondLeast, adjacentleft);
174                rootIndex = rootIndex * 2 1;
175            else {
176                throw new RuntimeException("This is error condition. One of the adjacent "
177                        "elements must be root element");
178            }
179        }
180 
181        return secondLeast;
182    }
183 
184    /**
185     * Returns minimum of two passed in values.
186     *
187     * @param num1
188     * @param num2
189     * @return
190     */
191    private static int getMin(int num1, int num2) {
192        return Math.min(num1, num2);
193    }
194 
195    /**
196     * following uses Math.ceil(double) to round to upper integer value..since
197     * this function takes double value, diving an int by double results in
198     * double.
199     *
200     * Another way of achieving this is for number x divided by n would be -
201     * (x+n-1)/n
202     *
203     * @param values
204     * @return
205     */
206    private static int getNextRowSize(int[] values) {
207        double size = Math.ceil(values.length / 2.0);
208        return getIntValue(size);
209    }
210 
211    private static int getIntValue(double value) {
212        return new Double(value).intValue();
213    }
214 
215    /**
216     * Returns the root element of the two-dimensional array.
217     *
218     * @param tree
219     * @return
220     */
221    public static int getRootElement(int[][] tree) {
222        int depth = tree.length;
223        return tree[depth - 1][0];
224    }
225 
226    private static void printRow(int[] values) {
227        for (int i : values) {
228            // System.out.print(i + " ");
229        }
230        // System.out.println(" ");
231    }
232 
233    public static void main(String args[]) {
234        int[] values = { 245318710 };
235        // Get Second Minimum (Non-Optimized)
236        System.out.println("Second Minimum (using unoptimized algorithm): "
237                + getSecondMinimumNonOptimized(values));
238 
239        // Get Tree and obtain the Minimum Element in the array
240        int[][] outputTree = getOutputTree(values);
241        int min = getRootElement(outputTree);
242 
243        // Get Second Minimum (Optimized)
244        System.out.println("Second Minimum (Using optimized algorithm): "
245                + getSecondMinimum(outputTree, min));
246    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值