I'm looking to return the nth largest data value in the subtree rooted at this node in a BST that can have duplicate values.
Right now I have this but it doesn't seem to work
static int get(BSTNode node, int n) {
if ( node == null || n < 0)
return 0;
if ( n == 0)
return node.data;
get(node.right, n--);
get(node.left, n--);
return 0;
}
This is my Node class
private int data; // key
private int dataCount; // no. of keys with equal value stored at this node
private int treeSize; // no. of keys stored in the subtree rooted at this node
private BSTNode leftChild;
private BSTNode rightChild;
I have implemented an iterative solution.
Idea
The basic idea is to always go as far to the right as possible as the largest values will be there as long as a node has not yet been counted against n and save all values along the path we go in a stack.
If we can't go to the right anymore and we've not counted this node against n already we've found the largest node in the tree that we have not accounted for yet. So in that case we can account for that value by getting the node from the stack, decrementing n and adding that node to the set of nodes we've already accounted for. To find the next largest value we'll have to check if the current node has a left subtree and if it has, we need to continue there. If it does not we need to go back to our parent node which we do by getting the next value from the stack. This parent node will then automatically be the next largest value.
Here an illustration of those two scenarios possible when being at the largest value that has not been accounted for.
We're at the largest node that has not been accounted for and it has no left node, so we need to go back to the parent (using the stack) which will be the next largest node.
Parent
/ \
/ \
x largest
Alternatively: there is a left subtree, we need to examine first.
Parent
/ \
/ \
x largest
/
/
left subtree
/ \
/ \
Implementation
/**
* Finds the n-th largest key in a given subtree.
* #param root root node to start from
* #param n n-th largest key to get, n =< 1 will result in the largest key being returned
* #return returns n-th larges value, if n <= 1 return largest value
*/
public Node findNthLargestKey(Node root, int n){
if(root == null) return null;
if(n < 1) n = 1;
// early out: if you know how many nodes you have in the tree return if n is bigger than that
// based on number of nodes compared to n some assumptions could be made to speed up algorithm
// e.g. n == number of nodes -> get to left-most node and return it
// if n is close to number of nodes also probably start in left branch instead of all the way on the right side at the biggest node
var stack = new Stack<Node>();
// remember nodes that have been visited and have been counted against n
var done = new HashSet<Integer>();
// start at root node
stack.add(root);
// continue as long as the stack is not empty, if it is n was to big and the n-th largest value could not be found
while (!stack.empty()){
// pop next value from the stack (will be root in first iteration)
current = stack.pop();
// always try to go as far to the right as possible to get the biggest value that has not yet been counted against n
while (current != null && !done.contains(current.getKey())){
stack.add(current);
current = current.getRight();
}
// if we are here we've found the biggest value that has not yet been counted against n
var prev = stack.pop();
// if we have found the nth biggest value return
if(--n == 0){
return prev;
}
// otherwise mark this node as done and counted against n
done.add(prev.getKey());
// if node has a left successor, visit it first as this node has no right successors that have not been counted against n already
if(prev.getLeft() != null) stack.add(prev.getLeft());
}
// n-th largest value was not found (n was too big)
return null;
}
My Node looks like this with getters and setters defined of course. But the implementation will also work for your node, as the number of nodes with same value are irrelevant to find the n-th largest node. And even if they were not , the same algorithm would work but then you would need to decrement by the number of nodes with same value and the condition would need to be adjusted to n <= 0 to return.
public class Node {
private int key;
private Node right;
private Node left;
private Object anyData;
public Node(int key) {
this(key, null);
}
public Node(int key, Object anyData) {
this.key = key;
this.anyData = anyData;
this.left = null;
this.right = null;
}
}
Test
I've tested my implementation against random trees and the results have always been correct. This Test class however only checks results for the root node to be able to test the method for every node in the tree. I've additionally also run some test where n > number of nodes in tree which always has to return null for not found and for smaller subtrees.
public class Test {
public static void main(String[] args){
// values to insert into the tree
int[] curVals = fillArrayRand(20, 1, 200);
// Create tree
BinarySearchTree tree = new BinarySearchTree();
System.out.println("Tree has been created.");
// fill tree
for (var cur: curVals) {
tree.insertIter(new Node(cur));
}
// print values in order of insertion, first value will be the root value
System.out.println("Tree was filled with the following values: %s".formatted(Arrays.toString(curVals)));
// print tree in using inorder traversal
tree.printRec(Traversal.INORDER);
var sorted = Arrays.stream(curVals).sorted().distinct().toArray();
// always start at root node; which is the first node that gets inserted
// find() returns a given node by key
var startNode = tree.find(curVals[0]);
// now loop over sorted values (sorted in ascending order -> nth largest is at position n - i in the sorted array)
for (int i = 0; i < sorted.length; i++) {
var result = tree.findNthLargestKey(startNode, sorted.length - i);
// if key in i-th position of sorted array is the same as the key of result => success
// if result is null, the node was not found (should not happen here as sorted.length - i is never > sorted.length)
System.out.printf("#%d largest value:\t%d (expected)\t-\t%s (result)\t", sorted.length - i, sorted[i], result == null ? "not found": result.getKey());
if (result != null && sorted[i] == result.getKey()) {
System.out.println("SUCCESS");
} else System.out.println("FAILED");
}
}
public static int[] fillArrayRand(int size, int randStart, int randEnd){
int[] randArray = new int[size];
for(int i = 0; i < size; i++){
randArray[i] = (int)( (randEnd - randStart) * Math.random() + randStart);
}
return randArray;
}
}
Expected output
Tree has been created.
Tree was filled with the following values: [148, 65, 18, 168, 8, 148, 194, 186, 114, 22, 102, 51, 123, 169, 68, 118, 37, 18, 26, 18]
((((n,8,n),18,(n,22,(((n,26,n),37,n),51,n))),65,(((n,68,n),102,n),114,((n,118,n),123,n))),148,(n,168,(((n,169,n),186,n),194,n)))
#17 largest value: 8 (expected) - 8 (result) SUCCESS
#16 largest value: 18 (expected) - 18 (result) SUCCESS
#15 largest value: 22 (expected) - 22 (result) SUCCESS
#14 largest value: 26 (expected) - 26 (result) SUCCESS
#13 largest value: 37 (expected) - 37 (result) SUCCESS
#12 largest value: 51 (expected) - 51 (result) SUCCESS
#11 largest value: 65 (expected) - 65 (result) SUCCESS
#10 largest value: 68 (expected) - 68 (result) SUCCESS
#9 largest value: 102 (expected) - 102 (result) SUCCESS
#8 largest value: 114 (expected) - 114 (result) SUCCESS
#7 largest value: 118 (expected) - 118 (result) SUCCESS
#6 largest value: 123 (expected) - 123 (result) SUCCESS
#5 largest value: 148 (expected) - 148 (result) SUCCESS
#4 largest value: 168 (expected) - 168 (result) SUCCESS
#3 largest value: 169 (expected) - 169 (result) SUCCESS
#2 largest value: 186 (expected) - 186 (result) SUCCESS
#1 largest value: 194 (expected) - 194 (result) SUCCESS
Note: the output of the line with all the parenthesis is the output of the inorder traversal where (left node, parent, right node) and n means null for i. e. no node. The first node that gets inserted is the root node, so it's best to start to read the output from there.
Correctness
I should be possible using a loop invariant and induction to proof the algorithm is correct and produces the expected result for every correct binary search tree and input. The loop variant (informally) would be after an iteration i of the outer while loop, we have found the i-th largest node in the tree for 1 <= i <= n. I have not done that here, but using the idea that should be straightforward.
Effectiveness
I have not done a complexity analysis but it is obvious that the best case e.g. root node is largest value and we want the largest value the complexity is O(1). In worst case it will be O(n) no matter which node we search for. The algorithm could certainly be improved for some inputs i. e. if we have n close to the number of nodes in the tree, meaning we are searching for a small value. In that case it will be faster to start from the left-most node which is the smallest and search for the (number of nodes - n)-th smallest value. If you were to implement something like this you could certainly greatly improve the average case runtime.
When dealing with recursive data structures, you can often expect some recursive code to work on them. This case is not an exception either, just the recursive parts will need some dirtiness.
Let's use the 3-element BSTs for designing, labeled with insertion-order:
123 132 213,231 312 321
1 1 2 3 3
\ \ / \ / /
2 3 1 3 1 2
\ / \ /
3 2 2 1
Finding the largest element is easy:
just go to the right as long as you can
You will bump into 3, no matter what level it is.
Finding the second largest element is the revealing part:
going to the right as long as it's possible still seems to be a good start
then we look at where we are:
if it's a leaf node, return to the parent, and that will be the one (123,213,231 cases)
if there's a left-child, check that one, but as the 312 case in particular shows, "checking" the left-child actually means step 1, so again go to the right as long as it's possible.
The recursion is somewhat found, and these really are the steps we need for larger cases too. It's also somewhat seen that when we are going to the right, the "nth-ness" of the next number we will check doesn't change. It starts changing only when we are stepping to the left (132,312,321), or returning to a previous level (123,213,231).
The dark part is that we have to track this counter somehow. We found the answer when it reaches 0 (so starting this algorithm with n=0 finds the largest element), and after that (when n goes negative) it will just return the value it got from recursion.
First here is a JavaScript PoC, using a bit dirty hacks, like if a member variable doesn't exist at all yet, it still can be checked (the if(this.left) things), and the counter is a one-element array (so it can be modified across the recursive calls), the method is called as root.nthback([i]), where the [i] is an array literal. Also, the method doesn't bother returning anything when the element doesn't exist, that produces the two undefineds at the end of the test output. These shortcuts will be addressed in the Java variant at the end of this post.
The example input was just taken from the other answer, on top of their availability, they have some repeats too.
const init = [148, 65, 18, 168, 8, 148, 194, 186, 114, 22, 102, 51, 123, 169, 68, 118, 37, 18, 26, 18];
class Node {
nthback(n) {
if (this.right) {
let res = this.right.nthback(n);
if (n[0] < 0)
return res;
}
if (n[0]-- === 0)
return this.value;
if (this.left) {
let res = this.left.nthback(n);
if (n[0] < 0)
return res;
}
}
constructor() {
this.value = NaN;
}
add(value) {
if (isNaN(this.value)) {
this.value = value;
} else if (value < this.value) {
if (!this.left)
this.left = new Node;
this.left.add(value);
} else {
if (!this.right)
this.right = new Node;
this.right.add(value);
}
}
walk() {
let result = "";
if (this.left)
result = this.left.walk() + ",";
result += this.value;
if (this.right)
result += "," + this.right.walk();
return result;
}
}
const root = new Node;
for (const value of init)
root.add(value);
console.log(root.walk());
for (let i = 0; i < 22; i++)
console.log(root.nthback([i]));
So the actual magic is quite short and also symmetric:
nthback(n) {
if (this.right) { // 1
const res = this.right.nthback(n); // 2
if (n[0] < 0) // 3
return res;
}
if (n[0]-- === 0) // 4
return this.value;
if (this.left) { // 5
const res = this.left.nthback(n);
if (n[0] < 0)
return res;
}
}
If there is something on the right (1), it has to be checked (2), and if the counter is negative afterwards (3), the result we got back is the actual result of the entire call, so we pass it back.
If we are still in the method, (4) is where we check if the counter is exactly 0, because then this node has the actual result, which we can return. It's worth to remember that the n[0]-- part decrements the counter regardless of the outcome of the comparison. So if n[0] was 0 initially, it will become -1 and we return this.value;. If it was something else, it just gets decremented.
(5) does the (1)-(2)-(3) part for the left branch. And the hidden JavaScript thing is that we don't have to return anything. But the (4)-(5) parts will change when using your complete structure anyway.
Adding subtree-size allows early return if the incoming counter is simply larger than the size of the entire subtree: we just decrement the counter by the size, and return, the element is somewhere else. And this also means that when we don't return, the result is in the subtree, so we check the possible right branch, then ourselves, and if we are still inside the method, we don't even have to check if we have a left branch, because we do have it for sure, and it does contain the result, also for sure. So (5) will simply become a direct return this.left.nthback(n);. Which is quite a simplification.
Tracking multiplicity affects (4): instead of checking for 0, we will have to check if the counter is less than the multiplicity, and also, instead of decrementing the counter by 1, we have to subtract the actual multiplicity from it.
const init = [148, 65, 18, 168, 8, 148, 194, 186, 114, 22, 102, 51, 123, 169, 68, 118, 37, 18, 26, 18];
class Node {
nthback(n) {
if (this.size <= n[0]) {
n[0] -= this.size;
return 0;
}
if (this.right) {
let res = this.right.nthback(n);
if (n[0] < 0)
return res;
}
if (n[0] < this.count) {
n[0] -= this.count;
return this.value;
}
n[0] -= this.count;
return this.left.nthback(n);
}
constructor() {
this.value = NaN;
this.size = 0;
}
add(value) {
this.size++;
if (isNaN(this.value)) {
this.value = value;
this.count = 1;
} else if (value === this.value) {
this.count++;
} else if (value < this.value) {
if (!this.left)
this.left = new Node;
this.left.add(value);
} else {
if (!this.right)
this.right = new Node;
this.right.add(value);
}
}
walk() {
let result = "";
if (this.left)
result = this.left.walk() + ",";
result += this.value;
if (this.count > 1)
result += "x" + this.count;
result += "(" + this.size + ")";
if (this.right)
result += "," + this.right.walk();
return result;
}
}
const root = new Node;
for (const value of init)
root.add(value);
console.log(root.walk());
for (let i = 0; i < 22; i++)
console.log(root.nthback([i]));
So the final JavaScript variant could look like this:
nthback(n) {
if (this.size <= n[0]) { // 1
n[0] -= this.size;
return 0;
}
if (this.right) { // 2
let res = this.right.nthback(n);
if (n[0] < 0)
return res;
}
if (n[0] < this.count) { // 3
n[0] -= this.count; // 4
return this.value;
}
n[0] -= this.count; // 4
return this.left.nthback(n); // 5
}
Subtree-skipping happens in (1), by comparing subtree-size, and the target count we can immediately tell if the result is in this subtree or somewhere else. While JavaScript would allow a simple return; here, a 0 is produced instead, as it seems to be desired in the question.
The next step (2) is unchanged from the previous variant, looks exactly same, does exactly same.
(3) had to be taken apart. While the post-decrement operator was helpful previously, there's no post--= operator, so it happens for both outcomes of the comparison (4). Also, we don't compare === 0 as before, but we compare < this.count instead. By the way, we could have done that in the previous case too if we really wanted to, there this.count was always 1, so < 1 could have done the thing (the counter is never negative at this point, that can only happen in the left/right returns). In fact if we really-really wanted to, we could do the subtraction prior to the comparison, and instead of the current 0<=n fact and n<count check, we could shift the comparison downwards, knowing that now -count<=n and checking for n<0.
(5) became the one-liner as promised. If we reach this point, the result is unconditionally inside the left-branch, which unconditionally exists.
Making it Java
The simplest part is the array-trick: there could be a public method to call, accepting an actual number, and then it could wrap it into an array, and call a private method doing the actual job. Also changed the variable names to the ones you have:
public int nthback(int n) {
return nthback(new int[] { n });
}
private int nthback(int[] n) {
if (treeSize <= n[0]) {
n[0] -= treeSize;
return 0;
}
if (rightChild != null) {
int res = rightChild.nthback(n);
if (n[0] < 0)
return res;
}
if (n[0] < dataCount) {
n[0] -= dataCount;
return data;
}
n[0] -= dataCount;
return leftChild.nthback(n);
}
As you have private members, these methods have to reside in the same source file, and then they could just reside directly inside class BSTNode anyway.
Related
I'm learning how to traverse a tree by levels. The method I do should take the level of tree number and print back a nodes at the current level.
I watched this tutorial - "Print nodes at given Level", but still can't figure out how is recursion working in this concrete sample.
So please, help me to understand.
// Java program for Inserting a node
class GFG1 {
static class node {
int key;
node left, right;
}
static node newNode(int item)
{
node temp = new node();
temp.key = item;
temp.left = temp.right = null;
return temp;
}
// Function to insert a new node
static node insert(node node, int key)
{
// If the tree is empty, return a new node
if (node == null)
return newNode(key);
// Otherwise, recur down the tree
if (key < node.key) {
node.left = insert(node.left, key);
}
else if (key > node.key) {
node.right = insert(node.right, key);
}
return node;
}
static void printGivenLevel(node root, int level)
{
if (root == null)
return;
if (level == 1) {
System.out.print(" " + root.key);
}
else if (level > 1) {
printGivenLevel(root.left, level - 1);
printGivenLevel(root.right, level - 1);
}
}
public static void main(String[] args)
{
node root = null;
root = insert(root, 50);insert(root, 30);insert(root, 20);insert(root, 40);insert(root, 70);insert(root, 60);insert(root, 80);
printGivenLevel(root,2);
}
}
The method of traversal by levels is printGivenLevel:
So why it requires to write level-1 in printGivenLevel(root.left, level - 1) and not works with 'level +1', for example?
How do we come to the base condition, for example on the 3-th or 2-nd tier?
The tree looks like
50
30 70
20 40 60 80
level is an ambiguous term here, in the printGivenLevel it starts from the top "level", which is technically level 1, and we recurse down until we reach our desired "level".
Actual level "level" inside printGivenLevel(root, level)
1 50 3
2 30 70 2
3 20 40 60 80 1
Let's see how printGivenLevel(root, 2) executes, renaming printGivenLevel as print:
1. print(root, 2) -> print(root.left(30), 1) and print(root.right(70), 1)
2. print(root(30), 1) -> we are at "level 1", print the value 30.
3. print(root(70), 1) -> we are at "level 1", print the value 70.
The base condition always reaches when "level" becomes equal to 1, we start counting down from the top level, and when we reach our desired level, the "level" count becomes 1 and we print the value of the node.
So why it requires to write level-1 in printGivenLevel(root.left, level - 1) and not works with 'level +1', for example? How do we come to the base condition, for example on the 3-th or 2-nd tier?
If level is always increased, it'll keep on increasing and won't reach level 1, no value would be printed and it'd result in stackoverflow.
I was asked to code for the following problem:
Problem Description:
Given a linked list, find the number of duplicate elements in the
list.
Input Format:
First line contains an integer N - The number of nodes.
Second line contains N integers - Node values.
Output Format:
Print the total number of duplicates.
Constraints:
N <= 10^5
Value of node <= 10^6
Sample Input:
9
1 2 3 4 4 5 6 6 6
Sample Output:
3
Explanation:
In the given test case we have 3 duplicates i.e. one 4 and two 6.
My code:
import crio.ds.List.*;
/*public class ListNode {
public int val;
public ListNode next;
public ListNode(int x) { val = x; next = null; }
}*/
public class Solution {
public int countDuplicatesInALinkedList(ListNode head){
int counter = 0;
while(head.next != null){
ListNode ptr = head.next;
while(ptr != null){
if(head.val == ptr.val){
counter++;
break;
}
ptr = ptr.next;
}
head = head.next;
}
return counter;
}
}
I want to understand why my code is failing the edge case.
When the head-node is null your code will produce a NullPointerException when it enters the outer while-loop (while evaluating the condition head.next != null, which will fail if head is null).
Also, your solution is inefficient. You're checking every value against all others values in the list and takes a quadratic time O(n^2) to run.
This problem can be solved in a liner time O(n), in a single pass through the list and fewer code.
For that, you can utilize a HashSet which will store every previously encountered value. If an offered value rejected by the set, i.e. seen.add() returns false, that means this value is a duplicate.
public int countDuplicatesInALinkedList(ListNode head) {
Set<Integer> seen = new HashSet<>();
int counter = 0;
ListNode current = head;
while (current != null) {
if (!seen.add(current.val)) { // value has been rejected - i.e. it's a duplicate
counter++;
}
current = current.next;
}
return counter;
}
Sidenote: it's not considered to be a good practice to modify objects received as method parameters.
Problem 31
In England the currency is made up of pound, £, and pence, p, and
there are eight coins in general circulation: 1p, 2p, 5p, 10p, 20p,
50p, £1 (100p) and £2 (200p). It is possible to make £2 in the
following way: 1×£1 + 1×50p + 2×20p + 1×5p + 1×2p + 3×1p How many
different ways can £2 be made using any number of coins?
static int[] nums = {200,100,50,20,10,5,2,1};
static int size = nums.length;
static HashMap<Integer,Integer> pivots = new HashMap<>();
public static int checkSum(HashMap<Integer,Integer> pivots){
int target = 200;
int sum = 0;
for(Integer key: pivots.keySet()){
int pivot = pivots.get(key);
sum += nums[pivot];
if(sum > target) return 1;
}
if(sum < target) return -1;
return 0;
}
public static void shift(HashMap<Integer,Integer> pivots, int pivot_node){
if(pivots.size() + nums[pivots.get(1)] == 201 && pivots.get(1) != 0){
int p_1_value = pivots.get(1); //this part checks whether the current node(which is the first node)
//has reached children of all 1.
//Which means it's time to shift the root node.
pivots.clear();
pivots.put(1 , p_1_value);
shift(pivots, 1);
return;
}
if(pivots.get(pivot_node) != size - 1) {
pivots.put(pivot_node, pivots.get(pivot_node) + 1);
}
else{
shift(pivots , pivot_node - 1);
}
}
public static void branch(HashMap<Integer,Integer> pivots){
pivots.put(pivots.size() + 1, pivots.get(pivots.size()));
}
public static int search(){
int bool = checkSum(pivots);
int n = 0;
int count = 0;
while(n < 25) {
count++;
if (bool == 0) {
n++; // if the sum is equal to 200, we shift the last
//pivot to the next lower number.
shift(pivots, pivots.size());
}else if (bool == -1) {
branch(pivots); //if the sum is less than 200, we make a new pivot with value of the last pivot.
}else if (bool == 1) {
shift(pivots, pivots.size()); //if the sum is greater than 200,
//we shift to the last pivot to the next lower number.
}
bool = checkSum(pivots);
}
return n;
}
public static void main(String[] args){
pivots.put(1,0);
int n = search();
System.out.print("\n\n------------\n\n"+ "n: " + n);
}
This is an algorithm that searches for combinations of a set that add up to a target. It's kind of like a depth first tree search without using a tree. Each pivot represents node on the "tree". The shift() method changes the value of the node to the next lower value. The branch() method creates a new node with the same value of the last node. The checkSum() method checks whether the sum of the pivots are <,= or > the target, 200.
The correct answer for the number of ways is supposed to be around 73000. But my algorithm only returns about 300 ways.
I have no idea why this happens because my algorithm should reach every single possible combination that equals 200.
This is a visualization of how my algorithm works:
Your search algorithm doesn't find all possible combinations of coins that make up £2 because you are only shifting the "last pivot" to the next lower number, when you should be considering the items before that last one too.
Your algorithm will find this combination:
100, 50, 20, 20, 5, 2, 2, 1
but not this:
100, 20, 20, 20, 10, 5, 2, 2, 1
The second combination does not have the value 50 in it, but your algorithm breaks down the coin values backwards to forwards only -i.e. it will never break down 50 until all the following "pivots" are 1. You can easily see that if you print your HashMap<Integer,Integer> pivots every time the counter n is incremented.
You could try to fix your code by amending it to shift() using not only the last pivot but all the distinct previous pivots too. However, doing so you will create a lot of duplicates, so you'll need to keep a list of the distinct found combinations.
Another way to solve problem 31 is by using Dynamic Programming. Dynamic programming is best when it comes to problems that can be broken down in smaller bits. For example the solution of the same problem but where
target = 2 can be used to solve the problem where target = 5, which can be used to solve the problem where target = 10 and so on.
Good luck!
While solving the problem that reverse first K elements of linked list i have written the below recursive code but the last iteration executing twice i.e for k=1, function call reverseNode() happening twice. Can any body why it happening like that. Please correct me if i did any thing wrong in it.
Example :
If input is
1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8
and k = 4 then output is
4 -> 3 -> 2 -> 1 -> 5 -> 6 -> 7 -> 8
public void reverseListRecursion(int k) {
this.reverseNode(null, headNode,k);
}
public void reverseNode(Node node, Node nextNode,int k) {
while (k > 0) {
k = k-1;
this.reverseNode(node, nextNode.next,k);
}
if (k == 0) {
this.kNode = nextNode;
this.kNode.next = null;
}
if (node == null) {
nextNode.next = this.kNode;
} else {
nextNode.next = node;
}
}
Working code for my logic it is working expected. but when i try to use variable "k" in "if" condition instead of "presentCounter" then it is going wrong. Can any body tell me the reason.
public void reverseListRecursion(int count) {
this.reverseNode(null, headNode, count);
System.out.println("\n");
this.display(this.headNode);
}
/*
* Condition K <= Length of linked list.
*/
public void reverseNode(Node node, Node nextNode, int k) {
int presentCounter = k;
if (k > 1) {
k = k - 1;
this.reverseNode(nextNode, nextNode.next, k);
}
if (presentCounter == 1) {
this.kNode = nextNode.next; // Saving K's Next Node
this.headNode = nextNode; // Setting K node as head node
}
if (node == null) {
nextNode.next = this.kNode;
} else
nextNode.next = node;
}
Your recursion should be
if (k > 0) { // and not while
k = k-1;
this.reverseNode(node, nextNode.next,k);
}
...
There are several issues with this code you provided in your question:
public void reverseListRecursion(int k) {
this.reverseNode(null, headNode,k);
}
public void reverseNode(Node node, Node nextNode,int k) {
while (k > 0) {
k = k-1;
this.reverseNode(node, nextNode.next,k);
}
if (k == 0) {
this.kNode = nextNode;
this.kNode.next = null;
}
if (node == null) {
nextNode.next = this.kNode;
} else {
nextNode.next = node;
}
}
The wile loop will make the number of recursive calls grow to k!. Maybe this was your intention, but it certainly would not make an efficient algorithm. The job can be done by moving k-1 nodes, so making k! calls is not so efficient.
The first argument (node) never changes: the recursive call just passes the same argument, and so the value you pass in the initial call (null) is what this argument will be in every call. The outcome of the last if condition will therefore always be the same. This doesn't look right.
The first if condition will always be true, because it is opposite to the while condition that precedes it. So there should be no need to make the test for k == 0.
The code within the first if block makes this.kNode a synonym for nextNode and then its next property is set to null. There should be no reason at all to set any next property to null. If anything, it will break the linked list.
In the second if block the next property of nextNode is set to ... nextNode (see previous point, which shows that this.kNode was made synonymous for nextNode). So now you have a self-referencing node, which really is something you'd never want to have. This code makes the first k+1 nodes self-referencing, thereby effectively detaching them from the original linked list.
The initial call is made with headNode as second argument. This variable is apparently a private member of the class you are in. However, after execution the reversal, headNode will still reference the node it referred to before the call. The name suggests it should point to the first node in the list, but since the reversal will move another node at the front, headNode will point to the wrong node after completion. There is no other variable or property you have that will point to the first node in the list after the reversal. this.kNode could have been it, but the statements this.kNode.next = null and nextNode.next = this.kNode are not things you would do with the first node of a list.
There are too many issues with this code to get a clear view on what you actually tried to do.
I would suggest to go for this algorithm, explained by example:
list = 1 2 3 4 5 6 7 8
k = 4
Move the node that follows the original first node to the head of the list
list = 2 1 3 4 5 6 7 8
k = 3
Move the node that follows the original first node to the head of the list
list = 3 2 1 4 5 6 7 8
k = 2
Move the node that follows the original first node to the head of the list
list = 4 3 2 1 5 6 7 8
k = 1
As k = 1 no more moves have to be made.
This is how your code would look:
public void reverseListRecursion(int k) {
// The reversal returns the node that took the first position
this.headNode = this.reverseNode(this.headNode, k, this.headNode);
};
public Node reverseNode(Node origFirstNode, int k, Node currFirstNode) {
// If 1 element needs to be reversed, there is nothing to do:
if (k <= 1) return currFirstNode;
// Move the node after the original first node before the current first node
Node movingNode = origFirstNode.next;
origFirstNode.next = movingNode.next;
movingNode.next = currFirstNode;
// The moved node is now the current first node. Repeat:
return this.reverseNode(origFirstNode, k-1, movingNode);
};
The nice thing about this solution is that the reverseNode method does not need to reference this.headNode, and so it can be used to reverse elements in the middle of the list as well. You could add this method which takes a node as second argument:
public void reverseListRecursionAfter(int k, Node afterNode) {
afterNode.next = this.reverseNode(afterNode.next, k, afterNode.next);
};
This will reverse the nodes following the given node.
Here is a live snippet, with the same code translated to JavaScript (just for demo):
// Node class
function Node(val, next) {
this.val = val;
this.next = next;
this.toString = function (cascade) {
if (!cascade || this.next === null) return '(' + this.val + ')';
if (this.next === this) return '(' + this.val + ')-loop';
return '(' + this.val + ')->' + this.next.toString(true);
}
}
// List class
function List() {
this.headNode = null;
this.reverseListRecursion = function(k) {
// The reversal returns the node that took the first position
this.headNode = this.reverseNode(this.headNode, k, this.headNode);
};
this.reverseNode = function(origFirstNode, k, currFirstNode) {
// If 1 element needs to be reversed, there is nothing to do:
if (k <= 1) return currFirstNode;
// Move the node after the original first node before the current first node
var movingNode = origFirstNode.next;
origFirstNode.next = movingNode.next;
movingNode.next = currFirstNode;
// The moved node is now the current first node. Repeat:
return this.reverseNode(origFirstNode, k-1, movingNode);
};
this.insert = function (arr) {
for (var i = arr.length - 1; i >= 0; i--) {
this.headNode = new Node(arr[i], this.headNode);
}
}
this.toString = function () {
return '{' + this.headNode.toString(true) + '}';
}
}
var output = [];
// Sample data
var list = new List();
list.insert([1, 2, 3, 4, 5, 6, 7, 8]);
output.push('before: ' + list);
// Make the reversal call
list.reverseListRecursion(4);
output.push('after: ' + list);
// Show result in snippet
document.write(output.join('<br>'));
I have been trying to write a recursive heapify method that turns an array of integers into a min-heap. The Main and Heap classes are shown below. Most of the array shown in Main is already a min-heap, but the subtree [11, 4, 5] is not a min-heap. However, the heapify function doesn't seem to reach that subtree. I can't figure out what the problem is, any help would be greatly appreciated.
public class Heap {
public Heap(int[] array) {
heap = array;
}
public void heapify() {
heapifyHelper(0);
}
public void heapifyHelper(int rootIndex) {
if(isLeafIndex(rootIndex)) {
return;
}
else {
int leftChildIndex = getLeftChildIndex(rootIndex);
int rightChildIndex = getRightChildIndex(rootIndex);
int leftChildValue = heap[leftChildIndex];
int rightChildValue = heap[rightChildIndex];
int rootValue = heap[rootIndex];
if(leftChildValue < rootValue && leftChildValue < rightChildValue) {
swap(rootIndex, leftChildIndex);
heapifyHelper(leftChildIndex);
heapifyHelper(rightChildIndex);
}
else if(rightChildValue < rootValue && rightChildValue < leftChildValue) {
swap(rootIndex, rightChildIndex);
heapifyHelper(leftChildIndex);
heapifyHelper(rightChildIndex);
}
}
}
public int getLeftChildIndex(int parentIndex) {
return 2 * parentIndex + 1;
}
public int getRightChildIndex(int parentIndex) {
return 2 * parentIndex + 2;
}
public int getParentIndex(int childIndex) {
if(childIndex == 0) {
throw new IllegalArgumentException("Cannot get the parent index of the root.");
}
else {
return (childIndex / 2) - 1;
}
}
public boolean isLeafIndex(int index) {
int leftIndex = getLeftChildIndex(index);
int rightIndex = getRightChildIndex(index);
if(leftIndex >= heap.length && rightIndex >= heap.length) {
return true;
}
else {
return false;
}
}
public void swap(int index1, int index2) {
int temp = heap[index1];
heap[index1] = heap[index2];
heap[index2] = temp;
}
public void printHeap() {
System.out.println(Arrays.toString(heap));
}
int[] heap;
}
public class Main {
public static void main(String[] args) {
int[] x = {0, 5, 2, 9, 11, 6, 12, 21, 32, 4, 5};
Heap heap = new Heap(x);
heap.printHeap();
heap.heapify();
heap.printHeap();
}
}
There are several problems in your heapifyHelper:
public void heapifyHelper(int rootIndex) {
if(isLeafIndex(rootIndex)) {
return;
}
else {
int leftChildIndex = getLeftChildIndex(rootIndex);
int rightChildIndex = getRightChildIndex(rootIndex);
int leftChildValue = heap[leftChildIndex];
int rightChildValue = heap[rightChildIndex];
What if leftChildIndex == heap.length - 1? Then rightChildValue will cause an ArrayIndexOutOfBoundsException.
int rootValue = heap[rootIndex];
if(leftChildValue < rootValue && leftChildValue < rightChildValue) {
swap(rootIndex, leftChildIndex);
heapifyHelper(leftChildIndex);
heapifyHelper(rightChildIndex);
}
else if(rightChildValue < rootValue && rightChildValue < leftChildValue) {
What if both children are equal, and smaller than the parent? In that case you don't swap at all.
swap(rootIndex, rightChildIndex);
heapifyHelper(leftChildIndex);
heapifyHelper(rightChildIndex);
}
}
}
And the reason why the subtree [11, 4, 5] isn't reached is because you only call heapifyHelper for the children if one of the children is smaller than the parent, but when you call heapifyHelper(1), the two children of the node 5 are 9 and 11, both larger than the root value. (Actually, you don't even call heapifyHelper(1), since heap[0]is already smaller than both its children.)
But rectifying that alone by unconditionally recurring (on the children that exist) doesn't make your heapify correct. If you recur from the root to the leaves, each value can bubble up at most one level. You must recur from the leaves to the root(1), and you need to sift the values down completely, not just one level.
If you only swap a value with one of its children, each position is considered at most twice. Once when comparing it to its parent, once when comparing it to its children. When you go from the root to the leaves, when you compare a position to its children, no position above it (no position with a smaller index, even) can ever be changed anymore.
So each value can bubble up at most one level. If the smallest element is below the direct children of root, root won't become the smallest element in the tree. If you start from the leaves (or rather the parents of the leaves), the values can bubble up as far as they need. But if you only swap a value with the smaller of its children (if that is smaller than the value), each value can still only bubble down one level, which still need not create a heap.
Let us consider the tree
7
/ \
/ \
2 6
/ \ / \
1 3 4 5
If you go from the root to the leaves, you swap 2 and 7 first, giving
2
/ \
/ \
7 6
/ \ / \
1 3 4 5
The top two levels are now a min-heap.
Then you treat the left subtree, and finally the right subtree, producing
2
/ \
/ \
1 4
/ \ / \
7 3 6 5
altogether. Now the bottom two levels are composed of min-heaps, but the heap property was destroyed in the level above. To make that a heap again, the 1 must be sifted up further (in this case, just one level).
If you go from the leaves to the root, you first treat the right subtree,
6
/ \
4 5
producing
4
/ \
6 5
for that, then the left subtree
2
/ \
1 3
producing
1
/ \
2 3
there. Both subtrees are now min-heaps. Altogether, you have
7
/ \
/ \
1 4
/ \ / \
2 3 6 5
Then you'd swap 7 and 1, producing
1
/ \
/ \
7 4
/ \ / \
2 3 6 5
Now the root is the smallest value, but the last swap destroyed the heap property of the left subtree. To make that a heap again, the 7 must be sifted down further.
So you need a siftDown method (and/or a siftUp method) that sifts a value down (up) as far as needed.
private void siftDown(int index) {
int leftChildIndex = getLeftChildIndex(index);
if (leftChildIndex >= heap.length) {
// a leaf, no further sifting down possible
return;
}
int rightChildIndex = getRightChildIndex(index);
if ((heap[leftChildIndex] < heap[index])
&& (rightChildIndex >= heap.length || heap[rightChildIndex] >= heap[leftChildIndex)) {
// left child is smallest or only, and smaller than parent
swap(index, leftChildIndex);
siftDown(leftChildIndex);
} else
// left child not smaller than parent, or right child exists and is smaller than parent
if (rightChildIndex < heap.length && heap[rightChildIndex] < heap[index]) {
swap(index, rightChildIndex);
siftDown(rightChildIndex);
}
// otherwise, this one has no smaller child, so no more sifting needed
}
Then a correct heapify would be
public void heapify() {
// last index that has a child:
int lastNonLeafIndex = heap.length/2 - 1;
for(int index = lastNonLeafIndex; index >= 0; --index) {
siftDown(index);
}
}
That works because if you have a (binary) tree where both of the subtrees are min-heaps, sifting down the root value constructs a min-heap:
If the root value is smaller than (or equal to) both its children, the entire tree is already a min-heap.
Otherwise, after the root value has been swapped with the smaller of its children (without loss of generality the left), the other subtree is unchanged, hence still a min-heap. And, since the left child was the smallest value in the left subtree before the swap, the value at the root is the smallest value in the entire tree after the swap. Swapping may have destroyed the min-heap property of the left child, though. But the left-left and the left-right subtrees have not been changed, so they are still min-heaps. And the new left subtree is smaller than the original tree, so by the induction hypothesis, sifting down its root value creates a min-heap from that. So after sifting down has finished, we have a tree with the smallest value at the root, both of whose subtrees are min-heaps, that is, a min-heap.
Since each leaf is trivially a min-heap, for each index processed in heapify, the subtree rooted at that index becomes a min-heap.
The alternative, using siftUp:
private void siftUp(int index) {
if (index == 0) return; // root, nothing to do
int parentIndex = getParentIndex(index); // see Note below
if (heap[index] < heap[parentIndex]) {
swap(index, parentIndex);
siftUp(parentIndex);
}
}
public void heapify() {
for(int index = 1; index < heap.length; ++index) {
siftUp(index);
}
}
The code for siftUp is much shorter than for siftDown, since only two nodes are involved here, and there is no need to check whether any child index falls outside the array. But the heapify is less efficient (see footnote (1)).
siftUp is the method used to insert a new value into a heap. So this one builds a heap by inserting all values (except the root value) into an existing min-heap [when siftUp(index) is called, the part of the array before index is already a min-heap].
Note: your getParentIndex is incorrect,
return (childIndex / 2) - 1;
says the parent of index 1 is -1, and the parent of index 3 is 0, correct is
return (childIndex - 1) / 2;
(1) Actually, you can proceed from the root to the leaves, if you sift each value up as far as needed. It's just more efficient to heapify going from the [parents of the] leaves to the root. If you go from the root to the leaves, at level k you have 2^k values that may need to bubble up k levels, which gives an O(n*log n) complexity for building the heap. If you proceed from the [parents of the] leaves upward, you have 2^(log n - 1 - k) values that may need to bubble down k levels, which gives a complexity of O(n) for building the heap.
So i think I figured out what the problem is.
Your heapify helper stops the minute you find a root where the root is smaller than leftChild and rightChild.
In running your case.. you reach a situation where root (5) is lesser than 11 and 9..But 11 is not heapified..
Many ways to fix this. That i leave to you.
EDIT
So heapify is ideally meant only to put the first element in the rootIndex in a correct place. Not to create a Heap.
If you want to create a correct Heap, you need to insert a new element and call heapify on every such insert.