Java- Manually sort a string array using compareTo() method - java

Let me first say that I know that there a better ways to sort where you might use something other than an array. This an assignment for class where the user can store strings in an array, delete, display, and sort them. I am completely lost to where to go from here. I am trying to use a bubble sort and everything works except whatever is the first entry in my array won't sort. I avoid null pointer exceptions through filtering out null values which is why my if statement is so long.
private void sortItems(String[] cargohold) {
String temp;
for (int i = 0; i < cargohold.length - 1; i++) {
for (int j = 1; j < cargohold.length; j++) {
if (cargohold[i] != null && cargohold[j] != null && (cargohold[i].compareTo(cargohold[j]) < 0)) {
temp = cargohold[j];
cargohold[j] = cargohold[i];
cargohold[i] = temp;
}
}
}
}
I have tried a bunch of different ways of doing this and I can't find any good reason why this shouldn't work. I have looked through anything I could find on Stack Overflow as far as examples and no one is having the same issue I am.
To recap, I may have 5 strings, "Derp", "Herp", "Sam", "Alfred", "Bill" and this sort will give me: "Derp", "Alfred", "Bill", "Herp", "Sam". Thanks in advance for the guidance.

The line
if(cargohold[i] != null && cargohold[j] != null && (cargohold[i].compareTo(cargohold[j]) < 0))
should be
if(cargohold[j] != null && cargohold[j-1] != null && (cargohold[j].compareTo(cargohold[j-1]) < 0))
and the swapping should be done as:
temp = cargohold[j];
cargohold[j] = cargohold[j-1];
cargohold[j-1] = temp;
Remember that in bubblesort you compare adjacent elements, where as your code does not do that.
Flaw
There will be cases when i > j and i < j, but the swapping logic remains the same, and that is completely wrong.

Bubblesort algorithm with some optimizations:
private static void sortItems(String cargohold[]) {
String temp;
boolean wasSwap = true;
for (int index1 = 0; index1 < cargohold.length - 1 && wasSwap; ++index1) {
wasSwap = false;
for (int index2 = 0; index2 < cargohold.length - index1 - 1; ++index2) {
if (cargohold[index2].compareToIgnoreCase(cargohold[index2+1]) > 0) {
temp = cargohold[index2];
cargohold[index2] = cargohold[index2+1];
cargohold[index2+1] = temp;
wasSwap = true;
}
}
}
}
Average complexity being O(n^2) with the best case of O(n).

Your implementation is wrong.
Here is bubble sort (in a java-like shorthand):
for (index1 = 0; index1 < array.length - 1; ++index1)
for (index2 = index1 + 1; index2 < array.length; ++index2)
if (array[index1] < array[index1])
swap(array[index1], array[index2]);
note the index2 = index1 + 1 of the inner loop.

String[] str = { "red", "blue", "green" };
int n = str.length;
String temp;
System.out.println("Original String Array = " + Arrays.toString(str));
for(int a=0;a<n;a++) {
for(int b=a+1;b<n;b++) {
if( str[a].compareTo(str[b]) > 0 ) {
temp = str[a];
str[a] = str[b];
str[b] = temp;
}
}
}
System.out.println("Sorted String Array = " + Arrays.toString(str));

Related

How do I remove duplicated strings in an array of strings?

public static String[] dictionary(String s){
int count=0;
String[] ans = sentence(s);
int size = ans.length;
for (int i = 0; i < size - 1; i++) {
for (int j = i + 1; j < ans.length; j++) {
if (ans[i].compareTo(ans[j]) > 0) {
String temp = ans[i];
ans[i] = ans[j];
ans[j] = temp;
}
}
}
for (int i = 0; i < ans.length; i++) {
ans[i] = [i].to lowerCase);
}
return ans;
}
I have to find duplicated strings and remove them from an array of strings that are sorted lexicographically
Because you know that incoming array sorted, you can get current element and compare next elements with current one. If it is equals skip it, otherwise add this element to the result, and change current. Something like this
public static String[] dictionary(String s){
String[] ans = sentence(s);
if (ans == null || ans.length == 0) {
return ans;
}
List<String> result = new ArrayList<String>();
String current = ans[0];
result.add(current);
for (int i= 1; i < ans.length; i++) {
if (!ans[i].equals(current)) {
result.add(ans[i]);
current = ans[i];
}
}
return result.toArray(new String[0]);
}
If you don't want to use sets, you can do it like this. And it does not require a sorted array. It works as follows. Note: This changes the passed in array.
Get the first element. If it is null continue to next element.
Compare first element to next.
If equal, replace next with a null and increment removed
Continue processing elements.
Now create a new array of original size minus - removed.
And copy all non null values to new array and return it.
public static String[] removeDups(String[] arr) {
// keep track of removed values
int removed = 0;
for (int i = 0; i < arr.length-1; i++) {
for (int j = i+1; j < arr.length; j++) {
if (arr[i] == null) {
// skip this entry and go to next
break;
}
if (arr[i].equals(arr[j])) {
// designate as removed
arr[j] = null;
removed++;
}
}
}
// copy remaining items to new array and return.
String[] result = new String[arr.length-removed];
int k = 0;
for (int i = 0; i < arr.length; i++) {
if (arr[i] != null) {
result[k++] = arr[i];
}
}
return result;
}
If you want to do it with streams you can do it this way.
String[] result = Arrays.stream(arr).distinct().toArray(String[]::new);
A simple way to do this would be to place them into a set
Set<String> setsHaveNoDuplicates = new HashSet<>(Arrays.asList(ans));
Sets have no duplicates, and if you want to convert the set back to an array of strings, you can do this
String[] result = setsHaveNoDuplicates.toArray(new String[0]);

Sorting two dimensional String array as per LastName without any Collection API

As I attended interview yesterday for an Organisation as I done every thing is best but I was unable to build logic for a Question called :
String [][]Names={{"John","Pepper"},
{"Smith","Adams"},
{"Katpiller","RhodSon"},
{"BillMark","pearson"}
};
As per the Last Name in the above array need to be sorted without any Collection.sort or CompareTo() or any other Collection API.
My Implementation is :
String str[]=new String[3];
for(int j=0; j<Name.length;j++)
{
for (int i=0 ; i<2; i++)
{
str[i]=Name[j][i];
}
for(int i=0;i<str.length;i++)
{
for(int k=i+1;k<str.length;k++)
{
if(str[i].compareTo(str[k])>0)
{
String temp= str[i];
str[i]=str[k];
str[k]=temp;
}
}
System.out.print(str[i]+ " ");
}
System.out.println();
}
Please Help me out this Hurdle Thanks in Advance
for(int j = Names.length - 2; j >= 0; j--)
{
String name = Names[j][0];
String lastName = Names[j][1];
int i = j + 1;
while(i < Names.length && strCompare(Names[i][1],lastName) < 0)
{
Names[i - 1][0] = Names[i][0];
Names[i - 1][1] = Names[i][1];
i = i + 1;
}
Names[i - 1][0] = name;
Names[i - 1][1] = lastName;
}
for(String[] name : Names)
System.out.print(Arrays.toString(name));
This is an implementation of Insertion Sort. Which sorts in place without the need of other using additional space and it works good on small inputs.
And my own reallyyyyyy bad implementation of a string comparation (String#compareTo) haha.. just to give you an idea on how to do it. (dont know if it works on all cases)
int strCompare(String str1, String str2){
//gets char arrays for both strings.
char[] strChar1 = str1.toUpperCase().toCharArray();
char[] strChar2 = str2.toUpperCase().toCharArray();
//gets max length of the strings
int maxLength = strChar1.length > strChar2.length? strChar1.length : strChar2.length;
//loops character by character to compare them.
for(int i = 0; i < maxLength; i++)
{
//if the length of the char array 1 is less than the index, then its smaller than str2.
if(strChar1.length <= i)
return -1;
//if the length of the char array 2 is less than the index, then its smaller than str1.
if(strChar2.length <= i)
return 1;
//compare characters and return -1 or 1 depending which one is larger.
if(strChar1[i] < strChar2[i]){
return -1;
} else if (strChar1[i] > strChar2[i]){
return 1;
}
}
//return 0 to say that they are equal if the loop doesnt return anything.
return 0;
}
Hope it helps.
Output : [Smith, Adams][John, Pepper][Katpiller, RhodSon][BillMark, pearson]
IMHO there are few questions to ask before trying to solve this problem :
should the sort be case sensitive (I suppose not)
should the sort be optimized for a great number of names (I suppose not)
should the array be sorted in place (I suppose yes)
If the answer to the second question is true, a correct algorithm must be implemented (quick sort, merge sort), and the lowercased name must be pre-calculated, if not a direct bubble sort is enough (O(n2/2) but simpler to implement)
int k;
String[] temp;
for (int i=0; i<len(Names) - 1; i++) {
k = i;
for (j=i + 1; i<len(Names); j++) {
if (Names[k][1].toLowerCase().compareTo(Names[i][1].toLowerCase()) < 0) {
k = j;
}
}
if (k != i) {
temp = Names[i];
Names[i] = Names[k];
Names[k] = temp;
}
}
If String.compareTo is not allowed, it simply has to be re-implemented and I suppose there are no accented chars in last name (é, è, ö, ü, etc.)
int compare(String a, String b) {
int n = (a.size() < b.size()) ? a.size(), b.size();
int delta;
for (int i =0; i<n, i++) {
delta = a.codePointAt(i) < b.codePointAt(j);
if (delta != 0) {
return delta;
}
}
return len(b) - len(a);
}
and the line if (Names[k][1]..compareTo(Names[i][1]...) { has to be rewritten as :
if (compare(Names[k][1].toLowerCase(), Names[i][1].toLowerCase()) < 0) {
This algorithm is really badly optimized, but it was simple to write (and would be simple to test). It is then possible to add as needed :
better sort algo (quick sort)
pre calculation of lowercase last name
management of accented characters.

Removing from an Array without holes

I have an Array of Cars that contains Car objects and I've been stuck for a while now on the method that gets a car number and needs to delete all objects that has that car number and then return that array without any holes. it's my homework so I can't use any arrayList or Array. methods.
I've tried to do the following but for some reason when I try to run it, it doesn't complie and gives me a IndexOutOfBounds error and refer me to my toString method(it prints all of the cells in the array).
noOfCars, saves the amount of cars there actually is right now as we built the array with a container constructor for holding more places for future cars just in case.
public void removeCarNumbers(int CarNum) {
int count = 0;
for(int i = 0; i < this.noOfCars; i++) {
if(this.cars[i].getCarNum() == CarNum) {
this.cars[i] = this.cars[noOfCars- 1 - count];
count++;
}
}
CarsLines [] newArr = new CarLines[this.noOfCars- count];
for(int i = 0; i< this.noOfCars - count; i++)
{
newArr[i] = this.cars[i];
}
this.cars= newArr;
}
*edit: after playing with it more I still get an error that refers me to my toString() method. is there a problem with it as well?
public String toString()
{
String output = "";
for (int i = 0; i < this.noOfCars; i++)
output += "" + this.cars[i].toString() + "\n";
return output;
}
Try:
// count
int count = 0;
for(int i = 0 ; i < this.cars.length ; ++i) {
if(this.cars[i].getCarNum() == CarNum) {
++count;
}
}
// create new array
CarsLines[] newArr = new CarLines[this.cars.length - count];
int index = 0;
for(int i = 0 ; i < this.cars.length ; ++i) {
if(this.cars[i].getCarNum() != CarNum) {
newArr[index++] = this.cars[i];
}
}
// assign
this.cars = newArr;
I don't think those two loops are doing what you think they are. In the first loop you should eliminate Cars which have the same number, perhaps setting them to null:
for (int i = 0; i < this.cars.length; i++) {
if (this.cars[i] != null && this.cars[i].getCarNum() == CarNum) {
this.cars[i] == null;
} else {
count++;
}
}
This will now eliminate duplicates, and keep a count of unique Cars. In the second loop, fill in just the non-null values:
CarsLines [] newArr= new CarLines[count];
int index = 0;
for(int i = 0; i< this.cars,length; i++)
{
if (this.cars[i] != null) { // Only pass in the non-null values
newArr[index] = this.cars[i];
index++;
}
}
this.cars= newArr;
Before you had this creating pretty much an identical copy of your list because you were filling it in on a one-to-one basis i.e. the same index.
Another possible way of doing this would be an in place conversion:
for (int i = 0; i < this.cars.length; i++) {
if (this.cars[i] != null && this.cars[i].getCarNum() == CarNum) {
for (int j = i+1; j < this.cars.length; j++) { // This loop will shift the
this.cars[j-1] = this.cars[j]; // whole list down one when
} // a duplicate is found,
// overwriting it.
i--; // sets the index back to recheck the shifted list.
this.noOfCars--; // You removed one duplicate by overwriting it
}
}
In response to #Boris The Spiders Comment, you could reduce the complexity of this algorithm by eliminating the inner for loop. I would recommend doing this by tracking the last valid position and using that for updating.
int writeIndex = 0;
for (int readIndex = 0; readIndex < this.cars.length; readIndex++) {
if (this.cars[readIndex] != null && this.cars[readIndex].getCarNum() == CarNum) {
// pass on this because nextIndex should track only the valid cars.
} else {
this.cars[writeIndex++] = this.cars[readIndex];
}
if (writeIndex < readIndex) {
this.cars[i] = null;
}
}
The idea is that you keep one index for reading (i) and one index for writing writeIndex. If your readIndex gets ahead of your writeIndex (this means you found a duplicate) you should clear it after it is read, and you will transer if to the previous writeIndex.
EDIT In response to the addition to your question, you should be extra careful on how noOfCars is calculated as this is probably the source of your Exception, instead try a more reliable indexing method by using the intrinsic length property from your array:
public String toString() {
String output = "";
for (int i = 0; i < this.cars.length; i++) {
if (this.cars[i] != null) {
output += "" + this.cars[i].toString() + "\n";
} else {
// condition that the i-th car is null
output += "null\n"; // one possible approach
// you could also do nothing and your toString would output just the non-null cars
}
}
return output;
}
for reliability, at the cost of efficiency, you could implement a count() method to double check your tracking. Maybe as a temporary check until your sure it is working correctly.
public int getNumCars() {
int c = 0;
for (int i = 0; i < this.cars.length; i++) {
if (this.cars[i] != null) {
c++;
}
}
return c;
}
For every car that you remove try swapping the last element in the array into the "hole". Keepa counter for the max element that is valid and decrement it everytime you swap out an element into the hold. You are basically rearragning the array elemtns in place
int max=this.noOfCars.length;
int i=0;
do{
if(this.cars[i]==null)
break;
if(this.cars[i].getCarNum() == CarNum) {
this.cars[i] = this.cars[max-1];
this.cars[max-1] = null;
}
else{
i++;
}
}while(1==1);
PS: you might have to add a few checks for boundary conditions(like empty array)
Here is a simple example of how to do this with a single loop, no nested loops.
The idea is that you keep track of how maybe elements you have removed (also how many null elements you have found) and then, if the current element is not to be removed, just shift it back by that number.
public static void main(final String[] args) throws IOException {
final Integer[] data = {1, 2, 2, 3, 4, 5, 1, 2, 3, 7, 8, 2, null, null, null};
int shift = 0;
for (int i = 0; i < data.length; ++i) {
if (data[i] == null || data[i] == 2) {
shift++;
data[i] = null;
} else if (shift > 0) {
data[i - shift] = data[i];
data[i] = null;
}
}
System.out.println(Arrays.toString(data));
}
Output:
[1, 3, 4, 5, 1, 3, 7, 8, null, null, null, null, null, null, null]
So you can see the 2s have been removed and the rest have been shifted up.

Exception:java.lang.NullPointerException when comparing two strings of two different string arrays

I'm coding trough codingbat.com/java and ran into an error i don't understand. I got two String arrays and want to compare them. If I just use the arrays all works fine (but the result is not right). To get the right result I programmed a helper function which eliminates all duplicates of an array. I tested the helper function, it returns the array shortened of the duplicates.
I can retrieve the values in the new Arrays with _a[i] etc., and don't get errors, but if i use _a[0].equals(_b[0]) or _a[0].compareTo(_b[0]) I get a NullPointerException (_a[0] == _b[0] works fine...).
If I just use the original arrays a,b the code runs without problems. I don't comprehend why i get a NullpointerException there.
Thanks for any help!
Code:
public int commonTwo(String[] a, String[] b) {
String[] _a = killDuplicate(a);
String[] _b = killDuplicate(b);
int ai=0, bi=0,count=0;
for (int i = 0; ai < _a.length & bi < _b.length; i++){
if ( _a[ai].compareTo(_b[bi]) > 0) { //NullPointerException here, but not if I use a,b
bi++;
} else if ( _a[ai].compareTo(_b[bi]) < 0){ //NullPointerException here, but not if I use a,b
ai++;
} else {
count++;
ai++;
bi++;
}
}
return count;
}
Helper Function:
public String[] killDuplicate(String[] a){
String temp = "";
int counter = 0, counter2 = 0;
for (int i = 0; i < a.length; i++){
if (! a[i].equals(temp)){
temp = a[i];
} else {
a[i] = "";
counter++;
}
}
String[] result = new String[a.length - counter];
for (int i = 0; counter2 < counter; i++){
if (a[i].equals("")) {
counter2++;
}
} else {
result[i-counter2] = a[i];
}
return result;
}
I guess you assume that your array of strings is sorted, otherwise your killDuplicate method wouldn't make sense at all.
The problem with your code is that in the second for loop in killDuplicate method you iterate with condition counter2 < counter which says iterate until all found duplicates are passed. So when you find your last duplicate you exit without filling the rest of the array. Try with example: new String[]{"A", "A", "B", "C"} you'll get [A, null, null].
There are numerous things that can be improved but the simplest modification of your code below. (I've changed only the second for loop)
public String[] killDuplicate(String[] a) {
String temp = "";
int counter = 0, counter2 = 0;
for (int i = 0; i < a.length; i++) {
if (!a[i].equals(temp))
temp = a[i];
else {
a[i] = "";
counter++;
}
}
String[] result = new String[a.length - counter];
for (int i = 0; i < a.length; i++) {
if (a[i].equals("")) continue;
result[counter2] = a[i];
counter2++;
}
return result;
}

Checking a string for a palindrome (java) (rookie status) [closed]

This question is unlikely to help any future visitors; it is only relevant to a small geographic area, a specific moment in time, or an extraordinarily narrow situation that is not generally applicable to the worldwide audience of the internet. For help making this question more broadly applicable, visit the help center.
Closed 10 years ago.
I am trying to check to see if, entered a string, that the string is a palindrome
if it is display something positive
if not... something negative (invalid)
I am currently getting the answer invalid both times (no matter what is entered)
i'm not quite sure if there is a problem with the for loops or the boolean statement.
//ACTION FROM BUTTON PERFORMED HERE
private void ButtonActionPerformed(ActionEvent evt) {
//creating variables
String myString = textField1.getText();
int stringLength = myString.length();
char arrayOne[] = new char[stringLength];
char arrayTwo[] = new char[stringLength];
boolean palindrome = false;
//for loop to setup ARRAY ONE
for(int i = 0; i < stringLength-1; i++){
arrayOne[i] = myString.charAt(i);
}
//for loop to setup ARRAY TWO
for(int i = stringLength-1; stringLength-1 > i; i--){
arrayTwo[i] = myString.charAt(i);
}
//for loop checking if array indexes are equivalent in value (char)
for(int i = 0; i < stringLength-1; i++){
if(arrayOne[i] != arrayTwo[i]){
palindrome = false;
}
else{
palindrome = true;
}
}
//assigning text to the text boxes based on boolean palindrome
if(palindrome == true){
textField2.setText("Valid");
}
if(palindrome ==false){
textField2.setText("Invalid");
}
}
}
i think i commented it descently
Change
for(int i = stringLength-1; stringLength-1 > i; i--)
to
for(int i = 0; i < stringLength-1; i++)
and change
for(int i = stringLength-1; i-1 > 0; i--)
to
for(int i = stringLength-1; i-1 >= 0; i--)
EDIT:
That was a debugging fest!!
Here is a working code:
String myString = textField1.getText();
int stringLength = myString.length();
char arrayOne[] = new char[stringLength];
char arrayTwo[] = new char[stringLength];
boolean palindrome = true;
//for loop to setup ARRAY ONE
for(int i = 0; i <= stringLength-1; i++){
arrayOne[i] = myString.charAt(i);
}
//for loop to setup ARRAY TWO
for(int i = stringLength-1, pos = 0; i >= 0; i--, pos++){
arrayTwo[pos] = myString.charAt(i);
}
//for loop checking if array indexes are equivalent in value (char)
for(int i = 0; i <= stringLength-1; i++){
if(arrayOne[i] != arrayTwo[i]){
palindrome = false;
break;
}
}
//assigning text to the text boxes based on boolean palindrome
if(palindrome == true){
textField2.setText("Valid");
}
else{
textField2.setText("Invalid");
}
I agree with the other answers about your error, but I think a more concise solution would be
boolean isPalindrome(String myString) {
int n = myString.length;
for( int i = 0; i < n/2; i++ )
if (myString.charAt(i) != myString.charAt(n-i-1)) return false;
return true;
}
Your code would now be
private void ButtonActionPerformed(ActionEvent evt) {
String myString = textField1.getText();
textField2.setText( isPalindrome(myString) ? "Valid" : "Invalid" );
}
//for loop to setup ARRAY TWO
for(int i = stringLength-1; stringLength-1 > i; i--){
arrayTwo[i] = myString.charAt(i);
}
This falls over after the first iteration.
You need to change it to something like:
//for loop to setup ARRAY TWO
for(int i = stringLength-1; i > 0; i--){
arrayTwo[i] = myString.charAt(i);
}
This loop copies all characters except the last one which probably is not what you wanted:
//for loop to setup ARRAY ONE
for(int i = 0; i < stringLength-1; i++){
arrayOne[i] = myString.charAt(i);
}
It should probably be fixed like this:
//for loop to setup ARRAY ONE
for(int i = 0; i < stringLength; i++)
{
arrayOne [i] = myString.charAt (i);
}
Body of this loop:
//for loop to setup ARRAY TWO
for (int i = stringLength-1; stringLength-1 > i; i--)
{
arrayTwo [i] = myString.charAt (i);
}
is never executed, because initial value of i: stringLength - 1 does not satisfy loop condition: stringLength - 1 > i.
You should probably change it to be:
// For loop to setup ARRAY TWO
for (int i = 0; i < stringLength; i++)
{
arrayTwo [i] = myString.charAt (stringLength - i - 1);
}
Also, after this loop:
// for loop checking if array indexes are equivalent in value (char)
for (int i = 0; i < stringLength-1; i++)
{
if (arrayOne [i] != arrayTwo [i])
{
palindrome = false;
}
else
{
palindrome = true;
}
}
variable palindrome will contain result of last comparison only, so if all characters except the last ones were different but last characters were equal, palindrome will be true which is probably not what you wanted. Probably you should change the code like this:
palindrome = true;
for (int i = 0; i < stringLength; i++)
{
if (arrayOne [i] != arrayTwo [i])
{
palindrome = false;
}
}
Note that I also changed stringLength - 1 to stringLength, otherwise you were ignoring last characters.
The easiest way to test for a palindrome in java
String str = "Able was I ere I saw Elba"
boolean palindrome = str.equalsIgnoreCase(new StringBuilder(str).reverse().toString());
Yep, that's it.
public static void main(String[] args) {
String s = "akaq";
boolean b = false;
for (int i = 0, j = s.length() - 1; i < j; i++, j--) {
if (s.charAt(i) == s.charAt(j)) {
b = true;
continue;
} else {
b = false;
break;
}
}
if (b)
System.out.println("Palindrome");
else
System.out.println("Not Palindrome");
}
Try something like this instead of 2-3 for loops.
Change the first for loop from stringLength-1 to just stringLength because you are using < and not <=
Change the second for loop to
if(int i = stringLength-1; i>=0; i--)
Also, set palindrome to true by default and remove the
else{
palindrome = true;
}
part because now if the first and last characters of the loop are the same, but not the middle, it will return true.
EDIT: Also the third for loop should be stringLength and not stringLength-1 because you are using < and not <=
There's no need to copy everything into arrays. String is basically an array itself. You can access the individual characters with charAt().
Also there is no need to loop the entire length of the String since equality is associative.
So simply use :
public boolean isPalindrome(String s) {
for (int i = 0; i < s.length() / 2; i++) { // only until halfway
if (s.charAt(i) != s.charAt(s.length() - i - 1)) { // accessing characters of String directly
return false;
}
}
return true;
}
One last remark : if the String's length is odd, you don't need to check the middle chracter. So in the code above the diision by

Categories

Resources