首页 > 其他分享 >线性表:顺序表的实现以及遍历扩容

线性表:顺序表的实现以及遍历扩容

时间:2024-09-02 17:52:57浏览次数:7  
标签:sequenceLength arr 遍历 线性表 int 元素 顺序 public


Java学习手册+面试指南:https://javaxiaobear.cn

顺序表是在计算机内存中以数组的形式保存的线性表,线性表的顺序存储是指用一组地址连续的存储单元,依次存储线性表中的各个元素、使得线性表中再逻辑结构上响铃的数据元素存储在相邻的物理存储单元中,即通过数据元素物理存储的相邻关系来反映数据元素之间逻辑上的相邻关系。

1、顺序表的实现

1、API设计

类名

SequenceCase

构造方法

SequenceCase(int capacity):创建容量为capacity的SequenceList对象

成员方法

public void clear():空置线性表

public boolean isEmpty():判断线性表是否为空,是返回true,否返回false

public int length():获取线性表中元素的个数

public T get(int i):读取并返回线性表中的第i个元素的值

public void insert(int i,T t):在线性表的第i个元素之前插入一个值为t的数据元素。

public void insert(T t):向线性表中添加一个元素t

public T remove(int i):删除并返回线性表中第i个数据元素。

public int indexOf(T t):返回线性表中首次出现的指定的数据元素的位序号,若不存在,则返回-1。

成员变量

private T[] arr:存储元素的数组

private int N:当前线性表的长度

2、代码实现
public class SequenceCase<T> {

    /**
     * 存储线性表
     */
    public T[] arr;

    /**
     * 线性表元素个数
     */
    public int sequenceLength;

    public SequenceCase(int capacity) {
        arr = (T[]) new Object[capacity];
        sequenceLength = 0;
    }

    /**
     * 插入元素
     * @param name
     */
    public void insert(T name){
        if(sequenceLength == arr.length){
            throw new RuntimeException("线性表已满");
        }
        arr[sequenceLength++] = name;
    }

    /**
     * 指定索引插入元素
     * @param i
     * @param name
     */
    public void insert(int i, T name){
        if (i == arr.length){
            throw new RuntimeException("当前表已满");
        }
        if (i < 0 || i > sequenceLength){
            throw new RuntimeException("插入的位置不合法");
        }
        for (int j = sequenceLength; j > i; j--) {
            arr[j] = arr[j-1];
        }
        arr[i] = name;
        sequenceLength++;
    }

    /**
     * 根据索引删除元素
     * @param i
     */
    public T remove(int i){
        if (i < 0 || i > sequenceLength-1){
            throw new RuntimeException("当前要删除的元素不存在");
        }
        T t = arr[i];
        for (int j = i; j < sequenceLength; j++) {
            arr[j] = arr[j+1];
        }
        sequenceLength--;
        return t;
    }

    /**
     * 清空整个线性表
     */
    public void clear(){
        sequenceLength = 0;
    }

    /**
     * 线性表是否为空
     * @return
     */
    public boolean isEmpty(){
        return 0 == sequenceLength;
    }

    /**
     * 获取线性表的长度
     * @return
     */
    public int getSequenceLength(){
        return sequenceLength;
    }

    /**
     * 根据索引查询线性表
     * @param i
     * @return
     */
    public T getNameByIndex(int i){
        if(0 > i || i > sequenceLength){
            throw new RuntimeException("未存在该元素");
        }
        return arr[i];
    }

    /**
     * 获取线性表首次出现的序列号
     * @param t
     * @return
     */
    public int indexOf(T t){
        for (int i = 0; i < sequenceLength; i++) {
            if (t.equals(arr[i])){
                return i;
            }
        }
        return -1;
    }
}
public class SequenceCaseTest {
    public static void main(String[] args) {
        SequenceCase<String> aCase = new SequenceCase<>(10);
        aCase.insert("yhx");
        aCase.insert("xiaobear");
        aCase.insert("lwh");
        aCase.insert("xiaohuahua");
        aCase.insert(2,"love");
        String nameByIndex = aCase.getNameByIndex(2);
        System.out.println("索引2的名字为:" + nameByIndex);
        String remove = aCase.remove(2);
        System.out.println("删除索引2的元素名称为:" + remove);
        //清空操作
        aCase.clear();
        System.out.println("线性表长度为:" + aCase.getSequenceLength());
    }
}

2、顺序表的遍历

在java中,遍历集合的方式一般都是用的是foreach循环,如果想让我们的SequenceList也能支持foreach循环,则需要做如下操作:

  • 让SequenceList实现Iterable接口,重写iterator方法;
  • 在SequenceList内部提供一个内部类SIterator,实现Iterator接口,重写hasNext方法和next方法;
public class SequenceCase<T> implements Iterable<T>{
        private class SequenceIterator implements Iterator{

        private int temp;

        //遍历从0开始
        public SequenceIterator() {
            this.temp = 0;
        }

        @Override
        public boolean hasNext() {
            return temp < sequenceLength;
        }

        @Override
        public Object next() {
            return arr[temp++];
        }
    }

    @Override
    public Iterator<T> iterator() {
        return new SequenceIterator();
    }
}

3、顺序表的扩容

在之前的实现中,当我们使用SequenceCase时,先new SequenceCase(5)创建一个对象,创建对象时就需要指定容器的大小,初始化指定大小的数组来存储元素,当我们插入元素时,如果已经插入了5个元素,还要继续插入数据,则会报错,就不能插入了。这种设计不符合容器的设计理念,因此我们在设计顺序表时,应该考虑它的容量的伸缩性。

1、添加元素

添加元素时,应该检查当前数组的大小是否能容纳新的元素,如果不能容纳,则需要创建新的容量更大的数组,我们这里创建一个是原数组两倍容量的新数组存储元素。

2、删除元素

移除元素时,应该检查当前数组的大小是否太大,比如正在用100个容量的数组存储10个元素,这样就会造成内存空间的浪费,应该创建一个容量更小的数组存储元素。如果我们发现数据元素的数量不足数组容量的1/4,则创建一个是原数组容量的1/2的新数组存储元素。

/**
     * 重置线性表大小
     * @param newSize
     */
public void resize(int newSize){
    T[] temp = arr;
    arr = (T[]) new Object[newSize];
    for (int i = 0; i < temp.length; i++) {
        arr[i] = temp[i];
    }
}

添加元素时:

/**
     * 指定索引插入元素
     * @param i
     * @param name
     */
public void insert(int i, T name){
    if (i == arr.length){
        resize(arr.length*2);
    }
    if (i < 0 || i > sequenceLength){
        throw new RuntimeException("插入的位置不合法");
    }
    for (int j = sequenceLength; j > i; j--) {
        arr[j] = arr[j-1];
    }
    arr[i] = name;
    sequenceLength++;
}

删除元素时:

/**
     * 根据索引删除元素
     * @param i
     */
public T remove(int i){
    if (i < 0 || i > sequenceLength-1){
        throw new RuntimeException("当前要删除的元素不存在");
    }
    T t = arr[i];
    for (int j = i; j < sequenceLength; j++) {
        arr[j] = arr[j+1];
    }
    sequenceLength--;
    //当线性表元素不足数组的1/4时,重置数组元素大小
    if(0 < sequenceLength && sequenceLength < arr.length/4){
        resize(arr.length/2);
    }
    return t;
}

4、顺序表的时间复杂度

  • get(i):不难看出,不论数据元素量N有多大,只需要一次eles[i]就可以获取到对应的元素,所以时间复杂度为O(1);
  • insert(int i,T t):每一次插入,都需要把i位置后面的元素移动一次,随着元素数量N的增大,移动的元素也越多,时间复杂为O(n);
  • remove(int i):每一次删除,都需要把i位置后面的元素移动一次,随着数据量N的增大,移动的元素也越多,时间复杂度为O(n);
  • 由于顺序表的底层由数组实现,数组的长度是固定的,所以在操作的过程中涉及到了容器扩容操作。这样会导致顺序表在使用过程中的时间复杂度不是线性的,在某些需要扩容的结点处,耗时会突增,尤其是元素越多,这个问题越明显


线性表:顺序表的实现以及遍历扩容_开发语言

标签:sequenceLength,arr,遍历,线性表,int,元素,顺序,public
From: https://blog.51cto.com/xiaobear/11899823

相关文章

  • 16.STL-常用遍历算法
    常用遍历算法for_each用于遍历有返回值可以绑定参数进行输出transform搬运注意:目标容器要有容量#define_CRT_SECURE_NO_WARNINGS#include<iostream>usingnamespacestd;#include<vector>#include<algorithm>#include<functional>classMyPrint{publi......
  • 【C语言】顺序表详解,灵活运用所学知识
    文章目录前言1.什么是顺序表?1.1线性表2.编写你的顺序表!2.0赛前准备2.1初始化2.2容量检查2.3打印顺序表2.4尾插和尾删2.5头插和头删2.6插入和删除2.7查找和更改3.菜单一些err总结前言顺序表是我们学习数据结构第一阶段的必经之路什么是顺序表,且听我慢慢道来本篇博客用到的......
  • 【C语言】数据结构-栈(顺序表实现)
    文章目录前言1.什么是栈2.栈的实现3.敲代码!3.1头文件3.2函数实现4.知识巩固,来道OJ!结语前言在之前的数据结构学习中,我们学习了顺序表、链表这两种结构顺序表:博客链接1单链表:博客链接2链表OJ:博客链接3除了单链表以外,还有一个结构,是双向带头循环链表。这个链表的形式如下头节点的......
  • 45. 文件的顺序读写
    1按照字符读写文件fgetc、fputc1.1.写文件#include<stdio.h>intfputc(intch,FILE*stream);功能:将ch转换为unsignedchar后写入stream指定的文件中参数:ch:需要写入文件的字符stream:文件指针返回值:成功:成功写入文件的字符失败:返回-1charbuf[]="MynameisTao.";int......
  • 二叉树的遍历
    先序遍历usingnamespacestd;//定义二叉树节点结构体structTreeNode{charData;//节点的数据TreeNode*left;//左子节点TreeNode*right;//右子节点//构造函数TreeNode(chardata,TreeNode*leftChild=nullptr,T......
  • Prop效验与Prop默认值用法及循环遍历数组
    Prop效验与使用在HBuilderX里面你把组件传过去,向之前的那样的写法是没有默认值的,写了才有值,否则为空,所以我们可以用另一种方法,写法如下虽然这样写了但是不是完全体的,我们可以给他定个默认值和类型,就像那个String一样,可以约束对象只能是这个的类型这样子另一个页面......
  • php遍历文件夹以及子目录;
    php遍历文件夹以及子目录<?phpfunctionmy_dir($folderPath){ $arr_subdictory=array(); if(@$handle=opendir($folderPath)){ while(false!==($entry=readdir($handle))){ if($entry!="."&&$entry!=".."){//排除更目录 ......
  • 代码随想录刷题day13丨二叉树理论基础,递归遍历,迭代遍历,统一迭代,层序遍历
    代码随想录刷题day13丨二叉树理论基础,递归遍历,迭代遍历,统一迭代,层序遍历1.二叉树理论基础1.1二叉树种类满二叉树概述:如果一棵二叉树只有度为0的结点和度为2的结点,并且度为0的结点在同一层上,则这棵二叉树为满二叉树。这棵二叉树为满二叉树,也可以说深度为k,有2^k-1个节......
  • 栈和队列(1)——顺序栈,链栈
    数据结构:栈和队列(1)–顺序栈,链栈提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档文章目录数据结构:栈和队列(1)--顺序栈,链栈前言一、栈和队列1.1栈和队列的定义1.2队列的定义二、栈实现操作2.1顺序栈的结构体定义2.2顺序栈的基本操作:2.3链栈的结构体定......
  • computed计算属性及方法对比和循环遍历统计以及watch和watchEect监听的用法
    1.computed计算属性及方法对比1.了解computed计算属性和用法在我们的一些应用中可以看的应用会给我们提供一些计算类的功能比如取名,它会给你提供两个输入框,然后在你给这两个输入框输入值的时候会在下方生成你输入这个两个值的结合值,就比如你先输入了一个姓氏,然后输入一个名,下......