学习笔记
1.两个单链表的公共子节点
输入两个单链表,找到它们的第一个公共结点,例如:
怎么搞?首先我们很容易就会想起暴力解法,就像冒泡排序一样,先定住A链中的一个结点a1,然后比较所有B链中的结点地址,然后再定住A链中的第二个结点a2,然后再比较B链中所有的结点地址,如此循环往复直到找到公共结点。😊
这种方法很容易想到,但是时间复杂度高,pass!
我们现在思考一下单链表的定义,看看下面这个图是不是两个单链表:
很显然,不是。Why?
注意看,这两个链表公共结点c1的next到底是哪个?是a5还是b4🤔?链表是环环相扣的,核心就是一个结点只能有一个后继。
就好像我国的一夫一妻制一样,你可以有很多人爱你(两个结点例如a3和b2的后继可以相同,都是c1),但你只能爱一个人(一个结点例如c1的后继只能是一个)【😭哭死,纯爱战神应声倒地】
结论:两个单链表的公共结点之后一定都是公共结点。
那不妨我们从链表的末尾开始遍历,但是我们只知道每个链表的头指针,想想哪个数据结构有类似的功能——栈,先进后出💪。
定义两个栈,利用头指针分别把两个链表从头压入栈中,在一个个访问栈顶元素,如果两个栈的栈顶元素相同就保存下来,然后弹出此时栈顶元素,接着判断下两个栈顶元素是否相同,直到不相同为止。
LinkNode* findFirstCommonByStack(LinkNode* headA, LinkNode* headB) {
std::stack<LinkNode*> stackA;
std::stack<LinkNode*> stackB;
//把链表A的所有结点压入栈中
while (headA != nullptr) {
stackA.push(headA);
headA = headA->next;
}
//把链表B的所有结点压入栈中
while (headB != nullptr) {
stackB.push(headB);
headB = headB->next;
}
LinkNode* commonNode = nullptr;
//两个栈都没空
while (!stackA.empty() && !stackB.empty()) {
if (stackA.top() == stackB.top()) {
//返回公共结点
commonNode = stackA.top();
//弹出栈顶元素
stackA.pop();
stackB.pop();
}else{
break;
}
}
return commonNode;
}
findFirstCommonByStack()
接受两个参数,分别是两个链表的头指针,时间复杂度是O(m + n),其中m和n分别是两个链表的长度。
注意:C++标准库用使用栈要引入stack
的头文件:
#include <stack>
当然还可以使用Hash表会更简单,这个我们后面会详细讲解。
2. 判断链表是否有回文序列
示例:
- 输入:链表1->2->2->1
- 输出:true
思路一:将链表中所有的结点值按顺序保存到数组中,从数组两端向中间对比。这种方法很容易想到,但是会被认为逃避链表,不可取。
思路二:反转链表到一个newLink中,同时遍历比较两个链表,只要有一个不相同都是false
。
思路三:利用栈先进后出的特点保存链表的逆序,然后重新遍历链表,一遍出栈一边比较。这里我们演示思路三:
//Palindrome是回文的意思
bool isPalindrome(LinkNode* head) {
LinkNode* node = head;
//创建一个栈压入每个结点的地址
std::stack<LinkNode*> stack1;
while (node != nullptr) {
stack1.push(node);
node = node->next;
}
while (head != nullptr) {
if (head->elem != stack1.top()->elem) {
return false;
}
head = head->next;
}
return true;
}
注意👀,这里创建的栈保存的是每个结点的地址,stack1.top()
是一个地址,stack1.top()->elem
是它的值才能用于比较。这道题是LeetCode234,上面还有超多解法,大家可以去逛逛。