代码随想录刷题|LeetCode 203.移除链表元素 707.设计链表 206.反转链表

简介: 代码随想录刷题|LeetCode 203.移除链表元素 707.设计链表 206.反转链表

203.移除链表元素

题目链接:力扣

思路

一般方法:对于一般删除链表元素的方法而言,我们需要分情况进行处理,分为被删除节点是头节点和被删除节点不是头节点的情况。如果是头节点,就将下一个节点赋值给头节点;如果非头节点,就进行常规删除


       虚拟头节点:其实就是给头节点前面放一个虚拟节点,这样如果删除头节点的话就可以使用常规删除方法进行删除了

移除链表元素

一般方法

   第一步:判断删除头节点的情况(注意:这里使用的是while关键字,这里是一个连续排查的过程,有前几个节点都是目标值的情况)


       第二步:创建指针指向头节点(这里是头节点已经被删除干净的头节点)


       第三步:进行判断,被删除的这个节点会被绕过,所以要使用cur.next 进行判断:如果不是,指针前移;如果是,越过删除

/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode removeElements(ListNode head, int val) {
        while ( head!= null && head.val == val) {
            head = head.next;
        }
        //创建一个指针
        ListNode cur = head;
        while ( cur != null && cur.next != null ) {
            if ( cur.next.val == val) {
                cur.next = cur.next.next;
            } else {
                cur = cur.next;
            }
        }
        return head;
    }
}

虚拟头节点

       第一步:创建虚拟节点

       第二步:按照上个方法的第二步和第三步进行操作

       第三步:注意返回的是虚拟节点的next 才是真正的head

class Solution {
    public ListNode removeElements(ListNode head, int val) {
        //创建虚拟头节点
        ListNode dummy = new ListNode(0);
        dummy.next = head;
        ListNode cur = dummy;
        while ( cur != null && cur.next != null ) {
            if ( cur.next.val == val) {
                cur.next = cur.next.next;
            } else {
                cur = cur.next;
            }
        }
        return dummy.next;
    }
}


707.设计链表

题目链接:力扣

思路


 这个题目包含了对链表元素的增删改查,其中get(int index)是查;addAtHead(int val)、addAtTail(int val)、addAtIndex(int index, int val)是增;deleteAtIndex(int index)是删


设计链表

单向链表实现:


第一步:定义单向链表的节点


       第二步:定义链表的属性和链表


       第三步:get(int index)


               1、对非法下标进行判断


               2、创建指针来取值


               3、循环下标的次数,将指针指向要取的节点


               4、返回获取的值


       第四步:addAtIndex(int index, int val) 这个方法实现了,其他两个添加节点的方法也就实现了


               1、对非法下标进行判断


               2、创建要添加的节点、,创建临时指针


               3、循环到指定的下标下,添加节点


               4、链表长度增加


       第五步:deleteAtIndex(int index)


               这里和203 移除链表元素差不多


// 定义单向链表节点
class ListNode {
    int val;
    ListNode next;
    ListNode () {}
    ListNode (int val) {
        this.val = val;
    }
}
class MyLinkedList {
    // size存储链表元素的个数
    int size;
    // 头节点
    ListNode head;
    // 链表的构造方式
    public MyLinkedList() {
        size = 0;
        head = new ListNode(0);     
    }
    // 获取第index个节点的数值,下标从0开始
    public int get(int index) {
        // index小于0或者大于size-1的情况都是无效的索引
        if ( index < 0 || index > size - 1 ) {
            return -1;
        }
        // 创建一个指针来取值
        ListNode cur = head; 
        // 这里cur指向的是虚拟头节点
        // 这样当索引为0的时候,下面的循环执行一次,cur刚好指向真正的头节点
        for (int i = 0; i <= index ;i++)  {
            cur = cur.next;
        }
        return cur.val;
    }
    public void addAtHead(int val) {
        addAtIndex(0,val);
    }
    public void addAtTail(int val) {
        addAtIndex(size,val);
    }
    public void addAtIndex(int index, int val) {
        // 对非法下标进行判断
        if (index > size) {
            return;
        }
        if (index < 0) {
            index = 0;
        }
        // 创建要添加的指针
        ListNode add = new ListNode(val);
        // 对链表元素进行添加
        ListNode tmp = head;
        // 将tmp指针移动到
        for (int i = 0 ; i < index ; i++) {
            tmp = tmp.next;
        }
        add.next = tmp.next;
        tmp.next = add;
        // 对链表长度进行增加
        size++;
    }
    public void deleteAtIndex(int index) {
        // 对非法下标进行判断
        if (index < 0 || index > size - 1) {
            return;
        }
        // 如果是头节点,将头节点向前移一位
        if (index == 0) {
            head = head.next;
            return;
        }
        ListNode cur = head;
        for (int i = 0 ; i < index ; i++) {
            cur = cur.next;
        }
        cur.next = cur.next.next;
        // 链表长度减一
        size--;
    }
}

双向链表实现:

class ListNode{
    int val;
    ListNode next,prev;
    ListNode () {}
    ListNode (int val) {
        this.val = val;
    }
}
class MyLinkedList {
    // 记录链表中的元素
    int size;
    // 记录链表的虚拟头节点和尾节点
    ListNode head;
    ListNode tail;
    public MyLinkedList() {
        // 初始化操作
        this.size = 0;
        this.head = new ListNode(0);
        this.tail = new ListNode(0);
        head.next = tail;
        tail.prev = head;    
    }
    public int get(int index) {
        // 判断下标的合法性
        if (index < 0 || index >= size) {
            return -1;
        }
        // 创建一个指针
        ListNode cur = this.head;
        // 判断哪一边的遍历时间更短
        if (index >= size / 2) {
            // 从tail开始遍历
            cur = tail;
            for (int i = 0 ; i < size - index ; i++) {
                cur = cur.prev;
            }
        } else {
            for (int i = 0 ; i <= index ; i++) {
                cur = cur.next;
            }
        }
        return cur.val;
    }
    public void addAtHead(int val) {
        //等价于在第0个元素前添加
        addAtIndex(0,val);
    }
    public void addAtTail(int val) {
        //等价于在最后一个元素(null)前添加
        addAtIndex(size,val);
    }
    public void addAtIndex(int index, int val) {
        // 判断下标的合法性
        if(index>size){
            return;
        }
        //index小于0
        if(index<0){
            index = 0;
        }
        ListNode pre = this.head;
        for(int i=0; i<index; i++){
            pre = pre.next;
        }
        //新建结点
        ListNode newNode = new ListNode(val);
        newNode.next = pre.next;
        pre.next.prev = newNode;
        newNode.prev = pre;
        pre.next = newNode;
        // 长度增加
        size++;
    }
    public void deleteAtIndex(int index) {
        // 判断下标的合法性
        if (index < 0 || index >= size) {
            return;
        } 
        // 删除操作
        ListNode pre = this.head;
        for (int i = 0 ; i < index ; i++) {
            pre = pre.next;
        }
        pre.next.next.prev = pre;
        pre.next = pre.next.next;
        // 长度减少
        size--;
    }
}

206.反转链表

题目链接:力扣

思路

双指针:其实可以称为三指针了,cur负责指向pre,tmp负责临时保存

       递归法:是双指针的优化

反转链表

双指针法


 第一步:定义双指针


               cur指向头节点,pre指向null(反转时后节点指向前节点的方向)


       第二步:在cur指向null的时候循环结束


               要创建一个临时节点保存cur反转后后面的节点,防止丢失


               注意:在反转成功后,在移动指针的时候,要先移动pre,再移动cur


/**
 * Definition for singly-linked list.
 * public class ListNode {
 *     int val;
 *     ListNode next;
 *     ListNode() {}
 *     ListNode(int val) { this.val = val; }
 *     ListNode(int val, ListNode next) { this.val = val; this.next = next; }
 * }
 */
class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode pre = null;
        ListNode cur = head;
        while (cur != null) {
            // 利用临时指针保存cur后的下一个节点,防止找不到
            ListNode tmp = cur.next;
            cur.next = pre;
            // 注意这里先移动pre  再移动cur
            pre = cur;
            cur = tmp;    
        }
        return pre;
    }
}


递归法


class Solution {
    public ListNode reverseList(ListNode head) {
        return reverse(null , head);
    }
    private ListNode reverse(ListNode prev , ListNode cur) {
        if (cur == null) {
            return prev;
        }
        ListNode tmp = null;
        tmp = cur.next; // 保存下一个节点
        cur.next = prev; // 反转指针
        return reverse(cur,tmp);
    }
}
相关文章
|
2月前
【力扣】-- 移除链表元素
【力扣】-- 移除链表元素
39 1
|
3月前
|
Unix Shell Linux
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
本文提供了几个Linux shell脚本编程问题的解决方案,包括转置文件内容、统计词频、验证有效电话号码和提取文件的第十行,每个问题都给出了至少一种实现方法。
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
|
2月前
【LeetCode 27】347.前k个高频元素
【LeetCode 27】347.前k个高频元素
40 0
|
1月前
|
机器学习/深度学习 人工智能 自然语言处理
280页PDF,全方位评估OpenAI o1,Leetcode刷题准确率竟这么高
【10月更文挑战第24天】近年来,OpenAI的o1模型在大型语言模型(LLMs)中脱颖而出,展现出卓越的推理能力和知识整合能力。基于Transformer架构,o1模型采用了链式思维和强化学习等先进技术,显著提升了其在编程竞赛、医学影像报告生成、数学问题解决、自然语言推理和芯片设计等领域的表现。本文将全面评估o1模型的性能及其对AI研究和应用的潜在影响。
46 1
|
3月前
|
数据采集 负载均衡 安全
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
本文提供了多个多线程编程问题的解决方案,包括设计有限阻塞队列、多线程网页爬虫、红绿灯路口等,每个问题都给出了至少一种实现方法,涵盖了互斥锁、条件变量、信号量等线程同步机制的使用。
LeetCode刷题 多线程编程九则 | 1188. 设计有限阻塞队列 1242. 多线程网页爬虫 1279. 红绿灯路口
|
2月前
【LeetCode 06】203.移除链表元素
【LeetCode 06】203.移除链表元素
33 0
|
2月前
【LeetCode-每日一题】移除元素
【LeetCode-每日一题】移除元素
34 0
|
4月前
|
存储 算法
LeetCode第83题删除排序链表中的重复元素
文章介绍了LeetCode第83题"删除排序链表中的重复元素"的解法,使用双指针技术在原链表上原地删除重复元素,提供了一种时间和空间效率都较高的解决方案。
LeetCode第83题删除排序链表中的重复元素
|
3月前
|
存储 算法 C语言
C语言手撕实战代码_循环单链表和循环双链表
本文档详细介绍了用C语言实现循环单链表和循环双链表的相关算法。包括循环单链表的建立、逆转、左移、拆分及合并等操作;以及双链表的建立、遍历、排序和循环双链表的重组。通过具体示例和代码片段,展示了每种算法的实现思路与步骤,帮助读者深入理解并掌握这些数据结构的基本操作方法。
|
6月前
|
存储 SQL 算法
LeetCode力扣第114题:多种算法实现 将二叉树展开为链表
LeetCode力扣第114题:多种算法实现 将二叉树展开为链表