Sort digits of a given integer, without '0' - java

I'm trying to sort the digits of a given integer by turning the integer to a String and creating an array by the size of that String length.
I'm using the modulu option to separate the digits and in the end I'm reversing it by multiplying by 10.
The problem that it's going out of bound each time and I don't know how to make the size of the array to work good for me.
Here are the code :
String s = String.valueOf(num);
int[] arr = new int[s.length()+1];
while(num != 0) {
arr[(int) num % 10]++;
num = num / 10;
}
long result = 0;
for(int i = 0 ; i < arr.length - 1 ; i++){
for(int j = 0 ; j < arr[i] ; j++) {
result = result * 10;
result = result + i;
}
}
return result;

There seems to be a lot of overkill solving this, as in a lot of code, so here is my take on it.
static int sortDigits(int num) {
char[] arr = Integer.toString(num).toCharArray();
Arrays.sort(arr);
return Integer.parseInt(new String(arr));
}
Test
System.out.println(sortDigits(4201514)); // 112445
You can of course do the same for the long and BigInteger versions:
static long sortDigits(long num) {
char[] arr = Long.toString(num).toCharArray();
Arrays.sort(arr);
return Long.parseLong(new String(arr));
}
static BigInteger sortDigits(BigInteger num) {
char[] arr = num.toString().toCharArray();
Arrays.sort(arr);
return new BigInteger(new String(arr));
}

If I understand your question correctly, when given an integer, you want to "sort" each digit, ignoring 0's.
To do so, so you can first convert it to a string:
String value = String.valueOf(num);
Since you have a string, you can use the split() function to split each 'number' like so:
String[] numbers = value.split("");
Then, you can find the indexes of 0's (and store it somewhere).
ArrayList<Integer> indexes = new ArrayList<>();
for (int i = 0; i < numbers.length; i++) {
if (numbers[i].equals("0")) {
indexes.add(i);
}
}
Then, you can sort the array of strings (numbers) using the sort() function:
Arrays.sort(numbers);
Then, you can remove the 0's like this (by creating a new ArrayList):
ArrayList<String> copy = new ArrayList<>();
for (String s : numbers) {
if (!s.equals("0")) {
copy.add(s);
}
}
(Here, you may use ArrayUtils if you already imported the library.)
Then, concatenate each element to make one entire string with join():
String result = String.join("", copy);
Finally, using the indexes, insert 0's to where they were located at initially:
for (int i : indexes) {
result = result.substring(0, i) + "0" + result.substring(i);
}
result would be what you want.
Note: This might not be the most sufficient way to do it, so you can modify it anywhere you want.

Based on your code I made a few modification
size of array should be 10 since in a number only 0 to 9 digits are possible
use this array to make a frequency array of occurrence of each digit
iterate over the array from 1 to 9 and make a sorted number
public static long sortInt(long num) {
int[] arr = new int[10];
while(num != 0) {
arr[(int) num % 10] ++;
num /= 10;
}
long result = 0;
for(int i = 1 ; i < arr.length ; i++)
while(arr[i]-- != 0)
result = result * 10 + i;
return result;
}

Related

Find numbers with same first and last digits

Sorry if I type something wrong, I am new. I want to create a method that takes an array, and gives back an array with the numbers that have the same first and last digits in the previous array.Example: 12 is equal to 1342.
I have created this for loop to go through the numbers, but I don't know how to compare them.
public int[] findDuplicates(int[] a) {
List<Integer> result = new ArrayList<>();
for (int i = 0; i < a.length; i++) {
for (int j = i + 1; j < a.length; j++) {
if ((//what do I write here) ) {
//and here
}
}
}
return result.stream()
.mapToInt(Integer::intValue)
.toArray();
}
The easiest way is to parse your numbers into strings and compare them. I did it this way :
public int[] findDuplicates(int[] a) {
List<Integer> result = new ArrayList<>();
boolean[] numbersThatHaveBeenAdded = new boolean[a.length];
for (int i = 0; i < a.length; i++) {
for (int j = i + 1; j < a.length; j++) {
String iNumber = String.valueOf(a[i]);
String jNumber = String.valueOf(a[j]);
if (iNumber.charAt(0) == jNumber.charAt(0)
&& iNumber.charAt(iNumber.length()-1) == jNumber.charAt(jNumber.length()-1)) {
if (!numbersThatHaveBeenAdded[i]) {
result.add(a[i]);
numbersThatHaveBeenAdded[i] = true;
}
if (!numbersThatHaveBeenAdded[j]) {
result.add(a[j]);
numbersThatHaveBeenAdded[j] = true;
}
}
}
}
return result.stream()
.mapToInt(Integer::intValue)
.toArray();
}
And I used a boolean array to keep in memory the numbers I already added in the result, to avoid duplication.
You can use the while loop (number /= 10) algorithm to get the first digit.
And number % 10 to get the last digit.
And then you can compare between them with if condition, if true you add that number to the list.
And do not forget the return type of that method it must be List
public List<Integer> findDuplicates(int[] array){
List<Integer> result = new ArrayList<>();
for (int i = 0; i < array.length; i++) {
int firstDigit = array[i];
int lastDigit = array[i] % 10;
while (firstDigit >= 10)
firstDigit /= 10;
if (firstDigit == lastDigit)
result.add(array[i]);
}
return result;
}
I prefer to do this using integers instead of strings.
You can do it pretty easily with math using a log function. Here, ^ is exponentiation. This uses the fact that for some number n, n = 10^y, where y = log10(n). Unless y is a power of 10, it will have a fractional part which should be ignored. So log10(12345) = 4.091491094267951. Ignoring the fraction and computing 10^4 == 10,000 which will yield 1 when dividing 12345. The right most digit is simply 12345 % 10 using the remainder operator.
int[] testData = {1,3,122,121,455,19202,20222, 29927, 291957, 49855, 291293};
int[] result = findDuplicates(testData);
System.out.println(Arrays.toString(result));
prints
[29927, 291957, 1, 121, 122, 19202, 455, 49855]
This method uses a Map<Integer, List<Integer>> to collect the values.
The key is the first and last digits combined firstDigit * 10 + lastDigit which is determined in the getFirstAndLast method.
Once the map is generated, the values are processed to return just the array of values which met the criteria. Since this works with a single iteration, no duplicates will occur unless they were included in the data.
public static int[] findDuplicates(int[] array) {
// collect into a map. Key = firstDigit * 10 + lastDigit
Map<Integer, List<Integer>> map = Arrays.stream(array).boxed()
.collect(Collectors.groupingBy(i->getFirstAndLast(i)));
// map.values() returns a `Collection of lists.
// The following transforms the those Collections to a flattened array
// of ints, ignoring lists that have a single, unique, value.
return map.values().stream().filter(list -> list.size() > 1)
.flatMap(List::stream).mapToInt(Integer::intValue).toArray();
}
The method takes the target and the digits argument.
The digits from the target are extracted and compared the supplied digits value by building the digits value from the first and last digits of the target.
public static int getFirstAndLast(int target) {
int divisor = (int) Math.pow(10, (int) Math.log10(target));
int firstDigit = target / divisor;
int lastDigit = target % 10;
return firstDigit * 10 + lastDigit;
}
Notes:
Arrays are cumbersome, especially when working between primitive arrays and arrays or Lists of objects. I recommend you use Collections where ever possible.
The two statements in the findDuplicates method could have been combined. However, I find doing so to be cluttered and of no performance benefit.

error when adding string value to array index [duplicate]

This question already has answers here:
How to reverse a number more efficiently with Java
(4 answers)
Closed 1 year ago.
public class aa {
public static void main(String[] args) {
int number = 521;
String temp1 = "" + number;
int result = 0;
int[] temp2 = new int[temp1.length()];
for(int i=0; i<temp1.length(); i++){
int len = temp1.length();
temp2[i] = temp1.charAt(len-i-1);
System.out.println(temp2[i]);
System.out.println(temp1.charAt(len-i-1));
}
}
}
This program should make 521 to 125 (reverse). But when I run this program, the result is
49
1
50
2
53
5
I think that string value is right, but when I add that string value to array, it goes wrong way. Can some one tell me what is wrong?
As I was saying in the comments, temp2 is the wrong type. You declared it as an array of ints, so println, of course, is treating it as an array of numbers, not of printable characters.
Just change temp2's type to char[]:
public class aa {
public static void main(String[] args) {
int number = 521;
String temp1 = "" + number;
int result = 0;
char[] temp2 = new char[temp1.length()];
for(int i=0; i<temp1.length(); i++){
int len = temp1.length();
temp2[i] = temp1.charAt(len-i-1);
System.out.println(temp2[i]);
System.out.println(temp1.charAt(len-i-1));
}
// print the whole reversed number on one line
for(char c : temp2) {
System.out.print(c);
}
System.out.println();
}
}
Output
1
1
2
2
5
5
125
Of course this is not the optimal solution, just a way to fix the code you wrote so that it works. See Alex Rudenko's comment for a link with better solutions to the problem of reversing the digits of a number.
As temp2 is an array of int values, the following assignment needs to be fixed:
temp2[i] = temp1.charAt(len-i-1);
because it assign an ASCII value of char.
This can be done by subtracting a 0 or by using Character::getNumericValue
temp2[i] = temp1.charAt(len-i-1) - '0';
// or
temp2[i] = Character.getNumericValue(temp1.charAt(len-i-1));
A non-negative integer number may be reversed without using any String conversion:
The "length" of temp2 array is defined by Math.log10
Each digit is calculated with modulo operator % and dividing the input while it is greater than 0
public static void main(String[] args) {
int number = 521;
int len = number < 2 ? 1 : (int) Math.ceil(Math.log10(number));
int[] temp2 = new int[len];
for (int i = 0, n = number; n > 0; n /= 10, i++) {
temp2[i] = n % 10;
System.out.println(temp2[i]);
}
for (int i : temp2) {
System.out.print(i);
}
System.out.println();
}
Output
1
2
5
125
You question is not very clear about if you are asking for opinion. But a query that "when I add that string value to array, it goes wrong way."
So , String is a reference type, while your array is of int type. You need to parse the value from String to int
Integer.parseInt(String)

Trying to create a array with the intersection of two arrays but fails at creating array with the proper structure

So, I am trying to create 2 randomly generated arrays,(a, and b, each with 10 unique whole numbers from 0 to 20), and then creating 2 arrays with the info of the last two. One containing the numbers that appear in both a and b, and another with the numbers that are unique to a and to b. The arrays must be listed in a "a -> [1, 2, 3,...]" format. At the moment I only know how to generate the 2 arrays, and am currently at the Intersection part. The problem is, that I can create a array with the correct list of numbers, but it will have the same length of the other two, and the spaces where it shouldn't have anything, it will be filled with 0s when its supposed to create a smaller array with only the right numbers.
package tps.tp1.pack2Arrays;
public class P02ArraysExtractUniqsAndReps {
public static void main(String[] args) {
int nbr = 10;
int min = 0;
int max = 20;
generateArray(nbr, min, max);
System.out.println();
}
public static int[] generateArray(int nbr, int min, int max) {
int[] a = new int[nbr];
int[] b = new int[nbr];
int[] s = new int[nbr];
s[0] = 0;
for (int i = 0; i < a.length; i++) {
a[i] = (int) (Math.random() * (max - min));
b[i] = (int) (Math.random() * (max - min));
for (int j = 0; j < i; j++) {
if (a[i] == a[j]) {
i--;
}
if (b[i] == b[j]) {
i--;
}
}
}
System.out.println("a - > " + Arrays.toString(a));
System.out.println("b - > " + Arrays.toString(b));
for (int k = 0; k < a.length; k++) {
for (int l = 0; l < b.length; l++) {
if (a[k] == b[l]) {
s[l] = b[l];
}else {
}
}
}
System.out.println("(a ∪ (b/(a ∩ b)) - > " + Arrays.toString(s));
return null;
}
public static boolean hasValue(int[] array, int value) {
for (int i = 0; i < array.length; i++) {
if (array[i] == value) {
return true;
}
}
return false;
}
}
Is there any way to create the array without the incorrect 0s? (I say incorrect because it is possible to have 0 in both a and b).
Any help/clarification is appreciated.
First, allocate an array large enough to hold the intersection. It needs to be no bigger that the smaller of the source arrays.
When you add a value to the intersection array, always add it starting at the beginning of the array. Use a counter to update the next position. This also allows the value 0 to be a valid value.
Then when finished. use Array.copyOf() to copy only the first part of the array to itself, thus removing the empty (unfilled 0 value) spaces. This works as follow assuming count is the index you have been using to add to the array: Assume count = 3
int[] inter = {1,2,3,0,0,0,0};
inter = Arrays.copyOf(inter, count);
System.out.println(Arrays.toString(inter);
prints
[1,2,3]
Here is an approach using a List
int[] b = {4,3,1,2,5,0,2};
int [] a = {3,5,2,3,7,8,2,0,9,10};
Add one of the arrays to the list.
List<Integer> list = new ArrayList<>();
for(int i : a) {
list.add(i);
}
Allocate the intersection array with count used as the next location. It doesn't matter which array's length you use.
int count = 0;
int [] intersection = new int[a.length];
Now simply iterate thru the other array.
if the list contains the value, add it to the intersection array.
then remove it from the list and increment count. NOTE - The removed value must be converted to an Integer object, otherwise, if a simple int value, it would be interpreted as an index and the value at that index would be removed and not the actual value itself (or an Exception might be thrown).
once finished the intersection array will have the values and probably unseen zeroes at the end.
for(int i = 0; i < b.length; i++) {
int val = b[i];
if (list.contains(val)) {
intersection[count++] = val;
list.remove(Integer.valueOf(val));
}
}
To shorten the array, use the copy method mentioned above.
intersection = Arrays.copyOf(intersection, count);
System.out.println(Arrays.toString(intersection));
prints
[3, 2, 5, 0, 2]
Note that it does not matter which array is which. If you reverse the arrays for a and b above, the same intersection will result, albeit in a different order.
The first thing I notice is that you are declaring your intersection array at the top of the method.
int[] s = new int[nbr];
You are declaring the same amount of space for the array regardless of the amount you actually use.
Method Arrays.toString(int []) will print any uninitialized slots in the array as "0"
There are several different approaches you can take here:
You can delay initializing the array until you have determined the size of the set you are dealing with.
You can transfer your content into another well sized array after figuring out your result set.
You could forego using Array.toString, and build the string up yourself.

Terminated due to timeout for my hacker rank solution

Hello all please check the problemHackerRank Problem Statement
This is my solution for the above problem(link)
static int migratoryBirds(List<Integer> arr) {
int ar[]=new int[arr.size()];
for(int i=0;i<arr.size();i++){
ar[i] = Collections.frequency(arr,arr.get(i));
// ar[i] = obj.occuranceOfElement(arr,arr.get(i));
}
int maxAt = 0;
for (int i = 0; i < ar.length; i++) {
maxAt = ar[i] > ar[maxAt] ? i : maxAt;
}
return arr.get(maxAt);
}
my code is unable to handle when the array size is bigger,example 17623 elements in array.
Terminated due to timeout
The problem is in the second for loop which iterates over the array and gives me the index of the largest number in the array.Is there any other way that I could increase the performance.
Your problem is in this part:
for(int i = 0; i < arr.size(); i++)
ar[i] = Collections.frequency(arr, arr.get(i));
This is O(N²): Collections.frequency() iterates over whole list to calculate frequency for only one element. Manually, you can iterate over the list to calculate frequencey for all elements.
Moreover, ther're only 5 birds, so you need only 5 length array.
static int migratoryBirds(int[] arr) {
int max = 1;
int[] freq = new int[6];
for (int val : arr)
freq[val]++;
for (int i = 2; i < freq.length; i++)
max = freq[i] > freq[max] ? i : max;
return max;
}
Your problem is the call to Colletions.frequency, which is an O(N) operation. When you call it from inside a loop it becomes O(N²) and that consumes all your time.
Also, are you sure which implmentation of List you receive? You call list.get(i) which might also be O(N) if the implementation is a LinkedList.
The target of this exercise is to calculate the frequency of each value in one pass over the input. You need a place where you store and increase the number of occurrences for each value and you need to store the largest value of the input.
You have also skipped over a crucial part of the specification. The input has limits which makes solving the problem easier than you now think.
Here's another one:
static int migratoryBirds(List<Integer> arr) {
int freq[]=new int[6];
for(int i=0;i<arr.size();i++){
++freq[arr.get(i)];
}
int maxAt = 1;
for (int i = 2; i < freq.length; i++) {
if (freq[i] > freq[maxAt]) {
maxAt = i;
}
}
return maxAt;
}
We can determine the type number of the most common bird in one loop. This has the time complexity O(n).
static int migratoryBirds(int[] arr) {
int highestFrequency = 0;
int highestFrequencyBirdType = 0;
int[] frequencies = new int[5]; // there are 5 bird types
for (int birdType : arr) {
int frequency = ++frequencies[birdType - 1];
if (frequency > highestFrequency) {
highestFrequency = frequency;
highestFrequencyBirdType = birdType;
} else if (frequency == highestFrequency && birdType < highestFrequencyBirdType) {
highestFrequencyBirdType = birdType;
}
}
return highestFrequencyBirdType;
}
For each element in the array arr we update the overall highestFrequency and are storing the corresponding value representing the highestFrequencyBirdType . If two different bird types have the highest frequency the lower type (with the smallest ID number) is set.

Shuffle a specific number of strings in a String Array in Java

I have a long textfile and I need to shuffle all the words which are longer than 4 letters but the other words need to remain in the same place. This without using the Collections module.
I can manage to shuffle all the strings in the array, but I cant figure out for the life of me how to shuffle only a part.
public static String[] getScramble(String text) {
Random rgen=new Random();
String[] textArray=text.split(" ");
for(int i=0;i<textArray.length;i++){
int randPos=rgen.nextInt(textArray.length);
String temp=textArray[i];
if (textArray[i].length()>4){
textArray[i]=textArray[randPos];
textArray[randPos]=temp;
}else{
textArray[i]=textArray[i];
}
}
return textArray;
Thanks!
You only swap two words if both are larger than 4 characters, otherwise you keep them in the original position:
for(int i = 0; i < textArray.length; i++) {
int randPos = rgen.nextInt(textArray.length);
if (textArray[i].length() > 4 && textArray[randPos].length() > 4){
String temp = textArray[i];
textArray[i]=textArray[randPos];
textArray[randPos]=temp;
}
}
EDIT: if the number of long words is very small compared to the number of short words, this loop may behave badly (since it will fail to swap most of the long words), so you can improve it as follows:
for(int i=0;i<textArray.length;i++){
if (textArray[i].length() > 4) {
// find a long word to swap textArray[i] with
int randPos=rgen.nextInt(textArray.length);
while (textArray[randPos].length() <= 4){
randPos=rgen.nextInt(textArray.length);
}
// swap the two long words
String temp=textArray[i];
textArray[i]=textArray[randPos];
textArray[randPos]=temp;
}
}
When generating the random position for a word that is longer than 4 characters, you check to see if the new position also has a word with more than 4 characters. If not, you keep generating a new random position until you find one that works.
public static String[] getScramble(String text) {
Random rgen = new Random();
String[] textArray = text.split(" ");
for (int i = 0; i < textArray.length; i++) {
if( textArray[i].length() > 4) {
String temp = textArray[i];
int randPos = rgen.nextInt(textArray.length);
while( textArray[randPos].length() <= 4 ){
randPos = rgen.nextInt(textArray.length);
}
textArray[i] = textArray[randPos];
textArray[randPos] = temp;
}
}
return textArray;
}
A situation that might arise is if you have only one word with more than 4 characters, which means it's pointless to even try to randomize them, and you might waste a lot of time generating random positions to no avail. To optimize this, you can first check if you have less than 2 long words, and if so you don't need to do anything.
int longWordCount = 0;
for (int i = 0; i < textArray.length; i++) {
if( textArray[i].length() > 4 )
longWordCount++;
if( longWordCount == 2 )
break;
}
if( longWordCount > 1 ) {
for (int i = 0; i < textArray.length; i++) {
if (textArray[i].length() > 4) {
String temp = textArray[i];
int randPos = rgen.nextInt(textArray.length);
while (textArray[randPos].length() <= 4) {
randPos = rgen.nextInt(textArray.length);
}
textArray[i] = textArray[randPos];
textArray[randPos] = temp;
}
}
}
Here's a version that adapts Durstenfeld's algorithm. Note that ThreadLocalRandom is preferable to Random.
public static String[] getScramble(String text) {
ThreadLocalRandom rgen = ThreadLocalRandom.current();
String[] textArray = text.split(" ");
int[] indices = IntStream.range(0, textArray.length)
.filter(i -> textArray[i].length() > 4)
.toArray();
for (int i = indices.length; i > 1; --i) {
int j = indices[rgen.nextInt(i)];
int k = indices[i - 1];
if (j != k) {
String tmp = textArray[j];
textArray[j] = textArray[k];
textArray[k] = tmp;
}
}
return textArray;
}
The OP didn't say that Streams couldn't be used, only Collections. If that's a problem, one can replace the initialization of indices with a simple loop to initialize an array of int of the same size as textArray, using the variable i to keep track of the number of indices entered (so it would be declared and initialized before the main for loop).

Categories