最近在刷leetcode上关于链表的一些高频题,在写代码的过程中总结了链表的一些解题技巧和常见题型。

结点的删除

指定链表中的某个结点,将其从链表中删除。

由于在链表中删除某个结点需要找到该结点的前一个位置,然后将前一个结点的next指针直接绕过该结点即可删除。但找到该结点的前一个位置需要指针遍历,其实还有一种更简单的trick,就是将要删除的结点的值设为该结点的后一个的值,然后删除该结点的后一个结点(间接删除,不需要找遍历前一个指针),代码如下:

class Solution {
public void deleteNode(ListNode node) {
node.val = node.next.val;
node.next = node.next.next;
}
}

在表头前增加虚拟结点

很多场合下,在链表的表头前增加一个虚拟结点(dummy),并让其指向head,能简化很多操作。如在新创建一个链表或对链表进行遍历操作时,如果不增加虚拟结点,就需要处理当前结点是头结点的特殊情况(因为头结点前没有其他结点,导致操作代码不一致)。加了虚拟结点后就可以像操作其他结点一样对待头结点了,最后只需要返回虚拟结点的next就可以了。

如LeetCode上的这一题:Remove Nth Node From End of List

class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummy = new ListNode(0);
dummy.next = head;
ListNode p = dummy;
ListNode q = dummy;
while(n > 0) {
q = q.next;
n--;
} while(q.next != null) {
p = p.next;
q = q.next;
}
p.next = p.next.next; return dummy.next;
}
}

链表转置

这应该是我碰到的链表中最频繁的问题了,很多其他链表的题目可能也需要借助于链表转置这一功能,所以需要能够熟练地写出代码,这里给出包括迭代和递归两种版本的代码:

class Solution {
//迭代实现链表转置
public ListNode reverseList(ListNode head) {
ListNode prev = null;
while(head != null) {
ListNode next = head.next;
head.next = prev;
prev = head;
head = next;
}
return prev;
} //递归实现链表转置
public ListNode reverseList2(ListNode head) {
if(head == null || head.next == null)
return head; ListNode next = head.next;
//对head.next执行转置
ListNode newHead = reverseList(next);
//此时next变成了转置后的尾结点
next.next = head;
head.next = null;
return newHead;
}
}

快慢双指针

有时候需要找到链表的中间位置的结点,这时就需要设置两个指针slow和fast,slow每次往前移动一个,fast移动两个。当fast为空时,slow就指向了链表的中间位置。比如leetcode上的Palindrome Linked List在判断链表是否回文时,需要找到中间位置,然后将其后半部分转置和前半部分相比较,具体实现代码如下:

class Solution {
public boolean isPalindrome(ListNode head) {
if(head == null || head.next == null) {
return true;
} //注意不能直接转置整个链表,需要找到链表的中间,只转置后半部分
ListNode fast = head, slow = head;
while(fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
}
//这时slow指向了链表的中间结点(注意这种trick要记住)
slow = reverseList(slow);
while(head != null && slow != null) {
if(head.val != slow.val) {
return false;
}
head = head.next;
slow = slow.next;
} return true;
} private ListNode reverseList(ListNode head) {
ListNode prev = null;
while(head != null) {
ListNode next = head.next;
head.next = prev;
prev = head;
head = next;
}
return prev;
}
}

合并两个有序链表

这也是碰到的很常见的问题了,合并两个有序链表使其仍然保持有序,一般采用双指针法,这也需要能够熟练地写出无bug的代码来。这里给出迭代和递归两种实现方式:

class Solution {
public ListNode mergeTwoLists(ListNode l1, ListNode l2) {
if(l1 == null && l2 == null) {
return null;
} ListNode p = l1;
ListNode q = l2;
ListNode dummy = new ListNode(0);
ListNode pp = dummy;
while(p != null && q != null) {
if(p.val < q.val) {
dummy.next = new ListNode(p.val);
p = p.next;
}
else {
dummy.next = new ListNode(q.val);
q = q.next;
}
dummy = dummy.next;
} if(p != null) {
//直接将dummy指过去
dummy.next = p;
} if(q != null) {
dummy.next = q;
} return pp.next;
} //使用递归更简单
public ListNode mergeTwoLists2(ListNode l1, ListNode l2) {
if(l1==null) return l2;
if(l2==null) return l1; if(l1.val<l2.val){
l1.next = mergeTwoLists(l1.next,l2);
return l1;
}
else{
l2.next = mergeTwoLists(l1,l2.next);
return l2;
}
}
}

目前碰到的问题就这么多了,后面再继续补充吧。

关于LeetCode上链表题目的一些trick的更多相关文章

  1. 关于Leetcode上二叉树的算法总结

    二叉树,结构很简单,只是比单链表复杂了那么一丢丢而已.我们先来看看它们结点上的差异: /* 单链表的结构 */ struct SingleList{ int element; struct Singl ...

  2. leetcode top 100 题目汇总

    首先表达我对leetcode网站的感谢,与高校的OJ系统相比,leetcode上面的题目更贴近工作的需要,而且支持的语言广泛.对于一些比较困难的题目,可以从讨论区中学习别人的思路,这一点很方便. 经过 ...

  3. leetcode上题目的分类

    leetcode链表部分题目 https://zhuanlan.zhihu.com/p/29800285 <[Leetcode][链表]相关题目汇总/分析/总结> leetcode堆部分题 ...

  4. LeetCode之链表总结

    链表提供了高效的节点重排能力,以及顺序性的节点访问方式,并且可以通过增删节点来灵活地调整链表的长度.作为一种常用的数据结构,链表内置在很多高级编程语言里面.既比数组复杂又比树简单,所以链表经常被面试官 ...

  5. leetcode - 位运算题目汇总(下)

    接上文leetcode - 位运算题目汇总(上),继续来切leetcode中Bit Manipulation下的题目. Bitwise AND of Numbers Range 给出一个范围,[m, ...

  6. 面试大总结:Java搞定面试中的链表题目总结

    package LinkedListSummary; import java.util.HashMap; import java.util.Stack; /** * http://blog.csdn. ...

  7. (转)面试大总结之一:Java搞定面试中的链表题目

    面试大总结之一:Java搞定面试中的链表题目 分类: Algorithm Interview2013-11-16 05:53 11628人阅读 评论(40) 收藏 举报 链表是面试中常出现的一类题目, ...

  8. [LeetCode] 动态规划入门题目

    最近接触了动态规划这个厉害的方法,还在慢慢地试着去了解这种思想,因此就在LeetCode上面找了几道比较简单的题目练了练手. 首先,动态规划是什么呢?很多人认为把它称作一种"算法" ...

  9. Leetcode解题-链表(2.2.0)基础类

    1 基类的作用 在开始练习LeetCode链表部分的习题之前,首先创建好一个Solution基类,其作用就是: Ø  规定好每个子Solution都要实现纯虚函数test做测试: Ø  提供了List ...

随机推荐

  1. javascript入门篇(三)

    字符串属性和方法 原始值字符串,如'liang', 没有属性和方法(因为他们不是对象). 原始值可以使用 JavaScript 的属性和方法,因为 JavaScript 在执行方法和属性时可以把原始值 ...

  2. C# ASP.NET MVC:使用Cookie记住账号密码

    MVC记住账号密码 使用cookie操作 前端: <div> 用户名:<input type="text" id="UserName" val ...

  3. 【JDBC 笔记】

    JDBC 笔记 作者:晨钟暮鼓c个人微信公众号:程序猿的月光宝盒 对应pdf版:https://download.csdn.net/download/qq_22430159/10754554 没有积分 ...

  4. Vue.js 学习笔记 第2章 数据绑定和第一个Vue应用

    本篇目录: 2.1 Vue实例与数据绑定 2.2 指令与事件 2.3 语法糖 学习任何一种框架,从一个Hello World应用开始是最快了解该框架特性的途径. 我们先从一段简单的HTML代码开始,感 ...

  5. PostgreSQL 10.7 linux 主从配置

    PostgreSQL 10.7 主从安装 硬件环境 云服务商:华为云 Linux: CentOS7.1 工具:Xshell Xftp IP:114.115.251.168 Port: 5432 543 ...

  6. Oracle设置主键自增

    oracle没有mysql那样可以有自增主键,需要自己通过创建序列才能实现自增 /*创建自增序列*/ CREATE SEQUENCE CMB_CHINA_CITYS_ID MINVALUE --最小值 ...

  7. Linux命令大全汇总,让你工作更有效率!

    基础命令 useradd:用户名 -m:创建新用户 passwd 用户名:为用户设置密码(当创建新用户后,就需要设置密码) whoami:查看当前用户名 exit :退出当前用户,返回之前切换过来的用 ...

  8. 9. VIM 系列 - YouCompleteMe 实现代码补全

    目录 环境准备 插件安装 环境准备 $ sudo apt install build-essential cmake python3-dev python-dev $ sudo apt install ...

  9. Kubernetes 笔记 10 Job 机器人加工厂

    本文首发于我的公众号 Linux云计算网络(id: cloud_dev),专注于干货分享,号内有 10T 书籍和视频资源,后台回复「1024」即可领取,欢迎大家关注,二维码文末可以扫. Hi,大家好, ...

  10. MyX5TbsPlusDemo【体验腾讯浏览服务Android SDK (TbsPlus 版)】

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 按照官网上的说明:只需接入aar文件和调用一个接口即可完成TBS接入,我们会通过全屏Activity展示TBS WebView,适用 ...