代码随想录算法训练营第三天| 203.移除链表元素、206.翻转链表、707.设计链表

day-3

203.移除链表元素

给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。
 

示例 1:


输入:head = [1,2,6,3,4,5,6], val = 6
输出:[1,2,3,4,5]
示例 2:

输入:head = [], val = 1
输出:[]
示例 3:

输入:head = [7,7,7,7], val = 7
输出:[]
 

提示:

列表中的节点数目在范围 [0, 104]1 <= Node.val <= 50
0 <= val <= 50

https://leetcode.cn/problems/remove-linked-list-elements/description/
快慢指针,快用于查询,慢用于记录满足条件的点
public class sxl_203 {
    class ListNode{
        int val;
        ListNode next;

        ListNode(){

        }

        ListNode(int val){
            this.val = val;
        }

        ListNode(int val,ListNode next){
            this.val = val;
            this.next = next;
        }
    }

    /**
     * 快慢指针+头节点的方式
     * 快指针用于遍历
     * 慢指针记录满足条件的结果,慢指针所在的位置就是满足的所有条件
     *
     * 我们用虚拟节点,主要是判断一下头结点为null的情况
     * 慢指针和虚拟节点一样
     * 快指针跟head一样
     */
    public ListNode removeElements(ListNode head, int val) {
        // 首先我们判断头结点是否为空,为空,我们就不用考虑了,直接返回null
        if(head == null){
            return null;
        }

        ListNode dummy = new ListNode(-1,head);// 添加虚拟节点,且在头节点的前面

        ListNode slow = dummy; // 定义慢指针,就为虚拟节点
        ListNode fast = head; // 定义快指针,就为头节点,用于判断
        while (fast!=null){
            if(fast.val == val){
                // 如果快指针当前位置的值为val,那么就说明这个节点不能要了,直接跳过去
                slow.next = fast.next;
                // 注意,我们只是跳过去了,让slow的next指向fast的next,但是slow没过去
                // 我们还要再判断fast的next是否满足呢,在跳出判断之后,fast还会走下去
            }else {
                // 如果值不相等,那么slow移动过去,这个值我们要了
                slow = fast;
            }
            // 不管怎么说,fast是一定要走下去的
            fast = fast.next;
        }
        return dummy.next;
    }
}

206.翻转链表

/**
 * 给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
 *
 *
 * 示例 1:
 *
 *
 * 输入:head = [1,2,3,4,5]
 * 输出:[5,4,3,2,1]
 * 示例 2:
 *
 *
 * 输入:head = [1,2]
 * 输出:[2,1]
 * 示例 3:
 *
 * 输入:head = []
 * 输出:[]
 *
 *
 * 提示:
 *
 * 链表中节点的数目范围是 [0, 5000]
 * -5000 <= Node.val <= 5000
 *
 * https://leetcode.cn/problems/reverse-linked-list/
 */
双指针是一个思路
递归是一个思路,递归可以假象成后序遍历
package com.example.leetcode.labuladong.sxl.sxl_3;

/**
 * 给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
 *
 *
 * 示例 1:
 *
 *
 * 输入:head = [1,2,3,4,5]
 * 输出:[5,4,3,2,1]
 * 示例 2:
 *
 *
 * 输入:head = [1,2]
 * 输出:[2,1]
 * 示例 3:
 *
 * 输入:head = []
 * 输出:[]
 *
 *
 * 提示:
 *
 * 链表中节点的数目范围是 [0, 5000]
 * -5000 <= Node.val <= 5000
 *
 * https://leetcode.cn/problems/reverse-linked-list/
 */
public class sxl_206 {
    class ListNode{
        int val;
        ListNode next;

        ListNode(){

        }

        ListNode(int val){
            this.val = val;
        }

        ListNode(int val, ListNode next){
            this.val = val;
            this.next = next;
        }
    }
    // 反转链表,这个也可以带入之前做过的labuladong的思想,二叉树后续遍历
    public ListNode reverseList(ListNode head) {
        // 如果是空的,或者就只有一个,那么我们直接返回即可
        if (head == null || head.next == null) {
            return head;
        }
        // 后续我们就是一个一个来反转,这就是递归,
        ListNode last = reverseList(head.next);
        head.next.next = head;
        head.next = null;
        return last;
    }

    /**
     * 双指针写法 清晰明了
     *
     * 定义3个节点
     * prev类似是反转后的头
     * cur是遍历当前链表的结点
     * temp是中间保存结点
     */
    public ListNode reverseList_1(ListNode head) {
        ListNode prev = null;
        ListNode cur = head;
        ListNode temp = null;

        while (cur != null) {
            temp = cur.next;
            cur.next = prev;
            prev = cur;
            cur =temp;
        }

        return prev;
    }

    /**
     * 递归写法1
     */
    public ListNode reverseList_2(ListNode head) {
        return reverse(null, head);
    }

    /**
     *  在前面双指针的基础上看,然后一直过去即可
     *  传参就是pre【前一个值】和cur【当前值】
     */
    private ListNode reverse(ListNode prev, ListNode cur) {
        if (cur == null) {
            return prev;
        }
        ListNode temp = null;
        temp = cur.next;// 先保存下一个节点
        cur.next = prev;// 反转
        // 更新prev、cur位置
        // prev = cur;
        // cur = temp;
        return reverse(cur, temp);
    }

    /**
     * 从后往前,类似说后续遍历,一定要把条件写在递归的后面
     * 从后往前翻转指针指向
     */
    public ListNode reverseList_3(ListNode head) {
        // 如果链表是空的或者链表就一个值,直接返回其本身
        if(head == null || head.next == null){
            return head;
        }

        // 递归调用,翻转第二个节点开始往后的链表
        ListNode last = reverseList_3(head.next);
        // 翻转头节点与第二个节点的指向
        head.next.next = head;
        // 此时的 head 节点为尾节点,next 需要指向 NULL
        head.next = null;

        return last;
    }
}

707.设计链表

你可以选择使用单链表或者双链表,设计并实现自己的链表。

单链表中的节点应该具备两个属性:val 和 next 。val 是当前节点的值,next 是指向下一个节点的指针/引用。

如果是双向链表,则还需要属性 prev 以指示链表中的上一个节点。假设链表中的所有节点下标从 0 开始。

实现 MyLinkedList 类:

MyLinkedList() 初始化 MyLinkedList 对象。
int get(int index) 获取链表中下标为 index 的节点的值。如果下标无效,则返回 -1 。
void addAtHead(int val) 将一个值为 val 的节点插入到链表中第一个元素之前。在插入完成后,新节点会成为链表的第一个节点。
void addAtTail(int val) 将一个值为 val 的节点追加到链表中作为链表的最后一个元素。
void addAtIndex(int index, int val) 将一个值为 val 的节点插入到链表中下标为 index 的节点之前。如果 index 等于链表的长度,那么该节点会被追加到链表的末尾。如果 index 比长度更大,该节点将 不会插入 到链表中。
void deleteAtIndex(int index) 如果下标有效,则删除链表中下标为 index 的节点。
 

示例:

输入
["MyLinkedList", "addAtHead", "addAtTail", "addAtIndex", "get", "deleteAtIndex", "get"]
[[], [1], [3], [1, 2], [1], [1], [1]]
输出
[null, null, null, null, 2, null, 3]

解释
MyLinkedList myLinkedList = new MyLinkedList();
myLinkedList.addAtHead(1);
myLinkedList.addAtTail(3);
myLinkedList.addAtIndex(1, 2);    // 链表变为 1->2->3
myLinkedList.get(1);              // 返回 2
myLinkedList.deleteAtIndex(1);    // 现在,链表变为 1->3
myLinkedList.get(1);              // 返回 3
 
https://leetcode.cn/problems/design-linked-list/
这个经典题,增加一个虚拟节点,后面的考虑就很简单了
package com.example.leetcode.labuladong.sxl.sxl_3;

/**
 *
 */
public class sxl_707 {
    //单链表
    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个节点的数值,注意index是从0开始的,第0个节点就是头结点
        public int get(int index) {
            //如果index非法,返回-1
            if (index < 0 || index >= size) {
                return -1;
            }
            ListNode currentNode = head;
            //包含一个虚拟头节点,所以查找第 index+1 个节点
            for (int i = 0; i <= index; i++) {
                currentNode = currentNode.next;
            }
            return currentNode.val;
        }

        //在链表最前面插入一个节点,等价于在第0个元素前添加
        public void addAtHead(int val) {
            addAtIndex(0, val);
        }

        //在链表的最后插入一个节点,等价于在(末尾+1)个元素前添加
        public void addAtTail(int val) {
            addAtIndex(size, val);
        }

        // 在第 index 个节点之前插入一个新节点,例如index为0,那么新插入的节点为链表的新头节点。
        // 如果 index 等于链表的长度,则说明是新插入的节点为链表的尾结点
        // 如果 index 大于链表的长度,则返回空
        public void addAtIndex(int index, int val) {
            if (index > size) {
                return;
            }
            if (index < 0) {
                index = 0;
            }
            size++;
            //找到要插入节点的前驱
            ListNode pred = head;
            for (int i = 0; i < index; i++) {
                pred = pred.next;
            }
            ListNode toAdd = new ListNode(val);
            toAdd.next = pred.next;
            pred.next = toAdd;
        }

        //删除第index个节点
        public void deleteAtIndex(int index) {
            if (index < 0 || index >= size) {
                return;
            }
            size--;
            if (index == 0) {
                head = head.next;
                return;
            }
            ListNode pred = head;
            for (int i = 0; i < index ; i++) {
                pred = pred.next;
            }
            pred.next = pred.next.next;
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值