链表的常见算法题

链表的常见算法题:

1
2
3
4
5
6
7
解决链表的算法题通常有两种方法:

1、 借助容器(栈、数组、哈希表...)

2、 快慢指针

在要求空间复杂度的情况下,我们会使用快慢指针
1
2
3
4
5
1、
- 输入链表头节点,奇数长度返回中点,偶数长度返回上终点
- 输入链表头节点,奇数长度返回中点,偶数长度返回下终点
- 输入链表头节点,奇数长度返回中点前一个,偶数长度返回上终点
- 输入链表头节点,奇数长度返回中点前一个,偶数长度返回下终点
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
//输入链表头节点,奇数长度返回中点,偶数长度返回上终点
public static Node Mid1(Node head){

//当有0个节点时,返回null
//当有1 、 2 个节点时,返回头节点
if(head == null || head.next == null || head.next.next == null){
return head;
}

Node show = head.next;
Node fast = head.next.next;

while(fast.next != null && fast.next.next != null){
fast = fast.next.next;
show = show.next;
}

return show;


}

//输入链表头节点,奇数长度返回中点,偶数长度返回下终点
public static Node Mid2(Node head){
if(head == null || head.next == null){
return null;
}

if(head.next.next == null){
return head;
}

Node fast = head.next;
Node show = head.next;

while(fast.next != null && fast.next.next != null){
fast = fast.next.next;
show = show.next;
}

return show;
}

//输入链表头节点,奇数长度返回中点前一个,偶数长度返回上终点
public static Node Mid3(Node head){
if(head == null || head.next == null || head.next.next == null){
return head;
}

Node fast = head.next.next;
Node show = head;

while(fast.next != null && fast.next.next != null){
fast = fast.next.next;
show = show.next;
}

return show;
}


//输入链表头节点,奇数长度返回中点前一个,偶数长度返回下终点
public static Node Mid4(Node head){
if(head == null || head.next == null){
return head;
}

if(head.next.next == null){
return head;
}


Node show = head;
Node fast = head.next;


while(fast.next != null && fast.next.next != null){
fast = fast.next.next;
show = show.next;
}

return show;


}
1
2、将单链表按某值划分成左边小,中间相等,右边大的形式。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
public static Node sort(Node head,int num){

//将改变后的链表分成3段链表

//第一段是小于num的链表
//设置头节点和尾节点
Node sH = null;
Node sT = null;
//第二段是等于num的链表
Node eH = null;
Node eT = null;
//第三段是大于num的链表
Node mH = null;
Node mT = null;


//遍历链表
Node cur = head;
while(cur != null){
if(cur.vaule < num){
if(sH == null){
sH = cur;
sT = cur;
}else{
sT.next = cur;
sT = cur;
}
}
else if(cur.vaule == num){
if(eH == null){
eH = cur;
eT = cur;
}else{
eT.next = cur;
eT = cur;
}
}
else{
if(mH == null){
mH = cur;
mT = cur;
}else{
mT.next = cur;
mT = cur;
}
}

cur = cur.next;
}

//得到三段链表之后,就需要将三段链表连接起来
//小于区域的尾巴连接等于区域的头,等于区域的尾巴连上大于区域的头
//但需要考虑各段链表是否为空

//如果有小于num的数
if(sT != null){
//将小于num的链表的尾节点连接到等于num链表的头节点
sT.next = eH;
// 如果,等于num的链表为空,
// 将sT赋给eT,,因为eT要去连接mH,不可能用一个null去连接
eT = eT == null ? sT : eT;
}

//连接
if(eT != null){
eT.next = mH;
}

//因为我们没有设置null,所以在最后要设置
if(mH != null) {
mT.next = null;
}

//选出头节点,返回
return sH != null ? sH : (eH != null ? eH : mH);
}
1
3、给定一个单链表的头节点head,请判断该链表是否为回文结构
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public static boolean ispalindrome(Node head){
//奇数找到中点,偶数找到上中点
//上一题算法给出
Node mid = Mid1(head);

Node cur = mid.next;

mid.next = null;

//用于逆序的中间变量
Node next = null;

//逆序
while(cur.next != null){
next = cur.next;
cur.next = mid;

mid = cur;
cur = next;
}

cur.next = mid;


//前后比较
Node head1 = head;
Node head2 = cur;

while(head1 != null && head2 != null){
if(head1.vaule != head2.vaule){
return false;
}

head1 = head1.next;
head2 = head2.next;
}

return true;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
4、一种特殊的单链表节点类描述如下
class Node1{
int Vaiue;
Node1 Next;
Node1 rand;
Node(int Val){Value = val}

}

rand指针是单链表节点结构中新增的指针,rand可能指向链表中的任意一个节点,也可能指向null
给定一个由Node1节点类型组成的无环单链表的头节点head,请实现一个函数完成这个链表的复制,并返回复制的新链表的头节点

【要求】
时间复杂度O(N),额外空间复杂度O(1)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
//方法一
public static Node1 copyListWithRand1(Node1 head){

//用来对应复制的节点
//前面是原来的节点,后面是新复制的节点
HashMap<Node1, Node1> node1Map = new HashMap<>();

//循环,复制节点中的值
Node1 cur = head;
while(cur != null){
Node1 node1 = new Node1(cur.vaule);
//新老节点对应
node1Map.put(cur,node1);
cur = cur.next;
}

cur = head;
//新复制节点来设置next和rand
while(cur != null){
Node1 node = node1Map.get(cur);
Node1 next = node1Map.get(cur.next);
Node1 rand = node1Map.get(cur.rand);

node.next = next;
node.rand = rand;

cur = cur.next;
}


return node1Map.get(head);
}

//方法二
public static Node1 copyListWithRand2(Node1 head){

Node1 cur = head;

//将复制的节点连接到原来的节点的next上
// 原来的链表: 1 -> 2 -> 3 -> null
// 复制之后的链表: 1 -> 1' -> 2 -> 2' -> 3 -> 3' ->null
while(cur != null){
Node1 node = cur.next;

Node1 node1 = new Node1(cur.vaule);

cur.next = node1;
node1.next = node;

cur = cur.next.next;
}

cur = head;
//新的链表
Node1 curCopy = null;
Node1 next = null;
//设置新链表的rand值
while(cur != null){
next = cur.next.next;
curCopy = cur.next;
curCopy.rand = cur.rand != null ? cur.rand.next : null;

cur = next;
}

Node1 res = head.next;

cur = head;

//设置新链表的next
while(cur != null){
next = cur.next.next;
curCopy = cur.next;
cur.next = next;

curCopy.next = next != null ? next.next : null;
cur = next;
}
return res;
}
1
2
3
4
5
5、给定两个可能有环环可能无环的单链表,头节点head1和head2.
请实现一个函数,如果两个链表相交,请返回相交的第一个节点,如果不相交,返回null

【要求】
如果两个链表长度之和为N,时间复杂度请达到O(N) 额外空间复杂度请达到O(1)
1
2
3
4
5
6
7
8
分析:
这题需要两种情况
1、 两条链表都没有环
2、 两天链表都有环,两个有环的链表一定有公共环,同样需要分多种情况
没有相交
两个链表入环的节点是同一个
两个链表入环的节点不是同一个

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
public static Node getloopNode(Node head){
if(head.next == null || head.next.next == null || head.next.next == null){
return null;
}

Node node1 = head.next;
Node node2 = head.next.next;

while(node1 != node2){
if(node2.next == null || node2.next.next == null){
return null;
}

node2 = node2.next.next;
node1 = node1.next;
}

node2 = head;

while(node1 != node2){
node1 = node1.next;
node2 = node2.next;
}
return node1;
}

public static Node noLoop(Node head1,Node head2){
if(head1 == null || head2.next == null){
return null;
}

Node cur1 = head1;
Node cur2 = head2;

//记录两条链表节点个数的差值
int n = 0;

while(cur1.next != null){
n++;
cur1 = cur1.next;
}

while(cur2.next != null){
n--;
cur2 = cur2.next;
}

//如果两个链表的最后一个节点不相同,那么他们一定不会相交
if(cur1 != cur2){
return null;
}

//如果相交
// n 是两条链表的节点个数的差值,
// n 的正负值决定那一条链表更长
// 长的链表先走n 步,然后两条链表一起走。
cur1 = n > 0 ? head1 : head2;
cur2 = cur1 == head1 ? head2 :head1;

n = Math.abs(n);

while(n != 0){
n--;
cur1 = cur1.next;
}

while(cur1 != cur2){
cur1 = cur1.next;
cur2 = cur2.next;
}

return cur1;
}

public static Node BothLoop(Node head1,Node loopNode1,Node head2,Node loopNode2){
Node cur1 = null;
Node cur2 = null;

//是公共节点
if(loopNode1 == loopNode2){
cur1 = head1;
cur2 = head2;
int n = 0;

while (cur1 != loopNode1){
n++;
cur1 = cur1.next;
}

while (cur2 != loopNode1){
n--;
cur2 = cur2.next;
}


cur1 = n > 0 ? head1 :head2;
cur2 = cur1 == head1 ? head2 : head1;
n = Math.abs(n);
while (n != 0) {
n--;
cur1 = cur1.next;
}
while(cur1 != cur2){
cur1 = cur1.next;
cur2 = cur2.next;
}

return cur1;
}else{
cur1 = loopNode1.next;
while(cur1 != loopNode1){
if(cur1 == loopNode2){
return loopNode1;
}
cur1 = cur1.next;
}
return null;
}
}

链表的常见算法题
https://johnjoyjzw.github.io/2020/01/08/链表的常见算法题/
Author
John Joy
Posted on
January 8, 2020
Licensed under