time complexity - Binary Indexed Tree - java

I am writing a binary indexed tree. As documentation, it requires nlogn time to pre process. But I am not able to understand why.
In my case I am constructing the tree from the Array, which should take 2n time, as first time traversing the array once to make it a Binary tree and then to update sum I am again traversing the tree in POST order fashion. so total 2n, not nlogn.
Can anybody explain why it needs nlogn time to pre-process the binary indexed tree.
public class BITree {
private class BTN {
int data;
int index;
BTN left,right;
public BTN(int data) {
this.data = data;
}
}
BTN head = null;
public BTN toBT(int[] arr,int start,int end){
if(start <= end){
int mid = start + (end - start)/2;
BTN btn = new BTN(arr[mid]);
btn.index = mid+1;
btn.left = toBT(arr,start,mid-1);
btn.right = toBT(arr,mid+1,end);
return btn;
}
return null;
}
public int sumAtIndex(BTN btn,int index){
int sum = 0;
if(index < btn.index)
sum += sumAtIndex(btn.left,index);
else if(index > btn.index) {
sum += btn.data + sumAtIndex(btn.right, index);
}
if(btn.index == index)
return btn.data + sum;
return sum;
}
public int replaceSum(BTN btn){
if(btn == null){
return 0;
}
int l = replaceSum(btn.left);
int r = replaceSum(btn.right);
int sum = btn.data + l + r;
btn.data += l;
return sum;
}
void inOrder(BTN btn){
if(btn != null) {
inOrder(btn.left);
System.out.print((btn.index+":"+btn.data)+",");
inOrder(btn.right);
}
}
public static void main(String[] args) {
int[] arr = {5,1,6,4,2,3,3};
BITree s2 = new BITree();
BTN btn = s2.toBT(arr,0,arr.length-1);
s2.replaceSum(btn);
s2.inOrder(btn);
System.out.println();
System.out.println(s2.sumAtIndex(btn,3));
}
}

This question is a duplicate of :Is it possible to build a Fenwick tree in O(n)?
#Thilo , Thanks for pointing out the optimized way for preprocessing the the BIT. Which can be done in O(n) time.
https://en.wikipedia.org/wiki/Talk:Fenwick_tree
https://stackoverflow.com/a/31070683/3080158
#SanketMakani, Thanks for sharing the link, it explains the BIT very well.
here is the working code, with O(n) pre processing time.
package com.rabin;
import java.util.StringJoiner;
/**
*
*/
public class BITree {
/**
* O(logn)
* #param arr
* #param index
* #param val
*/
void update(int arr[],int index, int val)
{
index++;
for(; index <= arr.length-1; index += index&-index)
arr[index] += val;
}
/**
* O(logn)
* #param arr
* #param noOfElements
* #return
*/
int query(int[] arr,int noOfElements)
{
int sum = 0;
for(; noOfElements > 0; noOfElements -= noOfElements&-noOfElements)
sum += arr[noOfElements-1];
return sum;
}
/**
* O(n)
* #param arr
*/
void toBIT(int[] arr){
int n = arr.length;
for(int i=1;i<=n;i++){
int j = i+ (i & -i);
if(j <= n)
arr[j-1] += arr[i-1];
}
}
static String arrayToString(int[] arr){
StringJoiner sj = new StringJoiner(",","[","]");
for(int i = 0; i< arr.length ;i++){
sj.add(String.valueOf(arr[i]));
}
return sj.toString();
}
public static void main(String[] args) {
int[] arr = {5,1,6,4,2,3,3};
BITree bit = new BITree();
System.out.println("Original Array:" +arrayToString(arr));
bit.toBIT(arr);
System.out.println("BIT Array:" +arrayToString(arr));
System.out.println("Sum of first 5 nos : "+ bit.query(arr,5));
bit.update(arr,0,8);
System.out.println("Sum of first 5 nos after update : "+ bit.query(arr,5));
}
}

#RBanerjee nicely written code, It is good to implement the BIT with one additional index, which helps in code comprehension. Plus it also signifies one additional thing - the least significant 1 bit from the BIT index indicates as to how many elements does the particular index stores. For e.g. index = 2 (010) can signify index 2 in BIT holds the values of 2 elements, similarly 4 (100) for 4, 6 (110) stores 2 values (namely, index 5 and 6) and so on.
Additionally, in your update method you aren't updating the value per se. You are adding the given value. Which I do not think signifies the meaning of update. It is a very subjective discussion, but I think of it as an update and not an increment. So, if the index 5 originally holds value 2, and when I want to update it to -1, it means the value after the update at index 5 is -1 and not 1.
As an extra step, it is good to provide a way to query the ranges in the array. For e.g. what is the value between indices 2 and 5 (inclusive).
<!-- language: java -->
package DataStructureImplementation;
import java.util.StringJoiner;
public class BinaryIndexedTree {
private final int[] bit;
private final int[] nums;
private final int n;
public BinaryIndexedTree(int[] nums) {
n = nums.length;
bit = new int[n + 1];
this.nums = nums;
System.arraycopy(nums, 0, bit, 1, nums.length);
build();
}
/**
* Builds a binary indexed tree in O(n) time.
*/
private void build() {
int j;
for (int i = 1; i <= n; ++i) {
j = i + (i & -i);
if (j <= n) bit[j] += bit[i];
}
}
/**
* Updates an indexed item in the original array to the given value.
* Also updates the values in the 'BIT' in O(logn) time.
* #param index - index of the item to update
* #param value - value to update to
*/
public void update(int index, int value) {
int diff = value - nums[index];
nums[index] = value;
index++;
while (index <= n) {
bit[index] += diff;
index += (index & -index);
}
}
/**
* Queries the sum of the first 'K' indices in the original array in O(logn) time.
* #param k - the number of items to aggregate.
* #return - the sum of first 'K' numbers in the original array.
* #throws Exception - if 'K' is out of bounds.
*/
public int query(int k) throws Exception {
if (k < 0 || k > n) throw new Exception("Invalid query range : " + k);
int sum = 0;
while (k > 0) {
sum += bit[k];
k -= (k & -k);
}
return sum;
}
/**
* Queries the sum of numbers from the original array between index1 and index2 (inclusive) in O(logn) time.
* #param index1 - left index.
* #param index2 - right index.
* #return - the sum of numbers between the given ranges.
* #throws Exception - if range is out of bounds.
*/
public int queryRange(int index1, int index2) throws Exception {
return query(index2 + 1) - query(index1);
}
/**
* Helper method to print the array contents.
* #param nums - the array to print.
* #return - the contents of the array as string.
*/
static String arrayToString(int[] nums){
StringJoiner stringJoiner = new StringJoiner(",","[","]");
for (int n : nums) {
stringJoiner.add(String.valueOf(n));
}
return stringJoiner.toString();
}
public static void main(String[] args) throws Exception {
int[] nums = {5,8,5,4,2,3};
BinaryIndexedTree binaryIndexedTree = new BinaryIndexedTree(nums);
System.out.println("Original Array : " + arrayToString(nums));
System.out.println("BIT Array : " + arrayToString(binaryIndexedTree.bit));
System.out.println("Sum of first 5 nos : " + binaryIndexedTree.query(5));
binaryIndexedTree.update(4,-1);
System.out.println("Original Array after update : " + arrayToString(nums));
System.out.println("BIT Array after update : " + arrayToString(binaryIndexedTree.bit));
System.out.println("Sum of first 5 nos after update : " + binaryIndexedTree.query(5));
System.out.println("Sum of numbers in range 2-5 : " + binaryIndexedTree.queryRange(2, 5));
}
}

Related

Recursion Problem - given array n and a number k

Given an array size n, and a positive number max(max represent the range of the numbers that we can use to place in the array).
I would like to count how many combinations of sorted numbers I can place in the array.
For example :
If n = 3, max = 2.(the only numbers we can use is 1/2 as max is 2) so there are 4 combinations of sorted arrays
1. {1,1,1}
2. {1,1,2}
3. {1,2,2}
4. {2,2,2}
I wrote some code and succeed to pass this specific example but any other example that max > 2 doesn't return the correct answer.
the problem as I identify it is when the recursion reaches the last index it doesn't try a third number it just folds back.
my code :
private static int howManySorted(int n, int max, int index, int numToMax, int prevNum) {
// If the value is bigger then max return 0
if(numToMax > max) {
return 0;
}
if (numToMax < prevNum) {
return 0;
}
//If Index reached the end of the array return 1
if(index == n) {
return 1;
}
int sortTwo = howManySorted(n, max, index+1, numToMax, numToMax);
int sortOne = howManySorted(n, max, index+1, numToMax+1, numToMax);
return ((sortOne+sortTwo));
}
public static int howManySorted(int n, int max) {
return howManySorted(n, max, 0, 1, 0);
}
start with "{1," and add elements "{1,1" and/or value "{2," with each recursion. when it reach n elements array we add to the counter. n is the number of elements in the array max is the maximal value for each element. minimal is 1. element is the current cell in the array being manipulated. we start with 1 (in actual array means 0). value is the current value of the current element. we start with 1.
// external function according to the given question
public static int count (int n, int max)
{
return count(n,max, 1, 1);
}
private static int count (int n, int max, int element, int value)
{
int counter = 0;
// only if our array reached n elements we count the comination
if (element == n)
counter++;
else // we need to continue to the next element with the same value
counter += count(n, max, element +1, value);
if (value < max) // if our current element didn't reach max value
counter += count (n, max, element, value+1);
return counter;
}
I think you would need to change your two recursive calls (this is why it only reaches value 2) and do as many calls as your max parameter:
private static int howManySorted(int n, int max, int index, int numToMax, int prevNum) {
// If the value is bigger then max return 0
if (numToMax > max) {
return 0;
}
if (numToMax < prevNum) {
return 0;
}
//If Index reached the end of the array return 1
if (index == n) {
return 1;
}
int result = 0;
for (int i = 0; i < max; i++)
result += howManySorted(n, max, index + 1, numToMax + i, numToMax);
return result;
}
I believe you can simplify your answer to something like this
private static long howManySorted(int length, int min, int max) {
if (length == 1) {
return max - min + 1;
}
// if (min == max) {
// return 1;
// }
long result = 0;
for (int i = min; i <= max; i++) {
result += howManySorted(length - 1, i, max);
}
return result;
}
public static long howManySorted(int length, int max) {
if ((length < 1) || (max < 1)) {
throw new IllegalArgumentException();
}
return howManySorted(length, 1, max);
}
Client should call the public method.
So as you can see terminate conditions are when remaining length is 1, or min reaches max. Even removing the second terminate condition doesn't change the result, but can improve the performance and number of recursions.
Just test my code, I think it figures out your problem:
class Test {
private static int howManySorted(int n, int max) {
//Better time complexity if u use dynamic programming rather than recursion.
if (n == 0) return 1;
int res = 0; // "res" can be a very large.
for (int i = n; i >= 1; i--) {
for (int j = max; j >= 1;j--) {
res += howManySorted(i-1, j-1);
}
}
return res;
}
public static void main(String[] args) {
System.out.println(howManySorted(3, 2));
}
}
This code will run faster if you use dynamic programming and be careful about the answer, it could be a very large integer.
You guys are forgetting he needs a solution using only recursion.
Probably a Java assignment for a CS class.
I also had that question.
This is the answer I came up with:
/**
* #param n Number of values in the array
* #param max Maximum value of each cell in the array
* #return int
*/
public static int howManySorted(int n, int max) {
return howManySorted(max, max, 1, n - 1);
}
/**
*
* #param value The current value
* #param max The maximum possible value (not allowed to use global parameters, so the parameter value always stays the same)
* #param min The minimum value allowed in this index. Determined by the value of the previous index (when first called, use value 1)
* #param index The index of the value being manipulated
* #return
*/
public static int howManySorted(int value, int max, int min, int index) {
//If any of these cases are found true, it means this value is invalid, don't count it
if (index < 0 || value < min) {
return 0;
}
//First lower the value in the same index, the result is the number of valid values from that branch
int lowerValue = howManySorted(value - 1, max, min, index);
//Now check all the valid values from the next index - value is max-1 to prevent counting twice some numbers
int lowerIndex = howManySorted(max - 1, max, value, index - 1);
//Return 1 (this number we are at right now) + all of its children
return 1 + lowerValue + lowerIndex;
}
I treated each series (e.g. '1,1,2') as an array, so at the beginning I wrote something like that:
public static void main(String[] args)
{
System.out.println(howManySorted(3, 2, 1, "")); // 4
System.out.println(howManySorted(2, 3, 1, "")); // 6
}
private static int howManySorted(int n, int max, int index, String builder)
{
if (max == 0) // if we exceeds max, return 0.
return 0;
if (n == 0) // num represents how many individual numbers we can have, if it is zero it means we have the required length (e.g. if n = 3, we have 'x1,x2,x3').
{
System.out.println(builder.substring(0, builder.length() - 2));
return 1;
}
int r1 = howManySorted(n - 1, max, index, builder + index + ", "); // i added additional var 'index' to represent each number in the list: (1,1,1)
int r2 = howManySorted(n, max - 1, index + 1, builder); // I'm increasing the index and decreasing the max (1,1,**2**)
return r1 + r2;
}
But eventually, we don't need the 'index' nor the 'builder', they were just to emphasize how I solved it...
public static void main(String[] args)
{
int max = 2, n = 3;
System.out.println(howManySorted(n, max)); // 4
int max1 = 3, n1 = 2;
System.out.println(howManySorted(n1, max1)); // 6
}
public static int howManySorted(int n, int max)
{
if (max == 0) // if we exceeds max, return 0.
return 0;
if (n == 0) // like we said, num represents how many individual numbers we can have, if it is zero it means we have the required length (e.g. if n = 3, we have 'x1,x2,x3').
return 1;
int r1 = howManySorted(n - 1, max);
int r2 = howManySorted(n, max - 1);
return r1 + r2;
}

binary search keeps returning element not present //

I tried to create the algorithm as practice, but I'm not too sure as for where I blundered but I keep getting "Element not present" even if the array is sorted.//
I'm trying to create random arrays and sort it at the same time. Meanwhile, I want to see how many comparisons it takes, and also measure how much time it takes for this program to run in (ms).
Some problems at the moment:
Keeps printing: Element not present, and time as 0. I know binary is time efficient, but I'm kind of sketchy with this being at 0/ or at 1 to 2 in many cases when the x is at 5000 or a million to 20 million. I think that should take some time still.
//main
import java.util.Arrays;
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
/**
*
* #author temur
*/
public class main {
public static int[] randomIntArray(int n, int low, int high) {
// Set up for generating random numbers
int range = high - low + 1;
int shift = low;
int[] arr = new int[n];
for (int i = 0; i < arr.length; i++) {
arr[i] = (int) (Math.random() * range) + shift;
}
return arr;
}
public static boolean isSorted(int[] arr) {
for (int i = 0; i < arr.length - 1; i++) {
if (arr[i] > arr[i + 1]) {
return false;
}
}
return true;
}
public static void swap(int[] arr, int i, int j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
public static void print(int[] arr) {
System.out.println(java.util.Arrays.toString(arr));
}
//____________________________________________________________
public static void main(String[] args) {
binary ob = new binary();
int[] arr = randomIntArray(1, 20000000, 20000000);
Arrays.sort(arr);
int x = 55432;
int n = 1;
long startTime = System.currentTimeMillis();
int result = ob.binarySearch(arr, 0, n - 1, x);
System.out.println(
"Element in evaluation: " + x);
if (result
== -1) {
System.out.println("Element not present");
} else {
System.out.println("Element found at index: "
+ result);
}
System.out.println(
"Ammount of comparisons: " + ob.count);
long endTime = System.currentTimeMillis();
System.out.println("Time (ms):"+ (endTime - startTime));
}
}
//binary
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
/**
*
* #author temur
*/
public class binary {
int count = 0;
int binarySearch(int arr[], int l, int r, int x) {
count += 1;
if (r >= l) {
int mid = l + (r - l) / 2;
if (arr[mid] == x) {
return mid;
}
if (arr[mid] > x) {
return binarySearch(arr, l, mid - 1, x);
}
return binarySearch(arr, mid + 1, r, x);
}
return -1;
}
}
You are making an array 1 element long, with elements randomly generated between 20000000 and 20000000. In other words, it's always {20000000}. Of course 55432 isn't going to be in that array, and the binary search is going to figure that out pretty fast.
It is simple that your randomIntArray with values you pass is going to give single length array.
Run Code:
int[] arr = randomIntArray(1, 20000000, 20000000);
System.out.println("Array "+Arrays.toString(arr));
Output:
Array [20000000]
so the value 55432 is not present in this array and that is a very fast to figure it out.

int array toString method that displays the values with spaces in between and a new line every ten values

I am a beginner when it comes to java and am having some issues(been sitting in front of this all day). I have searched far and wide for a solution, yet to no avail. My professor has asked me to fill out the blank methods, and I am especially having trouble with the toString method(although I am not feeling to good about the rest either), any help would be appreciated.
import java.util.Random;
public class SmartArray {
// declare an array of ints
int[] list;
Random r = new Random();
int pos = 0;
/**
* Creates and initializes an array of size n. It does not initialize the
* contents of the array.
*/
public SmartArray(int n) {
list = new int[n];
}
/**
*
* Initializes the contents of the array with random
*
* non-negative* numbers.
*
*/
public void initRandom() {
int i = 0;
int hold = 0;
while (i < list.length) {
hold = r.nextInt();
if (hold % 2 == 0) {
list[i] = hold;
i++;
} else {
hold = hold + 1;
list[i] = hold;
i++;
}
}
}
/**
*
* Allows client code to add an element to the array
*
* at the given position. Throws an ArrayIndexOutOfBounds
*
* exception with a message if the
*
* position is out of bounds.
*
*/
public void insert(int value, int pos) {
if (list.length > pos) {
list[pos] = value;
} else {
throw new ArrayIndexOutOfBoundsException("The position of your value is greater than the array");
}
}
/**
*
* Returns the position of target if target is
*
* one of the values in the array, otherwise -1.
*
* Implemented with a loop.
*
*/
public int find(int target) {
int position = 0;
for (int i = 0; i < list.length; i++) {
if (list[i] == target) {
position = i;
} else {
position = -1;
}
}
return position;
}
/**
*
* Same as the find method, except that it's implemented
*
* using recursion. (Hint: use a helper method.)
*
*/
public int recursiveFind(int pos, int target) {
if (pos >= list.length) {
return -1;
} else if (list[pos] == target) {
return pos;
} else {
return recursiveFind(pos + 1, target);
}
}
/**
*
* Returns the elements of the array, separated by
*
* spaces, with a newline after every 10 elements,
*
* so they can be easily displayed.
*
*/
public String toString() {
String listString = "";
int pos = 0;
for (int i = 0; i < list.length; i++) {
//list[i].toString();
listString = listString + (String) list[i] + " ";
pos++;
if (pos > 9) {
list = list + "\n";
pos = 0;
}
return listString;
}
}
}
Your method looks almost fine.
To add the newline, you should check for the remainder of the index.
You're also returning the resulting String inside the loop. You should only return after it.
public String toString() {
String listString = "";
for (int i = 0; i<list.length; i++){
listString = listString + (String)list[i] + " ";
if (i%9 == 0){ // if remainder of i%9 = 0, then add a newline
list = list + "\n";
}
}
return listString;
}
I don't want to give too much away since it's an academic assignment, but instead of pos, consider mod (%), which you use, so I know you're familiar with it.
Consider starting i at 1 and iterating through all values (no need to take 10 at a time). But test for the need for a newline like this:
i % 10 == 0
Whenever the ith value is divisible by 10, insert a newline.
Also, consider using Integer instead of int. In this way, you could simply do this:
listString = listString + list[i].toString() + " ";
Take a look at this post on SO.
Good luck!
How about:
public String toString() {
String listString = "";
for (int i = 1; i < list.length; i++) {
listString += list[i - 1] + (i % 10 == 0 ? "\n" : " ");
}
return listString;
}

Quicksort partition algorithm loop invariants defined in partition method

I implemented the partition method in the quicksort algorithm but i dont know if it satisfies the loop invariant that they were given to me.
public class qs {
private qs(){}
/**
* Sort an array in ascending order
* #param arr array to sor */
public static void quickSort(int[] arr){
qs(arr, 0, arr.length);
}
/**
* Sort a region of an array in ascending order.
* Elements outside the given region are unchanged.
* requires: 0 <= start <= end <= arr.length
* #param arr array to sort
* #param start start of region (inclusive)
* #param end end of region (exclusive)
*/
private static void qs(int[] arr, int start, int end){
if (end <= start+1){ //region of length 0 or 1
return;
}
int x = arr[start];
int p = partition(arr, start+1, end, x);
//now swap arr[start] with arr[p-1]
arr[start] = arr[p-1];
arr[p-1] = x;
qs(arr, start, p-1);
qs(arr, p, end);
}
/**
* Partition a region of an array.
* Rearranges elements in region so that small ones
* all have smaller indexes than the big ones.
* Elements outside the region are unchanged.
* requires: 0 <= start <= end <= arr.length
* #param arr array to partition
* #param start start of region (inclusive)
* #param end end of region (exclusive)
* #param x pivot - "small" and "big" are <x, >=x.
* #return start index (inclusive) of big elements
* in region after partition.
*/
private static int partition(
int[] arr, int start, int end, int x)
{
int l = start-1 ;
int r = end;
while (true) {
while(++l< r && arr[l] < x);
while(r-- > l && arr[r] > x);// find smaller item
if(l >= r) // if pointers cross,
break; // partition done
else {
int temp;
temp = arr[l];
arr[l] = arr[r];
arr[r] = temp;
}
}
return l;
}
public static void main(String[] args) {
int[] a = {15,8,4,8,9,6,4,1,2,5,6,4};
quickSort(a);
for (int i = 0; i < a.length; i++){
System.out.print(" "+a[i]);
}
}
}
Your code should work except one problem. Pivot that you are selecting is the starting point of the array rather than a middle point of the array. So instead assigning
int x = arr[start];
You better assign it to
int x = arr[(start + end)/2];
Your code must work.

Java quicksort.Identify changes in the partition while loop

What changes do i need to make to the partition loop if inside the partition method i want to initialise l=start or l=start+1 and end =end-1 ? If these values are initialised to l=start-1; and r=end; than the loop works fine but it does not satisfy the invariant.Any ideas?
public class qs {
private qs(){}
/**
* Sort an array in ascending order
* #param arr array to sor */
public static void quickSort(int[] arr){
qs(arr, 0, arr.length);
}
/**
* Sort a region of an array in ascending order.
* Elements outside the given region are unchanged.
* requires: 0 <= start <= end <= arr.length
* #param arr array to sort
* #param start start of region (inclusive)
* #param end end of region (exclusive)
*/
private static void qs(int[] arr, int start, int end){
if (end <= start+1){ //region of length 0 or 1
return;
}
int x = arr[start];
int p = partition(arr, start+1, end, x);
//now swap arr[start] with arr[p-1]
arr[start] = arr[p-1];
arr[p-1] = x;
qs(arr, start, p-1);
qs(arr, p, end);
}
/**
* Partition a region of an array.
* Rearranges elements in region so that small ones
* all have smaller indexes than the big ones.
* Elements outside the region are unchanged.
* requires: 0 <= start <= end <= arr.length
* #param arr array to partition
* #param start start of region (inclusive)
* #param end end of region (exclusive)
* #param x pivot - "small" and "big" are <x, >=x.
* #return start index (inclusive) of big elements
* in region after partition.
*/
private static int partition(
int[] arr, int start, int end, int x)
{
int l = start ;
int r = end-1;
while (l<r) {
while(++l< r && arr[l] < x);
while(--r > l && arr[r] > x);
if(l >= r) // if pointers cross,
break; // partition done
else {
int temp;
temp = arr[l];
arr[l] = arr[r];
arr[r] = temp;
}
}
return l;
}
public static void main(String[] args) {
int[] a = {15,8,4,8,9,6,};
quickSort(a);
for (int i = 0; i < a.length; i++){
System.out.print(" "+a[i]);
}
}
}
Pivot should be Middle point, not the starting of the array.
int x = arr[start];
Replace this line with this...
int x = arr[(start+end)/2]

Categories

Resources