I am currently reading cracking the coding interview and looking at questions on leetcode and have encountered the same confusion in both places. Specifically with LinkedList problems, which often involve using nodes and creating custom class implementations of Linked Lists. Now I understand what a LinkedList is and how each element is called a "node", etc. But this level seems to be too low level when actually working with LinkedList java data structure and is causing me confusion.
Does any of this actually have to do with java Collections List/ LinkedList api? it doesn't seem so. For example if I search the LinkedList api for "node", I don't even get a single hit.
Take the following leetcode question:
You are given two non-empty linked lists representing two non-negative
integers. The digits are stored in reverse order and each of their
nodes contain a single digit. Add the two numbers and return it as a
linked list.
You may assume the two numbers do not contain any leading zero, except
the number 0 itself.
Example
Input: (2 -> 4 -> 3) + (5 -> 6 -> 4)
Output: 7 -> 0 -> 8
Explanation:
342 + 465 = 807.
After reading this problem I went to my whiteboard and coded out a solution. As you may imagine, when I went to compare my answer to the solution I was immediately shocked as my code differed from the solution in the first line!
I wrote the following:
public LinkedList<Integer> addLinkedLists(LinkedList<Integer> l1, LinkedList<Integer> l2)
and the solution had the following:
public ListNode addTwoNumbers(ListNode l1, ListNode l2)
Please explain what I appear to be missing. Why doesn't the solution receive an actual LinkedList data structure? The question clearly states to return a LinkedList, yet it returns a custom implemented ListNode. I seem to be missing a basic understanding of what was asked.
Question on leetcode:
https://leetcode.com/problems/add-two-numbers/description/
This has nothing to do with the Java built-in LinkedList, except in concept.
One of the things they teach in Programming 101 (or whatever it's called), is how linked lists in general work.
They usually start with singly-linked lists, as illustrated on Wikipedia, and will then cover other types of linked lists, such as doubly-linked lists (which is how the built-in LinkedList is implemented). See Wikipedia article for full list of linked list types (section 3).
In a singly-linked list, the list is made up of nodes, each with a value and a reference to the next node. In a full list implementation, the nodes are internal to a List class (like is done by LinkedList), but for simple/early implementations, only the ListNode class exists, and a list is represented by the reference to the "head" / first node of the list.
It is this overly simple type of list the questions are working with. If you want to "crack the coding interview" for this low-level of programming, you should study how linked lists work internally.
You could read that Wikipedia article, or search the web for material about linked lists in Java.
While #Andreas answer is good, and more at a higher level, my misunderstanding seems to be that I am not familiar with how leetcode works and the device I viewed the question on made the "Submit Solution" part not easily noticeable. My mistake was thinking this was a stand-alone question. I didn't notice the following at the bottom of the page which implies how to answer the question:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
}
}
In the interview, the interviewer actually won't tell you what the function signature is like. You will have to decide the function signature based on the interview question (Of course the interviewee should discuss with the interviewer before writing code).
For this problem, LinkedList is a black box with only a few APIs to use. So looping the list will actually take O(n^2). Since for each element, you will have to start from the head or tail and move step by step to the desired position. However, with ListNode there is much more freedom, so you can loop the list at O(n), which is also the optimized time complexity for this problem.
Related
I've been going through algorithm challenges on LeetCode and just completed "Remove Nth Node From End of List".
Many of the top answers claimed to have found a "one pass" solution and I've included a Java example below.
Please could someone explain why "while(n-->0) h2=h2.next;" doesn't count as an extra pass of the linked list and, therefore, make this a "two pass" solution?
public ListNode RemoveNthFromEnd(ListNode head, int n) {
ListNode h1=head, h2=head;
while(n-->0) h2=h2.next;
if(h2==null)return head.next; // The head need to be removed, do it.
h2=h2.next;
while(h2!=null){
h1=h1.next;
h2=h2.next;
}
h1.next=h1.next.next; // the one after the h1 need to be removed
return head;
}
I've looked in the comments to this and other solutions and couldn't find an answer. Equally, a general Google search didn't yield an explanation.
Thanks in advance.
No, it's not one-pass. One-pass is defined with respect to a sequential I/O mechanism (canonically a tape) and means that each piece of data is read at most once, in order. Analogizing the linked list to the tape here, this algorithm is not one-pass because in general, some node will have its next field read once by h2=h2.next (in either loop) and again by h1=h1.next in the second loop.
The algorithm is not single pass, but not because of the first loop.
The first loop performs a partial pass on n elements.
The second loop performs two simultaneous partial passes on l-n elements (that on h2 being complementary to that in the first loop). In total, 2l-n lookups of next fields.
A single-pass solution can be implemented with the help of a FIFO queue of length n, but this is "hiding" a partial pass.
One of the tutorials I've watched said that accessing a stack implemented in either a linked list or array takes O(1) time complexity but he also said that 'searching' in unsorted data is not applicable. Now I'm confused if 'searching' is the same with 'accessing'?
One of the tutorials I've watched said that accessing a stack implemented in either a linked list or array takes O(1) time complexity
The tutorial is correct about array-based stacks. Assuming that the stack API permits it, you can get the N from top or N from bottom element of an array-based stack in O(1). It is just an array lookup.
For linked-list based stacks, it is more complicated. You can get the top (or maybe bottom) element in O(1). But getting the N'th from top element is an O(N) operation. You have to follow N references to get to the element you need.
but he also said that 'searching' in unsorted data is not applicable. Now I'm confused if 'searching' is the same with 'accessing'?
They are different things. Consider this:
String[] array = new String[] {"A", "B", "C"};
Accessing an element of array is array[i]. For an array that is an O(1) operation. (For a linked list, the equivalent is O(N))
Searching for an element of an array is like this:
for (String s : array) {
if (s.equals("Fred")) {
// found it!
}
}
As I have used these words, "accessing" and "searching" clearly mean different things.
Now as Erwin states, the meanings of terms like "searching" and "accessing" will vary from one author to another. But "searching" has a clear implication of needing to look for something ... rather than knowing where it will be.
It is not entirely clear what the tutorial you are reading might mean by "'searching' in unsorted data is not applicable". It is certainly possible to search in non-sorted data, but that will be an O(N) operation ... unless you have done something to organize the data beforehand. (Se my example code above.)
But then again a stack data structure is typically optimized for the special case of accessing the top of the stack. Not accessing the N'th element, or searching for an element with a particular value.
I'm studying Trees in Java, and came across some confusing lines in the book I'm studying. The diagram given for the in-order traversal is this:
The code for the traversal (recursive) is:
private void inOrder(Node leftRoot) {
if (localRoot != null) {
inOrder(localRoot.leftChild);
System.out.println(localRoot.iData + " ");
inOrder(localRoot.rightChild);
}
}
The lines I'm confused at are:
Now we’re back to inOrder(A), just returning from traversing A’s left
child. We visit A and then call inOrder() again with C as an argument,
creating inOrder(C). Like inOrder(B), inOrder(C) has no children, so
step 1 returns with no action, step 2 visits C, and step 3 returns
with no action. inOrder(B) now returns to inOrder(A).
However,
inOrder(A) is now done, so it returns and the entire traversal is
complete. The order in which the nodes were visited is A, B, C; they
have been visited inorder. In a binary search tree this would be the
order of ascending keys.
I've highlighted the parts where I'm stuck at. First, I think in the third step, inOrder(C)[and not inOrder(B)] returns to inOrder(A).And second, the order in which the nodes were visited should be B -> A -> C.
Please help me out!
Yes, you are correct on both counts. These seem to be typos, or errata.
As a sidenote, I recognized the diagram style in your post because I learned data structures years ago from the same book (Lafore). Unfortunately it does not seem that he has a list of errata published anywhere, which is disappointing, since most authors do strive to do this.
This is not a practical question, I just want to discuss and learn data structure design here, I heard it's asked by Google during onsite interview. Please advise me how to improve my design, thanks!
At the beginning I wanted to use a deque to store pairs of x,y coordinates for snake's body parts.
deque<pair<x, y>> snakeBodyParts;
Because it's very easy to push front when snake move - create new coordinate pair as the new head base on old head position and current direction, then pop back to remove the tail. In this way move, eat, check head hit wall are all O(1) operations and easy to implement with deque. But checking if new head position overlap with snakes body will require looping through all body parts' position - O(L) time complexity, L is number of body parts.
To improve it, I thought about putting all coordinates into an unordered_set(C++) or hashset(Java) while still keeping my old deque, it can give me O(1) for checking if head hits body now. But I don't know if it's a good idea because it almost doubles the memory and amount of codes, whenever I add/remove to deque, I need to do it to my hashset.
unordered_set<pair<int, int>, pair_hash> bodyPartsSet;
I also thought about creating my own structure which is like linkedlist, except it points to previous node:
SnakeBodyNode {
int x;
int y;
SnakeBodyNode * prev;
}
Then I also need two pointers pointing at head and tail as well as a direction variable.
SnakeBodyNode * head;
SnakeBodyNode * tail;
char dir;
However I don't see any advantage of this, still need to hash to get O(1) for checking if head hits body..
Is there any flaw in my deque + hash design or any one have better idea to share?
I would just use an unordered_set. Your concerns are:
Fast insert of new head - this is O(1) for unordered_set.
Fast deletion of existing tail - this is O(1) for unordered_set.
No duplicates (checking that head intersects body) - this is guaranteed
for unordered_set (it does not allow duplicates).
When inserting a new head, you don't have to do anything special to check that it intersects with the body; you'll get an error if it does.
I'm looking for Java solution but any general answer is also OK.
Vector/ArrayList is O(1) for append and retrieve, but O(n) for prepend.
LinkedList (in Java implemented as doubly-linked-list) is O(1) for append and prepend, but O(n) for retrieval.
Deque (ArrayDeque) is O(1) for everything above but cannot retrieve element at arbitrary index.
In my mind a data structure that satisfy the requirement above has 2 growable list inside (one for prepend and one for append) and also stores an offset to determine where to get the element during retrieval.
You're looking for a double-ended queue. This is implemented the way you want in the C++ STL, which is you can index into it, but not in Java, as you noted. You could conceivably roll your own from standard components by using two arrays and storing where "zero" is. This could be wasteful of memory if you end up moving a long way from zero, but if you get too far you can rebase and allow the deque to crawl into a new array.
A more elegant solution that doesn't really require so much fanciness in managing two arrays is to impose a circular array onto a pre-allocated array. This would require implementing push_front, push_back, and the resizing of the array behind it, but the conditions for resizing and such would be much cleaner.
A deque (double-ended queue) may be implemented to provide all these operations in O(1) time, although not all implementations do. I've never used Java's ArrayDeque, so I thought you were joking about it not supporting random access, but you're absolutely right — as a "pure" deque, it only allows for easy access at the ends. I can see why, but that sure is annoying...
To me, the ideal way to implement an exceedingly fast deque is to use a circular buffer, especially since you are only interested in adding removing at the front and back. I'm not immediately aware of one in Java, but I've written one in Objective-C as part of an open-source framework. You're welcome to use the code, either as-is or as a pattern for implementing your own.
Here is a WebSVN portal to the code and the related documentation. The real meat is in the CHAbstractCircularBufferCollection.m file — look for the appendObject: and prependObject: methods. There is even a custom enumerator ("iterator" in Java) defined as well. The essential circular buffer logic is fairly trivial, and is captured in these 3 centralized #define macros:
#define transformIndex(index) ((headIndex + index) % arrayCapacity)
#define incrementIndex(index) (index = (index + 1) % arrayCapacity)
#define decrementIndex(index) (index = ((index) ? index : arrayCapacity) - 1)
As you can see in the objectAtIndex: method, all you do to access the Nth element in a deque is array[transformIndex(N)]. Note that I make tailIndex always point to one slot beyond the last stored element, so if headIndex == tailIndex, the array is full, or empty if the size is 0.
Hope that helps. My apologies for posting non-Java code, but the question author did say general answers were acceptable.
If you treat append to a Vector/ArrayList as O(1) - which it really isn't, but might be close enough in practice -
(EDIT - to clarify - append may be amortized constant time, that is - on average, the addition would be O(1), but might be quite a bit worse on spikes. Depending on context and the exact constants involved, this behavior can be deadly).
(This isn't Java, but some made-up language...).
One vector that will be called "Forward".
A second vector that will be called "Backwards".
When asked to append -
Forward.Append().
When asked to prepend -
Backwards.Append().
When asked to query -
if ( Index < Backwards.Size() )
{
return Backwards[ Backwards.Size() - Index - 1 ]
}
else
{
return Forward[ Index - Backwards.Size() ]
}
(and also check for the index being out of bounds).
Your idea might work. If those are the only operations you need to support, then two Vectors are all you need (call them Head and Tail). To prepend, you append to head, and to append, you append to tail. To access an element, if the index is less than head.Length, then return head[head.Length-1-index], otherwise return tail[index-head.Length]. All of these operations are clearly O(1).
Here is a data structure that supports O(1) append, prepend, first, last and size. We can easily add other methods from AbstractList<A> such as delete and update
import java.util.ArrayList;
public class FastAppendArrayList<A> {
private ArrayList<A> appends = new ArrayList<A>();
private ArrayList<A> prepends = new ArrayList<A>();
public void append(A element) {
appends.add(element);
}
public void prepend(A element) {
prepends.add(element);
}
public A get(int index) {
int i = prepends.size() - index;
return i >= 0 ? prepends.get(i) : appends.get(index + prepends.size());
}
public int size() {
return prepends.size() + appends.size();
}
public A first() {
return prepends.isEmpty() ? appends.get(0) : prepends.get(prepends.size());
}
public A last() {
return appends.isEmpty() ? prepends.get(0) : appends.get(prepends.size());
}
What you want is a double-ended queue (deque) like the STL has, since Java's ArrayDeque lacks get() for some reason. There were some good suggestions and links to implementations here:
Java equivalent of std::deque?