array index in heapsort - c
In reading in Chapter 14 of Jon Bentley's "Programming Pearls", 2nd Edition, I understand that heaps use a one-based array and the easiest approach in C is to declare x[n+1] and waste element x[0] (page 148).
On page 157, Jon listed the complete heapsort pseudo code:
for i = [2, n]
siftup(i)
for (i = n; i >= 2; i--)
swap(1, i)
siftdown(i - 1)
Here is an implementation in C. However, the array index starts with 0, instead of 1.
void heapSort(int numbers[], int array_size)
{
int i, temp;
// Qiang: shouldn't the stop-condition be i >= 1?
for (i = (array_size / 2)-1; i >= 0; i--)
siftDown(numbers, i, array_size);
for (i = array_size-1; i >= 1; i--)
{
// Qiang: shouldn't the swap be done with numbmers[1], instead of numbers[0]?
temp = numbers[0];
numbers[0] = numbers[i];
numbers[i] = temp;
siftDown(numbers, 0, i-1);
}
}
void siftDown(int numbers[], int root, int bottom)
{
int done, maxChild, temp;
done = 0;
while ((root*2 <= bottom) && (!done))
{
if (root*2 == bottom)
maxChild = root * 2;
else if (numbers[root * 2] > numbers[root * 2 + 1])
maxChild = root * 2;
else
maxChild = root * 2 + 1;
if (numbers[root] < numbers[maxChild])
{
temp = numbers[root];
numbers[root] = numbers[maxChild];
numbers[maxChild] = temp;
root = maxChild;
}
else
done = 1;
}
}
My worry is, if the array starts with index 0, then the following properties will not hold (as written on page 148 in Jon's book):
leftchild(i) = 2*i
rightchild(i) = 2*i+1
parent(i) = i/2
It looks to me that the properties here only hold when the i starts with 1.
What strikes me is that the analysis part in the implementation used an array starting with index 1, while the implementation part used an array starting with index 0 and didn't waste the first element.
Am I missing anything here?
Edited
With help from interjay, I realized the error contained in the original implementation, which could be shown with a testing input array of {66,4,23,4,78,6,44,11,22,1,99}.
Changed the original siftDown() function a little bit to adjust the relationship between the index of parent and those of its children:
void siftDown(int numbers[], int root, int bottom)
{
int done, maxChild, temp;
done = 0;
while ((root*2 + 1 <= bottom) && (!done))
{
if (root*2 + 1 == bottom ||
numbers[root * 2 + 1] > numbers[root * 2 + 2])
maxChild = root * 2 + 1;
else
maxChild = root * 2 + 2;
if (numbers[root] < numbers[maxChild])
{
temp = numbers[root];
numbers[root] = numbers[maxChild];
numbers[maxChild] = temp;
root = maxChild;
}
else
done = 1;
}
}
Credits go to interjay, :-)
Afterword:
It looks the same error doesn't appear in the implementations in wikibooks and algorithmist. Hooray!
The heap elements can be stored starting with index 0 or index 1, the decision on which to use is up to you.
If the root element is at index 1, the mathematical relations between parent and child indices are simple as you've shown above, and for that reason many books choose to teach it that way.
If the root is at index 0, you'd get these relations instead:
leftchild(i) = 2*i+1
rightchild(i) = 2*i+2
parent(i) = (i-1) / 2
It doesn't matter which one you pick as long as you are consistent.
The C code you've shown seems incorrect to me. It starts with array index 0, but uses the parent/child relations appropriate for starting with index 1.
A reusable implementation of heapsort would want to start at a root index of 0 so the user could use a normal (0 based) array with it. You wouldn't want to require the user to allocate an extra member and start the array at index 1 just so they can use your heapsort function. You do need to use the modified parent/child calculations that #interjay shows.
Replying to little old thread, thought my small contribution might helps future visitors.
Experts please validate and correct my logic if I missed any scenarios.
Considered Qiang Xu link and interjay zero based index logic.
And here is the C# code and tested with the below inputs.
//-----------------------------------------------------------------------------------------------------------------------------------------------
// Input Arrays :
int[] ErrCaseArry = new int[] { 66, 4, 23, 4, 78, 6, 44, 11, 22, 1, 99};
int[] GenCaseArry = new int[] { 30, 20, 40, 10, 90, 160, 140, 100, 80, 70 };
int[] NearlySortedArry = new int[] { 1, 2, 3, 4, 6, 5 };
int[] FewSortedArry1 = new int[] { 3, 2, 1, 4, 5, 6 };
int[] FewSortedArry2 = new int[] { 6, 2, 3, 1, 5, 4 };
int[] ReversedArry1 = new int[] { 6, 5, 4, 3, 2, 1 };
int[] FewDuplsArry2 = new int[] { 1, 3, 1, 2, 1, 3 };
int[] MoreDuplsArry3 = new int[] { 1, 1, 2, 2, 1, 2 };
//-----------------------------------------------------------------------------------------------------------------------------------------------
public void HeapSort(int[] listToSort)
{
int LastChildIndex = listToSort.Length -1;
int parentElementIndex = ((LastChildIndex - 1)/ 2);
//1. Use this loop to Construct Heap Array (Max/Min) by using Heapify function on every node.
while (parentElementIndex >= 0) // (N - 1) / 2 to 0
{
Heapify(listToSort, parentElementIndex, LastChildIndex); // (N - 1) / 2 & Lenght - 1
parentElementIndex--;
}
//-----------------------------------------------------------------------------------------------------------------------------------------------
AppendArrayToResultString("Max Heap\t", listToSort);
//2. Heap sort algorithm takes largest element off the heap and places it at the end of an array.
// This phase continue until all the elements are placed in the array that are in sorted order.
int sortedElementIndex = listToSort.Length - 1;
//-----------------------------------------------------------------------------------------------------------------------------------------------
// In this loop get Largest Element to Zero'th postion and move to end. and reduce the loop count from Heapify Array. So that elements gets sorted from right.
while (sortedElementIndex >= 0) // (N - 1) to 1
{
// Swap the elements (root(maximum value)) of the heap with the last element of the heap
Swap(ref listToSort[0], ref listToSort[sortedElementIndex]);
// sortedElementIndex-- : Decrease the size of the heap by one so that the previous max value will stay in its proper placement
sortedElementIndex--;
if (sortedElementIndex == -1) break;
// Since largest elemented from 0 to last, Re Heapify and get the remaining largest element and place it in 0 position.
Heapify(listToSort, 0, (sortedElementIndex)); // 0 to (N - 1)
}
//-----------------------------------------------------------------------------------------------------------------------------------------------
}
//Heapify() function maintain the heap property (Max Heap or Min Heap). Can be recursive or can use iteration loop like while/for.
void Heapify(int[] listToSort, int parentIndext, int lastChildIndext)
{
//bool doneFlag = false;
int largestElementIndex = 0;
int leftChildIndex = parentIndext * 2 + 1;
int rightChildIndex = parentIndext * 2 + 2;
while (leftChildIndex <= lastChildIndext) //&& !doneFlag)
{
// If leftChild is larger than rightChild or it is the last child and there is no rightChild for this parent.
// Then consider leftChild as largestElement else consider rightChild as largestElement.
if (leftChildIndex == lastChildIndext || listToSort[leftChildIndex] > listToSort[rightChildIndex])
{
largestElementIndex = leftChildIndex;
}
else
{
largestElementIndex = rightChildIndex;
}
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
// If largestElement is larger than parent then swap them and make parent as largestElement to continue the loop.
if (listToSort[parentIndext] < listToSort[largestElementIndex])
{
// Make largestElement as parent. And continue finding if childs (left and right) are bigger than element in largestIndex position.
Swap(ref listToSort[parentIndext], ref listToSort[largestElementIndex]);
// Repeat to continue sifting down the child now
parentIndext = largestElementIndex;
leftChildIndex = ((parentIndext * 2) + 1);
rightChildIndex = ((parentIndext * 2) + 2);
}
else
{
//doneFlag = true;
break; // Trying to avoid extra flag condition check. Or return.
}
}
}
//-----------------------------------------------------------------------------------------------------------------------------------------------
void Swap(ref int num1, ref int num2)
{
int temp = num1;
num1 = num2;
num2 = temp;
}
Related
Algorithm not functioning for sort squared array based off of input
I'm working on building an algorithm that sorts in place for an array of nondecreasing integers, and it's not passing some of my tests. I was wondering why? I've included a sample input and output as well. import java.util.*; class Program { public int[] sortedSquaredArray(int[] array) { int[] res = new int[array.length]; int leftPointer = 0; int rightPointer = array.length - 1; int counter = 0; while (counter < array.length) { int leftSquared = array[leftPointer] * array[leftPointer]; int rightSquared = array[rightPointer] * array[rightPointer]; if (leftSquared < rightSquared) { res[counter] = leftSquared; leftPointer++; } else if (rightSquared <= leftSquared) { res[counter] = rightSquared; rightPointer--; } counter++; } return res; } } "array": [-50, -13, -2, -1, 0, 0, 1, 1, 2, 3, 19, 20] expected output: [0, 0, 1, 1, 1, 4, 4, 9, 169, 361, 400, 2500] what I'm getting: [400, 361, 9, 4, 1, 1, 0, 0, 1, 4, 169, 2500]
If the array was specified to be in increasing order, your attempt was very close: Just fill the result from larger squares to lower. (If the array starts with a non-negative value, just return (a copy of) the input array.)
Just as #greybeard wrote: your mistake is to fill from the lower end, but you do not know the lowest square yet, since you are checking the two numbers with the BIGGEST square value. This function should do what you want: public int[] sortedSquaredArray(int[] array) { if (array.length == 0 || array[0] >= 0) return array; int[] res = new int[array.length]; int leftPointer = 0; int leftSquared = array[leftPointer] * array[leftPointer]; int rightPointer = array.length - 1; int rightSquared = array[rightPointer] * array[rightPointer]; int counter = rightPointer; while (counter >= 0) { if (leftSquared >= rightSquared) { res[counter] = leftSquared; leftPointer++; leftSquared = array[leftPointer] * array[leftPointer]; } else { res[counter] = rightSquared; rightPointer--; rightSquared = array[rightPointer] * array[rightPointer]; } counter--; } return res; } Note the optimisations in line 3 and 4 and calculating the squared values only when needed. Also this function is NOT doing in-place sorting! It is returning a new array with the sorted squares. Doing an in-place sorting could be accomplished by re-assigning the values from the sorted array to the passed-in array before returning or using the passed-in array directly but having to move around the array-values a lot, if the left pointer is pointing to the bigger value. You can watch the code in action with your example data here.
What am I doing wrong in my C binary search code? (iterative & recursive)
What am I doing wrong here? The prototypes aren't changeable. I need to write both of the functions and this is what I wrote. They work at 90% of the times. The iterative way got an issue when i'm trying to search for the middle element. And the recursive way got some issue but I don't really know what's wrong. Sometimes it finds the element and other times not. One more thing I cannot change it's that I must avoid checking inside of the function whether array[middle] == key. The teacher wants us to do this check only when I exit the loop, and he wants the loop to only check if should I go right or left. int *BinarySearchIter(const int SortedArray[], int key, size_t length) { /* variables that will set the boundries of the searched area */ int left_index = 0, right_index = 0, middle = 0; int *result = NULL; assert(SortedArray); assert(length); left_index = 0; /* first elemenet in the array */ right_index = length - 1; /* last elemenet in the array */ /* while only one elemenet is left in the searched area */ while (left_index <= right_index) { /* split it to half */ middle = (left_index + right_index) / 2; /* if key greater, ignore left half, search only in right half */ if (SortedArray[middle] < key) { left_index = middle + 1; } /* if key smaller, ignore right half, search only in left half */ else { right_index = middle - 1; } } /* if we reach here, then element is the key or was not found */ result = (int *)SortedArray + middle; return (key == *result ? result : NULL); } /******************************************************************************/ int *BinarySearchRec(const int SortedArray[], int key, size_t length) { int left_index = 0; /* first elemenet of the array */ int right_index = length - 1; /* last elemenet in the array */ int middle = 0, isBigger = 0; if (1 == length) { return (key == *SortedArray ? (int *)SortedArray : NULL); } middle = (left_index + right_index) / 2; isBigger = (key > SortedArray[middle]); return (BinarySearchRec(SortedArray+(middle + 1)*isBigger, key, isBigger * (length - length /2 ) + !isBigger*(length/2))); } /******************************************************************************/ I was trying to run the following test; const int sorted_arr[] = { 2, 4, 8, 10, 12}; size_t arr_length = sizeof(sorted_arr) / sizeof(sorted_arr[0]), i = 0; int key_to_find = 0; int *iterative_res = NULL; int *recursive_res = NULL; for (i = 0; i < arr_length; ++i) { key_to_find = sorted_arr[i]; iterative_res = BinarySearchIter(sorted_arr, key_to_find, arr_length); recursive_res = BinarySearchRec(sorted_arr, key_to_find, arr_length); Print if any errors (nulls or key doesn't match any of the results) } And this is the output of the test: ERRORS: Needs to find: 8 But found: Iterative: NULL (failure) Recursive: NULL (failure) Needs to find: 12 But found: Iterative: 12 (success) Recursive: NULL (failure)
For Iterative Function Let's think what your code is doing. You have an array consists 5 elements and let's say you are searching for 8. 2 4 8 10 12 ^ In the first iteration the values are like this: left_index = 0, right_index = 4, middle_index = 2 In the if statement the program checks for (SortedArray[middle_index] < key) and that is wrong because 8 is not bigger than 8. So it executes the else statement. The mistake is here. In the else statement you are discarding the middle element. But in this case middle element is the key that you are looking for. So first thing that you need to change just like #Eric Postpischil said is changing your else statement to this: else { right_index = middle; } Let's continue with second iteration: 2 4 8 ^ left_index = 0, right_index = 2, middle_index = 1 After the second iteration, third iteration is only going to consist 1 element. 8 left_index = 2, right_index = 2, middle_index = 2 At this point the program stucks in the infinite loop because it always executes the else statement. Left and right indexes always stay same. So the second change that you need to do is changing your while condition to this: while (left_index < right_index) At this point when the left index and right index are equal, the loop will break. Than the last thing that you need to do is updating your middle index again. Or you can use left_index or right_index (which one is doesn't matter they are equal). At the end, your function should look like this: const int *BinarySearchIter(const int SortedArray[], int key, size_t length) { int left_index = 0, right_index = length - 1; while (left_index < right_index) { int middle = (left_index + right_index) / 2; if (SortedArray[middle] < key) { left_index = middle + 1; } else { right_index = middle; } } return SortedArray[right_index] == key ? SortedArray + right_index : NULL; } For Recursive Function Only thing you need to change is this: return BinarySearchRec(SortedArray+(middle + 1)*isBigger, key, (length / 2) + !isBigger * (length % 2)); The reasons are same as iterative function. You need to include the middle element. For lower part of the array, the array length needs to be equal to ceil(length / 2), and for upper part of the array, it needs to be equal to floor(length / 2). You can also do this: const int *BinarySearchRec(const int SortedArray[], int key, size_t length) { if (1 == length) return (key == *SortedArray ? SortedArray : NULL); int middle = (length + 1) / 2; if (key >= SortedArray[middle]) return BinarySearchRec(SortedArray + middle, key, length / 2); else return BinarySearchRec(SortedArray, key, middle); }
Maximum sum such that no two elements are adjacent
Now the available solution every where is to have an include and exclude sum . At the end max of these two will give me the output. Now initially I was having difficulty to understand this algorithm and I thought why not going in a simple way. Algo: Loop over the array by increasing array pointer two at a time Calculate the odd positioned element sum in the array Calculate the even positioned element sum At the end, take max of this two sum. in that way, I think complexity will be reduced to half O(n/2) Is this algo correct?
It's a case of dynamic programming. The algorithm is: Do not take (sum up) any non-positive items For positive numbers, split the problem in two: try taking and skiping the item and return the maximum of these choices: Let's show the 2nd step, imagine we are given: [1, 2, 3, 4, 5, 6, 10, 125, -8, 9] 1 is positive, that's why take_sum = max(1 + max_sum([3, 4, 5, 6, 10, 125, -8, 9])) // we take "1" skip_sum = max_sum([2, 3, 4, 5, 6, 10, 125, -8, 9]) // we skip "1" max_sum = max(take_sum, skip_sum) C# implementation (the simplest code in order to show the naked idea, no further optimization): private static int BestSum(int[] array, int index) { if (index >= array.Length) return 0; if (array[index] <= 0) return BestSum(array, index + 1); int take = array[index] + BestSum(array, index + 2); int skip = BestSum(array, index + 1); return Math.Max(take, skip); } private static int BestSum(int[] array) { return BestSum(array, 0); } Test: Console.WriteLine(BestSum(new int[] { 1, -2, -3, 100 })); Console.WriteLine(BestSum(new int[] { 100, 8, 10, 20, 7 })) Outcome: 101 120 Please, check, that your initial algorithm returns 98 and 117 which are suboptimal sums. Edit: In real life you may want to add some optimization, e.g. memoization and special cases tests: private static Dictionary<int, int> s_Memo = new Dictionary<int, int>(); private static int BestSum(int[] array, int index) { if (index >= array.Length) return 0; int result; if (s_Memo.TryGetValue(index, out result)) // <- Memoization return result; if (array[index] <= 0) return BestSum(array, index + 1); // Always take, when the last item to choose or when followed by non-positive item if (index >= array.Length - 1 || array[index + 1] <= 0) { result = array[index] + BestSum(array, index + 2); } else { int take = array[index] + BestSum(array, index + 2); int skip = BestSum(array, index + 1); result = Math.Max(take, skip); } s_Memo.Add(index, result); // <- Memoization return result; } private static int BestSum(int[] array) { s_Memo.Clear(); return BestSum(array, 0); } Test: using System.Linq; ... Random gen = new Random(0); // 0 - random, by repeatable (to reproduce the same result) int[] test = Enumerable .Range(1, 10000) .Select(i => gen.Next(100)) .ToArray(); int evenSum = test.Where((v, i) => i % 2 == 0).Sum(); int oddSum = test.Where((v, i) => i % 2 != 0).Sum(); int suboptimalSum = Math.Max(evenSum, oddSum); // <- Your initial algorithm int result = BestSum(test); Console.WriteLine( $"odd: {oddSum} even: {evenSum} suboptimal: {suboptimalSum} actual: {result}"); Outcome: odd: 246117 even: 247137 suboptimal: 247137 actual: 290856
dynamic programming inclusion exclusion approach is correct your algorithm would not work for test cases like 3 2 7 10 in this test case the two elements we take are 3 10 and sum is 13 instead of 3,7 or 2,10.may you understand what i am saying and for further clarity code is below Java Implementation public int maxSum(int arr[]) { // array must contain +ve elements only int excl = 0; int incl = arr[0]; for (int i = 1; i < arr.length; i++) { int temp = incl; incl = Math.max(excl + arr[i], incl); excl = temp; } return incl; }
printing paths from arrays?
I have a 2d array a[3][3] and the program reads 2 sets of IDs for 2 numbers on the array. I need to print all the possible paths from one number to another. So far, I know how to find how many paths exist every time: scanf("%d %d",&ai,&aj); scanf("%d %d",&bi,&bj); distance_i = bi - ai; if(distance_i<0){distance_i=distance_i*-1;} distance_j = bj - aj; if(distance_j<0){distance_j=ap_j*-1;} path = 1+(distance_i*distance_j); For example, if the array a is: 1 2 3 4 5 6 7 8 9 With input_1: 0,0 input_2: 1,2 The output must be: there are 3 possible paths: a) 1,2,5,8 b) 1,4,5,8 c) 1,4,7,8 But I can't find a way to print them. Any ideas?
From location[v1][h1] to location[v2][h2] Move kinds are: DOWN, RIGHT The width is: (v2-v1) * DOWN The height is: (h2-h2) * RIGHT => all path choice action list: [width, height] = [(v2-v1) * DOWN, (h2-h2) * RIGHT] Example: from location[0][0] to location [2][1] action list = [DOWN, DOWN, RIGHT] all unique path choices are (It make minus the duplicate repeated permutation from a given list): [DOWN, DOWN, RIGHT] [DOWN, RIGHT, DOWN] [RIGHT, DOWN, DOWN]
You will use backtracking (Depth-First Search) to find all possible routes. See program test here http://ideone.com/GqWLa5 #define VALID(x) ((x) >= 0 && (x) < 3) int arr[3][3]; // to detect previous visited cells and eliminate infinite recursion short vis[3][3] = { 0 }; int xtar, ytar; // destination cell int xsrc, ysrc; // source cell // to move in directions: down, up, right, and left, respectively const int dirx[] = { 0, 0, 1, -1 }; const int diry[] = { 1, -1, 0, 0 }; // temp buffer to print paths // max size = size of arr + zero termination char char tmp_path[3 * 3 + 1]; void rec(int x, int y, int idx) // idx is used to fill tmp_path { int i; tmp_path[idx] = arr[y][x] + '0'; if (x == xtar && y == ytar) // basic case { tmp_path[idx + 1] = 0; // put zero char printf("%s\n", tmp_path); // print path return; } if (vis[y][x]) return; // already visited vis[y][x] = 1; // otherwise, mark as visited for (i = 0; i < 4; ++i) // for each of the 4 directions if (VALID(y + diry[i]) && VALID(x + dirx[i])) rec(x + dirx[i], y + diry[i], idx + 1); vis[y][x] = 0; // reset visited so that can be visited again } main() { // input xtar, ytar, xsrc, ysrc, arr rec(xsrc, ysrc, 0); }
Maximum sum of non consecutive elements
Given an array of positive integers, what's the most efficient algorithm to find non-consecutive elements from this array which, when added together, produce the maximum sum?
Dynamic programming? Given an array A[0..n], let M(i) be the optimal solution using the elements with indices 0..i. Then M(-1) = 0 (used in the recurrence), M(0) = A[0], and M(i) = max(M(i - 1), M(i - 2) + A[i]) for i = 1, ..., n. M(n) is the solution we want. This is O(n). You can use another array to store which choice is made for each subproblem, and so recover the actual elements chosen.
Let A be the given array and Sum be another array such that Sum[i] represents the maximum sum of non-consecutive elements from arr[0]..arr[i]. We have: Sum[0] = arr[0] Sum[1] = max(Sum[0],arr[1]) Sum[2] = max(Sum[0]+arr[2],Sum[1]) ... Sum[i] = max(Sum[i-2]+arr[i],Sum[i-1]) when i>=2 If size is the number of elements in arr then sum[size-1] will be the answer. One can code a simple recursive method in top down order as: int sum(int *arr,int i) { if(i==0) { return arr[0]; }else if(i==1) { return max(arr[0],arr[1]); } return max(sum(arr,i-2)+arr[i],sum(arr,i-1)); } The above code is very inefficient as it makes exhaustive duplicate recursive calls. To avoid this we use memoization by using an auxiliary array called sum as: int sum(int *arr,int size) { int *sum = malloc(sizeof(int) * size); int i; for(i=0;i<size;i++) { if(i==0) { sum[0] = arr[0]; }else if(i==1) { sum[1] = max(sum[0],arr[1]); }else{ sum[i] = max(sum[i-2]+arr[i],sum[i-1]); } } return sum[size-1]; } Which is O(N) in both space and time.
O(N) in time and O(1) in space (DP) solution: int dp[2] = {a[0], a[1]}; for(int i = 2; i < a.size(); i++) { int temp = dp[1]; dp[1] = dp[0] + a[i]; dp[0] = max(dp[0], temp); } int answer = max(dp[0], dp[1]);
/** * Given an array of positive numbers, find the maximum sum of elements such * that no two adjacent elements are picked * Top down dynamic programming approach without memorisation. * An alternate to the bottom up approach. */ public class MaxSumNonConsec { public static int maxSum(int a[], int start, int end) { int maxSum = 0; // Trivial cases if (start == end) { return a[start]; } else if (start > end) { return 0; } else if (end - start == 1) { return a[start] > a[end] ? a[start] : a[end]; } else if (start < 0) { return 0; } else if (end >= a.length) { return 0; } // Subproblem solutions, DP for (int i = start; i <= end; i++) { int possibleMaxSub1 = maxSum(a, i + 2, end); int possibleMaxSub2 = maxSum(a, start, i - 2); int possibleMax = possibleMaxSub1 + possibleMaxSub2 + a[i]; if (possibleMax > maxSum) { maxSum = possibleMax; } } return maxSum; } public static void main(String args[]) { int a[] = { 8, 6, 11, 10, 11, 10 }; System.out.println(maxSum(a, 0, a.length - 1)); } }
The solution by #Ismail Badawi does not seem to work in the following case: Let us take the array: 8, 3, 1, 7 Then in this case, the algo returns max sum = 9 whereas it should be 15. A solution to correct it is given an array A[0..n], let M(i) be the optimal solution using the elements with indices 0..i. Then M(0) = A[0], and M(i) = max(M(i - 1), M(i - 2) + A[i], M(i-3) + A[i]) for i = 3, ..., n. M(n) is the solution we want. This is O(n).
IIUC: say your array is 1,2,3,4,5 then 3+5 would be 'correct' and 4+5 not, this means you'll have to find the largest numbers and check if they are consecutive. So an algorithm would be to make use of a second array, for the number of elements you need to add which you fill by traversing the original array and finding the largest non-consecutive integers, then add this up. For the above array I guess [1,3], [1,4], [1,5], [1,3,5], [2,4], [2,5], [3,5] would be valid non-consecutive integers to be summed, the max sum would be 9 in this case [1,3,5]. So, to adapt the above algorithm, I would suggest you step through the array using several temporary arrays to find all the non-consecutive integer lists, and then check which is the largest. Keep in mind that 'most elements' does not mean 'largest sum'.
Dynamic programming solution is the most elegant of all. And it serves for any value of the distance between two numbers that should not be considered. But for k= 1, which is for consecutive numbers constraint, I tried using backtracking. There are different patterns to be compared for the maximum sum. Below is the list : Number of patterns for 1 = 1 [1] Number of patterns for 2 = 2 [1][2] Number of patterns for 3 = 2 [1, 3][2] Number of patterns for 4 = 3 [1, 3][1, 4][2, 4] Number of patterns for 5 = 4 [1, 3, 5][1, 4][2, 4][2, 5] Number of patterns for 6 = 5 [1, 3, 5][1, 3, 6][1, 4, 6][2, 4, 6][2, 5] Number of patterns for 7 = 7 [1, 3, 5, 7][1, 3, 6][1, 4, 6][1, 4, 7][2, 4, 6][2, 4, 7][2, 5, 7] Number of patterns for 8 = 9 [1, 3, 5, 7][1, 3, 5, 8][1, 3, 6, 8][1, 4, 6, 8][1, 4, 7][2, 4, 6, 8][2, 4, 7][2, 5, 7][2, 5, 8] Number of patterns for 9 = 12 [1, 3, 5, 7, 9][1, 3, 5, 8][1, 3, 6, 8][1, 3, 6, 9][1, 4, 6, 8][1, 4, 6, 9][1, 4, 7, 9][2, 4, 6, 8][2, 4, 6, 9][2, 4, 7, 9][2, 5, 7, 9][2, 5, 8] Following is the code in java: public class MaxSeqRecursive { private static int num = 5; private static int[] inputArry = new int[] { 1,3,9,20,7 }; private static Object[] outArry; private static int maxSum = 0; public static void main(String[] args) { List<Integer> output = new ArrayList<Integer>(); output.add(1); convert(output, -1); for (int i = 0; i < outArry.length; i++) { System.out.print(outArry[i] + ":"); } System.out.print(maxSum); } public static void convert( List<Integer> posArry, int prevValue) { int currentValue = -1; if (posArry.size() == 0) { if (prevValue == 2) { return; } else { posArry.add(2); prevValue = -1; } } currentValue = (int) posArry.get(posArry.size() - 1); if (currentValue == num || currentValue == num - 1) { updateMax(posArry); prevValue = (int) posArry.get(posArry.size() - 1); posArry.remove(posArry.size() - 1); } else { int returnIndx = getNext(posArry, prevValue); if (returnIndx == -2) return; if (returnIndx == -1) { prevValue = (int) posArry.get(posArry.size() - 1); posArry.remove(posArry.size() - 1); } else { posArry.add(returnIndx); prevValue = -1; } } convert(posArry, prevValue); } public static int getNext( List<Integer> posArry, int prevValue) { int currIndx = posArry.size(); int returnVal = -1; int value = (int) posArry.get(currIndx - 1); if (prevValue < num) { if (prevValue == -1) returnVal = value + 2; else if (prevValue - value < 3) returnVal = prevValue + 1; else returnVal = -1; } if (returnVal > num) returnVal = -1; return returnVal; } public static void updateMax(List posArry) { int sum = 0; for (int i = 0; i < posArry.size(); i++) { sum = sum + inputArry[(Integer) posArry.get(i) - 1]; } if (sum > maxSum) { maxSum = sum; outArry = posArry.toArray(); } } } Time complexity: O( number of patterns to be compared)
Another Java Implementation ( runs in linear time ) public class MaxSum { private static int ofNonConsecutiveElements (int... elements) { int maxsofar,maxi2,maxi1; maxi1 = maxsofar = elements[0]; maxi2 = 0; for (int i = 1; i < elements.length; i++) { maxsofar = Math.max(maxi2 + elements[i], maxi1); maxi2 = maxi1; maxi1 = maxsofar; } return maxsofar; } public static void main(String[] args) { System.out.println(ofNonConsecutiveElements(6, 4, 2, 8, 1)); } }
My solution is O(N) time and O(1) space. private int largestSumNonConsecutive(int[] a) { return largestSumNonConsecutive(a, a.length-1)[1]; } private int[] largestSumNonConsecutive(int[] a, int end) { //returns array largest(end-1),largest(end) if (end==0) return new int[]{0,a[0]}; int[] largest = largestSumNonConsecutive(a, end-1); int tmp = largest[1]; largest[1] = Math.max(largest[0] + a[end], largest[1]); largest[0] = tmp; return largest; }
int nonContigousSum(vector<int> a, int n) { if (n < 0) { return 0; } return std::max(nonContigousSum(a, n - 1), nonContigousSum(a, n - 2) + a[n]); } this is the recursive approach with the help of which we can solve this question (OPTIMAL SUB-STRUCTURE HALLMARK OF DYNAMIC PROGRAMMING. Here we are considering two cases, in first we exclude a[n] and in the second we include a[n] and return the max of those sub cases found. We are basically finding all the subsets of the array and returning the length of the non-contiguous array with max sum. Use tabulation or memoization for avoiding same sub-problems.
A penny from me. public class Problem { /** * Solving by recursion, top down approach. Always try this recursion approach and then go with * iteration. We have to add dp table to optimize the time complexity. */ public static int maxSumRecur(int arr[], int i) { if(i < 0) return 0; if(i == 0) return arr[0]; if(i == 1) return Math.max(arr[0], arr[1]); int includeIthElement = arr[i] + maxSumRecur(arr, i-2); int excludeIthElement = maxSumRecur(arr, i-1); return Math.max(includeIthElement, excludeIthElement); } /** * Solving by iteration. Bottom up approach. */ public static void maxSumIter(int arr[]) { System.out.println(Arrays.toString(arr)); int dp[] = new int[arr.length]; dp[0] = arr[0]; dp[1] = Math.max(arr[0], arr[1]); for(int i=2; i <= arr.length - 1; i++) { dp[i] = Math.max(arr[i] + dp[i-2], dp[i-1]); } System.out.println("Max subsequence sum by Iteration " + dp[arr.length - 1] + "\n"); } public static void maxSumRecurUtil(int arr[]) { System.out.println(Arrays.toString(arr)); System.out.println("Max subsequence sum by Recursion " + maxSumRecur(arr, arr.length - 1) + "\n"); } public static void main(String[] args) { maxSumRecurUtil(new int[]{5, 5, 10, 100, 10, 5}); maxSumRecurUtil(new int[]{20, 1, 2, 3}); maxSumIter(new int[]{5, 5, 10, 100, 10, 5}); maxSumIter(new int[]{20, 1, 2, 3}); } }
Make a list of numbers that is the odd or even sums corresponding to each number so far; e.g. for input of [1,2,4,1,2,3,5,3,1,2,3,4,5,2] the odd-even sums would be [1,2,5,3,7,6,12,9,13,11,16,15,21,17] Now walk the list backwards greedily summing but skipping those elements whose odd/even sum is less than that of next-to-be-considered element. src = [1,2,4,1,2,3,5,3,1,2,3,4,5,2] odd_even_sums = src[:2] for i in xrange(2,len(src)): odd_even_sums.append(src[i] + odd_even_sums[i-2]) best = [] for i in xrange(len(src)-1,-1,-1): if i == 0: best.append(i) elif odd_even_sums[i-1] > odd_even_sums[i]: pass elif odd_even_sums[i-1] == odd_even_sums[i]: raise Exception("an exercise for the reader") else: best.append(i) best.reverse() print "Best:",",".join("%s=%s"%(b,src[b]) for b in best) print "Scores:",sum(odd_even_sums[b] for b in best) Outputs: Best: 0=1,1=2,2=4,4=2,6=5,8=1,10=3,12=5 Scores: 77
public static int findMaxSum(int[] a){ int sum0=0; //will hold the sum till i-2 int sum1=0;//will hold the sum till i-1 for(int k : a){ int x=Math.max(sum0+k, sum1);//max(sum till (i-2)+a[i], sum till (i-1)) sum0=sum1; sum1=x; } return sum1; } Below is the crux of algorithm: max(max sum till (i-2)+a[i], max sum till (i-1)) O(N) time complexity and O(1) space complexity.
A rather naive yet complete implementation. Recursion equation is T(n) = n^2 + nT(n-3), which if I'm not wrong leads to exponential time. The (n-3) comes from the fact a number cannot add with itself/previous/next numbers. The program reports the constituent list that makes up the sum (there are multiple, exponentially growing, of these lists, but it just picks one). import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; public class MaxSumNoAdjacent { private static class Sum { int sum; List<Integer> constituents = new ArrayList<>(); Sum(int sum, List<Integer> constituents) { this.sum = sum; this.constituents = constituents; } #Override public String toString() { return "sum: " + sum + " " + constituents.toString(); } } public static Sum maxSum(int[] arr) { List<Integer> input = new ArrayList<>(); for (int i : arr) { if (i != Integer.MIN_VALUE) { //Integer.MIN_VALUE indicates unreachability input.add(i); } } if (input.size() == 0) { return null; } if (input.size() == 1) { List<Integer> constituents = new ArrayList<>(); constituents.add(input.get(0)); return new Sum(input.get(0), constituents); } if (input.size() == 2) { int max = Math.max(input.get(0), input.get(1)); List<Integer> constituents = new ArrayList<>(); constituents.add(max); return new Sum(max, constituents); } Map<Integer, int[]> numberAndItsReachability = new HashMap<>(); for (int i = 0; i < input.size(); i++) { int[] neighbours = new int[input.size()]; if (i > 0) { neighbours[i-1] = Integer.MIN_VALUE; //unreachable to previous } if (i < input.size()-1) { neighbours[i+1] = Integer.MIN_VALUE; //unreachable to next } neighbours[i] = Integer.MIN_VALUE; //unreachable to itself for (int j = 0; j < neighbours.length; j++) { if (neighbours[j] == 0) { neighbours[j] = input.get(j); //remember values of reachable neighbours } } numberAndItsReachability.put(input.get(i), neighbours); } Sum maxSum = new Sum(Integer.MIN_VALUE, null); for (Entry<Integer, int[]> pair : numberAndItsReachability.entrySet()) { Sum sumMinusThisNumber = maxSum(pair.getValue()); //call recursively on its reachable neighbours if (sumMinusThisNumber != null) { int candidateSum = sumMinusThisNumber.sum + pair.getKey(); if (maxSum.sum < candidateSum) { sumMinusThisNumber.constituents.add(pair.getKey()); maxSum = new Sum(candidateSum, sumMinusThisNumber.constituents); } } } return maxSum; } public static void main(String[] args) { int[] arr1 = {3,2,5,10,7}; int[] arr2 = {3,2,7,10}; int[] arr3 = {5,5,10,40,50,35}; int[] arr4 = {4,4,4,4}; System.out.println(maxSum(arr1).toString()); System.out.println(maxSum(arr2).toString()); System.out.println(maxSum(arr3).toString()); System.out.println(maxSum(arr4).toString()); } }
Here is a C# version for reference (you may refer to: http://dream-e-r.blogspot.com/2014/07/maximum-sum-of-non-adjacent-subsequence.html): In-order to solve a problem using dynamic programming there should be a solution which has optimal substructure and overlapping sub problems properties. And the current problem has optimal substructure property. Say, f(i) is defined as maximum subsequence sum of non adjacent elements for 'i' items, then f( i) = 0 if i = 0 max (f(i-1), f(i-2) + a[i]) Below is the algorithm for the same (no te it can solved without the encapsulating data in 'record' - i just preferred it this way) - which should illustrate the above idea: int FindMaxNonAdjuscentSubsequentSum(int[] a) { a.ThrowIfNull("a"); if(a.Length == 0) { return 0; } Record r = new Record() { max_including_item = a[0], max_excluding_item = 0 }; for (int i = 1; i < a.Length; i++) { var t = new Record(); //there will be only two cases //1. if it includes the current item, max is maximum of non adjuscent sub //sequence sum so far, excluding the last item t.max_including_item = r.max_excluding_item + a[i]; //2. if it excludes current item, max is maximum of non adjuscent subsequence sum t.max_excluding_item = r.Max; r = t; } return r.Max; } Unit Tests [TestMethod] [TestCategory(Constants.DynamicProgramming)] public void MaxNonAdjascentSubsequenceSum() { int[] a = new int[] { 3, 2, 5, 10, 7}; Assert.IsTrue(15 == this.FindMaxNonAdjuscentSubsequentSum(a)); a = new int[] { 3, 2, 5, 10 }; Assert.IsTrue(13 == this.FindMaxNonAdjuscentSubsequentSum(a)); a = new int[] { 5, 10, 40, 50, 35 }; Assert.IsTrue(80 == this.FindMaxNonAdjuscentSubsequentSum(a)); a = new int[] { 1, -1, 6, -4, 2, 2 }; Assert.IsTrue(9 == this.FindMaxNonAdjuscentSubsequentSum(a)); a = new int[] { 1, 6, 10, 14, -5, -1, 2, -1, 3 }; Assert.IsTrue(25 == this.FindMaxNonAdjuscentSubsequentSum(a)); } where public static int Max(int a, int b) { return (a > b) ? a : b; } class Record { public int max_including_item = int.MinValue; public int max_excluding_item = int.MinValue; public int Max { get { return Max(max_including_item, max_excluding_item); } } }
public static int maxSumNoAdj(int[] nums){ int[] dp = new int[nums.length]; dp[0] = Math.max(0, nums[0]); // for dp[0], select the greater value (0,num[0]) dp[1] = Math.max(nums[1], Math.max(0, dp[0])); int maxSum = Math.max(dp[0], dp[1]); for(int i = 2; i < nums.length; i++){ int ifSelectCurrent = Math.max(nums[i] + dp[i-2], dp[i-2]);// if select, there are two possible int ifNotSelectCurrent = Math.max(dp[i-1], dp[i-2]); // if not select, there are two posible dp[i] = Math.max(ifSelectCurrent, ifNotSelectCurrent); // choose the greater one maxSum = Math.max(dp[i], maxSum); // update the result } return maxSum; } public static void main(String[] args) { int[] nums = {-9, 2, 3, -7, 1, 1}; System.out.println(maxSumNoAdj(nums)); }