代码随想录第三天 | 203.移除链表元素 + 707 设计链表 + 206 反转链表

LeetCode203.移除链表元素

一般我们处理链表有两种方式,一种是没有虚拟头结点,一种是有虚拟头结点。

那么这两者有什么区别呢?

没有虚拟头结点,需要注意的是需要对头结点也就是第一个结点做特殊的处理。

而使用了虚拟头结点,应该注意的是返回结果是应该是 dummyHead.next。

下面我们就对这两种方式的代码进行实现:

代码如下(不使用虚拟头结点):

 public ListNode removeElements(ListNode head, int target) {
        //如果头结点为空
        //直接返回即可
        if (head == null) {
            return head;
        }

        //注意:如果不使用虚拟头结点,需要格外处理如下这种情况
        //如果head.val == target
        while (head != null && head.val == target) {
            head = head.next;
        }

        //定义一个指针point
        ListNode point = head;
        //遍历链表
        while (point != null) {

            while (point.next != null && point.next.val == target) {
                point.next = point.next.next;
            }
            point = point.next;
        }

        return head;
    }

需要注意的是,我们要避免空指针的异常,所以我们要对指针进行非空的判断。

代码如下(使用虚拟头结点):

public ListNode removeElementsByDummyHead(ListNode head, int target) {
        if (head == null) {
            return head;
        }

        //创建虚拟头结点
        ListNode dummyHead = new ListNode(head, -1);
        ListNode point = dummyHead;

        //遍历链表
        while (point != null) {

            while (point.next != null && point.next.val == target) {
                point.next = point.next.next;
            }
            point = point.next;
        }

        //注意:使用虚拟头结点返回的时候要返回 dummyHead.next
        return dummyHead.next;
    }

LeetCode.707 设计链表

该题需要注意的点比较多,首先我们要知道我们在链表中如何删除或者添加一个元素到链表中。

添加:

我们需要找到他的前驱结点,并且我们这里还需要保存他前驱的节点的下一个节点。然后我们再将我们要添加的节点加入到前驱结点的后面,再将我们新添加的结点的next指向我们保存的结点即可。

//前驱结点
ListNode preNode;
//新结点
ListNode newNode;
//保存前驱节点的下一个节点
LisetNode temp = preNode.next;

//添加
preNode.next = newNode;
newNode.next = temp;

删除:

完成删除操作,也是要找到我们要删除的结点的前驱节点。也要保存我们要删除节点的下一个节点的下一个节点,然后我让前驱结点指向我们保存的结点,即完成了删除操作。

//前驱节点
ListNode preNode;
//保存前驱结点的下一个节点的下一个节点
ListNode temp = preNode.next.next;

//删除操作
preNode.next  = temp;

明白了链表如何删除和添加,那么完成这个设计链表就比较简单了。

提醒:我们需要注意 索引(index) 是否可用。

代码如下:

class MyLinkedList {

    //定义链表的数量
    private int size;

    //虚拟头结点
    private ListNode dummyHead;

    //初始化MyLinkedList
    public MyLinkedList() {
        size = 0;
        dummyHead = new ListNode(null, 0);
    }

    public int get(int index) {
        //判断索引是否有效
        if (index >= size || index < 0) {
            return -1;
        }

        ListNode point = dummyHead;

        for (int i = 0; i <= index; i++) {
            point = point.next;
        }
        //返回对应索引的值
        return point.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;
        }
        //添加元素,所以size++
        size++;
        //找到要加入节点的前驱结点
        ListNode point = dummyHead;
        for (int i = 0; i < index; i++) {
            point = point.next;
        }
        //创建新的节点
        ListNode newNode = new ListNode(val);
        //添加
        newNode.next = point.next;
        point.next = newNode;
    }

    public void deleteAtIndex(int index) {
        //判断索引是否正确
        if (index >= size || index < 0) {
            return;
        }

        //删除元素,size--
        size--;
        if (index == 0) {
            dummyHead = dummyHead.next;
            return;
        }

        ListNode point = dummyHead;
        //找到要删除节点的前驱结点
        for (int i = 0; i < index; i++) {
            point = point.next;
        }
        point.next = point.next.next;
    }


}

LeetCode.206 反转链表

本题我们使用双指针的思路去完成。

前言提到过,双指针的思路,我们必须明确定义的两个指针的作用。

point:用于指向旧链表的头结点

pre:用于指向新链表的头结点

开始对两个指针完成初始化。point 既然是旧链表的头结点,所以 point = head,pre 是指向新链表的头结点,而反转还没有开始,所以 pre = null。

注意这里还有一个细节问题,我们应该再定义一个指针,用于保存 point 的下一个节点 。

temp :用于保存 point 指针的下一个节点。

代码如下(双指针):

    public ListNode reverseList(ListNode head) {
        //定义两个指针
        ListNode point = head;
        //该指针用于记录 新的链表的头结点
        ListNode pre = null;
        //再定义一个指针,用于保存point.next的位置
        ListNode temp = null;

        while (point != null) {
            //保存位置
            temp = point.next;
            //开始反转
            point.next = pre;

            //反转完成后,向后移动,继续遍历
            pre = point;
            point = temp;

        }

        return pre;
    }

本题其实还可以使用递归的方式来实现。

递归的三要素:

  • 返回类型和入参
  • 终止条件
  • 单层的逻辑

代码如下(递归):

    public ListNode reverseListByDFS(ListNode head) {
       //和双指针一样,我们初始化 point,pre
       //传入 head,null
        return reverse(head, null);
    }
    

    public ListNode reverse(ListNode point, ListNode pre) {
        //终止条件
        if (point == null) {
            return pre;
        }

        //保存结点
        ListNode temp = null;
        temp = point.next;
        //反转
        point.next = pre;
        //更新pre,point的位置
        return reverse(temp, point);
    }

  • 12
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值