顺序表与链表

目录

1. 线性表

2. 顺序表

2.1 分类

2.2 顺序表的增与删

2.3 顺序表的扩容

3. 链表

3.1 简介

3.2 链表的增与删

4. 顺序表 battle 链表


1. 线性表

n个具有相同特性的数据元素的有限序列。

  • 线性表在逻辑上是连续的,但是在物理结构上不一定连续。

  • 常见的线性表:顺序表、链表、栈、队列、字符串...

2. 顺序表

物理结构连续的线性表,一般采用数组存储。

  • 2.1 分类

    • 静态顺序表:定长数组存储
    • 动态顺序表:动态数组存储
  • 2.2 顺序表的增与删

  • 2.2.1 思考
    • 变量
      • array:保存数据的空间
      • size:保存有效数据的个数
      • item:插入的数据
      • index:插入位置
      • 通俗的来讲,array就是房子的地址,size就是房子所能容纳的人数
    • 头插
      • 时间复杂度O(n)
      • 步骤
        1. 将size个数据依次后移
        2. 为避免数据被覆盖,先移动末尾的数据
        3. 称数据当前所在下标称为数据的下标,称数据所要移动到的下标为空间的下标
        4. 空间的下标(size)= 数据的下标(size-1)+1
        5. 数据的下标[size-1,0],空间的下标[size,1]
        6. 移动数据:array[空间]=array[数据]
        7.  令array[0]=item
    • 尾插
      • size同时表示下一个可用空间(所插入位置)的下标
      • 直接将item存储于下标为size的空间
      • 时间复杂度 O(1)
    • 插入
      • 时间复杂度 O(n)
      • 步骤(类似于头插)
        • 将size-1个数后移
        • 数据的下标[size-1,index],空间的下标[size,index+1]
    • 头删
      • 直接将下标为1及以后的数据(size-1个数据)前移
      • 避免数据被覆盖,先移动前面的数据
      • 数据的下标[1,size-1],空间的下标[0,size-2]
      • 时间复杂度 O(n)
    • 尾删
      • size-1表示要删除的数据的下标
      • 将末尾的数据置为0
      • 时间复杂度 O(1)
    • 删除(类似于头删)
      • 将size-index-1个数前移
      • 数据的下标[index+1,size-1],空间的下标[index,size-2]
      • 时间复杂度 O(n)
  • 2.2.2 代码
/**
 * 顺序表的增删
 * Author:qqy
 */
public class MyArrayList implements IArrayList {
    //ArrayList -> 底层使用数组实现
    //保存数据的空间
    private int[] array;
    //保存有效数据的个数
    private int size;

    MyArrayList(int capacity) {
        this.array = new int[capacity];
        this.size = 0;
    }

    @Override
    public void pushFront(int item) {
        for (int i = this.size; i >= 1; i--) {
            this.array[i] = array[i - 1];
        }
        this.array[0] = item;
        this.size++;
    }

    @Override
    public void pushBack(int item) {
        this.array[this.size] = item;
        this.size++;
    }


    @Override
    public void add(int item, int index) {
        if (index < 0 || index >= this.size) {
            throw new Error();
        }
        for (int i = this.size; i > index; i--) {
            this.array[i] = this.array[i - 1];
        }
        this.array[index] = item;
        this.size++;
    }

    @Override
    public void popFront() {
        if (this.size == 0) {
            throw new Error();
        }
        for (int i = 0; i < this.size - 1; i++) {
            this.array[i] = this.array[i + 1];
        }
        this.array[--this.size] = 0;
    }

    @Override
    public void popBack() {
        if (this.size != 0) {
            this.array[--this.size] = 0;
        } else {
            throw new Error();
        }
    }

    @Override
    public void remove(int index) {
        if (index < 0 || index >= this.size) {
            throw new Error();
        }
        if (this.size == 0) {
            throw new Error();
        }
        for (int i = index + 1; i < size; i++) {
            array[i - 1] = array[i];
        }
        size--;
    }
}
  • 2.2.3 时间复杂度
    • 尾插、尾删           均摊O(1),偶然扩容时变为O(n)
    • 头插、头删           O(n) 数据搬移耗时
    • 中间插入、删除    O(n) 数据搬移耗时
  • 2.3 顺序表的扩容

数组容量不够,需要扩容

  • 2.3.1 思考
    • 变量
      • this.array   房子地址
      • this.size    房子容纳人数
    • 扩容条件(搬家)
      • this,size==this.array.length 
    • 步骤
      1. 开辟新房子
      2. 搬家
      3. 登记新房子,退掉旧房子
  • 2.3.2 代码
//O(n)
private void ensureCapacity(){
        if(this.size<this.array.length){
            return;
        }
        
        //1.
        int capacity=this.array.length*2;
        int [] newArray=new int[capacity];
        //2.
        for(int i=0;i<this.size;i++){
            newArray[i]=this.array[i];
        }
        //3.
        this.array=newArray;
}

3. 链表

物理结构非连续的线性表,逻辑顺序通过链表中的引用连接次序实现。

  • 3.1 简介

    • 可以将链表看成如下结构:

      • 引用{value,next}

    • eg:@0X33{1,@0x66}、@0X66{1,@0x55}、@0X55{1,@0x77}、@0X77{1,null}
      • Node n1=//@0x33   Node n2=//@0x77
      • n1.next=n2;                  @0X33{1,@0x77}
      • n2=n1.next;                  n2=//@0x66
      • n1.next.next=n2;          @0X66{1,@0x77}
      • n1=n2;                         n1=//@0x77
    • 第一个结点没有前驱结点,代表整个链表。

    • 根据第一个结点,去寻找一个链表。如果一个结点都没有,则head=null

    • 注意:真正有用的是value,next用于维持结构。

  • 3.2 链表的增与删

  • 3.2.1 思考

    • 变量

      • item:插入的数据
      • index:插入位置
      • next:下一个结点的线索(引用)
      • head:保存链表中第一个结点的引用
      • value:保存有效数据

    • 链表中做插入/删除,都需要相应结点的前驱结点

    • 链表的第一个结点有特殊性,没有前驱结点

    • 头插

      • 将头结点赋给item的next

      • 将item赋给头结点

      • 时间复杂度O(1)

    • 尾插

      • 通过最后一个结点的next为null来找到最后一个结点

      • 将最后一个结点的next变为新的结点

      • 时间复杂度O(n)
    • 头删

      • 将第一个节点的next赋给头结点 -> 头结点的next赋给头结点

      • 时间复杂度O(1)

    • 尾删

      • 通过倒数第二个结点的next的next为null,找到倒数第二个节点

      • 将倒数第二的结点的next设为null

      • 时间复杂度O(n)

    • 插入

      • 找到指定位置的前一个结点

      • 插入

      • 时间复杂度O(n)

  • 3.2.2 代码

/**
 * 链表的增删
 * Author:qqy
 */
public class MyLinkedList {
    /**
     * 定义一个内部类,表示结点
     */
    public class Node {
        //保存有效数据
        private int value;
        //下一个结点的线索(引用)
        private Node next;

        public Node(int v) {
            this.value = v;
            this.next = null;
        }
    }

    //保存链表中第一个结点的引用,若无结点 -> head=null
    private Node head;

    MyLinkedList(){
        this.head=null;
    }

    public void pushFront(int item){
        Node node=new Node(item);
        node.next=this.head;
        this.head=node;
    }

    public void pushBack(int item){
        Node node=new Node(item);
        //链表没有结点
        if(this.head==null){
            this.head=node;
        }else {
            Node cur = this.head;
            //得到最后一个结点
            while (cur.next != null) {
                cur = cur.next;
            }
            //将node赋给最后一个结点的next
            cur.next = node;
        }
    }

    public void popFront(){
        if(this.head==null){
            throw new Error();
        }
        this.head=this.head.next;
    }

    public void popBack(){
        if(this.head==null){
            throw new Error();
        }
        //只有一个结点
        if(this.head.next==null){
            this.head=null;
        }else {
            Node cur = this.head;
            while (cur.next.next != null) {
                cur = cur.next;
            }
            cur.next = null;
        }
    }

    //中间插入
    public boolean addIndex(int index, int v) {
        Node node=new Node(v);
        if(index==0){
            node.next=this.head.next;
           this.head=node;
        }else {
            Node cur=this.head;
            for (int i = 0; cur.next!=null&& i < index - 1; i++) {
                cur = cur.next;
            }
            if(cur.next==null){
                return false;
            }
            node.next=cur.next;
            cur.next=node;
        }
        return true;
    }

    //通过循环遍历链表的每一个结点
    public void display(){
        Node cur=this.head;
        while(cur!=null){
            System.out.print(cur.value);
            if(cur.next!=null){
                System.out.print("-->");
            }
            cur=cur.next;
        }
    }
}

4. 顺序表 battle 链表

  • 顺序表
    • 可以通过索引随机访问任一结点,时间复杂度为O(1)
    • 单个数据存储,占用空间少,存储效率高
    • 不易产生内存碎片
  • 链表
    • 更好的插入、删除,头插尾插的时间复杂度都为O(1)
    • 整体来看,存储效率更高
本项目是一个基于SSM(Spring+SpringMVC+MyBatis)框架和Vue.js前端技术的大学生第二课堂系统,旨在为大学生提供一个便捷、高效的学习和实践平台。项目包含了完整的数据库设计、后端Java代码实现以及前端Vue.js页面展示,适合计算机相关专业的毕设学生和需要进行项目实战练习的Java学习者。 在功能方面,系统主要实现了以下几个模块:用户管理、课程管理、活动管理、成绩管理和通知公告。用户管理模块支持学生和教师的注册、登录及权限管理;课程管理模块允许教师上传课程资料、设置课程时间,并由学生进行选课;活动管理模块提供了活动发布、报名和签到功能,鼓励学生参与课外实践活动;成绩管理模块则用于记录和查询学生的课程成绩和活动参与情况;通知公告模块则实时发布学校或班级的最新通知和公告。 技术实现上,后端采用SSM框架进行开发,Spring负责业务逻辑层,SpringMVC处理Web请求,MyBatis进行数据库操作,确保了系统的稳定性和扩展性。前端则使用Vue.js框架,结合Axios进行数据请求,实现了前后端分离,提升了用户体验和开发效率。 该项目不仅提供了完整的源代码和相关文档,还包括了详细的数据库设计文档和项目部署指南,为学习和实践提供了便利。对于基础较好的学习者,可以根据自己的需求在此基础上进行功能扩展和优化,进一步提升自己的技术水平和项目实战能力。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值