网站首页 > 精选教程 正文
反转单向链表
已知单链表的头部节点 head ,将链表反转,如图:
图1:单链表反转效果展示
我平时基本没看过算法,只有对链表的概念有基础的认知。看到这个题目,感觉并不难。但是要写起来,还是有点伤害脑细胞。
第一次做这个题目,代码写得并不简洁,并且花了20多分钟,而且在idea中反复调试才得到了正确结论。
链表类的算法题,指针的不断切换,总让人头晕...不知道你是否也有这样的感触。
尝试解题
以下是我第一次尝试解题的代码:
public static ListNode reverseList(ListNode head) {
// 0.判断链表为空或者只有一个元素,无需反转
if(head == null || head.next == null){
return head;
}
ListNode pre = head;
ListNode next = pre.next;
ListNode temp;
while(next != null){
// 1.因为节点的next将指向前一个节点,临时缓存下一个节点
temp = next.next;
// 2.指向前一个节点
next.next = pre;
// 3.走到最后,跳出循环
if(temp == null){
break;
}
// 4.将next作为pre
pre = next;
// 5.将temp作为next
next = temp;
}
// 6.由于head并没有处理指向,这里指向null
head.next = null;
// 7.返回next作为返回值
return next;
}
好吧,我承认我的第一次解题思路是根据自己的感觉乱试出来的,有种情窦初开,小鹿乱撞的感觉,一共用了8步。但是不管怎么样结果如期而至。
反思&纠错
首先表扬下自己,做了异常情况的判断,即判断了null节点和只有一个节点的情况
// 0.判断链表为空或者只有一个元素,无需反转
if(head == null || head.next == null){
return head;
}
代码撸完后,感觉并不对,似乎走了不少弯路。一个这么简单的逻辑怎么可能写这么多行代码!这么多步骤!
然后...陷入沉思了...
第一个问题,我很快发现了,既然while循环有判断退出循环,我为什么还要判断在循环中break呢?
当时我是这样思考的:
不断挪动指针,next是最后一个节点并返回,如果不加if判断直接break,由于next = temp,因此next永远都是Null。那为什么不把pre作为返回值呢?迅速我改了一版,代码简洁多了。
public static ListNode reverseList(ListNode head) {
// 0.判断链表为空或者只有一个元素,无需反转
if(head == null || head.next == null){
return head;
}
ListNode pre = head;
ListNode next = pre.next;
ListNode temp;
while(next != null){
// 1.因为节点的next将指向前一个节点,临时缓存下一个节点
temp = next.next;
// 2.指向前一个节点
next.next = pre;
// 3.将next作为pre
pre = next;
// 4.将temp作为next
next = temp;
}
// 5.由于head并没有处理指向,这里指向null
head.next = null;
// 6.返回next作为返回值
return pre;
}
比起初版少了一步,但是总觉得第5步是多余的呢?似乎是可以在循环中统一处理,为什么最后要增加一个特殊处理呢?head.next = null。
存在即合理,但合理未必简洁。
为了让自己更清楚地看到指针变动的过程及变量的变化,我决定用图来模拟这段代码指针的动态变化过程:
脑空间不足,请理解我用图脑补...
4个节点的单链表:
图2:第一次循环,执行完第2步
图3:第二次循环,执行完第2步
图4:第三次循环,执行完第2步
图5:循环结束,执行完第5步
细心的你,肯定已经发现了非常蹩脚的地方,1->2这个指针一直以说不出来的不合理存在,并且需要在循环结束后手动清除。
又一次反思中...
我第一次pre从node1开始,假设我们将pre从null开始呢?
突然脑海里面想起来极客时间的《数据结构与算法之美》链表中的一节讲到了一个概念:哨兵。
翻书中...
哨兵解决的是国家间的边界问题。似乎有那么点意思。尝试着将pre从null开始,next从node1开始呢?
噼里啪啦,重新修改代码中:
public static ListNode reverseList(ListNode head) {
// 0.判断链表为空或者只有一个元素,无需反转
if(head == null || head.next == null){
return head;
}
ListNode pre = null;
ListNode next = head;
ListNode temp;
while(next != null){
// 1.因为节点的next将指向前一个节点,临时缓存下一个节点
temp = next.next;
// 2.指向前一个节点
next.next = pre;
// 3.将next作为pre
pre = next;
// 4.将temp作为next
next = temp;
}
// 5.返回next作为返回值
return pre;
}
代码从原来的8步减少到了6步,减少了这两步代码,确实是自己走过的弯路。
似乎代码没有可以再继续优化的空间了,这几行代码也是自己对动态规划类问题的初识。
写在最后
链表的概念很容易理解,但是算法实现时,却往往由于指针的挪动,带来一定的复杂度。极容易烧脑,也可能脑裂,哈哈哈哈~,但也有诀窍。
你也可以按照我的思路来尝试解决问题。
- 第一步先按照自己的思路去实现
- 第二步找到自己认为不合理的点
- 优化问题,总结
比如我的这个总结:
- 返回链表的节点可以以最后节点的前序节点,避开循环中再次判断break
- 可以在处理的前序节点(也可以末尾节点)作为一个特殊的哨兵,减少冗余代码
- 对于处理的临界点一定要仔细考虑,避免异常情况出现。
算法还是很有意思,我们在算法的海洋里再会!
我是公众号「面试怪圈」的kazz,喜欢我的原创,可以关注我,点个「在看」和「收藏」是对我最大的支持和鼓励!!!
欢迎到面试怪圈官网下载各种免费的学习资料:我的文章分享_java面试题_学习资料_基础知识点_项目实战以及算法等资料-面试怪圈
- 上一篇: 链表面试常考题目—链表逆序 链表逆序操作
- 下一篇: 剑指BAT:如何最优雅着反转单链表?
猜你喜欢
- 2024-11-02 LeetCode-025-K 个一组翻转链表 每k个一组翻转链表
- 2024-11-02 C++算法(五)反转链表 反转链表c#
- 2024-11-02 61. 旋转链表 反转链表 头插法
- 2024-11-02 字节面试算法集第三题链表反转 #算法
- 2024-11-02 Java数据结构和算法—链表 java中的链表数据结构
- 2024-11-02 面试现场:如何实现链表的逆序? 链表逆置是什么意思
- 2024-11-02 LeetCode-206-反转链表 反转链表 迭代
- 2024-11-02 迭代法 链表翻转 #软件开发 迭代法程序
- 2024-11-02 每日算法---单链表反转和是否有环
- 2024-11-02 C++:挑战鹅厂面试题(一)--反转链表
你 发表评论:
欢迎- 04-11Java面试“字符串三兄弟”String、StringBuilder、StringBuffer
- 04-11Java中你知道几种从字符串中找指定的字符的数量
- 04-11探秘Java面试中问的最多的String、StringBuffer、StringBuilder
- 04-11Python字符串详解与示例(python字符串的常见操作)
- 04-11java正则-取出指定字符串之间的内容
- 04-11String s1 = new String("abc");这句话创建了几个字符串对象?
- 04-11java判断字符串中是否包含某个字符
- 04-11关于java开发中正确的发牌逻辑编写规范
- 最近发表
-
- Java面试“字符串三兄弟”String、StringBuilder、StringBuffer
- Java中你知道几种从字符串中找指定的字符的数量
- 探秘Java面试中问的最多的String、StringBuffer、StringBuilder
- Python字符串详解与示例(python字符串的常见操作)
- java正则-取出指定字符串之间的内容
- String s1 = new String("abc");这句话创建了几个字符串对象?
- java判断字符串中是否包含某个字符
- 关于java开发中正确的发牌逻辑编写规范
- windows、linux如何后台运行jar(并且显示进程名)
- 腾讯大佬私人收藏,GitHub上最受欢迎的100个JAVA库,值得学习
- 标签列表
-
- nginx反向代理 (57)
- nginx日志 (56)
- nginx限制ip访问 (62)
- mac安装nginx (55)
- java和mysql (59)
- java中final (62)
- win10安装java (72)
- java启动参数 (64)
- java链表反转 (64)
- 字符串反转java (72)
- java逻辑运算符 (59)
- java 请求url (65)
- java信号量 (57)
- java定义枚举 (59)
- java字符串压缩 (56)
- java中的反射 (59)
- java 三维数组 (55)
- java插入排序 (68)
- java线程的状态 (62)
- java异步调用 (55)
- java中的异常处理 (62)
- java锁机制 (54)
- java静态内部类 (55)
- java怎么添加图片 (60)
- java 权限框架 (55)
本文暂时没有评论,来添加一个吧(●'◡'●)