817. 链表组件
给定一个链表(链表结点包含一个整型值)的头结点 head。
同时给定列表 G,该列表是上述链表中整型值的一个子集。
返回列表 G 中组件的个数,这里对组件的定义为:链表中一段最长连续结点的值(该值必须在列表 G 中)构成的集合。
示例1:
输入:
head: 0->1->2->3
G = [0, 1, 3]
输出: 2
解释:
链表中,0 和 1 是相连接的,且 G 中不包含 2,所以 [0, 1] 是 G 的一个组件,同理 [3] 也是一个组件,故返回 2。
示例2:
输入:
head: 0->1->2->3->4
G = [0, 3, 1, 4]
输出: 2
解释:
链表中,0 和 1 是相连接的,3 和 4 是相连接的,所以 [0, 1] 和 [3, 4] 是两个组件,故返回 2。
注意:
- 如果 N 是给定链表 head 的长度,1 <= N <= 10000。
- 链表中每个结点的值所在范围为 [0, N - 1]。
- 1 <= G.length <= 10000
- G 是链表中所有结点的值的一个子集.
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/linked-list-components
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
-
定义链表
private static class ListNode {
private int val;
private ListNode next;
public ListNode(int val) {
this.val = val;
}
public ListNode(int[] arr) {
if (arr == null || arr.length == 0) throw new NullPointerException("array is empty");
this.val = arr[0];
ListNode cur = this;
for (int i = 1; i < arr.length; i++) {
cur.next = new ListNode(arr[i]);
cur = cur.next;
}
}
@Override
public String toString() {
StringBuilder res = new StringBuilder();
ListNode cur = this;
while (cur != null) {
res.append(cur.val + "->");
cur = cur.next;
}
res.append("NULL");
return res.toString();
}
}
-
1.迭代法
思路 : 同时遍历链表和数组
- 同时遍历链表和数组,如果链表中存在节点值和数组中元素相同,则标记 isContains 为true
- 如果包含相同元素的话,做一个标记,如果是连续节点包含的话,只有第一次才计数, 也就是一个组件
- 注意:每次遍历链表时都要再将 isContains 赋值为 false
public static int numComponents(ListNode head, int[] G) {
if (G == null || G.length == 0) return 0;
ListNode cur = head;
int res = 0;
boolean isContains = false; //标记是否包含相同元素
boolean isComponent = false;
while (cur != null) {
isContains = false;
for (int i = 0; i < G.length; i++) {
if (cur.val == G[i]) {
isContains = true;
break;
}
}
if (isContains) {
if (!isComponent) {
isComponent = true;
res++;
}
} else {
if (isComponent) {
isComponent = false;
}
}
cur = cur.next;
}
return res;
}
复杂度分析:
-
时间复杂度:O(N * G), N 和 G 分别为链表和数组的长度, 执行效率很低,不推荐使用
-
空间复杂度:O(1), 只需要常数级别的空间复杂度
-
2. set集合法
思路:利用set集合中的contains()方法,以空间换时间
- 将数组放入set集合中
- 遍历该链表,判断该节点的值是否存在于set集合中,如果存在继续遍历到下一个不存在的的节点,则为一个组件
- 如果不存在,继续往下遍历即可
- 注意:当链表的最后一个节点的值也包含于set时,要注意如果下一个节点为空的话,也应该将组件 +1
![](https://img.haomeiwen.com/i9191278/e21874c4980fcfc7.png)
public static int numComponents2(ListNode head, int[] G) {
if (G == null || G.length == 0) return 0;
Set<Integer> set = new HashSet<>();
for (int num : G) {
set.add(num);
}
ListNode cur = head;
int res = 0;
while (cur != null) {
if (set.contains(cur.val)) {
cur = cur.next;
if (cur == null || !set.contains(cur.val)) {
res++;
}
} else {
cur = cur.next;
}
}
return res;
}
复杂度分析:
- 时间复杂度:O(N + G),N为链表的长度,G为数组长度, 我们需要遍历一次数组和一次链表
- 空间复杂度:O(G), G为数组长度, 由于需要将数组放入set集合中,会浪费G个空间
-
测试用例
public static void main(String[] args) {
int[] array = new int[] {0, 1, 2, 3, 4};
int[] G = new int[] {0, 3, 1, 4};
ListNode listNode = new ListNode(array);
System.out.println(listNode);
System.out.println(" 链表组件" + numComponents(listNode, G));
}
-
结果
0->1->2->3->4->NULL
链表组件2
-
源码
-
我会每天更新新的算法,并尽可能尝试不同解法,如果发现问题请指正
- Github
网友评论