链表学习一
两个链表公共子节点
题目
输入两个链表,找出它们的第一个公共节点。两个链表的头结点都是已知的,相交之后成为一个单链表,但是相交的位置未知,并且相交之前的结点数也是未知的,请设计算法找到两个链表的合并点。
解题思路
-
首先想到蛮力遍历法。遍历第一个链表的同时依次与第二个链表的元素比较,时间复杂度高,为O(n^2)
-
通过可用的数据结构, 可采用哈希或者集合,将一个链表存入Map或者set,之后遍历第二个链表,一共需要遍历两次,时间复杂度为O(n),空间复杂度为O(n)
-
栈。将两个链表分别入两个栈,之后同时出栈,元素一致则继续,最晚出栈的元素即为所找位置
-
拼接两个字符串
A: 0-1-2-3-4-5
B: a-b-4-5将它们拼接成AB,BA
AB: 0-1-2-3-4-5-a-b-4-5
BA: a-b-4-5-0-1-2-3-4-5right_a和right_b是一样的,这样同时遍历就能找到相同节点。
public ListNode findFirstCommonNode(ListNode pHead1, ListNode pHead2) { if (pHead1 == null || pHead2 == null) { return null; } ListNode p1 = pHead1; ListNode p2 = pHead2; while (p1 != p2) { p1 = p1.next; p2 = p2.next; if (p1 != p2) { //防止没有公共节点而死循环 //一个链表访问完了就跳到另外一个链表继续访问 if (p1 == null) { p1 = pHead2; } if (p2 == null) { p2 = pHead1; } } } return p1; }
-
差和双指针。第一轮遍历,求出两个链表长度差diff,第二轮遍历,长的先走diff,同时前进,相同时即为公共节点。
判断链表是否为回文序列
解题思路
回文序列涉及到倒序比较的问题,一般可以想到栈和双指针。使用栈可以将链表入栈出栈做到倒序从而进行遍历比较,使用双指针的快慢指针可以一次遍历而找到链表的中心位置。另外还可以创建一个新链表来逆序存储原始链表(可以使用头插法)从而进行反转链表。
合并有序链表
合并两个有序链表
将两个升序链表合并为一个新的升序链表并返回,新链表是通过拼接给定的两个链表的所有节点组成的。
解题思路
可以新建一个链表然后同时遍历两个链表,每次选较小的元素拼上去;
也可以一个链表不动将领一个链表插入相应位置。
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
ListNode prehead = new ListNode(-1);
ListNode prev = prehead;
while (list1 != null && list2 != null) {
if (list1.val <= list2.val) {
prev.next = list1;
list1 = list1.next;
} else {
prev.next = list2;
list2 = list2.next;
}
prev = prev.next;
}
// 最多只有一个还未被合并完,直接接上去就行了,这是链表合并比数组合并方便的地方
prev.next = list1 == null ? list2 : list1;
return prehead.next;
}
合并K个有序链表
public ListNode mergeKLists(ListNode[] lists) {
ListNode res = null;
for (ListNode list: lists) {
res = mergeTwoLists(res, list);
}
return res;
}
总结
链表学习一中没有涉及到什么困难的算法,在面对算法题时,解题思路还是要过一遍可以用的数据结构,之后再找特殊方法。
双指针思想里的快慢指针可以解决链表特定位置的元素的问题,例如寻找中间节点、寻找倒数第K个元素,旋转链表等。