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

1. 链表的定义和类型

  • 定义:链表是通过指针串联在一起的线性结构,每一个节点包含两部分:数据+下一个节点的指针

        注意:普通的单项列表不能回溯,移动前需要考虑是否需要用到目前节点的信息

        

  • 类型:单链表、双链表、循环链表 
    • 双链表:每个节点有两个指针域,即可以镶嵌查询也可以向后查询

  •         循环链表

  • 链表在内存中的存储方式:在内存中非连续分布,通过指针域的指针连接在内存中各个节点。         

2. 链表的代码实现

       1)定义

                在java中以class进行定义,代码实现如下:

public class ListNode {
    // 数据信息
    int val;

    // 下一节点信息
    ListNode next;

    // 节点的构造函数(无参)
    public ListNode() {
    }

    // 节点的构造函数(有一个参数)
    public ListNode(int val) {
        this.val = val;
    }

    // 节点的构造函数(有两个参数)
    public ListNode(int val, ListNode next) {
        this.val = val;
        this.next = next;
    }
}

        2)性能分析

                删除和添加节点的复杂度都是O(1), 查找的复杂度是O(n),并且长度可以动态改变

                适用场景:数据量不固定,频繁增删,较少查询

3. 203. 移除链表元素

        leetcode URL: 203. 移除链表元素 - 力扣(LeetCode)

        问题描述:删除所有元素为target的节点(可能需要删除不止一个)

  • 思路1:分为要删除的target是头节点和不是头节点的两种情况讨论
class Solution {
    public ListNode removeElements(ListNode head, int val) {
        // delete head pointer
        // 注意这里使用的是while循环,因为有可能是 1,1,2,3,4,此时需要删除不止一个头节点
        while (head != null && head.val == val){
            head = head.next;
        }
        // delete non-head pointer
        ListNode current = head;
        while(current != null && current.next != null){
            //注意:这里也需要用while循环,避免target 2,需要删除[1,2,2,1]这种情况
            while( current.next != null && current.next.val == val){
                current.next = current.next.next;
            }
            current = current.next;
        }
        return head;
    }
}
  • 思路2:设置一个虚拟的头节点,以统一的方式删除所有节点,更加简洁
class Solution {
    public ListNode removeElements(ListNode head, int val) {
        // 新建一个虚拟的头节点,并且让他指向头节点
        ListNode dummyHead = new ListNode();
        dummyHead.next = head;
        // 后面按照非头节点的方式删除
        ListNode current = dummyHead;
        while(current != null){
            while(current.next!=null && current.next.val == val){
                current.next = current.next.next;
            }
            current = current.next;
        }
        // 注意return的值
        return dummyHead.next;
    }
}
  • 在这题实现过程中需要注意的问题:

           1. 注意不能操作空字符串

           2. 注意删除的元素可能不止一个,需要在一些地方使用while循环,判断是选择if还是while

4. 707 设计链表 (包含链表的基础操作-重要)

        Leetcode link: 707. 设计链表 - 力扣(LeetCode)

        问题描述:进行链表的基本操作:头/尾部插入,删除第n个,在第n个节点前插入

        思路:这里运用了虚拟头的方式,先建了一个虚拟头节点

        遇到的问题:由于加了虚拟头对于尾结点和size的边界不清楚,此时index=size

        

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

}
class MyLinkedList {
    // 
    ListNode dummyhead;
    int size;

    public MyLinkedList() {
        size = 0;
        dummyhead = new ListNode(0);
    }

    //获取index的节点的值
    public int get(int index){
        if (index < 0 || index >= size) {
            return -1;
        }
        ListNode current = dummyhead;
        while(index != 0){
            current = current.next;
            index--;
        }
        return current.next.val;
    }
    
    public void addAtHead(int val) {
        addAtIndex(0,val);
    }
    
    public void addAtTail(int val) {
        addAtIndex(size,val);
    }
    
    public void addAtIndex(int index, int val) {
        ListNode current = dummyhead;
        // 遇到的问题:面对插入最后的节点,由于虚拟头本身占一个位置,因此尾结点应该是index=size
        if(index >= 0 && index <= size){
            while(index != 0){
                current = current.next;
                index--;
            }

            //add
            ListNode newNode = new ListNode(val);
            newNode.next = current.next;
            current.next = newNode;
            size++;
        }

    }
    
    public void deleteAtIndex(int index) {
        if (index < 0 || index >= size) {
            return;
        }
        size--;
        ListNode cur = dummyhead;
        while(index != 0){
            cur = cur.next;
            index--;
        }
        cur.next = cur.next.next;
    }
}

5. 206. 反转列表|双指针法|递归法

        Leetcode link: 206. 反转链表 - 力扣(LeetCode)

        两种写法:双指针 & 递归

  1. 双指针法:

                两个指针:current和previous两个指针 (同时用temp存储current的下一个值)

class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode pre = null;
        ListNode current = head;

        while(current!= null){
            ListNode temp = current.next;
            current.next = pre;
            pre = current;
            current = temp;
        }
        return pre;
    }
}

     2. 递归法:(有一点难理解,可以直接看206.反转链表

                写的时候遇到的问题:没有理解return在递归中的作用

class Solution {
    public ListNode reverseList(ListNode head) {
        return reverse(null, head);
    }

    ListNode reverse(ListNode pre, ListNode current){
        //终止方式
        if(current == null) return pre;
        
        ListNode temp = current.next;
        current.next = pre;
        pre = current;
        // 一定要注意每个阶段都在return什么
        return reverse(current,temp);
    }
}

6. 小结

        今天的链表部分总体来说比较简单,做的时候最好还是画图理解一下

        需要注意:1. 空节点不能进行操作 2.虚拟头节点方法可以避开对head的分类讨论

                          3. 理解递归的思想

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值