Thursday, March 6, 2014

The Five Essential Phone-Screen Questions

原始链接: 

The Five Essential Phone-Screen Questions

Stevey's Drunken Blog Rants™
I've been on a lot of SDE interview loops lately where the candidate failed miserably: not-inclined votes all around, even from the phone screeners who brought the person in initially.
It's usually pretty obvious when the candidate should have been eliminated during the phone screens. Well, it's obvious in retrospect, anyway: during the interviews, we find some horrible flaw in the candidate which, had anyone thought to ask about it during the phone screen, would surely have disqualified the person.
But we didn't ask. So the candidate came in for interviews and wound up wasting everyone's time.

Antipatterns

I've done informal postmortems on at least a hundred phone screens, many of them my own. Whenever a candidate bombs the interviews, I want to know what went wrong with the screen. And guess what? A pattern has emerged. Two patterns, actually.
The first pattern is that for most failed phone screens, the candidate did most of the talking. The screener only asked about stuff on the candidate's resume, and the candidate was able to talk with passion and enthusiasm about this incredibly cool thing they did, blah blah blah, and the screener was duly impressed.
That's how many/most phone screens go wrong.
The right way to do a phone screen is to do most of the talking, or at least the driving. You look for specific answers, and you guide the conversation along until you've got the answer or you've decided the candidate doesn't know it. Whenever I forget this, and get lazy and let the candidate drone on about their XML weasel-pin connector project, I wind up bringing in a dud.
The second pattern is that one-trick ponies only know one trick. Candidates who have programmed mostly in a single language (e.g. C/C++), platform (e.g. AIX) or framework (e.g. J2EE) usually have major, gaping holes in their skills lineup. These candidates will fail their interviews here because our interviews cover a broad range of skill areas.
These two phone screen (anti-)patterns are related: if you only ask the candidate about what they know, you've got a fairly narrow view of their abilities. And you're setting yourself up for a postmortem on your phone screen.

Acid Tests

In an effort to make life simpler for phone screeners, I've put together this list of Five Essential Questions that you need to ask during an SDE screen. They won't guarantee that your candidate will be great, but they will help eliminate a huge number of candidates who are slipping through our process today.
These five areas are litmus tests -- very good ones. I've chosen them based on the following criteria:
1) They're universal - every programmer needs to know them, regardless of experience, so you can use them in all SDE phone screens, from college hires through 30-year veterans.
2) They're quick - they're areas that you can probe very quickly, without eating too much into your phone-screen time. Each area can be assessed with 1 to 5 minutes of "weeder questions", and each area has almost unlimited weeder questions to choose from.
3) They're predictors - there are certain common "SDE profiles" that are easy to spot because they tend to fail (and I mean really fail) in one or more of these five areas. So the areas are amazingly good at weeding out bad candidates.
You have to probe all five areas; you can't skip any of them. Each area is a proxy for a huge body of knowledge, and failing it very likely means failing the interviews, even though the candidate did fine in the other areas.
Without further ado, here they are: The Five Essential Questions for the first phone-screen with an SDE candidate:
1) Coding. The candidate has to write some simple code, with correct syntax, in C, C++, or Java.
2) OO design. The candidate has to define basic OO concepts, and come up with classes to model a simple problem.
3) Scripting and regexes. The candidate has to describe how to find the phone numbers in 50,000 HTML pages.
4) Data structures. The candidate has to demonstrate basic knowledge of the most common data structures.
5) Bits and bytes. The candidate has to answer simple questions about bits, bytes, and binary numbers.
Please understand:   what I'm looking for here is a total vacuum in one of these areas. It's OK if they struggle a little and then figure it out. It's OK if they need some minor hints or prompting. I don't mind if they're rusty or slow. What you're looking for is candidates who are utterly clueless, or horribly confused, about the area in question.
For example, you may find a candidate who decides that a Vehicle class should be a subclass of ParkingGarage, since garages contain cars. This is just busted, and it's un-fixable in any reasonable amount of training time.
Or a candidate might decide, when asked to search for phone numbers in a bunch of text files, to write a 2000-line C++ program, at which point you discover they've never heard of "grep", or at least never used it.
When a candidate is totally incompetent in one of these Big Five areas, the chances are very high that they'll bomb horribly when presented with our typical interview questions. Last week I interviewed an SDE-2 candidate who made both of the mistakes above (a vehicle inheriting from garage, and the 2000-line C++ grep implementation.) He was by no means unusual, even for the past month. We've been bringing in many totally unqualified candidates.
The rest of this document describes each area in more detail, and gives example questions, and solutions.
Area Number One: Coding
The candidate has to write some code. Give them a coding problem that requires writing a short, straightforward function. They can write it in whatever language they like, as long as they don't just call a library function that does it for them.
It should be a trivial problem, one that even a slow candidate can answer in 5 minutes or less.
(If the candidate seems insulted by the thought of having to get their hands dirty with a trivial coding question, after all their years of experience, patents, etc., tell them it's required procedure and ask them to humor you. If they refuse, tell them we only interview people who can demonstrate coding skills over the phone, thank them for their time, and end the call.)
Give them a few minutes to write and hand-simulate the code. Tell them they need to make it syntactically correct and complete. Make them read the code to you over the phone. Copy down what they read back. Put it into your writeup. If they're sloppy, or don't want to give you exact details, give them one more chance to correct it, and then go with Not Inclined.
(Note added 10/6/04) -- another good approach being used by many teams is to give the candidate "homework". E.g. you can give them an hour to solve some coding problem (harder than the ones below) and email the solution to you. Works like a charm. Definitely preferable to reading code over the phone.
Anyway, here are some examples. I've given solutions in Java, mostly. I've gone back and forth on accepting solutions in other languages (e.g. Ruby, Perl, Python), and I've decided that candidates need to be able to code their answers in C, C++ or Java. It's wonderful if they know other languages, and in fact those who do tend to do a lot better overall. But to be an Amazon SDE, you need to prove you can do C++ or Java first.
Example 1:   Write a function to reverse a string.
Example Java code:

    public static String reverse ( String s ) {
        int length = s.length(), last = length - 1;
        char[] chars = s.toCharArray();
        for ( int i = 0; i < length/2; i++ ) {
            char c = chars[i];
            chars[i] = chars[last - i];
            chars[last - i] = c;
        }
        return new String(chars);
    }
Example output for "Madam, I'm Adam":   madA m'I ,madaM
Example 2:  Write function to compute Nth fibonacci number:
Java and C/C++:
    static long fib(int n) {
        return n <= 1 ? n : fib(n-1) + fib(n-2);
    }
(Java Test harness)
    public static void main ( String[] args ) {
        for ( int i = 0; i < 10; i++ ) {
            System.out.print ( fib(i) + ", " );
        }
        System.out.println ( fib(10) );
    }
(C/C++ Test Harness)
    main () {
        for ( int i = 0; i < 10; i++ ) {
            printf ( "%d, ", fib(i) );
        }
        printf ( "%d\n", fib(10) );
    }
Test harness output:  
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55
Example 3:  Print out the grade-school multiplication table up to 12x12
Java: (similar for C/C++)
    public static void multTables ( int max )
    {
        for ( int i = 1; i <= max; i++ ) {
            for ( int j = 1; j <= max; j++ ) {
                System.out.print ( String.format ( "%4d", j * i ));
            }
            System.out.println();
        }
    }
Example output:
   1   2   3   4   5   6   7   8   9  10  11  12
   2   4   6   8  10  12  14  16  18  20  22  24
   3   6   9  12  15  18  21  24  27  30  33  36
   4   8  12  16  20  24  28  32  36  40  44  48
   5  10  15  20  25  30  35  40  45  50  55  60
   6  12  18  24  30  36  42  48  54  60  66  72
   7  14  21  28  35  42  49  56  63  70  77  84
   8  16  24  32  40  48  56  64  72  80  88  96
   9  18  27  36  45  54  63  72  81  90  99 108
  10  20  30  40  50  60  70  80  90 100 110 120
  11  22  33  44  55  66  77  88  99 110 121 132
  12  24  36  48  60  72  84  96 108 120 132 144
Example 4:  Write a function that sums up integers from a text file, one int per line.
Java:
    public static void sumFile ( String name ) {
        try {
            int total = 0;
            BufferedReader in = new BufferedReader ( new FileReader ( name ));
            for ( String s = in.readLine(); s != null; s = in.readLine() ) {
                total += Integer.parseInt ( s );
            }
            System.out.println ( total );
            in.close();
        }
        catch ( Exception xc ) {
            xc.printStackTrace();
        }
    }
Example 5:  Write function to print the odd numbers from 1 to 99.
C/C++:
    void printOdds() {
        for (int i = 1; i < 100; i += 2) {
            printf ("%d\n", i); // or cout << i << endl;
        }
    }
Java:

    public static void printOdds() {
        for (int i = 1; i < 100; i += 2) {
            System.out.println ( i );
        }
    }
Example 6:  Find the largest int value in an int array.
Java:
    public static int largest ( int[] input ) {
    int max = Integer.MIN_VALUE;
    for ( int i = 0; i < input.length; i++ ) {
        if ( input[i] > max ) max = input[i];
        }
        return max;
    }
Example 7:  Format an RGB value (three 1-byte numbers) as a 6-digit hexadecimal string.
Java:
    public String formatRGB ( int r, int g, int b ) {
        return (toHex(r) + toHex(g) + toHex(b)).toUpperCase();
    }

    public String toHex ( int c ) {
        String s = Integer.toHexString ( c );
        return ( s.length() == 1 ) ? "0" + s : s;
    }
Or in Java 1.5:
    public String formatRGB ( int r, int g, int b ) {
        return String.format ( "%02X%02X%02X", r, g, b );
    }
Example output for (255, 0, 128):  
You can ask any question you like; doesn't have to be one of the ones above. They're just examples.
Some properties of a good weeder phone-screen coding question are:
  1. It's simple. It has to be something that you should be able to solve, trivially, in about 2 minutes or less. Not too tricky. Basic stuff.
  2. You've solved it. You shouldn't ask a question unless you've solved it yourself recently, so you know it's a reasonable question, and you can evaluate their answer to it. You should consider coding it yourself during the time you've given them to do it.
  3. It has loops or recursion. Recursion is actually preferable. Being able to reason recursively or inductively is important for many areas of computing, including using heirarchical data representations (e.g. XML), distributed computing, searching, and sorting. Many candidates simply can't think recursively, and this often goes undetected until interview-time. Try to find out at compile-time! Er, phone-screen time, that is.
  4. It has formatted output. This is a basic skill, useful for debugging, simple report generation, and lots of other things. "printf" is a universal standard; it exists in C, C++, Java, Perl, Ruby, Python, and virtually every other mainstream language, at least as a library call. Like file I/O, it's a good indicator as to whether the candidate has written "real" code before.
  5. It has text-file I/O. Candidates who have worked in frameworks for too long often become unable to function as programmers outside that framework. Not being able to do simple file I/O is a common indicator that they've grown overly dependent on a particular framework.
It's hard to cover all these things and still be a short weeder question. If you think of a question that has all these properties, let me know.
Area Number Two: Object-Oriented Programming
We shouldn't hire SDEs (arguably excepting college hires) who aren't at least somewhat proficient with OOP. I'm not claiming that OOP is good or bad; I'm just saying you have to know it, just like you have to know the things you can and can't do at an airport security checkpoint.
Two reasons:
1) OO has been popular/mainstream for more than 20 years. Virtually every programming language supports OOP in some way. You can't work on a big code base without running into it.
2) OO concepts are an important building block for creating good service interfaces. They represent a shared understanding and a common vocabulary that are sometimes useful when talking about architecture.
So you have to ask candidates some OO stuff on the phone.
a) Terminology
The candidate should be able to give satisfactory definitions for a random selection of the following terms:

  1. class, object (and the difference between the two)
  2. instantiation
  3. method (as opposed to, say, a C function)
  4. virtual method, pure virtual method
  5. class/static method
  6. static/class initializer
  7. constructor
  8. destructor/finalizer
  9. superclass or base class
  10. subclass or derived class
  11. inheritance
  12. encapsulation
  13. multiple inheritance (and give an example)
  14. delegation/forwarding
  15. composition/aggregation
  16. abstract class
  17. interface/protocol (and different from abstract class)
  18. method overriding
  19. method overloading (and difference from overriding)
  20. polymorphism (without resorting to examples)
  21. is-a versus has-a relationships (with examples)
  22. method signatures (what's included in one)
  23. method visibility (e.g. public/private/other)
These are just the bare basics of OO. Candidates should know this stuff cold. It's not even a complete list; it's just off the top of my head.
Again, I'm not advocating OOP, or saying anything about it, other than that it's ubiquitious so you have to know it. You can learn this stuff by reading a single book and writing a little code, so no SDE candidate (except maybe a brand-new college hire) can be excused for not knowing this stuff.
I draw a distinction between "knows it" and "is smart enough to learn it." Normally I allow people through for interviews if they've got a gap in their knowledge, as long as I think they're smart enough to make it up on the job.
But for these five areas, I expect candidates to know them. It's not just a matter of being smart enough to learn them. There's a certain amount of common sense involved; I can't imagine coming to interview at Amazon and not having brushed up on OOP, for example. But these areas are also so fundamental that they serve as real indicators of how the person will do on the job here.
b) OO Design
This is where most candidates fail with OO. They can recite the textbook definitions, and then go on to produce certifiably insane class designs for simple problems. For instance:

  • They may have Person multiple-inherit from Head, Body, Arm, and Leg.
  • They may have Car and Motorcycle inherit from Garage.
  • They may produce an elaborate class tree for Animals, and then declare an enum ("Lion = 1, Bear = 2", etc.) to represent the type of each animal.
  • They may have exactly one static instance of every class in their system.
(All these examples are from real candidates I've interviewed in the past 3 weeks.)
Candidates who've only studied the terminology without ever doing any OOP often don't really get it. When they go to produce classes or code, they don't understand the difference between a static member and an instance member, and they'll use them interchangeably.
Or they won't understand when to use a subclass versus an attribute or property, and they'll assert firmly that a car with a bumper sticker is a subclass of car. (Yep, 2 candidates have told me that in the last 2 weeks.)
Some don't understand that objects are supposed to know how to take care of themselves. They'll create a bunch of classes with nothing but data, getters, and setters (i.e., basically C structs), and some Manager classes that contain all the logic (i.e., basically C functions), and voila, they've implemented procedural programming perfectly using classes.
Or they won't understand the difference between a char*, an object, and an enum. Or they'll think polymorphism is the same as inheritance. Or they'll have any number of other fuzzy, weird conceptual errors, and their designs will be fuzzy and weird as well.
For the OO-design weeder question, have them describe:

  1. What classes they would define.
  2. What methods go in each class (including signatures).
  3. What the class constructors are responsible for.
  4. What data structures the class will have to maintain.
  5. Whether any Design Patterns are applicable to this problem.
Here are some examples:

  1. Design a deck of cards that can be used for different card game applications.
    Likely classes: a Deck, a Card, a Hand, a Board, and possibly Rank and Suit. Drill down on who's responsible for creating new Decks, where they get shuffled, how you deal cards, etc. Do you need a different instance for every card in a casino in Vegas?
  2. Model the Animal kingdom as a class system, for use in a Virtual Zoo program.
    Possible sub-issues: do they know the animal kingdom at all? (I.e. common sense.) What properties and methods do they immediately think are the most important? Do they use abstract classes and/or interfaces to represent shared stuff? How do they handle the multiple-inheritance problem posed by, say, a tomato (fruit or veggie?), a sponge (animal or plant?), or a mule (donkey or horse?)
  3. Create a class design to represent a filesystem.
    Do they even know what a filesystem is, and what services it provides? Likely classes: Filesystem, Directory, File, Permission. What's their relationship? How do you differentiate between text and binary files, or do you need to? What about executable files? How do they model a Directory containing many files? Do they use a data structure for it? Which one, and what performance tradeoffs does it have?
  4. Design an OO representation to model HTML.
    How do they represent tags and content? What about containment relationships? Bonus points if they know that this has already been done a bunch of times, e.g. with DOM. But they still have to describe it.
The following commonly-asked OO design interview questions are probably too involved to be good phone-screen weeders:

  1. Design a parking garage.
  2. Design a bank of elevators in a skyscraper.
  3. Model the monorail system at Disney World.
  4. Design a restaurant-reservation system.
  5. Design a hotel room-reservation system.
A good OO design question can test coding, design, domain knowledge, OO principles, and so on. A good weeder question should probably just target whether they know when to use subtypes, attributes, and containment.
Area Number Three: Scripting and Regular Expressions
Many C/C++/Java candidates, even some with 10+ years of experience, would happily spend a week writing a 2,500-line program to do something you could do in 30 seconds with a simple Unix command.
I now pose the following question to ALL candidates, whether on the phone or in an interview, because it eliminates so many of them:
Last year my team had to remove all the phone numbers from 50,000 Amazon web page templates, since many of the numbers were no longer in service, and we also wanted to route all customer contacts through a single page.
Let's say you're on my team, and we have to identify the pages having probable U.S. phone numbers in them. To simplify the problem slightly, assume we have 50,000 HTML files in a Unix directory tree, under a directory called "/website". We have 2 days to get a list of file paths to the editorial staff. You need to give me a list of the .html files in this directory tree that appear to contain phone numbers in the following two formats: (xxx) xxx-xxxx and xxx-xxx-xxxx.
How would you solve this problem? Keep in mind our team is on a short (2-day) timeline.
Here are some facts for you to ponder:

  1. Our Contact Reduction team really did have exactly this problem in 2003. This isn't a made-up example.
  2. Someone on our team produced the list within an hour, and the list supported more than just the 2 formats above.
  3. About 25% to 35% of all software development engineer candidates, independent of experience level, cannot solve this problem, even given the entire interview hour and lots of hints.
I take as much time as necessary to explain the problem to candidates, to ensure that they understand it and can paraphrase the problem requirements correctly.
For the record, I'm not being tricky here. Once candidates start down the wrong path (i.e. writing a gigantic C++ program to open every file and parse character by character, using a home-grown state machine), I stop them, tell them this will take too long, and ask if there are any other possibilities. I ask if there are any tools or utilities that might be of use. I give them plenty of hints, and ultimately I tell them the answer.
Even after I tell them the answer, they often still don't get it.
Here's one of many possible solutions to the problem:

  grep -l -R --perl-regexp "\b(\(\d{3}\)\s*|\d{3}-)\d{3}-\d{4}\b" * > output.txt
But I don't even expect candidates to get that far, really. If they say, after hearing the question, "Um... grep?" then they're probably OK. I can ask them for the approximate syntax for the regular expression to use, and as long as they have a reasonable clue, I'm fine with it. Heck, if they can tell me where they'd look to find the syntax, I'm fine with it.
They can also use find, or write a Perl script (or awk or bash or etc.). Anything that shows they have even the tiniest inkling of why Unix is Unix.
They can even write a Java or C++ program, provided they can actually write an entire working program in, say, half an hour or less, on the board, or at least convince me that they will get it working quickly. But I've only ever had that happen once; an insanely good C++ programmer burned through a 175-line C++ program on the whiteboard that more or less solved it. We made him an offer. But usually they throw in the towel when they find out they have to remember how to do file I/O, or traverse a directory tree.
For what it's worth, this failure mode is unique to Java and C/C++ programmers. Perl programmers laugh and solve it in 30 seconds or less. I have some easy questions that make Perl programmers cry, but this isn't one of them.
In my experience, a programmer who only knows one language (where C and C++ count as one language for this exercise) is usually completely lost in one of these Five Essential Areas.
You don't necessarily have to ask the HTML phone-number question. Another one I used to ask, one that worked equally well, was:
Let's say you're on my team, and I've decided I'm a real stickler for code formatting. But I've got peculiar tastes, and one day I decide I want to have all parentheses stand out very clearly in your code.
So let's say you've got a set of source files in C, C++, or Java. Your choice. And I want you to modify them so that in each source file, every open- and close-paren has exactly one space character before and after it. If there is any other whitespace around the paren, it's collapsed into a single space character.
For instance, this code:

foo (bar ( new Point(x, graph.getY()) ));
Would be modified to look like this:
foo ( bar ( new Point ( x, graph.getY ( ) ) ) ) ;
I tell you (as your manager) that I don't care how you solve this problem. You can take the code down to Kinko's Copies and manually cut and paste the characters with scissors if you like.
How will you solve this problem?
Same thing, more or less. You'd do it with a Unix command like sed (using a regular expression), or do it in your editor using a regex, or write a quick Ruby script, whatever. I'd even accept having them use a source-code formatter, provided they can tell me in detail how to use it, during the interview (to a level of detail that convinces me they've used it before.)
There are all sorts of variations on this problem. Generally you want to come up with a real-life scenario that involves searching text files for patterns, and see if the candidate wants to solve it by writing a giant chunk of C++ or Java code.
Area Number Four: Data Structures
SDE candidates need to demonstrate a basic understanding of the most common data structures, and of the fundamentals of "big-O" algorithmic complexity analysis.
Here's what they need to know about big-O. They need to know that algorithms usually fall into the following performance classes: constant-time, logarithmic, linear, polynomial, exponential, and factorial.
For the standard data structures in java.util, STL, or those built into a higher-level language, they need to know the big-O complexity for the operations on those data structures. Example: they should know that finding an element in a hashtable is usually constant-time, that finding an element in a balanced binary tree is order log(n), that finding an element in a linked list is order N, and that finding an element in a sorted array is order log(n). Similarly for insert/update/delete operations.
And they should be able to explain why each operation falls into a particular complexity class. For instance: "Computing a hash value doesn't depend on the number of items in the hashtable." Or: "you have to search the entire linked list, even if it's sorted, to find an arbitrary element in it." No math needed, no proofs, just explanations.
The (concrete) data structures they absolutely must understand are these:
1) arrays - I'm talking about C-language and Java-language arrays: fixed-sized, indexed, contiguous structures whose elements are all of the same type, and whose elements can be accessed in constant time given their indices.
2) vectors - also known as "growable arrays" or ArrayLists. Need to know that they're objects that are backed by a fixed-size array, and that they resize themselves as necessary.
3) linked lists - lists made of nodes that contain a data item and a pointer/reference to the next (and possibly previous) node.
4) hashtables - amortized constant-time access data structures that map keys to values, and are backed by a real array in memory, with some form of collision handling for values that hash to the same location.
5) trees - data structures that consist of nodes with optional data elements and one or more child pointers/references, and possibly parent pointers, representing a heirarchical or ordered set of data elements.
6) graphs - data structures that represent arbitrary relationships between members of any data set, represented as networks of nodes and edges.
There are, to be sure, many other important data structures one should know about, but not knowing about the six listed above is inexcusable, and grounds for rejection in a phone screen.
Candidates should be able to describe, for any of the data structures above:

  • what you use them for (real-life examples)
  • why you prefer them for those examples
  • the operations they typically provide (e.g. insert, delete, find)
  • the big-O performance of those operations (e.g. logarithmic, exponential)
  • how you traverse them to visit all their elements, and what order they're visited in
  • at least one typical implementation for the data structure
Candidates should know the difference between an abstract data type such as a Stack, Map, List or Set, and a concrete data structure such as a singly-linked list or a hash table. For a given abstract data type (e.g. a Queue), they should be able to suggest at least two possible concrete implementations, and explain the performance trade-offs between the two implementations.
Example weeder questions:
1) What are some really common data structures, e.g. in java.util?
2) When would you use a linked list vs. a vector?
3) Can you implement a Map with a tree? What about with a list?
4) How do you print out the nodes of a tree in level-order (i.e. first level, then 2nd level, then 3rd level, etc.)
5) What's the worst-case insertion performance of a hashtable? Of a binary tree?
6) What are some options for implementing a priority queue?
And so on. Just a few quick questions should cover this area, provided you don't focus exclusively on linear ordered sequences (lists, arrays, vectors and the like).
Area Number Five: Bits and Bytes
This area is fairly contentious, at least inasmuch as people who don't know this area claim you don't need to know it.
(Hint: that's true for everything. Nobody likes to admit they don't know something you need to know. I'll start: I should know more about math; it's inexcusable. I'm doing all kinds of stuff the long, slow, dumb way because of my rusty math skills. But at least I admit it, and I've been studying my math books semi-regularly in an attempt to repair my skills.)
Candidates do need to know about bits and bytes, at least at the level that I'm outlining here. Otherwise they're prone to having an integer-overflow error in their code that brings the website down and costs us millions. Or spending a week trying to decode a serialized object they're debugging. Or whatever. Computers don't have ten fingers; they have one. So people need to know this stuff.
Candidates should know what bits and bytes are. They should be able to count in binary; e.g. they should be able to tell you what 2^5 or 2^10 is, in decimal. They shouldn't stare blankly at you when you ask with 2^16 is. It's a special number. They should know it.
They should know at least the logical operations AND, OR, NOT, and XOR, and how to express them in their favorite/strongest programming language.
They should understand the difference between a bitwise-AND and a logical-AND; similarly for the other operations.
Candidates should know the probable sizes of the primitive data types for a standard 32-bit (e.g. Intel) architecture.
If they're a Java programmer, they should know exactly what the primitive types are (byte, short, int, long, float, double, char, boolean) and, except for boolean, exactly how much space is allocated for them per the Java Language specification.
Everyone should know the difference between signed and unsigned types, what it does to the range of representable values for that type, and whether their language supports signed vs. unsigned types.
Candidates should know the bitwise and logical operators for their language, and should be able to use them for simple things like setting or testing a specific bit, or set of bits.
Candidates should know about the bit-shift operators in their language, and should know why you would want to use them.
A good weeder question for this area is:
Tell me how to test whether the high-order bit is set in a byte.
Another, more involved one is:
Write a function to count all the bits in an int value; e.g. the function with the signature int countBits(int x)
Another good one is:
Describe a function that takes an int value, and returns true if the bit pattern of that int value is the same if you reverse it (i.e. it's a palindrome); i.e. boolean isPalindrome(int x)
They don't have to code the last two, just convince you they'd take the right approach. Although if you have them code it correctly, it can count for your Coding weeder question too.
C/C++ programmers should know about the sizeof operator and how (and why/when) to use it. Actually, come to think of it, everyone should know this.
All programmers should be able to count in hexadecimal, and should be able to convert between the binary, octal, and hex representations of a number.
Special Fast-Track Version
That's it for the Five Essential Phone Screen Questions. Hope ya liked it.
As a special reward for reading this far, here's a special Bonus Feature: a set of all-too-common answers that are almost always indicators of certain failure during our interviews. Even if I'm not on the loop!
Bad Sign #1:
Me:   So! What languages have you used, starting with your strongest?
Them: (briskly) C, C++.
Me:   (long, pregnant pause)
Them: (waiting patiently for me to continue)
Me:   Any others?
Them: Nope. C, C++.

Translation: (in thick Southern drawl)   "We got both kinds of music here: country and western."
Probable failure modes for this candidate:
  Will fail the HTML-phone-number question and the OO design question (but will get the OO terminology definitions mostly right.)
Bad Sign #1a:
Me:   So! What languages are you most familiar/proficient with?
Them: (worried) I've done mostly Java lately.
Me:   (long, pregnant pause)
Them: Yeah, um, Java.
Me:   Any others?
Them: Um, I did C in school a long time ago, but... pretty much mostly Java now.

Translation:   "Country and Western were both too hardcore for me. I got beat up in a bar."
Probable failure modes for this candidate:   Will fail the bits and bytes questions, the HTML-phone-number question, and most of the data structures questions.
Bad Sign #2:
Me:   So! What data structures do we have available to us, as programmers?
Them: Arrays, queues, vectors, stacks, lists, um, linked lists...
Me:   OK, any others?
Them: Um, doubly-linked lists, and, uh, array lists.
Me:   Have you ever used a tree?
Them: Oh! (laughs) Yeah, um, I forgot about those.

Translation:  "My family tree doesn't branch."
Probable failure modes for this candidate:   Very likely to fail data structures questions. Will fail any recursive problem, even a simple one like printing the elements of a linked list recursively. Will fail the HTML-phone-number question, since they obviously haven't ever used Perl if "hash" didn't leap to mind.
Bad Sign #3:
Me:   So! What the the primitive types in Java (or C++)?
Them: Ummmm, there's, um, int. And, uh, double.
Me:   Any others?
Them: Shoot, I'm drawing a blank right now. Um, String?

Translation:  "C made my head hurt.  Java is like sweet, sweet aspirin."
Probable failure modes for this candidate:   Will fail bits and bytes questions, and probably just about everything else as well.
Bad Sign #4:
Me:   So! What text-editor do you use?
Them: Visual Studio.
Me:   OK. What about on Unix?
Them: On Unix I use vi.
Me:   Er, yeah, vi is cool... ever used VIM?
Them: No, just vi. Always worked just fine for me.

Translation:  "Sometimes I type with my elbows when my hands are tired. It's just as fast."
Probable failure modes for this candidate: Will likely fail the HTML-phone-number question. Might pass the interviews, but will need to be scheduled in geologic eras.
Bad Sign #5:
Me:   So! What did you study in your Operating Systems class?
Them: Oh, that was a long time ago. I can hardly remember. Hehe.
Me:   How long ago was it?
Them: 2 years.

Translation:  "I want to use my MBA skills in a dynamic management role. When's lunch?"
Probable failure modes for this candidate:   Will probably fail the coding question. Probably any OS questions, too.
All my Insta-Bad Signs above are cliches, in that I've heard these answers from at least 10 to 15 candidates (per question!), none of whom ever got an offer from us. I tend to ask questions like these as a matter of course now.
Summary
This stuff is the ABC's for programmers. Actually it's only going up through maybe J or K; it's not even halfway through the alphabet. But most programmers out there in the Big Wide World will fail utterly in at least one of these areas.
Please cover all five areas if you're a phone screener. If you're the second screener, ask if you don't see evidence of them in the first screener's notes. (And then follow up and remind the first screener they should have asked these things.)

(Published Sep 28th 2004)


Comments

You can put a spin on your 'reverse a string' coding question - first have them write a func that prints out a C string without looping constructs or using local vars. Then if they get that, ask them to implement a reverse string function in the same manner as the first one. Don't say "use recursion" - let them figure out its straightforward applicability to the problem. That's, IMHO, how you can gauge if they 'think recursively' when lightly nudged in that direction:

void print(char *s) {
  if (*s != 0) {
    putchar(*s);
    print(s+1);
  }
}

void printreverse(char *s) {
  if (*s != 0)
    printreverse(s+1);
    putchar(*s);
  }
}

int main() {
  char *s = "Hello world";
  print(s);
  putchar('\n');
  printreverse(s);
  putchar('\n');
}
Posted by: Martin N. at September 29, 2004 07:39 AM


Nice post Steve - thanks for the checklist.
Would you really accept this answer to the 'Write function to compute Nth fibonacci number' question?

static long fib(int n) {
  return n <= 1 ? n : fib(n-1) + fib(n-2);
}
I'd hope that most candidates would know that (without memoization of results) the naive recursive solution is O(n!) in time. If they made that error in production code it would be and utter disaster (probably large enough to be noticed early by QA, but still...).
If the candidate didn't at least mention this caveat about their solution, I'd prompt them to compare & contrast with alternatives. If they didn't immediately give the iterative solution and explain the big-O difference, that would be a red flag for me.
regards,
Chris
Posted by: Chris N. at September 29, 2004 08:36 PM


Er, a small error in my comment:
>>>I'd hope that most candidates would know that (without memoization of results) the naive recursive solution is O(n!) in time.
I meant "is O(2^n) in time" of course.
Chris (blushing)
Posted by: Chris N. at September 29, 2004 08:39 PM


Yeah, that factorial fibo solution sucks. I'd be very happy if the candidate told me that they could do it tail-recursively with an accumulator parameter, even if I'm not sure you can do a doubly-recursive call tail-recursively. I'd still be happy.
I was thinking of splitting out recursion from basic coding, but that would be Six Essential Areas, and I only have five fingers.
Posted by: Steve Yegge at September 30, 2004 03:42 AM


Interesting post overall...a couple of comments/issues I've seen when doing phone screens:
1. How do you have a candidate read code to you over the phone when the candidate isn't a native English speaker and the phone connection is sub-optimal [I've had this more times than I can remember...]
2. I have some issues with the "scripting" category--it expresses a preference for hacky programmers who prefer speed over maintainability. On our team (Customer Behavior) we're still cleaning up a fair amount of such code that broke the moment a database machine got moved between data centers. Yes, the code was written quickly, but now our managers are wondering why we can't get to a stable production system so quickly. The section also prefers UNIX programmers over, say, people who worked with Windows for an entire career.
Posted by: Dan at October 7, 2004 01:44 AM


> 1. How do you have a candidate read code to you over the phone
> when the candidate isn't a native English speaker and the phone
> connection is sub-optimal [I've had this more times than I can
> remember...]
Me too.
The best approach I've seen is to give the candidate a "homework" question. Give them an hour to code up a solution to some problem and email it to you. Several teams are doing this regularly, with good results.
> 2. I have some issues with the "scripting" category--it
> expresses a preference for hacky programmers who prefer speed
> over maintainability.
I'm sorry if I gave that impression. We don't want those kinds of programmers here, obviously. The five question areas here are a delicate balance. This category is geared towards determining whether someone has the self-sufficiency to be able to respond quickly to emergencies affecting our customers. They still need to have good judgement and good design skills.
I never actually specify that they need to write it as a script. I just give them problems that are best handled that way: emergency queries and backfills, for example. I've had a few folks burn through 200-line Java or C++ apps that solved the problem, right there on the board, and they got offers.
But after 4 1/2 years over in Customer Service, I've found that knowing how to use "grep" and its ilk is a pretty important survival skill.
And we -are- Unix shop, after all. Unix isn't exactly a niche operating system. There are people who've figured out the basics on their own, even if their professional experience has all been with Microsoft technologies.
But feel free to ask whatever works for you!
Posted by: Steve Yegge at October 7, 2004 02:27 AM


The ultra-cool solution to Fib is the closed-form constant time solution. You need floating point and exponentiation, but that's constant time on modern hardware.
There's also an off-by-one error of sorts in your printOdds() functions. i should start at 1, not 0, or the function should be renamed printEvens().
Posted by: Darren V. at October 7, 2004 02:42 AM

Thanks Darren. Obviously extra-cool solutions are bonus points for the candidate.
Fixed the loop typo. It was caused, interestingly, when I corrected my original working code, after someone emailed me and complained about the performance. Originally it did this:

for (int i = 0; i < 100; i++) {
  if (i % 2 != 0) System.out.println(i);
}
The point was to see if the candidate could write something that worked at all, and I was whipping up examples at 3:00am. Had no idea it would get so much attention.
In any case, someone objected to the fact that it wasn't simply incrementing the loop counter by 2, so it performed poorly. So I went and "optimized" it (an optimization that would go undetected by humans or profilers in this case, as it's totally I/O bound), and in my haste, broke it.
I suppose I should claim that I rigged the whole thing as a demonstration of why optimizing stuff that doesn't matter is needlessly risky. Or that I broke it so I could rant about why unit testing is critical, even for your personal blog content.
But really I just made a bad fix. :)
Thanks again.
Posted by: Steve Yegge at October 7, 2004 03:11 AM


Thanks for posting this. I have been trying to improve my interviewing skills, and this looks like a good set of basics for phone screens.
Posted by: Timothy K. at October 11, 2004 11:20 PM


Just an FYI...
I tried using the find-the-phone-numbers question on a candidate yesterday, and she went straight for Java. I was getting ready to slap her down when I noticed that Java 1.4 contains a regular expression engine. With that enhancement, the write-a-complete-program solution becomes more reasonable.
Posted by: Christopher B. at November 17, 2004 09:37 AM

Yep. Most languages these days have (mostly) Perl5 compatible regexp engines. Java's file handling is a bit more cumbersome than it'd be in a scripting language, but not overly so. The problem is more about understanding fundamental pattern-matching tools than it is about scripting or any particular language.
Posted by: Steve Yegge at November 29, 2004 06:52 PM


I'm about to do my first phone screening and this was very helpful.
Maybe I just don't want to admit that I don't have vital information, but I don't know what 2^16 is. Jeff Bezos' phone number? I don't understand the significance of knowing powers of two off the top of one's head.
Posted by: Jason R. at March 22, 2005 10:58 PM


Jason: there are many domains for which knowing binary counting is useful, if not essential.
One is when you're doing stuff that involves a lot of bit- and byte-manipulation; examples include network protocols, writing binary serialization/marshalling code, and reading or reverse-engineering file formats.
Another is when you're doing any sort of memory or pointer coding (or more to the point, debugging) with C/C++ code. It's a lot easier to stare at hex-dumps if you're good at translating between decimal and hexadecimal (and binary).
Another broad class of problems involves data whose byte representation is especially significant. UTF-8 and Unicode are good examples. If you ever need to do internationalization, it can be a big help to have a crystal-clear understanding of the meaning of each bit in a byte, short, or int/word value. Actually, this may just be another sub-example of the first domain I mentioned, but you get the idea.
Lastly (and most importantly; all the other examples I've listed pale to insignificance in comparison to this one), it's important for algorithm time and space estimation. If you're trying to decide what data structure to use for a given data set, and you know off the top of your head that 2^16 is about 65,000, 2^20 is a million, 2^32 is 4 billion, and 2^64 is "big enough", then you'll have an easier time with the decision. Looking at it backwards -- if you use a balanced binary tree (or a log-n search algorithm), you can quickly estimate the base-2 logarithm of your data-set size, which gives you the rough number of steps involved in a lookup operation.
If you don't have a good feel for the growth rate of powers of 2, then a little old man will save your daughter, and you'll grant him anything you want in your kingdom, and he'll say he just wants one grain of rice on the first square of a chessboard, then 2 in the second, 4 on the third, and so on. And then: you'll be all out of rice.
Fortunately, you can memorize this stuff in less time than it took you to post your comment. :)
Posted by: Steve Yegge at March 23, 2005 04:11 AM


This is an *excellent* resource, Steve.
I've noticed that a lot of the Java-steeped interviewees will use Strings in their string reverse solutions like so:

    for (int i = s.length - 1; i >= 0; i--) {
        returnS += s.substring(i, 1);
    }
Most will understand that objects are being allocated and discarded per loop, and will use a StringBuffer to make things "more efficient". Interestingly, a couple of folks couldn't explain _why_ it would be more efficient.
It's helpful to ask how they would design a limited StringBuffer class using just primitive types to try to help dispel library addiction. It can be surprisingly intimidating for some folks, especially when it comes to reallocating arrays. (Ironic, considering the allocation-fest of their original solutions...)
Thus, I recommend it as another coding question -- though just to talk through, not to code over the phone. Alternatively, asking for the reverse() function using just primitives would do the same thing, but without the scariness of dreaded reallocation.


Wednesday, December 4, 2013

Facebook唯一华人总监魏小亮教你如何选择硅谷的IT公司

这里是作者的博客


如何选择硅谷的IT公司?Facebook 移动技术总监、Facebook“新兵营”的领队之一、
负责新员工培训的魏小亮(作者简介请见文章末尾)从四个方面写了四篇博文,给出了
他的详细建议。在获得作者许可后,伯乐在线把四篇博文合成一篇发布,以下是全文。


经过激烈的面试,恭喜你拿到一个硅谷IT公司的Offer了,更可喜的是,有几个公司同
时给你Offer。下一个头疼的事情:如何选择一个最适合自己的公司。

这两年跟不少拿到多个硅谷公司Offer的国内朋友打过电话聊天,觉得不少朋友的决定
因素比较随机——往往几个公司都没有具体了解,觉得差不多也都挺好的,大多数朋友
去工资最高的地方;也有一些去了HR比较主动的公司。最后到了那个公司发现并不是最
适合自己的…… 所以,我想在这里谈谈怎样选择公司。

最关键的一点: 获得每个公司尽量多的信息,根据各个公司的情况和自己的目标,做
理性的决策。HR给你Offer或者还价的时候,千万不要立即接受或者拒绝,可以礼貌地
说 “非常感谢。我非常珍惜这个机会。请让我认真思考一下,再给您答复。” 然后马
上开始你的决策过程:
(一):如何收集公司的信息
(二):决策过程的考虑因素
(三):薪酬和待遇
(四):关于HR

一、如何收集公司的信息
这里介绍一下收集信息的渠道。一个关键点是在收集信息的时候要尽量做到理性而没有
偏见:如果你带有很重的偏见去收集,这个过程本身就变成了收集证据去证明自己的观
点,而不是收集所有的信息去帮助自己决策了。

我想大家可以考虑以下几个信息源:
1、公司的产品和网页 :如果对方是一个产品型的公司(最终客户是普罗大众而不是
Fortune 500公司),一定去看看他们的产品和网页,看看自己对这些东西是不是感兴
趣。每天做自己感兴趣的产品,幸福感会很高,工作的压力会变成动力,绩效奖金也高
,升职也快 —— 硅谷的游戏类公司基本工资往往比其他IT公司相对低一些,原因是有
不少工程师喜欢玩游戏,他们愿意少拿一点薪水去做自己喜欢的游戏产品。其实长远来
看,自己开开心心的工作生活的确很重要,“有钱难买爷高兴”嘛。

2、公司的开源项目和技术会议讨论:不少的科技公司是有自己的开源项目代码库的,
也有不少公司参加一些技术会议。可以看看他们的项目和会议的录像或者幻灯片。从中
可以了解到这个公司的技术水平大概是怎样的,自己能从未来的同事学到什么东西。这
里要注意的是最好找工程师的幻灯片(而不是销售或者HR的幻灯片,那些信息量低而且
更多是广告成分)。

3、找公司里跟自己背景相似的员工:如果有朋友在那个公司工作,一定要找他问问情
况。如果没有,可以跟HR回信说 “在我决定之前,我能不能跟公司里的一个中国工程
师聊一下?我想了解他们来美国的过程和工作生活的情况。” 一般HR是很乐意为你找
这样的工程师的。跟背景相似的员工聊天会让你看到更真实的一面——因为在加入公司
之前,HR往往是主要的沟通渠道;但一个公司的工程师队伍的文化有可能跟HR的文化类
似,更可能截然不同(我自己面试过的几家IT公司,工程师文化最差的公司HR最好,工
程师文化最好的公司HR最差)。加入公司之后,你主要跟工程师合作,所以关键是要看
自己跟工程师是不是合得来。

4、如果有两个公司选择,最好能找到一个在这两个公司都工作过的未来同事,让他们
谈谈两个公司的区别和感想;一般这样的同事能给你一些比较。但要注意的是这样的同
事往往偏向现在的公司(否则HR也不太可能让他跟你谈:),所以有机会的话最好两家
公司都找一个类似情况的同事。

5、找你的未来经理或者部门主管:有些公司是预先已经给你安排了团队和经理的,如
果是在考虑这些公司,最好能跟未来的经理聊一下,看看是不是合得来。HR一般也会支
持这样的安排。

6、找一个你希望成为的人或者你信任的人给建议:如果你身边有一个你希望成为的人
(比如比较成功的师兄师姐,或者公司里面你特别尊重和信任的同事,或者你认为有远
见的长辈),可以请他跟你聊一下,把你收集到信息和你自己的情况和目标做一个分析
,让他给一些建议。”三人行必有我师”,有一个参谋往往能让自己避免低级错误。

(3、4、5 一般需要HR引荐,可能只能选其中的一到两个。我建议至少找一个中国工程
师聊一聊。)
最后我想要强调的是:这个信息收集过程也是你建立新的社交圈的过程。在其中交到的
朋友,无论你如何决定, 都要给他们说一声,感谢他们在这个过程给你的帮助;到了
硅谷安顿下来了可以找他们吃个饭,保持联系……硅谷本身就是一个很小的圈子(或者
说整个硅谷是一个很大的公司),在这个过程里面建立的联系,好好珍惜,以后再换工
作或者有其他机会,你还能继续得到帮助。对于你决定去的公司,帮过忙的同事更要主
动感谢,参加工作之后主动联系,他们往往会是你在新的公司里面的第一批良师益友。

二、决策过程的考虑因素
薪酬待遇是大部分朋友最关心的一点,我在下一篇将详细讲。在考虑薪酬的同时,我认
为最好能全面考虑各方面的因素:
1、个人的目标
你5年以后想做什么?想开一个自己的公司?还是想加入一个创业公司?还是想在某一
个领域做世界一流的技术专家?还是想在一个大公司里面做管理?还是想做一个普通的
工程师,有比较多的个人时间?

如果5年太远还没想,那在下面一两年你想学到什么东西?学到比较全面的Web2.0的技
术运作(但不一定精通), 还是想在一个方向学得很精通,还是想学产品的开发管理?

回答这个问题很重要,因为无论薪水多少加薪多快,我们都会觉得太少;最终的成就感
和幸福感往往来自于自己个人的成长和个人目标的实现。

不同的公司有不同的长处,最好能找到一个公司能给自己的成长提供相应的机会,有可
能达到自己的个人目标。

2、公司的规模、目标和文化
一般来说,公司越小,资源越有限,学到的东西越广泛,但不容易精,容易培养通才;
公司规模大,任何一个子领域的一点点改进就可以带来比较大的回报,更愿意投入资源
让人钻研一个子领域, 容易培养专才。

快速增长的公司会有更多的学习和晋升机会,但淘汰率可能也高;已经成型的大公司不
容易因为业绩问题淘汰,但更有可能整个部门解散。

在专注产品的公司工作,更容易学到产品开发概念,更重视能把握整个开发流程的人才
,只希望在一个领域钻研的人才则不一定很开心;专注技术的公司则相反。

工程师文化很强的公司把工程师看成创造机会的原动力,聚集了很多顶尖人才,工资高
,同时对个人创造力和主动性要求也高;销售文化很强的公司则往往把工程师看作成本
,工资能压则压,但工程师往往只需要跟进销售团队的反馈意见,不要求很强的主动性。

3、等级、业绩评估和晋升过程
如果公司有分工程师等级,可以问Recruiter你的等级是多少,整个公司的工程师等级
是怎样定义和划分的?不同公司的等级在数字上不一样,如果你有多个offers,可以问
HR不同公司的等级是怎样换算的。

如果你有朋友在对应的公司,或者Recruiter为你找到公司的工程师或者经理来讨论,
可以问一下他们公司的文化,工程团队如何做业绩评估,晋升的过程是怎么样的,谁(
peer还是manager)决定业绩和晋升? 他们对公平性的看法如何。

4、职业道路
跟工程师或者经理讨论的时候可以问一下公司为员工提供的职业路线:如果做得好,
两年之后或者五年之后在公司里面会有什么样的职业道路可选择? 公司会为你的职业
规划提供什么样的培训和支持?是否支持员工到大学里面在职学习? 是否可以在公司
内改变职业路线?跨部门调动是否有很高的门槛?

5、 文化环境
如果能跟一个中国工程师或者经理讨论,可以讨论一下中国文化背景的员工在公司的状
态如何(比如在工程师里的比例是多少,在经理的比例又是多少);普遍遇到的问题和
瓶颈是什么?公司有没有一个氛围能帮助刚从中国来到的新员工适应语言、工作、生活
和文化的改变?

如果希望以后在美国长久生活,可以问公司对绿卡的申请是不是很支持?一般要多长时
间才能开始(有些公司需要工作一两年才开始申请绿卡,有些工作开始之后几周就可以
申请);一般从开始申请到140的批准需要多长时间——有些大公司可能经常有部门解
散的情况(美国移民法律是一旦有部门解散,绿卡申请就会暂停,整个过程会拖得很长
)。

不同的公司对这些问题的回答都不一样。很难绝对地说哪个公司更好,关键是哪个公司
更适合你的目标。

三、薪酬和待遇
美国公司的薪酬和福利五花八门,每个公司都别出心裁地想一些与众不同的福利。 HR
会强调自己公司的特别福利,让你觉得比其他公司好,轻易接受了。我的建议是在跟HR
谈offer的时候着重收集信息,不要当场做决定。回家把所有的薪酬和福利都列出来,
然后综合其他更重要的因素(见上一篇),跟自己信任的人讨论一下,最后做一个理性
的决定,再回复HR。

下面把一些常见的薪酬和福利介绍一下:
1、基本工资。基本工资是给雇员的稳定可预测收入。

工程师的基本工资一般按每年收入多少来算。如果公司到了一定规模(一般一百人以上
),内部都有工程师等级——能力越强(或者有的公司是资历越深)的工程师等级越高
。基本工资往往与工程师等级挂钩。

大部分的新型公司工资只与等级挂钩,同一个级别的工资相差不会太大(比如+/- 15%
的浮动)。在这样的公司里面,如果你觉得你是一个非常上进的人,起薪多一点少一点
并不是很重要——因为如果很快能被升级,那你的工资就会跟新的一级挂钩,跟起薪没
有关系;另一方面,有些公司为了提高竞争力,可能开出一个比较高的起薪(高于相应
级别的最高工资),但当你升了一级的时候,你的工资还是会按照公司平均水平跟新的
一级挂钩, 升级所得到的实际加薪很少。总之,起薪的差异只影响第一次升级之前的
收入。

另外一些公司(我听到的都是非硅谷的传统大公司) ,工资是按照每年比去年涨一定
的百分比来算的,这样的公司起薪很重要——因为起薪低了每年按去年的百分比涨,都
要吃亏。

最好能跟HR讨论一下公司对薪酬的政策是哪一种。

2、奖金。奖金是短期激励手段——一般每年或者每半年做一次业绩评估,决定奖金数
目,业绩越好的雇员奖金越多。

具体的奖金计算方式可以跟HR讨论一下奖金的计算方式。一种比较常见的方式是由三个
系数决定:
A、基本目标系数  (Target Bonus Rate):往往根据工程师级别来定。级别越高,这个
系数可能越高,与当年业绩无关;
B、公司业绩系数 (Company multiplier):每半年或者每年由公司领导层或者董事会决
定整个公司的业绩,如果业绩正常,就是100%;如果业绩高于预期,系数就高于100%;
反之亦然;
C、个人业绩系数(Individual multiplier):每半年或者每年由经理、或者同事互评
和自评对自己的业绩进行评估,如果你的业绩跟你的级别预期一致,就是100%,高了或
者低了系数对应变化。

最后的奖金由三个系数和基本工资相乘,比如如果基本目标系数是10%,公司业绩和个
人业绩系数均为100%,那奖金就是基本工资的10%。从这里可以看出,在考虑Offer的时
候,奖金的基本目标系数影响最大。一个10%的目标系数和一个5%的目标系数意味着实
际上很可能有5%的基本工资差异。

3、 期权或者股票。期权或者股票是长期激励手段——一般是四到五年的授予时间 (
Vest Period),每年有20~25%的股票或者期权能到员工手上。有几个常见参数:
A、 Initial Vesting Period:拿到第一笔期权或者股票的时间,一般是加入公司之后
一年,也有加入公司之后半年的(不过很少);
B、Vesting Period:拿到所有期权、股票的总时间,一般是四年或者五年,每年平均
拿到1/4或者1/5;
C、Vesting Cliff:第一笔期权拿到之后,以后平均多长时间得到一笔,往往是三个月
,也有半年或者一个月的。

假设你拿到128股股票,Initial Vesting Period是1年,Vesting Period是四年,而
Vesting Cliff是3个月,那你实际拿到的股票是:加入公司一年后一次拿到32股;之后
每三个月会拿到8股,直到第四年底为止。

对于还没上市的公司,建议不要对股票、期权有多大期望(无论HR如何跟你说“这个公
司马上就要准备上市了”)。把它们当成一个彩票就好——这并不意味着你绝对不能去
这个公司,只是说你去这个公司的目的不是为了股票上可能挣到的钱,可能是学习过程
,可能是工作兴趣……

对于上市了的公司,或者你对这个公司极其有信心,可以算一下你预期的股票或者期权
价值,然后除以Vesting Period(比如说四年),算出每年的预期收益——因为不同公
司的Vesting Period可能不一样,所以每年的预期收益才是真正可比的。

4、股票或者期权增发。不少公司(尤其已经成立了几年的公司),会有股票或者期权
的增发措施,让员工每年都能拿到更多的股票,不至于员工在四年之后原始股票全部到
手,发现之后的每年收入骤降而直接离开。

股票或者期权增发的过程往往跟业绩挂钩(公司当然希望越好的员工留下来的机会越大
)。但不同的公司股票或者期权增发的力度很不一样。有的公司非常慷慨,有的公司就
只是形式上的给一些。可以跟HR讨论一下公司对股票增发的政策:比如说“每年给老员
工增发的股票和给新员工的原始股票大概比例是多少? ”

有的上市公司还提供员工股票购买计划(ESPP)。让员工有机会在一定时间以一定优惠
价格(比如九折)购买一定限额的公司股票,然后在一定时间之后(比如三个月)卖出
。如果公司股票在这三个月不掉10%,那你就能够获得一些收入。

这些股票或者期权政策五花八门,在考虑的时候也最好转换成每年的期望收益,以方便
比较。

5、入职奖金(Sign-On Bonus)。入职奖金是对新员工加入公司的一次性激励。
如果你在公司工作不到一年就离开,往往需要把入职奖金退还。一般可以大概估算一下
你打算在这个公司呆的时间(比如4年),然后把入职奖金平均分配到每年去,算出 平
均每年的收入。

==== 我觉得1~5是可以在Offer上跟HR讨价还价的重点;以下的一些基本不会太影响具
体的收入,公司往往是为了给员工提供方便而设立  ====

6、搬迁帮助(Moving Bonus)。很多公司提供搬迁的帮助,但形式很不一样:有的公
司直接给一定金额的Bonus,这种Bonus跟Sign on Bonus性质一样;有的公司则提供报
销上限,实报实销;还有的公司给你提供搬家公司服务。

同时,还有不少公司愿意为新员工提供短期(几个星期)的临时住宿。

这些问题都可以跟HR讨论,找到让你比较方便的形式。

7、法定假日和带薪假期
几乎所有公司都有法定假日和带薪假期。但每个公司的法定假日列表不一样,有些每年
只有四五天的法定假日(新年、国庆、感恩、圣诞),有的可能会有十天甚至以上。

带薪假期是员工自行决定的休假日,每个公司也有很大差异。我见过的最少是5天,最
多有21天,甚至不设上限——对于不设上限的公司,要搞清楚实际的情况,有的公司号
称不设上限,但实际上没有人敢休假。

一年大概有260天工作日(365 / 7 * 5),如果一个公司的假日和带薪假期是31天而另
一个公司是18天,里面也有5%的实际工资差别。

8、退休金计划(401K)。很多公司提供401K退休金计划;有些公司提供match——你给
自己存一定的退休金,公司会给你相应比例的存更多。这样的match往往有个上限;有
的甚至有Vest Period(比如你在公司工作了两年,才能拿到match的部分)。

如果HR向你推销一个公司有401K计划的match的话,要搞清楚他们的上限是多少;算出
每年的实际收益。另外要考虑的是放到401K的钱是要到六七十多岁退休的时候才能拿出
来的,所以实际效用不如同样数目的Bonus或者工资。

9、医疗保险。绝大部分公司为员工个人提供医疗保险;而如果员工的家人也需要医疗
保险的话,大部分公司需要员工分担一部分的费用。如果你需要为家人买保险的话,可
以把每个月的额外保险费转换成每年的费用。

10、早中午饭。很多公司也提供早中晚饭。这对单身的员工来说就省了5天的伙食费,
一年大概是5000左右吧。

11、学费补贴。有些公司为上夜校的员工提供学费补贴,往往每年几千美金。但要注意
可能会有不少约束条件,比如业绩要达到一定的要求,或者多少年才能去一次,或者上
学之后多少年需要留在公司,否则要求退还学费,等等。

12、其他。公司还可能有不少其他的补贴、福利。比起基本工资、奖金、股票来说,其
他大部分的福利都是很少的比例,更多的是公司为了方便员工,而不是作为吸引员工的
条件——但有时候HR会把它们作为公司竞争力来强调。如果有某些福利听起来真的很诱
人,最好搞清楚具体的约束条件,换算成每年的收入,再做比较。

四、关于HR
最后这一篇博文解释一下我对HR的一些理解。希望大家跟HR交流的时候更自信更有效。

大部分的朋友把跟他们在面试中联系的非工程师统称HR (Human Resource),实际上在
整个招聘的流程中,HR的职能有几种不同的专业分工(有些小公司比较简洁,也很可能
由同一个人做这些分工的几项甚至全部):

1、Sourcer:Sourcer的工作是为找到合适的面试候选人。Sourcer每天看很多的简历、
去各大论坛发帖子、去LinkedIn等网站搜索候选人……一旦找到ta觉得合适的人选,就
会通过Email或者电话联系。一般情况下Sourcer是第一个跟候选人联系的HR,她会问一
些基本的问题看看候选人是不是有可能通过面试,然后推荐给Recruiter。

2、Recruiter:一个应聘者进入了面试流程,就会有一个Recruiter跟ta联系。
Recruiter负责这位应聘者的面试、Offer、讨价还价、签约,直到应聘者加入公司。对
应聘者来说,Recruiter 是与其接触最多的人。(当然,最终Offer的等级、是否招聘
的决定还是由技术团队和管理团队做出的)。

3、Coordinator:Coordinator主要是帮助应聘者安排面试过程,比如说安排机票、酒
店、面试当天的接待等等。

4、Compensation Team:这个团队应聘者基本上不会接触到, 它的主要任务是考察业
界其他公司的待遇,总结待遇变化的趋势和本公司的薪酬竞争力,决定本公司每一个级
别员工的待遇标准:同一个级别的员工薪酬有一定的范围,除非极其个别情况,薪酬不
会超出这个范围。

从应聘者的角度上看,整个流程是:Sourcer跟你联系,coordinator为你安排面试,
Recruiter跟你联系,讨论你的具体情况(目前的薪酬、期望值等等),coordinator为
你安排下一步面试……工程师把面试结果提交,Recruiter把面试结果汇总,管理团队
讨论并且确定应聘者的是否合适,对应的级别是多少,Recruiter根据结果和
Compensation Team的薪酬范围跟应聘者讨论具体的薪酬;如果有非常特殊的情况,需
要跟技术总监或者副总裁进一步讨论(比如特殊人才的薪酬,或者由总监或者副总裁直
接联系应聘者)。最后Recruiter跟你一起在Offer上签字。

绝大部分的Recruiter都是谈判高手——他们每周要跟好几个应聘者讨价还价, 一个应
聘的工程师就算每年换工作,也很难有Recruiter的谈判经验丰富。另一方面,硅谷科
技公司里面Recruiter的工作流动性比较大,不少公司Recruiter是第一年合同,只有业
绩很好的第二年才会变成正式员工,所以Recruiter会想尽各种办法让拿到Offer的应聘
者加入。

大方向上,这样的Recruiter激励机制是好的,鼓励了Recruiter努力工作;但在具体操
作上,有些Recruiter会为了自己的短期目标而做出一些对应聘者不负责任的行为,比
如在电话里面给出一些有意无意的误导信息甚至恐吓。我听说的一个典型例子是某跨国
大公司在国内的Recruiter跟一个应聘者说 “你口头已经跟我说过你想来我们公司了,
如果你再去跟别的公司面试,你到了美国之后的信用记录就会很差!” 真是哭笑不得
——美国的信用记录只跟你的借贷还贷历史有关,而且在硅谷的科技公司全部是At-
will employment,法律上公司和员工互相可以随时终止雇佣关系,不要说口头答应了
,就算签字了你也可以自由地去别的公司面试或者换公司。但这样的误导信息对一个从
来没在美国工作过的朋友来说,还是很有影响的。另一方面,被误导而进入公司的员工
也很可能不会很开心,这样的事情也不符合公司的长远利益。

我写这一系列博文的起初动机就是希望能把整个招聘的过程解释清楚,让更多的朋友不
会吃亏。我想强调的是“不吃亏”就好 —— 大体来说,正如我在第一篇(如何收集公
司信息)和第二篇(决策过程的考虑因素)强调的,薪酬并不是最重要的,在哪个公司
工作更开心、更能学到自己想学的东西,才是最重要的。如果有朋友希望进一步把跟
Recruiter斗智斗勇作为一个爱好的话,有两本书建议看一下:“影响力”[1] 主要讲
的是黑武术——别人怎样利用人性的弱点去说服你;“寸土必争”[2] 主要讲的是白武
术——如何跟对方进行公平的讨价还价。

下面讨论一下几个关于Offer的常见问题:
1、Offer的时效限制:
一般公司在给出Offer的时候,都会给一个时效(比如说希望应聘者一个月之内答复,
否则Offer就不再有效)。这个时效限制可以保证公司避免一些风险,比如说:公司很
小的时候,只招一两个人,所以不能让多个Offer长期有效,否则几个人同时答应了,
公司就需要取消一些Offer,对应聘者影响很大;另外Offer里面薪酬的具体数字是跟据
市场变化而变化的,如果就业市场在几个月之后变差,新招员工的薪酬会降低,Offer
时效性太长会引起不公平(同一时间决定加入的员工,因为Offer发出的时间不一样而
差异很大);类似的因素也包括公司股价、期权价值的浮动。

另一个方面,一个公司给出一个Offer是需要很大代价的:以上所说的所有HR的时间、
工程师面试的时间、管理团队做决定的时间……可以说,对每个发出的Offer,无论接
受与否,如果考虑其背后需要筛选和面试其他不合适的应聘者的时间,公司可能已经耗
费了很大的成本。所以,如果一个应聘的工程师拿到了Offer,就算ta一时不能决定或
者不能接受,大部分公司还是很愿意保持Offer的——如果时间超过了几个月,Offer的
具体薪酬很可能有改变,Offer的决定和工作级别往往还是有效的(除非这个公司的经
济情况或者整个大环境变得特别差,公司突然决定不再扩张了)。

所以,Offer的决定(是否招收这位应聘者和招聘级别)往往是可以在比较长的时间内
有效的,而Offer的具体数字(工资、奖金、股权等等)是很可能变动很快的。我们最
好把这两个部分分开。

理解这一点很重要:我跟一些准备来硅谷工作的朋友交流的过程中,感觉他们觉得找工
作是一次性的买卖,成了就好,不成就再也不用打交道。有些Recruiter有时候也会给
出这样的误导:“如果你现在不来,以后就很难来了” [3] 或者“我这么辛苦找到VP
才给你争取了这个Offer,你不来的话就是不珍惜我的劳动,以后就别跟我们的团队合
作了……”[4] 其实这些说法都是说服你的手段。如果你现在不接受,而几周之后,或
者半年甚至一年之后再跟他们说想要去,绝大部分情况下,公司还是会非常积极的给你
安排新的Offer的,只是具体的数字可能不一样而已。

理解这一点的另一个意义是:尽量跟所有跟你交流过 Recruiter保持良好关系,就算你
不去他们的公司,也给每个人感谢;就算Recruiter在具体数字的讨价还价过程中跟你
说了一些不好听的话,别放在心上,有机会继续保持联系。这样的心态有助于你长期的
职业成长,因为Recruiter本身流动性也很大,他们到了另一个公司,如果还能想起你
是一个很优秀的应聘者,会给你带来新的机会。

2、Offer的讨价还价
我认为最好把所有可能拿到的Offer的公司都面试好了,知道自己究竟能去哪些公司,
每个公司提出的薪酬待遇是多少都搞清楚了,然后再跟Recruiter做协商。

如果你拿到了一个Offer,还有其他公司没有答复,可以跟其他公司说你有一个Pending
Offer,让他们加快速度;而跟给你Offer的公司说你需要一些时间决定,同时考虑到
对方的担心 ,你可以提出过几周再看Offer的具体数字(这样别人不会觉得你是希望拿
着他们的Offer来回讨价还价)。 在这个过程中,Recruiter很可能会催你决定,但正
如上一段说的,除非经济情况突然变化,绝大部分情况下Offer在几周之内是有效性的。

拿到所有能拿的Offer后,可以跟每个公司说你的所有可能选择,跟他们确认Offer里薪
酬待遇的具体数字,然后把不同的待遇转换成可比的每年收入(见上一部分“薪酬和待
遇”的分析)。这样你就大概知道你在当前就业市场的位置,有了基本的数据跟
Recruiter讨论薪酬待遇了。

在讨价还价之前,我个人的建议是想清楚自己对每个公司(而不是每个Offer的数字)
的态度和自己的自身情况:这是不是所有我能选择的公司里面我最希望去的公司(根据
第二篇讨论讨论的内容,个人目标和公司文化等等)?如果不是,就不必去讨价还价了
:一方面浪费了你的时间,另一方面容易引起误会。比如说你说了一个想要的待遇,对
方答应了,你不去,就不太好跟这个Recruiter拒绝了——拒绝别人总是需要有一些理
由的。

如果这个公司是你非常想去的,那就跟对应的Recruiter讨论Offer的细节。这里面很重
要的一点是讨价还价的目的是什么?为什么这个公司应该多给你工资和股票?我觉得可
以强调两点:1、你很喜欢这个公司,希望加入;2、其他公司的待遇更好,你希望的到
一个公平市场的待遇,让你可以很高兴的工作。

你希望加入这个公司这一点很重要:这表现了你的讨价还价是真诚的,同时把你和
Recruiter放在了同一战线上[5]:Recruiter也很希望你能在这个公司工作,讨价还价
的工程是你们俩共同努力来实现共同的目标。

公平性是大家都愿意接受的一个讨论基础。[6] 从公司角度来说,如果你的待遇低于市
场待遇,那你会觉得没有被公平对待,可能很快离开公司,对公司也不是一个好事情。
从个人角度来说,希望“不吃亏”就好,不要想着从中得到比别的类似的工程师更好的
待遇:待遇总是跟期望值挂钩的,如果你真的要到了更好的待遇,公司对你的期望值也
会更大。

如果这个公司的待遇正好也是最好的,那就很难讨价还价了——但你也没有吃亏;如果
这个公司的待遇比其他公司要差,你可以把其他Offer里面最好的拿出来跟Recruiter一
起讨论你的分析,强调你希望到这个公司和你希望公平这两点。如果你的分析有道理,
也就等于给Recruiter提供了跟管理团队协商依据。Recruiter会比较容易给你争取更好
的待遇。

最后讨价还价的结果可能是令人满意的(比如说公司的确把待遇水平涨到了你希望的水
平),也可能不完全满意:比如说公司的管理团队认为你的分析不完全合理,比如说对
股票未来价格的预测跟管理团队的预测不一样;或者另一个公司给出的待遇是非理性的
…… 一个前Yahoo的Recruiter曾经在Caltech的就业中心做过讨论,她的建议一般不要
超过两个回合的讨价还价——如果一开始的Offer是x,你回复说希望要a,对方说不行
,最高是y,你回复说要b,对方再给你z。到了这步基本上就很难再有大的变化了。这
是一个特定的Recruiter说的经验,可能不是放之四海皆准的道理,但我觉得也有一定
道理:第一回合是Recruiter自己的上限,第二回合是Recruiter跟管理团队讨论之后的
上限。在这个时候,如果对方给你的待遇还不能满足你的期望,我会建议静下心来想想
这个Offer是不是合适:长远来看,找一个自己喜欢的工作更重要;但你如果有一些短
期的期望(比如说需要一笔钱还房贷),那可能就要为了短期的目标而去一家自己不喜
欢(但薪酬更高)的公司了。如果短期没有迫切的期望,而两个公司Offer的差距也不
是很远,我还是建议去自己喜欢的地方。无论哪种决定,关键是要在讨价还价之后冷静
下来综合考虑,讨价还价的过程本身很容易让人专注于薪酬数字而忽视了其他的考虑因
素。

3、Offer的接受和谢绝
一旦决定,一般建议先接受一个公司的Offer,等到对方确认了,再谢绝其他的Offer。
这样的操作顺序有两个好处:第一是以防万一,如果你希望去的公司出了什么临时事
故不能聘用你了,你还没有拒绝其他公司,还可以轻松的接受另一个公司的Offer;第
二是而且减少自己犹豫的可能性——一旦你谢绝了其他的公司的Offer,对方可能还会
给你一个更高的Offer(往往比你拿到的高一点),这个时候如果你已经答应了最喜欢
的公司了,就不会为多一点点的待遇而再反复犹豫了。

无论接受Offer也好,谢绝Offer也好,一定要给所有公司所有跟你联系过的人发信感谢
。他们都在这个过程中花时间为你提供了有用的信息,感谢他们并且交个朋友;以后的
路很长,总有碰到的时候的。

4、Offer的绑定性
最后讨论一下Offer的绑定性。硅谷绝大部分的IT公司的绝大部分Offer都是At-will
employment:雇佣双方的任何一方都可以随时随地无条件的解除雇佣协议 。而一般礼
貌的做法是雇员决定离开公司的话,一般在离开前的两周通知公司;公司如果需要解除
雇佣协议,一般提供两周左右的补偿。

同样道理,就算你接受了一个Offer,随时可以谢绝——当然最好是礼貌的谢绝:跟对
方公司的HR沟通,解释为什么你要谢绝这个Offer,然后跟所有帮助过你的人联系,向
他们解释并且道歉。

我见到过的一些例子是有个朋友接受了一个公司的Offer,然后又打算去另一个公司上
班,但并没有通知第一个公司,直到报到的那天不出现,对方才知道。这就非常不好。

所以, 如果你有更喜欢更想去的公司,你是可以谢绝一个接受了的Offer的。只要你礼
貌地处理好这个过程,跟所有人充分沟通,以后也还是可以保持很好的关系的。这跟你
在美国的信用记录无关。关键是你是否真心想去一个公司。

——–【作者简介】——–
魏小亮(@魏小亮9),1997年国际信息学奥林匹克银牌。2001年,本科就读于清华大学
计算机系,在读期间担任系科协副主席,毕业时获得本科优秀毕业生称号;2004年,毕
业于加州理工学院计算机专业,获得硕士学位;2007年,于加州理工学院计算机专业,
获得博士学位。学习期间,在网络领域的顶级期刊IEEE/ACM Transaction of
Networking、会议IEEE Infocom上发表多篇文章,其中发表在IEEE/ACM Transaction
of Networking上的文章引用次数超过400次。目前,在美国Facebook公司担任总监,领
导Facebook移动产品线性能、可靠性和用户体验分析等方面的研发团队,同时也是
Facebook软件部署团队的一员,负责软件的快速部署;另外,他也是Facebook“新兵营
”的领队之一,负责新员工的培训。(摘自清华博学网)

Wednesday, September 25, 2013

Iterative Binary Tree Traversal in Java

/** Iteratively traverses the binary tree in pre-order */
public void preorder( ) {
    if( root == null ) return;

    Stack<Node> stack = new Stack<Node>( );
    stack.push( root );

    while( ! stack.isEmpty( ) ) {
        Node current = stack.pop( );
        if( current.right != null ) stack.push( current.right );
        if( current.left != null ) stack.push( current.left );
        System.out.print( current.data + " " );
    }
}

/** Iteratively traverses the binary tree in in-order */
public void inorder( ) {
    Node node = root;
    Stack<Node> stack = new Stack<Node>( );
    while( ! stack.isEmpty( ) || node != null ) {
        if( node != null ) {
            stack.push( node );
            node = node.left;
        } else {
            node = stack.pop( );
            System.out.print( node.data + " " );
            node = node.right;
        }
    }
}

/** Iteratively traverses the binary tree in post-order */
public void postorder( ) {
    if( root == null ) return;

    Stack<Node> stack = new Stack<Node>( );
    Node current = root;

    while( true ) {

        if( current != null ) {
            if( current.right != null ) stack.push( current.right );
            stack.push( current );
            current = current.left;
            continue;
        }

        if( stack.isEmpty( ) ) return;
        current = stack.pop( );

        if( current.right != null && ! stack.isEmpty( ) && current.right == stack.peek( ) ) {
            stack.pop( );
            stack.push( current );
            current = current.right;
        } else {
            System.out.print( current.data + " " );
            current = null;
        }
    }
}

Inorder Tree Traversal without Recursion(use stack)

Using Stack is the obvious way to traverse tree without recursion. Below is an algorithm for traversing binary tree using stack. See this for step wise step execution of the algorithm.
1) Create an empty stack S.
2) Initialize current node as root
3) Push the current node to S and set current = current->left until current is NULL
4) If current is NULL and stack is not empty then 
     a) Pop the top item from stack.
     b) Print the popped item, set current = current->right 
     c) Go to step 3.
5) If current is NULL and stack is empty then we are done.
Let us consider the below tree for example
            1
          /   \
        2      3
      /  \
    4     5

Step 1 Creates an empty stack: S = NULL

Step 2 sets current as address of root: current -> 1

Step 3 Pushes the current node and set current = current->left until current is NULL
     current -> 1
     push 1: Stack S -> 1
     current -> 2
     push 2: Stack S -> 2, 1
     current -> 4
     push 4: Stack S -> 4, 2, 1
     current = NULL

Step 4 pops from S
     a) Pop 4: Stack S -> 2, 1
     b) print "4"
     c) current = NULL /*right of 4 */ and go to step 3
Since current is NULL step 3 doesn't do anything. 

Step 4 pops again.
     a) Pop 2: Stack S -> 1
     b) print "2"
     c) current -> 5/*right of 2 */ and go to step 3

Step 3 pushes 5 to stack and makes current NULL
     Stack S -> 5, 1
     current = NULL

Step 4 pops from S
     a) Pop 5: Stack S -> 1
     b) print "5"
     c) current = NULL /*right of 5 */ and go to step 3
Since current is NULL step 3 doesn't do anything

Step 4 pops again.
     a) Pop 1: Stack S -> NULL
     b) print "1"
     c) current -> 3 /*right of 5 */  

Step 3 pushes 3 to stack and makes current NULL
     Stack S -> 3
     current = NULL

Step 4 pops from S
     a) Pop 3: Stack S -> NULL
     b) print "3"
     c) current = NULL /*right of 3 */  

Traversal is done now as stack S is empty and current is NULL. 

Iterative Tree Traversals (转载)

Tree traversals, are very important for any interview. Almost any interview question that you get, can be solved by finding out the correct traversal to use. You can read and compare various tree traversals from here .
Although the recursive tree traversals, can be coded very neatly but recursion is generally not preferred. Excessive recursive function calls may cause memory to run out of stack space.
Since the depth of a balanced binary search tree is about lg(n), we need not worry about running out of stack space, even if there are a million elements in the tree. But alas perfectly balanced trees, come at their own cost. Rather efficient height balanced trees is still an active area of interest. So it is quite possible in practical scenarios that the tree may not be balanced. If that is the scenario then we are asking for trouble using recursion, because in the worst case the height of the tree may go up to n and in this case, the stack space will eventually run out and the program will crash.
Iterative tree traversals can be coded easily, using a _ visited _ flag. This has been sufficiently explained at this wiki page . But this requires us to change the structure of the tree, and we would not want that. here we will attempt to develop iterative traversals, without modifying the structure.
Understanding the below given iterative traversals can be a little tricky so the best way to understand them is to work them out on a piece of paper. Use this sample binary tree and follow the steps of the below given codes.
BinaryTree
In Order Traversal:
The in order traversal requires that we print the leftmost node first and the right most node at the end. So basically for each node we need to go as far as down and left as possible and then we need to come back and go right. Steps of algorithm are:
  1. Start with the node equals root
  2. Store the node in the stack and visit it's left child.
  3. Repeat step 2 while node is not NULL, if it's NULL then pop it's parent (i.e. the last node in stack) and print it.
  4. Now move to node's right child and repeat step 1
  5. Repeat whole procedure while node is not NULL and stack is not empty
Inorder(Tree *p)
{
    stack < Tree* > S;
    do
    { 
        while (p!=NULL)                      
        { 
            // store a node in the stack and visit it's left child
            S.push(p);
            p=p->left; 
        }

        // If there are nodes in the stack to which we can move up
        // then pop it
        if (!S.empty())
        { 
            p=S.top();
            S.pop();

            // print the nodes value
            cout << p->ele << "-";

            // vistit the right child now
            p=p->right; 
        }

    // while the stack is not empty or there is a valid node
    }while(!S.empty()||p!=NULL);
}
Food for Thought: The above given iterative solution makes use of explicit stack. We have only managed to eliminate the recursion, but not the extra space requirement. Can there be another way in which we can give an in-order traversal without using a stack?
Yes! you can do that and there are two methods. But both of them require to change the tree structure as we generally us it.
  • One is pretty intuitive, and that is to have a parent pointer, in addition to the child pointer. This eliminates the need for extra space, because the whole purpose of using a stack was to save the parent nodes.
  • The second is not so intuitive, but a very popular data structure and that is Threaded Binary Tree . This along with the normal structure, also stores a pointer to the next in-order successor of a node. In case there is a right child then the in-order successor is the child otherwise it is one of the successors.
Threaded Binary Tree
Pre Order Traversal:
Pre order is very similar to in order traversal. The way of accessing all the nodes remains the same except the position where we print the node. Now the node is printed/visited before visiting the children of the nodes. Steps are very similar to in-order
  1. Start with node equal to root node
  2. Store the node in the stack, print it and visit it's left child.
  3. Repeat step 2 while node is not NULL, if it's NULL then pop it's parent (i.e. the last node in stack).
  4. Now move to node's right child and repeat step 2
  5. Repeat whole procedure while node is not NULL and stack is not empty
  Preorder(Tree *p)    
{ 
    stack < Tree* > S;
    do
    {     
        while (p!=NULL)
        { 
            // storea node in stack and print it's value
            S.push(p);
            cout << p->ele << "-";
            // visit the left child
            p = p->left; 
        }

        // visit the right child
        if (!S.empty())
        {
            p=S.top();
            S.pop();
            p = p->right; 
        }

    } while(!S.empty()||p!=NULL);
}
Post Order Traversal:
Post order traversal is the trickiest one out of them all and hence it is not very frequently asked as an interview question. But Amazon has asked the iterative implementation once. It is useful in some cases
  • Tree deletion: In order to free up allocated memory of all nodes in a tree, the nodes must be deleted in the order where the current node is deleted when both of its left and right sub-trees are deleted. This can be done using post-order traversal only.
  • It is also used in evaluating Post-fix or Reverse Polish Notation.
The problem with post-order traversal is that in the previous two cases when a node was popped of from the stack, then it was finally removed and was not accessed again. However in case of post-order the node needs to be accessed from the stack twice, and is deleted only in the second access.
First time we find a node, we push it on to the stack, the second time we examine it from the stack to go to it's right child and then finally after visiting both the right sub-tree and the left-subtree we remove the node from the stack. So a node stays in the stack as long as it's sub-trees have not been visited.
Since, a node has to be visited(printed in the trivial case) only after it's left and right child have been visited, we print a node's value after we have visited the left child followed by the right child. The property that is exploited in this implementation is that the the parent node will always be visited just after visiting it's right child.
After visiting the left child, when we come to the parent node in the stack if the right child is NULL, then we can straight away print the node, but if it's not NULL then we have to check whether the previous node which was printed is it's right child. If it's the right child then we can visit the current node, otherwise the right child has not been visited and we must proceed with the right child.
Hence in this traversal, we have to use a previous pointer, only then will we able to correctly traverse the node. Otherwise we do not know when the right child has been visited or not.
  1. Start with the root node.
  2. Store the node in the stack, and visit it's left child.
  3. Repeat step 1 while node is not NULL, if it's NULL:
    • Pick the top most node from the stack.
    • If it's right child is NULL, then print the node and set prev pointer to this node
    • If it's not NULL, check whether the right child is equal to prev pointer, if it is then print the node, else repeat step 1 with the right child
    • In this step, if you print the node then current is made equal to NULL and this sub-loop is repeated untill stack is not empty and current is NULL
  4. Since the root node remains in the stack till the end, the terminating condition is untill stack is empty
  Postorder(Tree *p)  
{
    stack < Tree* > S;
    Tree *prev;
    do
    { 
        while (p!=NULL)
        { 
            S.push(p);
            p=p->left;
        }
        if(!S.empty())
        {
            while(p==NULL && !S.empty())
            {
                p=S.top();
                if(p->right!=NULL)
                {
                    if(p->right == prev)
                    {
                        cout << p->ele  <<  "-";
                        S.pop();
                        prev = p;
                        p = NULL;
                    }
                    else
                        p = p->right;
                }
                else
                {
                    cout << p->ele  <<  "-";
                    S.pop();
                    prev = p;
                    p = NULL;
                }
            }
        }    
    }while(!S.empty());
}

说一下学术派的代码(java)

前面看到有人说写出来的代码让人一眼就看出是没有工作经验的 这个的确是有可能的,从我自身经验判断 一般来说,具备以下一个或几个特点的代码 会被认为是写java代码写不够多的人写出来的 

 1) 变量名和方法名首字母大写 这个是大忌,一般遇到了,不让过是很正常的 从某种意义上说,这个不是风格的问题 跟goto一样,其实是个错误 

2) 命名中使用下划线 尤其是自定义的系统变量,喜欢用下划线开始 这说明程序猿喜欢介入系统内部实现 which正好是java不提倡的做法 java提倡非侵入式编程 也就是对于已经做好的东西,比如jvm 采用输入参数形式来tune 其次,对于具体对象的管理,采用反射等高级手段来做 直接介入系统内部实现,比如介入jvm内部实现 的方式,其实是不提倡,甚至可以说是禁止的 对于下划线的命名,能不用就不用 所以当你遇到了_myInstance的时候,嗯 

3) static关键字的使用 能写出static方法是好事 说明你懂static是干什么的 但是多数时候,static其实并不是那么频繁滴被使用到 static的代码实现多数时候交给了框架去做 一般如果你要用static 建议单独搞一个类,然后在这个类的命名最后加上Util 比如MyApplicationUtil,这样就显得professional一点 static变量应该尽量避免 关于2和3,有一个特例,就是全局常量(不变的变量)的定义 比如public static final String BIG_COW = "goodbug"; 这个时候你需要static变量和下划线,其它时候,最好不要了 

4) 封装的意识         对于一个类,实现之后,看是否意识到实现了private和set/get方法 还是直接访问内部变量 这个是oop,set/get方法可以有很多理由 比如说,只实现get而不实现set,那么这就是read only 还有并发的时候,可以通过syncrhonized set/get方法来控制并发 等等 

5)最后一个就是看对于各种类的使用 比如Hashtable,这个已经too old了 还有Vector这几个类,都已经old太久了 有新类能够替换这些类,所以如果你打算用java写代码 请躲开这几个“老”类 

这几个细节,虽然说都是rule of thumb,都不难,稍微留意一下,都很容易避开 但是很有趣的是,往往是一些有其它语言经验的程序猿 比如写c++写得比较多的,转过来,会犯这些错误 所以有时候雇主更喜欢新人,白纸一张,可塑性很强 换编程语言的程序猿,会带来不少坏习惯 当然最理想的还是有相关经验的程序猿 只要能针对上诉五点,稍加练习,你的代码显得比较professional问题不大