标签:val let 从后往前 ted 双指针 width turn 初始化 next
5.移除链表元素
6.反转链表
7.回文链表
8.删除链表中的节点
9.链表的中间节点
将两个有序链表合并为一个新的有序链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
if(l1==nullptr)
return l2;
if(l2==nullptr)
return l1;
if(l1->val < l2->val){
l1->next = mergeTwoLists(l1->next,l2);
return l1;
}else{
l2->next = mergeTwoLists(l2->next,l1);
return l2;
}
}
};
class Solution {
public:
ListNode* mergeTwoLists(ListNode* l1, ListNode* l2) {
ListNode* newHead = new ListNode(-1);
ListNode* p = newHead;
while(l1!=nullptr && l2!=nullptr){
if(l1->val <= l2->val){
p->next = l1;
l1 = l1->next;
}else{
p->next = l2;
l2 = l2->next;
}
p = p->next;
}
p->next = (l1!=nullptr)? l1:l2;
return newHead->next;
}
};
给定一个排序链表,删除所有重复的元素,使得每个元素只出现一次。
方法一、由于要删除元素,因此要保存要删除元素的前一个结点
class Solution {
public:
ListNode* deleteDuplicates(ListNode* head) {
if(head==nullptr)
return head;
ListNode* pre = head;
ListNode* curr = pre->next;
while(curr){
while(curr && pre->val == curr->val){
ListNode* tmp = curr;
curr = curr->next;
pre->next = curr;
tmp = nullptr; //清除野指针
}
pre = pre->next;
if(pre)
curr = pre->next;
else
curr = nullptr;
}
return head;
}
};
给定一个链表,判断链表中是否有环。
class Solution {
public:
bool hasCycle(ListNode *head) {
if(head==nullptr)
return false; //注意空链表,认为是没有环的
ListNode *slow = head;
ListNode *fast = head;
while(fast){
ListNode *tmp = fast->next;
if(tmp){
fast = tmp->next;
}else{
return false;
}
slow = slow->next;
if(fast==slow)
return true;
}
return false;
}
};
通过检查一个结点此前是否被访问过来判断链表是否为环形链表。常用的方法是使用哈希表
class Solution {
public:
bool hasCycle(ListNode *head) {
if(head==nullptr)
return false;
ListNode *curr=head;
set<ListNode*> ss; //注意存的类型是ListNode*
while(curr){
if(ss.find(curr)!=ss.end()){ //存在
return true;
}else{
ss.insert(curr);
curr = curr->next;
}
}
return false;
}
};
环形链表中,环形会死循环没办法判断边界条件,因此我们我们每遍历一个链表就把后面的指向前面的,这样环形要再次循环时会反方向走,最后他会回到头节点,从而循环结束。
环形初始情况:

倒置后:

没有环形就是普通链表的倒置。
所以最后只要判断倒置后的首节点是不是head节点,是则true,不是则false。
时间复杂度最坏是2n(相当于从最后再来一次),即O(n),空间上只用了三个指针即O(1)。
public class Solution {
public boolean hasCycle(ListNode head) {
if(head==null)return false;
if(head.next==head)return true;
if(head.next==null)return false;
ListNode p=head.next,q=p.next,x;
head.next=null;
for(x=head,p.next=x;q!=null;p.next=x,x=p,p=q,q=q.next);//倒置整个链表
if(p==head)return true;//如果回到头节点说明存在环,不是则不存在环
else return false;
}
}
编写一个程序,找到两个单链表相交的起始节点。

在节点 c1 开始相交
对链表A中的每一个结点 a_i,遍历整个链表 B 并检查链表 B 中是否存在结点和 a_i相同。
复杂度分析
时间复杂度 : O(mn)。
空间复杂度 : O(1)。
遍历链表A并将每个结点的地址/引用存储在哈希表中。然后检查链表B中的每一个结点 b_i是否在哈希表中。若在,则 b_i为相交结点。
复杂度分析
时间复杂度 : O(m+n)。
空间复杂度 : O(m)或O(n)。
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
if(headA==nullptr || headB==nullptr)
return nullptr;
ListNode *p = headA;
set<ListNode *> ss;
while(p){
ss.insert(p);
p = p->next;
}
p = headB;
while(p){
if(ss.find(p)!=ss.end()){ //找到相同元素
return p;
}else{
p = p->next;
}
}
return nullptr;
}
};
创建两个指针pA和pB,分别初始化为链表A和B的头结点。然后让它们向后逐结点遍历。
当pA到达链表的尾部时,将它重定位到链表B的头结点 (你没看错,就是链表B); 类似的,当pB到达链表的尾部时,将它重定位到链表A的头结点。
若在某一时刻pA和pB相遇,则pA/pB为相交结点。
想弄清楚为什么这样可行, 可以考虑以下两个链表: A={1,3,5,7,9,11}和B={2,4,9,11},相交于结点9。由于B.length (=4)< A.length(=6),pB比pA少经过2个结点,会先到达尾部。将pB重定向到A的头结点,pA重定向到B的头结点后,pB要比pA多走2个结点。因此,它们会同时到达交点。
如果两个链表存在相交,它们末尾的结点必然相同。因此当pA/pB到达链表结尾时,记录下链表A/B对应的元素。若最后元素不相同,则两个链表不相交。
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
if(headA==nullptr || headB==nullptr)
return nullptr;
ListNode *pa = headA;
ListNode *pb = headB;
/*
* 如果是没有相交的链表,最后两个指针将分别指向尾部的nullptr,由相等退出循环
*/
while(pa != pb){
pa = (pa==nullptr)?headB:pa->next;
pb = (pb==nullptr)?headA:pb->next;
}
return pa;
}
};
删除链表中等于给定值 val 的所有节点。
由于可能会设计到头节点的删除,因此新建一个头头节点。
class Solution {
public:
ListNode* removeElements(ListNode* head, int val) {
if(head==nullptr)
return nullptr;
ListNode* newHead = new ListNode(-1);
newHead->next=head;
ListNode *pre = newHead, *curr=head;
while(curr){
if(curr->val==val){//删除
pre->next=curr->next;
curr=curr->next;
}else{
pre = curr;
curr=curr->next;
}
}
return newHead->next;
}
};
class Solution {
public ListNode removeElements(ListNode head, int val) {
if(head==null)
return null;
head.next=removeElements(head.next,val); //一直到尾元素,才开始处理下面的语句,即从后往前判断是否由target值。
if(head.val==val){
return head.next;
}else{
return head;
}
}
}
class Solution {
public ListNode removeElements(ListNode head, int val) {
//删除值相同的头结点后,可能新的头结点也值相等,用循环解决
while(head!=null&&head.val==val){
head=head.next;
}
if(head==null)
return head;
ListNode prev=head;
//确保当前结点后还有结点
while(prev.next!=null){
if(prev.next.val==val){
prev.next=prev.next.next;
}else{
prev=prev.next;
}
}
return head;
}
}
递归版本稍微复杂一些,其关键在于反向工作。假设列表的其余部分已经被反转,现在我该如何反转它前面的部分?


class Solution {
public:
ListNode* reverseList(ListNode* head) { //用三个相邻的指针从前往后移
if(head==nullptr || head->next==nullptr)
return head;
ListNode *pre = reverseList(head->next);
head->next->next=head;
head->next =nullptr;
return pre;
}
};
class Solution {
public:
ListNode* reverseList(ListNode* head) { //头插法
if(head==nullptr)
return head;
ListNode* newHead = new ListNode(-1);
newHead->next = head;
ListNode* pre=head;
ListNode* curr=pre->next;
while(curr){
pre->next = curr->next;
curr->next = newHead->next; //每一次插入到头元素,此处不是pre
newHead->next = curr;
curr = pre->next; //指向下一个要插入的节点
}
return newHead->next;
}
};
class Solution {
public:
ListNode* reverseList(ListNode* head) { //用三个相邻的指针从前往后移
if(head==nullptr)
return head;
ListNode *pre = nullptr, *curr=head, *pnext=head->next;
while(pnext){
curr->next=pre;
pre = curr;
curr=pnext;
pnext=pnext->next;
}
curr->next=pre;
return curr;
}
};
标签:val let 从后往前 ted 双指针 width turn 初始化 next
原文地址:https://www.cnblogs.com/GuoXinxin/p/11706297.html