Skip to content
Related Articles

Related Articles

Improve Article

Remove minimum elements from either side such that 2*min becomes more than max | Set 2

  • Difficulty Level : Hard
  • Last Updated : 24 Mar, 2020

Given an unsorted array, trim the array such that twice of minimum is greater than the maximum in the trimmed array. Elements should be removed from either end of the array. The number of removals should be minimum.

Examples:

Input: arr[] = {4, 5, 100, 9, 10, 11, 12, 15, 200}
Output: 4
We need to remove 4 elements (4, 5, 100, 200)
so that 2*min becomes more than max.

Input: arr[] = {4, 7, 5, 6}
Output: 0
We don’t need to remove any element as
4*2 > 7

Input: arr[] = {20, 7, 5, 6}
Output: 1



Approach: We have discussed various approaches to solve this problem in O(n3), O(n2 * logn), and O(n2) time in previous article. In this articles, we are going to discuss a O(n * logn) time solution using Sliding Window and Segment Tree concepts.

  1. Construct Segment Tree for RangeMinimumQuery and RangeMaximumQuery for the given input array.
  2. Take two pointers start and end, and initialize both to 0.
  3. While end is less than the length of the input array, do the following:
    • Find min and max in the current window using Segment Trees constructed in step 1.
    • Check if 2 * min ≤ max, if so then increment start pointer else update max valid length so far, if required
    • Increment end
  4. length(arr[]) – maxValidLength is the required answer.

Below is the implementation of the above approach:

Java




// Java implementation of the approach
public class GFG {
  
    // Function to return the minimum removals
    // required so that the array satisfy
    // the given condition
    public int removeMinElements(int[] a)
    {
        int n = a.length;
  
        RangeMinimumQuery rMimQ = new RangeMinimumQuery();
        int[] minTree = rMimQ.createSegmentTree(a);
  
        RangeMaximumQuery rMaxQ = new RangeMaximumQuery();
        int[] maxTree = rMaxQ.createSegmentTree(a);
  
        int start = 0, end = 0;
  
        // To store min and max in the current window
        int min, max;
        int maxValidLen = 0;
  
        while (end < n) {
            min = rMimQ.rangeMinimumQuery(minTree,
                                          start, end, n);
            max = rMaxQ.rangeMaximumQuery(maxTree,
                                          start, end, n);
            if (2 * min <= max)
                start++;
            else
                maxValidLen = Math.max(maxValidLen,
                                       end - start + 1);
            end++;
        }
        return n - maxValidLen;
    }
  
    class RangeMinimumQuery {
  
        // Creates a new segment tree from
        // the given input array
        public int[] createSegmentTree(int[] input)
        {
            int n = input.length;
            int segTreeSize = 2 * getNextPowerOfTwo(n) - 1;
            int[] segmentTree = new int[segTreeSize];
  
            createSegmentTreeUtil(segmentTree, input,
                                  0, n - 1, 0);
            return segmentTree;
        }
  
        private void createSegmentTreeUtil(int[] segmentTree,
                                           int[] input, int low,
                                           int high, int pos)
        {
            if (low == high) {
  
                // Its a leaf node
                segmentTree[pos] = input[low];
                return;
            }
  
            // Construct left and right subtrees and then
            // update value for current node
            int mid = (low + high) / 2;
            createSegmentTreeUtil(segmentTree, input, low,
                                  mid, (2 * pos + 1));
            createSegmentTreeUtil(segmentTree, input,
                                  mid + 1, high, (2 * pos + 2));
            segmentTree[pos] = Math.min(segmentTree[2 * pos + 1],
                                        segmentTree[2 * pos + 2]);
        }
  
        public int rangeMinimumQuery(int[] segmentTree, int from,
                                     int to, int inputSize)
        {
            return rangeMinimumQueryUtil(segmentTree, 0,
                                         inputSize - 1, from, to, 0);
        }
  
        private int rangeMinimumQueryUtil(int[] segmentTree, int low,
                                        int high, int from, int to, int pos)
        {
            // Total overlap
            if (from <= low && to >= high) {
                return segmentTree[pos];
            }
  
            // No overlap
            if (from > high || to < low) {
                return Integer.MAX_VALUE;
            }
  
            // Partial overlap
            int mid = (low + high) / 2;
            int left = rangeMinimumQueryUtil(segmentTree, low,
                                             mid, from, to,
                                             (2 * pos + 1));
            int right = rangeMinimumQueryUtil(segmentTree,
                                              mid + 1, high, from,
                                              to, (2 * pos + 2));
            return Math.min(left, right);
        }
    }
  
    class RangeMaximumQuery {
  
        // Creates a new segment tree from given input array
        public int[] createSegmentTree(int[] input)
        {
            int n = input.length;
            int segTreeSize = 2 * getNextPowerOfTwo(n) - 1;
            int[] segmentTree = new int[segTreeSize];
  
            createSegmentTreeUtil(segmentTree, input, 0, n - 1, 0);
            return segmentTree;
        }
  
        private void createSegmentTreeUtil(int[] segmentTree, int[] input,
                                           int low, int high, int pos)
        {
            if (low == high) {
  
                // Its a leaf node
                segmentTree[pos] = input[low];
                return;
            }
  
            // Construct left and right subtrees and then
            // update value for current node
            int mid = (low + high) / 2;
            createSegmentTreeUtil(segmentTree, input, low,
                                  mid, (2 * pos + 1));
            createSegmentTreeUtil(segmentTree, input,
                                  mid + 1, high, (2 * pos + 2));
            segmentTree[pos] = Math.max(segmentTree[2 * pos + 1],
                                        segmentTree[2 * pos + 2]);
        }
  
        public int rangeMaximumQuery(int[] segmentTree,
                                     int from, int to, int inputSize)
        {
            return rangeMaximumQueryUtil(segmentTree, 0,
                                         inputSize - 1, from, to, 0);
        }
  
        private int rangeMaximumQueryUtil(int[] segmentTree, int low,
                                 int high, int from, int to, int pos)
        {
            // Total overlap
            if (from <= low && to >= high) {
                return segmentTree[pos];
            }
  
            // No overlap
            if (from > high || to < low) {
                return Integer.MIN_VALUE;
            }
  
            // Partial overlap
            int mid = (low + high) / 2;
            int left = rangeMaximumQueryUtil(segmentTree, low,
                                             mid, from, to,
                                             (2 * pos + 1));
            int right = rangeMaximumQueryUtil(segmentTree,
                                              mid + 1, high, from,
                                              to, (2 * pos + 2));
            return Math.max(left, right);
        }
    }
  
    // Function to return the minimum power of 2
    // which is greater than n
    private int getNextPowerOfTwo(int n)
    {
        int logPart = (int)Math.ceil(Math.log(n)
                                     / Math.log(2));
        return (int)Math.pow(2, logPart);
    }
  
    // Driver code
    public static void main(String[] args)
    {
        int[] a = { 4, 5, 100, 9, 10, 11, 12, 15, 200 };
        GFG gfg = new GFG();
        System.out.println(gfg.removeMinElements(a));
    }
}

C#




// C# implementation of the approach
using System;
  
class GFG
{
  
    // Function to return the minimum removals
    // required so that the array satisfy
    // the given condition
    static int removeMinElements(int[] a)
    {
        int n = a.Length;
  
        RangeMinimumQuery rMimQ = new RangeMinimumQuery();
        int[] minTree = rMimQ.createSegmentTree(a);
  
        RangeMaximumQuery rMaxQ = new RangeMaximumQuery();
        int[] maxTree = rMaxQ.createSegmentTree(a);
  
        int start = 0, end = 0;
  
        // To store min and max in the current window
        int min, max;
        int maxValidLen = 0;
  
        while (end < n) 
        {
            min = rMimQ.rangeMinimumQuery(minTree,
                                        start, end, n);
            max = rMaxQ.rangeMaximumQuery(maxTree,
                                        start, end, n);
            if (2 * min <= max)
                start++;
            else
                maxValidLen = Math.Max(maxValidLen,
                                    end - start + 1);
            end++;
        }
        return n - maxValidLen;
    }
  
    class RangeMinimumQuery {
  
        // Creates a new segment tree from
        // the given input array
        public int[] createSegmentTree(int[] input)
        {
            int n = input.Length;
            int segTreeSize = 2 * getNextPowerOfTwo(n) - 1;
            int[] segmentTree = new int[segTreeSize];
  
            createSegmentTreeUtil(segmentTree, input,
                                0, n - 1, 0);
            return segmentTree;
        }
  
        public void createSegmentTreeUtil(int[] segmentTree,
                                        int[] input, int low,
                                        int high, int pos)
        {
            if (low == high) {
  
                // Its a leaf node
                segmentTree[pos] = input[low];
                return;
            }
  
            // Construct left and right subtrees and then
            // update value for current node
            int mid = (low + high) / 2;
            createSegmentTreeUtil(segmentTree, input, low,
                                mid, (2 * pos + 1));
            createSegmentTreeUtil(segmentTree, input,
                                mid + 1, high, (2 * pos + 2));
            segmentTree[pos] = Math.Min(segmentTree[2 * pos + 1],
                                        segmentTree[2 * pos + 2]);
        }
  
        public int rangeMinimumQuery(int[] segmentTree, int from,
                                    int to, int inputSize)
        {
            return rangeMinimumQueryUtil(segmentTree, 0,
                                        inputSize - 1, from, to, 0);
        }
  
        static int rangeMinimumQueryUtil(int[] segmentTree, int low,
                                        int high, int from, int to, int pos)
        {
            // Total overlap
            if (from <= low && to >= high) {
                return segmentTree[pos];
            }
  
            // No overlap
            if (from > high || to < low) {
                return int.MaxValue;
            }
  
            // Partial overlap
            int mid = (low + high) / 2;
            int left = rangeMinimumQueryUtil(segmentTree, low,
                                            mid, from, to,
                                            (2 * pos + 1));
            int right = rangeMinimumQueryUtil(segmentTree,
                                            mid + 1, high, from,
                                            to, (2 * pos + 2));
            return Math.Min(left, right);
        }
    }
  
    class RangeMaximumQuery {
  
        // Creates a new segment tree from given input array
        public int[] createSegmentTree(int[] input)
        {
            int n = input.Length;
            int segTreeSize = 2 * getNextPowerOfTwo(n) - 1;
            int[] segmentTree = new int[segTreeSize];
  
            createSegmentTreeUtil(segmentTree, input, 0, n - 1, 0);
            return segmentTree;
        }
  
        public void createSegmentTreeUtil(int[] segmentTree, int[] input,
                                        int low, int high, int pos)
        {
            if (low == high) {
  
                // Its a leaf node
                segmentTree[pos] = input[low];
                return;
            }
  
            // Construct left and right subtrees and then
            // update value for current node
            int mid = (low + high) / 2;
            createSegmentTreeUtil(segmentTree, input, low,
                                mid, (2 * pos + 1));
            createSegmentTreeUtil(segmentTree, input,
                                mid + 1, high, (2 * pos + 2));
            segmentTree[pos] = Math.Max(segmentTree[2 * pos + 1],
                                        segmentTree[2 * pos + 2]);
        }
  
        public int rangeMaximumQuery(int[] segmentTree,
                                    int from, int to, int inputSize)
        {
            return rangeMaximumQueryUtil(segmentTree, 0,
                                        inputSize - 1, from, to, 0);
        }
  
        public int rangeMaximumQueryUtil(int[] segmentTree, int low,
                                int high, int from, int to, int pos)
        {
            // Total overlap
            if (from <= low && to >= high) {
                return segmentTree[pos];
            }
  
            // No overlap
            if (from > high || to < low) {
                return int.MinValue;
            }
  
            // Partial overlap
            int mid = (low + high) / 2;
            int left = rangeMaximumQueryUtil(segmentTree, low,
                                            mid, from, to,
                                            (2 * pos + 1));
            int right = rangeMaximumQueryUtil(segmentTree,
                                            mid + 1, high, from,
                                            to, (2 * pos + 2));
            return Math.Max(left, right);
        }
    }
  
    // Function to return the minimum power of 2
    // which is greater than n
    static int getNextPowerOfTwo(int n)
    {
        int logPart = (int)Math.Ceiling(Math.Log(n)
                                    / Math.Log(2));
        return (int)Math.Pow(2, logPart);
    }
  
    // Driver code
    public static void Main(String[] args)
    {
        int[] a = { 4, 5, 100, 9, 10, 11, 12, 15, 200 };
        Console.WriteLine(removeMinElements(a));
    }
}
  
// This code is contributed by Rajput-Ji
Output:
4

Attention reader! Don’t stop learning now. Get hold of all the important DSA concepts with the DSA Self Paced Course at a student-friendly price and become industry ready.  To complete your preparation from learning a language to DS Algo and many more,  please refer Complete Interview Preparation Course.

In case you wish to attend live classes with experts, please refer DSA Live Classes for Working Professionals and Competitive Programming Live for Students.




My Personal Notes arrow_drop_up
Recommended Articles
Page :