Google Interview Question for Software Engineer / Developers






Comment hidden because of low score. Click to expand.
3
of 5 vote

You can do it using two queues.
Q = input queue
minQ = min queue.

Algo can be as follows,

Push the element in Q,
simultaneously, push the element in minQ if
if minQ.front < current element then
push element in minQ
else if minQ.front > current element
delete minQ.rear and push current element in minQ.


now
For the operation getMin()
return minQ front.
For the operation delete_front() on Q (original queue)
if (Q.front == minQ.front)
delete front from both the queue
else
delete front from only Q (original queue)

...........................................

E.g.
12 4 8 7 10 25

Q.push(12),
minQ.push(12)
Q - 12
minQ - 12

Q.push(4),
minQ.push(4)
Q - 12 4
minQ - 4

Q.push(8),
minQ.push(8)
Q - 12 4 8
minQ - 4 8

Q.push(7),
minQ.push(7)
Q - 12 4 8 7
minQ - 4 7

Q.push(10),
minQ.push(10)
Q - 12 4 8 7 10
minQ - 4 7 10

Q.push(25),
minQ.push(25)
Q - 12 4 8 7 10 25
minQ - 4 7 25

please correct me if I am wrong.

- bg January 01, 2011 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 votes

Q.push(7),
minQ.push(7)
Q - 12 4 8 7
minQ - 4 7

Q.push(10),
minQ.push(10)
Q - 12 4 8 7 10
minQ - 4 7 10

else if minQ.front > current element
delete minQ.rear and push current element in minQ.

In the both the above case, current element is greater than the minQ.rear but only in the first case minQ.rear is deleted, but in the second case it is not deleted.


The logic could be
while(minQ.rear < current element)
delete minQ.rear
insert current element in minQ

But this could lead to o(n) in worst case.

- Anonymous January 01, 2011 | Flag
Comment hidden because of low score. Click to expand.
-1
of 1 vote

sorry,
small change in bellow logic .
minQ.front replaced with minQ.rear
I need to compare rear of minQ instead of front.

if minQ.rear < current element then
push element in minQ
else if minQ.rear > current element
delete minQ.rear and push current element in minQ.

can you explain, how it will take o(n).

- bg January 01, 2011 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

how about the following e.g.:

12 13 4 8 7 10 25

step1:
Q:12
minQ:12

step2:
Q:12,13
minQ:12,13

step3:
Q:12,13,4
minQ:12,4 //is this correct?

- Anonymous January 01, 2011 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

Anonymous is right. u have to use the while loop. It may have worst case o(n) complexity, however it's amortized time complexity is o(1). Actually to prove this consider the potential function to be the length of the min_queue. Similar problems r in Corman.

- mmu January 03, 2011 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

It would be easier to think this way: an item in the minQ is either inserted once or inserted and deleted once. Since an item will not be processed more than 2 times total complexity of the operations on minQ is O(n). Hence the average complexity of the operation is O(1).

- onurtekdas February 01, 2011 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

@bg on January 01, 2011
if minQ.rear < current element then
push element in minQ

should it be
if minQ.rear <= current element then

This insures correct get_min() behavior after deletion when inserting *consecutive* duplicate elements. When inserting an element x that's the same as the rear of minQ, we insert x into minQ. See this example:

1 1 3

Q.push(1)
Q - 1
minQ - 1

Q.push(1)
Q - 1 1
minQ - 1 1

Q.push(3)
Q - 1 1 3
minQ - 1 1 3

Q.pop_front()
Q - 1 3
minQ - 1

get_min() returns 1 which is correct.

bg's original algorithm gives:

1 1 3

Q.push(1)
Q - 1
minQ - 1

Q.push(1)
Q - 1 1
minQ - 1

Q.push(3)
Q - 1 1 3
minQ - 1 3

Q.pop_front()
Q - 1 3
minQ - 3

get_min() return 3 where it should return 1

If duplicates are not consecutive, bg's algorithm gives the correct result:

1 3 1

Q.push(1)
Q - 1
minQ - 1

Q.push(3)
Q - 1 3
minQ - 1 3

Q.push(1)
Q - 1 3 1
minQ - 1 1

Q.pop_front()
Q - 3 1
minQ - 1

get_min() returns 1 which is correct.

- db August 11, 2012 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

in my 1 1 3 example

Q.pop_front()
Q - 1 3
minQ - 1

get_min() returns 1 which is correct.

should be

Q.pop_front()
Q - 1 3
minQ - 1 3

get_min() returns 1 which is correct.

- db August 11, 2012 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

You don;t need another queue just store the minimum value in a separate variable or create a separate node at the end just to store minimum value.
Addition of new node all occur before last node after it is updated.

- kevseb1993 July 28, 2014 | Flag
Comment hidden because of low score. Click to expand.
2
of 2 vote

how about queue using 2 stacks Sol, with every item in stack maintaining minimum value in stack till itself

- A.K. January 01, 2011 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 vote

bg is right. excellent alg.

- ww January 01, 2011 | Flag Reply
Comment hidden because of low score. Click to expand.
-1
of 1 vote

@ww, what do u mean by right ? algo by bg fails as pointed by Anonymous as
12 13 4 8 7 10 25

step1:
Q:12
minQ:12

step2:
Q:12,13
minQ:12,13

step3:
Q:12,13,4
minQ:12,4 //Incorrect !!!!!!

- ramesh January 02, 2011 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 vote

if minQ.rear < current element then
push element in minQ


Can we directly access the rear of a queue? queue.rear is not implemented usually, right?

- xzy0410 January 02, 2011 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 vote

@bg, Anonymous, xyz0410...
bg's algo is right with small modification as Anonymous corrected.

Rather than using queue to maintain minimum elements we can use double ended queue, so that we can enqueue and dequeue from both sides (front and rear).
For each element, we need to enqueue and dequeue in minQ(double ended queue) only once, hence even there is while loop , maximum number of operations will not cross 2n for n elements. Hence enque, deque and findmin operation will be constant time.

- Rakesh Soni January 02, 2011 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 vote

How this is correct for sequence 8,9,11,14,1,5
even if all the elements are pushed before doing getMin(), state of minQ is
minQ : 8, 9, 11, 1, 5

calling getMin() will return 8.

- Anonymous January 03, 2011 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 votes

minQ should be
8
8 9
8 9 11
8 9 11 14
1 8 9 11 14
1 5

- anonymous January 03, 2011 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

a slight modification would be when we push an element in the main queue we check for its position in the min queue
and discard all the elements in the min queue ahead of it.

i.e in your example when you push 1
it is smaller than all the elements in the min queue so remove all
so you min queue would look like : 1
and when you add 5 it would be :1 5
and if you next add 7 then it would be : 1 5 7
and then if you add 3 it would be: 1 3

here get the minimum from the front of the min queue
however, this increases the complexity of the original push operation to O(n) as now you would be scanning for its appropriate position in the min queue

- ketz January 12, 2011 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 vote

use one queue and two min queues:

Queue q, minq1, minq2;
isMinq1Current=true;

void push(int a)
{
  q.push(a);
  if(isMinq1Current)
  {
    if(minq1.empty) minq1.push(a);
    else
    {
      while(!minq1.empty && minq1.top<=a) minq2.push(minq1.pop());
      minq2.push(a);
      while(!minq1.empty) minq1.pop();
      isMinq1Current=false;
    }
  }
  else
  {
    //mirror if(isMinq1Current) branch. 
  }
}

int pop()
{ 
  int a = q.pop();
  if(isMinq1Current)
  {
    if(a==minq1.top) minq1.pop();
  }
  else
  {
    //mirror if(isMinq1Current) branch.    
  }

  return a;
}

- jiangok January 03, 2011 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 votes

Algorithm only to find_min in constant time. insert and delete should be simple.

class Node
{
Object data;
Node next;
Node previousMin;
}

Node globalMin;

insert(Node in)
{
in.previousMin = globalMin;
if(in.data is less than globalMin.data)
{
globalMin = in;
}

.......
}

delete()
{
if(front.data is equal to globalMin.data)
{
globalMin = front.previousMin;
}

......
}

Node findMin()
{
return globalMin;
}

- Anonymous January 04, 2011 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

@Anonymous :
In your delete function :
globalMin = front.previousMin; I think this logic is wrong. Since for every element, element.previousMin is an element which lies before the present element in the queue. So when current element is deleted its previousMin would have already been deleted according to your insert code logic.

Also in your insert function : If a sample input is 3 5 4 .. what is the minimum element once 3 is deleted ?

- genthu January 06, 2011 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

Genthu, you right, the above code will fail.

- Bandicoot January 20, 2011 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 vote

Anonymous, I also ended up with similar solution like urs have globalmin variable define and set its value no need to keep seperate stack for it

- Developer January 06, 2011 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 votes

@Anonymous :
In your delete function :
globalMin = front.previousMin; I think this logic is wrong. Since for every element, element.previousMin is an element which lies before the present element in the queue. So when current element is deleted its previousMin would have already been deleted according to your insert code logic.

Also in your insert function : If a sample input is 3 5 4 .. what is the minimum element once 3 is deleted ?

- genthu January 06, 2011 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 vote

@Anonymous :
In your delete function :
globalMin = front.previousMin; I think this logic is wrong. Since for every element, element.previousMin is an element which lies before the present element in the queue. So when current element is deleted its previousMin would have already been deleted according to your insert code logic.

Also in your insert function : If a sample input is 3 5 4 .. what is the minimum element once 3 is deleted ?

- genthu January 06, 2011 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 vote

@Anonymous : Sorry for multiple postings

- genthu January 06, 2011 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 vote

Hey, I might be wrong on this and refining my concepts but I have been thinking about this problem :

For getMin() , how about if we use Fibonaaci Heap.
getMin() and Insert is 0(1).
The only caveat to it is if we pop that element which is currently minimum, we will have to delete it and the amortized complexity for delete operation is log n.

- SJ January 08, 2011 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 votes

hey! i think that's a good logic .. you can just maintain a min-heap for finding minimum in O(1) time and you will satisfy all the requirements of the question .. and since it does not talk about time for deletion it would be safe to assume that the solution will still hold good .. when a item is POP'ed from the queue, it simply has to be found and deleted from the min-heap!

- RandomSurfer January 30, 2011 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 vote

In my humble opinion, this is simply not possible. The only scenario where this might be somehow feasible would be one where the keys could only have a relatively low number of different values. In this case, the algorithm would maintain an array of buckets (one per possible value) with nonempty buckets also forming a sorted search tree. Then there would be a log(V) factor in the complexities, where V is the number of diferent values (hopefully much lesser than N)

- Anonymous January 28, 2011 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 votes

I think the magic here is that once an element is compared to a younger member in the queue and found lacking, you can mark it as a "permanent loser." By the time an element works itself through the queue, it will likely have earned the "permanent loser" status, and you can pop it off the queue with no ceremony (zero time). A few elite nodes will work their way entirely through the queue without being displaced by younger elements, and their retirement will cause a flurry of successors to duke it out for the throne, but this power struggle is amortized by all the previous permanent losers retiring without ceremony. Also, the power struggle will cause roughly half of the participants to permanently retire, knowing that they already lost a younger element that will out-survive them by definition of the queue's inherent properties.

- showell30@yahoo.com March 19, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 vote

/* struct to hold the actual data */
typedef struct node
{
int data;
struct node *next;
struct node *nextMin;

}Node;

/* Queue head */
typedef struct
{
Node *head;
Node *tail;
Node *min;

}Queue;


/* Initialize the queue */
inline void initQueue(Queue *q)
{
q->tail = q->head = q->min = NULL;
}

/*check for queue empty */
inline int isEmpty(Queue *q)
{
return !q || q->head == NULL;
}

/* Deallocates the queue */
void endQueue(Queue *q)
{
if(isEmpty(q))
return;

Node *h = q->head;
Node *tmp;

while(h)
{
tmp = h;
h = h->next;
free(tmp);
}
}

/* inserts element at the end */
void enqueue(Queue *q, int el)
{
if(!q)
return;

Node *node = calloc(sizeof(Node), 1);
assert(node != NULL);
node->data = el;

if(!q->head) /* if Q empty */
{
q->min = q->head = q->tail = node;
}
else
{

if(q->min->data >= el) /*Update current min */
{
q->min = node;
}
else
{
Node *min = q->min;

while(min)
{
if(!min->nextMin || min->nextMin->data >= el)
{
min->nextMin = node;
break;
}
min = min->nextMin;
}
}

q->tail->nextMin = q->tail->next = node; /* attach at the end */
q->tail = node; /* move tail */

}

}

/*pops the element at the beginning */
int dequeue(Queue *q, int *el)
{
if(isEmpty(q))
return -1;

*el = q->head->data;

Node *tmp = q->head;

q->head = q->head->next; /* move head */

if(q->min == tmp) /* if head is the min, move the min */
{
q->min = tmp->nextMin;
}

free(tmp);

return 0;
}

/* Returns the minimum element in Q */
inline int findMin(Queue *q, int *el)
{
if(isEmpty(q))
return -1;

*el = q->min->data;

return 0;
}

/* prints the queue to stdout */

- Anonymous March 27, 2011 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 votes

For input 1 2 3 4 5

enqueue will execute in O(n) time in the loop:

while(min)
{
if(!min->nextMin || min->nextMin->data >= el)
{
min->nextMin = node;
break;
}
min = min->nextMin;
}
}

- sagarv April 25, 2011 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 vote

Here is my Code

include <string.h>
struct  node_s  {
                        int             minNext;
                        int             minPrev;
                        int             value;
                };


typedef struct{
        struct  node_s  *pBase;
        unsigned        int     rd;
        unsigned        int     wr;
        unsigned        int     len;

        unsigned        int     minHdr;
        unsigned        int     minTail;

        unsigned        int     counts; /*Debug Usage*/
} minQueueList;
void    initQueue(minQueueList  *pList, unsigned        int depth)
{
        if(NULL==pList||1>depth)
                return;
        memset((void*)pList, 0, sizeof(minQueueList));
        pList->pBase=malloc(sizeof(struct node_s)*(depth+1));
        if(NULL==pList->pBase)
                return;

        pList->len=depth+1;
        /*Following means that the list is empty*/
        pList->rd=0;
        pList->wr=0;
        pList->minHdr=depth+1;
        pList->minTail=pList->minHdr;
}

int     pushRearQueue(minQueueList *pList, int value)
{
        unsigned        int     nextWr;
        unsigned        int     curWr;
        unsigned        int     count;
        if(NULL==pList)
                return -1;
        curWr=pList->wr;
        nextWr=(pList->wr+1)%pList->len;
        if(nextWr==pList->rd)
        {
                // the queue is FULL
                return -1;
        }else
        {
                memset(pList->pBase+curWr, 0, sizeof(pList->pBase[curWr]));
                pList->pBase[curWr].value=value;
                pList->pBase[curWr].minNext=pList->len;/*NO NEXT FOR LATEST ONE ITEM*/
                if(pList->minHdr==pList->len)
                {
                        //no hdr at all before
                        pList->minHdr=curWr;
                        pList->minTail=curWr;
                        pList->pBase[curWr].minPrev=pList->len;/*NO PREV FOR  FIRST ITEM*/
                        pList->counts++;
                }else
                {
                        /*here we need to add new element*/
                        for(count=pList->minTail;1;)
                        {
                                pList->counts++;
                                if(pList->pBase[count].value>value)
                                {
                                        if(count==pList->minHdr)
                                        {
                                                pList->minHdr=curWr;
                                                pList->pBase[curWr].minPrev=pList->len;
                                                break;
                                        }else
                                        {
                                                count=pList->pBase[count].minPrev;
                                        }
                                }else
                                {
                                        pList->pBase[count].minNext=curWr;
                                        pList->pBase[curWr].minPrev=count;
                                        break;
                                }
                        }
                        pList->minTail=curWr;
                }
                pList->wr=nextWr;
        }
        return 0;
}

int     popFrontQueue(minQueueList* pList, int *pValue)
{
        if(NULL==pList || NULL==pValue)
                return -1;
        if(pList->wr==pList->rd)
                return -1; /* Empty Here*/
        *pValue=pList->pBase[pList->rd].value;
        if(pList->rd==pList->minHdr)
        {
                pList->minHdr=pList->pBase[pList->minHdr].minNext;
                if(pList->minHdr==pList->len)
                        pList->minTail=pList->len; /*That is the case it is empty now*/
        }
        pList->rd=(pList->rd+1)%pList->len;
}

int     getMinQueue(minQueueList *pList, int *pValue)
{
        if(NULL==pList || NULL==pValue|| pList->minHdr==pList->len)
                return -1;
        *pValue=pList->pBase[pList->minHdr].value;
}


/*this api is used for the verification usage*/
int     getMinQueueDummy(minQueueList *pList, int *pValue)
{
        int     minValue;
        int     temp;
        if(NULL==pList || NULL==pValue || (pList->rd==pList->wr))
                return  -1;
        minValue=pList->pBase[pList->rd].value;
        temp=pList->rd;
        while(temp!=pList->wr)
        {
                if(minValue>pList->pBase[temp].value)
                        minValue=pList->pBase[temp].value;
                temp=(temp+1)%pList->len;
        }
        *pValue=minValue;
}

- chenlc626 February 24, 2013 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 votes

My Analyze is that the addRear operation will cost O(2) in average. Because you can find the number of min list operation + the left over in the min list after each addRear operation will not exceed the twice number of total addRear operations. Although the worse case would be the queue depth.

- chenlc626 February 24, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

Here is the test code:

include "minqueue/minqueue.c"

int main()
{
        int len, value, minValue;
        minQueueList    minList;
        srandom(1);

        initQueue(&minList, 256);
        printf("Push consumes :");
        for(len=0;len<256;len++)
        {
                value=minList.counts;
                pushRearQueue(&minList, random()%(1000));
                if(!(len%16))
                        printf("\n");
                printf("%d ", minList.counts-value);

        }
        /*
        for(len=0;len<256;len++)
                {
                        getMinQueue(&minList, &value);
                        getMinQueueDummy(&minList, &minValue);
                        if(value!=minValue)
                                printf("ERROR dummy MIn is %d while quick min is %d\n",
                                        minValue, value);
                        printf("Min is %d now ", value);
                        popFrontQueue(&minList, &value);
                        printf(": Pop value is %d\n", value);
                }
        */
        printf("\nPush Consumes %d counts of min list op in total for %d number queue addRear: Average is %f\n", minList.counts, 256, minList.counts/256.0);

}

- chenlc626 February 24, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

Here is the test results:
Push consumes :
1 1 2 1 2 3 1 1 1 3 3 2 1 2 1 1
3 2 2 1 2 1 1 2 1 2 1 7 2 1 1 2
5 1 1 1 1 1 6 1 1 1 1 1 2 2 5 1
2 1 1 1 6 1 2 2 2 2 1 2 1 1 6 1
1 1 1 2 5 1 3 1 2 2 1 3 1 3 4 1
2 1 2 2 1 2 2 1 1 4 1 5 1 2 1 6
1 1 3 1 1 2 3 2 1 1 4 1 2 1 1 1
5 2 1 1 1 2 2 1 4 1 7 1 2 1 2 1
1 1 4 1 3 3 1 1 4 1 3 1 2 1 2 1
4 5 1 1 1 1 4 3 1 2 2 2 1 2 1 2
3 1 1 5 1 2 3 1 4 1 2 1 1 3 2 2
2 1 1 1 3 1 2 4 1 1 1 7 1 2 4 1
2 1 1 3 3 5 1 2 1 3 1 1 2 2 1 1
1 3 6 1 2 1 1 1 5 1 2 3 1 1 1 2
2 2 2 5 1 2 2 4 1 2 2 1 2 1 3 1
1 1 5 1 1 2 3 1 3 1 2 1 2 1 1 6
push Consumes 502 counts of min list op in total for 256 number queue addRear: Average is 1.960938

- chenlc626 February 24, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 votes

I have test 10000 items, the result is same, in average is less than 2 operation.

- chenlc626 February 24, 2013 | Flag
Comment hidden because of low score. Click to expand.
0
of 0 vote

My Analyze is that the addRear operation will cost O(2) in average. Because you can find the number of min list operation + the left over in the min list after each addRear operation will not exceed the twice number of total addRear operations.

- chenlc626 February 24, 2013 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 vote

My metaphor for this is that you have a league of 1000 fighters or so, and they have constant fighting ability throughout their career. Fighting ability is transitive. Also, the most experienced fighter always retires first, but you have a variable influx of new fighters.

99.9% of fighters are not elite. They come into the league, try to fight the most elite fighter, and lose. Their record goes to 0 and 1. Severely embarrassed, they don't waste their time fighting other inferior fighters; they instead get on the waiting list to become the next most elite fighter after the most elite fighter retires.

Soon, a non-elite fighter retires with no hoopla, and he's not on active waiting lists either. Then another non-elite fighter retires again with no hoopla. And another. And another. All the non-elite fighters have lost to somebody younger in their career, and that younger fighter has already been crowned or similarly defeated.

Finally, an elite fighter retires. During his reign since the latest elite fighter retired, maybe 100 new fighters have come into the league, lost to him, and stayed on the waiting list. There are a whole bunch of fighters who have only lost to the retiring elite fighter, and they establish a fighting ladder to find the new successor. Losers who lose to older fighters get to stay on the waiting list, but if an older fighter loses, he is deemed to be a permanent loser, because the younger fighter will always be around to beat them. Only one fighter remains undefeated after the ladder works out, and he's the new elite guy. Fighter who lost to older fighters don't get elite status, but they stay on the waiting list.

My intuition says this system fairly keeps one fighter in the elite position, and it minimizes the need for unnecessary fights, but I'm not sure it's even amortized O(1) fights.

- showell30@yahoo.com March 19, 2013 | Flag Reply
Comment hidden because of low score. Click to expand.
0
of 0 vote

Here's the final solution:
q = queue
min = double-ended queue

void push(int x) {
 q.push(x);
 while(!min.empty() && min.rear() > x) {
  min.deleteRear(); 
 }
 min.push(x);
}

int pop() {
 int x = q.pop();
 if(x == min.front()) {
  min.pop(); //removes front element
 }
 return x;
}

int get_min() {
 return min.front();
}

- Ashok Bathini October 07, 2014 | Flag Reply


Add a Comment
Name:

Writing Code? Surround your code with {{{ and }}} to preserve whitespace.

Books

is a comprehensive book on getting a job at a top tech company, while focuses on dev interviews and does this for PMs.

Learn More

Videos

CareerCup's interview videos give you a real-life look at technical interviews. In these unscripted videos, watch how other candidates handle tough questions and how the interviewer thinks about their performance.

Learn More

Resume Review

Most engineers make critical mistakes on their resumes -- we can fix your resume with our custom resume review service. And, we use fellow engineers as our resume reviewers, so you can be sure that we "get" what you're saying.

Learn More

Mock Interviews

Our Mock Interviews will be conducted "in character" just like a real interview, and can focus on whatever topics you want. All our interviewers have worked for Microsoft, Google or Amazon, you know you'll get a true-to-life experience.

Learn More