*******1
***2 *****3
4 ****5 6 ****7
经过1,2,4,4,4,2,5,5,5,2,1,3,6,6,6,3,7,7,7,3,1,递归序每个数都会调用三次
先序遍历:头、左、右 1,2,4,5,3,6,7 由递归序转化而来,第一次到就打印,2、3次的不打印
中序遍历:左、头、右 4,2,5,1,6,3,7 由递归序转化而来,第二次到就打印,1、3次的不打印
后序遍历:左、右、头 4,5,2,6,7,3,1 由递归序转化而来,第三次到就打印,1、2次的不打印
递归版
非递归版:
非递归先序遍历(需要一个栈)
1、从栈中弹出一个节点
2、打印(处理)
3、先右再左(如果有左右孩子)
4、重复
非递归中序遍历(需要一个栈)
1、每颗子树的整棵树的左边界进栈
2、依次弹出过程打印(处理)
3、对弹出的节点右树处理(弹出过程中看看有无右树,有就处理)
4、重复
进一步解释:(右分解为左头右) 持续把右分解为左头
例如:
124进栈,弹4无右,弹2有右5,5进栈,弹5无右,继续弹1。36进栈,弹6无右,弹3有右7,7进栈,弹7
4,2,5,1,6,3,7
非递归后序遍历:(需要两个栈)
1、先压左再压右,弹出会先处理右再处理左。从栈中弹出一个节点.这样整个处理就是跟根右左
2、根右左放入收集栈中,再出来就是左右根
3、重复
4、打印收集栈
#include
#include
struct Node {
int value;
Node* left;
Node* right;
Node(int val) : value(val), left(nullptr), right(nullptr) {}
};
void preOrderRecur(Node* head) {
if (head == nullptr) {
return;
}
std::cout << head->value << " ";//先序遍历,第一次遍历这个数就打印
preOrderRecur(head->left);
preOrderRecur(head->right);
}
void inOrderRecur(Node* head) {
if (head == nullptr) {
return;
}
inOrderRecur(head->left);
std::cout << head->value << " ";//中序遍历,第二次遍历这个数才打印
inOrderRecur(head->right);
}
void posOrderRecur(Node* head) {
if (head == nullptr) {
return;
}
posOrderRecur(head->left);
posOrderRecur(head->right);
std::cout << head->value << " ";//后序遍历,第三次遍历这个数才打印
}
//非递归先序遍历
void preOrderUnRecur(Node* head) {
std::cout << "pre-order: ";
if (head != nullptr) {
std::stack<Node*> stack;
stack.push(head);
while (!stack.empty()) {
head = stack.top();
stack.pop();
std::cout << head->value << " ";
if (head->right != nullptr) {
stack.push(head->right);
}
if (head->left != nullptr) {
stack.push(head->left);
}
}
}
std::cout << std::endl;
}
//非递归中序遍历
void inOrderUnRecur(Node* head) {
std::cout << "in-order: ";
if (head != nullptr) {
std::stack<Node*> stack;
while (!stack.empty() || head != nullptr) {
if (head != nullptr) {//全部左边界进栈,head窜到nullptr时
stack.push(head);
head = head->left;
}
else {
head = stack.top();
stack.pop();
std::cout << head->value << " ";
head = head->right;//往右动继续搞左边界
}
}
}
std::cout << std::endl;
}
//非递归后序遍历
void posOrderUnRecur1(Node* head) {
std::cout << "pos-order: ";
if (head != nullptr) {
std::stack<Node*> s1, s2;
s1.push(head);
while (!s1.empty()) {
head = s1.top();
s1.pop();
s2.push(head);
if (head->left != nullptr) {//先左后右,但是第一个栈弹出是先右后左的进入收集栈
s1.push(head->left);
}
if (head->right != nullptr) {
s1.push(head->right);
}
}
while (!s2.empty()) {
std::cout << s2.top()->value << " ";
s2.pop();
}
}
std::cout << std::endl;
}
void posOrderUnRecur2(Node* head) {
std::cout << "pos-order: ";
if (head != nullptr) {
std::stack<Node*> stack;
stack.push(head);
Node* c = nullptr;
while (!stack.empty()) {
c = stack.top(); // 正确使用 top() 获取栈顶元素
if (c->left != nullptr && head != c->left && head != c->right) {
stack.push(c->left);
}
else if (c->right != nullptr && head != c->right) {
stack.push(c->right);
}
else {
std::cout << c->value << " "; // 先输出节点值
stack.pop(); // 然后使用 pop() 移除栈顶元素
head = c;
}
}
}
std::cout << std::endl;
}
int main() {
Node* root = new Node(5);
root->left = new Node(3);
root->right = new Node(8);
root->left->left = new Node(2);
root->left->right = new Node(4);
root->left->left->left = new Node(1);
root->right->left = new Node(7);
root->right->left->left = new Node(6);
root->right->right = new Node(10);
root->right->right->left = new Node(9);
root->right->right->right = new Node(11);
std::cout << "==============recursive==============" << std::endl;
std::cout << "pre-order: ";
preOrderRecur(root);
std::cout << std::endl;
std::cout << "in-order: ";
inOrderRecur(root);
std::cout << std::endl;
std::cout << "pos-order: ";
posOrderRecur(root);
std::cout << std::endl;
std::cout << "============unrecursive=============" << std::endl;
preOrderUnRecur(root);
inOrderUnRecur(root);
posOrderUnRecur1(root);
posOrderUnRecur2(root);
// Clean up
// Free the tree nodes here to prevent memory leaks
}
二叉树的宽度优先遍历(Breadth-First Traversal),也被称为 层序遍历(Level-Order Traversal),是一种逐层遍历二叉树的方式。其基本思想是:从树的根节点开始,逐层地访问每个节点。在每一层中,从左到右访问节点,直到所有节点都被访问。
特点:
广度优先:这意味着我们首先访问根节点,接着访问根节点的子节点,然后是这些子节点的子节点,依此类推。
使用队列:广度优先遍历通常使用 队列(Queue)数据结构来实现。队列遵循 先进先出(FIFO) 的原则,这使得我们可以按照层次顺序处理节点。
过程:
初始化:将根节点入队。
队列处理:
从队列中取出一个节点,处理该节点(通常是输出该节点的值)。
如果该节点有左子节点,则将其左子节点加入队列。
如果该节点有右子节点,则将其右子节点加入队列。
重复:继续从队列中取出节点并处理,直到队列为空。
#include
#include
struct Node {
int value;
Node* left;
Node* right;
Node(int val) : value(val), left(nullptr), right(nullptr) {}
};
void widthTraversal(Node* head) {
if (head == nullptr) {
return;
}
std::queue<Node*> queue;
queue.push(head);
while (!queue.empty()) {
Node* cur = queue.front(); // 获取队列中的第一个元素(当前节点)
queue.pop();
std::cout << cur->value << " ";
if (cur->left != nullptr) {
queue.push(cur->left);
}
if (cur->right != nullptr) {
queue.push(cur->right);
}
}
std::cout << std::endl;
}
int main() {
Node* root = new Node(1);
root->left = new Node(2);
root->right = new Node(3);
root->left->left = new Node(4);
root->left->right = new Node(5);
root->right->left = new Node(6);
root->right->right = new Node(7);
std::cout << "Width-first traversal of binary tree: ";
widthTraversal(root);
// Clean up
// You should add a delete tree function here to properly free memory.
}
二叉树的最大宽度是指二叉树中某一层节点的最大个数。二叉树的宽度一般定义为每一层节点的数量,其中 最大宽度 是所有层中节点数的最大值。
计算二叉树宽度的常见方法:
我们可以通过 层序遍历(Breadth-First Search, BFS) 来计算二叉树的最大宽度。层序遍历可以保证按层次从上到下逐一访问每一层的节点。
使用哈希表求二叉树的最大宽度
#include
#include
#include
struct Node {
int value;
Node* left;
Node* right;
Node(int val) : value(val), left(nullptr), right(nullptr) {}
};
int getMaxWidth(Node* head) {
if (head == nullptr) {
return 0;
}
int maxWidth = 0;//用于记录遍历过程中遇到的最大宽度
int curWidth = 0;//用于记录当前层级的节点数
int curLevel = 0;//用于追踪当前所在的层级。
std::unordered_map<Node*, int> levelMap;
levelMap[head] = 1;
std::queue<Node*> queue;
queue.push(head);
Node* node = nullptr;
while (!queue.empty()) {
node = queue.front();
//从队列中取出一个节点(队列的前端),并将其从队列中移除。
queue.pop();
Node* left = node->left;//取得当前节点的左右子节点。
Node* right = node->right;
if (left != nullptr) {
//如果左子节点存在,则将其层级设为当前节点层级加1,并将其加入队列。
levelMap[left] = levelMap[node] + 1;
queue.push(left);
}
if (right != nullptr) {
//如果右子节点存在,则将其层级设为当前节点层级加1,并将其加入队列。
levelMap[right] = levelMap[node] + 1;
queue.push(right);
}
//如果当前节点的层级大于 curLevel(表示开始了一个新层级的遍历),则更新 curLevel 为当前节点的层级,
//并重置 curWidth 为1。如果还在当前层级,只需将 curWidth 递增。
if (levelMap[node] > curLevel) {
curLevel = levelMap[node];
curWidth = 1; // Reset width for new level,已经发现了一个节点了
}
else {
curWidth++;
}
maxWidth = std::max(maxWidth, curWidth);
}
return maxWidth;
}
int main() {
Node* root = new Node(1);
root->left = new Node(2);
root->right = new Node(3);
root->left->left = new Node(4);
root->left->right = new Node(5);
root->right->left = new Node(6);
root->right->right = new Node(7);
std::cout << "Maximum width of the binary tree is: " << getMaxWidth(root) << std::endl;
// Clean up
// You should add a delete tree function here to properly free memory.
}
不使用哈希表求二叉树的最大宽度
#include
#include
struct Node {
int value;
Node* left;
Node* right;
Node(int val) : value(val), left(nullptr), right(nullptr) {}
};
int getMaxWidth(Node* head) {
if (!head) {
return 0;
}
std::queue<Node*> queue;
queue.push(head);
int maxWidth = 0;
Node* curEnd = head; // 当前层的最后一个节点
Node* nextEnd = nullptr; // 下一层的最后一个节点,nextEnd永远是最新进队列的
int curWidth = 0; // 当前层的宽度
while (!queue.empty()) {
Node* node = queue.front();
queue.pop();
curWidth++;
if (node->left) {
queue.push(node->left);
nextEnd = node->left;
}
if (node->right) {
queue.push(node->right);
nextEnd = node->right;
}
if (node == curEnd) { // 如果当前节点是当前层的最后一个节点
maxWidth = std::max(maxWidth, curWidth); // 更新最大宽度
curWidth = 0; // 重置当前层宽度
curEnd = nextEnd; // 更新当前层的最后一个节点为下一层的最后一个节点
}
}
return maxWidth;
}
int main() {
Node* root = new Node(1);
root->left = new Node(2);
root->right = new Node(3);
root->left->left = new Node(4);
root->left->right = new Node(5);
root->right->left = new Node(6);
root->right->right = new Node(7);
std::cout << "Maximum width of the binary tree is: " << getMaxWidth(root) << std::endl;
/* 队列使用:队列用于存储每一层的节点,以进行层序遍历。
宽度计算:每次从队列中移除一个节点时,当前层宽度curWidth递增。当遇到当前层的最后一个节点curEnd时,比较并更新最大宽度maxWidth,然后重置当前层宽度为0,并将curEnd设置为nextEnd以跟踪下一层的最后一个节点。
结束条件:当队列为空时,遍历完成,函数返回计算出的最大宽度maxWidth。*/
// Note: Proper memory cleanup should be added here to free the allocated nodes.
return 0;
}
【题目】给定两个可能有环也可能无环的单链表,头节点head1和head2。请实现一个函数,如果两个链表相交,请返回相交的 第一个节点。如果不相交,返回null。
【要求】如果两个链表长度之和为N,时间复杂度请达到O(N),额外空间复杂度请达到O(1)。
可能存在的情况:
1、无环链表相交
2、无环链表不相交
3、两个有环链表在环外相交
4、两个有环链表在环内相交但不在同一个入环点
5、两个有环链表完全不相交
6、一个有环,一个无环还相交(不存在)
#include
#include
class Node {
public:
int value;
Node* next;
Node(int val) : value(val), next(nullptr) {}
};
//快指针走两步,慢指针走一步,相遇后(环内小于两圈),快指针回到head处,同时开始走一步,相遇时就是入环节点
Node* getLoopNode(Node* head) {
if (!head || !head->next || !head->next->next) {
return nullptr;
}
Node* slow = head->next;
Node* fast = head->next->next;
while (slow != fast) {//跳出while即表示快慢指针第一次在环上相遇了
if (!fast->next ||!fast->next->next) {/*!fast->next 用来检查 fast 是否到了链表的最后一个节点。
!fast->next->next 用来检查 fast 是否有足够的节点来进行下一次跳跃*/
return nullptr;
}
fast = fast->next->next;
slow = slow->next;
}
fast = head;
while (slow != fast) {//跳出while即表示快慢指针再一次相遇了,即为入环节点
slow = slow->next;
fast = fast->next;
}
return slow;
}
//如果两个链表都无环,返回第一个相交的节点。若相交,从相交处开始往下都共有。如果不相交,返回null
//若end地址相同,长链表先走差值步,在一起走,相遇即为交点
Node* noLoop(Node* head1, Node* head2) {
if (!head1 || !head2) {
return nullptr;
}
Node* cur1 = head1;
Node* cur2 = head2;
int n = 0;
while (cur1->next) {
n++;
cur1 = cur1->next;
}
while (cur2->next) {
n--;
cur2 = cur2->next;
}
if (cur1 != cur2) {//地址不相同,即为不相交
return nullptr;
}
//n是链表1长度减去链表2长度的值,n是差值
cur1 = (n > 0) ? head1 : head2;//谁长,谁的头变为cur1
cur2 = (cur1 == head1) ? head2 : head1;//谁短,谁的头变为cur2
n = std::abs(n);
//长链表先走差值步
while (n--) {
cur1 = cur1->next;
}
//再一起走
while (cur1 && cur2 && cur1 != cur2) {
/*cur1&& cur2 确保指针仍然有效,避免在遍历到链表末尾时出现空指针访问错误。
cur1 != cur2 确保当前指针指向的是不同的节点,只有当两个指针指向相同的节点时才跳出循环,说明链表相交。*/
cur1 = cur1->next;
cur2 = cur2->next;
}
return cur1;
}
//两个链表都有环
//1)纯不相交
//2)入环节点相同(无环链表相交问题)
//3)入环节点不同
Node* bothLoop(Node* head1, Node* loop1, Node* head2, Node* loop2) {
Node* cur1 = nullptr;
Node* cur2 = nullptr;
if (loop1 == loop2) {//认为loop1和loop2就是终止节点,情况2入环节点相同
cur1 = head1;
cur2 = head2;
int n = 0;
while (cur1 != loop1) {
n++;
cur1 = cur1->next;
}
while (cur2 != loop2) {
n--;
cur2 = cur2->next;
}
cur1 = (n > 0) ? head1 : head2;
cur2 = (cur1 == head1) ? head2 : head1;
n = std::abs(n);
while (n--) {
cur1 = cur1->next;
}
while (cur1 != cur2) {
cur1 = cur1->next;
cur2 = cur2->next;
}
return cur1;
}
else {//cur1走一圈看看能不能遇到loop2,遇到即为情况3,否则为情况1
cur1 = loop1->next;//跳过入环节点,在环内遍历,如果从loop1开始遍历,下面那个while会直接成立
while (cur1 != loop1) {
if (cur1 == loop2) {
return loop1;//返回一个交点即可
}
cur1 = cur1->next;
}
return nullptr;
}
}
Node* getIntersectNode(Node* head1, Node* head2) {
if (!head1 || !head2) {
return nullptr;
}
Node* loop1 = getLoopNode(head1);//获得入环节点loop1
Node* loop2 = getLoopNode(head2);//获得入环节点loop2
if (!loop1 && !loop2) {//若都为空,走无环链表相交问题
return noLoop(head1, head2);
}
if (loop1 && loop2) {//若都不为空,走有环链表相交问题
return bothLoop(head1, loop1, head2, loop2);
}
return nullptr;//一个为空,一个不为空,一定不相交
}
void freeList(Node* head) {
Node* temp;
while (head) {
temp = head->next;
delete head;
head = temp;
}
}
void freeLoopList(Node* head, Node* loopNode) {
if (!head || !loopNode) return;//如果 head 或 loopNode 为空,说明链表为空或无效,直接返回,避免空指针异常
// 释放环外部分
Node* start = head;
while (head != loopNode) {
Node* temp = head->next;
delete head;
head = temp;
}
// 释放环内部分,确保只释放一圈
Node* firstLoopNode = loopNode;
do {//使用do...while,先执行一次,再判断条件。因为环中的节点至少会被访问一次,即使 loopNode 本身就是环的起点。
Node* temp = loopNode->next;
delete loopNode;
loopNode = temp;
} while (loopNode != firstLoopNode);
}
int main() {
// 测试无环链表相交
Node* head1 = new Node(1);
head1->next = new Node(2);
head1->next->next = new Node(3);
head1->next->next->next = new Node(4);
head1->next->next->next->next = new Node(5);
Node* head2 = new Node(6);
head2->next = new Node(7);
head2->next->next = head1->next->next; // 7->3
std::cout << "No loop intersect at: ";
Node* intersectNode = getIntersectNode(head1, head2);
if (intersectNode) std::cout << intersectNode->value << std::endl;
else std::cout << "null" << std::endl;
// 测试无环链表不相交
Node* head3 = new Node(8);
head3->next = new Node(9);
Node* head4 = new Node(10);
head4->next = new Node(11);
std::cout << "No loop no intersect: ";
intersectNode = getIntersectNode(head3, head4);
if (intersectNode) std::cout << intersectNode->value << std::endl;
else std::cout << "null" << std::endl;
// 测试两个有环链表在环外相交(13入环)
/*head5: 12 → 13 → 14 → 15 → 16 → 17 → 15 (loopStart1)→ ...
↑ |
|_______________ |
head6 : 18 → 19 → 13 → 14 → 15 → 16 → 17 → 15 (loopStart1)→ ...
↑ |
|_______________ |*/
Node* head5 = new Node(12);
head5->next = new Node(13);
head5->next->next = new Node(14);
Node* loopStart1 = new Node(15);
head5->next->next->next = loopStart1;
loopStart1->next = new Node(16);
loopStart1->next->next = new Node(17);
loopStart1->next->next->next = loopStart1; // Creating loop
Node* head6 = new Node(18);
head6->next = new Node(19);
head6->next->next = head5->next; // 19->13
std::cout << "Both loop intersect outside loop at: ";
intersectNode = getIntersectNode(head5, head6);
if (intersectNode) std::cout << intersectNode->value << std::endl;
else std::cout << "null" << std::endl;
// 测试两个有环链表在环内相交但不在同一个入环点
//head5: 12 → 13 → 14 → 15 → 16 → 17 → 15 (loopStart1)→ ...
// ↑ |
// |_______________ |
//head7 : 20 → 21 → 22 → 23 → 21 (loopStart2)→ ...
// ↑ |
// |_________ |
//head5 and head7 intersect at node 22 inside the loop.
Node* head7 = new Node(20);
Node* loopStart2 = new Node(21);
head7->next = loopStart2;
loopStart2->next = new Node(22);
loopStart2->next->next = new Node(23);
loopStart2->next->next->next = loopStart2; // Creating another loop
loopStart1->next->next->next = loopStart2->next; // Connecting loops at different points
std::cout << "Both loop intersect inside loop but different loop points: ";
intersectNode = getIntersectNode(head5, head7);
if (intersectNode) std::cout << intersectNode->value << std::endl;
else std::cout << "null" << std::endl;
// 测试两个有环链表完全不相交
Node* head8 = new Node(24);
head8->next = new Node(25);
Node* loopStart3 = new Node(26);
head8->next->next = loopStart3;
loopStart3->next = new Node(27);
loopStart3->next->next = loopStart3; // Creating third loop
std::cout << "Both loop not intersect: ";
intersectNode = getIntersectNode(head5, head8);
if (intersectNode) std::cout << intersectNode->value << std::endl;
else std::cout << "null" << std::endl;
// 释放无环链表
freeList(head1);
freeList(head2);
// 释放有环链表
freeLoopList(head5, loopStart1);
freeLoopList(head6, loopStart1); // 如果共享环,确保不重复释放
freeLoopList(head7, loopStart2);
//注意处理共享环的情况,避免重复释放同一个环中的节点
return 0;
}