Which approach is better and optimized between two of the following? - java

Below are the two approaches for reversing the string with calling API methods. Please tell which approach is better with due justification
public String functionOne(String str){
char arr[] = str.toCharArray();
int limit = arr.length/2;
for (int i = arr.length-1, j = 0; j < limit; i--, j++) {
char c = arr[i];
arr[i] = arr[j];
arr[j] = c;
}
return new String(arr);
}
public String functionTwo(String str) {
StringBuilder strBuilder = new StringBuilder();
char[] strChars = str.toCharArray();
for (int i = strChars.length - 1; i >= 0; i--) {
strBuilder.append(strChars[i]);
}
return strBuilder.toString();
}
Actually when I run my code on string of length 100000, approach second took double time as that of first approach. By using System.currentTimeMillis() I found execution difference of 1 in first approach and 2 in second approach.

How about this:
new StringBuilder("some string").reverse().toString();
The API already in place for this will likely use the most efficient manner.

Second is more readable: I can skim through that without having to think about what it's doing. I would go with that every time (unless there's a good reason why you need it to be milliseconds quicker?)
The first one stops my brain for a few seconds. That means it's dangerous and can easily be broken by future changes. It either needs comments, or replacing (with the second one).

Both are equally same. First is using n/2 operation which is O(n) and the second one is doing it in n operation which is also of O(n) time complexity.
In practice both will run almost equally well because n or n/2 operation won't make much difference.
EDIT: If you don't get the time complexity meaning, try generating a random string of large length say 1 million and calculate the time for both approach.

Related

Reducing run time in java

Below Java code produces the valid output but it takes more time to execute. Code works fine in eclipse, but it do not work in an online compiler like hackerrank or hackerearth since it takes more time for execution.Someone help me to find the solution for my time complexity problem.
I have tried to find the solution of the problem but i wasn't able to fix the performance by reducing the time..
Scanner scan = new Scanner(System. in );
String s = "aab";
String s1 = "";
String s2 = "";
int n1 = 0;
int length = 0;
long n = 882787;
long count = 0;
while (s1.length() < n) {
s1 = s1 + s;
}
if (s1.length() > n) {
count = s1.length() - n;
n1 = (int) count;
}
for (int i = 0; i < s1.length() - n1; i++) {
if (s1.charAt(i) == 'a') {
length += 1;
}
}
System.out.println(length);
Explanation of the above program:
I have a string s,in lowercase English letters that .I have repeat the string for n times and I store it in the new string.
I have to find the number of occurrences of 'a' in my new string
How do i actually reduce the time complexity for the above program
Thanks in advance
I would use a regular expression to create a String based on the initial input consisting of only letter 'a'(s). Take the length of that String and multiply it by n. That is one line that looks like
System.out.println(s.replaceAll("[^a]+", "").length() * n);
You are going to add s to the string n/s.length() times, call this N:
int N = n / s.length();
Each time you add s to the string you are going to append the number of As in s:
int a = 0;
for (int i = 0; i < s.length(); ++i) {
a += s.charAt(i) == 'a' ? 1 : 0;
}
// Or int a = s.replaceAll("[^a]", "").length();
So multiple these together:
int length = a * N;
String is immutable. Modification of a string is in fact create a new String object and put both old and new String into Java String constant poom
If you don't want to change your algorithm, I'd suggest to use StringBuilder to improve the speed of the execution. Note that StringBuilder is not thread safe
String s="aab";
int n1 = 0;
StringBuilder sb1 = new StringBuilder();
int length=0;
long n=882787;
long count=0;
while(sb1.length() < n) {
sb1.append(s);
}
if(sb1.length()>n) {
count =sb1.length()-n;
n1=(int)count;
}
for(int i=0;i<sb1.length()- n1;i++) {
if(sb1.charAt(i)=='a') {
length+=1;
}
}
System.out.println(length);
From here
When to use which one :
If a string is going to remain constant throughout the program, then
use String class object because a String object is immutable. If a
string can change (example: lots of logic and operations in the
construction of the string) and will only be accessed from a single
thread, using a StringBuilder is good enough. If a string can change,
and will be accessed from multiple threads, use a StringBuffer because
StringBuffer is synchronous so you have thread-safety.
I see multiple possible optimizations:
a) One pattern that is not that good is creating lots of Strings through repeated string concatenation. Each "s1 = s1 + s;" creates a new instance of String which will be obsolet the next time the command runs (It increases the load, because the String instances will be additional work for the Garbage Collector).
b) Generally: If you find, that your algorithm takes too long, then you should think about a complete new way to solve the issue. So a different solution could be:
- You know the length you want to have (n) and the length of the small string (s1) that you use to create the big string. So you can calculate: How often will the small string be inside the target string? How many characters are left?
==> You can simply check the small string for the character you are looking for. That multiplied by the number how often the small string will be inside the big string is the first result that you get.
==> Now you need to check the substring of the small string that are missing.
Example: n=10, s1="aab", Looking for "a":
So first we check how often the s1 will fit into a new string of n Characters n/length(s1) => 3
So we check how often the "a" is inside "aab" -> 2
First result is 3*2 = 6
But we checked for 3*3 = 9 characters so far, but we want 10 characters. So we need to check n % length(s1) = 1 character of s1 and in this substring ("a"), wie have 1 a, so we have to add 1.
So the result is 7 which we got without building a big string (which is not required at all!)
Just check how many times the char occurs in the original and multiple it by n. Here's a simple way to do so without even using regex:
// take these as function input or w/e
String s = "aab";
String find = "a";
long n = 882787;
int count = s.length() - s.replaceAll(find, "").length();
System.out.println(count * n);

Why is the second code more efficient than the first one?

I am confused between two codes, why the second one I am going to give here is more efficient than the first one.
Both of the codes just reverse a String, but first code is slower than the other and I am not able to understand why.
The first code is:
String reverse1(String s) {
String answer = "";
for(int j = s.length() - 1; j >= 0; j--) {
answer += s.charAt(j);
}
return answer;
}
The second code is:
String reverse2(String s) {
char answer[] = new char[s.length()];
for(int j = s.length() - 1; j >= 0; j--) {
answer[s.length() - j - 1] = s.charAt(j);
}
return new String(answer);
}
And I'm not able to understand how the second code is more efficient than the first one, I'd appreciate any insight on this.
The first code declares
String answer;
Strings are immutable. Therefore, every append operation reallocates the entire string, copies it, then copies in the new character.
The second code declares
char answer[];
Arrays are mutable, so each iteration copies only a single character. The final string is created once, not once per iteration of the loop.
Your question is perhaps difficult to answer exactly, in part because the answer would depend on the actual implementation of the first version. This, in turn, would depend on what version of Java you are using, and what the compiler decided to do.
Assuming that the compiler keeps the first version verbatim as you wrote it, then yes, the first version might be more inefficient, because it would require allocating a new string for each step in the reversal process. The second version, on the contrary, just maintains a single array of characters.
However, if the compiler is smart enough to use a StringBuilder, then the answer changes. Consider the following first version:
String reverse1(String s) {
StringBuilder answer = new StringBuilder();
for (int j = s.length() - 1; j >= 0; j--)
answer.append(s.charAt(j));
return answer;
}
Under the hood, StringBuilder is implemented using a character array. So calling StringBuilder#append is somewhat similar to the second version, i.e. it just adds new characters to the end of the buffer.
So, if your first version executes using literal String, then it is more inefficient than the second version, but using StringBuilder it might be on par with the second version.
String is immutable. Whenever you do answer += s.charAt(j); it creates a new object. Try printing GC logs using -XX:+PrintGCDetails and see if the latency is caused by minor GC.
String object is immutable and every time you made an add operation you create another object, allocating space and so on, so it's quite inefficient when you need to concatenate many strings.
Your char array method fits your specific need well, but if you need more generic string concatenation support, you could consider StringBuilder
In this code you are creating a new String object in each loop iteration,because String is immutable class
String reverse1(String s) {
String answer = "";
for (int j = s.length() - 1; j >= 0; j--)
answer += s.charAt(j);
return answer;
}
In this code you have already allocated memory to char array,Your code will create only single String at last line, so it is more efficient
String reverse2(String s) {
char answer[] = new char[s.length()];
for (int j = s.length() - 1; j >= 0; j--)
answer[s.length() - j - 1] = s.charAt(j);
return new String(answer);
}
Why is the second code more efficient than the first one?
String is immuable, by answer += s.charAt(j); you are creating a new instance of String in each loop, which makes your code slow.
Instead of String, you are suggested to use StringBuilder in a single thread context, for both performance and readablity(might be a little slower than fix-sized char array but has a better readablity):
String reverse1(String s) {
StringBuilder answer = new StringBuilder("");
for (int j = s.length() - 1; j >= 0; j--)
answer.append(s.charAt(j));
return answer.toString();
}
The JVM treats strings as immutable. Hence, every time you append to the existing string, you are actually create a new string! This means that a new string object has to be created in heap for every loop iteration. Creating an object and maintaining its lifecycle has its overhead. Add to that the garbage collection of the discarded strings (the string created in the previous iteration won't have a reference to it in the next, and hence, it is collected by the JVM).
You should consider using a StringBuilder. I ran some tests and the time taken by the StringBuilder code is not much smaller than that of the fixed-length array.
There are some nuances to how the JVM treats strings. There are things like string interning that the JVM does so that it does not have to create a new object for multiple strings with the same content. You might want to look into that.

How to make a program to print the series : 1,12,123...12345678910,1234567891011...& so on to the nth term?

//I tried this one but output was wrong for tenth term
import java.io.*;
public class series
{
public static void main(String args[])throws IOException
{
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
int n,i,i1=0,s=0,c=0;
System.out.println("Enter the term of the series you want to get");
n=Integer.parseInt(in.readLine());
for (i=1;i<=n;i++)
{
i1=i;
while (i1!=0)
{
c+=1;
i1=i1/10;
}
s=(int)(s*(Math.pow(10,c))+i);
c=0;
System.out.print(s+" ");
}
}
}
I don't know why you are using your current approach. I would go about this by keeping track of the previous term which was printed.
StringBuilder term = new StringBuilder("");
final int N = 20;
for (int i=1; i <= N; ++i) {
term.append(i);
if (i > 1) System.out.print(",");
System.out.print(term.toString());
}
Demo
Edit: The reason I suggest using a string to display each term is that your requirement appears to mainly be one of presentation. That is, you're not actually doing any math with each term, so why not just avoid a numeric type completely, which also avoids things like overflow and potential loss of precision.
Whilst Tim's answer is neat, I think that the exercise is sufficiently basic that StringBuilder is beyond its scope (*).
Instead, you can use a nested for loop:
final int N = 20;
for (int i=1; i <= N; ++i) {
if (i > 1) System.out.print(",");
for (int a = 1; a <= i; ++a) {
System.out.print(a);
}
}
(This is also going to be more memory efficient, since there is no need to keep reallocating the StringBuffer's internal buffer as i increases. But this is really of secondary (or even lesser) concern).
(*) Yes, you could do the same without StringBuilder, just using String concatenation; but that would be inefficient in ways that beginners may not "get", and so it is something that is best just steered around. Nested loops are far more generally useful than string concatenation in whatever form as a concept to get your head around.
The main reason you are getting wrong output after 10th term is Integer Overflow. You can use long to get rid of that to certain more terms but a definitely better solution is as answered using Strings

Is the time complexity of this code O(N^2)

This is a solution of mine for one of the problems in leetcode. Through my deductions, I concluded it to have an overall O(N^2) time complexity. However, I would like to get a confirmation on this just so that I don't continue making the same mistakes when it comes to judging the time/space complexity of an algorithm.
Oh, and the problem goes as follows:
Given an input string, reverse the string word by word.
e.g. "I am you" == "you am I"
The code is as follows:-
public String reverseWords(String s) {
//This solution is in assumption that I am restricted to a one-pass algorithm.
//This can also be done through a two-pass algorithm -- i.e. split the string and etc.
if(null == s)
return "";
//remove leading and trailing spaces
s = s.trim();
int lengthOfString = s.length();
StringBuilder sb = new StringBuilder();
//Keeps track of the number of characters that have passed.
int passedChars = 0;
int i = lengthOfString-1;
for(; i >= 0; i--){
if(s.charAt(i) == ' '){
//Appends the startOfWord and endOfWord according to passedChars.
sb.append(s.substring(i+1, (i+1+passedChars))).append(" ");
//Ignore additional space chars.
while(s.charAt(i-1) == ' '){
i--;
}
passedChars = 0;
}else{
passedChars++;
}
}
//Handle last reversed word that have been left out.
sb.append(s.substring(i+1, (i+1+passedChars)));
//return reversedString;
return sb.toString();
}
My reasoning for this being an O(N^2) algorithm:-
The loop = O(n)
StringBuilder.append = O(1)
Substring method = O(n) [as of Java 7]
On that note, if anyone else has a better solution than this, please feel free to share it! :)
I was aiming for a one-pass solution and therefore, opted out of splitting the string before the loop.
Appreciate the help!
EDIT: I meant to ask about the time complexity of the portion of the code that contains the loop. I apologize in advance if the question was misleading/confusing. The whole chunk of code is meant for clarification purposes. :)
Time complexity is O(n).
Each insertion (append(x)) to a StringBuilder is done in O(|x|), where |x| is the size of the input string you are appending. (independent of the state of the builder, on average).
Your algorithm iterates the entire string, and use String#substring() for each word in it. Since the words do not overlap, it means you do a substring() for each word one time, and append it to the builder (also once) - giving you 2|x| for each word x.
Summing it up, gives you
T(S) = |S| + sum{2|x| for each word x}
But since sum{|x| for each word x} <= |S|, this gives you total of:
T(S) = |S| + 2sum{|x| for each word x} = |S| + 2|S| = 3|S|
Since |S| is the size of the input (n), this is O(n)
Note that the important part is in jdk7, the substring() method is linear in the size of the output string, not the original one (you copy only the relevant part, not all of the string).
Here is an alternative solution which I believe may perform a little better.
public String reverseWords(String s) {
String[] array = s.split(" ");
int len = array.length;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < len; i++) {
sb.append(" ").append(array[len - i - 1]);
}
return sb.toString().trim();
}
Amit has already given you detailed explanation on the complexity computation, I would like to give you a simpler version.
In general, if we have nested loops, we consider the complexity to be O(N^2). But this is not the case always, as you have to do some activity n times for each nth part of input. E.g., if you have input of size 3, you have to do some action 3 times on each of the element. Then, you can say that your algorithm has O(n^2) complexity.
Since you are traversing and processing each part of your input string only once (even though you are using nested loops), complexity should be on the order of O(n). For proof, Amit has done quite a job.
Although, I would have used below code to reverse the order of words
String delim = " ";
String [] words = s.split(delim);
int wordCount = words.length;
for(int i = 0; i < wordCount / 2; i++) {
String temp = words[i];
words[i] = words[wordCount - i - 1];
words[wordCount - i - 1] = temp;
}
String result = Arrays.toString(words).replace(", ", delim).replaceAll("[\\[\\]]", "");

What is the most efficient way to reverse a char array?

I tried two different ways to reverse a char array
//method 1
char c[] = {'A', 'B', 'C', 'D'};
char c_rev[] = new char[4];
for (int i = 3; i >= 0; i--) {
c_rev[i] = c[3 - i];
}
System.out.println(Arrays.toString(c_rev));
//method 2
char c[] = {'A', 'B', 'C', 'D'};
Stack<Character> st = new Stack();
for (int i = 0; i < 4; i++) {
st.push(c[i]);
}
for (int i = 0; i < 4; i++) {
c[i] = st.pop();
}
System.out.println(Arrays.toString(c));
I just wondered what will be the most efficient one. Method 1 or Method 2 ?
Can anyone help me or give any suggestions ?
In terms of time complexity, they're both O(n). Performance wouldn't be significant here.
Which to choose? None. I would use a ready method StringBuilder#reverse:
String reversed = new StringBuilder(new String(c)).reverse().toString();
If I wanted to choose one from the two methods you posed, I would have choose the first one, it have only one loop and it's straightforward, no methods will be called, no helper objects will be created; you simply create a new array and directly push to it the new elements.
#MarounMaroun's answer will work for char arrays that are really strings. If you are worried only about those two methods then the first involves less heap allocations and GC.
However in general for an array I would use neither of your methods, but instead:
int len = c.length;
for(int i = 0; i < len / 2; i++) {
char ch = c[i];
c[i] = c[len - i - 1];
c[len - i - 1] = ch;
}
It is:
shorter to type than method 2
only iterates for half the array length (it is still O(n) due to the ops per iteration)
will work for any array type
doesn't need extra object allocations (vs Method 2) or two arrays (vs Method 1)
I would, however, be careful of micro-optimizations like this. The difference between this and Method 1 is probably minimal for small array allocations so you are better off using the one that is easiest for you to understand. (Similarly I only pulled the len variable out for clarity - some microbenchmarks claim it speeds up loops, but the downside is it pollutes your local variables with something that is only used inside the loop).
I'd say that method 1 is probably faster since it doesn't require the creation, allocation, and destruction of new objects (i.e. the Stack and its internal objects). For the same reason it should also have a lower impact on the garbage collector.
If this is a frequent operation in your code, you can benchmark both methods using something like Google caliper. Otherwise, I wouldn't worry about it :)
Logical operators works more efficiently than using a method of assignment operators (swapping) using third variable:
public static char[] reverseArray(char[] array) {
for(int i=0; i<array.length/2; i++)
{
array[i]^=array[array.length-i-1];
array[array.length-i-1]^=array[i];
array[i]^=array[array.length-i-1];
}
return array;
}
public static void main(String[] args) {
char[] input = {'H','e','l','l','o'};
System.out.println(reverseArray(input));
char[] input2 = {'J','a','v','a'};
System.out.println(reverseArray(input2));
}

Categories