“有茶有酒多兄弟,急难何曾见一人?”
正文
上一篇简单介绍了LinkedList,代码都很简单,基本上没有什么可说的,这一篇就来介绍一下链表的使用,反转链表,这个链表和LinkedList是不一样的,因为LinkedList是双向链表,不需要反转也可以从后往前遍历,这里创建的链表是单向的,只能从前往后,不能从后往前,如果想要从后往前遍历,就需要把链表反转,先创建一个节点类
LinkNode
1
2
3
4
5
6
7
8
public class LinkNode {
public int val;
public LinkNode next;
public LinkNode(int x) {
val = x;
}
}
上面是一个节点类,下面再写一个打印的类
printLink
1
2
3
4
5
6
7
8
public static void printLink(LinkNode mLink) {
LinkNode tempNoe = mLink;
while (tempNoe != null) {
System.out.print(tempNoe.val + "\t");
tempNoe = tempNoe.next;
}
System.out.println();
}
先来看第一个反转的方法
reverseList1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public static LinkNode reverseList1(LinkNode head) {
//反转当前链表,prev是指前一个节点,表示已经反转好的链表,最开始的时候为null
LinkNode prev = null;
while (head != null) {
//这里可以认为head是当前节点,因为下面通过不停的赋值,让head始终指向当前节点,
//tem是临时节点,保存的是当前节点的下一个,如果不保存,在执行head.next = prev
//的时候把当前节点之后的节点都搞丢了。
LinkNode tmp = head.next;
//让前一个节点等于当前节点的下一个(前一个节点是已经反转好的),这个是反转的关键,
//因为反转是从头结点开始的,而prev可以认为是前面几个已经反转好的节点,然后挂载在
//当前节点的下一个,所以这里不停的通过white循环,然后不断的把已反转好的prev挂载到
//当前节点后面,最终实现了链表的反转。
head.next = prev;
//挂载完之后让head等于prev节点,表示已经反转过来了,prev表示反转之后的链表
prev = head;
//让保存的tmp节点等于head,实际就相当于在往后挪了一位。然后在循环
head = tmp;
}
return prev;//返回反转后的节点。
}
下面初始化一下链表
initLink
1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static LinkNode initLink() {
LinkNode node1 = new LinkNode(1);
LinkNode node2 = new LinkNode(2);
LinkNode node3 = new LinkNode(3);
LinkNode node4 = new LinkNode(4);
LinkNode node5 = new LinkNode(5);
LinkNode node6 = new LinkNode(6);
node1.next = node2;
node2.next = node3;
node3.next = node4;
node4.next = node5;
node5.next = node6;
return node1;
}
在调用reverseList1方打印一下,看一下结果
1
2
1 2 3 4 5 6
6 5 4 3 2 1
上一排是正常打印,下一排是经过反转之后的结果,实现了链表的反转,那么还有没有其他方式,继续看
reverseList2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static LinkNode reverseList2(LinkNode head) {
//可以把head节点当做当前节点,如果当前节点为null,或者没有后继节点,直接返回当前节点,
//因为就一个节点没法反转
if (head == null || head.next == null)
return head;
//递归遍历,从当前节点的下一个节点开始,prev表示已经反转好的,所以这一步执行完之后
//当前节点head之后的都已反转完成(当前节点head没有反转)
LinkNode prev = reverseList2(head.next);
//既然head.next已经反转完成,那么head.next显然是已经反转到链表最后了,然后再让head等于head.next的下一个,
//把head节点放到已经反转的链表的最后。其实每次head.next都是为null,因为在下一步把它置为null了,然后
//再让head等于head.next的下一个
head.next.next = head;
//然后让head.next节点为空,因为这里当前节点head是已经反转好的最后一个节点,所以可以置为null。
head.next = null;
return prev;
}
再看一下打印结果
1
2
1 2 3 4 5 6
6 5 4 3 2 1
也实现了反转,如果不好理解可以在改一下,接着往下看
reverseList3
1
2
3
4
5
6
7
8
9
10
11
12
13
public static LinkNode reverseList3(LinkNode head) {
if (head == null || head.next == null)
return head;
//在reverseList2中可能这一点不太好理解,虽然prev是已经反转之后的列表,但是prev指向的
//是表头,那怎么样才能把head添加到表尾,只能通过循环,找到最后一个。
LinkNode prev = reverseList3(head.next);
LinkNode current = prev;
while (current.next != null)
current = current.next;//找最后一个
current.next = head;//把head添加到最后一个的next中
head.next = null;
return prev;
}
接着往下看
reverseList4
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
public static LinkNode reverseList4(LinkNode head) {
//这个和reverseList1差不多
if (null == head) {
return head;
}
//还是一样,pre是表示已经反转好的链表,这里的pre暂且认为他是等于head节点的,仅仅等于head这一个节点
//不包含head后面的节点,暂且这样认为,虽然不对
LinkNode pre = head;
LinkNode cur = head.next;//cur认为是当前节点
LinkNode _next;//_next认为是当前节点的下一个节点
while (null != cur) {
_next = cur.next;//保存当前节点的下一个节点
//pre是表达已经反转好的链表,让他等于当前链表的下一个,就是挂载在当前链表的下一个
cur.next = pre;
//挂载完之后表示又反转好了一个,然后让他等于pre,因为pre就是已经反转好的链表
pre = cur;
//跳到下一个链表循环,继续循环
cur = _next;
}
//将原链表的头节点的下一个节点置为null,再将反转后的头节点赋给head,这里为什么要置null,因为如果不置null,
//head.next一直是有值的,这个会造成死循环,那为什么reverseList1中最后没有把head.next置为null,那是因为一开始
//的时候就让head.next = prev; 而最开始prev是大于null的,其实已经把prev置为null了
head.next = null;
head = pre;//反转之后的链表赋值给head
return head;//其实上一步也可以不赋值,直接返回pre就可。
}
继续看
reverseList5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static LinkNode reverseList5(LinkNode prev, LinkNode curr) {
//prev是前一个节点,表示已经反转好的链表,可以为null,如果curr是first节点,那么prev就是null,
//就表示还没有排序
LinkNode next = curr.next;
if (next == null) {
// 没有下个节点则停止循环。
curr.next = prev;
return curr;
}
// 将反转好的节点prev赋值给当前节点的下一个,这里在第一次调用的时候prev为null,curr是头结点,
//所以第一步的时候就已经把尾节点置为null了。
curr.next = prev;
//这里是通过递归,不断的取出当前节点,然后在不断的让已反转好的节点挂载到当前节点中
return reverseList5(curr, next);
}
继续
reverseList6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public static LinkNode reverseList6(LinkNode head) {
//cuur可以认为是当前节点
LinkNode curr = head.next;
//_next表示是当前节点curr节点的下一个节点,在下面循环中用到
LinkNode _next = curr.next;
//让头结点孤立起来,让他没有后继节点,成为孤零零的一个节点,这时我们可以认为head节点是已经反转好的节点
head.next = null;
while (_next != null) {
//让已经反转好的节点head成为当前节点的下一个,实现反转
curr.next = head;
//因为curr链表已经是反转之后的了,所以让curr等于head,head表示已经反转好的链表
head = curr;
//让下一个节点成为当前节点,然后下面循环
curr = _next;
//移动到下一个节点,继续
_next = _next.next;
}
//curr是最后一个节点,因为上面循环结束的条件是_next != null,而_next可以认为是curr的下一个节点
//当curr的下一个节点为null的时候,说明curr是尾节点,所以这里把前面已经反转好的节点挂载到最后一个节点,
//最终实现所以节点反转。
curr.next = head;
return curr;
}
继续
reverseList7
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public static LinkNode reverseList7(LinkNode head, LinkNode next) {
//这个和reverseList5很相似,如果下一个节点为null,直接返回
if (next == null) {
return head;
}
//head可以认为是已经反转好的,如果head节点的下一个是next,要把head节点的下一个置为null,
//否则会出现死循环,因为head暂且认为是已经反转好的,然后会挂载到next的下面,如果不置为null,
//那么相互挂载,就会出现死循环。这里要明白这一点,head的next是已经反转好的head节点的下一个,
// 不是已经反转好的最后一个
if (head.next.equals(next)) {
head.next = null;
}
if (next.next != null) {
//保存next的下一个,下面递归的时候传进去
LinkNode next2 = next.next;
//head可以认为是反转好的,直接赋值给next的下一个
next.next = head;
//递归
return reverseList7(next, next2);
}
//将反转好的head赋值给next的下一个
next.next = head;
return next;
}
接着往下看
reverseList8
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static LinkNode reverseList8(LinkNode head) {
LinkNode tail = head.next;
if (tail.next != null) {
LinkNode prev = reverseList8(tail);// prev表示已经反转好的
LinkNode last = prev;
while (last.next != null)
last = last.next;//找到最后一个
last.next = head;//挂载到最后一个上
head.next = null;
return prev;
}
tail.next = head;
head.next = null;
return tail;
}
再看
reverseList9
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static LinkNode reverseList9(LinkNode head) {
//这个和第一个很相似
if (head == null || head.next == null) {
return head;
}
LinkNode preNode = null;
LinkNode curNode = head;
LinkNode nextNode;
while (curNode != null) {
nextNode = curNode.next;
curNode.next = preNode;
preNode = curNode;
curNode = nextNode;
}
return preNode;
}