链表反转-问题描述
- 现存一个单项链表,next指向下一个节点(如图),node1->node2->node3->node4->node5
- 问:如何实现把链表反转成:node5->node4->3->node2->node1
初步思想
- 我的第一思想是:我遍历到最后然后再来倒序的进行链表反转;感觉对于反转的话,第一时间我肯定需要找到最后一个节点,然后把他变为头节点,然后再找到次级节点然后再反转指针,不过这个怎么实现呢,一头晕。bigo,突然想了一下,我递归到最深层,然后把指针反转。
- 初步去实现了一下代码,没有深层次的解。因为这种只会为了实现时间复杂度和空间复杂度都会成阶层的增加。
public ListNode test(ListNode head){
ListNode last = head.next;
if(head.next==null){
return head;
}else{
test(head.next);
}
// 找到last最后的一个元素,然后指向head
ListNode curr = last;
while (curr!=null){
}
}
解法
迭代法
-
我可以新建立一个节点prevNode,然后遍历整个原链表,当遍历到第一个元素node1的时候,把prevNode的next指向node;
-
当遍历第二个元素node2的时候,用一个临时的next元素存储prevNode.next元素,即node1;接下来prevNode.next指向node2,后续node2.next指向临时的next元素,即node1;
-
当遍历第三个元素node3的时候,用一个临时的next元素存储prevNode.next元素,即node2;接下来prevNode.next指向node3,后续node3.next指向临时的next元素,即node2;
-
当遍历第四个元素node4的时候,用一个临时的next元素存储prevNode.next元素,即node3;接下来prevNode.next指向node4,后续node4.next指向临时的next元素,即node3;
-
当遍历第五个元素node5的时候,用一个临时的next元素存储prevNode.next元素,即node4;接下来prevNode.next指向node5,后续node5.next指向临时的next元素,即node4;
是不是结果就已经把链表反转了node5->node4->3->node2->node1。
public ListNode flipNode(ListNode node){
//prevNode节点
ListNode prev = null;
// 下一个节点
ListNode next ;
// 循环节点
ListNode curr = node;
// 遍历最后节点结束
while (curr!=null){
// 记录next节点
next = curr.next;
// 指向前一个节点
curr.next=prev;
// 记录前一个节点
prev = curr;
// 当前执行的节点
curr = next;
}
return prev;
}
递归法
思路:
-
当前节点node的下一个节点的next指针指向当前节点即:node.next.next=node;
-
当前节点node的next指向null;(这里存在一个问题就是node2的指针指向了node1,那么浅蓝色的这个指针也没有了,那么就找不到node3节点了)
-
那么通过迭代的方式直接倒序的方式来执行,先迭代到最后一个元素开始执行次方式那么:
-
后续就都是一样的操作了。话不多说直接上代码
public ListNode flipNode1(ListNode node){
// 临界点,边界值返回条件
if(node==null||node.next==null){
return node;
}
// 递归找最后一个元素
ListNode last = flipNode1(node.next);
// node的下一个节点的next指向node
node.next.next=node;
// node的next指向null
node.next=null;
return last;
}
升级版本来了
既然是链表反转,那么可以指定一个区间进行局部链表的反转?
链表:node1->node2->node3->node4->node5
指定区间[2,4],那么结果是node1->node4->node3->node2->node5;
这个时候大家能理解透彻前面的解法,其实这个升级版本也不是太难,也有一些自己的想要解的想法了,其实大部分还是差不多的。(其实我也是这么想的,然而还是花了一定的时间写出来)
还是使用的递归法,具体细节就不细讲了,简单来说就是记录区间前一个节点和区间后一个节点,然后区间中实现链表反转
private ListNode prev = null;
private ListNode next = null;
public ListNode flip(ListNode node,int n,int m,int num){
num++;
if(num==n-1){
prev = node;
}else if(num>=n&& num<=m){
if(node==null||node.next==null||(num==m)){
next=node.next;
prev.next=node;
return node;
}
ListNode last = flip(node.next,n,m,num);
node.next.next=node;
if(num==n){
node.next=next;
}else{
node.next=null;
}
return last;
}
return flip(node.next,n,m,num);
}
总结
链表反转虽然是一个很简单的算法,但是真正实际写的时候还是有很多需要注意的,比如使用迭代解法的时候,是需要记录prev节点和next节点,还要考虑while的结束条件;递归解法其实也是需要考虑递归临界条件,还有就是空间的思维,哪个节点的next指向哪里。所以我不想做一个只会伪代码的人,想法出来一定要去写,不然以后你的想法都会消失,要相信实操过后,身体是会有记忆的。