Woodstock Blog

a tech blog for general algorithmic interview questions

[NineChap 2.1] Binary Search

Binary Search

Recursion or While-Loop?

In general, it’s never a good idea to do binary search with recursion, because that’ll make the interview too boring.

Template

link

This template is able to locate the first (or last) occurance of an element when array contains duplications.

If item too small/large, left/right boundary is returned.

Read Question “Search for a Range” for more details.

int binarySearch(vector<int> &A, int target) {
    if (A.size() == 0) {
        return -1;
    }

    int start = 0;
    int end = A.size() - 1;
    int mid;

    while (start + 1 < end) {
        mid = start + (end - start) / 2;
        if (A[mid] < target) {
            start = mid;
        } else {
            end = mid;
        }
    }

    if (A[start] == target) {
        return start;
    }
    if (A[end] == target) {
        return end;
    }

    return -1;
}

Keypoints

  1. start + 1 < end
  2. start + (end-start)/2
  3. A[mid] ==, <, >
  4. A[start/end] == target

Question list

Binary search

  1. Search Insert Position

  2. Search for a Range

  3. Search in Rotated Sorted Array

  4. Search in Rotated Sorted Array II

  5. Search a 2D Matrix

Additional

  1. Search a 2D Matrix II - A tricky question

  2. Find the First Bad Version

    The code base version is an integer and start from 0 to n. One day, someone commit a bad version in the code case, so it caused itself and the following versions are all failed in the unittests. You can determine whether a version is bad by the following interface:

    boolean isBadVersion(int version);

    Find the first bad version.

  3. Find a peek

    There is an array which we can assume the numbers in adjcent positions are different. and A[0] < A[1] && A[A.length - 2] > A[A.length - 1]. We consider a position P is a peek if A[P] > A[P-1] && A[P] > A[P+1]. Find a peek in this array.

Code

All following code are written with the template provided above.

Search Insert Position

public int searchInsert(int[] A, int target) {
    // 6 minutes
    if (A == null || A.length == 0) {
        return 0;
    }
    int left = 0, right = A.length - 1;
    int mid;
    while (left + 1 < right) {
        mid = left + (right - left) / 2;
        if (A[mid] < target) {
            left = mid;
        }
        else {
            right = mid;
        }
    }
    if (target <= A[left]) {
        // equal or less than first element
        return left;
    }
    else if (A[left] < target && target <= A[right]) {
        return right;
    }
    else {
        // bigger than largest element
        return right + 1;
    }
}

Search for a Range

public int[] searchRange(int[] A, int target) {
    // 6 minutes
    int[] result = new int[2];
    result[0] = -1;
    result[1] = -1;
    if (A == null || A.length == 0) {
        return result;
    }
    // find the start point of target
    int left = 0, right = A.length - 1, mid;
    while (left + 1 < right) {
        mid = left + (right - left) / 2;
        if (A[mid] < target) {
            left = mid;
        }
        else {
            right = mid;
        }
    }
    if (A[left] == target) {
        result[0] = left;
    }
    else if (A[right] == target) {
        result[0] = right;
    }
    else {
        return result;
    }
    // find the end point of target
    left = 0;
    right = A.length - 1;
    while (left + 1 < right) {
        mid = left + (right - left) / 2;
        if (A[mid] <= target) {
            left = mid;
        }
        else {
            right = mid;
        }
    }
    if (A[right] == target) {
        result[1] = right;
    }
    else if (A[left] == target) {
        result[1] = left;
    }
    return result;
}

Search in Rotated Sorted Array

Note: this is an high-freq qeustion. Every company will ask this question.

The solution is using 4 if-conditions. It takes long time first, because I compare A[mid] with target. It become complex.

We should compared A[left] and A[mid] first, then it’ll be much easier for coding.

public int search(int[] A, int target) {
    // this is the 4th time that I do this question
    // 7 minutes
    if (A == null || A.length == 0) {
        return -1;
    }
    int left = 0;
    int right = A.length - 1;
    int mid;
    while (left + 1 < right) {
        mid = left + (right - left) / 2;
        if (A[mid] == target) {
            return mid;
        } else if (A[left] < A[mid]) {
            if (A[left] <= target && target < A[mid]) {
                right = mid;
            } else {
                left = mid;
            }
        } else {
            if (A[mid] < target && target <= A[right]) {
                left = mid;
            } else {
                right = mid;
            }
        }
    }
    if (A[left] == target) {
        return left;
    } else if (A[right] == target) {
        return right;
    } 
    return -1;
}

Search in Rotated Sorted Array II

There are multiple ways to remove duplications. My previous solution is removing duplicate before entering the while-loop, which is a very good idea.

Binary can’t be used, because there might be: value of start = mid = end. In this case, the entire list needs to be search. Impossible!

The worse case will anyway take O(n) time. To indicate the time complexity is regardless of binary search, Mr. Huang suggests the following code:

public boolean search(int[] A, int target) {
    for (int i = 0; i < A.length; i ++) {
        if (A[i] == target) {
            return true;
        }
    }
    return false;
}

Search a 2D Matrix

my code (2D search):

public boolean searchMatrix(int[][] matrix, int target) {
    // 13 miniutes
    if (matrix == null || matrix.length == 0 || matrix[0].length == 0) {
        return false;
    }
    int m = matrix.length;
    int n = matrix[0].length;
    // find target vertically from matrix[0] to matrix[m-1]
    int top = 0, bottom = m - 1;
    int mid;
    while (top + 1 < bottom) {
        mid = top + (bottom - top) / 2;
        if (matrix[mid][0] < target) {
            top = mid;
        }
        else {
            bottom = mid;
        }
    }
    // locate the row number
    int row = -1;
    if (matrix[top][0] <= target && target <= matrix[top][n-1]) {
        row = top;
    }
    else if (matrix[bottom][0]<=target && target <= matrix[bottom][n-1]) {
        row = bottom;
    }
    else {
        return false;
    }
    // now find target from matrix[row]
    int left = 0, right = n - 1;
    while (left + 1 < right) {
        mid = left + (right - left) / 2;
        if (matrix[row][mid] < target) {
            left = mid;
        }
        else {
            right = mid;
        }
    }
    return (matrix[row][left] == target || matrix[row][right] == target);
}

better code (1D search):

public boolean searchMatrix(int[][] matrix, int target) {
    int rows = matrix.length;
    int cols = matrix[0].length;
    int start = 0;
    int end = rows * cols - 1;
    while (start <= end) {
        int mid = (start + end) / 2;
        // Tricks to treat it as a 1-D array
        int digit = matrix[mid / cols][mid % cols];
        if (target == digit) {
            return true;
        } else if (target > digit) {
            start = mid + 1;
        } else {
            end = mid - 1;
        }
    }
    return false;
}

Search a 2D Matrix II

Read my new post for details.

Find the First Bad Version

A simple binary search.

Find a peek

A binary search, and for each ‘mid’ point, judge weather it’s a peek, or it’s upward sloping, or downward sloping.

Code skipped.

Conclusion

Always try to exclude a half.

[Testing] Software Testing

First Word

There are generally four levels of tests: unit testing, integration testing, system testing, and acceptance testing.

Software testing methods are traditionally divided into white-box and black-box testing.

One of the testing methodologies is “V-model”.

Testing Types and process

Regression testing

There’re a lot of types, one of them is called “Regression testing”.

Regression testing focuses on finding defects after a major code change has occurred. In other words, it encsures that changes does not introduce new faults.

Testing process

Traditional: waterfall. Testing is done by independent group of testers after development. This practice often results in the testing phase being used as a project buffer to compensate for project delays.

New trend: Agile or Extreme, which adhere to TDD Model. In this process, unit tests are written first. This methodology increases the testing effort done by development.

Bottom Up Testing: lowest level components are tested first, then integrated and used to facilitate the testing of higher level components.

Top Down Testing: top integrated modules are tested and the branch of the module is tested step by step until the end.

Testing Methods

Black-box

Black-box testing is a method of software testing that examines the functionality of an application without peering into its internal structures or workings. This can be applied to every level of software testing: unit, integration, system and acceptance.

White-box

White-box testing (also known as structural testing) is a method of testing software that tests internal structures or workings of an application, as opposed to its functionality. In white-box testing, an internal perspective of the system and programming skills are used to design test cases. The tester chooses inputs to exercise paths through the code and determine the appropriate outputs.

Other-box

A black-box tester is unaware of the internal structure of the application to be tested, while a white-box tester has access to the internal structure of the application.

Gray-box testing is a combination of both. Testers require both highlevel and detailed knowledge of the application.

Whitebox in detail

Whitebox testing is based on program code. The extent to which source code is executed (covered).

  1. statement coverage
  2. path coverage
  3. (multiple-) condition coverage
  4. decision / branch coverage
  5. loop coverage
  6. definition-use coverage

Use of “flow graphs” to test the coverage. source

Blackbox in detail

  1. Equivalence partitioning
  2. Boundary value analysis
  3. Behavioural testing (interaction with w/ an environment)
  4. Random testing (random walk thru the system/mouse click)
  5. Stress testing (huge data, DoS attack, power off)
  6. Error guessing (Ad hoc, not really a technique)

Equivalence partitioning

This is related to “validate” input. First identify input equivalence classes, then make the test case by changing each valid cases into invalid.

The testing theory related to equivalence partitioning says that only one test case of each partition is needed. In other words it is sufficient to select one test case out of each partition to test the program. To use more cases will not find new faults.

The values within one partition are considered to be “equivalent”. Thus the number of test cases can be reduced considerably.

Example:

There are 12 months per year

Valid partition is from january to december

Invalid partition is from <=0 and >=13

A longer but better example

Boundary Value Analysis

  1. Testing boundary conditions (directly on, above, and beneath the edges)
  2. Choose input boundary values
  3. Choose input boundary values that reaches output boundary (given the input value, choose expected output +/- 1)

Example:

Suppose an integer boundary is 1 to 50, Then test for 0,2 values & 49, 51 vales.

Suppose input = 5, output = 100, then test input = 5, output = 99, 100, 101

Don’t start with designing white-box test cases! Start with black-box test cases, then check white-box coverage.

source

More examples

Q: how to test a log-in window?

A: Eq. class: username empty, exits, don’t exist and exceed length limit. Same for password field. Boundary: input length equals, less than or larger than length limit. source

Q: how to test a vendor machine?

  1. Functional: put in $$ and get a coke

  2. Edge condition: put in Chinese RMB?

  3. Stress: keep putting $$ and popping goods

  4. Security: can you break the machine?

Last Word

Sometime it is not feasible to test everything. Instead, prioritize your testing so that you only focus on areas that present the greatest risk or have the greatest probability of occurring.

For example, you might choose to test the slowest client computer, the busiest server, or the least reliable network link.

source.aspx)

[Question] ASCII, Utf-8, Utf-16 and Unicode

First Word

ASCII, UTF-? are encoding schemes. Unicode is character set.

Or in other words, UTF-8 is an encoding used to translate binary data into numbers. Unicode is a character set used to translate numbers into characters.

ASCII

ASCII is a character-encoding scheme based on the English alphabet. It encodes 128 characters into 7-bit integers.

ASCII was the most commonly used character encoding on the World Wide Web until December 2007, when it was surpassed by UTF-8, which includes ASCII as a subset.

UTF-8

UCS Transformation Format—8-bit

UTF-8 is a variable-width encoding that can represent every character in the Unicode character set. It uses 1 byte for ASCII characters, and up to 4 bytes for others.

UTF-8 is the dominant character encoding for the World Wide Web, accounting for more than half of all Web pages.

Utf-8 is good because:

  1. backward compatibility with ASCII

  2. avoid the complications of endianness and byte order marks in UTF-16 and UTF-32

UTF-8 has become the dominant character encoding for the World Wide Web, accounting for more than half of all Web pages

UTF-16 and UTF-32

UTF-16 is used for text in the OS API in Microsoft Windows 2000/XP/2003/Vista/7/8/CE. The word “Unicode” means something else according to this person:

Windows uses UTF-16LE encoding internally as the memory storage format for Unicode strings, it considers this to be the natural encoding of Unicode text. In the Windows world, there are ANSI strings and there are Unicode strings (stored internally as UTF-16LE).

This was all devised in the early days of Unicode, before UTF-8 was invented. This is why Windows’s support for UTF-8 is all-round poor.

UTF-8: “size optimize”:

Best suited for Latin character based data (or ASCII), it takes only 1 byte.

UTF-16: “balance”

It takes minimum 2 bytes per character which is enough for existing set of the mainstream languages with having fixed size on it to ease character handling, but size is still variable and can grow up to 4 bytes per character.

UTF-32: “performance”

Allows using of simple algorithms as result of fixed size characters (4 bytes) but with memory disadvantage

Unicode

Unicode is a computing industry standard for the consistent encoding. It expresses most of world’s writing systems with more than 110,000 characters covering 100 scripts and symbols.

Unicode can be implemented by Utf-8, Utf-16 and now-obsolete Ucs-2. When unicode is encoded with UTF-8, its code value is same as ASCII.

Table of the scheme

Bits of
code point
First
code point
Last
code point
Bytes in
sequence
Byte 1 Byte 2 Byte 3 Byte 4 Byte 5 Byte 6
  7 U+0000 U+007F 1 0xxxxxxx
11 U+0080 U+07FF 2 110xxxxx 10xxxxxx
16 U+0800 U+FFFF 3 1110xxxx 10xxxxxx 10xxxxxx
21 U+10000 U+1FFFFF 4 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
26 U+200000 U+3FFFFFF 5 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
31 U+4000000 U+7FFFFFFF 6 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx

Example

Translate ‘€’ into binary Utf-8.

  1. The Unicode code point for “€” is U+20AC.

  2. According to the scheme table above, this will take 3 bytes (16 bits).

  3. Convert hexadecimal 20AC into a 16-bit binary 0010000010101100.

  4. Fill in the 16 bits into: 1110xxxx 10xxxxxx 10xxxxxx, we got 11100010 10000010 10101100

  5. Almost done. The result can be concisely written in hexadecimal, as E2 82 AC.

Note that the last byte of any encoding starts with ‘0’, in this way computer can easily identify the encoding length of every character. Also note that ASCII code is natively supported by Utf-8.

[Question] Junit Hand-on Notes

First Word

Junit is open source testing framework developed for unit testing java code. It is now the default framework for testing Java development.

Is it popular?

A very interesting survey shows that JUnit is THE MOST POPULAR LIBRARY FOR JAVA used by 30.7% of projects (it tied with slf4j-api).

BTW, Apache Commons and Google Guava are the most important common libraries for Java.

Unit Test

A unit test is a piece of code that executes a specific functionality in the code. Tests should be written for critical and complex parts of your application (not every single statement). The percentage of code which is tested by unit tests is typically called test coverage.

Unit test is really a coding process, not a testing process.

In contrast, there is “Manual testing”.

Why should we use it?

  1. Test early and often automated testing.

  2. Junit tests can be integrated with the build so regression testing can be done at unit level.

  3. Test code reusage.

Other kinds of tests?

Integration Test (also called Functional Test) test the behavior of a component or the integration between a set of components.

Performance tests are used to benchmark software components in a repeatable way

TDD

TDD (Test-Driven Development) write unit test before writing the code. It is commonly believed that tests should be written before the code during development cycle.

Some believes that TDD results in less time in the debugger, and more time thinking about outside-in design. Some also believes that TDD is more fun than writing tests after the code seems to be working.

Junit

In Java, ‘assert’ is a keyword. So, JUnit 3.7 deprecated assert() and replaced it with assertTrue(). JUnit 4 is compatible with the assert keyword.

Test Class/Suite

Test methods are contained in a class called Test class. JUnit assumes that all test methods can be executed in an arbitrary order (thus no dependency).

Several test classes can be combined into a test suite. Running a test suite will execute all test classes in the specified order.

Test fixtures

Fixtures is a fixed state of a set of objects used as a baseline for running tests. It includes:

  1. setUp() method which runs before every test invocation.

  2. tearDown() method which runs after every test method.

“extends TestCase” or @annotation?

JUnit 3-style: create a class that “extends TestCase”, and start test methods with the word test. In Eclipse, all methods named “testSomething” are automatically run.

JUnit 4-style: create a ‘normal’ class and prepend a @Test annotation to the method (name do not have to be “testSomething”). This is recommended way.

Other than normal test

There are:

  1. Expected Exception Test: @Test(expected = ArithmeticException.class)

  2. Ignore Test: @Ignore(“Not Ready to Run”)

  3. Time Test: @Test(timeout = 1000)

  4. Suite Test: @Suite.SuiteClasses({ JunitTest1.class, JunitTest2.class })

  5. Parameterized Test: read below

Junit 4 has introduced a new feature Parameterized tests. Parameterized tests allow developer to run the same test many times using different values (passed in as a collecetion). One good example.

Miscellaneous

Q: What happens if a JUnit Test Method is Declared as “private”?

A: If a JUnit test method is declared as “private”, it compiles successfully. But the execution will fail. This is because JUnit requires that all test methods must be declared as “public”.

Q: How do you test a “protected” method?

Access Levels
Modifier Class Package Subclass World
public Y Y Y Y
protected Y Y Y N
no modifier Y Y N N
private Y N N N

A: When a method is declared as “protected”, it can only be accessed within the same package where the class is defined. Hence to test a “protected” method, define test class in the same package.

Things that I don’t understand

Q: How do I test things that must be run in a J2EE container (e.g. servlets, EJBs)?

A: Refactoring J2EE components to delegate functionality to other objects that don’t have to be run in a J2EE container will improve the design and testability of the software. Cactus is an open source JUnit extension that can be used for unit testing server-side java code.

Q: What are Parameterized tests in JUnit?

A: Parameterized tests allow developer to run the same test over and over again using different values.

[LeetCode 151] Reverse Words in a String

Question

link

Given an input string, reverse the string word by word.

For example,
Given s = "the sky is blue",
return "blue is sky the".

Clarification:

  • What constitutes a word?
    A sequence of non-space characters constitutes a word.
  • Could the input string contain leading or trailing spaces?
    Yes. However, your reversed string should not contain leading or trailing spaces.
  • How about multiple spaces between two words?
    Reduce them to a single space in the reversed string.

Stats

Adjusted Difficulty 3
Time to use --------

Ratings/Color = 1(white) 2(lime) 3(yellow) 4/5(red)

Analysis

This is an very classic question, and there is a very nice solution.

Solution

First solution is read each character from the back, and insert each word into answer String. I used this method, and passed in 2 attempts. I have always been afriad of string question, and this time it gave me an easy time.

I post my code below. Altough it can be better if I use StringBuilder. Most blog/forum would give this solution including this one.

However, previous solution uses extra space. We can do it in-place suggested by this post.

Reverse the entire string, then reverse the letters of each individual word.

After the first pass the string will be

s1 = "Z Y X si eman yM"
    

and after the second pass it will be

s1 = "Z Y X is name My"
    

There are 2 things to note.

One, the edge cases are easily omitted, like all-space input, double-space in between, and a lot more.

Two, The Clarification part is extremely helpful for writing a bug-free solution. It’s always better to clarify further about what the question is ask. For example, can we use String.split() and String.substring()? (normally we would better not to) Can we use any extra space? That decides weather we copy by substring or by character.

In conclusion, this is an easy question that’s not easy to get correct answer. Practise more! And since this is the last post of Leetcode questions, my focus from tomorrow onwards will be shifted to “CC150”. Thanks for reading!

Code

My code. pointer solution.

public String reverseWords(String s) {
    if (s.length() == 0) return "";
    int p = s.length() - 1;
    // p points to the last non-space character
    String ans = "";
    while (p >= 0) {
        int j = p;
        while (j >= 0 && s.charAt(j) != ' ') {
            j --;
        }
        ans += s.substring(j + 1, p + 1) + " ";
        p = j;
        while (p >= 0 && s.charAt(p) == ' ') {
            p--;
        }
    }
    return ans.trim();
}

Updated on Sep 12th: updated with 3-step-reverse method.

public String reverseWords(String s) {
    if (s == null || s.length() == 0) {
        return s;
    }
    StringBuilder ans = new StringBuilder();
    int len = s.length();
    int p = len - 1;
    while (p >= 0) {
        while (p >= 0 && s.charAt(p) == ' ') {
            p--;
        }
        if (p == -1) {
            break;
        }
        StringBuilder word = new StringBuilder();
        while (p >= 0 && s.charAt(p) != ' ') {
            word.append(s.charAt(p));
            p--;
        }
        if (ans.length() == 0) {
            ans.append(word.reverse().toString());
        } else {
            ans.append(" " + word.reverse().toString());
        }
    }
    return ans.toString();
}

[LeetCode 143] Reorder List

Question

link

Given a singly linked list L: L0L1→…→Ln-1Ln,
reorder it to: L0LnL1Ln-1L2Ln-2→…

You must do this in-place without altering the nodes' values.

For example,
Given {1,2,3,4}, reorder it to {1,4,2,3}.

Stats

Adjusted Difficulty 4
Time to use --------

Ratings/Color = 1(white) 2(lime) 3(yellow) 4/5(red)

Analysis

This is a difficult question to think.

I first solved it with an stack which stores the second half nodes. It works. However, IT IS NOT A IN-PLACE SOLUTION, thus it’s wrong.

Eventually I did not solve it.

There is only one standard solution from the Internet. This blog explains it.

  1. Break list in the middle to two lists (use fast & slow pointers)
  2. Reverse the order of the second list
  3. Merge two list back together

Simple, right? Because of the nature of linked list, a lot of things can be done in-place, so we need not use any other data strucutres.

Solution

The code is a bit lengthy and difficult to write. It took me a while, but passed in 1 go.

Code

First, code written by me

public void reorderList(ListNode head) {
    if (head == null || head.next == null) {
        return;
    }
    ListNode slow = head, fast = head;
    while (fast != null) {
        if (fast.next != null && fast.next.next != null) {
            fast = fast.next.next;
            slow = slow.next;
        }
        else {
            fast = null;
        }
    }
    // if length = 2, slow point to 1st
    // if length = 3, slow point to 2nd
    // if length = 4, slow point to 2nd
    ListNode secondHalf = slow.next;
    slow.next = null;
    // now reverse secondHalf
    ListNode p = secondHalf;
    while (p.next != null) {
        ListNode tail = p.next.next;
        p.next.next = secondHalf;
        secondHalf = p.next;
        p.next = tail;
    }
    // now merge 2 list: head and secondHalf
    ListNode a = head, b = secondHalf;
    while (a != null && b != null) {
        ListNode temp1 = a.next;
        ListNode temp2 = b.next;
        a.next = b;
        b.next = temp1;
        a = temp1;
        b = temp2;
    }
}

Second, code written by someone

public void reorderList(ListNode head) {
    // IMPORTANT: Please reset any member data you declared, as
    // the same Solution instance will be reused for each test case.
    if (head == null || head.next == null) return;
    ListNode fast = head;
    ListNode slow = head;
    while(fast != null && fast.next != null) {
        fast = fast.next.next;
        slow = slow.next;
    }
    ListNode reverseHead = slow.next;           // find the second half of list
    slow.next = null;                           // make first half end point to null
    reverseHead = reverse(reverseHead);         // reverse second half     
    ListNode cur = head;        
    while(reverseHead != null) {                // link together
        ListNode tmp = reverseHead.next;
        reverseHead.next = cur.next;
        cur.next = reverseHead;
        cur = cur.next.next;
        reverseHead = tmp;
    }
}
private ListNode reverse(ListNode head) {
    if (head == null || head.next == null) return head;
    ListNode prev = new ListNode(0);
    prev.next = head;
    head = prev;
    ListNode cur = prev.next;
    while(cur.next != null) {
        ListNode tmp = cur.next;
        cur.next = tmp.next;
        tmp.next = prev.next;
        prev.next = tmp;
    }
    return prev.next;
}

[LeetCode 149] Max Points on a Line

Question

link

Given n points on a 2D plane, find the maximum number of points that lie on the same straight line.

Stats

Adjusted Difficulty 5
Time to use --------

Ratings/Color = 1(white) 2(lime) 3(yellow) 4/5(red)

Analysis

This is a difficult coding question.

The idea is simple. For n points, there are n * (n-1) lines. Check slopes and then count total, we would get the answer.

However, coding of this idea is very difficult.

Solution

Firstly, there are 2 special cases when calculating the slope. The 2 points may locate in same position. And when point1.x = point2.x, slope = infinity. It’s easy to omit these 2 cases and result in mistake.

Secondly, when we count, we declare 2 variables: samePointNumber and maxPointCountWithSameSlope. It’s very important to initialize both values to 1 instead of 0! Why? Because these values just can’t be 0. I failed my 2nd version code when input = {(0,0), (0,0)}, the program shows result of 0, instead of 2.

Thirdly, about what data structure to use for counting. There is a discussion about this at here

  1. storing the vertical slopes as Double.NaN. That allows Double to represent every slope uniquely as (y/x).
  2. It is unsafe using floating points to make a hash, and -0.0 != 0.0

It’s great that using Double.NaN, it saves us time and effort to count vertical points. Second point is very valid, but it turns out that using HashMap<Double, Integer> can AC.

P.S. It is always not a good practise to use Double as hash key. See here.

Fourthly, I made a mistake here:

double slope = (p.y - q.y) / (p.x - q.x);

And it’s wrong. Why? Note that Point.x and Point.y are both integers. Integer division will return integer. We must cast it.

double slope = (double) (p.y - q.y) / (p.x - q.x);

Last, OMG I wish this is last, but not least, we can reduce execution time to half by checking only the points with larger index than the anchor point (that’s the name for ‘current point’). Good idea, right?

One more thing, how to iterate thru the HashMap (value only)? There is an easy way:

for (Integer a : map.values()) {
    a;
}

That’s all I’ve found for now.

Updated on Aug 12th, 2014

Based on the solution given in CC150 v4 Q10.6 on Page 199, it’s a proper way to solve with HashMap<Line, Integer> instead of using HashMap<Double, Integer>.

The reason is mentioned, it’s ‘unsafe using floating points to make a hash’.

Note that if we were to write our own ‘Line’ class, we must override the 2 methods:

  1. public int hashCode() {}
  2. public boolean equals(Object o) {}

Code

written by me, Version3 using HashMap

public int maxPoints(Point[] points) {
    if (points.length <= 1)
        return points.length;
    HashMap<Double, Integer> map = null;
    int totalMax = 0;
    for (Point p : points) {
        int samePoint = 1;
        map = new HashMap<Double, Integer>();
        for (Point q : points) {
            if (q == p || p.x > q.x) {
            } else if (p.x == q.x && p.y == q.y) {
                samePoint++;
            } else {
                double slope = Double.NaN;
                if (p.x != q.x) {
                    slope = (double) (p.y - q.y) / (p.x - q.x);
                }
                if (!map.containsKey(slope)) {
                    map.put(slope, 1);
                }
                map.put(slope, map.get(slope) + 1);
            }
        }
        int pointMax = 1;
        for (Integer a : map.values()) {
            pointMax = Math.max(pointMax, a);
        }
        totalMax = Math.max(totalMax, pointMax + samePoint - 1);
    }
    return totalMax;
}

[LeetCode 146] LRU Cache

Question

link

Design and implement a data structure for Least Recently Used (LRU) cache. It should support the following operations: get and set.

get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1.
set(key, value) - Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item.

Stats

Adjusted Difficulty 4
Time to use Very difficult

Ratings/Color = 1(white) 2(lime) 3(yellow) 4/5(red)

Analysis

This is a difficult question, I can’t write the solution easily even after a month.

Solution

The solution is to use a Doubly-linked-list and a HashMap. Doing this allows O(1) search, remove and insert. A very nice and sophisticated data structure example, and very high frequency in interviews.

2 important things to note while coding:

  1. We need 2 helper methods: removeNode() and setNodeAsHead().

    Because we reuse both methods for get() and set() methods.

  2. Initialization of LRU

    We need 5 variables: capacity, current size(optional but good to have), hashmap, head, tail.

  3. Initialization of DoubleLinkedListNode

    This is easy, but do not forget about both key and value variable. We must use DoubleLinkedListNode.key when we want to delete tail.

Code

Updated on July 1st, 2014.

This question tests your ability to write some DS by yourself, eg. DoubleLinkedList.

public class LRUCache {

    int size;
    int capacity;

    DoubleLinkedList head;
    DoubleLinkedList tail;
    HashMap<Integer, DoubleLinkedList> map;

    public LRUCache(int capacity) {
        this.size = 0;
        this.capacity = capacity;
        head = null;
        tail = null;
        map = new HashMap<Integer, DoubleLinkedList>();
    }

    public void remove(DoubleLinkedList node) {
        if (node == head && node == tail) {
            head = null;
            tail = null;
        } else if (node == head) {
            head.next.prev = null;
            head = head.next;
        } else if (node == tail) {
            tail.prev.next = null;
            tail = tail.prev;
        } else {
            node.prev.next = node.next;
            node.next.prev = node.prev;
        }
        node.prev = null;
        node.next = null;
    }

    public void setHead(DoubleLinkedList node) {
        node.next = head;
        node.prev = null;
        if (head != null) {
            head.prev = node;
        }

        head = node;
        if (tail == null) {
            tail = node;
        }
    }

    public int get(int key) {
        if (!map.containsKey(key)) {
            // if key is not found
            return -1;
        } else {
            // if key is found
            DoubleLinkedList target = map.get(key);
            remove(target);
            setHead(target);
            return head.val;
        }
    }

    public void set(int key, int value) {
        if (this.get(key) != -1) {
            // key exist before, just replace the old value
            DoubleLinkedList old = map.get(key);
            old.val = value;
        } else {
            // this is a new key-value pair, insert it
            DoubleLinkedList newHead = new DoubleLinkedList(key, value);
            map.put(key, newHead);
            setHead(newHead);
            if (size == capacity) {
                // delete tail
                map.remove(tail.key);
                remove(tail);
            } else {
                size++;
            }
        }
    }

    class DoubleLinkedList {
        int key;
        int val;
        DoubleLinkedList prev;
        DoubleLinkedList next;
        public DoubleLinkedList(int k, int v) {
            this.key = k;
            this.val = v;
        }
    }
}

[Design] Implemention of DFS Using a Stack

First Word

This post talks about how to implement DFS without recursion.

We have studied 2 kinds of DFS in the post DFS, BFS and space efficiency. We will skip “true DFS” here, and only talk about “pseudo-DFS” implementation.

Remember, space usage of psudo-DFS is O(depth x branching factor).

Analysis

A DFS without recursion is basically the same as BFS - but use a stack instead of a queue as the data structure.

More details are discussed in this thread.

Implementation

The following code come from this post.

DFS(source):
  s <- new stack
  visited <- {} //empty set
  s.push(source)
  while (s.empty() == false):
    current <- s.pop()
    if (current is in visited):
        continue
    visited.add(current)
    //do something with current
    for each node v such that (source,v) is an edge:
        s.push(v)

[LeetCode 150] Evaluate Reverse Polish Notation

Question

link

Evaluate the value of an arithmetic expression in Reverse Polish Notation.

Valid operators are +, -, *, /. Each operand may be an integer or another expression.

Some examples:

  ["2", "1", "+", "3", "*"] -> ((2 + 1) * 3) -> 9
  ["4", "13", "5", "/", "+"] -> (4 + (13 / 5)) -> 6

Stats

Adjusted Difficulty 2
Time to use --------

Ratings/Color = 1(white) 2(lime) 3(yellow) 4/5(red)

Analysis

This is a classic question. It’s easy.

Code

my code

public int evalRPN(String[] tokens) {
    if (tokens == null || tokens.length == 0)
        return 0;
    Stack<Integer> stack = new Stack<Integer>();
    for (int p = 0; p < tokens.length; p ++) {
        char cur = tokens[p].charAt(0);
        if (cur >= '0' && cur <= '9' 
                || tokens[p].length() > 1)
            stack.push(Integer.parseInt(tokens[p]));
        else if (cur == '+') {
            int num1 = stack.pop();
            int num2 = stack.pop();
            stack.push(num1 + num2);
        }
        else if (cur == '-') {
            int num1 = stack.pop();
            int num2 = stack.pop();
            stack.push(num2 - num1);
        }
        else if (cur == '*') {
            int num1 = stack.pop();
            int num2 = stack.pop();
            stack.push(num1 * num2);
        }
        else if (cur == '/') {
            int num1 = stack.pop();
            int num2 = stack.pop();
            stack.push(num2 / num1);
        }
    }
    return stack.pop();
}