链表part02

今天是8月3日,学习了链表的第二部分。

  1. 交换链表两个节点,考察对next的操作和tmp的灵活运用。
  2. 删除链表的倒数第N个节点,双指针减少遍历次数。
  3. 链表相交,移动链表尾对齐,其实就是动长链表的指针。
  4. 环形链表,记住方法。

4. 24交换链表两个节点

题目:给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。


交换节点比上一道更明显,需要更改原始列表中的节点的next。那么需要合理设置指针+更换next的顺序+及时存储tmp

  1. 注意指针指的位置:比如节点1->2->3,那么cur指向1,可以操作2和3的next。
ListNode* swapPairs(ListNode* head) {
    ListNode* dummyHead = new ListNode(0); // 设置一个虚拟头结点
    dummyHead->next = head; 
    ListNode* cur = dummyHead;
    while(cur->next != nullptr && cur->next->next != nullptr) {
        ListNode* tmp = cur->next; // 记录临时节点
        ListNode* tmp1 = cur->next->next->next; // 记录临时节点

        cur->next = cur->next->next;    // 步骤一
        cur->next->next = tmp;          // 步骤二
        cur->next->next->next = tmp1;   // 步骤三

        cur = cur->next->next; // cur移动两位,准备下一轮交换
    }
    ListNode* result = dummyHead->next;
    delete dummyHead;
    return result;
}

5. 19删除链表的倒数第N个节点(双指针)

题目:删除链表的倒数第 n 个结点,并且返回链表的头结点。


链表只可以从前往后挨个遍历,想要一遍解决问题常用双指针。删除倒数第n个,只需要让fast早走n步,然后fast和slow同步走,fast走到末尾时,slow->next就是要被删除的节点。

要删除某个点,slow需要指着这个点的上一个节点,才可以做到删除。

ListNode* removeNthFromEnd(ListNode* head, int n) {
    // 虚拟头节点 
    ListNode* dummyhead = new ListNode(0);
    dummyhead->next=head;
	//快慢指针
    ListNode* fast = dummyhead;
    ListNode* slow = dummyhead;
	//先动fast指针
    while(fast->next!=nullptr){//保证动fast时不出界
        if(n>0){
            n--;
            fast=fast->next;
        }   
        else
            break; 
    }
	//fast和slow同时动,直到fast到达最后一个节点停止
    while(fast->next!=nullptr){
        fast=fast->next;
        slow=slow->next;
    }
    // ListNode *tmp = slow->next;  C++释放内存的逻辑
    // slow->next = tmp->next;
    // delete tmp;
    slow->next=slow->next->next;
    return dummyhead->next;
}

6. 链表相交(移动链表尾对齐)

题目:两个单链表的头节点 headAheadB ,找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null


求两个链表交点节点的指针。最普通的想法,两个for嵌套,但是考虑相交的特点,相交点后面的都一样,所以:

  1. 求出两个链表的长度,并求出两个链表长度的差值,然后让两个链表末尾对齐的位置。
  2. 此时比较curA和curB是否相同,如果不相同,同时向后移动curA和curB,如果遇到curA == curB,则找到交点。
  3. 否则循环退出返回空指针。
  1. 交点不是数值相等,而是指针相等。
  2. 链表尾对齐,其实就是curA移动到中间,剩下的A链表和B一样长。
 ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
     ListNode* curA = headA;
     ListNode* curB = headB;
     int lenA = 0, lenB = 0;
     while (curA != NULL) { // 求链表A的长度
         lenA++;
         curA = curA->next;
     }
     while (curB != NULL) { // 求链表B的长度
         lenB++;
         curB = curB->next;
     }
     curA = headA;
     curB = headB;
     // 让curA为最长链表的头,lenA为其长度
     if (lenB > lenA) {
         swap (lenA, lenB);
         swap (curA, curB);
     }
     // 求长度差
     int gap = lenA - lenB;
     // 让curA和curB在同一起点上(末尾位置对齐)
     while (gap--) {
         curA = curA->next;
     }
     // 遍历curA 和 curB,遇到相同则直接返回
     while (curA != NULL) {
         if (curA == curB) {
             return curA;
         }
         curA = curA->next;
         curB = curB->next;
     }
     return NULL;
 }

7. 142环形链表

题目:给定一个链表的头节点 head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null


a. 如何判定是否有环?

快慢指针法,定义 fast 和 slow 指针,从头结点出发,fast指针每次移动两个节点,slow指针每次移动一个节点,如果 fast 和 slow指针在途中相遇 ,说明这个链表有环。

  • fast指针一定先进入环中,如果fast指针和slow指针相遇的话,一定是在环中相遇。
  • 相对于slow来说,fast是一个节点一个节点的靠近slow的,所以fast一定可以和slow重合。

b. 如何判断环的起始点?

那么相遇时: slow指针走过的节点数为: x + y, fast指针走过的节点数:x + y + n (y + z),n为fast指针在环内走了n圈才遇到slow指针, (y+z)为 一圈内节点的个数A。

因为fast指针是一步走两个节点,slow指针一步走一个节点, 所以 fast指针走过的节点数 = slow指针走过的节点数 * 2:

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

两边消掉一个(x+y): x + y = n (y + z)

因为要找环形的入口,那么要求的是x,因为x表示 头结点到 环形入口节点的的距离。

所以要求x ,将x单独放在左面:x = n (y + z) - y = (n - 1) A + z

因此,发现从相遇点到入环点的距离z加上 n−1 圈的环长,恰好等于从链表头部到入环点的距离x。

因此,当发现 slow 与 fast 相遇时,再额外使用一个指针 ptr。起始,它指向链表头部;随后,它和 slow 每次向后移动一个位置。最终,它们会在入环点相遇。

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;
}

今日古诗

西江月·日日深杯酒满
朱敦儒〔宋代〕

日日深杯酒满,朝朝小圃花开。自歌自舞自开怀,无拘无束无碍。
青史几番春梦,黄泉多少奇才。不须计较与安排,领取而今现在。

上片起首写出词人终日醉饮花前的生活。深杯酒满见得饮兴之酣畅,小圃花开点出居处之雅致。无一字及人,而人的精神风貌已隐然可见。后二句则直陈快乐自由的情态;
下片文情陡变,两个对句表达了词人对世事人生的认识,末句是对上片所描述闲逸自得生活之底蕴的概括和揭示。全词用语浅自而意味悠远,清新淡雅,韵味天成,语意俱佳,流露出一种闲旷的情调

posted @ 2024-08-04 18:43  YueHuai  阅读(413)  评论(0编辑  收藏  举报