Design data structure for simple greedy snake game during interview - java

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.

Related

Does a partial traversal of a linked-list count as "one pass" of the list?

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.

java- understanding LinkedList interview questions with nodes

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.

element of a Linked List

Can someone please explain In Java how do you find middle element of a linked list in single pass?
I have googled it, but cannot seem to find a simple explanation on how to code it.
LinkedList<String> list = new LinkedList<>();
list.add("foo");
list.add("bar");
list.add("baz");
String middle = list.get(list.size()/2);
System.out.println(middle); // bar
The call to assign middle will pass through half of the list during the get call.
As pointed out in the comments, the middle is the worst place to operate on a LinkedList. Consider using another variation, such as ArrayList.
I think this is a sort of trick question that you see on lists of possible interview questions.
One solution would be to have two pointers to step through the list, one taking steps of two and one taking steps of one.
When the pointer that is taking two steps at a time reaches the end of the list, the one taking only one step will be halfway through.
I doubt that this practice is really useful though..
good luck!
Since it's a LinkedList, you won't be able to find out its size until after the first (and only) pass. To find the middle element, you need to know two things; what index is at the middle, and what is the value of the element at that index. Finding the middle index is easy--just make one pass through the list, keeping a counter of how many nodes there are. As you do this, you'll need to keep track of each element in a separate data structure, perhaps an ArrayList, since you're only allowed one pass through the LinkedList. Once you're done, half the counter to find the middle index, and return the ArrayList element at that index.
The pseudo code looks like this:
int count
ArrayList elements
for each node in LinkedList:
count++
elements.append(node)
middleIndex = count/2
middleElement = elements.getIndex(middleIndex)
return middleElement
Of course, you'll need to take care of the case where there isn't a single middle element.

Graph traversal - finding and returning the shortest distance

I am using a Breadth first search in a program that is trying to find and return the shortest path between two nodes on an unweighted digraph.
My program works like the wikipedia page psuedo code
The algorithm uses a queue data structure to store intermediate results as it traverses the graph, as follows:
Enqueue the root node
Dequeue a node and examine it
If the element sought is found in this node, quit the search and return a result.
Otherwise enqueue any successors (the direct child nodes) that have not yet been discovered.
If the queue is empty, every node on the graph has been examined – quit the search and return "not found".
If the queue is not empty, repeat from Step 2.
So I have been thinking of how to track number of steps made but I am having trouble with the limitations of java (I am not very knowledgeable of how java works). I originally was thinking that I could create some queue made up of a data type I made that stores steps and nodes, and as it traverses the graph it keeps track of the steps. If ever the goal is reached just simply return the steps.
I don't know how to make this work in java so I had to get rid of that idea and I moved on to using that wonky Queue = new LinkedList implementation of a queue. So basically I think it is a normal integer queue, I couldn't get my data type I made to work with it.
So now I have to find a more basic approach so I tried to use a simple counter, this doesn't work because the traversal algorithm searches down many paths before reaching the shortest one so I had an idea. I added a second queue that tracked steps, and I added a couple counters. Any time a node is added to the first queue I add to the counter, meaning I know that I am inspecting new nodes so I am not a distance further away. Once all those have been inspected I can then increase the step counter and any time a node is added to the first queue I add the step value to the step queue. The step queue is managed just like the node queue so that when the goal node is found the corresponding step should be the one to be dequeued out.
This doesn't work though and I was having a lot of problems with it, I am actually not sure why.
I deleted most of my code in panic and frustration but I will start to try and recreate it and post it here if anyone needs me to.
Were any of my ideas close and how can I make them work? I am sure there is a standard and simple way of doing this as well that I am not clever enough to see.
Code would help. What data structure are you using to store the partial or candidate solutions? You say your using a queue to store nodes to be examined, but really the objects stored in the queue should wrap some structure (e.g. List) that indicates the nodes traversed to get to the node to be examined. So, instead of simple Nodes being stored in the queue, some more complex object would be needed to make available the information necessary to know the complete path taken to that point. A simple node would only have information about itself, and it's children. But if you're examining node X, you also need to know how you arrived to node X. Just knowing node X isn't enough, and the only way (I know of) to know the path taken to node X is to store the path in the object that represents a "partial solution" or "candidate solution". If this is done, then finding the length of the path is trivial, because it's just the length of this list (or whichever data structure chosen). Hope I'm making some sense here. If not, post code and I'll take a look.
EDIT
These bits of code help show what I mean (they're by no means complete):
public class Solution {
List<Node> path;
}
Queue<Solution> q;
NOT
Queue<Node> q;
EDIT 2
If all you need is the length of the path, and not the path, per se, then try something like this:
public class Solution {
Node node; // whatever represents a node in you algorithm.
int len; // the length of the path to this node.
}
// Your queue:
LinkedList<Solution> q;
With this, before enqueuing a candidate solution (node), you do something like:
Solution sol = new Solution();
sol.node = childNodeToEnqueue;
sol.len = parentNode.len + 1;
q.add(sol);
The easiest solution in order to track distance during a traversal is to add a simple array (or a map if you vertices are not indexed by integers).
Here is pseudo code algorithm:
shortest_path(g, src, dst):
q = new empty queue
distances = int array of length order of g
for i = 0 to order: distances[i] = -1
distances[src] = 0
enqueue src in q
while q is not empty:
cur = pop next element in q
if cur is dst: return distances[dst]
foreach s in successors of cur in g:
if distances[s] == -1:
distances[s] = distances[cur] + 1
enqueue s in q
return not found
Note: order of a graph is the number of vertices
You don't need special data structures, the queue can just contains vertices' id (probably integers). In Java, LinkedList class implements the Queue interface, so it's a good candidate for your queue. For the distances array, if your vertices are identified by integers an integer array is enough, otherwise you need a kind of map.
You can also separate the vertex tainting (the -1 in my algo) using a separate boolean array or a set, but it's not really necessary and will waste some space.
If you want the path, you can also do that with a simple parent array: for each vertex you store its parent in the traversal, just add parent[s] = cur when you enqueue the successor. Then retrieving the path (in reverse order) is a simple like this:
path = new empty stack
cur = dst
while cur != src:
push cur in path
cur = parent[cur]
push src in path
And there you are …

What is a data structure that has O(1) for append, prepend, and retrieve element at any location?

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?

Categories