Google Interview Question
Applications DevelopersCountry: India
Interview Type: In-Person
Not just good, actually. This is the best solution so far (in fact, probably even beyond the interviewer's expectations).
The second best is the one with the selection algorithm + binary search (has -1 votes unfortunately, but not surprising as people seem to find it difficult to understand)
Wouldn't it be simpler to modify quick select for very obvious change of partition choosing and terminal condition. No extra space.
This is a neat solution. But please correct me if I am wrong here. Wouldn't this algorithm fail in a case where no such n exists that there are n values that are greater than or equal to n, in the array.
Say the array is [900, 902, 901, 903, 1000]. My understanding is that there is no n satisfying the given condition. But this algorithm would give you '5' as the answer.
The 'n' in the question refers to the number of values. In that input, there are 5 values >= than 5. The fact that the values are >= 900 is not an issue.
Adriana, my M is the number of values. In the worst case, all M values are processed. So it's just O(M).
What is the correct answer supposed to be with this input
{1,4,2,1}
When I try this code in converted into java I get 2, should the answer be 1?
In that input, there are 2 numbers (2 and 4) larger or equal than 2. So the answer is 2
If we sort the numbers and pair together with the count of elements greater or equal than the elementin the sorted array, which is essentially the total count minus its index , we can easily determine where the answer is. For (1,2,3,4) the array of pairs is (1,4), (2,3), (3, 2) and (4,1). Compare the first number in the pair and the second number in the pair, there is an element after which the first number becomes greater than the second number in the pair. The second number in the pair is our answer.
To find the figures in the pairs, we need to count occurrences of the numbers in the array, hence the solution given by Miguel. Bellow is the same algorithm implemented in scala.
import scala.collection.mutable.Seq
def solve(data:Seq[Int]):Int = {
val length = data.size
var count = Seq.fill(length+1)(0)
data.foreach(d=>if (d>=length) count(length)+=1 else if (d>0) count(d)+=1)
var am = 0
for (i<-length until 0 by -1) {
am+=count(i)
if (am>=i) return i
}
return 0
}
val length = Random.nextInt(100)
val data = Seq.tabulate(length)(_=>Random.nextInt(1000))
println(length)
println(data.sorted zip (length-1 to 0 by -1))
println("Solution", solve(data))
There is a problem with the input {900, 2, 901, 3, 1000};
Th O/p is : 3,2,1 cz it is going by index.
Slight change fixes it, cz you need to compare by value not index.
int am = count[n];
for (int i = n-1; i >= 0; i--) {
am += count[i]; // amount of numbers >= i
if (am >= values[i])
{
cout<<values[i]<<endl;
}
}
Isnt it just easier, to start with n, then iterate through and if a number is smaller then n then reduce n with one?
@Miguel - Isn't the answer for this question is always upper bound of n/2 , if array contains non - negative numbers.
say for Array [ 900,2,901,3,1000]
Vector constructed : [ 1,1,1,1,1]
int am = 0;
for (int i = n; i > 0; i--) { // 5 to 0
am += count[i]; // always adding 1 for non negative
if (am >= i) // sum = 3 , when i = 3, so return
return i;
}
M elements, want to find n.
Binary search on n, using selection algorithm.
Guess n = n'. Find the n' largest element K using linear time selection algorithm. Check if K >= n'.
We can ignore half the elements already considered, so O(M) time algorithm.
A question: Binary search should be on a sorted array, right?
Even selection algorithm can take O(M) time, so your method may not be a
linear time in total.
@zhang.
"We can ignore half the elements already considered, so O(M) time algorithm." is accurate statement.
Binary search here implies you first guess n = M/2 and keep refining that guess.
You don't need the array sorted.
This answer is right. If you don't understand, ask. Downvoting only shows how ignorant you are.
Once again, the answer to this question is VERY SMALL modification of quick select algorithm. THe pivot is known to be how many from end, see if its value corresponds condition, choosing left or right partition is trivial. Quick select has just tiny bi simpler choosing conditions.
@CT: Once again. The above solution is guaranteed linear, even with the selection algorithm running multiple times.
Replacing the guaranteed O(M) selection algorithm with expected O(M) quickselect might be the practical thing to do, but you do need to consider the multiple calls to quickselect involved.
The solution by Miguel Oliviera is going to be the most practical and best performing, beating your quickselect.
Not sure what you are harping about.
Could you be a bit more detailed on "We can ignore half the elements already considered, so O(M) time algorithm"?
Did you mean that when do selection, we can shrink the search range? I don't understand this. Since the array is not sorted, the n-th element can occur in any place of the array, which means the "selection" algorithm will always have to search the entire array.
Am I missing anything here?
@xiaoxipan:
Finding the 7th largest allows you to partition the array into elements greater and lesser.
This is similar to how quickselect works, in fact.
To clarify a little.
Say you found the 7th largest, and now want to find the 15th largest.
You partition based on 7th largest (part of selection algo) and then find the 8th largest in partition which has the smaller elements.
Got it. Thank you anonymous. Actually std::nth_element does exactly what you expected. Your solution is the best in the sense that its time complexity is O(M) and space complexity is O(1).
This algorithm works well with time complexity O(M)+O(M/2)+...+O(1)=O(M), and space complexity O(1). It is the best solution till now. Please up vote Anonymous' answer.
The code and the test case:
#include <assert.h>
#include <time.h>
#include <stdlib.h>
#include <iostream>
#include <vector>
#include <algorithm>
int Solve( std::vector<int> &arr ){
int m = arr.size(), max = 0;
for( int low = 0, high = arr.size(); low < high; ){
int n = (low+high)/2;
std::nth_element( arr.begin()+low, arr.begin()+n, arr.begin()+high,
[](const int &a, const int &b){ return a>b; } );
//now arr[0..n-1] >= arr[n], arr[n+1...] <= arr[n]
if( arr[n] >= n+1 ){
max = std::max( max, n+1 );
low = n+1;
}
else {
high = n;
}
}
return max;
}
int SolveSlow( std::vector<int> &arr ){
std::sort( arr.begin(), arr.end(), [](const int &a, const int &b){ return a>b; } );
for( int n = arr.size() ; n > 0; --n ){
if( arr[n-1] >= n )
return n;
}
return 0;
}
int main(){
srand( time(0) );
int length = 1000;
for( int i = 0;i < 1000; ++ i ){
int m = length;
std::vector<int> arr(m);
for( int j = 0;j < m; ++ j ){
arr[j] = rand()%(length*2);
}
std::vector<int> aArr = arr;
int n1 = SolveSlow( arr );
int n2 = Solve( aArr );
assert( n1 == n2 );
}
return 0;
}
Here is my Java implementation of the same approach with test cases:
private static final Random RNG = new Random(1234);
public static int maximumN(int[] input) {
return maximumNRecursive(input, 0, input.length);
}
private static int maximumNRecursive(int[] input, int begin, int end) {
if (begin + 1 == end) {
if (input[begin] > begin)
return end;
return begin;
}
int pivot = begin + RNG.nextInt(end - begin);
int pivotVal = input[pivot];
swap(input, pivot, end - 1);
int storeIx = begin;
for (int i = begin; i < end; i++) {
if (input[i] > pivotVal) {
swap(input, i, storeIx);
storeIx++;
}
}
swap(input, storeIx, end - 1);
if (storeIx < pivotVal)
return maximumNRecursive(input, storeIx + 1, end);
else
return maximumNRecursive(input, begin, storeIx + 1);
}
private static void swap(int[] array, int i, int j) {
int tmp = array[i];
array[i] = array[j];
array[j] = tmp;
}
public static void main(String[] args) {
int[][] inputs =
{ {900, 1000, 901, 1000, 1000}, {1, 2, 3, 4}, {900, 2, 901, 3, 1000}, {1, 1, 1}};
int[] expecteds = {5, 2, 3, 1};
for (int i = 0; i < inputs.length; i++) {
System.out.println(format("expected: %d actual: %d", expecteds[i], maximumN(inputs[i])));
}
}
Sort the array in descending order. Loop over it from the beginning till you find A[i] where i > A[i]. This would be nlog(n).
Hi Neeraj
Can you pls explain how can you use binary search to find the element. You wouldnt know what element to search for right.
Here is an example for my query for the
Array = {900, 2, 901, 1, 3, 4,902,903,1000}
Sorted array = {1,2,3,4,900,901,902,903,1000}
Iteration1:
Min = 0, max = array length
The middle element (pivot) = 900.
Comparison: 900<=number of elements to his right? --> No
Iteration2:
So min = 0, max = (index of 900)-1;
pivot value = 2
Comparison: 2<=number of elements to his right --> yes
Now the problem is you got the answer but thats not the correct one so you will still have to keep running iterations and would not be able to stop.
So how is binary search would really be useful?
^^
Modify your condition to find the value. Check both the constraints
1> N should have N greater values than it
2> N should be largest for the upgiven property.
For #2 check N+1 fails the #1 test.
Here are your conditions for moving left/right.
if(arr[mid] > (hi-mid) && arr[mid+1] <= (hi-mid+1))
return arr[mid]
if(arr[mid] > (hi-mid) ) go to right
else go to left
** Not handled edge cases, hope you get the drift.
Saurabh, you were almost there.
You just ended your binary search prematurely.
we have the sorted array={1,2,3,4,900,901,902,903,1000}
after 2nd iteration we have min=1(index of 2) max=3
3rd iteration:
pivot value=3
no of elements on right side of 3>3
so we move right
4th iteration:
min=2 max=3
pivot=4 (since we already know 3 satisfies the property)
now for 4 we have >=4 values which are>4
now min=max=3
This is our final answer -> 4
int geN(vector<int>& arr)
{
sort(arr.begin() , arr.end());
int N = arr.size();
for(int i = N - 1 ; i >= 0 ; --i)
{
if(arr[i] <= N - i)
return arr[i];
}
return -1000;
}
int main()
{
cout <<geN(arr1);
cout <<"\n"<<geN(arr2);
return 0;
}
Idea: inside the quick sort algorithm iteration process only one "half" of array depending on "pivotValue", length of left and right sub-array.
Pros: in avarage case should be still O(nlogn) but with smaller koefficient
Cons: worst case is still O(n^2)
Agree. Have the same idea, would suggest to use 3-way partition in order to count repeated values.
1. sort the array in ascending order
2. then look for i where n-i+1>=a[i]; if no such element exists, then return 0
3. time complexity o(nlogn)
import bisect, random
def max_number_possible_n_array(ar):
"""
:param ar: array
:return: max possible n such that at least n values >= n
m = len(ar)
1. sort the array in ascending order O(m) = m*log(m)
2. do binary search for the 1st element > O(m) = log(m)
3. go right from the index and evaluate len(ar)-index (number of elements) >= arr[index] and return the last element find... O(m) = m
running time m*log(m)
"""
b = sorted(ar)
i = bisect.bisect_right(b, 0)
if i == len(b):
return -1
l = len(b)
while l-i >= b[i] and i < l:
i += 1
return b[i-1]
if __name__ == '__main__':
print max_number_possible_n_array([1,2,3,4])
print max_number_possible_n_array( [900, 2, 901, 3, 1000])
ar = [random.randint(1,100) for x in range(20)]
v = sorted(ar)
print v
print max_number_possible_n_array(ar)
///
import java.util.Arrays;
public class MaxN {
public static int arrayReturn(int[] arr){
Arrays.sort(arr);
int length = arr.length;
int i = length/2-1; //for identifying the position
int howmany = length - i ;
while( i>0 && arr[i] > howmany )
i--;
return arr[i];
}
public static void main(String args[]){
int[] array = {900, 2, 901 , 3, 1000};
int i = arrayReturn(array);
System.out.println(i);
}
}
\\\
This would be my answer :
1. Let the input array be A[] of size M.
2. Sort the array A in ascending order
3. Var N = -1 ( Assuming all elements are positive )
4. for each element A[i] starting from A[M-1] to A[0] do the following:
__4.a. if A[i] <= (M-i-1) : N = A[i] ; Break
5. if N == -1 then " No Results found" else "Answer is "+ N
Analysis :
Sorting : Time :O(M) Counting Sort ( Space Complexity depends on Input range O(A[M-1])
or Time : O(nlogn) and Space : O(1) for heap Sort
Finding the element : Time O(M)
Total : O(M) + O(M) = O(M)
Correct me if i am wrong
1. Build a TreeMap (Map with sorted keys) and count the frequency of each number
2. Traverse in descending order and sum the frequency of the numbers
3. Stop when sum >= n
O(n log(n)) time
O(n) space
public int solve(int[] array) {
TreeMap<Integer,Integer> map = new TreeMap<Integer,Integer>();
for (int i : array) {
int count = map.containsKey(i) ? map.get(i) : 0;
map.put(i, count + 1);
}
int n = 0;
for (Entry ent : map.descendingMap().entrySet()) {
n += (Integer)ent.getValue();
if (((Integer)ent.getKey()).compareTo(Integer.valueOf(n)) < 0) {
return (Integer)ent.getKey();
}
}
return 0;
}
N: size of array. O(N) time (amortized), O(1) space. A modification of quicksort like many others pointed out.
(semi rigorous) proof for complexity:
At any stage, T(n) = T(a n) + O(n); a<1 at subproblem size n (i.e., we are depending on a smaller problem of size a N). In reality we will have different values of
a
at every stage but lets say its the average value.
on summing up starting from problem size N, we have N + a N + a^2 N + .... + 1 and termination is upon reaching 1 or 0. so, we have: T(N) = (1-a^(N+1)/(1-a))*N.
when N-<inf, T(N) -> N.
O(1) space because it is in place.
package pracPkg;
import java.util.Arrays;
public class NGreaterThanN {
public static void main(String[] args){
int[] arr;
arr = new int[]{3,2,1,9,8,7};
//arr = new int[]{1,4,9,8,7,3,5};
System.out.println(Arrays.toString(arr));
System.out.println(nGn(arr));
}
public static int nGn(int[] arr){
return nGn(arr, 0, arr.length-1, -1);
}
private static int nGn(int[] arr, int start, int end, int bestAns){
if(end< start)
return bestAns;
if(start == end){
if(arr.length - start >= arr[start])
bestAns = arr[start];
return bestAns;
}
int ind = partition(arr, start, end);
if(arr.length - ind >= arr[ind]){
bestAns = arr[ind];
return nGn(arr,ind+1,end,bestAns);
}
else
return nGn(arr,start,ind-1,bestAns);
}
private static int partition(int[] arr, int start, int end){
int pivot = arr[start];
int i = start, j= start+1;
while(j<=end){
if(arr[j]<pivot){
int temp = arr[i+1];
arr[i+1] = arr[j];
arr[j] = temp;
i++;
}
j++;
int temp = arr[i];
arr[i] = pivot;
arr[start] = temp;
}
return i;
}
}
How about this?
I can solve without extra storage.
1. First, I will do quick select for the median position.
2. See the left side of median to find maximum candidate.
1 will take 2n on the vatage, 2 will take n, so total 3n, leading O(n).
public static Result findMax_N_Num_HavingMorethanOrEqual_N_Num_in_unsortedArray(int[] arr) {
validateArray(arr);
assert(arr.length >= 2);
int halfLen = arr.length/2 + arr.length%2;
int medianIndex = quickIndexSelect(arr, halfLen);
int maxNum = 0;
boolean isFound = false;
for (int i = medianIndex; i >=0; i--) {
if (arr[i] <= (halfLen)) {
isFound = true;
maxNum = Math.max(maxNum, arr[i]);
}
}
return new Result(isFound, isFound ? maxNum : null);
}
private static class Result {
private final boolean isFound;
private final Integer num;
private Result(boolean isFound, Integer num) {
this.isFound = isFound;
this.num = num;
}
}
public class Test {
public static int getNumber(int[] A) throws Exception {
int result = helper(A, 0, A.length - 1, 0);
if (result == 0)
throw new Exception("Number does not exist!");
return A[A.length - result];
}
// return the num of elements which is greater to equal to the pivot
public static int helper(int[] A, int start, int end, int tailSize) {
if (start > end)
return 0;
// choose the starting number of this range as pivot
int left = start, mid = start + 1, right = end;
while (mid <= right) {
if (A[mid] < A[left]) {
swap(A, left, mid);
mid++;
left++;
} else if (A[mid] == A[left])
mid++;
else {
swap(A, mid, right);
right--;
}
}
int greaterOrEqualToPivot = end - left + 1 + tailSize;
if (greaterOrEqualToPivot >= A[left]) {
int subResult = helper(A, right + 1, end, tailSize);
if (subResult != 0)
return subResult;
else
return greaterOrEqualToPivot;
} else
return helper(A, start, left - 1, greaterOrEqualToPivot);
}
public static void swap(int[] A, int left, int right) {
int temp = A[left];
A[left] = A[right];
A[right] = temp;
}
public static void main(String[] args) throws Exception {
System.out.println(getNumber(new int[] { 900, 2, 901, 3, 1000 }));
}
}
import java.lang.Integer;
public class HighestNumArray {
public static int highChecker(int[] a) {
int length = a.length;
int count = 0;
if (a.length==0)return Integer.MIN_VALUE;
int thenum = Integer.MIN_VALUE;
for (int i = 0; i < length; i++) {
count = 0;
for (int j = 0; j < length; j++) {
if (j == i)
continue;
if (length - a[i] > 0) {
if (a[i] < a[j]) {
count++;
if (count == a[i] && a[i] > thenum) {
thenum = a[i];
break;
}
}
}
}
}
return thenum;
}
public static void main(String[] args) {
int[] a = new int[] { 900, 2, 901, 3, 1000, 50, 60, 12, 34, 45, 56, 7,
7, 3, -1, -2, 8 };
System.out.println((highChecker(a)==Integer.MIN_VALUE)?"Not value":highChecker(a));
}
}
int chr[6] = { 9, 15, 11, 7, 2, 4 };
//return 0;
int i, j, tmp;
int len = sizeof(chr) / sizeof(*chr);
for (i = 1; i < len; i++)
{
j = i;
while (j > 0 && chr[j - 1] > chr[j])
{
tmp = chr[j];
chr[j] = chr[j - 1];
chr[j - 1] = tmp;
j--;
}
}
tmp = 0;
for (i = 1; i < len; i++)
{
if ((chr[i] >= (len - chr[i])) && ((len - chr[i]) > 0) )
tmp = chr[i];
//return 0;
}
cout << tmp << endl;
int main() {
string s = "moss wasgreatman a great mawasgreatmann";
string t[3] = { "was", "great", "manxxx" };
bool blnMatch = true;
int len = sizeof(t) / sizeof(*t);
for (int i = 0; i < len; i++) //iterate through array of substrings
{
for (int j = 0; j < s.length(); j++) //check for start in long string
{
if (t[i][0] == s[j])
{
blnMatch = true; // initialize boolean
for (int x = 1; x < t[i].length(); x++) //Loop through substring, matching letters in both strings.
{
if ((s[j + x] != t[i][x]))
blnMatch = false; //sets flag to false when no match.
}
}
if ((j >= (s.length() - t[i].length()) - 1) && (blnMatch == false)) // if end of string and no match, prints.
{
cout << t[i] << ": substring not found" << endl;
break;
}
}
}
}
I tried in Java and check with different types of the input and it returns correctly.
Can someone help me with runtime complexity of this program?
public static void main(String[] args) {
// int[] inputArray = {1,2,3,4};
// int[] inputArray = {27,9,13,3,5,2};
// int[] inputArray = {4,4,4,4,4};
// int[] inputArray = {4,4,4};
// int[] inputArray = {901,909,905,899};
// int[] inputArray = {900, 2, 901, 3, 1000};
int[] inputArray = {1,4,2,1};
int res = solve(inputArray);
System.out.println("res: " + res);
}
private static int solve(int[] in) {
int max = -1; //max=-1
int size = in.length; //size=4
for(int i=0; i<in.length; i++) {
int cnt = 0; //cnt=0 | cnt=0
if(in[i] <= size) { // 1<4 | 2<4
for(int j=0; j<in.length; j++) {
// System.out.println("i,j : "+ in[i] + " "+ in[j]);
if(in[i] <= in[j]) { // 1>=1 | 2>=1 2>=2 2>=3
cnt++; // cnt=1 | cnt=1
// System.out.println("cnt: " + cnt);
if(cnt == in[i] && in[i] > max) { // 1==1&1>-1| 1=2
max = in[i]; // max = 1 |
// System.out.println("max1: " + max);
break;
}
}
}
// System.out.println("\n");
}
}
return max;
}
Best method:
1.Sort the array in descending order.
2.Find n @A[i]
3.If i<(m-n+1)
4. Output values A[i] toA[m-n+1]
5.Else no output
Complexity would still be O(nlogn)
function nGreaterThanN(arr) {
var max = arr.length;
for (var i = 0; i < arr.length; i++) {
if (arr[i] < max) {
max--;
}
}
return max;
}
public class MaximumPossible {
public static void solve (int [] input){
Arrays.sort(input);
int length = input.length - 1;
List<Integer> temp = new ArrayList<>();
for(int i = 0; i<=length; i++){
if(input[i] <= length-i){
temp.add(input[i]);
}
}
System.out.println(findMax(temp));
}
private static int findMax(List<Integer> temp) {
if(temp.isEmpty()) return 0;
int max = temp.get(0);
for(int i = 1 ; i<temp.size() ; i++){
if(max < temp.get(i)){
max = temp.get(i);
}
}
return max;
}
public static void main(String[] args) {
int[] input = {1,4,2,1};
MaximumPossible.solve(input);
input = new int[]{1, 2, 3, 4};
MaximumPossible.solve(input);
input = new int[]{900, 2, 901, 3, 1000};
MaximumPossible.solve(input);
input = new int[]{900, 902, 901, 903, 1000};
MaximumPossible.solve(input);
}
}
Lets say the array has M numbers. For the purpose of this problem, negative values and 0s are irrelevant. Also, numbers larger than M can be treated as M because the answer is never larger than M.
So, we can count the number of existing values between 1 and M. Then, process the values backwards (M to 1) to find the answer, adding the counts of the values processed so far.
This yields an O(M) algorithm with extra O(M) memory.
- Miguel Oliveira June 21, 2014