链表双指针

链表双指针

  • 框架
  • 刷题
    • 0019. 删除链表的倒数第 K 个节点
    • 0021. 合并两个有序链表
    • 0023. 合并 k 个升序链表
    • 0142. 环形链表Ⅱ (0141)
    • 0160. 相交链表
    • 0234. 回文链表
    • 0876. 链表的中间节点

框架

刷题

0019. 删除链表的倒数第 K 个节点

题目: 给定一个链表, 删除链表的倒数第 k 个节点, 并返回链表的头节点.

示例:

输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]

思路:

// 首先想到双指针技巧中的快慢指针.
// 1. 先让快指针走 k 步.
// 2. 再让快慢指针同时走 n-k 步.
// 3. 当快指针指向尾部时, 满指针指向倒数第 k 个节点.

代码:

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummy = new ListNode(-1);
        dummy->next = head;
        ListNode *p1 = dummy, *p2 = dummy;	// 首先想到双指针技巧中的快慢指针.
        for (int i = 0; i < n; ++i) {		// 1. 先让快指针走 k 步.
            p1 = p1->next;
        }
        while (p1->next != nullptr) {		// 2. 再让快慢指针同时走 n-k 步.
            p1 = p1->next;					// 3. 当快指针指向尾部时, 满指针指向倒数第 k 个节点.
            p2 = p2->next;
        }
        ListNode *temp = nullptr;
        temp = p2->next;
        p2->next = p2->next->next;
        temp->next = nullptr;
        return dummy->next;
    }	//     l     r
};		// -1, 1, 2, 3   k=2

0021. 合并两个有序链表

题目: 给定两个升序链表, 将其合并为一个升序链表并返回.

示例:

输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]

思路:

// 1. 利用双指针, 每个指针指向一个链表.
// 2. 比较指针对应的值, 将小的节点指向大的节点.
// 3. 剩下的, 谁还没排完都接在最后面.

代码:

class Solution {
public:
    ListNode* mergeTwoLists(ListNode* list1, ListNode* list2) {
        ListNode* dummy = new ListNode(-1), * p = dummy;
        ListNode* p1 = list1, * p2 = list2;			// 1. 利用双指针, 每个指针指向一个链表.
        while (p1 != nullptr && p2 != nullptr) {
            if (p1->val < p2->val) {				// 2. 比较指针对应的值, 将小的节点指向大的节点.
                p->next = p1;
                p1 = p1->next;
            } else {
                p->next = p2;
                p2 = p2->next;
            }
            p = p->next;
        }
        if (p1 != nullptr) {						// 3. 剩下的, 谁还没排完都接在最后面.
            p->next = p1;
        }
        if (p2 != nullptr) {
            p->next = p2;
        }
        return dummy->next;
    }   //   1       2
};      //  [1, 3]  [1, 4]

0023. 合并 k 个升序链表

题目: 给定一个链表数组, 每个链表都是升序排列, 将这些链表合成一个升序链表.

示例:

输⼊:lists = [[1,4,5],[1,3,4],[2,6]]
输出:[1,1,2,3,4,4,5,6]
解释:链表数组如下:
[
 1->4->5,
 1->3->4,
 2->6
]
将它们合并到⼀个有序链表中得到。
1->1->2->3->4->4->5->6

思路:

// 构建一个优先队列,升序排列(小根堆).
// 每次比较, 每个链表都放进去一个.
// 当那个链表上的值为最小值时, 再添加新的元素进去.

代码:

class Solution {
	ListNode* mergeKLists(vector<ListNode*> lists) {
		struct {
			bool operate()(ListNode* a, ListNode* b) {
				return a->val > b->val;
			}
		};
		priority_queue<ListNode*, vector<ListNode*>, comp> q;// 构建一个优先队列,升序排列(小根堆).
		
		for (ListNode* node : lists) {						// 每次比较, 每个链表都放进去一个.
			if (node != nullptr) q.push(node);
		}
		ListNode* dummy = new ListNode(-1), * p = dummy;
		while (!q.empty()) {
			ListNode* node = q.top();						// 当那个链表上的值为最小值时, 再添加新的元素进去.
			q.pop();
			p->next = node;
			p = p->next;
			if (node->next != nullptr) q.push(node->next);		
		}
		return dummy->next;
	}
};

0142. 环形链表Ⅱ (0141)

题目: 给定一个链表, 判断是否有环,有的话返回入环节点.

示例:

输入:head = [3,2,0,-4], pos = 1
输出:返回索引为 1 的链表节点
解释:链表中有一个环,其尾部连接到第二个节点。

思路:

// 判断有无环形链表, 想到快慢指针.
// 慢指针走一步, 快指针走两步.
// 当有环的话总会相遇.
// 同时将快指针指向dummy, 快慢指针同速运动, 相遇处为入环点.
// 示例:
// idx: -1, 0, 1, 2, 3  pos=1
// [-1,-1] [0,1] [1,3] [2,2] | [2,-1] [3,0] [1,1]
// idx: -1, 0, 1, 2, 3  pos=-1
// [-1,-1] [0,1] [1,3]
// if(fast=slow) return true; while(fast->next&&fast->next->next) return false 

代码:

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode* dummy = new ListNode(-1);
        dummy->next = head;
        ListNode* fast = dummy, * slow = dummy;
        while (fast->next && fast->next->next) {	// 慢指针走一步, 快指针走两步.
            slow = slow->next;
            fast = fast->next->next;
            if (fast == slow) {
                fast = dummy;
                while (fast != slow) {
                    slow = slow->next;
                    fast = fast->next;
                }
                return fast;
            }
        }
        return nullptr;   
    }
};

0160. 相交链表

题目: 给定两个头节点 headAheadB, 判断连链表有无相交.

示例:

输入:intersectVal = 8, listA = [4,1,8,4,5], listB = [5,6,1,8,4,5], skipA = 2, skipB = 3
输出:Intersected at '8'
解释:相交节点的值为 8 (注意,如果两个链表相交则不能为 0)。

思路:

定义两个指针 p1 和 p2, 分别指向 headA 和 headB.
p1 走完 A 链表后, 继续走 B 链表.
p2 走完 B 链表后, 继续走 A 链表.
若不相交, 则 p1==p2 为 nullptr 时停止.
若相交, 则 p1==p2 为相交点时停止.

代码:

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode *p1 = headA, *p2 = headB;	// 定义两个指针 p1 和 p2, 分别指向 headA 和 headB.
        while (p1 != p2) {
            if (p1 == nullptr) p1 = headB;	// p1 走完 A 链表后, 继续走 B 链表.
            else p1 = p1->next;
            if (p2 == nullptr) p2 = headA;	// p2 走完 B 链表后, 继续走 A 链表.
            else p2 = p2->next;
        }
        return p1;							// 若不相交, 则 p1==p2 为 nullptr 时停止.
    }										// 若相交, 则 p1==p2 为相交点时停止.
};

0234. 回文链表

题目: 给定一个链表, 判断该链表是否回文.

示例:

输入:head = [1,2,2,1]
输出:true

思路:

1. 找到链表的中间节点.
2. 反转后半部分链表.
3. 判断是否回文.
// pre    cur    next          pre  cur  next
// null < head < node < node < node 
// idx [0, 1, 2, 3, 4]
// [0,1][1,3] reutrn 2

代码:

bool isPalindrome(ListNode *head) {
    ListNode *p1 = head, *p2 = head;	// 1.找到链表的中间节点.
    while (p2->next && p2->next->next) {
        p1 = p1->next;
        p2 = p2->next->next;
    }
    ListNode *left = head, *right = reverse(p1->next);	//2. 反转后半部分链表.
    while (right) {
        if (left->val != right->val) return false;
        left = left->next;
        right = right->next;
    }
    p1->next = reverse(reHead);
    return true;
}
/* 从 head 处反转链表 */
ListNode *reverse(ListNode *head) {
    ListNode *pre = nullptr, *cur = head;
    while (cur) {
        ListNode *next = cur->next;
        cur->next = pre;
        pre = cur;
        cur = next;
    }
    return pre;
}

0876. 链表的中间节点

题目: 给定一个链表, 返回链表的中间节点.

示例:

输入:[1,2,3,4,5,6]
输出:此列表中的结点 4 (序列化形式:[4,5,6])
由于该列表有两个中间结点,值分别为 3 和 4,我们返回第二个结点。

思路:

// 寻找链表的中间节点, 则想到快慢指针.
// 定义一个虚拟头节点 dummy->head.
// 慢指针走一步, 快指针走两步.
// 当快指针到头的时候, 慢指针于中间.
// 示例:
// idx: -1, 0, 1, 2, 3, 4
// [-1,-1] [0,1] [1,3]
// idx: -1, 0, 1, 2, 3
// [-1,-1] [0,1] [1,3] 
// while(fast->next&&fast->next->next) return slow->next;

代码:

class Solution {
public:
    ListNode* middleNode(ListNode* head) {			// 寻找链表的中间节点, 则想到快慢指针.
        ListNode* dummy = new ListNode(-1);			// 定义一个虚拟头节点 dummy->head.
        dummy->next = head;
        ListNode* slow = dummy, * fast = dummy;
        while (fast->next && fast->next->next) {	// 慢指针走一步, 快指针走两步.
            slow = slow->next;						// 当快指针到头的时候, 慢指针于中间.
            fast = fast->next->next;
        }
        return slow->next;
    }
};

你可能感兴趣的:(链表,数据结构,c++)