文章目录
将二叉树bt中每一个结点的左右子树互换的C语言算法
typedef struct node {
int data;
struct node *lchild, *rchild;
} btnode;
void ADDQ(queue *Q, btnode *bt); // Assuming these are predefined
btnode* DELQ(queue *Q); // Assuming these are predefined
int EMPTY(queue *Q); // Assuming these are predefined
void EXCHANGE(btnode *bt) {
queue Q;
initQueue(&Q); // Assuming initQueue initializes the queue
btnode *p, *q;
if (bt) {
ADDQ(&Q, bt); // Add root to the queue
while (!EMPTY(&Q)) {
p = DELQ(&Q); // Dequeue a node
if (p->lchild) {
ADDQ(&Q, p->lchild); // Enqueue left child
}
if (p->rchild) {
ADDQ(&Q, p->rchild); // Enqueue right child
}
// Swap the left and right children
q = p->lchild;
p->lchild = p->rchild;
p->rchild = q;
}
}
}
// Note: Ensure to implement or include necessary queue operations (ADDQ, DELQ, EMPTY, initQueue) for the complete functionality.
二叉树中序遍历的非递归算法
Status Inorder(BiTree T) {
InitStack(s); // 初始化一个栈s
push(s, T); // 将根结点T进栈
while (!StackEmpty(s)) { // 当栈不为空时循环
while (GetTop(s, p) && p) { // 取栈顶元素p,并且p不为空
push(s, p->lchild); // 将p的左子结点进栈
}
pop(s, p); // 栈顶元素出栈
if (!StackEmpty(s)) { // 如果栈不为空
pop(s, p); // 再次弹出栈顶元素
printf("%d ", p->data); // 访问p结点
push(s, p->rchild); // 将p的右子结点进栈
}
}
return OK;
}
详细讲解
- 初始化栈:
InitStack(s); // 初始化一个栈s
首先,我们初始化一个空栈s
,用于存储树节点的指针。
- 将根结点进栈:
push(s, T); // 将根结点T进栈
将根节点T
指针压入栈中,开始遍历。
- 外层循环:当栈不为空时循环:
while (!StackEmpty(s)) { // 当栈不为空时循环
只要栈不为空,就继续循环。这个循环确保了所有节点都会被访问。
- 内层循环:处理当前节点的左子树:
while (GetTop(s, p) && p) { // 取栈顶元素p,并且p不为空
push(s, p->lchild); // 将p的左子结点进栈
}
-
GetTop(s, p)
:取栈顶元素并将其赋值给p
。如果栈顶元素不为空,则继续循环。 -
push(s, p->lchild)
:将当前节点p
的左子节点压入栈中。
内层循环的目的是不断深入左子树的最左端,将路径上的所有节点压入栈中。
- 出栈处理节点:
pop(s, p); // 栈顶元素出栈
if (!StackEmpty(s)) { // 如果栈不为空
pop(s, p); // 再次弹出栈顶元素
printf("%d ", p->data); // 访问p结点
push(s, p->rchild); // 将p的右子结点进栈
}
-
pop(s, p)
:弹出栈顶元素,返回给p
。 -
if (!StackEmpty(s))
:检查栈是否为空,如果不为空则继续处理。 -
pop(s, p)
:再次弹出栈顶元素。这是因为在前一个pop
操作后,栈顶可能是一个NULL
指针,我们需要弹出实际的节点。 -
printf("%d ", p->data)
:访问并打印当前节点的数据。 -
push(s, p->rchild)
:将当前节点的右子节点压入栈中。
上述步骤处理了一个节点并将其右子树的根节点压入栈中,准备下一次遍历。
通过这个算法,我们实现了非递归的中序遍历。在中序遍历中,访问顺序是左子树 -> 根节点 -> 右子树。栈的使用帮助我们记录路径并回溯到正确的节点,确保遍历顺序正确。
图解
下面是一个示例二叉树以及其非递归中序遍历过程的图解:
示例二叉树
1
/ \
2 3
/ \
4 5
非递归中序遍历过程
我们用栈来帮助进行中序遍历,访问顺序应该是:4, 2, 5, 1, 3。
初始状态:
-
栈
s
为空 -
根节点
1
Stack: []
Current node: 1
步骤 1:将根节点及其左子节点压入栈
-
压入节点
1
-
当前节点变为
1
的左子节点2
Stack: [1]
Current node: 2
-
压入节点
2
-
当前节点变为
2
的左子节点4
Stack: [1, 2]
Current node: 4
-
压入节点
4
-
当前节点变为
4
的左子节点(NULL
)
Stack: [1, 2, 4]
Current node: NULL
步骤 2:处理左子节点为空的情况
- 弹出节点
4
并访问
Visited: 4
Stack: [1, 2]
- 将当前节点变为
4
的右子节点(NULL
)
Current node: NULL
步骤 3:继续回溯到上一个节点
- 弹出节点
2
并访问
Visited: 2
Stack: [1]
- 将当前节点变为
2
的右子节点5
Current node: 5
-
压入节点
5
-
当前节点变为
5
的左子节点(NULL
)
Stack: [1, 5]
Current node: NULL
步骤 4:处理右子节点
- 弹出节点
5
并访问
Visited: 5
Stack: [1]
- 将当前节点变为
5
的右子节点(NULL
)
Current node: NULL
步骤 5:回溯到根节点
- 弹出节点
1
并访问
Visited: 1
Stack: []
- 将当前节点变为
1
的右子节点3
Current node: 3
-
压入节点
3
-
当前节点变为
3
的左子节点(NULL
)
Stack: [3]
Current node: NULL
步骤 6:处理最后一个节点
- 弹出节点
3
并访问
Visited: 3
Stack: []
- 当前节点变为
3
的右子节点(NULL
)
Current node: NULL
最终结果
访问顺序为:4, 2, 5, 1, 3
每一步操作通过栈的状态和当前节点的变化,保证了中序遍历的顺序。
非递归方法交换左右孩子
要完成将二叉树中左、右孩子交换的非递归算法,可以参考以下详细实现:
#include <stdio.h>
typedef struct node {
int data;
struct node *lchild, *rchild;
} Node, *Tree;
void exchange(Tree t) {
Tree r, p;
Tree stack[500];
int tp = 0;
// (1) 初始化堆栈,将根节点放入堆栈
stack[tp] = t;
while (tp >= 0) {
// (2) 取出栈顶元素
p = stack[tp--];
if (p != NULL) {
// 交换p的左、右子树
r = p->lchild;
p->lchild = p->rchild;
p->rchild = r;
// 将p的左右子节点分别入栈
stack[++tp] = p->lchild;
stack[++tp] = p->rchild;
}
}
}
// 辅助函数用于创建新节点
Tree createNode(int data) {
Tree newNode = (Tree)malloc(sizeof(Node));
newNode->data = data;
newNode->lchild = newNode->rchild = NULL;
return newNode;
}
// 辅助函数用于中序遍历打印树
void inorder(Tree t) {
if (t != NULL) {
inorder(t->lchild);
printf("%d ", t->data);
inorder(t->rchild);
}
}
int main() {
// 创建一个简单的二叉树
Tree root = createNode(1);
root->lchild = createNode(2);
root->rchild = createNode(3);
root->lchild->lchild = createNode(4);
root->lchild->rchild = createNode(5);
printf("Original tree (inorder): ");
inorder(root);
printf("\n");
exchange(root);
printf("Tree after exchanging children (inorder): ");
inorder(root);
printf("\n");
return 0;
}
具体步骤如下:
- 初始化堆栈:
stack[tp] = t;
将根节点放入堆栈。
- 循环遍历堆栈:
while (tp >= 0) {
p = stack[tp--];
当堆栈不为空时,取出栈顶元素p
。
- 交换左右子树:
if (p != NULL) {
r = p->lchild;
p->lchild = p->rchild;
p->rchild = r;
如果当前节点p
不为空,交换它的左、右子树。
- 将左右子树入栈:
stack[++tp] = p->lchild;
stack[++tp] = p->rchild;
将当前节点p
的左、右子树分别入栈。
通过上述步骤实现了对二叉树的左右子树的交换。通过非递归方法和使用堆栈,避免了递归带来的栈溢出风险。
广义表建立二叉树
广义表建立二叉树的算法
#include <iostream>
#include <stack>
#include <sstream>
using namespace std;
struct BinTreeNode {
char data;
BinTreeNode* leftchild;
BinTreeNode* rightchild;
};
void CreateBinTree(BinTreeNode* &BT, const string& ls) {
stack<BinTreeNode*> s;
while (!s.empty()) s.pop(); // MakeEmpty(s):置空栈
BT = NULL; // 置空二叉树
BinTreeNode* p = NULL;
int k = 0;
istringstream ins(ls); // 把串ls定义为输入字符串流对象ins
char ch;
ins >> ch; // 从ins顺序读入一个字符
while (ch != '#') { // 逐个字符处理,直到遇到‘#’为止
switch (ch) {
case '(':
s.push(p); // (1) 当前结点进栈
k = 1;
break;
case ')':
s.pop(); // Pop(s):退栈
break;
case ',':
k = 2; // (2) 切换到处理右孩子
break;
default:
p = new BinTreeNode;
p->data = ch; // 设置结点数据
p->leftchild = NULL;
p->rightchild = NULL;
if (BT == NULL) {
BT = p; // (4) 置根结点
} else if (k == 1) {
s.top()->leftchild = p; // 处理左孩子
} else {
s.top()->rightchild = p; // 处理右孩子
}
break;
}
ins >> ch; // (5) 读入下一个字符
}
}
// 辅助函数用于中序遍历打印树
void inorder(BinTreeNode* t) {
if (t != NULL) {
inorder(t->leftchild);
cout << t->data << " ";
inorder(t->rightchild);
}
}
int main() {
BinTreeNode* BT;
string ls = "A(B(D,E(H,I)),C(,F(G,)))#";
CreateBinTree(BT, ls);
cout << "Inorder traversal of the constructed tree: ";
inorder(BT);
cout << endl;
return 0;
}
具体填补的地方如下:
- 当前结点进栈:
s.push(p);
在遇到左括号 (
时,将当前节点 p
压入栈中。
- 切换到处理右孩子:
k = 2;
在遇到逗号 ,
时,切换到处理右孩子。
- 弹出栈顶元素:
s.pop();
在遇到右括号 )
时,弹出栈顶元素。
- 设置根结点:
BT = p;
如果树为空,则当前节点 p
为根结点。
- 读入下一个字符:
ins >> ch;
在每次处理完一个字符后,读入下一个字符。
通过这些步骤,我们可以从广义表的字符串表示构建二叉树,并通过中序遍历验证树的结构是否正确。
中序线索树遍历
要完成中序线索树的遍历算法,首先需要理解线索树的结构和遍历方法。以下是该算法的完整实现及详细解释:
中序线索树遍历算法
void inorderthread(BinTreeNode* thr) {
BinTreeNode* p = thr->lchild;
while (p != thr) { // (1) p不等于头结点时循环
while (p->ltag == 0) // (2) p的左标志域为0时循环
p = p->lchild; // (3) p指向其左孩子
printf("%c ", p->data); // 打印p的数据
while (p->rtag == 1 && p->rchild != thr) { // (4) p的右标志域为1且p的右孩子不为头结点时循环
p = p->rchild;
printf("%c ", p->data); // 打印p的数据
}
p = p->rchild; // (5) p指向其右孩子
}
}
代码说明
- 初始化:
BinTreeNode* p = thr->lchild;
从头结点的左孩子开始(即实际的树的根结点)。
- 外层循环:
while (p != thr) {
当p
不等于头结点时循环。遍历整个树。
- 内层循环:
while (p->ltag == 0)
p = p->lchild;
如果p
的左标志域ltag
为0,则p
指向其左孩子,一直找到最左节点。
- 打印数据:
printf("%c ", p->data);
打印当前节点的数据。
- 处理线索化右子树:
while (p->rtag == 1 && p->rchild != thr) {
p = p->rchild;
printf("%c ", p->data);
}
如果p
的右标志域rtag
为1,且p
的右孩子不为头结点,则通过右线索访问后继节点并打印其数据。
- 移动到右孩子:
p = p->rchild;
最后,p
指向其右孩子,继续循环。
示例二叉树
假设我们有以下中序线索化的二叉树:
A
/ \
B C
/ / \
D E F
其线索化过程略。
遍历过程
-
从头结点的左孩子(即A)开始。
-
沿左子树找到最左节点(即D)。
-
打印D。
-
处理D的右线索,找到B,打印B。
-
处理B的右孩子(即A),打印A。
-
沿右子树找到E,打印E。
-
处理E的右线索,找到C,打印C。
-
沿右子树找到F,打印F。
整个遍历过程依次打印:D, B, A, E, C, F。