数据结构之链表

简介: 数据结构之链表

动态数据结构


网络异常,图片无法展示
|


链表的特征


网络异常,图片无法展示
|


网络异常,图片无法展示
|


数组和链表的对比


网络异常,图片无法展示
|


链表的设计


表头添加元素


网络异常,图片无法展示
|


在中间添加元素


关键是找到待添加节点的前一个节点。


如果不设置虚拟头结点,由于head没有钱一个节点,所以就需要对头结点进行特殊的处理。


网络异常,图片无法展示
|


网络异常,图片无法展示
|


删除元素


网络异常,图片无法展示
|


这里最主要的是获取当前节点的前一个节点和当前节点。


  • 获取当前节点


/**
 * index = 2
 * 1, 2, 3, 4
 * cur = dummyHead.next = 1, i < index => i < 2
 * i = 0; cur = 2
 * i = 1; cur = 3;
 */
Node cur = dummyHead.next;
for(int i = 0; i < index; i++) {
    cur = cur.next;
}


  • 获取当前节点的前一个节点


/**
 * index = 2
 * 1, 2, 3, 4
 * prev = dummyHead = null, i < index => i < 2
 * i = 0; cur = 1
 * i = 1; cur = 2;
 */
Node prev = dummyHead;
for(int i = 0; i < index; i++) {
   prev = prev.next;
}


代码实现


public class LinkedList<E> {
    private Node dummyHead;
    private int size;
    private class Node {
        public E e;
        public Node next;
        public Node(E e, Node next) {
            this.next = next;
            this.e = e;
        }
        public Node(E e) {
            this.e = e;
        }
        public Node() {
            this(null, null);
        }
        @Override
        public String toString() {
            return e.toString();
        }
    }
    public LinkedList() {
//        链表中本身存在一个空节点,虚拟节点。
         this.dummyHead = new Node(null, null);
         this.size = 0;
    }
//    获取元素个数
    public int getSize() {
        return size;
    }
//    判断是否为空
    public boolean isEmpty() {
        return size == 0;
    }
//    头部添加元素
    public void addFirst(E e) {
//        先创建一个node节点
//        Node node = new Node(e);
//        node.next = head;
//        head = node;
//        head = new Node(e, head);
        add(0, e);
    }
//    在中间添加节点
    public void add(int index, E e) {
        if(index > size && index < 0) {
            throw new Error("add fail, index > size && index < 0");
        }
//        先创建一个prev节点,来查找预添加节点的前一个节点。
        Node prev = dummyHead;
        // 这里的边界是index。因为prev开始是等于第一个节点之前的空节点的。
        for(int i = 0; i < index; i++) {
            prev = prev.next;
        }
        Node node = new Node(e);
        node.next = prev.next;
        prev.next = node;
        size++;
    }
//    从尾部添加节点
    public void addLast(E e) {
        add(size - 1, e);
    }
//    查找元素
    public E get(int index) {
        /**
         * index = 2
         * 1, 2, 3, 4
         * cur = dummyHead.next = 1, i < index => i < 2
         * i = 0; cur = 2
         * i = 1; cur = 3;
         */
        Node cur = dummyHead.next;
        for(int i = 0; i < index; i++) {
            cur = cur.next;
        }
        return cur.e;
    }
//    确定是否有该元素
    public boolean contains(E e) {
        Node cur = dummyHead.next;
        for(int i = 0; i < size; i++) {
            if(cur.e == e) {
                return true;
            }
            cur = cur.next;
        }
        return false;
    }
//    修改元素
    public void set(int index, E e) {
        if(index < 0 || index >= size) {
            throw new Error("set fail, index < 0 || index >= size");
        }
        Node cur = dummyHead;
        for(int i = 0; i < index; i++) {
            cur = cur.next;
        }
        cur.e = e;
    }
//    删除元素
    public E remove(int index) {
        if(index > size && index < 0) {
            throw new Error("remove fail, index > size && index < 0");
        }
//        找到待删除节点之前的节点。
        /**
         * index = 2
         * 1, 2, 3, 4
         * prev = dummyHead = null, i < index => i < 2
         * i = 0; cur = 1
         * i = 1; cur = 2;
         */
        Node prev = dummyHead;
        for(int i = 0; i < index; i++) {
           prev = prev.next;
        }
//        将prev的next指向下下一个元素。
        Node cur = prev.next;
        prev.next = cur.next;
        size--;
        return cur.e;
    }
    public E removeFirst() {
        return remove(0);
    }
    public E removeLast() {
        return remove(size - 1);
    }
    // 通过指定元素删除元素
    public void removeElement(E e) {
        Node prev = dummyHead; // 从头开始遍历
        while(prev.next != null) {
            if(e.equals(prev.next.e)) {
                prev.next = prev.next.next;
                prev.next.next = null;
                size--;
            }
            prev = prev.next;
        }
    }
    @Override
    public String toString() {
        StringBuilder res = new StringBuilder();
        res.append("LinkedList:    ");
        Node cur = dummyHead.next;
        while(cur != null) {
            res.append(cur.e + "=>");
            cur = cur.next;
        }
        return res.toString();
    }
}


复杂度分析


网络异常,图片无法展示
|


网络异常,图片无法展示
|


网络异常,图片无法展示
|


网络异常,图片无法展示
|


用链表设计栈结构


我们可以使用链表头部作为栈顶结构。


网络异常,图片无法展示
|


public class StackByLinkedList<E> implements StackInterface<E> {
//    我们可以使用链表头部作为栈顶结构。
    private LinkedList<E> list;
    public StackByLinkedList() {
        list = new LinkedList();
    }
    @Override
    public void push(E e) {
        list.addFirst(e);
    }
    @Override
    public E pop() {
        return list.removeFirst();
    }
    @Override
    public int getSize() {
        return list.getSize();
    }
    @Override
    public E peek() {
        return list.get(0);
    }
    @Override
    public boolean isEmpty() {
        return list.isEmpty();
    }
    @Override
    public String toString() {
        StringBuilder res = new StringBuilder();
        res.append("linkedListStack top: ");
        res.append(list);
        return res.toString();
    }
}


用链表设计队列结构


这里需要注意的是,当队列中只有一个元素时,删除了元素,需要将tail指针指向null。


网络异常,图片无法展示
|


public class LinkedListQueue<E> implements QueueInterface<E> {
//    节点总数
    private int size;
//    头部节点
    private Node head;
//    尾部节点
    private Node tail;
    private class Node {
        public E e;
        public Node next;
        public Node(E e) {
            this.e = e;
        }
        public Node(E e, Node next) {
            this.e = e;
            this.next = next;
        }
        public Node() {
            this(null, null);
        }
        @Override
        public String toString() {
            return e.toString();
        }
    }
    public LinkedListQueue() {
        this.size = 0;
        this.head = null;
        this.tail = null;
    }
    //    入队
    @Override
    public void enqueue(E e) {
//        需要先判断队列是否为空
        if(tail == null) {
            tail = new Node(e);
            head = tail;
        }else {
//            改变尾节点指向。
            tail.next = new Node(e);
            tail = tail.next;
        }
        size++;
    }
    @Override
    public E dequeue() {
        if(head == null) {
            throw new Error("linkedListQueue is empty");
        }
        E cur = head.e;
        head = head.next;
//        如果队列中只有一个元素,那么删除后需要改变tail指向
       if(head == null) {
            tail = null;
        }
        size--;
        return cur;
    }
    @Override
    public E getFront() {
        return head.e;
    }
    @Override
    public int getSize() {
        return size;
    }
    @Override
    public boolean isEmpty() {
        return size == 0;
    }
    @Override
    public String toString() {
        StringBuilder res = new StringBuilder();
        res.append("LinkedListQueue : ");
        Node cur = head;
        while(cur != null) {
            res.append(cur.e + "=>");
            cur = cur.next;
        }
        return res.toString();
    }
}


移除链表元素 LeetCode-203


  • 方式一:删除头结点时另做考虑


class Solution {
    public ListNode removeElements(ListNode head, int val) {
       // 查看头部节点是否为val元素。可能删除后下一个节点依旧是val。
       while(head != null && head.val == val) {
           head = head.next;
       }
    //    当删除头部节点时,链表为空,则返回false.
        if(head == null) {
            return null;
        }
        // 判断中间元素是否是val
        ListNode prev = head;
        while(prev.next != null) {
            // 查找到了,将跳跃一个元素
            if(prev.next.val == val) {
                prev.next = prev.next.next;
            }else {
            // 没有查到,将指向下一个元素继续。
                prev = prev.next;
            }
        }
        return head;
    }
}


  • 方式二: 创建一个虚拟头结点。 注意: 这个虚拟节点的next始终指向头结点。给出虚拟头结点的作用是为了可以统一处理头结点和中间节点。因为我们需要找到的是待删除元素的前一个元素。


class Solution {
    public ListNode removeElements(ListNode head, int val) {
        // 创建一个虚拟节点。并且next始终指向头结点。
        ListNode dummyHead = new ListNode(-1);
        dummyHead.next = head;
        // 创建一个查询的前一个节点
        ListNode prev = dummyHead;
        while(prev.next != null) {
            if(prev.next.val == val) {
                prev.next = prev.next.next;
            }else {
                prev = prev.next;
            }
        }
        return dummyHead.next;
    }
}



相关文章
|
存储 算法 Perl
数据结构实验之链表
本实验旨在掌握线性表中元素的前驱、后续概念及链表的建立、插入、删除等算法,并分析时间复杂度,理解链表特点。实验内容包括循环链表应用(约瑟夫回环问题)、删除单链表中重复节点及双向循环链表的设计与实现。通过编程实践,加深对链表数据结构的理解和应用能力。
217 4
|
9月前
|
存储 机器学习/深度学习 算法
C 408—《数据结构》算法题基础篇—链表(下)
408考研——《数据结构》算法题基础篇之链表(下)。
339 30
|
9月前
|
存储 算法 C语言
C 408—《数据结构》算法题基础篇—链表(上)
408考研——《数据结构》算法题基础篇之链表(上)。
441 25
|
10月前
|
机器学习/深度学习 存储 C++
【C++数据结构——线性表】单链表的基本运算(头歌实践教学平台习题)【合集】
本内容介绍了单链表的基本运算任务,涵盖线性表的基本概念、初始化、销毁、判定是否为空表、求长度、输出、求元素值、按元素值查找、插入和删除数据元素等操作。通过C++代码示例详细解释了顺序表和链表的实现方法,并提供了测试说明、通 - **任务描述**:实现单链表的基本运算。 - **相关知识**:包括线性表的概念、初始化、销毁、判断空表、求长度、输出、求元素值、查找、插入和删除等操作。 - **测试说明**:平台会对你编写的代码进行测试,提供测试输入和预期输出。 - **通关代码**:给出了完整的C++代码实现。 - **测试结果**:展示了测试通过后的预期输出结果。 开始你的任务吧,祝你成功!
488 5
|
11月前
|
数据库
数据结构中二叉树,哈希表,顺序表,链表的比较补充
二叉搜索树,哈希表,顺序表,链表的特点的比较
数据结构中二叉树,哈希表,顺序表,链表的比较补充
|
12月前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
351 5
|
存储 C语言
【数据结构】手把手教你单链表(c语言)(附源码)
本文介绍了单链表的基本概念、结构定义及其实现方法。单链表是一种内存地址不连续但逻辑顺序连续的数据结构,每个节点包含数据域和指针域。文章详细讲解了单链表的常见操作,如头插、尾插、头删、尾删、查找、指定位置插入和删除等,并提供了完整的C语言代码示例。通过学习单链表,可以更好地理解数据结构的底层逻辑,提高编程能力。
1133 4
|
算法 安全 搜索推荐
2024重生之回溯数据结构与算法系列学习之单双链表精题详解(9)【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构王道第2.3章之IKUN和I原达人之数据结构与算法系列学习x单双链表精题详解、数据结构、C++、排序算法、java、动态规划你个小黑子;这都学不会;能不能不要给我家鸽鸽丢脸啊~除了会黑我家鸽鸽还会干嘛?!!!
|
存储 Web App开发 算法
2024重生之回溯数据结构与算法系列学习之单双链表【无论是王道考研人还是IKUN都能包会的;不然别给我家鸽鸽丢脸好嘛?】
数据结构之单双链表按位、值查找;[前后]插入;删除指定节点;求表长、静态链表等代码及具体思路详解步骤;举例说明、注意点及常见报错问题所对应的解决方法
|
存储 Java
数据结构第三篇【链表的相关知识点一及在线OJ习题】
数据结构第三篇【链表的相关知识点一及在线OJ习题】
133 7

热门文章

最新文章