题目:
将给定的单链表L: L 0→L 1→…→L n-1→L n,
重新排序为: L 0→L n →L 1→L n-1→L 2→L n-2→…
要求使用原地算法,并且不改变节点的值
例如:
对于给定的单链表{1,2,3,4},将其重新排序为{1,4,2,3}.
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public void reorderList(ListNode head) {
}
}
分析
思路一:
(这个思路是我一开始想的思路,不仅麻烦而且不符合题目要求)
1.将该链表复制为两个新的独立链表head1,head2
2.每次将head2的节点插入到head1的倒数第二个节点,然后将head1的最后两个节点一起拿出来链到我们的结果链表,然后将head1进行断链。
举个例子:
- 第一次将head2的0节点,插入到head1的倒数第二个节点,也就是n节点的前驱,这样head1节点的最后两个节点就是0->n,
- 之后就可以将这两个节点取出来插入到一个新的空链表,也就是我们的结果存储链表
- 然后将head1链表断链,经过第一次的循环,我们的结果链表中存了0->n,head1中的尾节点变成了n-1,下次我们还是找到倒数第二个节点插入即可
- 经过第二次插入,head1中最后两个节点为1->n-1,我们取出来,再断链!
3.而实际上这个算法是可行的,只是太过于复杂,因为我对链表的复制还不是很熟悉,以为直接指向就就可以了,但是实际上必须所有的节点全部创建新节点传值,才可以实现。
思路二:(标准解法)
1.找到head的中间节点,进行断链,分为head1,head2
2.将head2进行原地反转
3.将head2和head1进行交叉插入即可
这个方法是我没有想到的,首先如何找到中间节点,这样就要用到快慢节点去寻找,设置一个快节点,慢节点即可。其次链表的原地反转也是我没有接触过的,因为之前对算法基本没怎么学!后来链表的原地反转是从头开始,每次断一个节点,连一个节点!
public static void reorderList1(ListNode head) {
if(head == null || head.next == null) return;
ListNode fastPoint = head;//快节点
ListNode slowPoint = head;//慢节点
ListNode midPoint;//中间节点
//找到中间节点
while(fastPoint.next != null && fastPoint.next.next != null ){
fastPoint = fastPoint.next.next;
slowPoint = slowPoint.next;
}
midPoint = slowPoint;
//断成两个链表
ListNode head2 = midPoint.next;
midPoint.next = null;
//后半段链表翻转
ListNode q = head2.next;
head2.next = null;
while(q != null){
ListNode qNext = q.next;
q.next = head2;
head2 = q;
q = qNext;
}
//将head2 插入head1
ListNode head1 = head;
while( head2 != null){
ListNode newList = head2.next;
head2.next = head1.next;
head1.next = head2;
head2 = newList;
head1 = head1.next.next;
}
}
总结:
1.对于快慢节点寻找中间节点的方法第一次接触,另外我还知道快慢节点还可以判断是否为循环链表
2.链表原地反转也是我不会的,也要仔细琢磨
3.中间程序运行有一个bug就是,以后对于链表是否为空的判断,一定要先判断前驱节点,再判断后驱节点,例如不能这么写while(p->next && p),这样就会出现空指针异常的情况
4.关于链表复制和断链的问题,我要切记每次遍历只要将头结点记录好就行了,链表就不会断。链表的复制不是简单的指向