两个单链表相交:
【题目】给定两个可能有环也可能无环的单链表,头节点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;
}
}
}