单链表反转
package LinkListReview;
class ListNode {
int val;
ListNode next;
public ListNode(int val) {this.val = val;}
}
/**
* 题目描述:反转一个单链表。
* -------------------------------------------
* 示例:
* 输入: 1->2->3->4->5->NULL
* 输出: 5->4->3->2->1->NULL
*/
/*
思路:
使用双指针,一个指针用来遍历原链表 a,另一个用来指向带返回链表 b 的头节点
每遍历一个原链表的节点,就在 b 的头节点前面加一个节点
*/
class Problem1 {
public ListNode reverseList(ListNode head) {
ListNode newhead = null; //待返回链表 b 的头节点,初始化成 null
ListNode current = head; //用来遍历链表 a 的指针
//遍历链表 a,把 a 里的数据添加到 b 的头部,这其实就类似于最简单的在链表末尾追加元素一样,只不过现在是在链表头部追加元素了
while(current != null) {
ListNode temp = newhead; //当前头
newhead = new ListNode(current.val); //一个新的头
newhead.next = temp; //新头指向旧头
current = current.next;
}
return newhead; //返回新链表
}
}
/**
* 题目描述:反转从位置 m 到 n 的链表。请使用一趟扫描完成反转。
* 说明:
* 1 ≤ m ≤ n ≤ 链表长度。
* ------------------------------------------------------------------------------
* 示例:
* 输入: 1->2->3->4->5->NULL, m = 2, n = 4
* 输出: 1->4->3->2->5->NULL
*/
/*
思路:
首先是最简单的单链表的反转,然后再分析,可以把它想象成是一条线,我们把它切成三段,
一段是待反转前面的部分,一段是待反转后面的部分,最后一段是待反转的部分,
我们只需要把待反转的部分转过来,然后再与前面的两段缝合到一起即可
*/
class Problem2 {
public ListNode reverseBetween(ListNode head, int m, int n) {
ListNode newhead = null; //反转部分的头
ListNode newtail = null; //反转部分的尾
ListNode current = head; //用来遍历原链表的指针
ListNode leftLink = null; //带反转部分左侧的链表
int i = 1;
if(m == n) return head; //如果 m == n,那么说明没有要反转的部分,直接返回原链表
//遍历原链表
while(current != null) {
if(i >= m && i <= n) {
//添加新的头节点
ListNode temp = newhead;
newhead = new ListNode(current.val);
newhead.next = temp;
if(newtail == null) newtail = newhead; //如果是第一次添加,那么该节点也是反转部分的尾节点
}
//记录一下待反转部分的左半部分,以便缝合
if(i == m - 1) {
leftLink = current;
}
//当反转完成之后进行缝合
if(i == n) {
if(m == 1) head = newhead; //如果 m 是 1,那么待反转的左半部分就是空的,直接让原来的头等于现在的头即可
if(m != 1) leftLink.next = newhead; //如果 m 不等于 1,那么就把左半部分和反转之后的链表缝合起来
newtail.next = current.next; //把反转之后的链表和右半部分缝合起来
}
i++;
current = current.next;
}
return head;
}
}
引申:
链表反转可以借用双指针来完成,在双向链表中同样可以用这种方法,但双向链表有一种更快的方法,就是用两个指针,一个指向头,一个指向尾,然后两边同时向中间靠拢,互换里面存储的数据即可。