Hey I am searching for a better method to search a String element in an array of LinkedLists.
public static void main(String[] args) {
int m = 1000;
LinkedList<String>[] arrayOfList = new LinkedList[m];
for (int i = 0; i < m; i++) {
arrayOfList[i] = new LinkedList<>();
}
}
This is my search method:
public int search(String word) {
for (int i = 0; i < m; i++) {
for (int j = 0; j < arrayOfList[i].size(); j++) {
if (arrayOfList[i].get(j).equals(word)) {
return i;
}
}
}
return -1;
}
This is how my LinkedLists look like:
Example output: arrayOfList[0] = [house,car,tree.....]
arrayOfList[1] = [computer,book,pen....]
......
until arrayOfList[1000] = [...]
My search method should find the index of my word. Example: search("computer") = 1; search("house") = 0
Ah, a classic!
LinkedList is notoriously bad for random access, i.e. the list.get(j) method.
It's much better at iterating through the list, so it can jump from each item to the next item.
You could use list.iterator(), but the foreach loop does the same thing:
public int search(String word) {
for (int i = 0; i < m; i++) {
for (String listValue: arrayOfList[i]) {
if (listValue.equals(word)) {
return i;
}
}
}
return -1;
}
The other answer notes that you can get much better performance by iterating over each LinkedList rather than using List.get. That's because List.get has to search from the start of the list each time. For example, if the LinkedList has 100 elements, then on average each call to List.get(j) will have to iterate over 50 elements, and you're doing that 100 times. The foreach loop just iterates over the LinkedList elements once.
The foreach strategy runs in O(n) time, that is, the time required to perform the lookup increases proportional to n, the total number of words, because you have to search them all for each word.
If you're going to be doing this a lot, and you can use data structures other than LinkedList, then you should iterate through your array of LinkedList once and build a HashMap where the key is the word and the value is the number of the array in which that word appears. Setting up this HashMap will require O(n) time, but subsequent lookups will only require O(1) time, meaning a constant time regardless of the number of words involved. So if you're going to do more than a single lookup, creating the HashMap will have better performance in big-O terms, although for a very small number of lookups (2 or 3) it may still be faster to scan the arrays.
You can build a HashMap like this:
Map<String, Integer> index = new HashMap<>();
for (int i = 0; i < m; i++) {
for (String word: arrayOfList[i]) {
index.put(word, i);
}
}
Now search just becomes:
public int search(String word) {
return index.getOrDefault(word, -1);
}
Depending on how the strings are constructed in your program and how often you call the search method, testing on your strings' hash code can improve the performance. ex:
public int search(String word) {
int wordHashCode = word.hashCode();
for (int i = 0; i < m; i++) {
for (String listValue: arrayOfList[i]) {
if (listValue.hashCode() == wordHashCode && listValue.equals(word)) {
return i;
}
}
}
return -1;
}
Related
I decided to reduce the number of comparisons required to find an element in an array. Here we replace the last element of the list with the search element itself and run a while loop to see if there exists any copy of the search element in the list and quit the loop as soon as we find the search element. See the code snippet for clarification.
import java.util.Random;
public class Search {
public static void main(String[] args) {
int n = 10000000;
int key = 10000;
int[] arr = generateRandomSize(n);
long start = System.nanoTime();
int find = sentinels(arr, key);
long end = System.nanoTime();
System.out.println(find);
System.out.println(end - start);
arr = generateRandomSize(n);
start = System.nanoTime();
find = linear(arr, key);
end = System.nanoTime();
System.out.println(find);
System.out.println(end - start);
}
public static int[] generateRandomSize(int n) {
int[] arr = new int[n];
Random rand = new Random();
for (int i = 0; i < n; ++i) {
arr[i] = rand.nextInt(5000);
}
return arr;
}
public static int linear(int[] a, int key) {
for(int i = 0; i < a.length; ++i) {
if (a[i] == key) {
return i;
}
}
return -1;
}
public static int sentinels(int[] a, int key) {
int n = a.length;
int last = a[n-1];
a[n-1] = key;
int i = 0;
while (a[i] != key) {
++i;
}
a[n-1] = last;
if ((i < n - 1) || a[n-1] == key ) {
return i;
}
return -1;
}
}
So using sentinel search we are not doing 10000000 comparisons like i < arr.length. But why linear search always shows up better performance?
You'd have to look at the byte code, and even deeper to see what hotspot is making from this. But I am quite sure that this statement is not true:
using sentinel search we are not doing 10000000 comparisons like i <
arr.length
Why? Because when you access a[i], i has to be bounds checked. In the linear case on the other hand, the optimiser can deduce that it can omit the bounds check since it "knows" that i>=0 (because of the loop structure) and also i<arr.length because it has already been tested in the loop condition.
So the sentinel approach just adds overhead.
This makes me think of a smart C++ optimisation (called "Template Meta Programming" and "Expression Templates") I did about 20 years ago that led to faster execution times (at cost of a much higher compilation time), and after the next compiler version was released, I discovered that the new version was able to optimise the original source to produce the exact same assembly - in short I should have rather used my time differently and stayed with the more readable (=easier to maintain) version of the code.
Write a function:
class Solution{
public int solution(int[] A);
}
that, given an array A of N integers, returns the smallest positive integer(greater than 0)
that does not occur in A.
For example, given A = [1,3,6,4,1,2], the function should return 5.
Given A = [1,2,3], the function should return 4.
Given A = [-1, -3], the function should return 1.
Write an efficient algorithm for the following assumptions.
N is an integer within the range [1..100,000];
each element of array A is an integer within the range [-1,000,000..1,000,000].
I wrote the following algorithm in Java:
public class TestCodility {
public static void main(String args[]){
int a[] = {1,3,6,4,1,2};
//int a[] = {1,2,3};
//int b[] = {-1,-3};
int element = 0;
//checks if the array "a" was traversed until the last position
int countArrayLenght = 0;
loopExtern:
for(int i = 0; i < 1_000_000; i++){
element = i + 1;
countArrayLenght = 0;
loopIntern:
for(int j = 0; j < a.length; j++){
if(element == a[j]){
break loopIntern;
}
countArrayLenght++;
}
if(countArrayLenght == a.length && element > 0){
System.out.println("Smallest possible " + element);
break loopExtern;
}
}
}
}
It does the job but I am pretty sure that it is not efficient. So my question is, how to improve this algorithm so that it becomes efficient?
You should get a grasp on Big O, and runtime complexities.
Its a universal construct for better understanding the implementation of efficiency in code.
Check this website out, it shows the graph for runtime complexities in terms of Big O which can aid you in your search for more efficient programming.
http://bigocheatsheet.com/
However, long story short...
The least amount of operations and memory consumed by an arbitrary program is the most efficient way to achieve something you set out to do with your code.
You can make something more efficient by reducing redundancy in your algorithms and getting rid of any operation that does not need to occur to achieve what you are trying to do
Point is to sort your array and then iterate over it. With sorted array you can simply skip all negative numbers and then find minimal posible element that you need.
Here more general solution for your task:
import java.util.Arrays;
public class Main {
public static int solution(int[] A) {
int result = 1;
Arrays.sort(A);
for(int a: A) {
if(a > 0) {
if(result == a) {
result++;
} else if (result < a){
return result;
}
}
}
return result;
}
public static void main(String args[]){
int a[] = {1,3,6,4,1,2};
int b[] = {1,2,3};
int c[] = {-1,-3};
System.out.println("a) Smallest possible " + solution(a)); //prints 5
System.out.println("b) Smallest possible " + solution(b)); //prints 4
System.out.println("c) Smallest possible " + solution(c)); //prints 1
}
}
Complexity of that algorithm should be O(n*log(n))
The main idea is the same as Denis.
First sort, then process but using java8 feature.
There are few methods that may increase timings.(not very sure how efficient java 8 process them:filter,distinct and even take-while ... in the worst case you have here something similar with 3 full loops. One additional loop is for transforming array into stream). Overall you should get the same run-time complexity.
One advantage could be on verbosity, but also need some additional knowledge compared with Denis solution.
import java.util.function.Supplier;
import java.util.stream.IntStream;
public class AMin
{
public static void main(String args[])
{
int a[] = {-2,-3,1,2,3,-7,5,6};
int[] i = {1} ;
// get next integer starting from 1
Supplier<Integer> supplier = () -> i[0]++;
//1. transform array into specialized int-stream
//2. keep only positive numbers : filter
//3. keep no duplicates : distinct
//4. sort by natural order (ascending)
//5. get the maximum stream based on criteria(predicate) : longest consecutive numbers starting from 1
//6. get the number of elements from the longest "sub-stream" : count
long count = IntStream.of(a).filter(t->t>0).distinct().sorted().takeWhile(t->t== supplier.get()).count();
count = (count==0) ? 1 : ++count;
//print 4
System.out.println(count);
}
}
There are many solutions with O(n) space complexity and O(n) type complexity. You can convert array to;
set: array to set and for loop (1...N) check contains number or not. If not return number.
hashmap: array to map and for loop (1...N) check contains number or not. If not return number.
count array: convert given array to positive array count array like if arr[i] == 5, countArr[5]++, if arr[i] == 1, countArr[1]++ then check each item in countArr with for loop (1...N) whether greate than 1 or not. If not return it.
For now, looking more effective algoritm like #Ricola mentioned. Java solution with O(n) time complexity and O(1) space complexity:
static void swap(final int arr[], final int i,final int j){
final int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
static boolean isIndexInSafeArea(final int arr[], final int i){
return arr[i] > 0 && arr[i] - 1 < arr.length && arr[i] != i + 1 ;
}
static int solution(final int arr[]){
for (int i = 0; i < arr.length; i++) {
while (isIndexInSafeArea(arr,i) && arr[i] != arr[arr[i] - 1]) {
swap(arr, i, arr[i] - 1);
}
}
for (int i = 0; i < arr.length; i++) {
if (arr[i] != i + 1) {
return i+1;
}
}
return arr.length + 1;
}
I have a program I have to do in my comp sci class that is about keep track of cookie orders (think girl scout cookies). Here is a method designed to remove a certain variety of cookie and return the number of boxes removed. It keeps going out of bounds. The ArrayList is the "orders" variable it is made up of CookieOrder object(CookieOrder class below)
public int removeVariety(String cookieVar)
{
int counter = 0;
for(int i = orders.size() - 1; i > 0; i--)
{
if(orders.get(i).getVariety().equals(cookieVar))
{
counter += orders.get(i).getNumBoxes();
orders.remove(i);
}
}
return counter;
}
Here's the CookieOrder class
private String variety;
private int boxes;
public CookieOrder (String variety, int numBoxes)
{
this.variety = variety;
boxes = numBoxes;
}
public String getVariety()
{
return variety;
}
public int getNumBoxes()
{
return boxes;
}
Don't use for loop, use an iterator and use the Iterator#remove()
You initialize the for loop variable to start from orders.size() and go down to 0. But inside the loop you remove orders, so orders.size() is decreased but the loop variable i will not notice it.
The simplest solution is to loop from 0 to orders.size() - 1 - it will be evaluated every time, so it will notice the removal.
The problem is you are modifying the list as you are looping through it at the same time. That's not going to work. If you just want to sum up some value, you don't need to remove the item from the list.
Hope this helps
Try this instead note you should really use an Iterator but I think you will not have covered that yet in your course. Specifically note the difference between i > 0 and i >= 0:
public int removeVariety(String cookieVar)
{
int counter = 0;
for (int i = orders.size() - 1; i >= 0; i--)
{
CookieOrder o = orders.get(i);
if (o.getVariety().equals(cookieVar))
{
counter += o.getNumBoxes();
orders.remove(i);
}
}
return counter;
}
What you are doing is basically traversing the list in reverse order, so that the removed elements do not affect the indices of the remaining elements.
Alternative iterator based solution
If your curious here is a solution that uses an iterator:
public int removeVariety(String cookieVar)
{
int counter = 0;
Iterator iter = orders.iterator();
while (iter.hasNext())
{
CookieOrder o = iter.next();
if (o.getVariety().equals(cookieVar))
{
counter += o.getNumBoxes();
iter.remove();
}
}
return counter;
}
If you are using Java 8:
return orders.stream()
.filter(ord -> ord.getVarienty().equals(cookieVar))
.peek(orders::remove)
.mapToInt(ord -> ord.getNumBoxes())
.sum();
or using iterator (more imperative way):
int c = 0;
Iterator<CookieOrder> it = orders.iterator();
while (it.hasNext()) {
CookieOrder current = it.next();
if(current.getVarienty().equals(cookieVar)){
it.remove();
c += current.getNumBoxes();
}
}
return c;
What you were trying to do is to bite your own tail here :)
Do not modify a collection that you are currently processing.
I have a random set S of integers and the cardinality (n) of this set may vary from 10 to 1000. I need to store all sums of the nCr combinations of size r generated from this set. Usually r range from 3 to 10.
E.g. if S={102,233,344,442,544,613,71289,836,97657,12} and r=4, Then The sums generated will be {0,1,2,3}=102+233+344+442, {0,1,2,4}=102+233+344+544,....so on.
I implemented a findCombi function (below) in Java which gave me all nCr combinations in terms of r sized sets of indices and then I sifted through these sets in another function to generate the sum of corresponding elements.
But the program is giving heapspace error, probably because of exponential nature and I have 100-5000 of such sets, S. Or may be there is a memory leak?
Is there a faster and lesser-memory consuming way to do it?
Note: dsize=n, combiSize=r
List <List<Integer>> findCombi(int dsize,int combiSize) {
if( (combiSize==0) || (dsize==0) ){
return null;
}
long n=dsize;
int r=combiSize;
for(int i=1;i<combiSize;i++) {
n=n*(dsize-i);
r=r*i;
}
int totalcombi=(int) n/r;
List <List<Integer>> combiData=new ArrayList<>(totalcombi);
int pos;
List <Integer> combi=new ArrayList<>(combiSize);
for(int i=0;i<combiSize;i++) {
combi.add(i,i);
}
combiData.add(new ArrayList<>(combi));
pos=combiSize-1;
while(true) {
if(combi.get(pos)<(dsize-combiSize+pos)) {
combi.set(pos,combi.get(pos)+1);
if(pos==(combiSize-1)) {
combiData.add(new ArrayList<>(combi));
}
else {
combi.set(pos+1,combi.get(pos));
pos++;
}
}
else {
pos--;
}
if(pos==-1) {
break;
}
}
return combiData;
}
I needed something like that earlier, so here is some code adapted from the project I made back then. The method allSums builds a list of indices of size r, which is used to represent all the possible combinations. At each step, the current sum is added to the result set, then the next combination is generated. Since the results are put in a set, there is no way a result could appear twice. I included a main method so you can see it work. I hope this is clear, feel free to ask questions.
import java.util.*;
public class Program {
static private Set<Integer> allSums(List<Integer> values, int r) {
HashSet<Integer> res = new HashSet<>();
if ((values.isEmpty()) || r > values.size()) {
return res;
}
// build the list of indices
List<Integer> li = new ArrayList<>();
for (int i = 0; i < r; i++) {
li.add(i);
}
li.add(values.size()); // artificial last index : number of elements in set
while (true) {
// add the current sum to the result
int sum = 0;
for (int i = 0; i < r; i++) {
sum += values.get(li.get(i));
}
res.add(sum);
// move to the next combination
// first, find the last index that can be incremented
int i = r-1;
while ((i >= 0) && (li.get(i) == li.get(i+1)-1)) {
i--;
}
// was such an index found ?
if (i == -1) {
break; // if not, it's over
}
// increment the last index and set all the next indices to their initial value
li.set(i,li.get(i)+1);
for (int j = i+1; j < r; j++) {
li.set(j, li.get(j-1)+1);
}
}
return res;
}
public static void main(String[] args) {
List<Integer> values = new ArrayList<>();
values.add(10);
values.add(100);
values.add(1000);
values.add(10000);
values.add(100000);
Set<Integer> s = allSums(values, 3);
for (int i : s) {
System.out.println(i);
}
}
}
Im a beginner in Java and I had this doubt. Is it possible to use the Enhanced for loop in Java on an ArrayList, but start at the specified point rather than ArrayList[0].
For eg. ArrayList<Integer> calc = new ArrayList<Integer>;
// calc contains {0,1,2,3,4,5,6,7}
Can I use enhanced for loop and start iterating from calc[2] rather than calc[0]?? If possible, how can I do that?
In my particular case, using a enhanced for loop would be better, rather than a normal for loop.
The best way in Java would be like this:
for (Integer i : calc.subList(start, calc.size()) {
...
}
subList is an efficient view of the original list, so it's almost exactly what you need.
UPDATE
OK, motivated by Mikera's comments, I benchmarked it on jmh. This is the benchmarked code:
import org.openjdk.jmh.annotations.GenerateMicroBenchmark;
public class Benchmark1
{
static final List<Integer> list = new ArrayList(asList(1,2,3,4,5,6,7,8,9,10));
static { for (int i = 0; i < 5; i++) list.addAll(list); }
#GenerateMicroBenchmark
public long testIterator() {
long sum = 0;
for (int i : list) sum += i;
return sum;
}
#GenerateMicroBenchmark
public long testIndexed() {
long sum = 0;
for (int i = 0; i < list.size(); i++) sum += list.get(i);
return sum;
}
#GenerateMicroBenchmark
public long testSublistIterator() {
long sum = 0;
for (int i : list.subList(1, list.size())) sum += i;
return sum;
}
#GenerateMicroBenchmark
public long testIndexedSublist() {
long sum = 0;
final List<Integer> l = list.subList(1, list.size());
for (int i = 0; i < l.size(); i++) sum += l.get(i);
return sum;
}
}
And these are the results:
Benchmark ops/msec
-------------------------
Indexed 1860.982
IndexedSublist 1642.059
Iterator 1818.657
SublistIterator 1496.994
Conclusions:
enhanced for on the main list is as fast as indexed iteration, once past the initialization cost;
traversal of the sublist is somewhat slower than of the main list, and iteration is somewhat slower than indexed traversal;
all the differences are negligible for all practical purposes.
You're stuck using a traditional loop here...
for (int i = 2; i < calc.size(); i++) {
Integer x = calc.get(i);
}
Well, unless you're willing to create a temporary subList just for using an enhanced for loop, which is fine, because sublists are views of the original list and don't create a new list object:
for (Integer x : calc.subList(2, calc.size())) {
}
for(Item e : list.subList(1, list.size())) {
// ...
}
You could iterate through the sublist, as below:
for (Integer integerMember : calc.subList(2, calc.size()) {
// operation here
}
It should be noted that the enhanced for loop is less efficient for ArrayList then standard indexing. This is because we have to create an Iterator object and call hasNext and next on it on each step of the loop, causing unnecessary overhead.
For this reason I would recommend indexing in the traditional way rather than using the sublist approach.