Trying to solve Top K Frequent Elements using a LinkedList - java

I'm trying to solve LeetCode #347. Top K Frequent Elements. I know of a few approaches to this problem, but I'm trying to do it the following way.
1) Take the input array, for example: [1,1,1,2,2,3]
2) Create a map of the integer->frequency of its appearance, ie. {[1, 3], [2, 2], [3, 1]}
3) Create a LinkedList with Nodes that contain the integer->frequency pairs and insert elements into this LinkedList in ascending orders of frequency, ie. head->[1,3]->[2,2]->[3,1]->null
4) Print the first k value elements of this LinkedList, ie. [1, 2]
Which should theoretically give me the correct answer.
I'm using the following implementation:
class Solution {
public List<Integer> topKFrequent(int[] nums, int k) {
/* input: [1,1,1,2,2,3], 2
result: [1,3]
expected: [1,2]
*/
//stores integer->frequency pairs
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
Node sortedList = null; //head node of list
List<Integer> res = new ArrayList<Integer>(); //result array
//populate map with integer->frequency pairs
for(int i : nums) {
if(map.containsKey(i)) {
map.put(i, map.get(i)+1);
} else {
map.put(i, 1);
}
}
//System.out.println(map);
//go through map
for(Map.Entry<Integer, Integer> entry : map.entrySet()) {
Integer key = entry.getKey();
Integer value = entry.getValue();
List<Integer> pair = new ArrayList<Integer>(); //make a pair
pair.add(key);
pair.add(value);
//System.out.println(pair);
Node newNode = new Node(pair);
System.out.println(newNode.data);
if(sortedList == null) {
//System.out.println(newNode.data);
newNode.next = sortedList;
sortedList = newNode; //insert at head if linked list is empty
} else {
Node current = sortedList; //point current to head
//move current pointer until we find a spot where current's frequency is less than newNode's frequency
while(current.next != null && current.next.data.get(1) < newNode.data.get(1)) {
current = current.next;
}
newNode.next = current.next;
current.next = newNode;
}
}
int count = 0;
//loop until k and return first k keys
while(sortedList != null && count < k) {
//System.out.println("key:"+sortedList.data.get(0) + " value:"+ sortedList.data.get(1));
res.add(sortedList.data.get(0));
sortedList = sortedList.next;
count++;
}
return res;
}
class Node {
List<Integer> data = new ArrayList<Integer>();
Node next;
Node(List<Integer> pair) {
data = pair;
}
}
}
However, for some reason my LinkedList is populating as head->[1,3]->[3,1]->[2,2]->null instead of the proper sorted manner. I've tried debugging it but have not been able to figure out which part I'm messing up. I've also written it out on paper and it seems to work so I'm sure I'm messing something up in my code.
What am I doing wrong here?

Problem is in piece of code where you are trying to insert into linked list into sorted order. First thing is that you start comparing from current.next.data you should start comparison from very first node. Second you were not handling case when element has to be inserted at last node and also at very first node. and you have condition < which means it will inserted In decreasing order.
Inside the map iteration code replace if else condition with below code.It works fine
if(sortedList == null) {
//System.out.println(newNode.data);
newNode.next = sortedList;
sortedList = newNode; //insert at head if linked list is empty
} else {
Node current = sortedList; //point current to head
Node prev=null;
//move current pointer until we find a spot where current's frequency is less than newNode's frequency
while(current != null && current.data.get(1) > newNode.data.get(1)) {
prev=current;
current = current.next;
}
if(current==null) {
prev.next=newNode;
}else if(current==sortedList) {
newNode.next=current;
sortedList=newNode;
}
else {
newNode.next = current.next;
current.next = newNode;
}
}
Here if current==null means data has to be inserted at last and last node and at that time last node will be referenced by prev so prev.next=newNode;will assign newNode to last.
if current==sortedList means data has to be inserted at first node. otherwise data needs to be inserted in middle.

Related

Java doubly linked list looping

My goal is to loop backwards through a list I have created and if an element is smaller than my chosen number t a node with the value of t will be inserted in front of the element that is smaller. Also, if every element in the list is bigger than t a node with the value of t will be put at the start of the list. Example:
DoublyLinkedList<Integer> list = new DoublyLinkedList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list.add(5);
list.addAtFirstSmaller(4);
System.out.println("Nodelist: " + list.toString());
And I should expect the outcome:
[1, 2, 4, 3, 4, 5]
But instead I get:
[1, 2, 3, 4, 4, 5]
This is my code so far:
public void addAtFirstSmaller(T t)
{
ListNode<T> node = tail;
ListNode<T> newNode = new ListNode<T>(t);
while(node.previous != null)
{
int compared = node.previous.element.compareTo(t);
node = node.previous;
if(compared < 0)
{
ListNode<T> temp = node.next;
node.next = newNode;
newNode.next = temp;
newNode.next.previous = newNode;
node.previous = null;
size++;
}
else if(compared > 0 && node.previous == null)
{
addFirst(t);
size++;
}
}
}
To me it seems like everything is pushed to the right. Any idea on what to do?
As you already verified: the example output is correct as the idea is to maintain the list sorted. The misinterpretation was understandable, as "in front of" is somewhat ambiguous.
However, there are still a few issues with your code:
Although the assignment node.previous = null will make the loop end once you have inserted the new node, this breaks a previous link that really should remain untouched. Only the head node should have previous equal to null, while in general node could have a predecessor, which must remain its predecessor.
newNode.previous never receives a value. It should be set to node.
When the value to insert is greater than the value in the tail node, the node will be inserted at the wrong spot -- there is never a scenario in your code where the new node will become the new tail, yet this is a possibility that should be foreseen.
I would assume that addFirst would already increase size, so increasing it again after you call this function is wrong. This really should be the responsibility of addFirst.
Assuming that you have implemented addLast, the corrected code could be like this:
public void addAtFirstSmaller(T t) {
ListNode<T> node = tail;
ListNode<T> newNode = new ListNode<T>(t);
while (node != null && node.element.compareTo(t) >= 0) {
node = node.previous;
}
if (node == null) {
addFirst(t);
} else if (node == tail) {
addLast(t);
} else {
ListNode<T> temp = node.next;
node.next = newNode;
newNode.next = temp;
newNode.previous = node;
temp.previous = newNode;
size++;
}
}
In case you don't have it yet, addLast could look like this:
public void addLast(T t) {
ListNode<T> newNode = new ListNode<T>(t);
tail.next = t;
t.previous = tail;
tail = t;
size++;
}
I just received an answer from my instructor, and it seems like my output is correct ([1, 2, 3, 4, 4, 5] that is). I will mark this thread as closed as the code is working.

Trouble sorting linked list

outputI'm doing a project for class where I have to sort a linked list using insertion sort. I am supposed to take user input, convert it into an int array and insert it into a linked list. My problem is for some reason when I go to print the linked list post sort, it only prints the first node. The code worked jsut fine when I was initially testing it(I was manually entering what integers to insert), but now that I'm using arrays it doesn't seem to work. Can anyone help?
(this is only one class from my project but let me know if more information is needed).
Edit: I added a picture of what my output lokos like
import java.util.Arrays;
public class SortLL {
static LL top;
static Node head;
static Node sorted;
//function to insert node at head
public void toHead(int newData){
Node newNode = new Node(newData);
newNode.link = head;
head = newNode;
}
public static void insertion(Node ref){ //problem right now is that i'm only passing in one node
sorted = null;
Node current = ref;
while(current != null){
Node next = current.link;
sortInsert(current);
current = next;
}
head = sorted;
}
static void sortInsert(Node newNode){ //item in this case is val
if(sorted == null || sorted.item >= newNode.item){
newNode.link = sorted;
sorted = newNode;
} else {
Node current = sorted;
while(current.link != null && current.link.item < current.item){
current = current.link;
}
newNode.link = current.link;
current.link = newNode;
}
}
void printList(Node head)
{
while (head != null)
{
System.out.print(head.item + " ");
head = head.link;
}
}
public static void sortList(int[] arrA, int[] arrB){
int[] arr = new int[arrA.length + arrB.length];
System.arraycopy(arrA, 0, arr, 0, arrA.length);
System.arraycopy(arrB, 0, arr, arrA.length, arrB.length);
System.out.println("checking array " + Arrays.toString(arr));
SortLL sort = new SortLL();
for(int i=0;i<arr.length;i++){
sort.toHead(arr[i]);
}
System.out.println("sortLL.java\n\n");
sort.printList(sort.head);
sort.sortInsert(sort.head);
System.out.println("\nLinkedList After sorting");
sort.printList(sort.head);
}
}
Inside your printList() method, you shift the head variable while iterating over the list. When you move the head variable to the end, you essentially destroy the linked list since you lose your reference to the beginning of it. Java will then automatically treat the unreferenced nodes as garbage.
From what I see, after you first call sort.printList(sort.head), you destroyed your original linked list, so it didn't work when sorting.
When calling printList(), it might help to use a temporary node (Node temp = head) so that you don't lose your original head variable.

Why can't I remove occurrences in the linked list?

Problem: Given a value, remove all instances of that value from a linked list. More info below: JAVA
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
public class Solution {
public ListNode removeElements(ListNode head, int val) {
ListNode n = head; //1, 2, 6, 3, 4, 5, 6
while(n.next == null){
if(n.next.val == val){
n.next = n.next.next;
}
n = n.next;
if(n == null){break;}
}
return head;
}
}
Since its a pass by reference, it should be updating shouldn't it?
I tried:
removeElements([1,2,6,3,4,5,6], 6)
But it didn't remove anything. So what I am doing incorrectly?
There are a couple of issues:
you want to loop until a node is null not until is not null (i.e. while( ... != null))
you might want to loop until n is null, not until n.next is null, otherwise you'd skip the last element
you want to check for n.val == val not n.next.val == val, otherwise you'd skip the first element
if you check n you want to keep track of the previous node in case you need to remove n, i.e. prev.next = n.next.
if the first element (the head) is to be removed you need to replace the head, i.e. return the second element (this can be done by checking prev == null which would mean that n is the current head).
As mentioned by Thomas in his first point, you wrote the while loop incorrectly. Also, because you have a single linked list, you need to keep track of the previous node (also mentioned by Thomas).
public static ListNode removeElements(ListNode head, int val)
{
ListNode n = head;
ListNode prev = null; //the previous node.
while (n != null)
{
if (n.value == val)
{
//if prev is null it means that we have to delete the head
//and therefore we need to advance the head
if (prev == null)
{
head = n.next;
prev = null;//remains at null because there's nothing before head
n = head;//new head, let's start from here
} else
{
prev.next = n.next; //let's skip the element to delete
n = n.next;//let's advance to the next node
}
} else
{
//nothing to delete, let's advance to the next node and make
//the current node the previous node in the next iteration
prev = n;
n = n.next;
}
}
return head;
}
Its always a good practice to solve these questions with proper test cases. Design the possible test cases including the corner cases. Then follow your algorithm and see if it solves the problem for the test cases. Write the code and dry run it, this will do a sanity check of the code as well as of the logic.
Below are the cases for this question for which test cases must be written.
Null list,
List with one element and value being equal to the value to be deleted
List with one element and value not being equal to the value to be deleted
List with two elements and first node value being equal to the value to be deleted
List with two elements and last node value being equal to the value to be deleted
List with three elements and first node value being equal to the value to be deleted
List with three elements and second node value being equal to the value to be deleted
List with three elements and last node value being equal to the value to be deleted
Below is the code for the problem of removing nodes with a given value in a linked list.
public ListNode removeElements(ListNode head, int val) {
while(head != null && head.val == val){
head = head.next;
}
if(head == null){
return null;
}
ListNode node = head;
while(node.next != null){
if(node.next.val == val){
node.next = node.next.next;
}else{
node = node.next;
}
}
return head;
}

Insertion sort using Linked Integer Nodes

Hey there I have been trying to get an insertion sort method to work for a class I'm taking and we have been told to use insertion sort to sort a linked list of integers without using the linked list class already in the Java libraries.
Here is my inner Node class I have made it only singly linked as i don't fully grasp the circular doubly linked list concept yet
public class IntNode
{
public int value;
public IntNode next;
}
And here is my insertion sort method in the IntList class
public IntList Insertion()
{
IntNode current = head;
while(current != null)
{
for(IntNode next = current; next.next != null; next = next.next)
{
if(next.value <= next.next.value)
{
int temp = next.value;
next.value = next.next.value;
next.next.value = temp;
}
}
current = current.next;
}
return this;
}
The problem I am having is it doesn't sort at all it runs through the loops fine but doesn't manipulate the values in the list at all can someone please explain to me what I have done wrong I am a beginner.
you need to start each time from the first Node in your list, and the loop should end with the tail of your list -1
like this
public static IntList Insertion()
{
IntNode current = head;
IntNode tail = null;
while(current != null&& tail != head )
{
IntNode next = current;
for( ; next.next != tail; next = next.next)
{
if(next.value <= next.next.value)
{
int temp = next.value;
next.value = next.next.value;
next.next.value = temp;
}
}
tail = next;
current = head;
}
return this;
}
The insertion operation only works if the list being inserted into is already sorted - otherwise you're just randomly swapping elements. To start out, remove an element from the original list and construct a new list out of it - this list only has one element, hence it is sorted. Now proceed to remove the remaining elements from the original list, inserting them into the new list as you go. At the end the original list will be empty and the new list will be sorted.
I agree with the Zim-Zam opinion also.
The loop invariant of insertion sort also specifies this: "the subarray which is in sorted order".
Below is the code, I implemented for insertion sorting in which I created another linked list that contains the element in sorted order:
Node newList=new Node();
Node p = newList;
Node temp=newList;
newList.data=head.data;
head=head.node;
while(head!=null)
{
if(head.data<newList.data)
{
Node newTemp = new Node();
newTemp.data=head.data;
newTemp.node=newList;
newList=newTemp;
p=newList;
}
else
{
while(newList!=null && head.data>newList.data)
{
temp=newList;
newList=newList.node;
}
Node newTemp = new Node();
newTemp.data=head.data;
temp.node=newTemp;
newTemp.node=newList;
newList=p;
}
head=head.node;
}

Remove all occurrences of item from a Linked List

I've been working on this lab assignment for a few hours and can't understand why this code is not working. The question is to add the method int removeEvery(T item) that removes all occurrences of item and returns the number of removed items to a link list class that implements a link list interface.
This is my code: It removes some occurrences of the item, but not all of them.
public int removeEvery(T item){
int index = 0;
Node currentNode = firstNode;
for(int i = 1; i <= numberOfEntries; i++)
{
System.out.println(currentNode.getData());
if (item.equals(currentNode.getData())){
index++;
remove(i);}
else{
currentNode = currentNode.getNextNode();}
}
if(index != 0)
return index;
return -1;
}
Here is the remove method that was included in the LinkList class:
public T remove(int givenPosition)
{
T result = null; // return value
if ((givenPosition >= 1) && (givenPosition <= numberOfEntries))
{
assert !isEmpty();
if (givenPosition == 1) // case 1: remove first entry
{
result = firstNode.getData(); // save entry to be removed
firstNode = firstNode.getNextNode();
if (numberOfEntries == 1)
lastNode = null; // solitary entry was removed
}
else // case 2: givenPosition > 1
{
Node nodeBefore = getNodeAt(givenPosition - 1);
Node nodeToRemove = nodeBefore.getNextNode();
Node nodeAfter = nodeToRemove.getNextNode();
nodeBefore.setNextNode(nodeAfter); // disconnect the node to be removed
result = nodeToRemove.getData(); // save entry to be removed
if (givenPosition == numberOfEntries)
lastNode = nodeBefore; // last node was removed
} // end if
numberOfEntries--;
} // end if
return result; // return removed entry, or
// null if operation fails
} // end remove
There is something special with your linked list, you can access next element with current.getNextNode but you delete using the element index. You should look in the rest of your implementation how this index is managed. Does the first element have index 0 or 1 (you start your loop with 1). What happens to the indexes of all elements when you remove one. Do the elements know their index ?
You could use something like
int deletedNodes = 0;
int currentIndex = 0; // check if 1 or 0
currentNode = fist;
while(currentNode != null){ // I guess lastNode.getNextNode() is null
if(//should remove){
remove(currentIndex);
deletedNodes++
// probably no need to change the index as all element should have been shifted back one index
} else {
currentIndex++; // index changes only if no node was deleted
}
currentNode = currentNode.getNextNode(); // will work even if it was deleted
}
return deletedNodes;
I think the problem you have comes from remove(i).
When you remove the i-th element, the i+1-th element becomes the i-th and so on: every element is shifted. Therefore if you need to remove 2 elements in your list that are at index j and j+1, removing the j-th element calling remove(j) will shift the j+1-th element at the index j. Hence removing that second element requires calling remove(j) again, and not remove(j+1).
So you need to decrement i after removing.
Since your remove method actually decrements numberOfEntries, the condition on your while loop is properly updated. So all you need to do is replace
if (item.equals(currentNode.getData())) {
index++;
remove(i);
}
else {
currentNode = currentNode.getNextNode();
}
by
if (item.equals(currentNode.getData())) {
index++;
remove(i--);
}
// update the current node, whether removing it or not
currentNode = currentNode.getNextNode();
Iterator.remove()
This problem you are describing shows the usefulness of Iterator.remove() when using data structures from the JDK for going through an iterable collection and removing elements as you go through it.
After removing a node, as #Vakimshaar suggested, you need to decrement the i because the node at this index has been removed and there is a new node at the same index. In addition to that, you also need to update the currentNode reference as it would still be pointing to the node you've just removed, but it should really be pointing to the new node that has moved to this index.
So in the if (item.equals(currentNode.getData())){ block you need to do the following:
Node nextNode = currentNode.getNextNode();
index++;
remove(i--);
currentNode = nextNode;
With this, your code should correctly remove all occurrences.
Here is a Java Code to delete all occurrences of an item from a linked list :
public class LinkedList{
Node head;
class Node{
int data;
Node next;
Node(int d){data =d; next = null;}
}
public void push(int new_data){
Node new_node = new Node(new_data);
new_node.next = head;
head = new_node;
}
public void insertAfter(Node givenNode, int new_data){
if(givenNode == null)
System.out.println("Given node cannot be empty");
Node new_node = new Node(new_data);
new_node.next = givenNode.next;
givenNode.next = new_node;
}
public void append(int new_data){
Node new_node = new Node(new_data);
if(head == null)
head = new_node;
else{
Node last = head;
while(last.next != null)
last = last.next;
last.next = new_node;
}
}
public void printList(){
Node temp = head;
while(temp != null){
System.out.println(temp.data + " ");
temp = temp.next;
}
}
void deleteNode(int key){
// Store head node
Node temp = head, prev=null;
// If head node itself holds the key or multiple occurrences of key
while(temp != null && temp.data == key){
head = temp.next;
temp = head;
}
// Delete occurrences other than head
while(temp != null){
// Search for the key to be deleted, keep track of the
// previous node as we need to change 'prev.next'
while(temp != null && temp.data != key){
prev = temp;
temp = temp.next;
}
// If key was not present in linked list
if(temp == null) return;
// Unlink the node from linked list
prev.next = temp.next;
//Update Temp for next iteration of outer loop
temp = prev.next;
}
}
public static void main(String[] args){
LinkedList llist = new LinkedList();
llist.push(6);
llist.append(7);
llist.append(7);
llist.append(7);
llist.append(9);
llist.push(10);
llist.deleteNode(7);
llist.printList();
}
}
Output :
10
6
9

Categories