Use binary search if sorted otherwise use linear search - java

I'm given a problem where the user is given an empty recipe book and they can input and sort the recipes.
I know that a book is sorted if it is empty, has one recipe, and two recipes(ascending/descending). These can use binary search.
But when the user inputs a third recipe it could be either "cookies, donut, turkey" (which is sorted) or "cookies, donut, apples" and that's not sorted. If it's not sorted then I have to use linear search.
This is what I have so far
public void sortBook(int choice, boolean ascend) {
RecipeBookComparator comparing = new RecipeBookComparator(choice, ascend);
mList.sort(comparing);}
public class RecipeBookComparator implements Comparator {
private int mSortRBook;
private boolean mAscend;
public RecipeBookComparator (int choice, boolean ascend) {
mSortRBook = choice;
mAscend = ascend;
}
public int compare(Object o1, Object o2) {
Recipe s1 = (Recipe)o1, s2 = (Recipe)o2;
switch (mSortRBook) {
case 1:
if (mAscend == true) {
int compareName = s1.getName().compareTo(s2.getName());
if (compareName != 0) {
return compareName;
}
}
else {
int compareName = s1.getName().compareTo(s2.getName());
if (compareName != 0) {
return compareName * -1;
}
} ///more cases...
I know what I'm supposed to do, but I don't know how to approach it "code-wise"

To find out if a list is sorted, you would have to compare each element with ist neighbors. If only one element of thousands is not in order, a binary search can fail. So you would have to check the complete list. But going through all the list to check if the list is sorted takes longer than to look for one element in the list with linear search, so that makes no sense. Use linear search, if you don't know for sure if a list is sorted or not. That's it.

Your code says:
mList.sort(comparing);
I believe you've misunderstood what you've been asked to do - assuming that "use binary search if sorted otherwise use linear search", the title of your question, is what you are supposed to do. You are not supposed to sort them at all. This problem requires no knowledge of how to sort things in Java whatsoever.
What you need to understand is searching, not sorting. And how to check whether a sequence of inputs is already sorted.
Now, admittedly, technically you can check if a sequence is already sorted by actually sorting it and then checking if the resulting sequence is in the same order as what you started with. But I wouldn't recommend that.
What I would recommend, instead, is using a Comparator to compare each pair of adjacent elements (if any) in the sequence, to check if the sequence is monotonically increasing or monotonically decreasing.

Related

Bring Class Leader Id on top of the list using Java8 Streams

I have a list of students and I want to bring only the leaderId on top of the same list following remains the remaining list items using Java Streams. I tried the below logic but it is not working as expected
List<Student> students = ....
Long leaderId = 123;
students.stream().sort((s1,s2) -> leaderId.equals(s1.getId()) || leaderId.equals(s2.getId()) ? 1: 0).collect(Collectors.toList());
Can anyone please give me some suggestion on this
Example
List of students:
[{id:121, name:"John"}, {id:131, name:"Susan"}, {id:123, name:"Jacob"}, {id:155, name:"Sunny"}]
Expected Output
[{id:123, name:"Jacob"}, {id:121, name:"John"}, {id:131, name:"Susan"}, {id:155, name:"Sunny"}]
You have to write your own comparator first which places the leader at the front of the list when sorted using it. Then you have to use it for sorting the list. I don't see any point of using the streams here, since the iterative code looks more simple and readable. Here's how it looks in practice.
static class LeaderFirstComparator implements Comparator<Student> {
final long leaderId;
LeaderFirstComparator(long leaderId) {
this.leaderId = leaderId;
}
#Override
public int compare(Student o1, Student o2) {
if (o1.id == leaderId && o2.id != leaderId)
return -1;
else if (o1.id != leaderId && o2.id == leaderId)
return 1;
else
return 0;
}
}
And the client code:
students.sort(new LeaderFirstComparator(leaderId));
Update
If the first object is the leader, then it should come before the second, hence returning -1 according to the contract. Otherwise, if the second object is the leader, then the first one should come after it, hence 1. If nether of the objects are the leader or both are leaders, then the original ordering is preserved. Thus returning 0.
As per the below comment, you can further simplify it to this one liner:
students.sort(Comparator.comparing(s -> s.getId() != leaderId));
If the leader has a concrete id equals to 123, yo can get it using only a filter
List leaderStudents = students.stream().filter(s1 -> leaderId.equals(s1.getId()).collect(Collectors.toList());

Creating a dictionary: Method to prevent the same word from being added more than once

I need to create a method to determine whether or not the word I'm trying to add to my String[] dictionary has already been added. We were not allowed to use ArrayList for this project, only arrays.
I started out with this
public static boolean dictHasWord(String str){
for(int i = 0; i < dictionary.length; i++){
if(str.equals(dictionary[i])){
return true;
}
}
return false;
}
However, my professor told me not to use this, because it is a linear function O(n), and is not effective. What other way could I go about solving this method?
This is a example of how to quickly search through a Array with good readability. I would suggest using this method to search your array.
import java.util.*;
public class test {
public static void main(String[] args) {
String[] list = {"name", "ryan"
};
//returns boolean here
System.out.println(Arrays.asList(list).contains("ryan"));
}
}
If you are allowed to use the Arrays class as part of your assignment, you can sort your array and use a binary search instead, which is not O(n).
public static boolean dictHasWord(String str){
if(Arrays.binarySearch(dictionary, str) != -1){
return true;
}
return false;
}
Just keep in mind you must sort first.
EDIT:
Regarding writing your own implementation, here's a sample to get you going. Here are the javadocs for compareTo() as well. Heres another sample (int based example) showing the difference between recursive and non recursive, specifically in Java.
Although it maybe an overkill in this case, but a hash-table would not be O(n).
This uses the fact that every String can be turnt into an int via hashCode(), and equal strings will produce the same hash.
Our dictionary can be declared as:
LinkedList<String>[] dictionary;
In other words in each place several strings may reside, this is due to possible collisions (different strings producing the same result).
The simplest solution for addition would be:
public void add(String str)
{
dictionary[str.hashCode()].add(str);
}
But in order to do this, you would need to make an array size equal to 1 less the maximum of hashCode() function. Which is probably too much memory for you. So we can do a little differently:
public void add(String str)
{
dictionary[str.hashCode()%dictionary.length].add(str);
}
This way we always mod the hash. For best results you should make your dictionary size some prime number, or at least a power of a single prime.
Then when you want to test the existence of the string you do exactly what you had in the original, but you use the specific LinkedList that you get from the hash:
public static boolean dictHasWord(String str)
{
for(String existing : dictionary[str.hashCode()%dictionary.length])
{
if(str.equals(existing)){
return true;
}
}
return false;
}
At which point you may ask "Isn't it O(n)?". And the answer is that it is not, since the hash function did not take into consideration the number of elements in array. The more memory you will give to your array, less collisions you will have, and more this approach moves towards O(1).
If somebody finds this answer searching for a real solution (not homework assignment). Then just use HashMap.

Comparator not functioning as intended for PriorityQueue

I wrote this comparator function to be used with a priority queue. It works fine this way, it gives the words with the least frequency when I poll().
But this is frequency and I want them to behave in the opposite way. I swapped the return values -1 and +1, and that is leads to unordered and mixed results on poll().
Why does that happen, and how to fix it? Also, why is it necessary to always specify the initial size of a PriorityQueue while specifying the comparator in the constructor? Does it have any effect that I am unable to see?
public class StringFrequencyComparator implements Comparator<Word>
{
public int compare(Word x, Word y)
{
if (x.frequency() < y.frequency())
{
return -1;
}
if (x.frequency() > y.frequency())
{
return +1;
}
return 0;
}
}
Update: I had made a mistake. I was inserting values in the PriorityQueue and then updating their frequencies. But, I was confused because in spite of this mistake the orders were perfect for the comparator and only mixed up when I swapped the -1s and 1s values forcing me towards believing there was something wrong with the comparator. I still wonder how it was perfect.

Eliminating or avoiding adding duplicates in a ArrayList with custom Object

I have a custom object in this structure
static class Node {
int col;
int row;
int g;
int h;
int f;
public Node(int col, int row, int g, int h) {
this.col = col;
this.row = row;
this.g = g;
this.h = h;
this.f = g+h;
}
}
The col and row variables are unique, and may only occur once in ArrayList<Node> myList.
Is there a way optimal way to avoid adding or checking for possible duplicate without having to make a nasty for-loop?
I am aware that Set interface possibly could be a solution for this as duplicates cannot occur, but i have alot of code right now, which i do not want to refactor unless it becomes necessary.
Here are your options. All of these solutions require proper implementation of equals and hashCode. Since you want row and col to be unique:
public boolean equals(Object obj) {
if (obj == null || obj.getClass() != Node.class) {
return false;
}
Node other = (Node) obj;
if (other.col != this.col) {
return false;
}
if (other.row != this.row) {
return false;
}
return true;
}
public int hashCode() {
int result = 7;
result += row * 31;
result += col * 31;
return result;
}
Iterate over the List
You don't have to do the iteration yourself, but that is exactly what calling List.contains will do. This one is pretty easy:
if (!myList.contains(node)) {
myList.add(node);
}
This will iterate for you, so you don't have to write the loop.
List to Set to List
Here you have two sub-options. If you want to preserve the order of your input list, then you can use LinkedHashSet. If you don't care, you can just use HashSet. What I mean is if I have a List with elements A, B, C, converting it to a HashSet and back may produce a different list, like B, C, A. LinkedHashSet keeps the elements in insertion order, avoiding this problem. In any case, you'll just do this:
Set<Node> nodeSet = new [Linked]HashSet<Node>(myList);
nodeSet.add(node);
myList = new ArrayList<Node>(nodeSet);
Remember that this is essentially doing iteration as well, but it's using a hash-code shortcut instead of checking every element's equality, which may be a big deal with enough nodes. If your node list is small (less than 1000 elements) then I doubt this will make much of a difference, and you may as well use the first one.
Converting everything to Set
You mentioned that this would require a lot of refactoring in your code, but this isn't a bad thing, especially if you plan on working on this code a lot in the future. My rule of thumb is if the refactoring will make the code easier to maintain, adding a little extra development time is never a bad thing. Writing maintainable, readable, and understandable code is what the experts do (the question here isn't relevant, but this particular answer is). Since Set implies unique elements and List does not, then it makes sense to make the change. The compiler will pretty much tell you all the places you have to change with its errors, and it might take less time than you think.
Add a equals method in Node if possible :
#Override
public boolean equals(Node n){
if(this.getRow().equals(n.getRow()) && this.getCol().equals(n.getCol())
return true;
else
return false;
}
And then use list-to-set-to-list trick.
Try this method:
List<Node> removeDuplicateNodes(List<Node> inputList){
return (inputList ==null or inputList.size()==0)? Collections.EMPTY_LIST: new ArrayList<Node>(new HashSet<Node>(inputList));
}
Update :
If you wan't to maintain input order, use LinkedHashSet
List<Node> removeDuplicateNodes(List<Node> inputList){
return (inputList ==null or inputList.size()==0)? Collections.EMPTY_LIST: new ArrayList<Node>(new LinkedHashSet<Node>(inputList));
}
Add all the elements to a new Set, then put all the elements from the Set to a new List. That will make it.
I always find weird to see cases where people want to use a List (for the get(int) method, I guess) when they require unicity, which is only achieved through Set.
Anyway, by manipulating a little the equals/hashcode (making equals return true when row and col are the same) method and adding calls to List#contains(Object), you could have your goal reached without sacrifying your List
EDIT
Notice you could also create a Comparator and rely upon Collections#sort(List, Comparator) to have your list sorted and items with the same value melted into only one value.
Keep both a Set and a List. Use the Set to check for duplicates. Add to both Set and List if no dupe.
...assuming that Node has an .equals method defined...
private final Set<Node> seen = new HashMap<Node>();
private final List<Node> uniqueNodes = new ArrayList<Node>();
public void insertIfUnique(final Node n) {
if (seen.contains(n)) {
return;
}
seen.add(n);
uniqueNodes.add(n);
}
Ideally, you'd use Set, but if you'd like to avoid reimplementing your data structures from ArrayList<Node> to Set, you can implement Set as a gatekeeper:
Each time an element is about to be inserted into your ArrayList, check if the row-col pair is already in the Set.
If not, register the row-col pair into the Set
If the pair already exists in the set, do not insert it
Each time an element is about to be removed from your ArrayList, remove it from the Set.
And thus it's a "gatekeeper".
All of the Set operations are O(1) since they are hashed; minimal refactoring and no nasty loops as desired.

Creating a Queue that does not allow duplicate elements and should allow index based retrieval

I want to create a Queue which should not allow duplicate elements and I should be able to access elements of this queue based on index. Please let me know how should I implement this?
Well it is clear that Java doesn't have the exact data structure matching your specification and requirement. The closest that can match your requirement is probably a LinkedHashSet. It is basically a Set (matching your unique items requirement) whose elements are kept in insertion-order (like a Queue) and to get an element by index you can use set.toArray() to get an array or create a list out of the set (however it will cost cost some extra memory).
I am planning to use ConcurrentLinkedQueue for my problem. Here is the sample code
import java.util.concurrent.ConcurrentLinkedQueue;
public class FinalQueue {
private ConcurrentLinkedQueue<String> queue;
public FinalQueue()
{
queue = new ConcurrentLinkedQueue<String>();
}
public synchronized void enqueue(String ipAddress)
{
if(!queue.contains(ipAddress))
queue.add(ipAddress);
}
public String dequeue()
{
return queue.poll();
}
public String toString()
{
return "" + queue;
}
/**
* #param args
*/
public static void main(String[] args) {
FinalQueue queue = new FinalQueue();
queue.enqueue("1.2.3.4");
queue.enqueue("2.3.4.5");
queue.enqueue("1.1.1.1");
queue.enqueue("1.2.3.4");
System.out.println(queue.toString());
System.out.println("Dequeue..." + queue.dequeue());
System.out.println("Dequeue..." + queue.dequeue());
System.out.println(queue.toString());
}
}
You could always just use an ArrayList. It's good for accessing elements based on index and when adding elements you can always just check if the ArrayList contains the element to be added. My initial instinct was to use a Set for the disallowing of duplicates, but the elements are Sets are not indexed. If you can find a way to index the elements in Sets, then that would be my recommendation.
Don't call it a queue because by definition a queue only is a first in first out data structure.
Depending upon your input values, i believe you should use an array and a hash function. The hash determines which index an element is located using its value and vice versa i.e. when given an index it returns the value contained in it.
Since you are using a hash, the repetition is avoided when a collision occurs i.e. you can check if a value previously existed in an index and if it's the same value or not.
C++ stl has a good class for set though java i don't think have one. But the point is set does not offer index based retrieval.

Categories

Resources