代码随想录4(链表2)

24. 两两交换链表中的节点

方法一:迭代

思路:首先我们把虚拟头节点定义出来:

ListNode* dummyhead=new ListNode(0);

dummyhead->next=head;

然后,假如结点至少两个:

实质上就是对pre,one,two,last这四个结点操作,

pre->next=two;
pre->next->next=one;
pre->next->next->next=last;

这样在至少有两个结点的情况下就完成了一次交换,那么如果后面还有很多个结点,就只需要让pre=pre->next->next,然后继续用这种方式,先重新定义one,two,last,再改变顺序

而若链表中只有1个结点或者链表为空,则不用交换:

if(pre->next==null||pre->next->next==null)

pre一开始就在dummyhead,这样就很好判断

至少两个结点的情况:最后终止的情况是只剩下一个结点或者没有结点,那么终止条件其实也是上面那个if判断

所以最后可以把if直接写成while

while结束后再创一个指针用来表示头结点:

ListNode* ans=dummyhead->next;
delete dummyhead;
return ans;

因为之前的头结点肯定变了,而虚拟头节点还是在那里,指向新的头结点

最后还要记得释放dummyhead的内存

完整代码:

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        ListNode* dummyHead = new ListNode(0);
        dummyHead->next = head; 
        ListNode* pre = dummyHead;
        while(pre->next != nullptr && pre->next->next != nullptr) {
            ListNode* one = pre->next; 
            ListNode* two = pre->next->next;
            ListNode* three=pre->next->next->next; 
//这里我就直接写成one,two,three了

            pre->next =two;    
            pre->next->next = one;          
            pre->next->next->next =three;   

            pre = pre->next->next;
        }
        ListNode* ans = dummyHead->next;
        delete dummyHead;
        return ans;
    }
};

当然,其实two这个指针是没有必要设置的,因为它是第一个操作点,链表的初始顺序没有改变,

pre->next=two等价于pre->next=pre->next->next;

方法二:递归:

递归就是要把握两点:一是递归的终止条件,二是递归的式子

在这里:函数名为:ListNode* swapPairs(ListNode* head)

终止条件:head==nullptr||head->next=nullptr;

递归:我们定义一个newhead,newhead=head->next;

这个结点是交换之后的新的头结点,各个结点的指针指向的改变过程是

head->next=swapPairs(newhead->next),

newhead->next=head;

return newhead;

完整代码:

struct ListNode* swapPairs(struct ListNode* head){
    if(!head || !head->next)
        return head;
    struct ListNode *newHead = head->next;
    head->next = swapPairs(newHead->next);
    newHead->next = head;
    return newHead;
}

19.删除链表的倒数第N个节点

方一:栈

我觉得这个思路挺巧妙的,刚好栈的第n个结点就是倒数第n个:

1.cur指针遍历链表来入栈,

2.删除结点要找前一个结点,所以pop了n次,再pop一次

3.因为不知道倒数第n个结点是不是头结点,所以还是要虚拟头结点和ans:ans = dummy->next;

4.记得删dummyhead

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummy = new ListNode(0, head);
        stack<ListNode*> stk;
        ListNode* cur = dummy;
        while (cur) {
            stk.push(cur);
            cur = cur->next;
        }
        for (int i = 0; i < n; ++i) {
            stk.pop();
        }
        ListNode* prev = stk.top();
        prev->next = prev->next->next;
        ListNode* ans = dummy->next;
        delete dummy;
        return ans;
    }
};

方二:双指针

用fast,slow,只要fast在slow前面n个结点,那么都一起往后移一个点,直到fast==nullptr

这里复制的leetcode上面的题解,之所以要让first=head;原因是要让second指向被删除结点的前一个结点

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummy = new ListNode(0, head);
        ListNode* first = head;
        ListNode* second = dummy;
        for (int i = 0; i < n; ++i) {
            first = first->next;
        }
        while (first) {
            first = first->next;
            second = second->next;
        }
        ListNode* temp=second->next;
        second->next=temp->next;
        delete temp;

        
        ListNode* ans = dummy->next;
        delete dummy;
        return ans;
    }
};


160链表相交

先算出两个链表的长度m,n,再让长的链表的指针向前移动|m-n|个位置,然后两个链表同时一步一步移动,若相交,则必定两个指针会相遇

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode* pA=headA;int lengthA=0;
        ListNode* pB=headB;int lengthB=0;
        while(pA){
            lengthA++;
            pA=pA->next;
        }
        while(pB){
            lengthB++;
            pB=pB->next;
        }
        pA=headA;
        pB=headB;
        if(lengthA<lengthB){
            swap(lengthA,lengthB);
            swap(pA,pB);
        }
        int gap=lengthA-lengthB;
        while(gap--){
            pA=pA->next;
        }
        while(pB){
            if(pA==pB)
            return pA;
            pA=pA->next;
            pB=pB->next;
        }
return NULL;
    }
};

142.环形链表II

. - 力扣(LeetCode)

代码如下:

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode* fast = head;
        ListNode* slow = head;
        while(fast != NULL && fast->next != NULL) {
            slow = slow->next;
            fast = fast->next->next;
            // 快慢指针相遇,此时从head 和 相遇点,同时查找直至相遇
            if (slow == fast) {
                ListNode* index1 = fast;
                ListNode* index2 = head;
                while (index1 != index2) {
                    index1 = index1->next;
                    index2 = index2->next;
                }
                return index2; // 返回环的入口
            }
        }
        return NULL;
    }
};

2*(x+y)=x+y+n*(z+y)

x+y=n*(y+z)

x=(n-1)*(y+z)+z;

n==1时x=z;

n>1,x=z+(n-1)*圈

index1=head;

index2=fast;

有环就会相遇在交点处,n=1,index2走的路程是z,n>1,index2多走了(n-1)圈也会在开口处相遇

  • 31
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值