每天一道算法题(java数据结构与算法)——>反转链表
Posted stormzhuo
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了每天一道算法题(java数据结构与算法)——>反转链表相关的知识,希望对你有一定的参考价值。
这是LeetCode上的 [024,反转链表],难度为 [简单]
题目
给定单链表的头节点 head ,请反转链表,并返回反转后的链表的头节点。
示例 1:
输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]
示例 2:
输入:head = [1,2]
输出:[2,1]
示例 3:
输入:head = []
输出:[]
题解1(递归方法)
题解
思路分析
递归反转其实就是从原链表的第一个存数据的结点开始,依次递归调用反转每一个结点,直到把最后一个结点反转完毕,整个链表就反转完毕。下面通过带哨兵结点的例子来说明(不带哨兵结点也是类似的)
原链表
步骤
- 调用reverse(Node curr)方法反转每一个结点,从元素1结点开始;
- 如果发现curr还有下一个结点,则递归调用reverse(curr.next)对下一个结点反转;
- 最终递归的出口是元素4结点,因为它没有下一个元素了,当到了出口处,以下分两种方式(带哨兵结点和不带哨兵结点)
- 带哨兵结点:让head(哨兵结点)指向元素4结点;共递归调用4次
- 不带哨兵结点:让4结点指向3结点,共递归调用3次
- 递归开始返回
带哨兵结点的递归过程
代码实现
public class Solution
public ListNode reverseList1(ListNode head)
/* 每次递归时都会做的条件判断
head == null是为了防止第一次通过对象调用reverseList时(递归还没开始),链表为空
head.next == null是判断每次递归是head是不是最后一个结点,若是,则返回尾结点(递归出口)*/
if (head == null || head.next == null)
return head;
// 递归,反复调自己,直到有返回值时,即到尾结点时返回尾结点(递归出口),preNode始终是尾结点(会变成头结点)
ListNode preNode = reverseList1(head.next);
// 让当前结点的下一个结点指向当前结点(实现反转)
head.next.next = head;
// 让当前结点的下一个结点为null(它下一个结点在上一步已经指向它了,即链表没有断开
head.next = null;
// 返回原始的尾结点,递归后变成头结点
return preNode;
结点类
public class ListNode<T>
T val;
ListNode next;
public ListNode()
public ListNode(T val)
this.val = val;
public ListNode(T val, ListNode next)
this.next = next;
测试代码
public class Test
@org.junit.Test
public void test1()
ListNode<Integer> head = new ListNode<>(1);
ListNode<Integer> list2 = new ListNode<>(2);
ListNode<Integer> list3 = new ListNode<>(3);
ListNode<Integer> list4 = new ListNode<>(4);
ListNode<Integer> list5 = new ListNode<>(5);
head.next = list2;
list2.next = list3;
list3.next = list4;
list4.next = list5;
System.out.println("测试反转之前--------------------");
ListNode h1 = head;
while (h1 != null)
System.out.print(h1.val + " ");
h1 = h1.next;
System.out.println();
System.out.println("测试反转之后--------------------");
ListNode h2 = new Solution().reverseList1(head);
while (h2!= null)
System.out.print(h2.val + " ");
h2 = h2.next;
测试结果
复杂度分析
假设链表为n
时间复杂度:递归调用相当与遍历链表,所以时间复杂度为O(n)
空间复杂度:递归调用过程中需要声明n个结点,故空间复杂度为O(n);
题解2(迭代法)
思路分析
迭代法就是在遍历链表时,让每一个结点指向它的前一个结点,由于单向链表只有后缀指针,没有前缀指针,故需要在遍历时存储前一个结点的信息,第一个结点没有前一个结点,则指向null成为尾结点,让每一个结点指向它的前一个结点之前,需要先存储这个结点的下一个结点信息,防止链表断开
步骤
- 声明3个结点curr,pre,next,curr存储当前遍历的结点,pre存储当前节点的前一个结点,next存储当前结点的下一个结点
- 遍历链表
- 遍历过程先存储当前结点的下一个结点
- 然后再让当前结点指向它的前一个结点
- 让当前结点作为它下一个结点的前一个结点,重置pre
- 让当前结点的下一个结点作为当前结点,重置curr;
代码实现
public class Solution
public ListNode reverseList2(ListNode head)
// 声明一个结点,存储当前结点
ListNode curr = head;
// 声明一个结点,存储当前结点的前一个结点
ListNode pre = null;
// 遍历链表,循环终止条件为当前结点为尾结点
while (curr != null)
// 存储当前结点的下一个结点
ListNode next = curr.next;
// 当前结点的下一个结点指向当前结点的前一个结点
curr.next = pre;
// 重置当前结点的前一个结点,让当前结点作为当前结点的下一个结点的前一个结点
pre = curr;
// 重置当前结点,让当前结点的下一个结点作为当前结点
curr = next;
return pre;
复杂度分析
假设当前链表长度为n
时间复杂度:需要遍历链表,故时间复杂度为O(n)
空间复杂度:声明了三个结点curr,pre,next,故空间复杂度为O(1)
以上是关于每天一道算法题(java数据结构与算法)——>反转链表的主要内容,如果未能解决你的问题,请参考以下文章
每天一道算法题(java数据结构与算法)——> 链表的中间结点
每天一道算法题(java数据结构与算法)——>链表中环的入口节点
每天一道算法题(java数据结构与算法)——> 链表中的两数相加