Как найти высоту двоичного дерева

Напишите эффективный алгоритм для вычисления высоты бинарного дерева. Высота или глубина бинарного дерева — это общее количество ребер или узлов на самом длинном пути от корневого узла до конечного узла.

Программа должна учитывать общее количество узлов на самом длинном пути. Например, высота пустого дерева равна 0, а высота дерева только с одним узлом равна 1.

Потренируйтесь в этой проблеме

Рекурсивное решение

Идея состоит в том, чтобы обойти дерево в мода на заказ и вычислить высоту левого и правого поддерева. Высота поддерева с корнем в любом узле будет на единицу больше, чем максимальная высота его левого и правого поддеревьев. Рекурсивно примените это свойство ко всем узлам дерева восходящим образом (пост-порядок) и верните максимальную высоту поддерева с корнем в этом узле.

Алгоритм может быть реализован следующим образом на C++, Java и Python:

C++

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

#include <iostream>

using namespace std;

// Структура данных для хранения узла бинарного дерева

struct Node

{

    int key;

    Node *left, *right;

    Node(int key)

    {

        this->key = key;

        this->left = this->right = nullptr;

    }

};

// Рекурсивная функция для вычисления высоты заданного бинарного дерева

int height(Node* root)

{

    // базовый случай: пустое дерево имеет высоту 0

    if (root == nullptr) {

        return 0;

    }

    // повторяем для левого и правого поддерева и учитываем максимальную глубину

    return 1 + max(height(root->left), height(root->right));

}

int main()

{

    Node* root = new Node(15);

    root->left = new Node(10);

    root->right = new Node(20);

    root->left->left = new Node(8);

    root->left->right = new Node(12);

    root->right->left = new Node(16);

    root->right->right = new Node(25);

    cout << «The height of the binary tree is « << height(root);

    return 0;

}

Скачать  Выполнить код

результат:

The height of the binary tree is 3

Java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

// Класс для хранения узла бинарного дерева

class Node

{

    int key;

    Node left = null, right = null;

    Node(int key) {

        this.key = key;

    }

}

class Main

{

    // Рекурсивная функция для вычисления высоты заданного бинарного дерева

    public static int height(Node root)

    {

        // базовый случай: пустое дерево имеет высоту 0

        if (root == null) {

            return 0;

        }

        // повторяем для левого и правого поддерева и учитываем максимальную глубину

        return 1 + Math.max(height(root.left), height(root.right));

    }

    public static void main(String[] args)

    {

        Node root = new Node(15);

        root.left = new Node(10);

        root.right = new Node(20);

        root.left.left = new Node(8);

        root.left.right = new Node(12);

        root.right.left = new Node(16);

        root.right.right = new Node(25);

        System.out.println(«The height of the binary tree is « + height(root));

    }

}

Скачать  Выполнить код

результат:

The height of the binary tree is 3

Python

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

# Класс для хранения узла бинарного дерева.

class Node:

    def __init__(self, key=None, left=None, right=None):

        self.key = key

        self.left = left

        self.right = right

# Рекурсивная функция для вычисления высоты заданного бинарного дерева

def height(root):

    # Базовый случай: пустое дерево имеет высоту 0

    if root is None:

        return 0

    # повторяется для левого и правого поддерева и учитывает максимальную глубину

    return 1 + max(height(root.left), height(root.right))

if __name__ == ‘__main__’:

    root = Node(15)

    root.left = Node(10)

    root.right = Node(20)

    root.left.left = Node(8)

    root.left.right = Node(12)

    root.right.left = Node(16)

    root.right.right = Node(25)

    print(‘The height of the binary tree is’, height(root))

Скачать  Выполнить код

результат:

The height of the binary tree is 3

Временная сложность приведенного выше рекурсивного решения равна O(n), куда n это общее количество узлов в бинарном дереве. Вспомогательное пространство, необходимое программе, равно O(h) для стека вызовов, где h это высота дерева.

Итеративное решение

В итеративной версии выполните обход порядка уровней на дереве. Тогда высота дерева равна общему количеству уровней в нем. Ниже приведена программа на C++, Java и Python, которая демонстрирует это:

C++

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

#include <iostream>

#include <list>

using namespace std;

// Структура данных для хранения узла бинарного дерева

struct Node

{

    int key;

    Node *left, *right;

    Node(int key)

    {

        this->key = key;

        this->left = this->right = nullptr;

    }

};

// Итерационная функция для вычисления высоты заданного бинарного дерева

// путем обхода дерева по уровням

int height(Node* root)

{

    // пустое дерево имеет высоту 0

    if (root == nullptr) {

        return 0;

    }

    // создаем пустую queue и ставим в queue корневой узел

    list<Node*> queue;

    queue.push_back(root);

    Node* front = nullptr;

    int height = 0;

    // цикл до тех пор, пока queue не станет пустой

    while (!queue.empty())

    {

        // вычисляем общее количество узлов на текущем уровне

        int size = queue.size();

        // обрабатываем каждый узел текущего уровня и ставим их в queue

        // непустые левый и правый потомки

        while (size)

        {

            front = queue.front();

            queue.pop_front();

            if (front->left) {

                queue.push_back(front->left);

            }

            if (front->right) {

                queue.push_back(front->right);

            }

        }

        // увеличиваем высоту на 1 для каждого уровня

        height++;

    }

    return height;

}

int main()

{

    Node* root = new Node(15);

    root->left = new Node(10);

    root->right = new Node(20);

    root->left->left = new Node(8);

    root->left->right = new Node(12);

    root->right->left = new Node(16);

    root->right->right = new Node(25);

    cout << «The height of the binary tree is « << height(root);

    return 0;

}

Скачать  Выполнить код

результат:

The height of the binary tree is 3

Java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

import java.util.ArrayDeque;

import java.util.Queue;

// Класс для хранения узла бинарного дерева

class Node

{

    int key;

    Node left = null, right = null;

    Node(int data) {

        this.key = data;

    }

}

class Main

{

    // Итерационная функция для вычисления высоты заданного бинарного дерева

    // путем обхода дерева по уровням

    public static int height(Node root)

    {

        // пустое дерево имеет высоту 0

        if (root == null) {

            return 0;

        }

        // создаем пустую queue и ставим в queue корневой узел

        Queue<Node> queue = new ArrayDeque<>();

        queue.add(root);

        Node front = null;

        int height = 0;

        // цикл до тех пор, пока queue не станет пустой

        while (!queue.isEmpty())

        {

            // вычисляем общее количество узлов на текущем уровне

            int size = queue.size();

            // обрабатываем каждый узел текущего уровня и ставим их в queue

            // непустые левый и правый потомки

            while (size > 0)

            {

                front = queue.poll();

                if (front.left != null) {

                    queue.add(front.left);

                }

                if (front.right != null) {

                    queue.add(front.right);

                }

            }

            // увеличиваем высоту на 1 для каждого уровня

            height++;

        }

        return height;

    }

    public static void main(String[] args)

    {

        Node root = new Node(15);

        root.left = new Node(10);

        root.right = new Node(20);

        root.left.left = new Node(8);

        root.left.right = new Node(12);

        root.right.left = new Node(16);

        root.right.right = new Node(25);

        System.out.println(«The height of the binary tree is « + height(root));

    }

}

Скачать  Выполнить код

результат:

The height of the binary tree is 3

Python

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

from collections import deque

# Класс для хранения узла бинарного дерева.

class Node:

    def __init__(self, data, left=None, right=None):

        self.key = data

        self.left = left

        self.right = right

# Итерационная функция для вычисления высоты заданного бинарного дерева

#, выполняя обход по порядку уровней в дереве

def height(root):

    # Пустое дерево # имеет высоту 0

    if root is None:

        return 0

    # создает пустую queue и ставит в queue корневой узел

    queue = deque()

    queue.append(root)

    height = 0

    # Цикл # до тех пор, пока queue не станет пустой

    while queue:

        # вычисляет общее количество узлов на текущем уровне

        size = len(queue)

        # обрабатывает каждый узел текущего уровня и ставит их в queue.

        # непустые левый и правый потомки

        while size > 0:

            front = queue.popleft()

            if front.left:

                queue.append(front.left)

            if front.right:

                queue.append(front.right)

            size = size 1

        # увеличивает высоту на 1 для каждого уровня

        height = height + 1

    return height

if __name__ == ‘__main__’:

    root = Node(15)

    root.left = Node(10)

    root.right = Node(20)

    root.left.left = Node(8)

    root.left.right = Node(12)

    root.right.left = Node(16)

    root.right.right = Node(25)

    print(‘The height of the binary tree is’, height(root))

Скачать  Выполнить код

результат:

The height of the binary tree is 3

Временная сложность приведенного выше итеративного решения равна O(n), куда n это общее количество узлов в бинарном дереве. Вспомогательное пространство, необходимое программе, равно O(n) для структуры данных queue.

Спасибо за чтение.

Пожалуйста, используйте наш онлайн-компилятор размещать код в комментариях, используя C, C++, Java, Python, JavaScript, C#, PHP и многие другие популярные языки программирования.

Как мы? Порекомендуйте нас своим друзьям и помогите нам расти. Удачного кодирования :)

You were given the solution by mata, but I suggest you also look at your code and understand what it is doing:

    while self.right != None:
        self = self.right
        path = path +1

What will this do? it will find the right child, then its right child, and so on. So this checks only one path of the «rightmost» leaf.

This does the same for the left:

   while self.left != None:
        self = self.left
        path = path +1

The idea in recursion is that for each subproblem, you solve it using the exact same recipe for all other subproblems. So if you would apply your algorithm only to a subtree or a leaf, it would still work.

Also, a recursive definition calls itself (although you can implement this with a loop, but that is beyond the scope here).

Remeber the definition:

Recursion: see definition of Recursion.

;)

Автор оригинала: Pankaj Kumar.

В этой статье мы будем изучать сбалансированные бинарные деревья, и мы постараемся реализовать программу в Python, чтобы определить, сбалансирован ли двоичное дерево или нет. Чтобы прочитать эту статью, вы должны быть знакомы с концепцией бинарных деревьев.

Что такое сбалансированное бинарное дерево?

Сбалансированное бинарное дерево определяется как бинарное дерево, в котором на каждом узле его левое поддерево и правое судно имеет равную высоту или их высоту отличаться всего на 1.

Другими словами, если мы рассмотрим любой узел дерева в качестве корня дерева, то высоты его левого подмаревки и правого подмаревки никогда не должны отличаться более чем на 1.

Как проверить, является ли бинарное дерево сбалансировано или нет?

Согласно определению высота левого поддерева и правого поддерева не должна превышать одного на любом узле.

Поэтому, если мы рассмотрим дерево, которое будет сбалансировано на любом узле, нам придется найти высоту его левого подделка и правого подмаревки.

Тогда мы проверим разницу в высотах. Если разница выйдет больше 1 на любом узле, мы объявим, что дерево не сбалансировано. Ниже приведен алгоритм для этой процедуры:

Algorithm CheckBalancedBinaryTree:
Input: Root Node of the binary tree.
Output:True if binary tree is balanced and False otherwise.
Start.
0.If tree is empty, return True.
1. Check the height of left sub-tree.
2.Check the height of right sub-tree.
3.If difference in height is greater than 1 return False.
4.Check if left sub-tree is balanced.
5.Check if right sub-tree is balanced.
6. If left sub-tree is balanced and right sub-tree is also balanced, return True.
End

Мы выяснили алгоритм для проверки, если бинарное дерево сбалансировано, но мы не знаем, как рассчитать высоту дерева и подделки. Таким образом, мы сначала реализуем программу, чтобы найти высоту дерева, если узел корневого узла, а затем мы реализуем вышеуказанный алгоритм.

Как найти высоту сбалансированного бинарного дерева?

Чтобы найти высоту бинарного дерева, мы можем просто помнить следующие пункты.

  • Если корень пуст, то высота дерева будет 0.
  • Если root не пустой, то высота дерева будет равна максимальной высоте левого подделка корневого и правого подделка корня, добавленного 1.

Имея в виду вышеуказанные точки, алгоритм нахождения высоты дерева:

  • Высота алгоритма (дерево):
  • Вход: root дерева
  • Выход: высота дерева
  • Начинать.
  • 1. Если корень нет, возврат 0.
  • 2.Нажмите высоту левого подделки .//Height(root.leftChild)
  • 3.Вы высота правого поддерева .//Height(root.rightChild)
  • 4.Видите максимальное значение в 2 и 3 и добавьте 1 к нему.
  • Конец

Теперь мы реализуем вышеуказанный алгоритм и выполните его для следующего бинарного дерева.

AskPython31 2.

Программа найти высоту бинарного дерева

Ниже приведен код для поиска высоты бинарного дерева.

class BinaryTreeNode:
  def __init__(self, data):
    self.data = data
    self.leftChild = None
    self.rightChild=None
    
def insert(root,newValue):
    #if binary search tree is empty, make a new node and declare it as root
    if root is None:
        root=BinaryTreeNode(newValue)
        return root
    #binary search tree is not empty, so we will insert it into the tree
    #if newValue is less than value of data in root, add it to left subtree and proceed recursively
    if newValuehright:
            return hleft+1
        else:
            return hright+1
    
root= insert(None,15)
insert(root,10)
insert(root,25)
insert(root,6)
insert(root,14)
insert(root,20)
insert(root,60)
print("Printing the height of the binary tree.")
print(height(root))
Output:

Printing the height of the binary tree.
3

Теперь мы знаем, как найти высоту бинарного дерева. Таким образом, мы теперь будем реализовать алгоритм, чтобы проверить, является ли двоичное дерево сбалансировано или не для указанного выше двоичного дерева.

Программа для проверки, если бинарное дерево сбалансировано или нет

Следующая программа была реализована для проверки, если бинарное дерево сбалансировано или нет.

class BinaryTreeNode:
  def __init__(self, data):
    self.data = data
    self.leftChild = None
    self.rightChild=None
    
def insert(root,newValue):
    #if binary search tree is empty, make a new node and declare it as root
    if root is None:
        root=BinaryTreeNode(newValue)
        return root
    #binary search tree is not empty, so we will insert it into the tree
    #if newValue is less than value of data in root, add it to left subtree and proceed recursively
    if newValuehright:
            return hleft+1
        else:
            return hright+1

def CheckBalancedBinaryTree(root):
    #if tree is empty,return True
    if root==None:
        return True
    #check height of left subtree
    lheight= height(root.leftChild)
    rheight = height(root.rightChild)
    #if difference in height is greater than 1, return False
    if(abs(lheight-rheight)>1):
        return False
    #check if left subtree is balanced
    lcheck=CheckBalancedBinaryTree(root.leftChild)
    #check if right subtree is balanced
    rcheck=CheckBalancedBinaryTree(root.rightChild)
    #if both subtree are balanced, return True
    if lcheck==True and rcheck==True:
        return True

    
    
root= insert(None,15)
insert(root,10)
insert(root,25)
insert(root,6)
insert(root,14)
insert(root,20)
insert(root,60)
print("Printing True if binary tree is balanced:")
print(CheckBalancedBinaryTree(root))
Output:

Printing True if binary tree is balanced:
True

Поскольку бинарное дерево в нашем примере сбалансировано, программа напечатала правда. Вы можете проверить его для несбалансированных бинарных деревьев, изменяя программу для вставки новых значений.

Заключение

В этой статье мы изучали концепцию сбалансированного бинарного дерева. Мы также обсудили алгоритмы нахождения высоты бинарного дерева и проверки, если бинарное дерево сбалансировано или нет. Оставайтесь настроиться на более информативные статьи.

Счастливое обучение!

kation

3 / 3 / 1

Регистрация: 26.10.2014

Сообщений: 17

1

Вычисление высоты бинарного дерева поиска на С++

09.12.2015, 19:07. Показов 24833. Ответов 3

Метки нет (Все метки)


Студворк — интернет-сервис помощи студентам

Никак не могу вывести нормально высоту дерева, уже второй день маюсь, подскажите пожалуйста, в чем проблема ?
Например : Высоту бинарного дерева с узлами f, e, a, c, d, f, b Выводит : 2, Хотя должно вывести : 5

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#include "stdafx.h"
#include <iostream>
#include <conio.h>
#include <cmath>
 
using namespace std;
 
 
struct Node
{
    Node *l,*r; 
    char x; 
    
};
 
 
 
 
void add(char x,Node *&MyTree) 
{
    if (NULL==MyTree)  
    {
        MyTree=new Node; 
        MyTree->x=x; 
        MyTree->l=MyTree->r=NULL; 
    }
    else
    {
                   if (x<MyTree->x)   //Если нововведенный элемент x меньше чем элемент x из семечка дерева, уходим влево
                      {
                          if (MyTree->l!=NULL) add(x,MyTree->l); 
                          else 
                          {
                              MyTree->l=new Node; 
                              MyTree->l->l=MyTree->l->r=NULL; 
                              MyTree->l->x=x; 
                          }
                      }
                 
                    if (x>=MyTree->x)   //Если нововведенный элемент x больше чем элемент x из семечка дерева, уходим вправо
                      {
                          if (MyTree->r!=NULL) add(x,MyTree->r); 
                          else 
                          {
                              MyTree->r=new Node;  
                              MyTree->r->l=MyTree->r->r=NULL; 
                              MyTree->r->x=x; 
                          }
                      }
        
    }
    }
 
//Обход в инфиксном порядке
 void Show(Node *&tree)
{
    if (NULL==tree)    return;   
   
 
    Show(tree->l); 
    
    cout<<tree->x<<endl;
    
    
    Show(tree->r); 
}
 
  /*void Show(Node *&tree) // Обход в прямом порядке
{
    if (NULL==tree)    return;    //Если дерева нет, выходим
   
    cout<<tree->x<<endl; //Посетили узел
    Show(tree->l); //Обошли левое поддерево   
    Show(tree->r); //Обошли правое поддерево   
}*/
 
 
 int lookup(Node *&tree)
{
    int h1=0,h2=0;
    if (NULL==tree)    {return 0; }  
    if(tree->l){h1=lookup(tree->l);}
    if(tree->r){h1=lookup(tree->r);}
    return(max(h1,h2)+1);
 }
 
 
 
void main()
{
 setlocale(LC_ALL,"Russian");
    int n,how_much; 
    char x;
    Node *MyTree=NULL; 
 cout << "Сколько узлов будет в дереве ? (Введите кол-во узлов (min : 2))" << endl;
 for(int menu=0;menu==0;)
 {
 cin >> n;
 if(n>1)
 {menu++;}
 else
 {cout << "Введено неправильное кол-во узлов. Попробуйте еще." << endl;}
 }
  for (int i=0;i<n;i++) 
  {
      cout<<"Vvedite element #"<< i+1 << " : "; 
      cin>>x; 
      add(x,MyTree); 
  }
 
  Show(MyTree); // Простой обход
 how_much=lookup(MyTree);
 
 cout << " Высота данного бинарного дерева : " << how_much << endl ;
 
 
  getch ();
  
}



0



Геомеханик

837 / 640 / 940

Регистрация: 26.06.2015

Сообщений: 1,409

09.12.2015, 20:25

2

Цитата
Сообщение от kation
Посмотреть сообщение

Хотя должно вывести : 5

Всё правильно, именно 5.

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <iostream>
 
struct Node {
    Node* l, *r;
    char  x;
};  
void Node_Add(Node*& p, char x);
void Node_Clear(Node* p);
int  Node_Height(const Node* p);
 
int main(void){
    char s[] = "feacdfb";
    Node* tr = NULL;
    for(const char* i = &s[0]; *i; ++i)
        Node_Add(tr, *i);
 
    std::cout << "height: " << Node_Height(tr) << std::endl;
    Node_Clear(tr);
    return 0;
}
 
//вставка
void Node_Add(Node*& p, char x){
    if(p == NULL){
        p = new (std::nothrow) Node();
        if(p != NULL){
            p->l = p->r = NULL;
            p->x = x;
        }
    } else if(x < p->x)
        Node_Add(p->l, x);
    else
        Node_Add(p->r, x);
}
 
//удаление всех
void Node_Clear(Node* p){
    if(p != NULL){
        if(p->l != NULL)
            Node_Clear(p->l);
        if(p->r != NULL)
            Node_Clear(p->r);
        delete p;
    }
}
 
//высота дерева
int Node_Height(const Node* p){
    int l, r, h = 0;
    if(p != NULL){
        l = Node_Height(p->l);
        r = Node_Height(p->r);
        h = ((l > r) ? l : r) + 1;
    }
    return h;
}

Пример работы кода



1



kation

3 / 3 / 1

Регистрация: 26.10.2014

Сообщений: 17

09.12.2015, 20:56

 [ТС]

3

Ну, я и говорю что пять, просто мне интересно, почему у меня выводит именно два, а не пять ? Х)

Добавлено через 26 минут

Цитата
Сообщение от Геомеханик
Посмотреть сообщение

Всё правильно, именно 5.
C++

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <iostream>
struct Node {
 Node* l, *r;
 char x;
}; 
void Node_Add(Node*& p, char x);
void Node_Clear(Node* p);
int Node_Height(const Node* p);
int main(void){
 char s[] = "feacdfb";
 Node* tr = NULL;
 for(const char* i = &s[0]; *i; ++i)
 Node_Add(tr, *i);
std::cout << "height: " << Node_Height(tr) << std::endl;
 Node_Clear(tr);
 return 0;
}
//вставка
void Node_Add(Node*& p, char x){
 if(p == NULL){
 p = new (std::nothrow) Node();
 if(p != NULL){
 p->l = p->r = NULL;
 p->x = x;
 }
 } else if(x < p->x)
 Node_Add(p->l, x);
 else
 Node_Add(p->r, x);
}
//удаление всех
void Node_Clear(Node* p){
 if(p != NULL){
 if(p->l != NULL)
 Node_Clear(p->l);
 if(p->r != NULL)
 Node_Clear(p->r);
 delete p;
 }
}
//высота дерева
int Node_Height(const Node* p){
 int l, r, h = 0;
 if(p != NULL){
 l = Node_Height(p->l);
 r = Node_Height(p->r);
 h = ((l > r) ? l : r) + 1;
 }
 return h;
}

Ладно, хоть переписал под себя твой код и все заработало, спасибо :3

Может кому поможет сдать лабу, я поделюсь таким кодом (Просто пример выше переписал под динамику Х) )

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#include "stdafx.h"
#include <iostream>
#include <conio.h>
#include <iostream>
 
using namespace std;
 
struct Node {
    Node* l, *r;
    char  x;
};  
void Node_Add(Node*& p, char x);
void Node_Clear(Node* p);
int  Node_Height(const Node* p);
 
int main(void){
 
    Node* tr = NULL;
    
 setlocale(LC_ALL,"Russian"); // Моё
    int n,how_much; 
    char x;
 
 cout << "Сколько узлов будет в дереве ? (Введите кол-во узлов (min : 2))" << endl;
 for(int menu=0;menu==0;)
 {
 cin >> n;
 if(n>1)
 {menu++;}
 else
 {cout << "Введено неправильное кол-во узлов. Попробуйте еще." << endl;}
 }
 char *s = new char[n];
  int count=0;
     for(; count<n; count++)
     {
         cout << "Введите элемент #" << count+1 << " : ";
         cin >> s[count];
        Node_Add(tr, s[count]);
     }
 
    cout << "height: " << Node_Height(tr) << std::endl;
    Node_Clear(tr);
    delete []s;
    getch ();
    return 0;
}
 
//вставка
void Node_Add(Node*& p, char x){
    if(p == NULL){
        p = new (std::nothrow) Node();
        if(p != NULL){
            p->l = p->r = NULL;
            p->x = x;
        }
    } else if(x < p->x)
        Node_Add(p->l, x);
    else
        Node_Add(p->r, x);
}
 
//удаление всех
void Node_Clear(Node* p){
    if(p != NULL){
        if(p->l != NULL)
            Node_Clear(p->l);
        if(p->r != NULL)
            Node_Clear(p->r);
        delete p;
    }
}
 
//высота дерева
int Node_Height(const Node* p){
    int l, r, h = 0;
    if(p != NULL){
        l = Node_Height(p->l);
        r = Node_Height(p->r);
        h = ((l > r) ? l : r) + 1;
    }
    return h;
}



2



0 / 0 / 0

Регистрация: 19.11.2020

Сообщений: 124

11.04.2021, 13:05

4

с написать рекурсивную функцию которая определяет высоту дерева Т (На СиС++)



0



Бинарные деревья поиска и рекурсия – это просто

Время на прочтение
8 мин

Количество просмотров 524K

Существует множество книг и статей по данной теме. В этой статье я попробую понятно рассказать самое основное.

Бинарное дерево — это иерархическая структура данных, в которой каждый узел имеет значение (оно же является в данном случае и ключом) и ссылки на левого и правого потомка. Узел, находящийся на самом верхнем уровне (не являющийся чьим либо потомком) называется корнем. Узлы, не имеющие потомков (оба потомка которых равны NULL) называются листьями.

image
Рис. 1 Бинарное дерево

Бинарное дерево поиска — это бинарное дерево, обладающее дополнительными свойствами: значение левого потомка меньше значения родителя, а значение правого потомка больше значения родителя для каждого узла дерева. То есть, данные в бинарном дереве поиска хранятся в отсортированном виде. При каждой операции вставки нового или удаления существующего узла отсортированный порядок дерева сохраняется. При поиске элемента сравнивается искомое значение с корнем. Если искомое больше корня, то поиск продолжается в правом потомке корня, если меньше, то в левом, если равно, то значение найдено и поиск прекращается.

image
Рис. 2 Бинарное дерево поиска
Сбалансированное бинарное дерево поиска — это бинарное дерево поиска с логарифмической высотой. Данное определение скорее идейное, чем строгое. Строгое определение оперирует разницей глубины самого глубокого и самого неглубокого листа (в AVL-деревьях) или отношением глубины самого глубокого и самого неглубокого листа (в красно-черных деревьях). В сбалансированном бинарном дереве поиска операции поиска, вставки и удаления выполняются за логарифмическое время (так как путь к любому листу от корня не более логарифма). В вырожденном случае несбалансированного бинарного дерева поиска, например, когда в пустое дерево вставлялась отсортированная последовательность, дерево превратится в линейный список, и операции поиска, вставки и удаления будут выполняться за линейное время. Поэтому балансировка дерева крайне важна. Технически балансировка осуществляется поворотами частей дерева при вставке нового элемента, если вставка данного элемента нарушила условие сбалансированности.

image
Рис. 3 Сбалансированное бинарное дерево поиска

Сбалансированное бинарное дерево поиска применяется, когда необходимо осуществлять быстрый поиск элементов, чередующийся со вставками новых элементов и удалениями существующих. В случае, если набор элементов, хранящийся в структуре данных фиксирован и нет новых вставок и удалений, то массив предпочтительнее. Потому что поиск можно осуществлять алгоритмом бинарного поиска за то же логарифмическое время, но отсутствуют дополнительные издержки по хранению и использованию указателей. Например, в С++ ассоциативные контейнеры set и map представляют собой сбалансированное бинарное дерево поиска.

image
Рис. 4 Экстремально несбалансированное бинарное дерево поиска

Теперь кратко обсудим рекурсию. Рекурсия в программировании – это вызов функцией самой себя с другими аргументами. В принципе, рекурсивная функция может вызывать сама себя и с теми же самыми аргументами, но в этом случае будет бесконечный цикл рекурсии, который закончится переполнением стека. Внутри любой рекурсивной функции должен быть базовый случай, при котором происходит выход из функции, а также вызов или вызовы самой себя с другими аргументами. Аргументы не просто должны быть другими, а должны приближать вызов функции к базовому случаю. Например, вызов внутри рекурсивной функции расчета факториала должен идти с меньшим по значению аргументом, а вызовы внутри рекурсивной функции обхода дерева должны идти с узлами, находящимися дальше от корня, ближе к листьям. Рекурсия может быть не только прямой (вызов непосредственно себя), но и косвенной. Например А вызывает Б, а Б вызывает А. С помощью рекурсии можно эмулировать итеративный цикл, а также работу структуры данных стек (LIFO).

int factorial(int n)
{
    if(n <= 1)  // Базовый случай
    {
        return 1;
    }
    return n  * factorial(n - 1); //рекурсивеый вызов с другим аргументом
}
// factorial(1):     return 1
// factorial(2):     return 2 * factorial(1) (return 2 * 1)
// factorial(3):     return 3 * factorial(2) (return 3 * 2 * 1)
// factorial(4):     return 4 * factorial(3) (return 4 * 3 * 2 * 1)
// Вычисляет факториал числа n (n должно быть небольшим из-за типа int
// возвращаемого значения. На практике можно сделать long long  и вообще
// заменить рекурсию циклом. Если важна скорость рассчетов и не жалко память, то
// можно совсем избавиться от функции и использовать массив с предварительно
// посчитанными факториалами).

void reverseBinary(int n)
{
    if (n == 0)  // Базовый случай
    {
        return;
    }
    cout << n%2;
    reverseBinary(n/2); //рекурсивеый вызов с другим аргументом
}
// Печатает бинарное представление числа в обратном порядке

void forvardBinary(int n)
{
    if (n == 0)  // Базовый случай
    {
        return;
    }
    forvardBinary(n/2); //рекурсивеый вызов с другим аргументом
    cout << n%2;
}
// Поменяли местами две последние инструкции
// Печатает бинарное представление числа в прямом порядке
// Функция является примером эмуляции стека

void ReverseForvardBinary(int n)
{
    if (n == 0)  // Базовый случай
    {
        return;
    }
    cout << n%2; // печатает в обратном порядке
    ReverseForvardBinary(n/2); //рекурсивеый вызов с другим аргументом
    cout << n%2; // печатает в прямом порядке
}
// Функция печатает сначала бинарное представление в обратном порядке,
// а затем в прямом порядке


int product(int x, int y)
{
    if (y == 0)  // Базовый случай
    {
        return 0;
    }
    return (x + product(x, y-1)); //рекурсивеый вызов с другим аргументом
}
// Функция вычисляет произведение x * y ( складывает x y раз)
// Функция абсурдна с точки зрения практики,
// приведена для лучшего понимания рекурсии

Кратко обсудим деревья с точки зрения теории графов. Граф – это множество вершин и ребер. Ребро – это связь между двумя вершинами. Количество возможных ребер в графе квадратично зависит от количества вершин (для понимания можно представить турнирную таблицу сыгранных матчей). Дерево – это связный граф без циклов. Связность означает, что из любой вершины в любую другую существует путь по ребрам. Отсутствие циклов означает, что данный путь – единственный. Обход графа – это систематическое посещение всех его вершин по одному разу каждой. Существует два вида обхода графа: 1) поиск в глубину; 2) поиск в ширину.

Поиск в ширину (BFS) идет из начальной вершины, посещает сначала все вершины находящиеся на расстоянии одного ребра от начальной, потом посещает все вершины на расстоянии два ребра от начальной и так далее. Алгоритм поиска в ширину является по своей природе нерекурсивным (итеративным). Для его реализации применяется структура данных очередь (FIFO).

Поиск в глубину (DFS) идет из начальной вершины, посещая еще не посещенные вершины без оглядки на удаленность от начальной вершины. Алгоритм поиска в глубину по своей природе является рекурсивным. Для эмуляции рекурсии в итеративном варианте алгоритма применяется структура данных стек.

С формальной точки зрения можно сделать как рекурсивную, так и итеративную версию как поиска в ширину, так и поиска в глубину. Для обхода в ширину в обоих случаях необходима очередь. Рекурсия в рекурсивной реализации обхода в ширину всего лишь эмулирует цикл. Для обхода в глубину существует рекурсивная реализация без стека, рекурсивная реализация со стеком и итеративная реализация со стеком. Итеративная реализация обхода в глубину без стека невозможна.

Асимптотическая сложность обхода и в ширину и в глубину O(V + E), где V – количество вершин, E – количество ребер. То есть является линейной по количеству вершин и ребер. Записи O(V + E) с содержательной точки зрения эквивалентна запись O(max(V,E)), но последняя не принята. То есть, сложность будет определятся максимальным из двух значений. Несмотря на то, что количество ребер квадратично зависит от количества вершин, мы не можем записать сложность как O(E), так как если граф сильно разреженный, то это будет ошибкой.

DFS применяется в алгоритме нахождения компонентов сильной связности в ориентированном графе. BFS применяется для нахождения кратчайшего пути в графе, в алгоритмах рассылки сообщений по сети, в сборщиках мусора, в программе индексирования – пауке поискового движка. И DFS и BFS применяются в алгоритме поиска минимального покрывающего дерева, при проверке циклов в графе, для проверки двудольности.
Обходу в ширину в графе соответствует обход по уровням бинарного дерева. При данном обходе идет посещение узлов по принципу сверху вниз и слева направо. Обходу в глубину в графе соответствуют три вида обходов бинарного дерева: прямой (pre-order), симметричный (in-order) и обратный (post-order).

Прямой обход идет в следующем порядке: корень, левый потомок, правый потомок. Симметричный — левый потомок, корень, правый потомок. Обратный – левый потомок, правый потомок, корень. В коде рекурсивной функции соответствующего обхода сохраняется соответствующий порядок вызовов (порядок строк кода), где вместо корня идет вызов данной рекурсивной функции.

Если нам дано изображение дерева, и нужно найти его обходы, то может помочь следующая техника (см. рис. 5). Обводим дерево огибающей замкнутой кривой (начинаем идти слева вниз и замыкаем справа вверх). Прямому обходу будет соответствовать порядок, в котором огибающая, двигаясь от корня впервые проходит рядом с узлами слева. Для симметричного обхода порядок, в котором огибающая, двигаясь от корня впервые проходит рядом с узлами снизу. Для обратного обхода порядок, в котором огибающая, двигаясь от корня впервые проходит рядом с узлами справа. В коде рекурсивного вызова прямого обхода идет: вызов, левый, правый. Симметричного – левый, вызов, правый. Обратного – левый правый, вызов.

image
Рис. 5 Вспомогательный рисунок для обходов

Для бинарных деревьев поиска симметричный обход проходит все узлы в отсортированном порядке. Если мы хотим посетить узлы в обратно отсортированном порядке, то в коде рекурсивной функции симметричного обхода следует поменять местами правого и левого потомка.

struct  TreeNode
{
    double data; // ключ/данные
    TreeNode *left; // указатель на левого потомка
    TreeNode *right; // указатель на правого потомка
};

void levelOrderPrint(TreeNode *root) {
    if (root == NULL)
    {
       return;
    }
    queue<TreeNode *> q; // Создаем очередь
    q.push(root); // Вставляем корень в очередь

    while (!q.empty() ) // пока очередь не пуста
    {
        TreeNode* temp = q.front(); // Берем первый элемент в очереди
        q.pop();  // Удаляем первый элемент в очереди
        cout << temp->data << " "; // Печатаем значение первого элемента в очереди

        if ( temp->left != NULL )
            q.push(temp->left);  // Вставляем  в очередь левого потомка

        if ( temp->right != NULL )
            q.push(temp->right);  // Вставляем  в очередь правого потомка
   }
}


void preorderPrint(TreeNode *root)
{
    if (root == NULL)   // Базовый случай
    {
       return;
    }
    cout << root->data << " ";
    preorderPrint(root->left);   //рекурсивный вызов левого поддерева
    preorderPrint(root->right);  //рекурсивный вызов правого поддерева
}
// Функция печатает значения бинарного дерева поиска в прямом порядке.
// Вместо печати первой инструкцией функции может быть любое действие
// с данным узлом

void inorderPrint(TreeNode *root)
{
    if (root == NULL)   // Базовый случай
    {
       return;
    }
    preorderPrint(root->left);   //рекурсивный вызов левого поддерева
    cout << root->data << " ";
    preorderPrint(root->right);  //рекурсивный вызов правого поддерева
}
// Функция печатает значения бинарного дерева поиска в симметричном порядке.
// То есть в отсортированном порядке

void postorderPrint(TreeNode *root)
{
    if (root == NULL)   // Базовый случай
    {
       return;
    }
    preorderPrint(root->left);   //рекурсивный вызов левого поддерева
    preorderPrint(root->right);  //рекурсивный вызов правого поддерева
    cout << root->data << " ";
}
// Функция печатает значения бинарного дерева поиска в обратном порядке.
// Не путайте обратный и обратноотсортированный (обратный симметричный).

void reverseInorderPrint(TreeNode *root)
{
    if (root == NULL)   // Базовый случай
    {
       return;
    }
    preorderPrint(root->right);  //рекурсивный вызов правого поддерева
    cout << root->data << " ";
    preorderPrint(root->left);   //рекурсивный вызов левого поддерева

}
// Функция печатает значения бинарного дерева поиска в обратном симметричном порядке.
// То есть в обратном отсортированном порядке

void iterativePreorder(TreeNode *root)
{
    if (root == NULL)
    {
       return;
    }
    stack<TreeNode *> s;  // Создаем стек
    s.push(root);  // Вставляем корень в стек
    /* Извлекаем из стека один за другим все элементы.
       Для каждого извлеченного делаем следующее
       1) печатаем его
       2) вставляем в стек правого! потомка
          (Внимание! стек поменяет порядок выполнения на противоположный!)
       3) вставляем в стек левого! потомка */
    while (s.empty() == false)
    {
        // Извлекаем вершину стека и печатаем
        TreeNode *temp = s.top();
        s.pop();
        cout << temp->data << " ";

        if (temp->right)
            s.push(temp->right); // Вставляем в стек правого потомка
        if (temp->left)
            s.push(temp->left);  // Вставляем в стек левого потомка
    }
}
// В симметричном и обратном итеративном обходах просто меняем инструкции
// местами по аналогии с рекурсивными функциями.

Надеюсь Вы не уснули, и статья была полезна. Скоро надеюсь последует продолжение

банкета

статьи.

Понравилась статья? Поделить с друзьями:
  • Как можно найти крота
  • Как найти большее основание равнобокой трапеции
  • Как исправить пинг на модеме
  • Андроид начал тупить как исправить
  • Как найти забытый емайл