Given an array of positive and negative numbers(no zeroes), I have to arrange them in such a way that the positive and negative numbers should be arranged consecutively.
The number of positive and negative numbers may not be equal i.e. if there is no positive number(or negative) left, then all the remaining negative numbers(or positive) are appended to the end of the array.
The order is important i.e.if input array is {2,-1,-3,-7,-8, 9, 5,-5,-7},then the output array should be {2,-1, 9,-3, 5,-7,-8,-5,-7}. The code is done in O(n) without using another array.
Here is my solution in java, I test it again several case and it works. However, I'm not sure if this run in O(n) time. Basically, I count the number of positive and negative number first. then I have two index i = 0 and j =1. j is always 1 step ahead i. From there I keep checking if the number in i is positive and j is negative, if it isn't, i will iterate through the array until I find the next positive/negative for the correct position and move it to the correct position.
Any suggestion is much appreciated. Thanks!
//array of negative and positive, arrange them so that positive number follow by negative
// if no pos or neg left, the rest append to the array.
//should be O(n) no additional array
public static void alter(int[] a) {
int pos = 0;
int neg = 0;
int index = 0;
while (c < a.length) {
if (a[index] > 0) {
pos++;
} else neg++;
index++;
}
int i = 0;
int j = 1;
int temp = 0;
//run until no more positive number or negative number
while (pos > 0 && neg > 0) {
//
if (a[i] > 0) {
pos--;
if (a[j] < 0) {
i += 2;
j += 2;
neg--;
} else // a[j] > 0
{
while (a[j] > 0) {
j++;
}
//a[j] < 0
neg--;
//move that number to the appropriate place
while (j > i) {
temp = a[j];
a[j] = a[j - 1];
a[j - 1] = temp;
j--;
} // end while
i += 2;
j += 2;
}
} else // a[i] < 0
{
while (a[i] < 0) {
i++;
}
//a[i] > 0
//move that number to the appropriate place
while (i > (j - 1)) {
temp = a[i];
a[i] = a[i - 1];
a[i - 1] = temp;
i--;
}
} //end else
}
}
However, I'm not sure if this run in O(n) time
I do not think it runs in O(n). The moment you have to "find the next correct element and move it to the correct location", you need to
Loop over the remainder of the array. Which means that for the worst case scenario (array with all positive elements at the start, and all negative elements at the end) you will loop for each of the positive elements one more time over half of the "unsorted" part of the array
When you move the element back to the correct location, you move it position by position. This means you loop once more over the "unsorted" part of the array
Not really sure yet how you would get it running in O(n) if you need to respect the requirement that the ordering must be respected without using a third array.
Yes, it can be done in O(n).
Lets assume that c is current position. And a[c] is positive number.
1) Increment i from c towards end of array until i points to first wrong number (number that have the same sign as previous, in this case positive).
2) Set j := i;
3) Increment j towards end of array until j points to number with wanted sign (in this case - negative).
4) Swap a[i] with a[j].
5) Set c := j;
6) Set j := c + 1;
In each step you always increment i or j or c. Never decrement.
Variable i can be incremented no more than n times. Same to j and c.
So maximum number of increments is 3n ~ O(n).
PFB my code. If we will use Stack then it will make our problem more easier.
public class AlternatePosNeg {
public static void main(String[] args) {
int arr[] = { 2, -1, -3, -7, -8, 9, 5, -5, -7 };
Stack<Integer> pos = new Stack<>();
Stack<Integer> neg = new Stack<>();
int i;
for (i = 0; i < arr.length; i++) {
if (arr[i] > 0) {
pos.push(arr[i]);
} else {
neg.push(arr[i]);
}
}
int tempArr[] = new int[arr.length];
i = 0;
int sizePos = pos.size();
int sizeNeg = neg.size();
while (i < tempArr.length) {
if (sizePos > sizeNeg) {
if (pos.size() > 0) {
tempArr[i] = pos.pop();
}
if (neg.size() > 0) {
tempArr[i + 1] = neg.pop();
i++;
}
} else {
if (neg.size() > 0) {
tempArr[i] = neg.pop();
}
if (pos.size() > 0) {
tempArr[i + 1] = pos.pop();
i++;
}
}
i++;
}
for (int no : tempArr) {
System.out.print(no + " ");
}
}
}
Related
Given an array of integers I need to find the maximum quantity of non zero elements between two zero elements
For example: int[] arr = {1,2,0,2,3,4,5,0,1,2,3,4,5,6,7,8,8,0,1}
This should return 9. However, this returns 4:
static int solution(int[] arr) {
int count = 0;
int maxCount = 0;
for (int i = 0; i < arr.length - 1; i++) {
if (arr[i] == 0 && i < arr.length - 2) {
i++;
while (arr[i] != 0) {
count++;
i++;
}
maxCount = count;
}
}
return maxCount;
}
UPD: For the case of {1,2,3,0,0,3,2,1}, function should return 0
Some issues:
The inner loop may run i out of the array's range.
When you find the ending zero, your code does not take into account that this zero is also the start of a new group, and "misses" it.
count is never reset to zero, so it keeps increasing also when you get into a second "group"
maxCount is set unconditionally, yet it should only be updated if the new count is greater than the count you already got.
There is no provision for when there is no group at all. I would suggest to return -1 in that case, so to differentiate it from the case where there is a group of length 0. This -1 should then be the initial value of the counts.
Also:
It is strange that the outer loop condition is made more strict by the if condition at the top of your loop. So why not make that i< arr.length-2 the loop condition?
Here is a correction of your attempt:
static int solution(int[] arr) {
int count = -1; // Start with invalid count
int maxCount = -1;
for (int i = 0; i < arr.length - 2; i++) { // Stricter loop condition
if (arr[i] == 0) {
count = 0; // Reset counter
// Safety & Look ahead for zero
while (i + 1 < arr.length && arr[i + 1] != 0) {
count++;
i++;
}
if (i + 1 >= arr.length) break; // No ending zero found
// Only update if improvement
if (count > maxCount) maxCount = count;
}
}
return maxCount;
}
You can however reduce the code a bit by using the outer loop to do the job of the inner loop:
static int solution(int[] arr) {
// Start with a count that can safely increment with `++` without getting positive
int count = -arr.length;
int maxCount = -1;
for (int i = 0; i < arr.length; i++) {
if (arr[i] == 0) {
if (count > maxCount) maxCount = count;
count = 0; // Start of a potential new group
} else {
count++;
}
}
return maxCount;
}
And instead of maintaining count you could maintain the last starting index of a group:
static int solution(int[] arr) {
int start = arr.length; // Invalid start index
int maxCount = -1;
for (int i = 0; i < arr.length; i++) {
if (arr[i] == 0) {
if (i - start > maxCount) maxCount = i - start;
start = i + 1;
}
}
}
This problem can be solved in a single pass. Observe the counts of non-zero elements when progressing from left to right:
1,2,0,2,3,4,5,0,1,2,3,4,5,6,7,8,8,0,1
- - 0 1 2 3 4 0 1 2 3 4 5 6 7 8 9 0 1
Now consider the partial solution for the left subarray processed
1,2,0,2,3,4,5,0,1,2,3,4,5,6,7,8,8,0,1
- - 0 1 2 3 4 0 1 2 3 4 5 6 7 8 9 0 1
0 0 0 0 0 0 0 4 4 4 4 4 4 4 4 4 4 9 9
This tells you how to solve:
keep a counter of the number of non-zero elements since the previous zero. (Increment on nonzero, reset to zero on zero.)
keep the "answer" updated: every time you see a zero, keep the largest of the counter and the current answer, initially zero.
For the initial elements, it suffices to keep the count negative so that it does not influence the answer. A safe option is to initialize with minus the length of the list.
Technically, the loop is based on an invariant expressing that the counter holds the number of numbers since the previous zero (or a negative value), while answer holds the correct answer for the subarray traversed so far.
counter= - length(list)
answer= 0
for element in list:
if element == 0:
answer= max(counter, answer)
counter= 0
else:
counter= counter + 1
This will fix your all scenario,
Time complexity is O(n)
public static int maxCountBwZero(int arr[]) {
int i = 0;
int tempCount = 0;
int size = arr.length;
int maxCount = 0;
boolean startCount = false;
while (i < size) {
if (arr[i] == 0) {
if (maxCount == 0 && tempCount == 0) {
startCount = true;
} else {
if (tempCount > 0 && (tempCount >= maxCount)) {
maxCount = tempCount;
}
tempCount = 0;
}
} else if (startCount) {
tempCount++;
}
i++;
}
return maxCount;
}
public static void main(String[] args) {
int[] arr = { 1, 2, 0, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 0, 1 };
int[] arr1 = { 1, 2, 3, 0, 0, 3, 2, 1 };
System.out.println(maxCountBwZero(arr));
System.out.println(maxCountBwZero(arr1));
}
So I was trying to solve the LeetCode question n.1838 (Frequency of the most frequent element) and it works except for a test case with an input array of 500000 elements (1 <= nums.length <= 10^5): it gives me TimeLimitExceeded and I don't understand why.
I tried to explain my approach with comments in the code, but it's very easy: I sort the array first so I can start from the last element (the bigger one), I loop the array and for each element in the array i calculate the frequency for different cases:
if nums[i] == nums[i - 1] increment the frequency
if k < nums[i] - nums[i - 1] there's no need to do anything, so i just update j to get outside of the loop
else I add to nums[i - 1] the difference so it can be nums[i], I update q and num.
I then update the max frequency and once I'm outside the while loop I do the same for each element of the array.
Here's my method:
public int maxFrequency(int[] nums, int k) {
//corner case
if (nums.length == 1)
return 1;
//order array
Arrays.sort(nums);
int max = 0;
int freq = 0;
int num = 0; //will always be the int at the index - j (j will be incremented each time)
int j = 0;
//start from the last element of the array
for (int i = nums.length - 1; i >= 0; i--) {
num = nums[i - j];
int q = k;
while (q >= 0 && i - j >= 0) {
//if element equals itself, increment frequency
if (nums[i] - num == 0) {
freq++;
j++;
} // if k < nums[i] - num (aka even if we add k to num we won't have a new nums[i])
else if (nums[i] - num > q) {
j = i + 1; //to get out of the while loop
} else { //case where i can add k because k >= nums[i] - num
q -= nums[i] - num;
freq++;
j++;
} // update num only if i - j > 0 so we dont have ArrayOutOfBoundException
if (i - j >= 0) {
num = nums[i - j];
} //update max frequency
max = Math.max(freq, max);
}
freq = 0;
j = 0;
}
return max;
}
Any advice or hint would be helpful, thanks in advance.
quantity of positive and negative numbers in array must be equal.
an array mustn't contain zero.
The main issue is that array is not always consist of 50/50 of percent of positive and negative numbers.
If you can show me much simply way to code it you are welcome!
public static void main(String[] args) throws IOException {
int array[] = new int[12];
int positiveCounter = 0;
int negativeCounter = 0;
for (int i = 0; i < array.length; i++) {
array[i] = createRandom();
while (array[i] == 0) {
array[i] = createRandom();
}
if (positiveCounter > array.length / 2) {
array[i] = -1 * array[i];
positiveCounter--;
negativeCounter++;
}
if (negativeCounter > array.length / 2) {
array[i] = -1 * (-array[i]);
negativeCounter--;
positiveCounter++;
}
if (array[i] > 0) {
positiveCounter++;
}
if (array[i] < 0) {
negativeCounter++;
}
}
System.out.println(Arrays.toString(array));
System.out.println("Pos: " + positiveCounter);
System.out.println("Neg: " + negativeCounter);
}
static int createRandom() {
Random random = new Random();
int x = -10 + random.nextInt(11 - (-10));
return x;
}
The comments may lead to a very compact way to achieve your goal, but for what it is worth I'd like to explain why your code is not working as expected, and show a way that it will.
You attempt to invert the sign to keep the amount of positive and negative numbers balanced. This will work, but you have a flaw in your code. You must only invert the number if the current number needs to be inverted. If you look at this code you will see it always inverts and adjusts the counters even if array[i] is negative (in which case nothing needs to be done):
if (positiveCounter > array.length / 2) {
array[i] = -1 * array[i]; // <- This is a problem
positiveCounter--;
negativeCounter++;
}
To follow should fix your issue; the inversion is only done as needed:
for (int i = 0; i < array.length; i++) {
array[i] = createRandom();
while (array[i] == 0) {
array[i] = createRandom();
}
if (positiveCounter >= array.length / 2 && array[i] > 0) {
array[i] = array[i] * -1; // Force the number to be negative
}
if (negativeCounter >= array.length / 2 && array[i] < 0) {
array[i] = array[i] * -1; // Force the number to be positive
}
if (array[i] > 0) {
positiveCounter++;
}
if (array[i] < 0) {
negativeCounter++;
}
}
Here is what I have so far, and I am usually pretty good at tracing programs but i just can't figure out where i got stuck. It's supposed to get "N" and Mod it by D, which equals N-1, while D is greater than 1. And once it is done, it goes on the one number less than the original one, and does the same thing while N is greater than 2. And as for the "Count", i just added it so that I could check if the number was prime. Ex: if the count = N-2, which is basically every number from 2 to N-1, then that number is prime.
public class Challenge14 {
public void inti() {
int count = 0;
int N = 7;
int D = N - 1;
int A = 0;
while (N > 2) {
D = N - 1;
while (D > 1) {
A = N % D;
D--;
if (A == 0) {
break;
}
else {
count++;
}
}
if (count == N - 2) {
System.out.println(N);
}
N--;
}
}
}
Use a for loop.
for (int i = 1; i<N; i++) {
System.out.println(i);
}
Taken from Introduction to Algorithms
Describe a Θ(n lg n)-time algorithm
that, given a set S of n integers and
another integer x, determines whether
or not there exist two elements in S
whose sum is exactly x.
This is my best solution implemented in Java so far:
public static boolean test(int[] a, int val) {
mergeSort(a);
for (int i = 0; i < a.length - 1; ++i) {
int diff = (val >= a[i]) ? val - a[i] : a[i] - val;
if (Arrays.binarySearch(a, i, a.length, diff) >= 0) {
return true;
}
}
return false;
}
Now my 1st question is: Is this a correct solution? From my understanding, mergeSort should perform the sort in O(n lg n), the loop should take O(n lg n) (n for the iteration multiplied by O(lg n) for the binary search, resulting in O(2n lg n), so it should be correct.
My 2nd question is: Are there any better solutions? Is sorting the array essential?
Your solution seems fine. Yes you need to sort because its a pre requisite for binary search. You can make a slight modification to your logic as follows:
public static boolean test(int[] a, int val)
{
Arrays.sort(a);
int i = 0; // index of first element.
int j = a.length - 1; // index of last element.
while(i<j)
{
// check if the sum of elements at index i and j equals val, if yes we are done.
if(a[i]+a[j] == val)
return true;
// else if sum if more than val, decrease the sum.
else if(a[i]+a[j] > val)
j--;
// else if sum is less than val, increase the sum.
else
i++;
}
// failed to find any such pair..return false.
return false;
}
There's another very fast solution: Imagine you have to solve this problem in Java for about 1 billions integers. You know that in Java integers go from -2**31+1 to +2**31.
Create an array with 2**32 billion bit (500 MB, trivial to do on today's hardware).
Iterate over your set: if you have an integer, set corresponding bit to 1.
O(n) so far.
Iterate again over your set: for each value, check if you have a bit set at "current val - x".
If you have one, you return true.
Granted, it needs 500 MB of memory.
But this shall run around any other O(n log n) solution if you have, say, to solve that problem with 1 billion integers.
O(n).
This is correct; your algorithm will run in O(n lg n) time.
There is a better solution: your logic for calculating diff is incorrect. Regardless of whether a[i] is greater than or less than val, you still need diff to be val - a[i].
Here's an O(n) solution using a hash-set:
public static boolean test(int[] a, int val) {
Set<Integer> set = new HashSet<Integer>();
// Look for val/2 in the array
int c = 0;
for(int n : a) {
if(n*2 == val)
++c
}
if(c >= 2)
return true; // Yes! - Found more than one
// Now look pairs not including val/2
set.addAll(Arrays.asList(a));
for (int n : a) {
if(n*2 == val)
continue;
if(set.contains(val - n))
return true;
}
return false;
}
I do think I have spotted a minor bug in your implementation, but testing should uncover that one quickly.
The approach looks valid, and will reach the desired performance. You might simplify it by replacing the iterative binary search with a scan through the array, in effect replacing the binary search by a linear search that resumes where the previous linear search left off:
int j = a.length - 1;
for (int i = 0; i < a.length; i++) {
while (a[i] + a[j] > val) {
j--;
}
if (a[i] + a[j] == val) {
// heureka!
}
}
This step is O(n). (Proving that is left as an exercise for you.) Of course, the entire algorithm still takes O(n log n) for the merge sort.
A simple solution is, after sorting, move pointers down from both ends of the array, looking for pairs that sum to x. If the sum is too high, decrement the right pointer. If too low, increment the left one. If the pointers cross, the answer is no.
Your analysis is correct, and yes you must sort the array or else binary search does not work.
Here's is an alternate solution, by adding few more conditions into mergesort.
public static void divide(int array[], int start, int end, int sum) {
if (array.length < 2 || (start >= end)) {
return;
}
int mid = (start + end) >> 1; //[p+r/2]
//divide
if (start < end) {
divide(array, start, mid, sum);
divide(array, mid + 1, end, sum);
checkSum(array, start, mid, end, sum);
}
}
private static void checkSum(int[] array, int str, int mid, int end, int sum) {
int lsize = mid - str + 1;
int rsize = end - mid;
int[] l = new int[lsize]; //init
int[] r = new int[rsize]; //init
//copy L
for (int i = str; i <= mid; ++i) {
l[i-str] = array[i];
}
//copy R
for (int j = mid + 1; j <= end; ++j) {
r[j - mid - 1] = array[j];
}
//SORT MERGE
int i = 0, j = 0, k=str;
while ((i < l.length) && (j < r.length) && (k <= end)) {
//sum-x-in-Set modification
if(sum == l[i] + r[j]){
System.out.println("THE SUM CAN BE OBTAINED with the values" + l[i] + " " + r[j]);
}
if (l[i] < r[j]) {
array[k++] = l[i++];
} else {
array[k++] = r[j++];
}
}
//left over
while (i < l.length && k <= end) {
array[k++] = l[i++];
//sum-x-in-Set modification
for(int x=i+1; x < l.length; ++x){
if(sum == l[i] + l[x]){
System.out.println("THE SUM CAN BE OBTAINED with the values" + l[i] + " " + l[x]);
}
}
}
while (j < r.length && k <= end) {
array[k++] = r[j++];
//sum-x-in-Set modification
for(int x=j+1; x < r.length; ++x){
if(sum == r[j] + r[x]){
System.out.println("THE SUM CAN BE OBTAINED with the values" + r[j] + " " + r[x]);
}
}
}
}
But the complexity of this algorithm is still not equal to THETA(nlogn)