数据结构—单链表的概述与应用、顺序表与链表的比较(上)

简介: 数据结构—单链表的概述与应用、顺序表与链表的比较

第二章:线性

(一) 单链表

       1.定义

采用链式存储方式存储的线性表称为链表。


            链表中每一个结点包含存放数据元素值的数据域和存放逻辑上相邻节点的指针域。


                  数据域 data:用于存放数据元素的值。


                  指针域 next:用于存放后继结点的地址。

56dd66c51fe84344a837c58ea48c6061.png

2.术语

            非空表

fe9229758c96405599f44daec52acdc6.png

空表:若线性表为空,则头结点的指针域为空

a0b0f80a89124311ada984d624a43e23.png

3. 结点类的描述

             单链表是由若干个结点连接而成。

             数据域 data:用于存放数据元素的值。

             指针域 next:用于存放后继结点的地址

package data.linear_table.linked_list;
//单链表:结点类
public class Node {
    public Object data ;  //存放结点值 : 数据域
    public Node next ;    // 后继结点的引用 : 指针域
    //无参时的构造函数
    public Node() {
        this(null,null);
    }
    //带一个参数时的构造函数
    public Node(Object data) {
        this(data,null);
    }
    //带两个参数时的构造函数
    public Node(Object data , Node next) {
        this.data = data ;
        this.next = next ;
    }
}

4. 单链表类的描述

               只需一个头指针就能唯一标识它。所以单链表类只需设置一个头指针即可。

package data.linear_table.linked_list;
import data.linear_table.IList;
//单链表类
//实现线性表IList接口
public class LinkList  implements IList {
    public Node head ;     //单链表的头指针
    //单链表的构造函数
    public LinkList(){
        //初始化头结点
        head = new Node();
    }
    //构造一个长度为n的单链表
    public LinkList(int n ,boolean order) {
        //初始化结点
        this();
        if (order){
            //用尾插法顺序建立单链表
            create1(n);
        }else {
            //用头插法逆位序建立单链表
            create2(n);
        }
    }
    //用尾插法顺序建立单链表,其中n为单链表中的结点个数
    public void create1(int n ) {
        //{...}
    }
    //用头插法逆位序建立单链表,其中n为单链表中的结点个数
    public void create2(int n ) {
        //{...}
    }
    //设为空链表
    @Override
    public void clear() {
        head.data = null ;
        head.next = null ;
    }
    //判断带头结点的单链表是否为空
    @Override
    public boolean isEmpty() {
        return head.next == null ;
    }
    //输出单链表中所有的结点
    @Override
    public void display() {
        //取出带头结点的单链表中的首结点
        Node node = head.next;
        while (node != null) {
            //输出结点的值
            System.out.println(node.data+" ");
            //取出下一个结点
            node = node.next ;
        }
        //换行
        System.out.println();
    }
    //求带头结点的单链表长度
    @Override
    public int length() {
        //{...}
        return 0 ;
    }
    @Override
    public Object get(int i) throws Exception {
        //{...}
        return null;
    }
    @Override
    public void insert(int i, Object x) throws Exception {
        //{...}
    }
    @Override
    public void remove(int i) throws Exception {
        //{...}
    }
    @Override
    public int indexOf(Object x) {
        //{...}
        return 0;
    }
}

注:单链表类中的“//{...}”中的代码将在后续的具体操作中实现


-----------------------------------------------------------------------------------------------


    5. 单链表的具体操作实现

               单链表的长度

public int length() {
    Node p = head.next;         // 获得第一个结点
    int length = 0;           // 定义一个变量记录长度
    while(p != null) {
        length ++;            //计数
        p = p.next;           //p指向下一个结点
    }
    return length;
}

单链表的查找操作

                               按位序号查找操作:i 结点的限制长度为[0,n-1]之间(n为单链表的当前长度)

public Object get(int i) {
    Node p = head.next;       //初始化,p指向头结点
    int j = 0;            //计数器
//链表没有下一个 && 处理有效部分【从首结点开始向后查找,直到p指向第i个结点】
    while(p != null && j < i) {   
        //指向后继结点
        p = p.next;
        j++;           
    }
    //i 小于0 或者i 大于表长度减一时,即不合法
    //解析 j > i :  j = 0 时—— j > i 即 0 > i 即 i < 0 
   //当j遍历累加到 i-1 有效部分之后,即 j < i 即要使位置不合法,则需 j > i 
    if(j > i || p == null) {
        throw new Exception("元素不存在");
    }
    return p.data;          //返回数据
}

按值查找索引号: 根据结点的数据域值进行对比查找,返回下标索引

//查询给定值的索引号,如果没有返回-1
public int indexOf(Object x) {
    Node p = head.next;     // 初始化,p指向头结点
    int j = 0;          // 不匹配元素的个数,计数器
    while(p != null && !p.data.equals(x)) { // 循环依次找【不匹配】元素
        p = p.next;
        j++;
    }
    // 返回匹配索引,如果没有返回-1
    if(p != null) {
        return j;
    } else {
        return -1;
    }
}

单链表的插入操作

                       需求:向索引i前面插入一个新结点s

                       思路:获得i的前驱,结点P

—— 带头结点的单链表上插入操作算法

9ceab938e68f47b3a3a0874ceffa5033.png

    @Override
    //在带头结点的单链表的第i个结点之前插入一个数据域值为x的新结点
    public void insert(int i, Object x) throws Exception {
        //获得i前驱,结点p
        Node p = head ;  // 从头结点head开始移动
        int count = -1 ;  // 使用 -1 表达头结点的索引
        //请用 i= -1/0/1 去测试
        //循环条件: 结点不为null , 并且 计数长度小于 i-1前驱的下标。
        while (p != null && count < i -1 ) {
            p = p.next ;
            count++ ;
        }
        //判断位置是否不合理
        if (count < 0 || p == null ){
            throw new Exception("位置非法");
        }
        //创建一个结点,存入数据x
        Node s = new Node(x);
        //插入位置为表的中间或表尾时
        s.next = p.next ;
        p.next = s ;
    }

注意:指向结点的顺序不可变


//创建一个结点,存入数据x

       Node s = new Node(x);

       //插入位置为表的中间或表尾时

       s.next = p.next ;                            // 第1步

       p.next = s ;                                   // 第2 步


两步的顺序不可调换


原因:


       如若调换位置:如下图

e57b802732e744acaa42415742f4c1a7.png

若位置顺序不变:如下图

78c94307788d4b8a99059ccb73b8aaed.png


—— 不带头结点的单链表上插入操作算法:考虑到在表aca9e728e0c64b2aab69b4f10f08b793.png

@Override
    //在不带头结点的单链表的第i个结点之前插入一个数据域值为x的新结点
    public void insert(int i, Object x) throws Exception {
        //获得i前驱,结点p
        Node p = head ;  // 从头结点head开始移动
        int count = -1 ;  // 使用 -1 表达头结点的索引
        //请用 i= -1/0/1 去测试
        //循环条件: 结点不为null , 并且 计数长度小于 i-1前驱的下标。
        while (p != null && count < i -1 ) {
            p = p.next ;
            count++ ;
        }
        //判断位置是否不合理
        if (count > i || p == null ){
            throw new Exception("位置非法");
        }
        //创建一个结点,存入数据x
        Node s = new Node(x);
        //判断: 插入位置为表头时
        if( i == 0 ){
            s.next = head ;
            head = s ;
        }else {
            //插入位置为表的中间或表尾时
            s.next = p.next ;
            p.next = s ;
        }
    }

头插入节点的情况


相关文章
|
3天前
|
算法 安全 测试技术
golang 栈数据结构的实现和应用
本文详细介绍了“栈”这一数据结构的特点,并用Golang实现栈。栈是一种FILO(First In Last Out,即先进后出或后进先出)的数据结构。文章展示了如何用slice和链表来实现栈,并通过golang benchmark测试了二者的性能差异。此外,还提供了几个使用栈结构解决的实际算法问题示例,如有效的括号匹配等。
golang 栈数据结构的实现和应用
|
6天前
|
Java
java数据结构,双向链表的实现
文章介绍了双向链表的实现,包括数据结构定义、插入和删除操作的代码实现,以及双向链表的其他操作方法,并提供了完整的Java代码实现。
java数据结构,双向链表的实现
|
6天前
|
存储 Java
java数据结构,线性表链式存储(单链表)的实现
文章讲解了单链表的基本概念和Java实现,包括头指针、尾节点和节点结构。提供了实现代码,包括数据结构、接口定义和具体实现类。通过测试代码演示了单链表的基本操作,如添加、删除、更新和查找元素,并总结了操作的时间复杂度。
java数据结构,线性表链式存储(单链表)的实现
|
16天前
|
存储 Java
【数据结构】优先级队列(堆)从实现到应用详解
本文介绍了优先级队列的概念及其底层数据结构——堆。优先级队列根据元素的优先级而非插入顺序进行出队操作。JDK1.8中的`PriorityQueue`使用堆实现,堆分为大根堆和小根堆。大根堆中每个节点的值都不小于其子节点的值,小根堆则相反。文章详细讲解了如何通过数组模拟实现堆,并提供了创建、插入、删除以及获取堆顶元素的具体步骤。此外,还介绍了堆排序及解决Top K问题的应用,并展示了Java中`PriorityQueue`的基本用法和注意事项。
22 5
【数据结构】优先级队列(堆)从实现到应用详解
|
27天前
|
存储 机器学习/深度学习
【数据结构】二叉树全攻略,从实现到应用详解
本文介绍了树形结构及其重要类型——二叉树。树由若干节点组成,具有层次关系。二叉树每个节点最多有两个子树,分为左子树和右子树。文中详细描述了二叉树的不同类型,如完全二叉树、满二叉树、平衡二叉树及搜索二叉树,并阐述了二叉树的基本性质与存储方式。此外,还介绍了二叉树的实现方法,包括节点定义、遍历方式(前序、中序、后序、层序遍历),并提供了多个示例代码,帮助理解二叉树的基本操作。
42 13
【数据结构】二叉树全攻略,从实现到应用详解
|
28天前
|
存储 Java 索引
【数据结构】链表从实现到应用,保姆级攻略
本文详细介绍了链表这一重要数据结构。链表与数组不同,其元素在内存中非连续分布,通过指针连接。Java中链表常用于需动态添加或删除元素的场景。文章首先解释了单向链表的基本概念,包括节点定义及各种操作如插入、删除等的实现方法。随后介绍了双向链表,说明了其拥有前后两个指针的特点,并展示了相关操作的代码实现。最后,对比了ArrayList与LinkedList的不同之处,包括它们底层实现、时间复杂度以及适用场景等方面。
42 10
【数据结构】链表从实现到应用,保姆级攻略
|
4天前
|
存储 数据安全/隐私保护 Python
Python常用数据结构——字典的应用
Python常用数据结构——字典的应用
|
6天前
|
JSON 前端开发 JavaScript
一文了解树在前端中的应用,掌握数据结构中树的生命线
该文章详细介绍了树这一数据结构在前端开发中的应用,包括树的基本概念、遍历方法(如深度优先遍历、广度优先遍历)以及二叉树的先序、中序、后序遍历,并通过实例代码展示了如何在JavaScript中实现这些遍历算法。此外,文章还探讨了树结构在处理JSON数据时的应用场景。
一文了解树在前端中的应用,掌握数据结构中树的生命线
|
24天前
|
存储 算法 C语言
数据结构基础详解(C语言):单链表_定义_初始化_插入_删除_查找_建立操作_纯c语言代码注释讲解
本文详细介绍了单链表的理论知识,涵盖单链表的定义、优点与缺点,并通过示例代码讲解了单链表的初始化、插入、删除、查找等核心操作。文中还具体分析了按位序插入、指定节点前后插入、按位序删除及按值查找等算法实现,并提供了尾插法和头插法建立单链表的方法,帮助读者深入理解单链表的基本原理与应用技巧。
|
24天前
|
存储 C语言
数据结构基础详解(C语言): 树与二叉树的应用_哈夫曼树与哈夫曼曼编码_并查集_二叉排序树_平衡二叉树
本文详细介绍了树与二叉树的应用,涵盖哈夫曼树与哈夫曼编码、并查集以及二叉排序树等内容。首先讲解了哈夫曼树的构造方法及其在数据压缩中的应用;接着介绍了并查集的基本概念、存储结构及优化方法;随后探讨了二叉排序树的定义、查找、插入和删除操作;最后阐述了平衡二叉树的概念及其在保证树平衡状态下的插入和删除操作。通过本文,读者可以全面了解树与二叉树在实际问题中的应用技巧和优化策略。