二叉树相关问题及两链表相交问题(B站左神视频五整理)

二叉树

二叉树的前中后序遍历:

*******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;
    }

你可能感兴趣的:(链表,c++,算法,数据结构)