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

203.移除链表元素

建议:本题最关键是要理解 虚拟头结点的使用技巧,这个对链表题目很重要。 

题目链接:https://leetcode.cn/problems/remove-linked-list-elements/

文章链接:https://programmercarl.com/0203.%E7%A7%BB%E9%99%A4%E9%93%BE%E8%A1%A8%E5%85%83%E7%B4%A0.html

视频链接:https://www.bilibili.com/video/BV18B4y1s7R9/?spm_id_from=333.788&vd_source=c9ff2916e0b93a628546c1c7c57aa525m

思路分析 

 

 

  1.  首先考虑如何删除节点,如图所示,即想要删除的当前节点的前置节点指向下下个节点
  2. 确定当前节点为判断是否要删除的目标,即定义cur
  3. 删除时需要前一个节点的帮助,需要定义变量pre
  4. 建立虚拟头节点dummy,指向head,使链表包括头节点在内的每一个节点都有前置节点
  5. 判断当前节点不为空,即cur!=null,开始遍历
  6. 最终返回虚拟头结点dummy的下一个节点,即dummy.next 

在加一点细节,代码如下:

public ListNode removeElements(ListNode head, int val) {
        if (head == null || (head.next == null && head.val != val)) {
            return head;
        }
        if (head.next == null && head.val == val) {
            return null;
        }
        //定义虚拟头节点,指定dummy的下一个节点是头节点,建立联系,将虚拟头节点加入链表
        ListNode dummy = new ListNode(-1, head);

        //定义临时节点pre,cur来遍历整个链表
        //为什么不用dummy,head直接遍历,如果用dummy,head遍历,他们指向的值不断地再改变,最后无法返回最初链表的头节点
        ListNode pre = dummy;
        ListNode cur = head;

        //判断当前节点不为空时,一直遍历链表
        while (cur != null) {
            //如果当前节点的值等于要删除的目标
            if (cur.val == val) {
                //将当前节点cur的前置节点pre的下一个节点指向当前节点的下一个节点cur.next
                pre.next = cur.next;
            } else {
                //否则,当前节点cue的前置节点pre向下走一步遍历
                pre = pre.next;
            }
            //一直判断的都是当前节点cur的值,所以不管是否等于目标,都要向下遍历
            cur = cur.next;
        }
        //返回最初设置的虚拟节点的下一个节点
        return dummy.next;
    }

总结

 使用虚拟头节点的话,删除头节点和非头节点的操作是比一样的,设置一个虚拟头结点,这样原链表的所有节点就都可以按照统一的方式进行移除了

707.设计链表 

建议: 这是一道考察 链表综合操作的题目,不算容易,可以练一练 使用虚拟头结点

题目链接:https://leetcode.cn/problems/design-linked-list/

文章链接:https://programmercarl.com/0707.%E8%AE%BE%E8%AE%A1%E9%93%BE%E8%A1%A8.html#%E4%BB%A3%E7%A0%81

视频链接:https://www.bilibili.com/video/BV1FU4y1X7WD/?spm_id_from=333.788&vd_source=c9ff2916e0b93a628546c1c7c57aa525

 思路分析

简单的删除节点和添加节点操作,注意的是头节点的index为0,而我们创建的虚拟头节点的索引可以看为-1

public class MyLinkedList {

    //记录链表中元素的数量
    int size;
    //记录链表的虚拟头结点
    ListNode head;

    public MyLinkedList() {
        //初始化操作
        this.size = 0;
        this.head = new ListNode(0);
    }

    /**
     * get(index):获取链表中第 index 个节点的值。如果索引无效,则返回-1。
     * @param index
     * @return
     */
    public int get(int index) {
        //获取第index个节点的数值,注意index是从0开始的,第0个节点就是头结点
        if (index < 0 || index >= size) {
            return -1;
        }
        ListNode cur = head;
        //包含一个虚拟头节点,所以查找第 index+1 个节点
        for (int i = 0; i <= index; i++) {
            cur = cur.next;
        }
        return cur.val;

    }

    /**
     * addAtHead(val):在链表的第一个元素之前添加一个值为 val 的节点。
     * 插入后,新节点将成为链表的第一个节点。
     * @param val
     */
    public void addAtHead(int val) {
        addAtIndex(0, val);
    }

    /**
     * addAtTail(val):将值为 val 的节点追加到链表的最后一个元素。
     * @param val
     */
    public void addAtTail(int val) {
        addAtIndex(size, val);
    }

    /**
     * addAtIndex(index,val):在链表中的第 index 个节点之前添加值为 val
     * 的节点。如果 index 等于链表的长度,则该节点将附加到链表的末尾。
     * 如果 index 大于链表长度,则不会插入节点。如果index小于0,则在头部插入节点。
     * @param index
     * @param val
     */
    public void addAtIndex(int index, int val) {
        ListNode addNode = new ListNode(val);
        if (index <= 0) {
            //直接添加在头部
            addNode.next = head.next;
            head.next = addNode;
            size++;
        } else if (index <= size) {
            
            //添加在中间或尾部
            //第1个节点索引为0,所以第index+1个节点,索引为index
            ListNode cur = head;
            
            /*在链表中的第 index 个节点之前添加值为 val 的节点,
             所以只需要遍历到第index-1的节点,进行添加
            */
            for (int i = 0; i < index; i++) {
                cur = cur.next;
            }
            addNode.next = cur.next;
            cur.next = addNode;
            size++;
        }

    }

    /**
     * deleteAtIndex(index):如果索引 index 有效,
     * 则删除链表中的第 index 个节点。
     * @param index
     */
    public void deleteAtIndex(int index) {
        if (index >= 0 && index < size) {
            ListNode cur = head;
            for (int i = 0; i < index ; i++) {
                cur = cur.next;
            }
            cur.next = cur.next.next;
            size--;
        }

    }
}

 

206.反转链表

题目链接:https://leetcode.cn/problems/reverse-linked-list/

文章链接:https://programmercarl.com/0206.%E7%BF%BB%E8%BD%AC%E9%93%BE%E8%A1%A8.html#%E5%8F%8C%E6%8C%87%E9%92%88%E6%B3%95

视频链接:https://www.bilibili.com/video/BV1nB4y1i7eL/?spm_id_from=333.788&vd_source=c9ff2916e0b93a628546c1c7c57aa525

思路分析

 

  1.  将已反转和未反转的看成两各部分,一直在两个部分的头,进行调换指向方向翻转操作
  2. 首先初始化当前节点cur=head,初始化prev=null
  3. 开始遍历,当cur不为空时
  4. 设置临时节点temp,我们在从前往后遍历的时候,在改变当前节点的指向方向时,应该记录原方向的下一个节点。若向翻转方向,没用temp保存,那么就不知道接着要遍历到哪一个节点了。
  5. 翻转方向cur.next=pre
  6. 向下遍历pre=cur,cur=temp,我们应该先移动prev,此时cur没变,cur为prev位置要遍历的下一个节点,所以先赋值prev,在赋值cur,否则我们该找不到prev按照图中从左到右遍历顺序的下一个节点

代码如下 

public ListNode reverseList(ListNode head) {
        ListNode pre = null;
        ListNode cur = head;
        ListNode temp = null;
        while (cur != null) {
            temp = cur.next;
            cur.next = pre;
            pre = cur;
            cur = temp;
        }
        return pre;
    }

 

总结

首先应拆分翻转的部分,清楚每一次指向方向的改变以及谁先谁后的顺序,在遍历过程中也应该注意prev,cur,temp的赋值的先后顺序

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值