2024-07-12
한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina
Baum ist einnichtlinear Eine Datenstruktur, bei der es sich um eine Reihe hierarchischer Beziehungen handelt, die aus n (n>=0) begrenzten Knoten bestehen.BündelnEr wird Baum genannt, weil er wie ein umgedrehter Baum aussieht, was bedeutet, dass die Wurzeln nach oben und die Blätter nach unten zeigen.
- Wurzelknoten: Der Wurzelknoten hat keinen Vorgängerknoten.
- Bis auf den Wurzelknoten sind die übrigen Knoten in Teilbäume unterteilt, deren Struktur der eines Baums ähnelt. Der Wurzelknoten jedes Teilbaums hat genau einen Vorgänger und kann 0 oder mehr Nachfolger haben.
- Daher ist der Baumrekursive Definitionvon.
Die Baumstruktur ist komplizierter als die lineare Tabelle und es ist schwieriger, sie zu speichern und darzustellen.Es ist notwendig, sowohl den Wertebereich als auch die Beziehung zwischen Knoten und Knoten zu speichern.Tatsächlich gibt es viele Möglichkeiten, Bäume darzustellen, wie zum Beispiel:Parent-Notation, Child-Notation, Child-Eltern-Notation und Child-Bruder-NotationWarten.
Bei der Einführung der folgenden Speicherstruktur nehmen wir den folgenden Baum als Beispiel.
Wir gehen davon aus, dass die Knoten des Baums gleichzeitig in einer Reihe kontinuierlicher Räume gespeichert sindAn jeden Knoten ist ein Indikator angehängt, der die Position seines übergeordneten Knotens in der verknüpften Liste anzeigt. . Mit anderen Worten: Jeder Knoten weiß nicht nur, wer er ist, sondern auch, wo sich seine übergeordneten Knoten befinden.
Unter diesen sind Daten das Datenfeld, in dem die Dateninformationen des Knotens gespeichert werden. Und parent ist ein Zeigerfeld, das die Indizes der Eltern des Knotens im Array speichert.
Das Folgende ist der Knotenstruktur-Definitionscode für unsere übergeordnete Darstellung.
/*树的双亲表示法结点结构定义*/
#define MAX_TREE_SIZE 100
typedef int TElemType; //树结点的数据类型,目前暂定为整型
/*结点结构*/
typedef struct TreeNode
{
TElemType data; //结点数据
int parent; //双亲位置
}TreeNode;
/*树结构*/
typedef struct
{
TreeNode nodes[MAX_TREE_SIZE]; //结点数组
int r, n; //根的位置和结点数
}PTree;
Mit einer solchen Speicherstruktur können wir die übergeordneten Knoten anhand des verwendeten übergeordneten Knotens leicht findenDie Zeitkomplexität beträgt 0(1) , bis der übergeordnete Wert -1 ist, was anzeigt, dass die Wurzel des Baumknotens gefunden wurde. Wenn wir jedoch wissen möchten, was die untergeordneten Knoten des Knotens sind, durchlaufen Sie bitte die gesamte Struktur.
Gerade haben wir die Speicherstruktur des Baums aus der Perspektive der Eltern und aus der Perspektive des Kindes untersucht. Was wäre, wenn wir uns für eine hierarchische Struktur wie einen Baum nur das ansehen würden? Brüder der Knoten. Nach der Beobachtung haben wir herausgefunden, dass für jeden Baum das erste Kind seines Knotens eindeutig ist, wenn er existiert, und sein rechter Bruder ebenfalls eindeutig ist, wenn er existiert. Daher setzen wir zwei Zeiger, die auf das erste Kind des Knotens und das rechte Geschwister des Knotens zeigen.
/*树的孩子兄弟表示法结构定义*/
typedef struct TreeNode
{
TElemtype data;
struct TreeNode *firstchild, *rightsib;
} TreeNode,* pTreeNode;
Ein Binärbaum ist eine endliche Menge von Knoten, nämlich:
- oder leer
- Es besteht aus einem Wurzelknoten und zwei Binärbäumen, auch bekannt als linker Teilbaum und rechter Teilbaum.
Wie auf dem Bild oben zu sehen ist:
- In einem Binärbaum gibt es keinen Knoten mit einem Grad größer als 2
- Die Teilbäume eines Binärbaums können in linke und rechte Teilbäume unterteilt werden, und die Reihenfolge kann nicht umgekehrt werden, so wie es beim Binärbaum der Fall istgeordneter Baum
Hinweis: Jeder Binärbaum besteht aus den folgenden Situationen:
Binärbäume können im Allgemeinen mit zwei Strukturen gespeichert werden:Eine sequentielle Struktur, eine Kettenstruktur.
Gewöhnliche Binärbäume eignen sich nicht für die Speicherung in Arrays, da möglicherweise viel Speicherplatz verschwendet wird.UndVollständige Binärbäume eignen sich besser für die sequentielle Strukturspeicherung .In Wirklichkeit tun wir das normalerweiseEin Heap (ein Binärbaum) verwendet zum Speichern ein Array sequenzieller StrukturenEs ist zu beachten, dass der Heap hier und der Heap im virtuellen Prozessadressraum des Betriebssystems zwei verschiedene Dinge sind. Das eine ist eine Datenstruktur und das andere ist eine Bereichssegmentierung, die den Speicher im Betriebssystem verwaltet.
Informationen zur spezifischen Implementierung und Anwendung finden Sie unter:[Datenstruktur] 08. Heap und Heap-Anwendungen
Der einfachste Weg, eine binäre Baumstruktur zu erlernen, besteht darin, sie zu durchlaufen.sogenanntBei der Durchquerung des Binärbaums (Traversal) werden entsprechende Operationen an den Knoten im Binärbaum nacheinander gemäß bestimmten Regeln ausgeführt, und jeder Knoten wird nur einmal bedient . Die von den zugreifenden Knoten ausgeführten Operationen hängen vom jeweiligen Anwendungsproblem ab. Das Durchlaufen ist eine der wichtigsten Operationen an einem Binärbaum und bildet auch die Grundlage für andere Operationen an einem Binärbaum.
//根 左子树 右子树
void PrevOrder(pTreeNode root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
printf("%d ", root->val);
PrevOrder(root->left);
PrevOrder(root->right);
}
Die folgende Abbildung zeigt den rekursiven Prozess:
//左子树 根 右子树
void InOrder(pTreeNode root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
InOrder(root->left);
printf("%d ", root->val);
InOrder(root->right);
}
Das Folgende ist der rekursive Prozess:
//左子树 右子树 根
void PostOrder(pTreeNode root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
PostOrder(root->left);
PostOrder(root->right);
printf("%d ", root->val);
}
Das Folgende ist der rekursive Prozess:
Level-Order-Traversal: Zusätzlich zum Pre-Order-Traversal, In-Order-Traversal und Post-Order-Traversal kann Level-Order-Traversal auch für Binärbäume durchgeführt werden. Gehen Sie davon aus, dass die Ebenennummer des Wurzelknotens des Binärbaums 1 ist. Die Durchquerung der Ebenenreihenfolge beginnt am Wurzelknoten des Binärbaums. Zuerst wird der Wurzelknoten der ersten Ebene besucht, dann werden die Knoten auf der zweiten Ebene besucht Ebene von links nach rechts und dann die dritte Ebene Die Knoten der Ebene und so weiter.Der Prozess, die Knoten des Baums Schicht für Schicht von oben nach unten und von links nach rechts zu besuchen, ist eine Durchquerung der Schichtreihenfolge.。
Illustration:
Hier greifen wir in die Warteschlange ein, um eine Vorbestellungsdurchquerung des Binärbaums durchzuführen.
// 层序遍历
void LevelOrder(pTreeNode root)
{
Queue q;
QueueInit(&q);
if (root)
{
QueuePush(&q, root);
}
while (!QueueEmpty(&q))
{
pTreeNode front = QueueFront(&q);
QueuePop(&q);
if (front == NULL)
{
printf("NULL ");
}
else
{
printf("%d ", front->val);
QueuePush(&q, front->left);
QueuePush(&q, front->right);
}
}
QueueDestory(&q);
}
Um einen Binärbaum zu erstellen und zu zerstören, verwenden wirDurchquerung eines BinärbaumsZum Beispiel.
//二叉树的创建
struct TreeNode* Creat(char* arr,int n,int* i)
{
if(*i<n&&arr[*i]=='#')
{
(*i)++;
return NULL;
}
TreeNode* newnode=(TreeNode*)malloc(sizeof(TreeNode));
newnode->left=NULL;
newnode->right=NULL;
newnode->val=arr[(*i)++];
newnode->left=Creat(arr,n,i);
newnode->right=Creat(arr,n,i);
return newnode;
}
//二叉树的销毁
void TreeDestroy(struct TreeNode* root)
{
if(root==NULL)
{
return;
}
TreeDestroy(root->left);
TreeDestroy(root->right);
free(root);
}
Die folgenden Operationen werden alle nach dem Prinzip der Durchquerung ausgeführt.
// 二叉树节点个数
int TreeSize(pTreeNode root)
{
if (root == NULL)
{
return 0;
}
return TreeSize(root->left) + TreeSize(root->right) + 1;
}
// 二叉树叶子节点个数
int TreeLeafSize(pTreeNode root)
{
if (root == NULL)
{
return 0;
}
if (root->left == NULL && root->right == NULL)
{
return 1;
}
return TreeLeafSize(root->left) + TreeLeafSize(root->right);
}
// 二叉树第k层节点个数
int TreeLevelKSize(pTreeNode root, int k)
{
if (root == NULL)
{
return 0;
}
if (k == 1)
{
return 1;
}
return TreeLevelKSize(root->left, k - 1) + TreeLevelKSize(root->right, k - 1);
}
// 二叉树查找值为x的节点
pTreeNode TreeFind(pTreeNode root, TreeDataType x)
{
if (root == NULL)
{
return NULL;
}
//相等就返回
if (root->val == x)
return root;
//找左子树
pTreeNode left=TreeFind(root->left, x);
if (left)
{
return left;
}
//找右子树
pTreeNode right = TreeFind(root->right, x);
if (right)
{
return right;
}
//都没找到
return NULL;
}
//二叉树的高度
int TreeHeight(pTreeNode root)
{
if (root == NULL)
{
return 0;
}
int max_left = TreeHeight(root->left) ;
int max_right = TreeHeight(root->right);
return max_left > max_right ? max_left + 1 : max_right + 1;
}
//判断是否是完全二叉树
bool TreeComplete(pTreeNode root)
{
Queue q;
QueueInit(&q);
if (root)
{
QueuePush(&q, root);
}
while (!QueueEmpty(&q))
{
pTreeNode front = QueueFront(&q);
QueuePop(&q);
if (front == NULL)
{
break;
}
else
{
QueuePush(&q, front->left);
QueuePush(&q, front->right);
}
}
while (!QueueEmpty(&q))
{
pTreeNode front = QueueFront(&q);
QueuePop(&q);
if (front)
{
QueueDestory(&q);
return false;
}
}
QueueDestory(&q);
return true;
}