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

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

单链表的删除操作

65ccd610dc22456591d81ffd3e7dd063.png

@Override
    public void remove(int i) throws Exception {
        //删除
        Node p = head ;  // 从头结点head开始移动
        int count = -1 ;  // 使用-1表达头结点的索引
        //循环条件: 结点不为null , 并且 计数长度小于 i-1前驱的下标。
        while (p != null && count < i -1 ) {
            p = p.next ;
            count++ ;
        }
        //判断位置是否不合理: 表长度小于0 或 所获结点为null
        if (count < 0 || p == null ){
            throw new Exception("位置非法");
        }
        //修改链指针,使待删除结点从单链表中脱离出来
        p.next = p.next.next ;
    }

6. 单链表的建立操作

               单链表是一种动态存储结构,不需要预先分配存储空间。因此,生成单链表的过程是一个结点“逐个插入”的过程。


               根据插入位置的不同,可将创建单链表的方法分两种:头插法和尾插法


                       头插法:从表尾到表头的逆向创建;插入的位置永远是表头0

f120aeefc83f4adb98d2168d283f268d.png

  //用头插法逆位序建立单链表,其中n为单链表中的结点个数
    public void create2(int n ) throws Exception {
        //构造用于输出的对象
        Scanner scanner = new Scanner(System.in);
        //逆序输入n个结点的数据域值
        for( int j = 0 ; j < n ; j++) {
            //生成新结点,插入到表头
            insert(0,scanner.next());
        }
    }

尾插法:从表头到表尾的创建;插入的位置永远是表尾【表的当前长度】length()

f6844e6271bd4733b66650bc2aa24793.png

    //用尾插法顺序建立单链表,其中n为单链表中的结点个数
    public void create1(int n ) throws Exception {
        //构造用于输出的对象
        Scanner scanner = new Scanner(System.in);
        //输入n个结点的数据值域
        for( int j = 0 ; j < n ; j++) {
            //生成新结点,插入到表尾
            insert(length(),scanner.next());
        }
    }

(二) 单链表的应用举例

       线性表接口

package data.linear_table;
//线性表接口
public interface IList {
    public void clear() ;  //清空
    public boolean isEmpty();  //判断是否为空
    public int length();   // 表的长度
    public Object get(int i) throws Exception; //获取元素的值
    public void insert(int i , Object x) throws Exception; //在指定位置,插入指定元素
    public void remove(int i ) throws Exception; //删除指定元素
    public int indexOf(Object x) ;  //查找指定元素第一次出现的位置
    public void display() ;  //输出元素的值
}

单链表的结点类

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 ;
    }
}

单链表类

package data.linear_table.linked_list;
import data.linear_table.IList;
import java.util.Scanner;
//单链表类
//实现线性表IList接口
public class LinkList  implements IList {
    public Node head ;     //单链表的头指针
    //单链表的构造函数
    public LinkList(){
        //初始化头结点
        head = new Node();
    }
    //构造一个长度为n的单链表
    public LinkList(int n ,boolean order) throws Exception {
        //初始化结点
        this();
        if (order){
            //用尾插法顺序建立单链表
            create1(n);
        }else {
            //用头插法逆位序建立单链表
            create2(n);
        }
    }
    //用尾插法顺序建立单链表,其中n为单链表中的结点个数
    public void create1(int n ) throws Exception {
        //构造用于输出的对象
        Scanner scanner = new Scanner(System.in);
        //输入n个结点的数据值域
        for( int j = 0 ; j < n ; j++) {
            //生成新结点,插入到表尾
            insert(length(),scanner.next());
        }
    }
    //用头插法逆位序建立单链表,其中n为单链表中的结点个数
    public void create2(int n ) throws Exception {
        //构造用于输出的对象
        Scanner scanner = new Scanner(System.in);
        //逆序输入n个结点的数据域值
        for( int j = 0 ; j < n ; j++) {
            //生成新结点,插入到表头
            insert(0,scanner.next());
        }
    }
    //设为空链表
    @Override
    public void clear() {
        head.data = null ;
        head.next = null ;
    }
    //判断带头结点的单链表是否为空
    @Override
    public boolean isEmpty() {
        return head.next == null ;
    }
    //求带头结点的单链表长度
    @Override
    public int length() {
        Node p = head.next;         // 获得第一个结点
        int length = 0;           // 定义一个变量记录长度
        while(p != null) {
            length ++;            //计数
            p = p.next;           //p指向下一个结点
        }
        return length;
    }
    @Override
    public Object get(int i) throws Exception {
        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;          //返回数据
    }
    @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 ;
        }
    }
    @Override
    public void remove(int i) throws Exception {
        //删除
        Node p = head ;  // 从头结点head开始移动
        int count = -1 ;  // 使用-1表达头结点的索引
        //循环条件: 结点不为null , 并且 计数长度小于 i-1前驱的下标。
        while (p != null && count < i -1 ) {
            p = p.next ;
            count++ ;
        }
        //判断位置是否不合理: 表长度小于0 或 所获结点为null
        if (count < 0 || p == null ){
            throw new Exception("位置非法");
        }
        //修改链指针,使待删除结点从单链表中脱离出来
        p.next = p.next.next ;
    }
    @Override
    //查询给定值的索引号,如果没有返回-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;
        }
    }
    //输出单链表中所有的结点
    @Override
    public void display() {
        //取出带头结点的单链表中的首结点
        Node node = head.next;
        while (node != null) {
            //输出结点的值
            System.out.println(node.data+" ");
            //取出下一个结点
            node = node.next ;
        }
        //换行
        System.out.println();
    }
}

例1:编程实现查找线性表(0,1,2,3,....., n-1)中第 i([1,n])个数据元素的直接前驱,并输出其值。并要求在单链上实现。

package data.linear_table.linked_list;
import java.util.Scanner;
public class Examp1 {
    public static void main(String[] args) throws Exception {
        //例1:编程实现查找线性表(0,1,2,3,....., n-1)中
        // 第 i([1,n])个数据元素的直接前驱,并输出其值。并要求在单链上实现。
        int n = 10 ;
        //创建一个空的单链表
        LinkList L = new LinkList();
        //将0,1,2,3.... 到 n-1 依次插入到表尾
        for(int i = 0 ; i < n ; i++) {
            //调用插入方法,在第i个位置上插入数据域值为i的数
            L.insert(i, i);
        }
        System.out.println("请输入i 的值:");
        int i = new Scanner(System.in).nextInt();
        if (i > 0 && i <= n) {
            System.out.println("第"+i+"个元素的直接前驱是:"+L.get(i-1));
        }else {
            System.out.println("第"+i+"个元素的直接前驱不存在!");
        }
    }
}

  —— 所输入i的值必须在{0,n]的范围内。第0个元素为表头,没有前驱。输出结果:

eb4c495b7a664d0aaa0ffd856d1cfc5a.png

(三)顺序表与链表的比较


        链表比较灵活,插入和删除操作效率较高,但空间利用率低,适用于实现动态的线性表;


        顺序表实现比较简单,并且空间利用率也较高,可高效的进行随机存取,但顺序表不易扩充,插入和删除操作效率较低,适合于实现相对“稳定”的静态线性表。


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


章节仅是博主阅读书籍的总结和理解,若有不对或欠妥的地方,还请各位大佬批评指正!!!


相关文章
|
1月前
|
存储 算法 Perl
数据结构实验之链表
本实验旨在掌握线性表中元素的前驱、后续概念及链表的建立、插入、删除等算法,并分析时间复杂度,理解链表特点。实验内容包括循环链表应用(约瑟夫回环问题)、删除单链表中重复节点及双向循环链表的设计与实现。通过编程实践,加深对链表数据结构的理解和应用能力。
57 4
|
1天前
|
数据库
数据结构中二叉树,哈希表,顺序表,链表的比较补充
二叉搜索树,哈希表,顺序表,链表的特点的比较
数据结构中二叉树,哈希表,顺序表,链表的比较补充
|
27天前
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
53 5
|
26天前
|
并行计算 算法 测试技术
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面
C语言因高效灵活被广泛应用于软件开发。本文探讨了优化C语言程序性能的策略,涵盖算法优化、代码结构优化、内存管理优化、编译器优化、数据结构优化、并行计算优化及性能测试与分析七个方面,旨在通过综合策略提升程序性能,满足实际需求。
57 1
|
7月前
【移除链表元素】LeetCode第203题讲解
【移除链表元素】LeetCode第203题讲解
|
6月前
|
存储 SQL 算法
LeetCode力扣第114题:多种算法实现 将二叉树展开为链表
LeetCode力扣第114题:多种算法实现 将二叉树展开为链表
|
6月前
|
存储 SQL 算法
LeetCode 题目 86:分隔链表
LeetCode 题目 86:分隔链表
|
6月前
|
存储 算法 Java
【经典算法】Leetcode 141. 环形链表(Java/C/Python3实现含注释说明,Easy)
【经典算法】Leetcode 141. 环形链表(Java/C/Python3实现含注释说明,Easy)
66 2
|
7月前
<数据结构>五道LeetCode链表题分析.环形链表,反转链表,合并链表,找中间节点.
<数据结构>五道LeetCode链表题分析.环形链表,反转链表,合并链表,找中间节点
70 1
|
6月前
|
算法
【经典LeetCode算法题目专栏分类】【第7期】快慢指针与链表
【经典LeetCode算法题目专栏分类】【第7期】快慢指针与链表