【面试算法笔记】0202-链表-基本功能实现

个人主页:https://github.com/zbhgis

前言

本系列主要记录自己学习算法的过程中的感悟。

力扣203. 移除链表元素

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

注意点

链表操作的题目,为了减少判断操作,都可以引入一个虚拟头节点,指向head。

这里的移除操作,就是相当于将cur指向cur.next.next,跳过中间的cur.next,就是上一篇的图示。

本质就是遍历各个节点,然后修改指针指向,以及题目如果需要返回链表的话,就是返回头节点。

代码

Java 复制代码
/**
 * 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) {
        ListNode dummy = new ListNode(0, head);
        ListNode cur = dummy;
        while (cur.next != null) {
            if (cur.next.val == val) {
                cur.next = cur.next.next;
            } else {
                cur = cur.next;
            }
        }
        return dummy.next;
    }
}

时空复杂度分析

单层循环,最多需要遍历n次,n为链表长度,因此时间复杂度为O(n)

空间复杂度为O(1)

力扣707. 设计链表-单向

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

注意点

定义链表就不多说了。以及各个方法中的边界判断也就不说了,特殊的就是在addAtIndex中是可以到size下标。

get方法,就是设置一个cur,去遍历各个节点,到所需下标处返回。

addAtIndex方法,就是通过一个前驱节点,得到想要添加处的前一个节点prev,最后新的节点指向prev.next,prev指向新的节点。

deleteAtIndex方法,也是通过前驱节点遍历,只不过最后是把prev指向prev.next.next,跳过了所需下标的节点。

代码

Java 复制代码
public class MyLinkedList {

    // 单链表节点定义
    private class Node {
        int val;
        Node next;

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

    private Node dummy;   // 虚拟头节点
    private int size;     // 链表长度

    /** 初始化 */
    public MyLinkedList() {
        dummy = new Node(0);
        size = 0;
    }

    /** 获取下标 index 的节点值 */
    public int get(int index) {
        if (index < 0 || index >= size) {
            return -1;
        }

        Node cur = dummy.next;
        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);
    }

    /** 在下标 index 前插入节点 */
    public void addAtIndex(int index, int val) {
        if (index < 0 || index > size) return;

        Node prev = dummy;
        for (int i = 0; i < index; i++) {
            prev = prev.next;
        }

        Node newNode = new Node(val);
        newNode.next = prev.next;
        prev.next = newNode;
        size++;
    }

    /** 删除下标 index 的节点 */
    public void deleteAtIndex(int index) {
        if (index < 0 || index >= size) return;

        Node prev = dummy;
        for (int i = 0; i < index; i++) {
            prev = prev.next;
        }

        prev.next = prev.next.next;
        size--;
    }
}

时空复杂度分析

除了addAtHead和addAtTail方法是O(1),其他时间复杂度为O(n)

局部空间复杂度为O(1),整体因为要存储n个节点,所以整体空间复杂度为O(n)

力扣707. 设计链表-双向

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

注意点

同上。

代码

Java 复制代码
class MyLinkedList {
    public class Node {
        int val;
        Node prev;
        Node next;

        Node() {}
        Node(int val) {this.val = val;}
        Node(int val, Node prev, Node next) {
            this.val = val;
            this.prev = prev;
            this.next = next;
        }
    }
    private final Node dummyHead;
    private final Node dummyTail;
    private int size;
    public MyLinkedList() {
        dummyHead = new Node(0);
        dummyTail = new Node(0);

        dummyHead.next = dummyTail;
        dummyTail.prev = dummyHead;
        size = 0;
    }
    
    public int get(int index) {
        if (index < 0 || index >= size) return -1;
        Node cur;
        if (index < size / 2) {
            cur = dummyHead.next;
            for (int i = 0; i < index; i ++) cur = cur.next;
        } else {
            cur = dummyTail.prev;
            for (int i = size - 1; i > index; i --) cur = cur.prev;
        }
        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 < 0 || index > size) return;
        Node prev, next;
        if (index < size / 2) {
            prev = dummyHead;
            for (int i = 0; i < index; i ++) prev = prev.next;
            next = prev.next;
        } else {
            next = dummyTail;
            for (int i = size; i > index; i --) next = next.prev;
            prev = next.prev;
        }
        
        Node newNode = new Node(val, prev, next);
        prev.next = newNode;
        next.prev = newNode;
        size ++;
    }
    
    public void deleteAtIndex(int index) {
        if (index < 0 || index >= size) return;
        Node prev, next;
        if (index < size / 2) {
            prev = dummyHead;
            for (int i = 0; i < index; i ++) prev = prev.next;
            next = prev.next;
        } else {
            next = dummyTail;
            for (int i = size; i > index; i --) next = next.prev;
            prev = next.prev;
        }

        prev.next = next.next;
        next.next.prev = prev;
        size --;
    }
}

/**
 * Your MyLinkedList object will be instantiated and called as such:
 * MyLinkedList obj = new MyLinkedList();
 * int param_1 = obj.get(index);
 * obj.addAtHead(val);
 * obj.addAtTail(val);
 * obj.addAtIndex(index,val);
 * obj.deleteAtIndex(index);
 */

时空复杂度分析

同上

力扣206. 反转链表

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

注意点

从前往后遍历节点,但是需要遍历到的节点找到指针方向。

代码

Java 复制代码
/**
 * 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) {
            ListNode nxt = cur.next;
            cur.next = pre;
            pre = cur;
            cur = nxt;
        }
        return pre;
    }
}

时空复杂度分析

单层循环,最多需要遍历n次,n为链表长度,因此时间复杂度为O(n)

空间复杂度为O(1)

参考

https://programmercarl.com/链表理论基础.html

相关推荐
你想知道什么?1 小时前
Matplotlib学习笔记
笔记·学习·matplotlib
3DVisionary1 小时前
如何用单目高速DIC把“看不见“的振动变成“数得清“的数据
java·spring boot·python·数码相机·微小振动·精密件·单目高速dic
tkevinjd1 小时前
416分割等和子集
java·python·算法·leetcode·职场和发展
Keven_112 小时前
算法札记:Tarjan与拓扑序(Topo)的关系
算法·拓扑·tarjan
qq_452396232 小时前
第三篇:《Jenkins Pipeline as Code:Declarative Pipeline 深度实战》
java·servlet·jenkins
AOwhisky2 小时前
Linux(CentOS)系统管理入门笔记:第二期核心知识点自测与详解
linux·笔记·centos
KaifuZeng2 小时前
通信与接口协议面试问题汇总一
单片机·嵌入式硬件·面试·通信与接口协议
ShallWeL2 小时前
AUC、F1、召回率怎么选
人工智能·算法·机器学习
再让我睡两分钟2 小时前
【无标题】
android·java·数据库·人工智能·prompt·ai应用开发