2

I wrote a method to find the second smallest number in an array without using sort.I thought I could extend the same logic to find the third smallest in the array..However I realised it was not so easy..My brain must be addled by lack of sleep or something..Below is my code for findSecondSmallest and findThirdSmallest.. Can someone correct the flaw in my logic for the later?

public class ArrayElementSelection{
    public static int findSecondSmallest(int[] a){
            int N = a.length;
            int min = 0;
            int secondSmallest = 0;
            for(int i=1;i<N;i++){
                if(a[i] < a[min]){
                    secondSmallest = min;
                    min = i;

                }else if(a[i] < a[secondSmallest]){
                    secondSmallest = i;
                }
            }
            return a[secondSmallest];
        }

    public static int findThirdSmallest(int[] a){
            int N = a.length;
            int min = 0;
            int secondSmallest = 0;
            int thirdSmallest = 0;
            for(int i=1;i<N;i++){
                if(a[i] < a[min]){
                    min = i;
                }else if(a[i] < a[secondSmallest]){
                    secondSmallest = i;
                }else if(a[i]< a[thirdSmallest]){
                    thirdSmallest = i;
                }
            }

            return a[thirdSmallest];
        }

    public static void main(String[] args) {
         int[] a = new int[]{4,2,3,1,5};
         System.out.println(findThirdSmallest(a));
    }
}

>> 4
Luiggi Mendoza
  • 85,076
  • 16
  • 154
  • 332
damon
  • 8,127
  • 17
  • 69
  • 114
  • 2
    For better help sooner, post an [SSCCE](http://sscce.org/). – Andrew Thompson Jul 11 '13 at 17:44
  • Finding the kth smallest element in an array of length n can be done in linear time for any k, as shown in other posts, but since k=3 probably the O(kn) solutions talked about here here will be faster (and definitely much simpler than the O(n) solution for general k) – user2566092 Jul 11 '13 at 21:31

9 Answers9

3

When you update one of the three values, you might need to update the others as well.

If you got a new minimum, the previous minimum is now the second lowest... etc.

On that same line of reasoning, I suspect your second lowest example doesn't work for all arrays either.

Run your example (or run the logic by hand, it's even easier), on an array like: [5,4,3,2,1]

You'll end up with the min value pointing at the right position, and second/third still pointing at 0. you basically need to "shift" the values above when you find a new value to keep.

pcalcao
  • 15,789
  • 1
  • 44
  • 64
3

Okay let's clean up a bit. We want to find the third smallest element in the array.

    NavigableSet<Integer> min3 = new TreeSet<Integer>();
    //We keep only 3 elements in min3, as soon as the set size grows over 3
    //we remove the last element, which is the max.

    for (int x : array) {
        min3.add(x);
        if (min3.size() > 3) {
            min3.pollLast();
        } 
    }

    if (array.length >= 3) {
        Integer thirdMinimum = min3.pollLast();
        System.out.println(thirdMimimum);
    } else {
       //array must contain at least 3 elements
    }

The above code snippet finds the third unique minimum. Instead of a set of integers, if we keep a sorted list of integers. We can find the third non unique minimum.

bsd
  • 2,707
  • 1
  • 17
  • 24
  • 2
    This is a great solution. By keeping a sorted array of the lowest 3 elements, you are minimizing the insertion time. When we are looking for the "k"th smallest, for large k, then this would have a big impact. Instead of being O(nk), it would be O(n log k). – Trenin Jul 12 '13 at 12:39
  • I should explain more about my solution. – bsd Jul 12 '13 at 12:57
2

Here's the logic to find the 3 smallest items in an array. Let's assume your source array has 100 elements. Create a new array called temp with 3 elements, and immediately add the first 3 elements in the source array to it, then sort it (trivial to sort 3-element array). Mark down the size of the largest element in temp, call it maxTemp.

Now begin looping from the 4th element through the entire source array. If you find an element SMALLER than maxTemp:

1) Determine where in the temp array this new value fits, then put it in and shift the larger values up accordingly. Get rid of the previous maxTemp as its now too big to qualify.

2) Set the new maxTemp

3) Continue looping

When done, you'll have your temp array holding the 3 smallest elements.

Kon
  • 10,702
  • 6
  • 41
  • 58
2

There's a general method for doing this: use a priority queue.

  • Make a priority queue where the largest item is dequeued first
  • Now iterate over the items in your list, adding them into the priority queue
  • Whenever the priority queue exceeds k items, dequeue an item
  • After you've finished iterating, the kth smallest item is at the front of the priority queue

Priority queue operations take O(log(X)) time, where X is the number of items in the queue. Since our priority queue doesn't exceed k, each operation takes O(log(k)) time. The whole process will take O(n log(k)) time, which is O(n) if you make k a constant by setting k=3.

Craig Gidney
  • 17,763
  • 5
  • 68
  • 136
1

Probably not the best way to do this, but to extend how your first example works I believe it would look like this:

public static int findThirdSmallest(int[] a){
        int N = a.length;
        int min = 0;
        int secondSmallest = 1;
        int thirdSmallest = 2;
        for(int i=1;i<N;i++){
            if(a[i] < a[min]){
                thirdSmallest = secondSmallest;
                secondSmallest = min;
                min = i;
            }
        }
        return a[thirdSmallest];
    }

It'll have bugs if the array is less than 3 long, but otherwise I think it should work.

EDIT: Actually looking at it your algorithm is incorrect, I don't believe there is any way to get the nth smallest number while only going through the array only 1 time, naively you need to go through find the min, then go through again and compare to the min to find the next number and so on. I say naively because that has terrible performance for getting the nth smallest element, but it would get the job done.

Kevin DiTraglia
  • 25,746
  • 19
  • 92
  • 138
1

Lets assume you are part-way through your single pass through the array. You have the smallest, second smallest, and third smallest number so far.

The next number you see could be the new smallest, second smallest, or third smallest, so your program has to take care of this.

Here is an example that does it in one pass generically. You can use the same method to find the smallest, second smallest, or 10th smallest by simply changing the first parameter. Complexity is O(n*m) where n is the size of the array, and m is the order of the element you want to get (e.g. for third smallest, m is 3):

public class Smallest {

/**
 * Returns the index of the "pos"th smallest element.
 * 
 * @param pos
 *            Which element to return
 * @param a
 *            The array to search
 * @return -1 if there are less than pos elements
 */
public static int findSmallest(int pos, int[] a) {
    int i, j;

    // Keep an array of "pos" elements.
    List<Integer> smallest = new ArrayList<Integer>(3);

    // Init with -1 to indicate we haven't found an element yet.
    for (i = 0; i < pos; i++) smallest.add(i, -1);

    // Search for the smallest "pos" elements.
    for (i = 0; i < a.length; i++) {
        // See where in our smallest array, this element goes.
        for (j = 0; j < pos; j++) {
            Integer spos = smallest.get(j);
            if ((spos == -1) || (a[i] < a[spos])) {
                // The current element a[i] is the "j+1"th smallest.
                smallest.add(j, i);
                smallest.remove(3);
                break;
            } // if ((spos == -1) || (a[i] < a[spos]))
        } // for (j = 0; j < pos; j++)
    } // for (i = 0; i < a.length; i++) 
    return smallest.get(pos-1);
}

/**
 * @param args
 */
public static void main(String[] args) {
    // Find the 3rd smallest in the set.
    int a[] = {9,3,4,8,1,2,5,7,6};
    int thirdSmallest = findSmallest(3, a);
    System.out.println("Third smallest is a[" + thirdSmallest +"]="+a[thirdSmallest]);
}
}
Trenin
  • 2,041
  • 1
  • 14
  • 20
0

I found this interesting, so I quickly wrote small recursive method to solve it. This solves for the Nth smallest element given (an array of ints, the Nth smallest number, and 0 (a starting reference))

public class Thir {
    public static void main(String[] args) {
      int[] a = new int[]{4,2,3,1,5};
      System.out.println(findNthSmallest(a,3,0));
    }

    static int findNthSmallest(int [] array, int n, int last) {
      if (n == 0)
        return last;
      int MAX = 9999;
      int pos = 0;
      int curMin = 0;
      boolean changed = false;
      for (int i = 0; i < array.length; i++)
      {
        if (changed == false && array[i] > last)
         {
          curMin = array[i];
          changed = true;
         }
        if (array[i] < curMin)
        {
          pos = i;
          curMin = array[i];
        }
      }
      array[pos] = MAX;
      return findNthSmallest(array, n-1, curMin);
    }
}
Clark Kent
  • 1,178
  • 1
  • 12
  • 26
  • Interesting practise in recursion, but this effectively sorts the array and passes through the array n times. A much more efficient solution would only need to pass through the array once. – Trenin Jul 11 '13 at 19:07
  • @Trenin Why an interesting practice in recursion? This doesn't necessarily sort the array but turn the (n-1) numbers into really big numbers, then return the smallest element. – Clark Kent Jul 11 '13 at 19:47
  • Sorry - misread the algorithm. However, you are still passing through the array n times. You run through it once to find the smallest, again to find the second smallest, etc. It can be done with one pass through the array. – Trenin Jul 12 '13 at 12:37
0

You need just to use some priority queue or TreeSet in order to get X-th smallest or largest element in time N * log N, otherwise you're doing in N*M time, which is too bad to be considered.

Basicly create TreeSet and pit items into it as you traverse an array, then after you finished - you may simply iterate over TreeSet and N-th item under the iterator is what you're looking for.

jdevelop
  • 12,176
  • 10
  • 56
  • 112
  • n log n is not necessarily better than n*m. In fact, n*m is better once n>=10 since m is contsant at 3 < log 10. Also, the poster asked for an implementation that doesn't sort, which your method does. – Trenin Jul 11 '13 at 19:06
  • as far as I read, he doesn't want to sort an array. – jdevelop Jul 11 '13 at 19:35
0
public int thirdSmallest(Int[] arr){

    for(int x = 0; x < arr.length; x++){
       for(int y = 0; y < arr.length; y++){
           if(arr[x] > arr[y]){
             int z = arr[x];
             arr[x] = arr[y];
             arr[y] = z;
           }
       }       
    }
  return arr[2]; //third smallest
}
DotNetRussell
  • 9,716
  • 10
  • 56
  • 111