Programming Interviews Exposed: Secrets to Landing Your Next Job Notes

Table of Contents

Chapter 1: Before the Search

Develop Marketable Skills

The appendix covers how your résumé is primarily a marketing tool to get you job interviews. This assumes, of course, that you have marketable skills to offer a prospective employer. You can only stretch the truth so far, and if even you exaggerate or outright lie about your skills and what you’ve accomplished in the past, you probably won’t make it through technical interviews designed specifically to weed out the liars and exaggerators of this world. What you need to do, then, is develop skills and accomplishments that will make you stand out from the crowd both on paper and in the interviews, especially if you’re entering the job market for the first time. Here are some approaches you can take:

  • Upgrade your credentials - Companies such as Google are well known for favoring job applicants with graduate degrees. Getting a master’s or doctorate degree is one way to upgrade your credentials. Although pursuing a graduate degree is a large commitment on your part, you can upgrade your credentials in other ways, such as taking university or professional development courses or participating in programming contests.
  • Get certified - Certification is a touchy issue in the software development profession, but there’s no doubt that some jobs either prefer or require candidates to be certified in specific technologies, especially IT jobs.
  • Work on a side project - A great way to expand your skill set is to work on a project that is not directly related to your primary work or study focus. Starting or joining an open-source development project is one way to go. Or if you’re working at a company, see if they’ll let you spend time on an ancillary project.
  • Do well in school - Although grades aren’t everything, they are one measure that companies use in order to rank new graduates with little job experience. The better your grades, especially in computer science and mathematics courses, the more you’ll impress a potential employer.
  • Keep learning - The end of formal education doesn’t mean you should stop learning, especially when there’s so much information about programming available from a wide variety of sources. Whether it’s books or blogs, there’s always a way to keep yourself current, no matter what type of programming you do. It’s also a great way to expand your horizons and discover other areas of interest.
  • Be an intern - New graduates who’ve managed to secure employment during their nonschool terms - especially those that participate in cooperative education programs - have a huge leg up over their peers who haven’t yet ventured into the real world. Software development in the field is often very different from software development in an academic setting, and potential employers are very cognizant of this.

The key is to keep learning, no matter what stage of your career you’re at. Marketable skills don’t develop overnight; they take some effort and initiative on your part, but they’ll have long-lasting effects on your career.

Note that one of the best ways to develop marketable skills is to accomplish something, whether it’s in your current job, something you did as a side project, or something you worked on as an intern or for a class project. Being able to talk intelligently and confidently about a project for which you played a primary role in its success is incredibly important. Make sure you can describe the problem clearly and succinctly and how your project solved the problem, even to a nontechnical person. Displaying a passion for programming is always positive and one way to make yourself stand out from the other candidates.

Chapter 2: The Job Application Process

Chapter 3: Approaches to Programming Problems

Interactivity Is Key

The code you write in the interview is probably the only example of your code that your interviewer will see. If you write ugly code, your interviewer will assume you always write ugly code. This is your chance to shine and show your best code. Take the time to make your code solid and pretty.

Tip Brush up on the languages you expect to use, and write your best code. Programming questions are designed to see both how well you can code and how you solve problems. If all the interviewer wanted to do were measure your coding ability, he could give you a piece of paper with problems and come back an hour later to evaluate how you did, just as they do in programming contests. What the interviewer wants is to see your thought processes as you work through each stage of the programming problem.

The problem-solving process in these interviews is very interactive, and if you’re having difficulty, the interviewer will generally guide you to the correct answer via a series of hints. Of course, the less help you need to solve the problem, the better you look, but showing an intelligent thought process and responding well to the hints you are given is also very important.

Even when you immediately know the answer to a problem, because it’s something you’ve already done before, don’t just blurt it out. Break the answer down into discrete steps and explain the thought processes behind each step. The point is to show the interviewer that you understand the underlying concepts, not that you’ve managed to memorize the answer to a programming puzzle.

If you know any additional information pertaining to the problem, you may want to mention it during the process to show your general knowledge of programming, even if it’s not directly applicable to the problem at hand. In answering these problems, show that you’re not just a propeller-head coder. Demonstrate that you have logical thought processes, are generally knowledgeable about computers, and can communicate well.

Tip Keep talking! Always explain what you are doing! Otherwise, the interviewer has no way of knowing how you tackle complex programming problems.

Solving the Questions

Now it’s time to solve the questions, but don’t just jump in. Instead, remember you want to fully understand the problem. You should try an example, and then focus on the algorithm that will solve the problem. You then want to explain your solution and code for the solution. The following steps walk you through the process:

  • The Basic Steps

    The best way to solve an interview problem is to approach it methodically. Here are the suggested steps:

    1. Make sure you understand the problem. Your initial assumptions about the problem may be wrong, or the interviewer’s explanation may be very brief or difficult to follow. You can’t demonstrate your skills if you don’t understand the problem. Don’t hesitate to ask your interviewer questions about the problem, and don’t start solving it until you understand it. The interviewer may be deliberately obscuring things in order to determine whether you can find and understand the real problem.
    2. Once you understand the question, try an example. This example may lead to insights about how to solve the problem or bring to light any remaining misunderstandings that you have. Starting with an example also demonstrates a methodical, logical thought process. Examples are especially useful if you don’t see the solution right away.

    Tip Make sure you understand the problem before you start solving it, and then start with an example to solidify your understanding.

    1. Focus on the algorithm you will use to solve the problem. Often, this will take a long time and require additional examples. This is to be expected. Interactivity is important during this process. If you stand quietly staring at the whiteboard, the interviewer has no way of knowing whether you’re making productive headway or are simply clueless. Talk to your interviewer and tell him or her what you are doing. For example, you might say something like, “I’m wondering whether I can store the values in an array and then sort them, but I don’t think that will work because I can’t quickly look up elements in an array by value.” This demonstrates your skill, which is the point of the interview, and may also lead to hints from the interviewer, who might respond, “You’re very close to the solution. Do you really need to look up elements by value, or could you …”

      It may take a long time to solve the problem, and you may be tempted to begin coding before you figure out an exact solution. Resist this temptation. Consider who you would rather work with: someone who thinks about a problem for a long time and then codes it correctly the first time or someone who hastily jumps into a problem, makes several errors while coding, and doesn’t have any idea where he or she is going. Not a difficult decision, is it?

    2. After you’ve figured out your algorithm and how you will implement it, explain your solution to the interviewer. This gives him or her an opportunity to evaluate your solution before you begin coding. Your interviewer may say, “Sounds great, go ahead and code it,” or something like, “That’s not quite right because you can’t look up elements in a hash table that way.” In either case, you gain valuable information.
    3. While you code, it’s important to explain what you’re doing. For example, you might say, “Here, I’m initializing the array to all zeroes.” This narrative enables the interviewer to follow your code more easily.

    Tip Explain what you are doing to your interviewer before and while coding the solution. Keep talking!

    1. Ask questions when necessary. You generally won’t be penalized for asking factual questions that you might otherwise look up in a reference. You obviously can’t ask a question like, “How do I solve this problem?” but it is acceptable to ask a question like, “I can’t remember - what format string do I use to print out a localized date?” While it’s better to know these things, it’s okay to ask this sort of question.
    2. After you’ve written the code for a problem, immediately verify that the code is correct by tracing through it with an example. This step demonstrates very clearly that your code works in at least one case. It also illustrates a logical thought process and your desire to check your work and search for bugs. The example may also help you flush out minor bugs in your solution.
    3. Make sure you check your code for all error and special cases, especially boundary conditions. Many error and special cases are overlooked by programmers; forgetting these cases in an interview indicates you may forget them on the job. For example, if you allocate memory dynamically, make sure you check that the allocation did not fail. (If time does not allow for extensive checking, at the very least explain that you should check for such failures.) Covering error and special cases will impress your interviewer and help you correctly solve the problem.

    Tip Try an example, and check all error and special cases.

    Once you try an example and feel comfortable that your code is correct, the interviewer may ask you questions about what you wrote. Commonly, these questions focus on running time, alternative implementations, and complexity. If your interviewer does not ask you these questions, you should volunteer the information to show that you are cognizant of these issues. For example, you could say, “This implementation has linear running time, which is the best possible because I have to check all the input values. The dynamic memory allocation will slow it down a little, as will the overhead of using recursion.”

  • When You Get Stuck

    Getting stuck on a problem is expected and an important part of the interviewing process. Interviewers want to see how you respond when you don’t recognize the answer to a question immediately. Giving up or getting frustrated is the worst thing to do if this happens to you. Instead, show interest in the problem and keep trying to solve it:

    • Go back to an example. Try performing the task and analyzing what you are doing. Try extending from your specific example to the general case. You may have to use very detailed examples. This is okay, because it shows the interviewer your persistence in finding the correct solution.

    Tip When all else fails, return to a specific example. Try to move from the specific example to the general case and from there to the solution.

    • Try a different data structure. Perhaps a linked list, an array, a hash table, or a binary search tree will help. If you’re given an unusual data structure, look for similarities between it and more-familiar data structures. Using the right data structure often makes a problem much easier.
    • Consider the less-commonly used or more-advanced aspects of a language. Sometimes the key to a problem involves one of these features.

    Tip Sometimes a different data structure or advanced language feature is key to the solution.

    Even when you don’t feel stuck, you may be having problems. You may be missing an elegant or obvious way to implement something and writing too much code. Almost all interview coding questions have short answers. You rarely need to write more than 15 lines of code and almost never more than 30. If you start writing a lot of code, you may be heading in the wrong direction.

Chapter 4: Linked Lists

Linked List Problems

A C++ version looks like the following:

class Stack
{
public:
    Stack();
    ~Stack();
    void push( void *data );
    void *pop();
protected:
    // Element struct needed only internally
    typedef struct Element {
        struct Element *next;
        void *data;
    } Element;

    Element *head;
};

Stack::Stack() {
    head = NULL;
    return;
}

Stack::~Stack() {
    while( head ){
        Element *next = head->next;
        delete head;
        head = next;
    }
    return;
}

void Stack::push( void *data ){
    //Allocation error will throw exception
    Element *element = new Element;
    element->data = data;
    element->next = head;
    head = element;
    return;
}

void *Stack::pop() {
    Element *popElement = head;
    void *data;

    /* Assume StackError exception class is defined elsewhere */
    if( head == NULL )
        throw StackError( E_EMPTY );

    data = head->data;
    head = head->next;
    delete popElement;
    return data;
}

Null or Cycle

Important You are given a linked list that is either NULL-terminated (acyclic), as shown in Figure 4-5, or ends in a cycle (cyclic), as shown in Figure 4-6.

Write a function that takes a pointer to the head of a list and determines whether the list is cyclic or acyclic. Your function should return false if the list is acyclic and true if it is cyclic. You may not modify the list in any way.Null or Cycle

Write a function that takes a pointer to the head of a list and determines whether the list is cyclic or acyclic. Your function should return false if the list is acyclic and true if it is cyclic. You may not modify the list in any way.

In outline form, this algorithm looks like this:

Start two pointers at the head of the list
Loop infinitely
    If the fast pointer reaches a NULL pointer
        Return that the list is NULL terminated
    If the fast pointer moves onto or over the slow pointer
        Return that there is a cycle
    Advance the slow pointer one node
    Advance the fast pointer two nodes

You can now implement this solution:

/* Takes a pointer to the head of a linked list and determines if
 * the list ends in a cycle or is NULL terminated
 */
bool determineTermination( Node *head ){
    Node *fast, *slow;
    fast = slow = head;
    while( true ){
        if( !fast || !fast->next )
            return false;
        else if( fast == slow || fast->next == slow )
            return true;
     else {
         slow = slow->next;
         fast = fast->next->next;
     }
  }
}

Chapter 5: Trees and Graphs

Following is some additional tree-related vocabulary:

  • Parent - A node that points to other nodes is the parent of those nodes. Every node except the root has one parent. In Figure 5-1, B is the parent of D, E, and F.
  • Child - A node is the child of any node that points to it. In Figure 5-1, each of the nodes D, E, and F is a child of B.
  • Descendant - All the nodes that can be reached by following a path of child nodes from a particular node are the descendants of that node. In Figure 5-1, D, E, F, H, I, J, and K are the descendants of B.
  • Ancestor - An ancestor of a node is any other node for which the node is a descendant. For example, A, B, and D are the ancestors of I.
  • Leaves - The leaves are nodes that do not have any children. G, H, I, and K are leaves.

Common Searches

  • Breadth-First Search

    One way to search a tree is to do a breadth-first search (BFS). In a BFS you start with the root, move left to right across the second level, then move left to right across the third level, and so forth. You continue the search until either you have examined all of the nodes or you find the node you are searching for. The time to find a node is O(n), so this type of search is best avoided for large trees. A BFS also uses a large amount of memory because it is necessary to track the child nodes for all nodes on a given level while searching that level.

  • Depth-First Search

    Another common way to search for a node is by using a depth-first search (DFS). A depth-first search follows one branch of the tree down as many levels as possible until the target node is found or the end is reached. When the search can’t go down any farther, it is continued at the nearest ancestor with unexplored children. DFS has much lower memory requirements than BFS because it is not necessary to store all of the child pointers at each level. In addition, DFS has the advantage that it doesn’t examine any single level last (BFS examines the lowest level last). This is useful if you suspect that the node you are searching for will be in the lower levels. For example, if you were searching a job hierarchy tree looking for an employee who started less than three months ago, you would suspect that lower-level employees are more likely to have started recently. In this case, if the assumption were true, a DFS would usually find the target node more quickly than a BFS.

Traversals

  • Preorder traversal of a node performs the operation first on the node itself, then on its left descendants, and finally on its right descendants. In other words, a node is always visited before any of its children.
  • Inorder traversal performs the operation first on the node’s left descendants, then on the node itself, and finally on its right descendants. In other words, the left subtree is visited first, then the node itself, and then the node’s right subtree.
  • Postorder traversal performs the operation first on the node’s left descendants, then on the node’s right descendants, and finally on the node itself. In other words, all of a node’s children are visited before the node itself.

Binary Tree Problems

  • Preorder Traversal, No Recursion
    Create the stack
    Push the root node on the stack
    While the stack is not empty
        Pop a node
        If the node is not null
            Print its value
            Push the node's right child on the stack
            Push the node's left child on the stack
    
    void preorderTraversal( Node root ){
        NodeStack stack = new NodeStack();
        stack.push( root );
        while( true ){
            Node curr = stack.pop();
            if( curr == null ) break;
            curr.printValue();
            Node n = curr.getRight();
            if( n != null ) stack.push( n );
            n = curr.getLeft();
            if( n != null ) stack.push( n );
        }
    }
    
  • Lowest Common Ancestor

Chapter 6: Arrays and Strings

Arrays

An array is a sequence of variables of the same type arranged contiguously in a block of memory.

Strings

Strings are sequences of characters. However, what constitutes a character depends greatly on the language being used and the settings of the operating system on which the application runs.

Array and String Problems

Both hash tables and arrays provide constant-time lookup; you need to decide which one you will use. On the one hand, hash tables have a higher lookup overhead than arrays. On the other hand, an array would initially contain random values that you would have to take time to set to zero, whereas a hash table initially has no values. Perhaps the greatest difference is in memory requirements. An array would need an element for every possible value of a character. This would amount to a relatively reasonable 128 elements if you were processing ASCII strings, but if you had to process Unicode strings you would need more than 65,000 elements, assuming a 16-bit Unicode encoding. In contrast, a hash table would require storage for only the characters that actually exist in the input string. Therefore, arrays are a better choice for long strings with a limited set of possible character values; hash tables are more efficient for shorter strings or when there are many possible character values.

Chapter 7: Recursion

Although recursion is a very powerful technique, it is not always the best approach, and rarely is it the most efficient approach. This is due to the relatively large overhead for routine calls on most platforms. For a simple recursive routine like factorial, many computer architectures spend more time on call overhead than the actual calculation. Iterative routines, which use looping constructs instead of recursive routine calls, do not suffer from this overhead and are frequently more efficient.

Tip Iterative solutions are usually more efficient than recursive solutions.

This implementation is significantly more efficient than our previous recursive implementation because no routine calls are involved. Although it represents a different way of thinking about the problem, it’s not really any more difficult to write than the recursive implementation

Tip A recursive algorithm can be implemented without recursive calls by using a stack, but it’s usually more trouble than it’s worth.

In an interview, a working solution is of primary importance; an efficient solution is secondary. Unless you’ve been told otherwise, go with whatever type of working solution comes to you first. If it’s a recursive solution, you might want to mention the inefficiencies inherent in recursive solutions to your interviewer, so it’s clear that you know about them. In the rare instance that you see a recursive solution and an iterative solution of roughly equal complexity, you should probably mention them both to the interviewer, indicating that you’re going to work out the iterative solution because it’s likely to be more efficient.

Recursion Problems

Chapter 8: Concurrency

Basic Thread Concepts

  • Threads

    A thread is the fundamental unit of execution within an application: A running application consists of at least one thread. Each thread has its own stack and runs independently from the application’s other threads. Threads share the resources used by the application as it runs, such as file handles or memory, which is why problems can occur. Data corruption is a common side effect of having two threads simultaneously write data to the same block of memory, for example.

  • Monitors and Semaphores

    In order to avoid data corruption and other problems, applications must control how threads interact with shared resources, referred to as thread synchronization. The two fundamental thread synchronization constructs are monitors and semaphores. Which you use depends on what your system or language supports.

Concurrency Problems

  • Busy Waiting
  • Producer/Consumer
    public class IntBuffer {
        private int   index;
        private int[] buffer = new int[8];
    
        public synchronized void add( int num ){
            while( index == buffer.length - 1 ){
                try {
                    wait();
                }
                catch( InterruptedException e ){
                }
            }
    
            buffer[index++] = num;
            notifyAll();
        }
    
        public synchronized int remove(){
            while( index == 0 ){
                try {
                    wait();
                }
                catch( InterruptedException e ){
                }
            }
    
            int ret = buffer[--index];
            notifyAll();
            return ret;
        }
    }
    
    public class Producer extends Thread {
        private IntBuffer buffer;
    
        public Producer( IntBuffer buffer ){
            this.buffer = buffer;
        }
    
        public void run(){
            Random r = new Random();
            while( true ){
                int num = r.nextInt();
                buffer.add( num );
                System.out.println( "Produced " + num );
            }
        }
    }
    
    public class Consumer extends Thread {
        private IntBuffer buffer;
    
        public Consumer( IntBuffer buffer ){
            this.buffer = buffer;
        }
    
        public void run(){
            while( true ){
                int num = buffer.remove();
                System.out.println( "Consumed " + num );
            }
        }
    }
    
    
    IntBuffer b = new IntBuffer();
    Producer p = new Producer( b );
    Consumer c = new Consumer( b );
    p.start();
    c.start();
    
  • The Dining Philosophers

    This application deadlocks when all philosophers have simultaneously picked up their left fork: Because no right fork is available to any philosopher, no philosopher can eat.

    One solution is to add a timeout to the waiting: If a philosopher is not able to eat within a predetermined amount of time after acquiring the first fork, then the philosopher drops the fork and tries again. The flaw with this solution is that it’s possible for one or more philosophers to starve because they never acquire both forks. This is referred to as livelock.

    The best solution requires a very simple change to the application. Instead of having all the philosophers pick up the left fork first, have one of the philosophers pick up the right fork first:

Chapter 9: Object-Oriented Programming

Interfaces and Abstract Classes

Explain the difference between an interface and an abstract class in object-oriented programming.

  • An interface declares a set of related methods, outside of any class.
  • An abstract class is an incomplete class definition that declares but does not define all of its methods.

Conceptually, then, an interface defines an application programming interface (API) that is independent of any class hierarchy. In fact, interfaces can be used in non-OO programming models, such as componentbased models like COM and CORBA. However, you’re focusing on the use of interfaces in an object-oriented context, where they are useful in their own right. Interfaces are the ultimate encapsulators, because they hide all the details of the classes that implement their methods from the user of the interface. They’re particularly important - almost necessary, in fact - in languages that only support single inheritance (classes can only inherit from one base class). A class that exposes its members via an interface is said to implement the interface.

Unlike an interface, an abstract class is a proper class: It can have data members and can be a subclass of other classes. Unlike a concrete (nonabstract) class, however, some of its behaviors are deliberately left to be defined by its own subclasses. Abstract classes cannot be instantiated because of this - only instances of concrete subclasses can be created.

Virtual Methods

Describe what virtual methods are and why they are useful.

A virtual method is a method whose implementation is determined at run time based on the actual type (class) of the invoking object. Nonstatic Java methods are always virtual, so Java programmers may have trouble answering this one; but in C# and C++, methods are only virtual when declared with the virtual keyword - nonvirtual methods are the default.

Once you explain what virtual methods are and why they’re useful, talk about their advantages and disadvantages. The primary advantage was just described: the run-time method selection. Virtual methods are also used to declare abstract methods. The disadvantages are that it takes longer to invoke a virtual method (at a minimum, one lookup needs to be done in a table to find the right method - you can’t jump directly to the method as you can with nonvirtuals) and that extra memory is required to store the information needed for the lookup.

Multiple Inheritance

Why do C# and Java disallow the multiple inheritance of classes?

In C++ it’s legal for a class to inherit (directly or indirectly) from more than one class, which is referred to as multiple inheritance. C# and Java, however, limit classes to single inheritance - each class inherits from a single parent class.

Multiple inheritance is a useful way to create classes that combine aspects of two disparate class hierarchies, something that often happens when using different class frameworks within a single application. If two frameworks define their own base classes for exceptions, for example, you can use multiple inheritance to create exception classes that can be used with either framework.

The problem with multiple inheritance is that it can lead to ambiguity. The classic example is when a class inherits from two other classes, each of which inherits from the same class:

 class A {
  protected:
    bool flag;
};

class B : public A {};

class C : public A {};

class D : public B, public C {
  public:
    void setFlag( bool nflag ){
        flag = nflag; // ambiguous
    }
};

In this example, the flag data member is defined by class A. But class D descends from class B and class C, which both derive from A, so in essence two copies of flag are available because there are two instances of A in D’s class hierarchy. Which one do you want to set? The compiler will complain that the reference to flag in D is ambiguous. One fix is to explicitly disambiguate the reference:

B::flag = nflag;

Another fix is to declare B and C as virtual base classes, which means that only one copy of A will exist in the hierarchy, eliminating any ambiguity.

There are other complexities with multiple inheritance, such as the order in which the base classes are initialized when a derived object is constructed, or the way members can be inadvertently hidden from derived classes. Because of these complexities, some languages restrict themselves to the much simpler single inheritance model. On the other hand, single inheritance is also very restrictive, because only classes with a common ancestor can share behaviors. Interfaces mitigate this restriction somewhat by allowing classes in different hierarchies to expose common interfaces even if they’re not implemented by sharing code.

Chapter 10: Databases

Database Fundamentals

Data in a relational database is stored in tables, which consist of rows and columns. (A set of tables is referred to as a schema.) Each table has at least one column, but there may be no rows. Each column has a type associated with it, which limits the type of data that can be stored in the column, as well as additional constraints. Although the columns are ordered, the rows aren’t. Any ordering that is required is done when the data is fetched (via a query) from the database.

Most tables have keys, although it’s not a requirement (but it is good design). A key is a column or set of columns that uniquely identifies a particular row in the table. One of the keys is designated to be the primary key. For example, in a table of employees, you would use the employee identification number - guaranteed to be unique for each employee - as the primary key.

A table can be linked to another table using a foreign key. A foreign key is usually a primary key value taken from the other table. Foreign keys ensure that data isn’t deleted prematurely: You can’t delete a row from a table if the foreign key of another table references the row. This is known as referential integrity, and it ensures that related tables are always in a consistent state with respect to each other.

Structured Query Language (SQL)

Database Transactions

The integrity of the data stored in a database is paramount: If the data is ever corrupted, every application that depends on the database may fail or be in error. While referential integrity helps keep the data consistent, the best way to ensure data integrity is to use a database transaction.

A transaction groups a set of related database manipulations together into a single unit. If any operation within the transaction fails, the entire transaction fails and any changes made by the transaction are abandoned (rolled back). Conversely, if all the operations succeed, then all the changes are committed together as a group.

The four properties of a transaction are as follows:

  • Atomicity - The database system guarantees that either all operations with the transaction succeed or else they all fail.
  • Consistency - The transaction must ensure that the database is in a correct, consistent state at the start and the end of the transaction. No referential integrity constraints can be broken, for example.
  • Isolation - All changes to the database within a transaction are isolated from all other queries and transactions until the transaction is committed.
  • Durability - Once committed, changes made in a transaction are permanent. The database system must have some way to recover from crashes and other problems so that the current state of the database is never lost.

Database Problems

  • Simple SQL

    Given a database with the table

    Olympics(
        city CHAR(16),
    
        year INT(4)
    )
    

    write a SQL statement to insert Montreal and 1976 into the database.

    INSERT INTO Olympics VALUES( 'Montreal', 1976 )
    
  • Company and Employee Database

    Important You are given a database with the following tables:

    Company (
        companyName CHAR(30),
        id          INT(4) PRIMARY KEY
    )
    
    EmployeesHired (
        id            INT(4) PRIMARY KEY,
        numHired      INT(4),
        fiscalQuarter INT(4),
        FOREIGN KEY id REFERENCES Company
    )
    
    • Write a SQL statement that returns the names of all the companies that hired employees in fiscal quarter 4.
    SELECT companyName FROM Company, EmployeesHired
    WHERE Company.id = EmpolyeesHired.id AND fiscalQuarter = 4 AND numHired > 0
    
    • Now, using the same schema, write a SQL statement that returns the names of all companies that did not hire anyone in fiscal quarters 1 through 4.
    SELECT companyName FROM Company WHERE id NOT IN
    (SELECT id from EmployeesHired WHERE numHired > 0)
    
    • Finally, return the names of all companies and the total number of employees that each company hired during fiscal quarters 1 through 4.
    SELECT companyName, SUM(numHired)
    FROM Company, EmployeesHired
    WHERE Company.id = EmployeesHired.id
    GROUP BY companyName
    
  • Max, No Aggregates

    Important Given the following SQL database schema

    Test (
        num INT(4)
    
    )
    

    write a SQL statement that returns the maximum value from num without using an aggregate (MAX, MIN, etc.).

    There is one minor bug in this query. If the maximum value is repeated in the Test table, it will be returned twice. To prevent this, use the DISTINCT keyword. This changes the query to the following:

    SELECT DISTINCT num FROM Test WHERE num NOT IN
    (SELECT Lesser.num FROM Test AS Greater, Test AS Lesser
    WHERE Lesser.num < Greater.num)
    
  • Three-Valued Logic

    Important Given the following table

    Address (
        street CHAR(30) NOT NULL,
        apartment CHAR(10),
    
        city CHAR(40) NOT NULL,
    
    )
    

    write a SQL statement that returns nonapartment addresses only.

    The trick is in the use of the equality operator (‘=’) to test for a NULL column value. In most databases, a comparison to NULL returns UNKNOWN - even when comparing NULL to NULL. The proper way to check for a NULL or non-NULL column is to use the IS NULL or IS NOT NULL syntax. Thus, the original query should be restated as follows:

    SELECT * FROM Address WHERE apartment IS NULL

Chapter 11: Other Programming Topics

Bit Manipulation

Graphics and Bit Operations Problems

Rectangle Overlap

The two rectangles do not overlap when

  • A’s UL’s x value is greater than B’s LR’s x value or
  • A’s UL’s y value is less than B’s LR’s y value or
  • A’s LR’s x value is less than B’s UL’s x value or
  • A’s LR’s y value is greater than B’s UL’s y value.

This solution is much simpler, requiring only four comparisons and one negation. You can implement the function as follows:

boolean overlap( Rect a, Rect b ){
    return !( a.ul.x > b.lr.x ||
              a.ul.y < b.lr.y ||
              a.lr.x < b.ul.x ||
              a.lr.y > b.ul.y );
}

Working through these rules, you’ll get the following function:

boolean overlap( Rect a, Rect b){
    return( a.ul.x <= b.lr.x &&
            a.ul.y >= b.lr.y &&
            a.lr.x >= b.ul.x &&
            a.lr.y <= b.ul.y );
}

To ensure that you didn’t make a mistake, it’s a good idea to verify that these conditions make sense. The preceding function determines that two rectangles overlap if

  • A’s left edge is to the left of B’s right edge and
  • A’s upper edge is above B’s bottom edge and
  • A’s right edge is to the right of B’s left edge and
  • A’s bottom edge is below B’s upper edge.

Big-endian or Little-endian

Write a function that determines whether a computer is big-endian or little-endian.

Endianness is important to know when reading or writing data structures, especially across networks, so that different applications can communicate with each other. Sometimes the endianness is hidden from the developer: Java uses a fixed endianness to store data, regardless of the underlying platform’s endianness, so data exchanges between two Java applications won’t normally be affected by endianness. But other languages, C in particular, don’t specify an endianness for data storage, leaving the implementation free to choose the endianness that works best for the platform. C is used to solve this problem.

If you set the value of the integer to 1, you can distinguish between the MSB and the LSB because in an integer with the value 1, the LSB has the value 1 and the MSB has the value 0.

The code for this test is as follows:

/* Returns true if the machine is little-endian, false if the
 * machine is big-endian
 */
bool endianness(){
    int   testNum;
    char *ptr;

    testNum = 1;
    ptr = (char *) &testNum;
    return (*ptr); /* Returns the byte at the lowest address */
}

This solution is sufficient for an interview. However, as the goal of an interview is not just to solve problems, but also to impress your interviewer, you may want to consider a slightly more elegant way to solve this problem. It involves using a feature of C/C++ called union types. A union is like a struct, except that all of the members are allocated starting at the same location in memory.

/* Returns true if the machine is little-endian, false if the
 * machine is big-endian
 */
bool endianness(){
    union {
        int theInteger;
        char singleByte;
    } endianTest;

    endianTest.theInteger = 1;
    return endianTest.singleByte;
}

Number of Ones

Write a function that determines the number of 1 bits in the computer’s internal representation of a given integer.

The number will be shifted to the right, but the new bit added on the left will depend on how the shift operator treats negative values. Let’s avoid the problem entirely and use Java’s >>> operator for our example. In the other C-like languages, you can also avoid the problem by reading the value as an unsigned integer. (That solution doesn’t work with Java because there are no unsigned integer types in Java.) Using either the >>> or an unsigned integer means that the shift operator will not sign extend, and the new bits that are added during the right shifting will be 0’s. The number will eventually become all 0’s. Finally, consider the case where you are given a positive integer. This is the sample case that you worked with, and the algorithm works correctly here.

The code for this algorithm is as follows:

int numOnesInBinary( int number )
{
     int numOnes = 0;
     while( number != 0 ){
          if( ( number & 1 ) == 1 )
              numOnes++;
          number = number >>> 1;
     } 
     return numOnes;
}

You can count the number of times that you can perform this process before the integer’s value reaches 0. This is the number of 1’s in the computer’s representation of the number. In outline form this algorithm is as follows:

Start with count = 0
While the integer is not zero
    AND the integer with the integer – 1
    Increment count
Return count

Here is the code:

int numOnesInBinary( int number ){
     int numOnes = 0;
     while( number != 0 ){
          number = number & (number – 1);
          numOnes++;
     }
     return numOnes;
}

This solution has a running time of O(m), where m is the number of 1’s in the solution.

Chapter 12: Counting, Measuring, and Ordering Puzzles

Tackling Brainteasers

This property of brainteasers works most strongly to your advantage when you are faced with a problem that has only two possible answers (for example, any yes or no question). Whichever answer seems at first to be correct is probably wrong. Of course, it’s probably not a good idea to say, “The answer must be yes because if it were no this would be a very simple problem and you wouldn’t have bothered to ask it.” You can, however, use this knowledge to guide your thinking.

Tip Remember that the obvious answer is almost never the right answer.

Solve the Right Problem

For example, suppose you are given the problem of finding an arrangement that maximizes the number of oranges you can fit in the bottom of a square box. You would probably automatically assume that the oranges are small spherical fruit, that they are all about the same size, that “in the bottom” means in contact with the bottom surface of the box, and that the oranges must remain intact (you can’t puree them and pour them in). These assumptions may seem ridiculous - they are all rather obvious and they are all correct. The point is that assumptions are inherent in all communication or thought; you can’t begin to work on a problem without assumptions.

Tip If the solution that seems logical is wrong, you made a false assumption. Categorize your assumptions, and try to identify those that are false.

Don’t Be Intimidated

You don’t have to devise a plan for getting all the way to the solution before you start - things will come to you as you work:

  • Break a problem into parts - If you can identify a subproblem, try solving that, even if you’re not sure it’s critical to solving the main problem.
  • Try a simplified problem - Try solving a simplified version of the problem; you may gain insights that will be useful in solving the full problem.
  • Try specific examples - If the problem involves some sort of process, try working through a few specific examples. You may notice a pattern you can generalize to other cases. Above all, keep talking, keep thinking, and keep working.

Estimation Problems

One estimation problem is “How many gas stations are there in the United States?” It has been so widely reported that this problem was posed by Microsoft that it seems almost certain to be apocryphal; nevertheless, it is a good example.

These problems are usually not difficult compared with the more common brainteasers. You’re not expected to know the actual statistic or fact. Instead, you are expected to do a rough order of magnitude calculation based on facts you do know. Because everything is an estimate anyway, try to adjust or round your figures so that any large numbers you use are powers (or at least multiples) of ten - this will significantly simplify your arithmetic.

Taking the gas station problem as an example, your calculation might go like this: “It takes me about six minutes to fill up my car. I go to the gas station about once a week, and there are usually two other cars there. If I assume this is average for Americans, each gas station services about 30 cars an hour. Suppose a gas station were open 12 hours a day, 7 days a week. That would be 84 hours a week. In reality, a gas station is probably open more than 12 hours a day, so I’ll say the average gas station is open 100 hours a week. That means it services 3,000 cars a week. There’s somewhere upwards of 250 million people in the United States. Not everyone has a car, so suppose there are 100 million cars on the road. If every car goes to the gas station once a week, like mine does, and each station sees 3,000 cars a week, there would have to be about 33,000 gas stations in the United States.” This figure is probably off by a lot, but it’s likely within an order of magnitude (that is, there are more than 3,300 gas stations and fewer than 330,000). It’s much more important that you are able to form a framework for the estimation and rapidly work through the calculations than that you accurately estimate the statistic. For more practice, try estimating the number of kindergarten teachers in your state, the circumference of the earth, and the weight of a ferry boat.

Brainteaser Problems

Chapter 13: Graphical and Spatial Puzzle

Tip Whenever possible, draw a picture.

Tip If the problem involves motion or change, draw multiple pictures of different points in time.

Tip Visualization may be more appropriate than diagramming for three-dimensional problems, but in either case, attack the problem spatially.

Graphical and Spatial Problems

Chapter 14: Knowledge-Based Questions

C++ versus Java

Important What are the differences between C++ and Java? C++ and Java are syntactically very similar. Java’s designers intended this to make it easy for C++ developers to learn Java. Apart from this area of similarity, Java and C++ differ in a variety of ways, largely because of their different design goals. Security, portability, and rapid development were of paramount importance in the design of Java, whereas C++ is more concerned with performance and backward compatibility with C. Java is compiled to virtual machine byte-code and requires a virtual machine to run; C++ is compiled to native machine code. This usually makes C++ faster, but it gives Java greater potential for portability and security.

C++ is a superset of C and maintains features such as programmer-controlled memory management, pointers, and a preprocessor for full backward compatibility with C. In contrast, Java eliminates these and other bug-prone features. Java replaces programmer memory deallocations with garbage collection. Java further dispenses with C++ features such as operator overloading and multiple inheritance. These choices are seen by some to make Java a better choice for rapid development and for projects where portability and security are more important than performance.

Tip A limited form of multiple inheritance can be simulated in Java using interfaces.

In Java, all objects are passed by reference, whereas in C++, the default behavior is to pass objects by value. Java does not perform automatic type casting as C++ does, though newer Java features such as generics and autoboxing now handle many common cases. In Java, all methods are virtual, meaning the implementation for a method is selected according to the type of the object as opposed to the type of the reference. In C++, methods must be explicitly declared as virtual. Java has defined sizes for primitive data types, while type sizes are implementation-dependent in C++ (and C).

In situations where there is legacy C code and a great need for performance, C++ has certain benefits, especially when low-level system access is required. In situations where portability, security, and speed of development are emphasized, Java (or a similar language such as C#) may be a better choice.

Garbage Collection

Important Discuss what garbage collection is and explain different ways it can be implemented.

Garbage collection is the process by which a program automatically finds and reclaims memory that is no longer used or no longer accessible by an application. This reclamation occurs without programmer assistance. C#, Java, Lisp, and Perl are examples of languages with garbage-collection facilities.

Garbage collection provides several advantages over having a programmer explicitly deallocate memory. It eliminates bugs due to dangling pointers and memory leaks. It also promotes greater simplicity in program and interface design because the complicated mechanisms traditionally used to ensure that memory is properly freed (such as “smart pointers” in C++) are unnecessary. In addition, because programmers don’t have to worry about memory deallocation, program development proceeds at a more rapid pace.

Garbage collection is not without its disadvantages, however. Garbage-collected programs often run more slowly because of the overhead needed for the system to determine when to deallocate and reclaim memory no longer needed. In addition, the system will occasionally over-allocate memory and may not free memory at the best possible time.

One method of garbage collection is to use reference counting. This involves tracking how many variables reference an object. Initially, there will be one reference to a piece of memory. The reference count will increase if the variable referencing it is copied. When a variable referencing an object changes value or goes out of scope, the object’s reference count is decremented. If a reference count ever goes to 0, the memory associated with the object is freed: If no one is keeping a reference to the object, then the object (and hence its memory) is no longer needed.

Reference counting is simple and relatively fast. However, it doesn’t handle circular references. Consider what happens in the case of a circularly linked list with nothing external pointing to it. Every element in the list has a nonzero reference count, yet the memory isn’t referenced by any object outside the list itself. Thus, the memory could safely be deallocated, but reference-based garbage collection won’t free it.

A second method of garbage collection is known as mark and sweep. In the first pass, the memory manager will mark all objects that can be accessed by any thread in the program. In the second pass, all unmarked objects are deallocated, or swept away.

Mark and sweep handles circular references, but it’s also less efficient. The garbage collector runs at different points in an application’s execution and may cause the application to pause while the garbage collecting occurs.

Network Performance

Important What are the two major issues in networking performance?

Any network can be measured by two major characteristics: latency and bandwidth. Latency refers to how long it takes a given bit of information to get through the network. Bandwidth refers to the rate at which data moves through the network once communication is established. The perfect network would have infinite bandwidth and no latency.

A pipe is a good analog for a network. The time it takes for a molecule of water to go through the whole pipe is determined by the length; this is analogous to the latency. The width of the pipe determines the bandwidth: how much water can pass in a given time.

Latency and bandwidth problems are often encountered when searching the Web. If you wait a long time for a page to display and then it appears quickly, this indicates good bandwidth but high latency. On the other hand, if a page starts loading right away but takes a long time to load, that is a symptom of a low-latency, low-bandwidth connection.

Hash Tables versus Binary Search Trees

Important Compare and contrast a hash table and a binary search tree. If you were designing the address book data structure for a personal digital assistant (PDA) with limited memory, which one would you use?

A binary search tree can insert and retrieve in O(log(n)). This is fast, though not as fast as a hash table’s O(1). A binary search tree, however, also maintains its data in sorted order.

In a PDA, you want to keep as much memory as possible available for data storage. If you use an unordered data structure such as a hash table, you need additional memory to sort the values, as you undoubtedly want to display the values in alphabetical order. Therefore, if you use a hash table, you have to set aside memory for sorting that could otherwise be used as storage space.

If you use a binary search tree, you won’t have to waste memory or processing time on sorting records for display. Although binary tree operations are slower than hash table operations, a device like this is likely to have no more than about 10,000 entries, so a binary search tree’s O(log(n)) lookup will undoubtedly be fast enough. For these reasons, a binary search tree is better suited for this kind of task than a hash table.

Chapter 15: Nontechnical Questions

“What Do You Want to Do?”

Always pay attention to who is asking this question. If it’s a human-resource representative scheduling interviews, be honest and tell him what you want to do. The HR rep will generally use this information to set up interviews with appropriate groups.

If you’re asked this question by a more-technical interviewer, watch out! If you answer this question poorly, you won’t get an offer. These interviewers ask this question partly because they want to find out your goals and ambitions. If you want to do something different from the job that is available, your interviewer will probably decide that you should look for a different job. If you want the job, make sure you indicate that you’re interested in doing it, sound sincere, and give a reason. For example, you could say, “I’ve always been interested in systems-level programming and really enjoy it, so I’m hoping to join a large company and do systems-level work.” Or, you could say, “I want to do Web programming so I can show my work to my friends. I’m hoping to do this at a start-up where I can use my Web server experience and watch the company grow.”

Sometimes, you may not be sure what specific kind of job you’re interviewing for. In these cases, you can always fall back on describing the company you’re applying to as the ideal company for you. Mention that you’re hoping to do development that’s exciting and provides a lot of opportunity to contribute and learn. You can say that you see the work as just one part of the package; other important parts are the team and the company. This sort of response shows that you have your act together and prevents you from talking your way out of a job.

There is a fine line between sounding enthusiastic and seeming dateless and desperate. No one wants an employee who has been rejected by everyone else. Make sure your answer never sounds like you’d be happy to take any sort of job the company would be willing to offer. This sort of response virtually guarantees nothing more than a “thank you for coming in” letter.

It’s also possible that you know exactly what you want to do and wouldn’t accept any other kind of job. If so, don’t talk yourself up for a job you’d never accept anyway. This approach may prevent you from getting some job offers, but they aren’t jobs that you want anyway. One advantage to expressing exactly what you want to do is that even if you don’t begin the day interviewing with an interesting group, you may end the day interviewing with such a group.

One final note on answering this question: It’s a good opportunity to mention that you want to work with a great team - don’t pass it up. Make sure that being a member of a great team comes across as one of your priorities.

“What Is Your Work Style?”

This question usually indicates that the company you’re interviewing with has an unorthodox work style. For example, it may be a start-up requiring long hours in cramped conditions or a larger company that’s just beginning a new project. Or perhaps they’re fervent believers in the two-person team programming model. In any case, know what your work style is and make sure it’s compatible with the company.

“Tell Me About Your Experience.”

This question is one that everyone should practice and have an answer for. We cannot overemphasize this! Make sure your answer highlights specific achievements and be enthusiastic as you talk about your projects. Enthusiasm is extremely important!

Talk not only about the factual aspects of your previous assignments, but also about what you learned. Talk about what went right, but also what went wrong. Describe positive and negative experiences and what you learned from each of them. Keep your response to around 30–60 seconds, depending on your experience. Again, be sure to practice this ahead of time.

“What Are Your Career Goals?”

This question gives you a chance to explain why you want this job (apart from the money) and how you see it fitting into your overall career. This is similar to the question about what you want to do. The employer is concerned that you may not want to do the job. In this case, it is because the job may not fit into your career goals. It’s certainly okay to be confused about what you want to do - many people are. Try to have at least a general idea of where you see yourself going. Your answer might be as simple as, “I’m hoping to work in development for a while and work on some great projects. Then, I’m looking to go into project management. Beyond that, it’s hard to say.” This answer shows motivation and convinces the employer that you’ll succeed on the job.

“Why Are You Looking to Change Jobs?”

Interviewers generally want to know what you don’t like to do. Clearly, you don’t like your last job or you would probably still be there. In addition, there’s a fear that you may be trying to cover a weakness that caused you to leave your last job. Therefore, try to answer this question by citing either a change in environment, a factor out of your control, or a weakness that the interviewer already knows. For example, to cite a change in environment you could say, “I’ve worked in a large company for five years and experienced the software development process for a mature product. I no longer want to be a number in a large company. I want to join a start-up and be a key person from the ground up and watch something grow.” Or, you could answer, “I worked at a start-up that didn’t have its act together. Now I want to work at a company that does.”

To cite a factor out of your control, you could say, “My current company has given up on the project I’ve been working on and they’re trying to relocate me to something that I don’t find interesting.” Or, you could respond, “My company was acquired and the whole atmosphere has changed since then.”

It’s also generally acceptable to cite a weakness that the interviewer already knows. You could say, “My last job required extensive systems-level programming. I was way behind everyone else on that topic, and I don’t find that sort of work very exciting. I’m much more interested in doing Web programming, which I do have experience in.”

One final note: Even though money is often a good reason to change jobs, be careful about citing it as a prime reason. This raises the possibility that your current employer doesn’t offer you more money because you’re not that valuable and you’re hoping someone else won’t notice this fact.

“How Much Money Do You Want to Make?”

This question may appear in any context. It’s most common, though, either at the initial screening or when the company has decided to make you an offer. If it’s asked at the beginning, the employer may want to know if it’s even worth talking to you, given your salary expectations, or the employer may genuinely have no idea what the position should pay. It is generally considered wise to put this question off as long as possible. It is not in your interest to discuss numbers until you’ve convinced the potential employer of your value. If you can’t escape this question in the early stages of an interview, try to give a range of salaries with the amount that you want at the low end. This gives you good bargaining room later.

If you’re asked the question near the end of the process, this can only indicate good things. If the interviewer has no interest in hiring you at this point, he won’t bother asking this question. Generally, larger companies have less latitude in compensation packages than smaller companies. If you’re asked this question, it probably indicates the company is willing to negotiate. It’s important to realize companies are often unaware of how to make a competitive offer. This is your chance to tell them how to do exactly that.

First, it’s important to do your homework ahead of time when answering this question. If you find that people with similar jobs in your area are making $40,000–$55,000 a year, you’re probably not going to make $80,000 a year. Second, never undersell yourself. If you’re looking for an annual salary of $70,000, don’t tell an employer that you’re looking for around $60,000 a year with the hope that the employer will, for some reason, offer more. This response makes it almost impossible for the employer to make a good offer. Third, consider carefully what you want in a total compensation package. You may be graduating from college and want a signing bonus to offset the costs of finding an apartment, moving, and placing deposits. Or, you may be looking to join a start-up offering generous stock options and slightly lower salaries. In any case, it’s important to figure out exactly what you’re looking for in terms of bonuses, benefits, stock options, and salary.

In general, try not to tip your hand too early when answering this question. The person with more information generally does better in a negotiation. Instead of answering a question about salary directly, ask what range the interviewer is prepared to offer. There are four possible answers to your question.

First, the range may be about what you expected. In this case, you can usually gain a slightly higher salary by following these rules: First, try not to act too excited - stay cool. Next, say that you had a similar but slightly higher range in mind, setting your minimum at the maximum of the offered range. For example, if the employer says, “We’re expecting to pay $40,000 to $45,000,” you should respond, “That seems about right. I’m looking to make $45,000 to $50,000 and hoping for the high end of that range.” Finally, negotiate in a professional manner until you agree on a number with the interviewer; you’ll probably receive an offer between $43,000 and $48,000.

The second possibility is that the negotiator starts with a range higher than you expected. This is great!

In the third case, the negotiator may not answer your question. He or she may give a response like “We have a wide range of salaries depending on the applicant. What were you expecting?” This response is actually quite favorable because it indicates that he or she probably has the authority to pay you a competitive salary. The response shows that the negotiator is willing to negotiate, but it also indicates that you may be subject to some hardball negotiating skills. Bearing in mind that negotiation will follow, respond with one number, the high end of your range. This gives you room to negotiate and still receive a favorable offer. For example, if you’re expecting between $55,000 and $60,000, say, “I’m expecting $60,000 a year.” Presenting it like this leaves the other negotiator less room to lowball you than if you give a range. Avoid weaker expressions like “I’m hoping for …” or “I’d really like …” The negotiator may accept your number, or may try to negotiate a slightly lower salary. If you remain professional and negotiate carefully, your final salary should fall within your desired range. Alternatively, the negotiator may respond by telling you that the company has a substantially lower range in mind. In this case, your response should be the same as in option four, which is described next.

The fourth option is that the offer may be less than you expected. In these cases, here are some tactics to try to increase the offer. First, re-emphasize your skills and state the salary range you were expecting. For example, if you were offered a salary of $35,000 but were expecting $50,000, you may say, “I have to admit I’m a little disappointed with that offer. Given my extensive experience with Web development and the contributions I can make to this company, I’m expecting a salary of $50,000.” The negotiator may need time to get back to you, which is perfectly fine. If the negotiator doesn’t increase the offer after hearing your range, he or she will often cite one of the following three reasons:

  • That amount wasn’t budgeted.
  • Similar employees at the company don’t make that much.
  • Your experience doesn’t warrant such a salary.

None of these is an acceptable reason. First, the budget may be a constraint on the company, but it shouldn’t be a constraint on your salary. If the company really wants you, it will find the money and a way around this artificial barrier. If the company truly can’t find the money, it’s such a cash-strapped, close-to-death organization that you probably don’t want to work there anyway.

Second, it doesn’t matter what the company pays other employees. That’s between the company and those employees. Other employees shouldn’t determine your compensation. You can respond by saying, “I wasn’t aware that my compensation would be tied to other employees’ compensations. I’m looking for a package that is commensurate with my skills of X and believe that $Y is such a package.”

Finally, if you’ve done your homework, you know your experience and skills do warrant such a salary and the company is trying to lowball you. Simply re-emphasize your skills and explain that, after doing your research, you know your desired salary is indeed the competitive market salary. The company may realize it is out of touch with the market and increase its offer.

If the negotiator does not increase the offer but you still want the job, you have two last-ditch tactics. First, you can say that you’re tempted to take the job, but that you’d like a salary review in six months to discuss your performance and compensation. You generally have a much stronger hand before you join a company, so you shouldn’t expect miracles. Most negotiators, however, will grant this request. Make sure you get it in writing if you go this route. Second, try to negotiate other parts of the package. For example, you may be able to get additional vacation days, flex hours, or a sign-on bonus.

Here are a few final thoughts on the salary issue. Some people are embarrassed or shy about talking about salary. You should realize that you’re already looking to engage in a business relationship, and salary is just one more part of the picture. No employer expects you to work for free, and there’s no reason you should act as if compensation isn’t important.

Many negotiators will cite factors such as benefits or work style to draw you to a company. These factors may be important reasons to join a company, and you’d certainly want all of the benefits spelled out. These perks, though, are generally not negotiable. Don’t bother discussing non-negotiable factors in a negotiation, and don’t get sidetracked if your negotiator mentions them.

“Why Should We Hire You?”

This question is obnoxious, rude, and belittling. It implies that there’s no obvious reason why you’re qualified for the job. Clearly, you have skills and experience that make you qualified; otherwise, the interviewer wouldn’t be talking to you. In these instances, avoid becoming defensive and reciting your résumé to list your qualifications. Instead, keep things positive by talking about why you want to work at the company and why the job is a good match for your skills. This response shows you can handle criticism and may deflect your interviewer.

“Do You Have Any Questions for Me?”

Conventional wisdom has always said to ask a question because it shows enthusiasm. Nothing spoils a good interview, though, like asking a stupid question right at the end. Asking a contrived question just because you feel you should won’t count in your favor. A thoughtful and articulate question can tell you a lot about the company and impress your interviewer. Often, your interviewer doesn’t tell you what he or she does. This is a good time to ask. It lets you know more about what you would potentially be doing and shows genuine interest in the person. In addition, if the interviewer mentioned anything during the interview that sounded interesting, ask for more detail about it. This can yield further insight into your potential future employer. Finally, if you don’t have questions, you can make a joke of it. You could say, “Gee, I know that I’m supposed to ask a question, but the people I interviewed with this morning answered all my questions. I guess you’re off the hook!”

Chapter 16: Conclusion

  • Keep your brain nimble - Puzzles are always good ways to train your mind to think outside the box.
  • Read about programming - Try to stay current with the latest ideas and trends in computing.
  • Visit technical interview sites - A Web search with the phrase “technical interview questions” will list a number of sites with questions similar to those found in this book. (Answers aren’t usually provided, but even if they are it’s better to work through the problems yourself.)

Author: Shi Shougang

Created: 2015-03-05 Thu 23:21

Emacs 24.3.1 (Org mode 8.2.10)

Validate