I am stuck with a homework question for something fairly stupid.
The mission is to find the smallest column sum on a 2D array and return it's index. No loops allowed, only recursion.
I managed the code, but I'm stuck with the simple task of calculating the column itself.
This is the code I wrote so far:
public static int maxSumCol(int[][] a) {
int maxCol=calculateCol(a, 0, 0);
int colIndex=0;
return maxSumCol(a, 0, 0, maxCol, colIndex);
}
private static int maxSumCol(int[][] a, int i, int j, int maxCol, int colIndex) {
if (j<a.length){
int tempCol=calculateCol(a, i, j);
if (tempCol > maxCol)
colIndex=j;
return maxSumCol(a, i, j+1, maxCol, colIndex);
}
return colIndex;
}
And this is the method that I built to calculate the column sum:
private static int calculateCol(int[][] a, int row, int col){
if (row<=a.length-1)
return a[row][col] + calculateCol(a, row+1, col);
return 0;
}
Unfortunately, I receive an ArrayIndexOutOfBoundsException every time I run the code.
I can't figure where my mistake is.
What I can see from your post, there are two problems.
First, when you calculate the sum of the columns you only check if the column index is less than the length of the outer matrix but this is the number of rows, not columns.
if (j<a.length){
int tempCol=calculateCol(a, i, j);
The second is that when you found a column with greater sum than the one you have store previously, you only update the colIndex but not the maxcol variable where you store the actual value of the sum
if (tempCol > maxCol)
colIndex=j;
Related
I came across the following question in LeetCode while studying stacks and I'm able to get the brute-force solution. There's a lot of videos + solutions on the DP method but I would like to understand the recursion with memoization method (since I'm not studying DP).
I thought that by maintaining the sum in calc(), I was keeping the previously computed results, which implies memoization... but I suppose that's not the case.
My current brute-force solution is
class Solution {
private int count = 0;
public int findTargetSumWays(int[] nums, int target) {
calc(nums, target, 0, 0);
return this.count;
}
private int calc(int[] nums, int target, int sum, int i) {
if (i == nums.length) {
if (sum == target) {
count++;
}
return sum;
}
calc(nums, target, sum + nums[i], i+1);
calc(nums, target, sum - nums[i], i+1);
return 0;
}
}
The problem statement is:
You are given an integer array nums and an integer target.
You want to build an expression out of nums by adding one of the symbols '+' and '-' before each integer in nums and then concatenate all the integers.
For example, if nums = [2, 1], you can add a '+' before 2 and a '-' before 1 and concatenate them to build the expression "+2-1".
Return the number of different expressions that you can build, which evaluates to target.
This solution is Targer Sum problem from leetcode with recursive aaproach
public int findTargetSumWays(int[] nums, int target) {
return countTarget(nums, 0, 0, target);
}
public int countTarget(int[] nums, int pos, int sum, int target) {
if (nums.length == pos) {
return sum == target ? 1 : 0;
}
return countTarget(nums, pos + 1, sum + -nums[pos], target)
+ countTarget(nums, pos + 1, sum + nums[pos], target);
}
Background
I had an interview today and I was asked the following question.
You are given a grid.
int [][] grid =
{
{0,0,0,2,5},
{0,0,0,1,5},
{0,1,1,1,1},
{2,0,0,0,0}
};
You start from the bottom left off the grid. You can only go up and right. The idea is to get to the TOP right corner. You are to take the path that will get you the most Value.
The output for the above would be 16
My solution
public static int getPathMaxSum(int [][] grid){
int row = grid.length-1;
int col = 0;
int currentSum = grid[row][col];
return getMax(grid,currentSum,row,col);
}
public static int getMax(int [][] grid,int sum,int row,int col){
if((isTopRight(grid,row,col)) || (!isValid(grid,row,col))){
return sum;
}else{
sum = sum + grid[row][col];
return Math.max(getMax(grid,sum,row-1,col),getMax(grid,sum,row,col+1));
}
}
public static boolean isTopRight(int [][] grid, int row, int col){
return row == 0 && col == grid[row].length-1;
}
public static boolean isValid(int [][] grid, int row, int col){
return (row >= 0 && row < grid.length) && (col >= 0 && col < grid[row].length);
}
I am trying to solve this recursively. I figure that i have 2 possible choices to make at every position and i want to get the max of the two choices but for some reason I am not able to get the correct output.
I have two helper functions that check if my position is valid meaning inside the grid and also if i have hit the top right and if i have then i hit my base case.
I would love inputs to see where i went wrong.
You don't need a sum parameter in your method.
I assume you already understand how recursion-topdown approach for this problem.
But again just for the sake of completeness, the basic formula is:
You start with a cell at row, col get its value and then either you look to UP (row-1, col) or RIGHT (row, col+1).
So the result is going to be:
grid[row][col] + Math.max( getMax( row-1, col, grid ), getMax( row, col+1, grid ) )
Base conditions:
a) If it is top right i.e. the destination, you don't need to recurse you just return the value at that cell.
b) If it is an invalid cell like you have written in your isValid method, you need to return Integer.MIN_VALUE because you could have a negative value in your other cells and you want them to be maximum.
So your getMax function needs to be:
public static int getMax(int [][] grid,int row,int col){
if (isTopRight(grid,row,col)) {
return grid[row][col];
} else if (!isValid(grid,row,col)){
return Integer.MIN_VALUE;
} else {
return grid[row][col] + Math.max(getMax(grid,row-1,col),getMax(grid,row,col+1));
}
}
You can see working example here
EDIT: Answer to the edited version of your code
Issues with your new solution:
int currentSum = grid[row][col]; and sum = sum + grid[row][col];
The sum is initialized with the value in the bottom left corner and in the initial call of getMax() the same value is added again. This is not what it should be like. Just start the sum with 0, adding will be done by getMax().
if((isTopRight(grid,row,col)) || (!isValid(grid,row,col))) then return sum;
For invalid positions this will work (see restrictions below my code), but not for the top right corner (since we haven't added the value of the corner yet). Thus pull the two conditions apart and only return directly on invalid positions. On any other position first add the value and then, if you reached the "goal", return the sum. Otherwise return the maximum of "going right" and "going up" (the recursive call is correct now).
Fixing these issues and implementing your example, I derived the following code:
public class Pathfinder {
public static void main(String... args) {
int [][] grid = {
{0,0,0,2,5},
{0,0,0,1,5},
{0,1,1,1,1},
{2,0,0,0,0}
};
System.out.println(getPathMaxSum(grid));
}
public static int getPathMaxSum(int[][] grid) {
int row = grid.length - 1;
int col = 0;
return getMax(grid, 0, row, col);
}
public static int getMax(int[][] grid, int sum, int row, int col) {
if(!isValid(grid, row, col))
return sum;
sum = sum + grid[row][col];
if(isTopRight(grid, row, col))
return sum;
return Math.max(getMax(grid, sum, row - 1, col), getMax(grid, sum, row, col + 1));
}
public static boolean isTopRight(int[][] grid, int row, int col) {
return row == 0 && col == grid[row].length - 1;
}
public static boolean isValid(int[][] grid, int row, int col) {
return (row >= 0 && row < grid.length) && (col >= 0 && col < grid[row].length);
}
}
Note, that this version will work for any grid (as long as the stack is big enough and we are not dealing with too large numbers, e.g. we won't get any integer overflow) if all entries are non-negative. Anyways, a grid with negative entries can be manipulated in such a way, that the best path will be found by this algorithm and the solution can be easily "translated" back to the original grid (just subtract the smallest value from every entry).
ANSWER TO THE ORIGINAL CODE
I see multiple issues with your code:
isValid(grid,row+1,col) and sum1 = grid[row+1][col];
You are trying to add 1 to the row, but you started (correctly) with int row = grid.length-1;. Adding 1 will give you an invalid position, thus the first branch will never be executed. Instead, you will need to subtract 1 from the row to "go up".
sum = sum + Math.max(sum1,sum2);
This changes sum, but you cannot see, in which direction you moved. And directly afterwards ...
getMax(grid,sum,row+1,col); and getMax(grid,sum,row,col+1);
... you do the recursive calls with the new, maximum sum, but from both spots. To get a correct solution, you should call them with the value, their direction represents. Note also, that row+1 needs to be replaced by row-1 here as well.
return sum;
You now return this maximum sum, but completely ignoring the returns of your recursive calls. You should instead compare their returns and return yourself the higher value of both.
Bactracking vs. Dynamic Programming
Your algorithm should work in general and is sufficient for small instances of the problem, but not for bigger ones (since it will make 2 recursive calls for every step and you have 2*(n-1) steps.. resulting in exponential runtime). An alternative approach with quadratic runtime would be to go backwards through the field and choose the best way by looking just one field right or up and adding the value of the current field to the maximum of this. Just start in the top right corner and go left, row by row from right to left.
I've been looking for a recursive selection sort, using only 2 parameters:
The array that has to be sorted
a value k, which indicates till which
element it has to be sorted.
Example: SelectionSort(array[] a, int k) with a being {6,3,5,7,2} and k being 2 will sort the first 3 elements, and will keep the last elements untouched.
I was thinking about starting with an if-statement for k being 0, and if that was the case, it would just return the array as it is, since you cant sort an array of size 1.
Something like:
public int[] sort(int[] a){
a = selectionSort(a, n-1);
return a;
}
public int[] selectionSort(int[] a, int k){
if (k = 0){
return a;
}
else{
selectionSort(a, k-1 );
... (part i really don't know)
}
I have no clue how to do the 'else' part since I only know that it has to call the method again.
I'm not allowed to create other methods. I also need to make sure I use exactly 2 parameters, nothing more, nothing less.
I have to work it out in pseudocode, but I understand some Java, so if someone could help me by using either pseudo, or Java, it would be so helpful
First some remarks to your code:
Your methods sort and selectionSort don't need to return an int[] array,
since the array object a stays the same all the time.
It is only the content within this array which changes.
Hence, you can use void as return-type.
In your if use (k == 0) instead of (k = 0)
You already figured out the first part.
Here it is how you can do the second part in pseudo code:
public void selectionSort(int[] a, int k) {
if (k == 0) {
return;
}
else {
selectionSort(a, k-1 );
find x such that a[x] is the smallest of a[k] ... a[a.length - 1]
if (a[k-1] > a[x]) {
swap a[k-1] and a[x]
}
}
}
I'm sure you are able to refine the pseudo code to real Java code.
By doing a simple google search, I found the biggest part of the code below on this site. I added the selectionSort method myself to suit your parameters.
public void selectionSort(int a[], int n)
{
recurSelectionSort(a, n, 0);
}
// Recursive selection sort. n is size of a[] and index
// is index of starting element.
static void recurSelectionSort(int a[], int n, int index)
{
// Return when starting and size are same
if (index == n)
return;
// calling minimum index function for minimum index
int k = minIndex(a, index, n-1);
// Swapping when index nd minimum index are not same
if (k != index){
// swap
int temp = a[k];
a[k] = a[index];
a[index] = temp;
}
// Recursively calling selection sort function
recurSelectionSort(a, n, index + 1);
}
// Return minimum index
static int minIndex(int a[], int i, int j)
{
if (i == j)
return i;
// Find minimum of remaining elements
int k = minIndex(a, i + 1, j);
// Return minimum of current and remaining.
return (a[i] < a[k])? i : k;
}
I have created the following code in Jana (Java-Based Abstract Notation for Algorithms) which creates a 2-dimensional array of length n:
fillMatrix(↕int matrix[1:n,1:n], ↓int n, ↓int a){
for(i=1…n){
for(j=1…n){
if(abs(↓(i-j))<=a){
matrix[i,j]=1
}else{
matrix[i,j]=0
}
}
}
}
int abs(↓int i){
if(i<0)
return –i
else
return i
}
This code has an asymptotic runtime of O(n^2).
My question is, assuming that each element of the matrix is initialized to 0 at the call, how can I make this code more efficient?
Thanks in advance for the help!
Assuming you only have to initialize the cells that get 1 value (the rest of the cells are 0 by default):
If a is much smaller than n, you can initialize the cells that get 1 value in O(n + a*n) time.
For example, if a == 0, all you need is to set the n cells of the main diagonal of the matrix ((0,0),(1,0),...,(n-1,n-1)) to 1.
If a == 1, you need to set the n cells of the main diagonal + the 2*(n-1) cells of the diagonal adjacent to the main diagonal.
...
If a = c, you need to set O(n) + O(2c*n) cells to 1, which is O(n + c*n).
To implement this algorithm, you'll need to replace your O(n^2) loop with 2*a+1 O(n) loops (one loop for each relevant diagonal).
I think that you use the wrong tool for your problem. Not every problem is a nail, and so not every solution involves a hammer. If you create a Matrix interface, and program against that interface, you can solve the instantiation in O(1) and also use less memory:
interface Matrix {
int get(int i, int j);
}
class OrdinaryMatrix implements Matrix {
int[][] data;
public OrdinaryMatrix (int rows, int columns) { ... }
public int get(int i, int j) {
return data[i][j];
}
}
class SpecialMatrix implements Matrix {
private final int a;
public SpecialMatrix (int rows, int columns, int a) {
...
this.a = a;
}
public int get(int i, int j) {
return Math.abs(i-j)<=a ? 1 : 0
}
}
Im solving the QuickSort assignment at Algorithms class by Stanford and using the median rule to select the pivot element. The input is numbers from 1-10000 and output is the number of comparisons
My function are as follows :
public static int noOfComp = 0;
public static void quick_sort(int[] a, int p, int r){
if(p<r) {
noOfComp+= r-p;
int mid = partition(a, p, r);
quick_sort(a, p, mid-1);
quick_sort(a, mid+1, r);
}
}
public static int median(int a[],int p, int r){
int firstPos = p;
int len = r-p+1;
int lastPos = r;
int midPos = len%2==0 ? p + (len)/2-1: p + (len)/2 ;
int first = a[firstPos];
int middle = a[midPos];
int last = a[lastPos];
if (first <= middle) {
if (middle <= last) {
// first - middle - last
return midPos;
} else if (first <= last) {
// first - last - middle
return lastPos;
}
// last - first - middle
return firstPos;
}
if (first <= last) {
// middle - first - last
return firstPos;
} else if (middle <= last) {
// middle - last - first
return lastPos;
}
// last - middle - first
return midPos;
}
public static int partition(int[] a, int p, int r){
int chosen = median(a,p,r);
swap(a, p, chosen);
int pivot = a[p];
int i = p;
for (int j = p+1; j < a.length; j++) {
if (a[j] < pivot) {
i++;
swap(a, i, j);
}
}
swap(a, i,p);
return i;
}
//main
public static void main(String[] args) throws Throwable{
int i=0;
Scanner in = new Scanner(new File("C:\\Users\\Uzumaki Naruto\\Documents\\QuickSort.txt"));
while(in.hasNext()){
i++;
in.next();
}
int[] a = new int[i];
i=0;
Scanner in2 = new Scanner(new File("C:\\Users\\Uzumaki Naruto\\Documents\\QuickSort.txt"));
while(in2.hasNext()){
a[i++] = in2.nextInt();
}
quick_sort(a, 0, a.length-1);
System.out.println("Number of comparisons : " + noOfComp);
}
The answer to question seems to be around 128k , but my algorithm output it 132k. I've read the code number of times but unable to ascertain the error.
Indeed, I also get an average count of around 132k with your code, executed on randomly shuffled arrays of unique numbers. I did not find any mistake in the algorithm, except for the following one, but it's not influencing your count result, which assumed correct code:
The loop in partition has a bad exit condition:
for (int j = p+1; j < a.length; j++) {
It should be:
for (int j = p+1; j <= r; j++) {
The following is not an error, but you can rewrite
int len = r-p+1;
int midPos = len%2==0 ? p + (len)/2-1: p + (len)/2 ;
as:
int midPos = p + (r-p)/2;
But: You did not count the comparisons made in the function median, and this should normally be done, otherwise an algorithm cannot be fairly compared with another (variant). So that results in 2 or 3 more comparisons per call of partition. This increases the average count to around 148k!
Here it says that:
the expected number of comparisons needed to sort n elements with random pivot selection is 1.386 n.log(n). Median-of-three pivoting brings this down to ≈ 1.188 n.log(n).
The thing is that for n = 10 000, 1.188 n.log(n) ≈ 158k so your algorithm seems to do fewer comparisons than this estimate, at least for this particular case of n.
I do see a way to reduce that number again.
Reducing the number of comparisons
The main idea is to profit from the comparisons you make in the function median by already putting the lowest and highest of the three inspected values in the right partition, so they do not need to be treated further by the loop in the function partition.
To give an example, if you have an array like this:
5, 1, 2, 9, 3
Then median will compare 5, 2 and 3 and choose 3 as pivot value. The function could now be extended to also put the three investigated elements in the right order, without extra comparisons, to get this:
2, 1, 3*, 9, 5
And then the pivot element would not have to be swapped to the start of the array, but to the second slot, because we already have decided that the left most element belongs to the lower partition:
2, 3*, 1, 0, 5
And now the main partition loop can concentrate on this sub-array, because also the last element is known the belong to the upper partition:
2, 3*, [1, 0], 5
At the end of the loop the final swap will be with the second element instead of the first:
2, 0, 1, 3*, 5
This will reduce the number of comparisons in the main loop with 2.
In this variant, the median function will always return the index of the second slot, after making a few swaps in the array:
public static int median(int a[],int p, int r){
int m = p + (r-p)/2;
// actually sort the three elements:
noOfComp++;
if (a[r] < a[m]) {
swap(a, r, m);
}
if (p < m) { // more than 2 elements
noOfComp++;
if (a[m] < a[p]) {
swap(a, m, p);
noOfComp++;
if (a[r] < a[m]) {
swap(a, r, m);
}
}
// put the middle element (pivot) in second slot
swap(a, m, p+1);
}
return p+1;
}
And partition will look like this:
public static int partition(int[] a, int p, int r){
int k = median(a, p, r); // always returns p+1 as pivot's index
int i = k; // (k..i] is lower partition
for (int j = p+2; j < r; j++) { // positions p and r can be excluded
if (a[j] < a[k]) {
i++;
swap(a, i, j);
}
}
swap(a, i, k); // place pivot between partitions
return i;
}
In quick_sort the count of comparisons will be two less:
noOfComp += r-p-2;
With the above adjustments the number of comparisons goes down from 148k to 135k on average.
So I am afraid that although the actual number of comparisons has been reduced this way, it still does not match the 128k.
Other ideas
I tried using insertion sort when the array became small, but it did not yield much of an improvement. Another idea is to improve the search for the median by looking at more elements, but only if the array is not too small, as the cost of looking for one must be small compared to the partitioning effort.
But the assignment may not allow for all this tweaking.