Collection与数据结构 链表与LinkedList(二):链表精选OJ例题(上)

1. 删除链表中所有值为val结点

OJ链接

java 复制代码
class Solution {
    public ListNode removeElements(ListNode head, int val) {
        if(head == null){
            return head;
        }
        ListNode pre = head;
        ListNode cur = head.next;
        while(cur != null){
            if(cur.val == val){
                pre.next = cur.next;
            }else{
                pre = pre.next;
            }
            cur = cur.next;
        }
        if(head.val == val){
            head = head.next;
        }
        return head;
    }
}

这个题我们在上一篇博客中实现单向链表中展示过,这里不再赘述.

2. 反转一个单链表

OJ链接

java 复制代码
class Solution {
    public ListNode reverseList(ListNode head) {
        if(head == null){
            return head;//列表为空的情况
        }
        if(head.next == null){
            return head;//结点只有一个的情况
        }
        ListNode cur = head.next;
        ListNode curNext = cur.next;//防止下一个结点找不到,所以定义curNext
        head.next = null;//先把头结点的next置为null,否者返回时会越界
        while(cur != null){
            cur.next = head;
            head = cur;
            cur = curNext;//头插法
            if(curNext !=null){
                curNext = curNext.next;//防止走到最后的时候空指针异常
            }
        }
        return head;
    }
}

动态演示:

翻转链表

注意

  1. 需要定义一个nodeNext结点来找到下一个结点,**,cur想要往后走的时候,不是我们想要找的下一个结点,因为在链表头插法修改了next的地址,**为了防止下一个结点的地址丢失,所以我们引入curNext.
  2. 在一开始的时候,需要把head.next置为空,因为翻转链表之后,一开始的头结点将是新链表的最后一个结点,最后一个结点的next为null.
  3. 在循环体内部要判断curNext是否为null ,若没有,cur走到了最后一个结点的时候,curNext就位null了,在执行curNext = curNext.next的时候,就会出现空指针异常.

3. 链表的中间结点

OJ链接

java 复制代码
class Solution {
    public ListNode middleNode(ListNode head) {
        if(head == null){
            return head;
        }
        ListNode slow = head;
        ListNode fast = head;//定义双指针
        while (fast != null && fast.next != null){//顺序不可以反,否则会nullException,
        //第一个是奇数的终止情况,第二个是偶数
            fast = fast.next.next;//快指针走两步
            slow = slow.next;//慢指针走一步
        }
        return slow;//最后慢指针指向的就是中间结点
    }
}

动态演示

寻找中间结点

注意

  1. 这个题大多数人想到的是通过计数的方法来遍历链表,之后再/2,这种方法固然没错,但是它的时间复杂度较高,它遍历了两次链表 ,为了只遍历一次就通过,我们引入了快慢指针,快指针的速度是慢指针的2倍.
  2. 这个题分为奇数项和偶数项,针对不同的项数,限制条件也不同.while (fast != null && fast.next != null)
  3. 限制条件while (fast != null && fast.next != null)的两个条件不可以反,如果fast==null,后面的条件就会报空指针异常.

4. 返回倒数第k个节点

OJ链接

这道题我们加大一点难度,把说明的条件删去.

java 复制代码
class Solution {
    public int kthToLast(ListNode head, int k) {
        if (head == null) {
            return head.val;
        }
        if(k < 0){//负数返回null
            return -1;
        }
        ListNode slow = head;
        ListNode fast = head;
        int count = 0;
        while (count != k - 1) {//先让fast走k-1步
            if (fast != null) {//防止k太大导致fast越界
                fast = fast.next;
                count++;
            } else {
                return -1;//如果越界返回null
            }
        }
        while (fast.next != null) {//fast指向最后一个结点就结束,所以加上next
            fast = fast.next;
            slow = slow.next;
        }
        return slow.val;
    }
}

动态演示

寻找倒数第k个结点

注意

  1. 这道题和上一道题一样,许多人首先想到的就是遍历链表,得到size,减去k之后再遍历,这种做法可通过,但是效率欠缺.所以我们又引入了快慢指针.
  2. 既然删掉了限制条件,那么k就有可能是无效的,其中一种就是k小于0,通过if(k < 0)来限制 .越界的情况,很多人又想,这不是还得遍历数组去得到size吗?其实不用,只要限制快指针在第一次走的时候不越界即可.if (fast != null)
  3. fast指向最后一个节结点的时候,slow就是中间结点,所以避免在while (fast.next != null)把next去掉,否者slow就会多走一格.

5. 合并两个有序链表

OJ链接

java 复制代码
class Solution {
    public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
        ListNode node = new ListNode(-1);//引入傀儡结点
        ListNode heada = list1;
        ListNode headb = list2;
        ListNode tmp = node;//定义临时结点用于遍历
        while(heada != null && headb != null){//遍历两个链表
            if(heada.val < headb.val){//比较对应位置的大小
                tmp.next = heada;
                heada = heada.next;
                tmp = tmp.next;//连接对应结点
            }else{
                tmp.next = headb;
                headb = headb.next;
                tmp = tmp.next;
            }
        }
        if(headb == null){//判断最后谁先走完,没走完的链表后面的值一定比已经走完那个链表后面的值大,所以直接连上即可
            tmp.next = heada;
        }else{
            tmp.next = headb;
        }
        return node.next;
    }
}

动态演示

合并两个有序链表

注意

  1. 这里我们为了把为了把两个链表串起来,我们需要引入傀儡结点,就像想要用一根线串起零散的珠子需要给这根线的头按上一根针一样.
  2. 两个链表总会有其中一个会先走完,其实走完之后,未走完的链表后面的结点其实都已经比走完链表的尾结点大了,所以直接串上去即可.
相关推荐
heartbeat..3 分钟前
Java Map 详解:原理、实现与使用场景
java·map·集合
果然途游9 分钟前
完整Java后端学习路径
java·开发语言·学习笔记
又是重名了10 分钟前
导出新方案-poi和easyexcel融合
java·poi·easyexcel
uup12 分钟前
看似简单的空指针 —— 包装类自动拆箱陷阱
java
天天摸鱼的java工程师16 分钟前
Docker+K8s 部署微服务:从搭建到运维的全流程指南(Java 老鸟实战版)
java·后端
用户83071968408217 分钟前
Apache Tomcat 体系结构深度解析
java·tomcat
管理大亨22 分钟前
企业级ELK:从日志收集到业务驱动
java·大数据·网络·数据库·elk·elasticsearch
BBB努力学习程序设计24 分钟前
Java并发包深度解析:从AQS到线程池的完全指南
java
xing-xing25 分钟前
Java集合Map总结
java
古城小栈26 分钟前
性能边界:何时用 Go 何时用 Java 的技术选型指南
java·后端·golang