剑指offer代码解析——面试题16反转单链表

本题的详细解析均在代码中注释:

/**
 * 题目:将单链表反转,并输出反转后链表的头结点
 * @author 大闲人柴毛毛
 */
public class RevertLink {
	/**
	 * 分析:本题是要将链表“反转”,而不是反向输出,这点要特别注意。
	 * 反转需要改变链表的结构,使所有指针都指向相反方向;
	 * 而反向输出不需要改变链表结构,只需反向输出即可。
	 * 对于反向问题可使用栈来实现,可参见我的博客《剑指offer——面试题5》,这里不再赘述。
	 * 下面来解决反转问题。 
	 */
	
	/**
	 * 反转单链表其实就是将链表中的指针指向相反方向,
	 * 若a1和a2是单链表中两个相邻的结点,未反转前的状态是:a1.next = a2,
	 * 现在进行反转:a2.next = a1.
	 * 此时,虽然a2指向了a1,但链表出现了“断裂”,a2和它的后继结点发生了断裂,无法继续进行反转操作。
	 * 因此,我们需要再增加一个指针a3,指向a2的后继结点。
	 * 当反转完a2结点后,从a3开始继续依次向后进行反转操作,直到整个链表反转完为止。
	 * 代码如下:
	 */
	
	/**
	 * 反转链表
	 * @param first 链表的头结点
	 * @return 返回反转后链表的头结点
	 */
	public static <T> Node<T> revertLink(Node<T> first){
		//当链表为空时
		if(first==null){
			System.out.println("链表为空!");
			return null;
		}
		
		//当链表只有一个结点时
		if(first.next==null){
			return first;
		}
		
		//当链表只有两个结点时
		if(first.next.next==null){
			//end为链表的尾结点,是反转后的头结点
			Node<T> end = first.next;
			//将第二个结点的next域指向第一个结点
			first.next.next = first;
			//将第一个结点的next域设置null
			first.next = null;
			return end;
		}
		
		//当链表有三个及以上结点时
		{
			Node<T> a1 = first;//a1指向头结点
			Node<T> a2 = first.next;//a2指向第二个结点
			Node<T> a3 = first.next.next;//a3指向第三个结点
		
			//将头结点的后继设为null
			first.next = null;
			
			//不停的将a1->a2反转为a2->a1,直到a3为空
			while(a3!=null){
				//a2的后继设为a1
				a2.next = a1;
				//a1、a2、a3依次向后移一位
				a1 = a2;
				a2 = a3;
				a3 = a3.next;
			}
			
			//将最后一个结点反向
			a2.next = a1;
			return a2;
		}
		//PS:这里使用局部代码块一方面增强了代码的可读性,另一方面使得局部代码块中的变量能够在代码块结束之后立即释放,从而节约了内存空间。
	}
}


/**
 * 定义结点 
 */
class Node<T>{
	public T data;
	public Node<T> next;
}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏程序员互动联盟

【答疑解惑】Java中的方法重载

语音版: 我定义了一个类如下: public class FirstJava { private int value; private int...

36010
来自专栏代码散人

ios开发 Runtime 详解part2(动态方法解析)

在 ios开发 Runtime 详解part1中我已经解释了Introspection,接下来介绍Runtime的其它特性。

601
来自专栏韦弦的微信小程序

Swift4 获取String子字符串

都说Swift2和Swift3不是同一门语言,但是我怎么觉得Swift4有时看着也像别人家的孩子。。。。 这里主要是更新下以前的写的Swift3的String...

782
来自专栏青玉伏案

iOS开发之SQLite--C语言接口规范(三)——Binding Values To Prepared Statements

  在前面的博客中已经介绍了如何连接SQLite数据库,并且简单的查询和遍历结果集。在前面用到了sqlite3_stmt *stmt,也就是预编译后的SQL语句...

1766
来自专栏技术之路

golang 常见疑惑总结

1143
来自专栏java相关

【PL/SQL编程基础】

1144
来自专栏javathings

synchronized 关键字的用法?

如果面试问到这个题目,那么就可以窃喜了,因为太简单了,只要写过多线程代码的人,肯定用到过 synchronized 关键字。我把答案总结在这里,背诵一下就可以了...

722
来自专栏Linux驱动

29.C++- 异常处理

C++内置了异常处理的语法元素 try catch try语句处理正常代码逻辑 当try语句发现异常时,则通过throw语句抛出异常,并退出try语句 catc...

2806
来自专栏大闲人柴毛毛

深入Java虚拟机——JVM内存详解

在C++中,程序员拥有每一个对象的所有权,但与此同时还肩负着释放对象内存空间的责任;而Java由于有了虚拟机的帮助,程序员拥有对象的所有权的同时不再需要释放对象...

33913
来自专栏java一日一条

Java内存管理原理及内存区域详解

Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干不同的数据区域,这些区域都有各自的用途以及创建和销毁的时间。Java虚拟机所管理的内存将会包...

281

扫码关注云+社区