Ce qui suit est une question d'entrevue.
Vous recevez un arbre binaire (pas nécessairement BST) dans lequel chaque nœud contient une valeur . Concevez un algorithme pour imprimer tous les chemins qui totalisent cette valeur . Notez qu'il peut s'agir de n'importe quel chemin dans l'arborescence - il ne doit pas nécessairement commencer à l'origine.
Bien que je puisse trouver tous les chemins dans l’arbre qui commencent à la racine ont la somme indiquée, je ne peux pas le faire pour les chemins qui ne commencent pas à la racine.
Bien, ceci est un arbre, pas un graphique. Donc, vous pouvez faire quelque chose comme ça:
Pseudocode:
global ResultList
function ProcessNode(CurrentNode, CurrentSum)
CurrentSum+=CurrentNode->Value
if (CurrentSum==SumYouAreLookingFor) AddNodeTo ResultList
for all Children of CurrentNode
ProcessNode(Child,CurrentSum)
Eh bien, cela vous donne les chemins qui commencent à la racine. Cependant, vous pouvez juste faire un petit changement:
for all Children of CurrentNode
ProcessNode(Child,CurrentSum)
ProcessNode(Child,0)
Vous aurez peut-être besoin d'y penser pendant une seconde (je suis occupé par d'autres choses), mais cela devrait fondamentalement utiliser le même algorithme enraciné à chaque nœud de l'arbre
EDIT: cela ne donne en réalité que le "noeud final". Cependant, comme il s'agit d'un arbre, vous pouvez simplement commencer à ces nœuds d'extrémité et remonter jusqu'à ce que vous obteniez la somme requise.
EDIT 2: et, bien sûr, si toutes les valeurs sont positives, vous pouvez interrompre la descente si votre somme actuelle est> = celle requise
Voici une réponse O(n + numResults)
(essentiellement identique à la réponse @ Quelqu'un, mais avec tous les problèmes résolus):
cumulativeSumBeforeNode
.cumulativeSumBeforeNode
(la valeur de cette clé sera une liste de nœuds).cumulativeSumBeforeNode
et la somme cible. Recherchez cette différence dans la table de hachage.cumulativeSumBeforeNode
dans la table de hachage.Code:
import Java.util.ArrayList;
import Java.util.Arrays;
import Java.util.HashMap;
import Java.util.HashSet;
import Java.util.List;
import Java.util.Map;
import Java.util.Set;
public class BinaryTreePathsWithSum {
public static void main(String[] args) {
BinaryTreeNode a = new BinaryTreeNode(5);
BinaryTreeNode b = new BinaryTreeNode(16);
BinaryTreeNode c = new BinaryTreeNode(16);
BinaryTreeNode d = new BinaryTreeNode(4);
BinaryTreeNode e = new BinaryTreeNode(19);
BinaryTreeNode f = new BinaryTreeNode(2);
BinaryTreeNode g = new BinaryTreeNode(15);
BinaryTreeNode h = new BinaryTreeNode(91);
BinaryTreeNode i = new BinaryTreeNode(8);
BinaryTreeNode root = a;
a.left = b;
a.right = c;
b.right = e;
c.right = d;
e.left = f;
f.left = g;
f.right = h;
h.right = i;
/*
5
/ \
16 16
\ \
19 4
/
2
/ \
15 91
\
8
*/
List<BinaryTreePath> pathsWithSum = getBinaryTreePathsWithSum(root, 112); // 19 => 2 => 91
System.out.println(Arrays.toString(pathsWithSum.toArray()));
}
public static List<BinaryTreePath> getBinaryTreePathsWithSum(BinaryTreeNode root, int sum) {
if (root == null) {
throw new IllegalArgumentException("Must pass non-null binary tree!");
}
List<BinaryTreePath> paths = new ArrayList<BinaryTreePath>();
Map<Integer, List<BinaryTreeNode>> cumulativeSumMap = new HashMap<Integer, List<BinaryTreeNode>>();
populateBinaryTreePathsWithSum(root, 0, cumulativeSumMap, sum, paths);
return paths;
}
private static void populateBinaryTreePathsWithSum(BinaryTreeNode node, int cumulativeSumBeforeNode, Map<Integer, List<BinaryTreeNode>> cumulativeSumMap, int targetSum, List<BinaryTreePath> paths) {
if (node == null) {
return;
}
addToMap(cumulativeSumMap, cumulativeSumBeforeNode, node);
int cumulativeSumIncludingNode = cumulativeSumBeforeNode + node.value;
int sumToFind = cumulativeSumIncludingNode - targetSum;
if (cumulativeSumMap.containsKey(sumToFind)) {
List<BinaryTreeNode> candidatePathStartNodes = cumulativeSumMap.get(sumToFind);
for (BinaryTreeNode pathStartNode : candidatePathStartNodes) {
paths.add(new BinaryTreePath(pathStartNode, node));
}
}
populateBinaryTreePathsWithSum(node.left, cumulativeSumIncludingNode, cumulativeSumMap, targetSum, paths);
populateBinaryTreePathsWithSum(node.right, cumulativeSumIncludingNode, cumulativeSumMap, targetSum, paths);
removeFromMap(cumulativeSumMap, cumulativeSumBeforeNode);
}
private static void addToMap(Map<Integer, List<BinaryTreeNode>> cumulativeSumMap, int cumulativeSumBeforeNode, BinaryTreeNode node) {
if (cumulativeSumMap.containsKey(cumulativeSumBeforeNode)) {
List<BinaryTreeNode> nodes = cumulativeSumMap.get(cumulativeSumBeforeNode);
nodes.add(node);
} else {
List<BinaryTreeNode> nodes = new ArrayList<BinaryTreeNode>();
nodes.add(node);
cumulativeSumMap.put(cumulativeSumBeforeNode, nodes);
}
}
private static void removeFromMap(Map<Integer, List<BinaryTreeNode>> cumulativeSumMap, int cumulativeSumBeforeNode) {
List<BinaryTreeNode> nodes = cumulativeSumMap.get(cumulativeSumBeforeNode);
nodes.remove(nodes.size() - 1);
}
private static class BinaryTreeNode {
public int value;
public BinaryTreeNode left;
public BinaryTreeNode right;
public BinaryTreeNode(int value) {
this.value = value;
}
public String toString() {
return this.value + "";
}
public int hashCode() {
return Integer.valueOf(this.value).hashCode();
}
public boolean equals(Object other) {
return this == other;
}
}
private static class BinaryTreePath {
public BinaryTreeNode start;
public BinaryTreeNode end;
public BinaryTreePath(BinaryTreeNode start, BinaryTreeNode end) {
this.start = start;
this.end = end;
}
public String toString() {
return this.start + " to " + this.end;
}
}
}
Basé sur la réponse de Christian ci-dessus:
public void printSums(Node n, int sum, int currentSum, String buffer) {
if (n == null) {
return;
}
int newSum = currentSum + n.val;
String newBuffer = buffer + " " + n.val;
if (newSum == sum) {
System.out.println(newBuffer);
}
printSums(n.left, sum, newSum, newBuffer);
printSums(n.right, sum, newSum, newBuffer);
printSums(n.left, sum, 0, "");
printSums(n.right, sum, 0, "");
}
printSums(root, targetSum, 0, "");
Voici une approche avec une complexité nlogn
.
Hashmap<CumulativeSum, reference to the corresponding node>
.SUM
.SUM-K
dans la HashMap
.HashMap
.Une solution propre en Java. Utilisation d'appels récursifs internes pour garder une trace des chemins traversés.
private static void pathSunInternal(TreeNode root, int sum, List<List<Integer>> result, List<Integer> path){
if(root == null)
return;
path.add(root.val);
if(sum == root.val && root.left == null && root.right == null){
result.add(path);
}
else if(sum != root.val && root.left == null && root.right == null)
return;
else{
List<Integer> leftPath = new ArrayList<>(path);
List<Integer> rightPath = new ArrayList<>(path);
pathSunInternal(root.left, sum - root.val, result, leftPath);
pathSunInternal(root.right, sum - root.val, result, rightPath);
}
}
public static List<List<Integer>> pathSum(TreeNode root, int sum) {
List<List<Integer>> result = new ArrayList<>();
List<Integer> path = new ArrayList<>();
pathSunInternal(root, sum, result, path);
return result;
}
Mise à jour: Je vois maintenant que ma réponse ne répond pas directement à votre question. Je le laisserai ici si cela s’avère utile, mais il n’est pas nécessaire de l’avancer. Si ce n'est pas utile, je vais l'enlever. Je suis d'accord avec @nhahtdh, cependant, quand il conseille, "Réutilisez votre algorithme avec tous les autres nœuds en tant que root."
On soupçonne que l'intervieweur est en train de pêcher récursion ici. Ne le déçois pas!
Avec un nœud, votre routine devrait s’appeler elle-même contre chacun de ses nœuds enfants, le cas échéant, puis ajouter la donnée propre du nœud aux valeurs de retour, puis renvoyer la somme.
Pour obtenir un crédit supplémentaire, avertissez l'intervieweur que votre routine peut échouer, en entrant une récursion sans fond et sans fin, si elle est utilisée sur un graphique général plutôt que sur un arbre binaire.
Nous pouvons le résoudre avec la programmation dynamique arborescente, et la complexité spatio-temporelle est égale à O (n ^ 2), où n est le nombre de tous les nœuds de l’arbre.
L'idée est la suivante:
Pour un nœud d’arbre, nous conservons un ensemble enregistrant toutes les sommes possibles, de u à tous ses descendants. Ensuite, de manière récursive, l’ensemble de nœuds peut être mis à jour par ses deux enfants, notamment en fusionnant les ensembles de deux enfants.
Le pseudocode est:
bool findPath(Node u, Set uset, int finalSum) {
Set lset, rset;
if (findPath(u.left, lset, finalSum) || findPath(u.right, rset, finalSum)) return true;
for (int s1 : lset) {
if (finalSum - u.val - s1 == 0 || rset.contains(finalSum - u.val - s1)) return true;
// first condition means a path from u to some descendant in u's left child
// second condition means a path from some node in u's left child to some node in u's right child
uset.insert(s1 + u.val); // update u's set
}
for (int s2 : rset) {
if (finalSum - u.val - s2 == 0) return true;
// don't forget the path from u to some descendant in u's right child
uset.insert(s2 + u.val); // update u's set
}
return false;
}
Je remarque que la question initiale est de trouver tous les chemins, mais l'algorithme ci-dessus consiste à déterminer s'il existe ou non. Je pense que l'idée est similaire, mais cette version rend le problème plus facile à expliquer :)
On peut réduire cet arbre à un graphe pondéré G, où chaque arête Pond = somme des valeurs dans chacun de ses nœuds.
Ensuite, exécutez l’algorithme de Floyd-Warshall sur le graphique G. En inspectant les éléments de la matrice résultante, vous pouvez obtenir toutes les paires de nœuds entre lesquelles la somme totale est égale à la somme souhaitée.
Notez également que le chemin le plus court donné par l'algorithme est également le seul chemin entre 2 nœuds de cet arbre.
Ceci est juste une autre approche, pas aussi efficace qu'une approche récursive.
public void printPath(N n) {
printPath(n,n.parent);
}
private void printPath(N n, N endN) {
if (n == null)
return;
if (n.left == null && n.right == null) {
do {
System.out.print(n.value);
System.out.print(" ");
} while ((n = n.parent)!=endN);
System.out.println("");
return;
}
printPath(n.left, endN);
printPath(n.right, endN);
}
Vous pouvez imprimer le chemin de l’arbre et le nœud n. comme ceci printPath (n);
void printpath(int sum,int arr[],int level,struct node * root)
{
int tmp=sum,i;
if(root == NULL)
return;
arr[level]=root->data;
for(i=level;i>=0;i--)
tmp-=arr[i];
if(tmp == 0)
print(arr,level,i+1);
printpath(sum,arr,level+1,root->left);
printpath(sum,arr,level+1,root->right);
}
void print(int arr[],int end,int start)
{
int i;
for(i=start;i<=end;i++)
printf("%d ",arr[i]);
printf("\n");
}
complexité (n logn) Complexité de l'espace (n)
Vous trouverez ci-dessous la solution utilisant la récursion . Nous effectuons une traversée dans l’ordre de l’arbre binaire, au fur et à mesure que nous descendons d’un niveau, nous additionnons le poids total du chemin en ajoutant le poids du niveau actuel aux poids des niveaux précédents du arbre, si nous atteignons notre somme, nous imprimons ensuite le chemin. Cette solution gérera les cas où nous pourrions avoir plus d'une solution le long d'un chemin d'accès donné.
Supposons que vous avez un arbre binaire enraciné à la racine.
#include <iostream>
#include <vector>
using namespace std;
class Node
{
private:
Node* left;
Node* right;
int value;
public:
Node(const int value)
{
left=NULL;
right=NULL;
this->value=value;
}
void setLeft(Node* left)
{
this->left=left;
}
void setRight(Node* right)
{
this->right = right;
}
Node* getLeft() const
{
return left;
}
Node* getRight() const
{
return right;
}
const int& getValue() const
{
return value;
}
};
//get maximum height of the tree so we know how much space to allocate for our
//path vector
int getMaxHeight(Node* root)
{
if (root == NULL)
return 0;
int leftHeight = getMaxHeight(root->getLeft());
int rightHeight = getMaxHeight(root->getRight());
return max(leftHeight, rightHeight) + 1;
}
//found our target sum, output the path
void printPaths(vector<int>& paths, int start, int end)
{
for(int i = start; i<=end; i++)
cerr<<paths[i]<< " ";
cerr<<endl;
}
void generatePaths(Node* root, vector<int>& paths, int depth, const int sum)
{
//base case, empty tree, no path
if( root == NULL)
return;
paths[depth] = root->getValue();
int total =0;
//sum up the weights of the nodes in the path traversed
//so far, if we hit our target, output the path
for(int i = depth; i>=0; i--)
{
total += paths[i];
if(total == sum)
printPaths(paths, i, depth);
}
//go down 1 level where we will then sum up from that level
//back up the tree to see if any sub path hits our target sum
generatePaths(root->getLeft(), paths, depth+1, sum);
generatePaths(root->getRight(), paths, depth+1, sum);
}
int main(void)
{
vector<int> paths (getMaxHeight(&root));
generatePaths(&root, paths, 0,0);
}
la complexité de l’espace dépend de la hauteur de l’arbre, alors qu’il s’agit d’un arbre équilibré, la complexité de l’espace est égale à 0 (log n) en fonction de la profondeur de la pile de récursion . Complexité temporelle O (n Log n) - basée sur un Arbre équilibré où il y a n nœuds à chaque niveau et à chaque niveau n travail sera fait (somme des chemins). Nous savons également que la hauteur de l’arbre est délimitée par O (log n) pour un arbre binaire équilibré. N quantité de travail effectuée pour chaque niveau sur un arbre binaire équilibré donne donc un temps d’exécution de O (n log n).
# include<stdio.h>
# include <stdlib.h>
struct Node
{
int data;
struct Node *left, *right;
};
struct Node * newNode(int item)
{
struct Node *temp = (struct Node *)malloc(sizeof(struct Node));
temp->data = item;
temp->left = NULL;
temp->right = NULL;
return temp;
}
void print(int p[], int level, int t){
int i;
for(i=t;i<=level;i++){
printf("\n%d",p[i]);
}
}
void check_paths_with_given_sum(struct Node * root, int da, int path[100], int level){
if(root == NULL)
return ;
path[level]=root->data;
int i;int temp=0;
for(i=level;i>=0;i--){
temp=temp+path[i];
if(temp==da){
print(path,level,i);
}
}
check_paths_with_given_sum(root->left, da, path,level+1);
check_paths_with_given_sum(root->right, da, path,level+1);
}
int main(){
int par[100];
struct Node *root = newNode(10);
root->left = newNode(2);
root->right = newNode(4);
root->left->left = newNode(1);
root->right->right = newNode(5);
check_paths_with_given_sum(root, 9, par,0);
}
Cela marche.....
Puisque nous avons besoin des chemins ayant la somme == k ..__, je suppose que la complexité maximale peut être O (total_paths_in_tree).
Alors pourquoi ne pas générer chaque chemin et vérifier la somme, c’est quand même un arbre avec des nombres négatifs et même pas un arbre de recherche binaire.
struct node{
int val;
node *left,*right;
node(int vl)
{
val = vl;
left = NULL;
right = NULL;
}
};
vector<vector<int> > all_paths;
vector<vector<int> > gen_paths(node* root)
{
if(root==NULL)
{
return vector<vector<int> > ();
}
vector<vector<int> > left_paths = gen_paths(root->left);
vector<vector<int> > right_paths = gen_paths(root->right);
left_paths.Push_back(vector<int> ()); //empty path
right_paths.Push_back(vector<int> ());
vector<vector<int> > paths_here;
paths_here.clear();
for(int i=0;i<left_paths.size();i++)
{
for(int j=0;j<right_paths.size();j++)
{
vector<int> vec;
vec.clear();
vec.insert(vec.end(), left_paths[i].begin(), left_paths[i].end());
vec.Push_back(root->val);
vec.insert(vec.end(), right_paths[j].begin(), right_paths[j].end());
paths_here.Push_back(vec);
}
}
all_paths.insert(all_paths.end(),paths_here.begin(),paths_here.end());
vector<vector<int> > paths_to_extend;
paths_to_extend.clear();
for(int i=0;i<left_paths.size();i++)
{
paths_to_extend.Push_back(left_paths[i]);
paths_to_extend[i].Push_back(root->val);
}
for(int i=0;i<right_paths.size();i++)
{
paths_to_extend.Push_back(right_paths[i]);
paths_to_extend[paths_to_extend.size()-1].Push_back(root->val);
}
return paths_to_extend;
}
Pour générer des chemins, j’ai généré tous les chemins de gauche et tous les chemins de droite Et j’ai ajouté les chemins left_paths + node-> val + right_paths à all_paths à chaque noeud. Et ont envoyé les chemins qui peuvent encore être étendus .i.e tous les chemins des deux côtés + noeud.
J'ai amélioré la logique de codage de la réponse d'Arvind Upadhyay. Une fois la boucle if effectuée, vous ne pouvez plus utiliser la même liste. Donc, besoin de créer la nouvelle liste. En outre, il est nécessaire de conserver le nombre de niveaux pour lesquels la logique passe du nœud actuel au chemin de recherche. Si nous ne trouvons pas de chemin, alors avant d'aller voir ses enfants, nous devons sortir de l'appel récursif pour qu'il soit égal au nombre de fois.
int count =0;
public void printAllPathWithSum(Node node, int sum, ArrayList<Integer> list)
{
if(node == null)
return;
if(node.data<=sum)
{
list.add(node.data);
if(node.data == sum)
print(list);
else
{
count ++;
printAllPathWithSum(node.left, sum-node.data, list);
printAllPathWithSum(node.right, sum-node.data, list);
count --;
}
}
if(count != 0)
return ;
printAllPathWithSum(node.left, this.sum, new ArrayList());
if(count != 0)
return;
printAllPathWithSum(node.right, this.sum, new ArrayList());
}
public void print(List list)
{
System.out.println("Next path");
for(int i=0; i<list.size(); i++)
System.out.print(Integer.toString((Integer)list.get(i)) + " ");
System.out.println();
}
Vérifiez le code complet à l’adresse: https://github.com/ganeshzilpe/Java/blob/master/Tree/BinarySearchTree.Java
// assumption node have integer value other than zero
void printAllPaths(Node root, int sum , ArrayList<Integer> path) {
if(sum == 0) {
print(path); // simply print the arraylist
}
if(root ==null) {
//traversed one end of the tree...just return
return;
}
int data = root.data;
//this node can be at the start, end or in middle of path only if it is //less than the sum
if(data<=sum) {
list.add(data);
//go left and right
printAllPaths(root.left, sum-data , path);
printAllPaths(root.right, sum-data , path);
}
//note it is not else condition to ensure root can start from anywhere
printAllPaths(root.left, sum , path);
printAllPaths(root.right, sum , path);
}
J'ai tenté une réponse, dans l'attente de la révision du code. Mon code ainsi que les relecteurs devraient être une source utile.
Chercher:
Recursively traverse the tree, comparing with the input key, as in binary search tree.
If the key is found, move the target node (where the key was found) to the root position using splaysteps.
Pseudocode:
Algorithm: search (key)
Input: a search-key
1. found = false;
2. node = recursiveSearch (root, key)
3. if found
4. Move node to root-position using splaysteps;
5. return value
6. else
7. return null
8. endif
Output: value corresponding to key, if found.
Algorithm: recursiveSearch (node, key)
Input: tree node, search-key
1. if key = node.key
2. found = true
3. return node
4. endif
// Otherwise, traverse further
5. if key < node.key
6. if node.left is null
7. return node
8. else
9. return recursiveSearch (node.left, key)
10. endif
11. else
12. if node.right is null
13. return node
14. else
15. return recursiveSearch (node.right, key)
16. endif
17. endif
Output: pointer to node where found; if not found, pointer to node for insertion.