5.二叉树---C++版左程云耗时100天打造算法与数据一周刷爆LeetCode

两个单链表相交:

【题目】给定两个可能有环也可能无环的单链表,头节点head1和head2。请实现一个函数,如果两个链表相交,请返回相交的第一个节点。如果不相交,返回null:

思路:

判断链表是否有环,需要快慢指针,若有环则快慢指针一定可以相遇,相遇后让其中一个指针回到头结点,再让他们每次走一步,最终相遇就是入环节点。若有环loop为入环节点,若没环则返回空

分类讨论:

1.若两个都无环,则loop=null,有两种情况,一种是相交,一种是不想交,区别方法,遍历每个链表获取长度length,同时记录最后一个链表的指针,通过判断最后一个指针是否相等来区分二者是否相交,若相交,则通过调整他们的长度,通过让长的链表减去长短链表之差,使得二者链表长度相等,让他们继续分别遍历,则它们一定会相遇。

2.若一个有环一个无环,则它们不可能相交;

3.若他们有环,则分为三种情况,1)不想交;2)在环外相交;3)在环内相交

2)其中若loop1=loop2则他们在环外相交,则通过各自计算头结点到入环节点的距离,重复无环链表的相交问题。返回相交部分

1)和3)区分,让loop1继续往下走,在走到自己之前看能否遇到loop2,若遇到则为3),那么返回loop1作为相交部分,若没遇到则返回空

#include<iostream>
using namespace std;
#include"getLoopNode.h"

Node* getLoopNode(Node* head) {
	if (head == nullptr || head->next == nullptr || head->next->next == nullptr)
		return nullptr;
	Node* n1 = head->next;
	Node* n2 = head->next->next;
	while (n1 != n2)
	{
		if (n2->next == nullptr || n2->next->next == nullptr)
			return nullptr;
		n1 = n1->next;
		n2 = n2->next->next;
	}
	n1 = head;
	while (n1 != n2)
	{
		n1 = n1->next;
		n2 = n2->next;
	}
	return n1;
}

如果在程序中使用 new 运算符动态分配了链表节点的内存,但没有在程序结束前或其它适当的时间点将其释放,就会发生内存泄漏,导致程序运行时占用的内存越来越多,最终可能导致程序异常终止或崩溃。

具体而言,如果链表中存在循环,那么在删除节点时,curr 指针会陷入循环中,无法终止,导致程序进入无限循环状态。同时,由于循环链表中的节点被重复删除,会导致内存泄漏问题,因为无法正确释放被循环引用的节点。

释放内存代码如下:核心思想,首先获得入环节点,获得环节点前一个节点的尾指针指向空,则会将环形链表变为非环链表,代码如下:

#include<iostream>
using namespace std;
#include"getLoopNode.h"
void deleteListWithCycle(Node*& head) {
    Node* slow = head;
    Node* fast = head;

    // 找到链表的循环
    while (fast != nullptr && fast->next != nullptr) {
        slow = slow->next;
        fast = fast->next->next;
        if (slow == fast) {
            break;
        }
    }

    // 如果链表没有循环,直接删除链表的所有节点
    if (fast == nullptr || fast->next == nullptr) {
        Node* curr = head;
        while (curr != nullptr) {
            Node* next = curr->next;
            delete curr;
            curr = next;
        }
        head = nullptr;

        return;
    }

    // 删除链表中的循环
    fast = head;
    while (slow->next != fast->next) {
        slow = slow->next;
        fast = fast->next;
    }
    slow->next = nullptr;

    // 删除链表的所有节点
    Node* curr = head;
    while (curr != nullptr) {
        Node* next = curr->next;
        delete curr;
        curr = next;
    }
    head = nullptr;
}


int main() {
	Node* head = new Node(1);
	head->next = new Node(2);
	head->next->next = new Node(3);
	head->next->next->next = new Node(4);
	head->next->next->next->next = new Node(5);
	head->next->next->next->next->next = new Node(6);
	head->next->next->next->next->next->next = head->next->next;
	Node* res=getLoopNode(head);
	cout << res->value << endl;
   deleteListWithCycle(head);
	return 0;
}

查找代码如下:

#include<iostream>
using namespace std;
#include"getLoopNode.h"

Node* getIntersectNode(Node* head1, Node* head2) {
	if (head1 == nullptr || head2 == NULL)
		return nullptr;
	//判断有环无环
	Node* loop1 = getLoopNode(head1);
	Node* loop2 = getLoopNode(head2);
	if (loop1 == nullptr && loop2 == nullptr)
		return noLoop(head1, head2);
	else if (loop1 != nullptr && loop2 != nullptr)
		return bothLoop(head1, loop1, head2, loop2);
	else
	{
		cout << "一个有环,一个无环,无相交节点" << endl;
		return nullptr;
	}

}
Node* getLoopNode(Node* head) {
	if (head == nullptr || head->next == nullptr || head->next->next == nullptr)
		return nullptr;
	Node* n1 = head->next;
	Node* n2 = head->next->next;
	while (n1 != n2)
	{
		if (n2->next == nullptr || n2->next->next == nullptr)
			return nullptr;
		n1 = n1->next;
		n2 = n2->next->next;
	}
	n1 = head;
	while (n1 != n2)
	{
		n1 = n1->next;
		n2 = n2->next;
	}
	return n1;
}


Node* noLoop(Node* head1, Node* head2) {
	int length1 = 1;
	int length2 = 1;
	Node* cur1 = head1;
	Node* cur2 = head2;

	while (cur1->next != nullptr)
	{
		cur1 = cur1->next;
		length1++;
	}
	while (cur2->next != nullptr)
	{
		cur2 = cur2->next;
		length2++;
	}
	if (cur1 != cur2)
	{
		cout << "无环同时也无相交节点" << endl;
		return nullptr;
	}
	else
	{
		Node* longList = length1 > length2 ? head1 : head2;
		Node* shortList = length1 < length2 ? head1 : head2;
		int lengthBetween = abs(length1 - length2);
		int i = 0;
		
		for (int i = 0; i < lengthBetween; i++)
		{
			longList = longList->next;
		}
		while (longList != shortList)
		{
			longList = longList->next;
			shortList = shortList->next;
		}
		cout << "无环但是有交点" << endl;
		return longList;
	}
	
}

Node* bothLoop(Node* head1, Node* loop1, Node* head2, Node* loop2) {
	int length1 = 1;
	int length2 = 1;
	Node* cur1 = head1;
	Node* cur2 = head2;
	if (loop1 == loop2)
	{
		while (cur1 != loop1)
		{
			cur1 = cur1->next;
			length1++;
		}
		while (cur2!= loop2)
		{
			cur2 = cur2->next;
			length2++;
		}
		Node* longList = length1 > length2 ? head1 : head2;
		Node* shortList = length1 < length2 ? head1 : head2;
		int lengthBetween = abs(length1 - length2);
		for (int i = 0; i < lengthBetween; i++)
		{
			longList = longList->next;
		}
		while (longList != shortList)
		{
			longList = longList->next;
			shortList = shortList->next;
		}
		cout << "有环有交点且在环外" << endl;
		return longList;
	}
	else
	{
		Node* loop = loop1->next;
		for (; loop != loop1;loop=loop->next)
		{
			if (loop == loop2)
			{
				cout << "有环有交点且在环内" << endl;
				return loop;
			
			}
		
		}
		cout << "有环无交点" << endl;
		return nullptr;
	}
}

 main函数

由于有两天链表,当他们相交时,会产生重复删除结点的问题,因此deleteListWithCycle函数需要重新改写,这里没有进行内存的释放,释放函数还需要根据情况分别讨论。

#include<iostream>
using namespace std;
#include"getLoopNode.h"
void deleteListWithCycle(Node*& head) {
    Node* slow = head;
    Node* fast = head;

    // 找到链表的循环
    while (fast != nullptr && fast->next != nullptr) {
        slow = slow->next;
        fast = fast->next->next;
        if (slow == fast) {
            break;
        }
    }

    // 如果链表没有循环,直接删除链表的所有节点
    if (fast == nullptr || fast->next == nullptr) {
        Node* curr = head;
        while (curr != nullptr) {
            Node* next = curr->next;
            delete curr;
            curr = next;
        }
        head = nullptr;

        return;
    }

    // 删除链表中的循环
    fast = head;
    while (slow->next != fast->next) {
        slow = slow->next;
        fast = fast->next;
    }
    slow->next = nullptr;

    // 删除链表的所有节点
    Node* curr = head;
    while (curr != nullptr) {
        Node* next = curr->next;
        delete curr;
        curr = next;
    }
    head = nullptr;
}


int main() {
	Node* head = new Node(1);
	head->next = new Node(2);
	head->next->next = new Node(3);
	head->next->next->next = new Node(4);
	head->next->next->next->next = new Node(5);
	head->next->next->next->next->next = new Node(6);
	head->next->next->next->next->next->next = head->next->next;
    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);
    head1->next->next->next->next->next = head->next->next->next->next;
    Node* res = getIntersectNode(head, head1);
    if (res != nullptr)
    {
        cout << res->value << endl;
    }

    //deleteListWithCycle(head);
    //deleteListWithCycle(head1);
	return 0;
}

递归打印二叉树:
 


class TreeNode {
public:
    int val;
    TreeNode* left;
    TreeNode* right;
    TreeNode(int value) : val(value), left(nullptr), right(nullptr) {}
};

void inorderTraversal(TreeNode* root) {
  
    if (root == nullptr) {
        return;
    }

    inorderTraversal(root->left);   // 遍历左子树
    std::cout << root->val << " ";  // 访问当前节点
    inorderTraversal(root->right);  // 遍历右子树
}

void inorderTraversal(TreeNode* root) {
    if (root == nullptr) {
        return;
    }

    inorderTraversal(root->left);   // 遍历左子树
    std::cout << root->val << " ";  // 访问当前节点
    inorderTraversal(root->right);  // 遍历右子树
}

void preorderTraversal(TreeNode* root) {
    if (root == nullptr) {
        return;
    }

    std::cout << root->val << " ";  // 访问当前节点
    preorderTraversal(root->left);  // 遍历左子树
    preorderTraversal(root->right); // 遍历右子树
}

void postorderTraversal(TreeNode* root) {
    if (root == nullptr) {
        return;
    }

    postorderTraversal(root->left);   // 遍历左子树
    postorderTraversal(root->right);  // 遍历右子树
    std::cout << root->val << " ";    // 访问当前节点
}

先序遍历二叉树(非递归)头左右:

1.将头压入栈

2.弹出并打印栈顶

3.找弹出元素的右孩子和左孩子,先将右孩子压入栈,再将左孩子压入栈

4.周而复始

void preOrderTraversal_no_recursion(TreeNode* root) {//
    if (root == nullptr)
        return;
    stack<TreeNode*> s;
    s.push(root);
    while (!s.empty())
    {
        TreeNode* cur;
        cur = s.top();
        s.pop();
        cout << cur->val << " " << endl;
        if (cur->right != nullptr)
            s.push(cur->right);
        if (cur->left != nullptr)
            s.push(cur->left);
    }
    
}

后序遍历二叉树(非递归)左右头:

1.将头压入栈A

2.弹出把元素压入另一个栈B中

3.找弹出元素的左孩子和右孩子,先将左孩子压入栈,再将右孩子压入栈

4.周而复始知道完成

5.依次从栈B中取出元素并做打印

void postOrderTraversal_no_recursion(TreeNode* root) {//
    if (root == nullptr)
        return;
    stack<TreeNode*>s1, s2;
    s1.push(root);
    while (!s1.empty())
    {
        TreeNode* cur = s1.top();
        s1.pop();
        s2.push(cur);
        if (cur->left != nullptr)
            s1.push(cur->left);
        if (cur->right != nullptr)
            s1.push(cur->right);

    }
    while (!s2.empty())
    {

        cout << s1.top()->val << endl;
        s1.pop();
    }


}

中序遍历二叉树(非递归)左头右:

1.每颗子树的左边界进栈

2.依次弹出并打印,对弹出节点的右数左上述循环

void inOrderTraversal_no_recursion(TreeNode* root) {
    if (root == nullptr)
        return;

    TreeNode* cur = root;
    stack<TreeNode*>  s;

    while (cur != nullptr|| !s.empty())
    {
        while (cur != nullptr)
        {
            s.push(cur);
            cur = cur->left;
        }
        cur = s.top();
        cout << s.top()->val << " ";
        s.pop();
        cur = cur->right;
    }

}

福利函数:打印二叉树

如何完成二叉树的宽度优先遍历(常见题目:求一颗二叉树的宽度)

1采用队列,把头结点放入,弹出就打印,先放左再放右

void weightOrderTraversal_no_recursion(TreeNode* root) {
    if (root == nullptr)
        return;

    TreeNode* cur ;
    queue<TreeNode*>  q;
    q.push(root);
    while (!q.empty())
    {
        cur = q.front();
        q.pop();
        cout << cur->val << " ";
        if (cur->left != NULL)
            q.push(cur->left);
        if (cur->right != NULL)
            q.push(cur->right);
       
        
    }

}

求出宽度:

思想:依然是按托与宽度优先遍历,在遍历的过程中我们可以发现,遍历的顺序是一层一层的,因此,我们需要用到哈希表,主要是用来记录节点与层数的关系,通过判断节点的层数与当前层数是否不一致来确定,层数是否要向下进行,同时对节点计数进行清0操作,

void weightOrderTraversal_count(TreeNode* root) {
    if (root == nullptr)
        return;
    std::unordered_map<TreeNode*, int> hashmap;   // 声明一个哈希表
    TreeNode* cur;
    hashmap[root] = 1;
    int maxcount = 0;
    queue<TreeNode*>  q;
    q.push(root);
    int curlevel = 1;
    int curlevelNode = 0;
    while (!q.empty())
    {
        cur = q.front();
        q.pop();
        int curNodelevel = hashmap[cur];
        if (curNodelevel == curlevel)
            curlevelNode++;
        else
        {
            maxcount = max(maxcount, curlevelNode);
            curlevel++;
            curlevelNode = 1;
        }
        if (cur->left != NULL) {
        q.push(cur->left);
        hashmap[cur->left] = curlevel+1;
    }
        if (cur->right != NULL) {
            q.push(cur->right);
        hashmap[cur->right] = curlevel + 1;
        }


    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值