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. 两个链表总会有其中一个会先走完,其实走完之后,未走完的链表后面的结点其实都已经比走完链表的尾结点大了,所以直接串上去即可.
相关推荐
激流丶7 分钟前
【Kafka 实战】如何解决Kafka Topic数量过多带来的性能问题?
java·大数据·kafka·topic
Themberfue10 分钟前
Java多线程详解⑤(全程干货!!!)线程安全问题 || 锁 || synchronized
java·开发语言·线程·多线程·synchronized·
让学习成为一种生活方式27 分钟前
R包下载太慢安装中止的解决策略-R语言003
java·数据库·r语言
晨曦_子画33 分钟前
编程语言之战:AI 之后的 Kotlin 与 Java
android·java·开发语言·人工智能·kotlin
南宫生1 小时前
贪心算法习题其三【力扣】【算法学习day.20】
java·数据结构·学习·算法·leetcode·贪心算法
Heavydrink1 小时前
HTTP动词与状态码
java
ktkiko111 小时前
Java中的远程方法调用——RPC详解
java·开发语言·rpc
计算机-秋大田2 小时前
基于Spring Boot的船舶监造系统的设计与实现,LW+源码+讲解
java·论文阅读·spring boot·后端·vue
神里大人2 小时前
idea、pycharm等软件的文件名红色怎么变绿色
java·pycharm·intellij-idea
weixin_432702262 小时前
代码随想录算法训练营第五十五天|图论理论基础
数据结构·python·算法·深度优先·图论