1 最简单的链表反转
- Reverse Linked List
链表反转一般会有递归与非递归的写法,递归写法代码简洁,看起来很酷,但是难以理解
1.一般写法
public ListNode reverseList(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode pre = head;
ListNode cur = head.next;
while (cur != null) {
ListNode t = cur.next;
cur.next = pre;
pre = cur;
cur = t;
}
head.next = null;
return pre;
}
其实原理 很简单,改变指针方向即可。
容易犯错的点就是忽略了边界值以及最后head.next需要指向空,即末尾。
2. 扩展写法
也可以计算出点个数来反转
private ListNode reverseList(ListNode head) {
int length = 0;
ListNode p = head;
while (p != null) {
length++;
p = p.next;
}
ListNode listNode = reverseN(head, length);
return listNode;
}
private ListNode reverseN(ListNode head, int length) {
ListNode pre = null;
ListNode cur = head;
while (length > 0) {
ListNode n = cur.next;
cur.next = pre;
pre = cur;
cur = n;
length--;
}
return pre;
}
虽然这种写法没有第一种简洁,但是如果想反转的不是全部节点,而是前多少个节点,这种写法就显出威力了,只需要稍微变形即可。如果全部反转,那么头节点的Next指向Null,如果部分反转,只需要把next的节点指向其余的节点即可。
private ListNode reverseList(ListNode head) {
int length = 0;
ListNode p = head;
while (p != null) {
length++;
p = p.next;
}
ListNode listNode = reverseN(head, 4);
return listNode;
}
private ListNode reverseN(ListNode head, int length) {
ListNode pre = null;
ListNode cur = head;
while (length > 0) {
ListNode n = cur.next;
cur.next = pre;
pre = cur;
cur = n;
length--;
}
if (head != null) {
head.next = cur;
}
return pre;
}
3 递归
递归写法一般都特别美
3.1 正常反转
思路其实也挺简单,不要跳进循环中。
一般写递归会注意三个条件:
1) 终止条件
2) 返回值
3) 递推式
private ListNode reverseList(ListNode head) {
if(head==null || head.next==null){
return head;
}
ListNode last = reverseList(head.next);
head.next.next=head;
head.next=null;
return last;
}
如上面这个,递推式即使原来的子问题,针对链表一般是指除当前要处理的元素之外的,因此一般都是node.next
终止条件即为空的时候
处理返回值。
除了递推之外的这个元素需要特别处理,假设后面已经是反转后的,那么再连接上当前的就是整个链表了。
3.2前 N个节点的反转
前N个节点的反转与之前的略微不同,主要区别是前面反转后的最后一个节点指向Null,代表结束
而这个最后一个节点指向其余的未反转的节点。
未反转的节点是当n==1时的next
ListNode next=null;
private ListNode reverseN(ListNode head, int length) {
if(length==1){
next=head.next;
return head;
}
ListNode tail = reverseN(head.next, length - 1);
head.next.next=head;
head.next=next;
return tail;
}
2. lintcode 92
反转从m到n的节点
public ListNode reverseBetween(ListNode head, int m, int n) {
if (head == null || head.next == null) {
return head;
}
ListNode pre = null;
ListNode cur = head;
while (m > 1) {
pre = cur;
cur = cur.next;
m--;
n--;
}
ListNode h = pre;
ListNode tail = cur;
while (n > 0) {
ListNode t = cur.next;
cur.next = pre;
pre = cur;
cur = t;
n--;
}
if (h == null) {
head = pre;
} else {
h.next = pre;
}
tail.next = cur;
return head;
}
如果用递归会比较难理解点
public ListNode reverseBetween(ListNode head, int m, int n) {
if(m==1){
return reverseN(head,n);
}
head.next= reverseBetween(head.next, m-1,n-1);
return head;
}
ListNode sucessor = null;
private ListNode reverseN(ListNode head, int m) {
if (head == null || head.next == null) {
return head;
}
if (m == 1) {
sucessor = head.next;
return head;
}
ListNode li = reverseN(head.next, m - 1);
head.next.next = head;
head.next = sucessor;
return li;
}
如果m=1,相当于反转从第1个到第n个,也就是说反转前n个,就是利用之前的算法。
如果m>1,比如revers(head,3,6)反转第三个到第6个,首先取1个元素后,那就是反转其余的第2个到第5个,即
revers(head.next,2,5),假设其余的2,5反转完成,原来的head保持不变,只需要把Head.next指向其余的即可。