From Cracking the Coding Interview.
Problem 2.1: Write code to remove duplicates from an unsorted linked list.
This is the solution that they provide:
public static void removeDuplicates(Node n) {
Hashtable<Integer, Boolean> table = new Hashtable<Integer, Boolean>();
Node previous = n;
while (n != null) {
if (table.containsKey(n.data)) {
previous.next = n.next;
} else {
table.put(n.data, true);
previous = n;
}
n = n.next;
}
}
My question is:
When you do n=n.next, wouldn’t you lose the head of the list (first node)?
How would you access to this list again with the duplicates removed if you don’t have access to the head?
And also isn’t it better to use a Set instead of a Table?
I don’t think you need Key and Value. I think you only need the Key, right?
Thank you
First, as already mentioned in a comment, the change of the local parameter has no effects to the callers input-variable.
Second, you are right, using a Set would be better, but only because the code is better to read. The code is syntactically correct, internally a Set is nothing else than a Map with the same
dummyobject as value for every key.
There is no return statement in the function, as it is set as void.
However, if the head of the list is kept outside of the function in some variable, it is enough to return the correct result.
That's because the first node is not going to be removed, as there will not be any duplicate at this time.
After all, you don't have any problem with changing the n value.
Related
I've forgotten basically everything from my DSA class because I'm an idiot, so I'm spending winter break refreshing myself and doing practice problems I find online. One of them is returning the middle node in a singly Linked List. If there are two middle nodes, return the second one. The posted solution is way different from my answer and I can't figure out why mine is wrong.
My implementation is basically:
public static Integer findMiddle() {
LinkedList<Integer> list = new LinkedList();
list.add(5);
list.add(7);
list.add(11);
list.add(13);
list.add(15);
list.add(17);
return list.get(list.size()/2);
}
EDIT: Here is the given online answer:
void printMiddle()
{
Node slow_ptr = head;
Node fast_ptr = head;
while (fast_ptr != null && fast_ptr.next != null) {
fast_ptr = fast_ptr.next.next;
slow_ptr = slow_ptr.next;
}
System.out.println("The middle element is ["
+ slow_ptr.data + "] \n");
}
But the online answer is a bit more complicated than that. I don't really understand why this is wrong (again, I'm an idiot). If the linked list holds Integers, I would be returning the Integer, right? What would be the benefit of returning a Node over returning the object in the list, like you would in an ArrayList?
Thank you to anyone reading this! I know this is a silly question I just don't quite understand why.
This question seems very much like what you're attempting to solve: https://leetcode.com/problems/middle-of-the-linked-list/
But short of seeing the exact question you're provided, the "book answer" you have is very likely assuming you don't have access to the whole list; rather, just the head (entry point) ListNode. Which means you're going to have to traverse it to the end, and that's where the Slow & Fast Pointer technique is useful.
If we assume the question provides access to the entire LinkedList object, then your answer is fine, since you can get its size()/2.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 2 years ago.
Improve this question
So basically my code iterates through the list and the original method is supposed to return the linked list however it doesn't seem to be adding the nodes that I link in the recursive method and I'm confused as to why. Can anyone help me?
// Append MyStringBuilder2 b to the end of the current MyStringBuilder2, and
// return the current MyStringBuilder2. Be careful for special cases!
public MyStringBuilder2 append(MyStringBuilder2 b)
{
//Test if Invalid
if(b.firstC==null){
return this;
}
//Test if condition is met
else {
CNode lastNode =firstC;
recurseAppendBuild(lastNode, b);
return this;
}
}
private void recurseAppendBuild(CNode lastNode, MyStringBuilder2 BPoint) {
//Test if all nodes have been added
if(lastNode.next==null&&BPoint.firstC==null) {
System.out.println("finished");
}
//Tests if all nodes in the original linked list have been passed through
else if(lastNode.next==null) {
lastNode.next= new CNode(BPoint.firstC.data);
BPoint.firstC=BPoint.firstC.next;
recurseAppendBuild(lastNode.next, BPoint);
}
//Recurse until condition is met
else {
recurseAppendBuild(lastNode.next, BPoint);
}
}
```
Okay, your code needs some work. Let's look at your first method. I'm going to rewrite it.
public MyStringBuilder2 append(MyStringBuilder2 fromBuilder)
{
if (fromBuilder.firstC != null) {
recurseAppendBuild(fromBuilder.firstC);
}
return this;
}
I changed a number of things.
I used a more meaningful name on the argument. It's a good idea to give your variables meaningful names, not just 'b'. Note that I never use one-character names. If nothing else, it can be really hard to search on that. If you do "int i" and then search for i, you'll get a LOT of hits that aren't i at all.
This is a very trivial thing and doesn't affect the quality of your code.
In all cases, you always return yourself, so the return statement can be after the if-else structure, which is easier to see that it's the same.
That eliminates the top if-block entirely, so I reversed the logic.
And I changed the method signature of your recursive method, for reasons I'll describe below.
The end result is short and sweet and easily understood.
Now, let's look at your second method:
private void recurseAppendBuild(CNode lastNode, MyStringBuilder2 BPoint) {
//Test if all nodes have been added
if(lastNode.next==null&&BPoint.firstC==null) {
System.out.println("finished");
}
//Tests if all nodes in the original linked list have been passed through
else if(lastNode.next==null) {
lastNode.next= new CNode(BPoint.firstC.data);
BPoint.firstC=BPoint.firstC.next;
recurseAppendBuild(lastNode.next, BPoint);
}
//Recurse until condition is met
else {
recurseAppendBuild(lastNode.next, BPoint);
}
}
Your variable named BPoint breaks JAVA naming standards. It should start with a lower case letter.
If you pass in a MyStringBuilder2 as the second argument, then as you move things from BPoint to the end of your list and recurse, you have to remove them from BPoint, which is a pain in the ass. So instead, I didn't point to the wrapper. In my code above, I passed in the head of the list (fromBuilder.firstC).
You are finished when your list-to-append-from (BPoint) is empty, not when lastNode is null. Your first if is flawed.
You aren't recursively adding items. You're recursively looking for the end of the list. I don't think that's what you really want.
You're messing up the integrity of BPoint. You're making a copy of the nodes as you add them, but you're then dropping the old ones from BPoint but NOT maintaining lastC at all.
And you have a significant problem if your list starts as empty, as firstC and lastNode will both be empty.
So let's think about it this way. First, doing this recursively is silly, but that's the assignment, so we'll work with it.
A recursive definition is:
AppendedList = OriginalList + firstItem + Append Tail of List.
private void recurseAppendBuild(CNode headToAppend) {
if (headToAppend == NULL) {
// All done.
return;
}
CNode nodeToAppend = new CNode(headToAppend.data);
if (lastC == nullptr) {
// Original list is empty.
firstC = lastC = nodeToAppend;
else {
lastC.next = nodeToAppend;
lastC = nodeToAppend; // Point the tail at the new tail
}
// And here you recurse, always.
recurseAppendBuild(headToAppend.next);
}
Let's look at this.
I'm assuming you keep both a firstC and lastC in your builder. It would be deeply inefficient otherwise. So you only need to pass in the chain of nodes, not the surrounding wrapper.
By putting a null-check at the top of this method, you eliminate other null checks. Note -- this means we can eliminate the null check in the first method.
Create the new copy right away. That part's easy, right?
If lastC is null, you have an empty list, so you just point both front and back of the list to your new node.
Otherwise you point the old tail's next pointer to your new node and update the tail pointer to remain pointed at the tail.
Either way, you can safely recurse with the next object in the original list.
Advantages of this method, aside from working, is that you don't destroy the original list, and it's pretty clear to read.
I'm writing program, which do some operations with doublylinked lists(my implementation).
There is one thing which i do not understand and it confusing me a lot.
Ok. So. I have two doubly linked list. I need to create method where argument is a index of first doublylinked list, and on that index i need to put second list.
I wrote that method:
public void PutInPlace(int i){
DoublyLinkedList ldw3 = new DoublyLinkedList(); // New doublylinked list.
Node current = ldw1.tail; // ldw1 - First doublylinked list, created earlier.
Node current1 = ldw2.tail; //ldw2 - Second doublylinked list, created earlier.
int counter = 0;
while(true){
ldw3.AddHead(current.number);
current = current.prev;
counter++;
if(counter == i){ // THAT if makes NullPointerException
ldw3.AddHead(current1.liczba);
current1 = current1.prev;
if(current1 == null)
break;
}
}
I dont wanna put all code, because it is long and can be not-easy to read. So, why "if(counter == i)" makes NullPointerException? Without that "if" program works. Where is the problem?
Thank you for help, guys!
current1 may be null. But you are trying to get the value
if(current1 != null)
{
ldw3.AddHead(current1.liczba);
current1 = current1.prev;
}
instead of
current1 = current1.prev;
(or)
change the statement like this,
if(current1 == null)
break;
ldw3.AddHead(current1.liczba);
current1 = current1.prev;
instead of
ldw3.AddHead(current1.liczba);
current1 = current1.prev;
if(current1 == null)
break;
I think the current variable is the one being null. The i variable has a unique value, let's say 3.
While(true) is an infinite loop, and your condition (counter==i) will be executed only once (since you always increment counter and it will hit the value 3 only once). So unless current1 has no previous, the break will never be it and you will arrive to a point where current has no more previous (making it null and giving an exception when doing current.prev)
A good practice is always checking if an object is null before using one of its methods or attributes.
No, that "if" cannot make a NullPointerException.
First, make sure you recompile everything, and run a test. Then post the output from that test, including the stack trace from the exception.
Second, post the source code verbatim (so the line numbers are clear).
Third, post the javap output for the PutInPlace method.
I am working my way through a university practical on LinkedLists.
I am having difficulty understanding the add() method of an iterator traversing a LinkedList. Our lecturer has given us the code to implement this, so copying exactly from him:
public class LinkedList
{
public LinkedList() {
Node first = null;
}
class Node {
public Object data;
public Node next;
}
class LinkedListIterator implements ListIterator {
public LinkedListIterator() {
Node position = null;
Node previous = null;
}
public void add (Object element) {
if (position == null) {
addFirst(element);
current = first;
} else {
//1 Node newNode = new Node();
//2 newNode.data = element;
//3 newNode.next = current.next;
//4 current.next = newNode;
//5 current = newNode;
}
previous = current
}
Note: I have deliberately not encapsulated the variables and have cut out excess code to save space. I'm aware it doesn't compile, but my question is more conceptual.
In the add method:
The if statement simply detects if the iterator's position is null, in which case it adds the element to the start of the LinkedList and sets the iterators position to this newly created node.
The else statement is confusing me though:
Lines 1 & 2: A new node is created and its data is set to the element parameter.
Line 3, the next variable of this new node is set to the current node's next node i.e. it is set to whatever is after the position of the node the Iterator is pointing at.
Line 4, the "next" of the node the Iterator is currently pointing at is changed to the newNode (effectively completing the insertion of the new node between two existing nodes).
Line 5, the Iterator's position is set to point at the newNode.
After the else statement, the previous node that the Iterator was pointing at is set to the current node.
Herein lies the problem - doing that effectively syncs up the position and the previous position of the iterator. I checked this through the Eclipse debugger. This renders the previous useless. However, I'm aware that you can't reverse traverse a LinkedList using the standard iterator anyway.
When I comment this line out, nothing seems to change. Is this line simply unnecessary or does it have some function that I'm not actually realising? (Another reason I am asking is because the reverse of this appears in our notes for the remove() method, which again doesn't seem to have a purpose.
Edit: It appears this answer may be answered as my course develops. For now, I am moving the previous = current line to above the current = newNode line. This appears to keep all values separate.
The contract for ListIterator.add(Object) specifies that a subsequent call to next would be unaffected, and a subsequent call to previous would return the new element, that is the purpose of updating previous in the implementation of add.
Your professor probably gave you sample code that will be expanded upon later in the course. It looks like the sample code includes parts that would support a doubly linked list in future. Doubly linked list do allow reverse traversal of the list. See wikipedia for details.
On a completely unrelated topic; I applaud you for actually trying to understand and comprehend what was given to you instead of just plug-and-chugging to get the assignment done. That habit will serve you well in the future!
This looks like a bug to me. The previous = current assignment needs to happen before the current = newNode assignment. Then previous would refer to the old current, and the new current would be the newly created node.
public void returnRental(Customer cust){
Rental toDelete = null; //Rental to be removed from list.
LinkedList<Video> toReturn = null; //List of videos to be added to inventory.
//Find appropriate rental according to customer name.
for(int i = 0; i < rentals.size(); i++){
if(cust.getName() == rentals.get(i).getRentee().getName()){
toReturn = rentals.get(i).getRented();
toDelete = rentals.get(i);
}
}
here is the snippet of code that is giving me problems. I've debugged it in eclipse quite a bit which ended up just confusing me more. It hits the if, and passes the condition. But once it gets to assigning values to "toReturn" it assigns it an empty list with size 0. Where as I check my rentals Linked list and the correct value are there, but for some reason it is not getting assigned to my variables correctly :( The same happens to "toDelete" but this isn't a list, it is one instance of my class Rental. (The linked list is a list of rentals, which contains a linked list of videos)
No errors are thrown...
Its a little difficult to explain, if you need more information please let me know and i'll clarify.
I'm at a loss, possibly because I'm not iterating through my linked list correctly?
Replace
if (cust.getName() == rentals.get(i).getRentee().getName()){
by
if (cust.getName().equals(rentals.get(i).getRentee().getName())){
You can't compare strings with == (except if your algorithm can ensure this is the same instance, which is almost never the case).
But the missing equals is not the only bug. It may be inside getRented() or elsewhere (you don't show what you do with toReturn and toDelete, so it's not clear if you don't have problems here).
Now, to go on chasing your bugs, you should either
debug, and put a breakpoint in your loop to check the state of rentals.get(i) and the execution at this point
if you can't debug, put a lot of System.println, so that you know what you have...
I've upvoted dystroy's answer because incorrect string comparison is always wrong.
But because that would fail differently (customer names not matching rentee names), I'm wondering if your issue is really caused by either of the following:
a problem in getRented(); or
cust having a null name on call, which would match a Rentee with a null name.
Possibly, your if condition is being hit more than once. First of all, check if this is actually happening. If so, check your logic and determine if you want to stop at the first occurence or at the last (this case seems to be the latter).
If you want to stop at the first occurence, break the iteration:
for(int i = 0; i < rentals.size(); i++){
if(cust.getName() == rentals.get(i).getRentee().getName()){
toReturn = rentals.get(i).getRented();
toDelete = rentals.get(i);
break;
}
}
for(int i = 0; i < rentals.size(); i++){
if(cust.getName().equals( rentals.get(i).getRentee().getName())){
toReturn.addAll(rentals.get(i).getRented());
//assumming it returns the list of Video object
toDelete = rentals.get(i);
}
}