最基础的动态数据结构——链表

定义

链表是一种物理存储单元上非连续、非顺序的存储结构数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点(链表中每一个元素称为结点)组成,结点可以在运行时动态生成。每个结点包括两个部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域。 相比于线性表顺序结构,操作复杂。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号的节点则需要O(n)的时间,而线性表和顺序表相应的时间复杂度分别是O(logn)和O(1)。

使用链表结构可以克服数组链表需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。但是链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大。链表最明显的好处就是,常规数组排列关联项目的方式可能不同于这些数据项目在记忆体磁盘上顺序,数据的存取往往要在不同的排列顺序中转换。链表允许插入和移除表上任意位置上的节点,但是不允许随机存取。链表有很多种不同的类型:单向链表双向链表以及循环链表。链表可以在多种编程语言中实现。像Lisp和Scheme这样的语言的内建数据类型中就包含了链表的存取和操作。程序语言或面向对象语言,如C,C++和Java依靠易变工具来生成链表。

代码实现

package com.company;

public class LinkedList<E> {

    /**
     * 内部类
     */
    private class Node {

        public E e;

        public Node next;

        public Node() {
            this(null, null);
        }

        public Node(E e) {
            this(e, null);
        }

        public Node(E e, Node next) {
            this.e = e;
            this.next = next;
        }

        @Override
        public String toString() {
            return e.toString();
        }
    }

    // 虚拟头结点
    private Node dummyHead;
    // 节点数量
    private int size;

    // 构造
    public LinkedList() {
        dummyHead = new Node(null, null);
        size = 0;
    }

    // 获取节点数量
    public int getSize() {
        return size;
    }

    // 是否为空
    public boolean isEmpty() {
        return size == 0;
    }

    // 头插法添加节点
    public void addFirst(E e) {
        add(0, e);
    }

    // 插入节点
    public void add(int index, E e) {
        if (index < 0 || index > size) {
            throw new IllegalArgumentException("Add failed.Ill index.");
        }

        Node prev = dummyHead;
        for (int i = 0; i < index; i++) {
            prev = prev.next;
        }
        prev.next = new Node(e, prev.next);
        size++;
    }

    // 尾插法添加节点
    public void addLast(E e) {
        add(size, e);
    }

    // 查看头结点的元素
    public E getFirst() {
        return get(0);
    }

    // 查看节点元素
    public E get(int index) {
        if (index < 0 || index > size) {
            throw new IllegalArgumentException("Add failed.Ill index.");
        }
        Node cur = dummyHead;
        for (int i = 0; i < index; i++) {
            cur = cur.next;
        }
        return cur.e;
    }

    // 查看尾节点的元素
    public E getLast() {
        return get(size - 1);
    }

    // 修改节点的元素
    public void set(int index, E e) {
        if (index < 0 || index > size) {
            throw new IllegalArgumentException("Add failed.Ill index.");
        }
        Node cur = dummyHead;
        for (int i = 0; i < index; i++) {
            cur = cur.next;
        }
        cur.e = e;
    }

    // 删除头结点
    public E removeFirst() {
        return remove(0);
    }

    // 删除节点
    public E remove(int index) {
        if (index < 0 || index > size) {
            throw new IllegalArgumentException("Remove failed.Ill index.");
        }
        Node prev = dummyHead;
        for (int i = 0; i < index; i++) {
            prev = prev.next;
        }
        Node delNode = prev.next;
        prev.next = delNode.next;
        E ret = delNode.e;
        delNode = null;
        size--;
        return ret;
    }

    // 删除尾节点
    public E removeLast() {
        return remove(size - 1);
    }

    // 是否存在元素
    public boolean contains(E e) {
        for (Node cur = dummyHead.next; cur != null; cur = cur.next) {
            if (cur.e.equals(e)) {
                return true;
            }
            cur = cur.next;
        }
        return false;
    }

    @Override
    public String toString() {
        StringBuilder stringBuilder = new StringBuilder();
        Node cur = dummyHead.next;
        while (cur != null) {
            stringBuilder.append(cur + "->");
            cur = cur.next;
        }
        stringBuilder.append("NULL");
        return stringBuilder.toString();
    }

}

移除链表元素

public class Solution {
    public ListNode removeElements(ListNode head, int val) {
        while (head != null && head.val == val) {
            head = head.next;
        }
        if (head == null) {
            return null;
        }
        ListNode listNode = head;
        while (listNode.next != null) {
            if (listNode.next.val == val) {
                listNode.next = listNode.next.next;
            } else {
                listNode = listNode.next;
            }
        }
        return head;
    }

反转链表

public class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode prev = null;
        ListNode cur = head;
        while (cur != null) {
            ListNode next = cur.next;
            cur.next = prev;
            prev = cur;
            cur = next;
        }
        return prev;
    }
}

删除链表中的节点

class Solution {
    public void deleteNode(ListNode node) {
        node.val = node.next.val;
        node.next = node.next.next;
    }
}

原文地址:http://blog.51cto.com/13559120/2350904

时间: 2024-10-11 19:39:52

最基础的动态数据结构——链表的相关文章

最基础的动态数据结构:链表

什么是链表 链表是一种线性结构,也是最基础的动态数据结构.我们在实现动态数组.栈以及队列时,底层都是依托的静态数组,靠resize来解决固定容量的问题,而链表是真正的动态数据结构.学习链表这种数据结构,能够更深入的理解引用(或者指针)以及递归.其中链表分为单链链表和双链链表,本文中所介绍的是单链链表. 链表中的数据是存储在一个个的节点中,如下这是一个最基本的节点结构: class Node { E e; Node next; // 节点中持有下一个节点的引用 } 我们可以将链表想象成火车,每一节

基础数据结构 链表、栈、队列

数据结构是程序设计中一个非常重要的部分,基本的数据结构包括链表.栈和队列,当然高级一点的还有树.图等,实际上链表.栈和队列都是线性表,只是在操作和表示方式上有所不同,线性表用顺序结构表示就是顺序表,用链结构表示就是链表,如果对线性表的操作加以限制,只能有在表尾进行插入和删除元素,这就变成栈了,如果只能允许元素从表尾插入,表头删除,这就变成队列了. 链表 /* * 数据结构 链表 * 链式结构 */ #include <iostream> using namespace std; enum St

java的动态数据结构和泛型

动态数据结构和泛型 0 详细介绍java中的数据结构 1 1 List 5 1.1 ArrayList 5 2 Set 6 2.1 HashSet与TreeSet的区别 6 3 Map 8 4 迭代器 9 5 泛型 9 0 详细介绍java中的数据结构 也许你已经熟练使用了java.util包里面的各种数据结构,但是我还是要说一说java版数据结构与算法,希望对你有帮助. 线性表,链表,哈希表是常用的数据结构,在进行Java开发时,JDK已经为我们提供了一系列相应的类来实现基本的数据结构.这些类

C++ 动态数据结构(一)

1.我们为什么要用动态数据数据结构呢? 因为类型相同的数据用数组存储存在许多的问题: (1)定义静态数组时必须指定数组的元素个数,此后无法更改数组大小,带来很多的不便,可能造成空间浪费或不足. (2)用指针可以申请动态数组,空间不会浪费或不足,由于动态申请的空间必须是连续的区域,所以当申请"大片"的连续区域时,有可能会失败. (3)在数组中插入或删除元素时需要大量移动元素,效率低. 所以动态数据结构就出现了! 态数据结构: (1)可以利用动态内存分配,实现动态数组,克服数组大小固定的问

动态单链表的传统存储方式和10种常见操作-C语言实现

顺序线性表的优点:方便存取(随机的),特点是物理位置和逻辑为主都是连续的(相邻).但是也有不足,比如:前面的插入和删除算法,需要移动大量元素,浪费时间,那么链式线性表 (简称链表) 就能解决这个问题. 一般链表的存储方法 一组物理位置任意的存储单元来存放线性表的数据元素,当然物理位置可以连续,也可以不连续,或者离散的分配到内存中的任意位置上都是可以的.故链表的逻辑顺序和物理顺序不一定一样. 因为,链表的逻辑关系和物理关系没有必然联系,那么表示数据元素之间的逻辑映象就要使用指针,每一个存储数据元素

C语言:创建动态单向链表,创建完成后,输出每一个节点的数据信息。

// //  main.c //  dynamic_link_list // //  Created by ma c on 15/8/5. //  Copyright (c) 2015年 bjsxt. All rights reserved. //  要求:写一个函数建立有3名学生数据的动态单向链表,并输出链表中每个结点的所有内容. /* 建立动态链表的思想: 1.开辟一个新结点,并使p1,p2指向它: 2.读入一个学生数据给p1所指的结点: 3.head = NULL,n = 0; 4.判断读

静态单链表和动态单链表的区别

链表中结点的分配和回收是由系统提供的标准函数malloc和free动态实现的,称之为动态链表. 如果程序支持指针,则可按照我们的一般形式实现链表, 需要时分配,不需要时回收即可. 动态链表的空间是可以动态扩展的. typedef struct  node{ EleType data; struct node * pNext; }Node; 有些高级语言中没有"指针"数据类型,只能用数组来模拟线性链表的结构, 数组元素中的指针"域"存放的不是元素在内存中的真实地址,而

数据结构链表学习

今天初步学习数据结构链表,学习过程中感觉对于指针的理解还差很多,而且对于VS的调试也不会使用,调查问题只能靠一遍一遍的梳理逻辑,效率不是一般的低下..接下来得赶紧学习下VS的使用.. 今天链表只是初步学习,写的例子也比较简单,如下: 定义链表的数据结构,只简单的定义了一个数据和一个指向后继的指针 1 struct List { 2 int num; 3 List *next; 4 }; 接下来就是链表的创建,返回一个指针地址赋给主函数中的头指针,用于之后的增删改查等等 1 List *creat

数据结构——链表的封装

链表是数据结构中最基本的线性结构,在编程中使用的频率最高,那么如何用封装思想来编写一个完整的链表操作呢?且看以下代码的实例. /*功能:演示链表的操作方法 */              /*作者: james */              /*时间: 2014.7.16 */              /*版本: v1.0 */              /*说明:使用面向对象思想封装一个链表*/ #include <stdio.h>              #include <s