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

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("[\\[\\]]", "");

Related

Remove Duplicates: Space Complexity for this Code

Given a string without spaces, the task is to remove duplicates from it.
Note: The original order of characters must be kept the same.
Example:
Input: S = "zvvo"
Output: "zvo"
Explanation: only keep the first
occurrence
I've written the following code, and I want to know its Space complexity.
public class Solution {
public String removeDups(String s) {
StringBuilder sb = new StringBuilder();
for(int i = 0; i< s.length(); i++){
String ch = Character.toString(s.charAt(i));
if (sb.indexOf(ch) == -1)
sb.append(ch);
}
return sb.toString();
}
}
Space complexity of your solution is O(n) since it requires allocating in memory an instance of StringBuilder which would contain up to n characters, and transforming it into a String of the same length.
These memory costs are fine, but time complexity of your solution can be improved. Presented code runs in O(n^2) because method indexOf() performs iteration over the characters in your StringBuilder instance under the hood, and it's being invoked for every character in the given string.
To reduce the time complexity you can maintain a HashSet, and offer each character to the Set, if the character gets rejected it would mean that it was encountered before.
Here's how it might be implemented:
public String removeDups(String s) {
Set<Character> seen = new HashSet<>();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
char ch = s.charAt(i);
if (seen.add(ch)) sb.append(ch);
}
return sb.toString();
}
Although we are using some additional memory, space complexity remains the same O(n) (because constants are not taken into consideration while evaluating the algorithm's complexity, 2*n and 3*n would result in O(n)). And time complexity would be linear O(n).

Find time complexity of this recursive function

This algorithm is a bit nonsense because I reduced it to the basic scheme.
Basically, it takes a string as input, scan this string and create a new string that does not contain the first letter of the old string.
Is that a O(n^2)? If you can justify the answer. Thank you.
recursiveProc(String myString){
if(myString.length() >= 1){
char firstLetter = myString.charAt(0);
String newString = "";
for(int i = 0; i < myString.length(); i++){
if(myString.charAt(i) != firstLetter){
newString = newString + myString.charAt(i);
}
}
recursiveProc(newString);
}}
It's actually worse than O(N^2). Looks like O(N^3).
Each recursive call will reduce the input String by at least one character, so there would be at most N recursive calls (in the worst case there would be exactly N recursive calls, each reducing the input String by exactly one character).
However, your loop takes O(N^2), since it has O(N) iterations, and each iteration creates a new String whose length is not a constant.
Suppose you have the String "0123456789"
The first recursive call will remove the '0' character by creating the following Strings:
"1"
"12"
"123"
"1234"
"12345"
"123456"
"1234567"
"12345678"
"123456789"
This would take O(N^2) time. And that's just the first recursive call.
You could improve it by using a StringBuilder instead of String concatenation to create the new String.
StringBuilder sb = new StringBuilder(myString.length()-1);
for(int i = 0; i < myString.length(); i++){
if(myString.charAt(i) != firstLetter){
sb.append(myString.charAt(i));
}
}
recursiveProc(sb.toString());
In that case the loop would take O(N) (since each iteration of the loop does constant work) and the entire recursion would take O(N^2).

Java simple application Complexity

I had to resolve a problem for an interview, and i didn't understood something.
The problem was, for a string like "we like hiking" to reverse the string like this: "ew ekil gnikih", this means, to explode the string like: str.split(" ") and then, each word to reverse and to add to the new string.
The problem was at the end of the problem, because was asking something about Complexity, like:
" - expected worst-case time complexity is O(N)"
- expected wors-case space complexity is O(N) (not counting the storage required for input arguments)
I resolved the problem like this:
public static String solution(String S){
if(S.length()>1 && S.length()<200000){
String toReturn = "";
String[] splitted = null;
if(S.contains(" ")){
splitted = S.split(" ");
}
else{
splitted = new String[]{S};
}
for(int i=0;i<=splitted.length-1;i++){
String wordToChange = splitted[i];
String reversedWord = "";
for(int j=wordToChange.length()-1;j>=0;j--)
reversedWord+=wordToChange.charAt(j);
toReturn+=" " + reversedWord;
}
return toReturn.trim();
}
return null;
}
What i should do about the complexity request?
Thanks!
About time complexity:
Worst-case O(N) means that you should have a worst case scenario in which you compute N iteration in order to get the result, in your case you split the String into an array of Strings [operation that probably takes O(N), but it depends on the method split()], then for each String in the array you start from the end of the String and invert the characters.
So your worst case is O(N) + O(N)... = O(N)
About space complexity:
Each String is an object and you reserve space in memory for each String you create, here you have an array of Strings, means that you create an object for each word or letter you split from the original string.
You worst case is a string like "I a m t h e o n e" in which you create a String for each character -> O(N)
Hope this clarifies your doubts
I think this is not the correct answer for at least the time complexity.
In my opinion the splitting of a String will most likely be a complexity of O(n). And it'll just add to next complexity, therefore we can forget about it.
Now you're creating an empty String and concatenating a char on top of each String. Here you're assuming this operation is as cheap like inserting a value into an array (O(1)).
In this case you should assume, that you're creating a new String each time with the member method concat(). If you're have the chance of looking into the implementation of String.concat(String) you'll see that this method is creating another char[] in the length of string1.length + string2.length and is copying both string1 and string2 into it. So this method alone has a complexity of O(n).
Because you're concatenating n chars for one word of n length, it has the complexity of O(n * (n / 2)) = O(n^2) for one word. Assuming the worst case of just one word, you have the complexity class of O(n^2).
I would concur with the space complexity class of O(n) for your code. But this is just under the assumption of the intelligence of the JVM reusing you're unused space.
Your code should work, but I think it is really not good code for this problem. You can improve it with a StringBuilder or direct operations on a char[].
The StringBuilder is a good idea, because it acts similar to a Vector or a ArrayList. I has a much bigger char[] working in its background than a String. If it's capacity is exceeded (by using the append() method), it will create a much bigger array, something like 2 time the size of the previous backing array. This might only be a time complexity of O(nlogn). But you can tweak it furthermore by initializing the StringBuilder with a capacity of the same size as your initial String. This means the backing char[] will not be copied at all, because you'll not exceed it's capacity.
Here is an example with the use of a char[]:
private static void reverseCharArray(char[] result, int start, int end) {
int halfDifference = (end - start) / 2;
for (int i = 0; i < halfDifference; i++) {
char buffer = result[start + i];
result[start + i] = result[end - i - 1];
result[end - i - 1] = buffer;
}
}
public static String reverseWordInString(String string) {
char[] stringAsCharArray = string.toCharArray();
int indexStart = 0;
for (int indexEnd = 0; indexEnd < stringAsCharArray.length; indexEnd++) {
if (stringAsCharArray[indexEnd] != ' ') {
continue;
}
StringOperations.reverseCharArray(stringAsCharArray, indexStart, indexEnd);
indexStart = indexEnd + 1;
}
StringOperations.reverseCharArray(stringAsCharArray, indexStart, stringAsCharArray.length);
return String.valueOf(stringAsCharArray);
}

I wrote a function that reverses the characters of each word in a sentence, while keeping the order of the words. I'm not sure of the time complexity

I wrote a function that does this, and it works; however, I'm a little bit uncertain about the time complexity of my function. I feel as though it's pretty inefficient, but I'm not sure if perhaps it's just the nature of the problem. I'm imagining there is a better way to do this opposed to how I did it. My initial thought was that this would be O(n^2) time complexity, but I think it may actually be worst, because of the split function I used. What's a better way to do this? Also, am I correct in thinking that this is actually worse than O(n^2)?
public static String wordReverse(String string){
//Split the string into an array such that each word is an element in the array
String[] arr = string.split(" ");
String result = "";
//Iterate throught the elements in the array
for(String value : arr){
String word = "";
//Reverse the letters of the element, and append them to a temp string
for(int i = value.length(); i > 0; i--){
word += value.charAt(i-1);
}
//Build the result string
result += word += " ";
}
//Return result string
return(result);
}
You're not worse that O(n^2). Actually, you're O(n).
split() is O(n), since it passed the array once.
You're reverse is O(n) too, since it checks all characters.
And concatenation is O(n) at worst.
The complexity of your approach is O(n) - linear time. The time increases linearly with the number of words and characters.

Finding the Big oh notation for my algorithm which uses arraylist.contains

I am in the process of learning about big oh notation. For the code below, I have a program that counts the number of words, skipping over the delimiters. For this algorithm, the for loop would be for the length of the sentence and then there is contains that iterates to the string as well. So according to me the the big oh notation for this algorithm would be O(n^3). Is that correct or am I missing something about big Oh notation ?
public class wordCount {
/**
* #param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
String sentence = "How are you,zak;far: mon. day ?:";
int count = 1;
ArrayList<Character> delim = new ArrayList<Character>();
delim.add(' ');
delim.add(',');
delim.add('.');
delim.add(':');
delim.add(';');
delim.add('"');
delim.add('\'');
for (int i = 0; i != sentence.length() - 1; i++) {
if (delim.contains(sentence.charAt(i))) {
if (!delim.contains(sentence.charAt(i + 1))) {
count++;
}
}
}
System.out.println("The count is: " + count);
}
}
No, you're not quite right - you're assuming that the "n" of the O(n) of contains is the same as the "n" that you're interested in. It's not - it's the length of the array list. In this case, that's the number of delimiters, not the sentence length.
So your algorithm is actually O(N * M) where N is the sentence length and M is the number of delimiters. If you view the set of delimiters as constant with only your sentence as input, your whole algorithm is O(N).
Even though you call contains conditionally the second time, the total number of contains calls per iteration is only ever 1 or 2 - it can't grow to (say) the number of delimiters or the size of the sentence.
It's not that simple. Assume we call the length of the string N and the length of delim M.
For each char in the string you are calling delim.contains(...) once or twice (which is O(M)), so the final complexity should be O(N x M).
Since this feels very much like homework (and if it is please add the homework tag), I'll provide some observations rather than a solution:
You have two different "N"'s here, the length of sentence and the number of items in delim. Those numbers can be vastly different.
You need to understand the big Oh performance of ArrayList<T>.Contains. Do you know what that is?
You have to think about how many loops are inside loops (and how far those loops go)
You have no more than two loops inside each other, not three.
for (int i = 0; i < sentence.length()-1; i++) { // loop level 1
boolean isDelim = delim.contains(sentence.charAt(i)); // loop level 2
if (!isDelim) continue;
if (!delim.contains(sentence.charAt(i + 1))) // loop level 2
count++;
}

Categories