Link链表的反转

Link链表的反转

Posted by 山大王 on August 24, 2017

“有茶有酒多兄弟,急难何曾见一人?”

正文

上一篇简单介绍了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;
    }
}

上面是一个节点类,下面再写一个打印的类

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;//返回反转后的节点。
}

下面初始化一下链表

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;
    }