1

Тема: Реалізація preorder обходу бінарного дерева за допомогою рекурсії

Просте завдання з LeetCode щодо preorder обходу бінарного дерева.
Написав ось таке рішення:

class Solution {
public:
    vector<int> preorderTraversal(TreeNode *root) {
        vector<int> answer;
        if (root != nullptr) {
            answer.push_back(root->val);
            preorderTraversal(root->left);
            preorderTraversal(root->right);
        }
        return answer;
    }
};

Але воно не працює як слід. Видає лише перший вузол дерева і все.
А ось таке рішення працює:

class Solution {
    void preorder(TreeNode *root, vector<int> &ans) {
        if (root != nullptr) {
            ans.push_back(root->val);
            preorder(root->left, ans);
            preorder(root->right, ans);
        }
    }
public:
    vector<int> preorderTraversal(TreeNode *root) {
        vector<int> answer;
        preorder(root, answer);
        return answer;
    }
};

Здавалося б, майже однакові рішення, але працює варіант лише
з допоміжною функцією. Чому так?

2

Re: Реалізація preorder обходу бінарного дерева за допомогою рекурсії

У першому варіанті рекурсивні виклики щось повертають, а куди ці дані далі йдуть?

Подякували: Teg Miles1

3

Re: Реалізація preorder обходу бінарного дерева за допомогою рекурсії

koala написав:

У першому варіанті рекурсивні виклики щось повертають, а куди ці дані далі йдуть?

Не звернув уваги, що в першому варіанті відразу вивід іде, якщо nullptr, без обходу решти вузлів і накопичення даних у векторі.

4

Re: Реалізація preorder обходу бінарного дерева за допомогою рекурсії

leetcode.com написав:

Рекурсивне рішеня є просте, чи можеш ти зробити це ітеративно?

ориґінал (en)

Recursive solution is trivial, could you do it iteratively?

Подякували: koala1

5

Re: Реалізація preorder обходу бінарного дерева за допомогою рекурсії

Та я б не сказав, що ітеративне складне. Ну так, не 5 рядків, а 10, але таке ж прямолінійне.

Подякували: leofun011

6

Re: Реалізація preorder обходу бінарного дерева за допомогою рекурсії

Зліпив за кілька хвилин за годину.

Код (C++)
class Solution {
    public:
    vector<int> preorderTraversal(TreeNode *r) {
        vector<int> v;
        stack<TreeNode *> s;
        s.push(nullptr);
        TreeNode *l = r;
        while(l) {
            v.push_back(l->val);
            r = l->right;
            l = l->left;
            if(l) { if(r) s.push(r); }
            else {
                if(r) l = r;
                else { l = s.top(); s.pop(); }
            }
        };
        return v;
    }
};

7 Востаннє редагувалося koala (30.07.2024 13:18:27)

Re: Реалізація preorder обходу бінарного дерева за допомогою рекурсії

Трохи не оптимально по операціях, але пару рядків зекономив:
    std::vector<int> preorderTraversal(TreeNode* root) {
        std::vector<TreeNode*> stack {root};
        std::vector<int> result;
        while(!stack.empty()) {
            auto node = stack.back();
            stack.pop_back();
            if(node) {
                result.push_back(node->val);
                stack.push_back(node->right);
                stack.push_back(node->left);
            }
        }
        return result;
    }

По хорошому треба перевіряти на порожність перед додаванням.

(ніколи не розумів, нащо той std::stack потрібен, який ще й на додачу std::deque насправді)

Подякували: leofun011

8

Re: Реалізація preorder обходу бінарного дерева за допомогою рекурсії

Якщо сильно потиснути, то можна й так (код, C++)
struct Solution {
    vector<int> preorderTraversal(TreeNode *l, TreeNode *r = nullptr) {
        vector<int> v;
        for(vector<TreeNode *> s{nullptr}; l; ) {
            v.push_back(l->val), r = l->right, l = l->left;
            if(r) if(!l) l = r; else s.push_back(r);
            else if(!l) l = s.back(), s.pop_back();
        }
        return v;
    }
};

9

Re: Реалізація preorder обходу бінарного дерева за допомогою рекурсії

Ну, так можна і в один рядок записати. Все ж треба по стейтментах розбивати.

Ще зранку щось таке уявляв, зараз руки дійшли написати:

Прихований текст
class NotNullStack: private vector<TreeNode *> {
    public:
        static NotNullStack make_stack(TreeNode *node) 
        {
            if(node==nullptr)
                return NotNullStack {};
            else
                return NotNullStack {node};
        }
        void push(TreeNode *node) 
        {
            if(node!=nullptr)
                vector::push_back(node);
        }
        TreeNode *pop()
        {
            if(empty())
                return nullptr;
            TreeNode *result = back();
            pop_back();
            return result;
        }
        using vector::empty;
    private:
        NotNullStack() {}
        NotNullStack(std::initializer_list<TreeNode *> init) : vector {init} {}
};

class Solution {
public:
    vector<int> preorderTraversal(TreeNode* root) {
        NotNullStack stack = NotNullStack::make_stack(root);
        vector<int> result;
        for(TreeNode* node = stack.pop(); node!=nullptr; node = stack.pop())
        {
            result.push_back(node->val);
            stack.push(node->right);
            stack.push(node->left);
        }
        return result;
    }
};

Все, тепер порожні ноди не кладуться у вектор, а дістається все за одну операцію, і ця логіка інкапсульована в окремий клас, як і належить.