线性表(1)普通线性表
来源:互联网 发布:c语言代码指令大全 编辑:程序博客网 时间:2024/06/15 02:34
线性表
线性表(List):零个或多个数据元素的有限序列
首先明确几个概念
1. 线性表是一个序列,元素之间是有顺序的
2. 若元素存在多个,第一个元素无前驱,最后一个元素无后继,其他每个元素有且只有一个前驱和后继
3. 线性表是强调有限的
如上图所示一个线性表,将线性表记为(a1,a2,a3…an)则ai+1领先于ai,ai+1成为ai的直接前驱元素,ai-1称为ai的直接后驱元素。
普通线性表
普通线性表内部封装了一个数组对象用来接收线性表的值。整体特性和数组类似,是比较简单且容易理解的一种数据结构。
模拟实现普通线性表
我们来自己模拟实现一个线性表的类,首先要明确该类的属性和方法
分析如下:
1. 增删改查必不可少
2. 表的属性,总容量,当前大小,最大容量
1. 搭建框架
先把准备实现的功能大概搭建起来
/** * 带有泛型,表示该线性表接受不同类型的对象 */public class MyList <T> { private static final int MAXLENGTH = 100;//最大列表长度 private static final int DEFAULT = 10; private int size = 0;//当前列表长度初始值 private Object[] data ;//存储list数据的数组 private Object[] emptyObj = {};//空表,clear的时候使用 /** * 默认构造函数 */ public MyList(){ initList(); } /** * 指定容量的构造函数 */ public MyList(int capacity){ ensure(capacity); } /** * 初始化操作,建立默认大小为10的列表 */ private void initList(){ data = new Object[DEFAULT]; } /** * 数组扩容 * @param capacity */ private void ensure(int capacity){ } /** * 清空 */ void clear(){ data = emptyObj;//data引用指向空列表 } /** * 判断list是否为空,true表示空否则false * @return */ boolean isEmpty(){ return this.size == 0; } /** * 添加新元素到list * @param t */ void add(T t){ } /** * 返回指定索引位置的元素 * @param idx * @return */ T get(int idx){ return null; } /** * 判断list中是否包含 t 元素 * @param t * @return */ boolean contain(T t){ return false; } /** * 向 list 指定 idx 位置插入 t 元素 * @param indx * @param t * @return */ boolean insert(int idx,T t){ return null; } /** * 返回list的长度 * @return */ int length(){ return this.size; } /** * A∪B操作,把旧的list没有的lst元素插入到list中,返回组合之后的新list * @param lst * @return */ MyList<T> union(MyList<T> lst){ return this; }}
框架的搭建参考了java.util.List接口的一些方法,总之先把需要或者说准备实现的功能先写个框框出来,然后再一个一个的分析并实现。
1. insert():添加新元素
增加新元素这时最基本的功能了,我们先来实现这个方法:
首先的分析一下,我们最终是要向MyArrayList中添加的值是放到该类的private Object[] data数组中去的,所以增加元素的时候就像向数组增加新元素一样了。
/** * 添加新元素到list * @param t */ void add(T t){ //判断是否越界 if(this.size > MAXLENGTH){ throw new RuntimeException("超过界限"); } //当前size大于data数组长度的时候 if(this.size > data.length-1){ ensure(this.size+1);//数组扩容 } data[this.size++]=t;//数组中插入新元素的值 }
注意该方法里面用到了ensure(int capacity);
方法,当数组容量不够的时候,用来动态增加数组的容量,而其他的就向在数组里面插入新数据一样简单明了了。
2. ensure():数组扩容
当当前线性表的容量不够的时候我们就要给内部数组扩容,用来接收更多的值,就向上一个insert方法中用到的情况一样。
首先分析一下:
因为是内部数组扩容,其实原理是简单粗暴的,就是把新创建一个需要的大小的数组容量,然后把旧的数组的值一个一个赋值给新数组就行了,说白了就是两个数组的拷贝,把小容量数组内容拷贝到大容量手中,然后让类内部的数组引用指向新创建的数组,达到扩容的效果。明白了原理实现起来就简单了。
/** * 数组扩容 * @param capacity */ private void ensure(int capacity){ Object[] oldData = data;//旧的数组 int oldLen = data.length;//旧数组的长度 data = new Object[capacity];//创建新的数组 //遍历旧的数组,赋值给新数组 for(int i=0;i<oldLen;i++){ data[i] = oldData[i]; } }
3. T get(int idx):返回指定索引位置的值
返回指定索引位置的数值,这个是比较简单而且容易理解的,本质就是返回内部数组的指定索引出的数值,相当于arr[idx],
需要注意的是当idx索引位置不合法的情况要主动抛出异常。
/** * 返回指定索引位置的元素 * @param idx * @return */ T get(int idx){ //先判断 if(idx > data.length || idx < 0){ throw new RuntimeException("idx越界或者小于零!"); } Object obj = data[idx]; return (T)obj; }
4. boolean contain(T t):检测是否包含指定对象
本质就是,遍历整个数组,一个一个比较看里面是否有对应的t,如果有就返回true否则返回false。
/** * 判断list中是否包含 t 元素 * @param t * @return */ boolean contain(T t){ for(int i=0;i<this.size;i++){ if(data[i] == t){ return true; } } return false; }
5. boolean insert(int idx,T t):指定位置出插入数值
这里要注意一个问题:
1. 插入元素,数组的长度肯定会比之前的长度增加一,首先要扩容。
2. 当idx在最后一个索引位置(数组的末尾)的时候直接扩容,插入即可。
3. 否则就要把插入位置之后的所有元素向后移动一位
/** * 向 list 指定 idx 位置插入 t 元素 * @param indx * @param t * @return */ boolean insert(int idx,T t){ //判断是否合法 if( idx < 0 || idx > data.length || idx == this.MAXLENGTH ){ throw new RuntimeException("idx越界或者小于零!"); } ensure(this.size+1);//数组扩大容量 //如果在末尾 if(idx == this.size){ data[idx] = t; this.size++; return true; } //把要插入元素之后的所有元素向后移动一位 for(int i=this.size-1;i >= idx;i--){ data[i+1] = data[i];//每一个元素向后移动一位 } this.size++; data[idx] = t; return true; }
6.T remove(int idx):删除指定idx位置并返回该位置的元素
首先分析:
1. 如果idx在数组末尾那么直接删除返回就可以了
2. 否则要把删除的值先赋给中间变量,然后idx之后的元素向前移动一位
/** * 删除指定idx位置并返回该位置的元素 * @param indx * @return */ T remove(int idx){ T t = null; //判断是否合法 if( idx < 0 || idx > data.length || idx == this.MAXLENGTH ){ throw new RuntimeException("idx越界或者小于零!"); } //返回指定索引位置的值 t = (T) data[idx]; //如果在末尾 if(idx == this.size){ this.size--; } //不在末尾,把idx元素向前移动 for(int i=idx;i<this.size;i++){ data[i] = data[i+1]; } return t; }
7. MyList union(MyList lst):对两个表U操作,返回一个新的表
分析:
也就是创建一个新的数组,长度为两个表的当前容量,然后把表1和表2的值一个一个赋值给新表,最后返回新表
/** * A∪B操作,把旧的list没有的lst元素插入到list中,返回组合之后的新list * @param lst * @return */ MyList<T> union(MyList<T> lst){ int len_o = this.length();//旧list的长度 int len_n = lst.length();//新的list长度 T t;//接收旧的list中没有的元素 for(int i=0;i<len_n;i++){ t = lst.get(i);//得到新lst中的元素 if( !this.contain(t) ){//如果旧的list中没有,就在list尾部添加 this.insert(len_o++,t); } } return this; }
测试
我们把一个list线性表的基本操作方法完成,下面进行测试,看是否正确?
public static void main(String[] args) { MyList<String>list = new MyList<String>(); list.add("A"); list.add("B"); list.add("C"); System.out.println(list.length());//3 System.out.println(list.get(0));//A System.out.println(list.insert(0, "AA"));//true System.out.println(list.length());//4 for(int i=0;i<90;i++){ list.add("new"+i); } System.out.println(list.length()); }
输出:
3Atrue494
与预期符合。
总结
完成了类的创建,运行一切良好,但是我们发现这个程序是有缺陷的,在数组扩容unsure
的之后,每次都要一次一次的移动元素,该方法的时间复杂度为
O(n),在进行insert
操作的时候不仅要扩容还要再次遍历数组把数组的值向后移动一位,时间复杂度是O(n),两个O(n)在小数据量的处理的时候察觉不到
,但是当数据量比较大的时候就会花费很长时间了。链表轻松了解决该问题。下一篇博客详细叙述。
- 线性表(1)普通线性表
- C++_008_数据结构_线性表_普通线性表
- 线性表--线性存储
- 线性表 线性结构
- 线性表1
- 线性表(1)
- 数据结构线性表1
- 线性表练习题1
- 线性表练习题1
- 线性表-1
- 数据结构--1、线性表
- 1、线性表
- cheet 1线性表
- 线性表(1)
- 线性表1
- 数据结构1--线性表
- 数据结构(1):线性表
- 线性表(1)
- 最大化(矩阵前缀和)
- 获取验证码,倒数5秒后才能再次获取
- Excel重要公式
- 高通AR增强现实最新教程unity3d遇见的问题和解决办法
- IIC设备驱动程序(十)————IIC总线驱动实现实例
- 线性表(1)普通线性表
- Java线程池的使用总结
- 自定义View
- python将图片转换为字符画
- 查询规划(使用EXPLAIN)
- maven copy jar包到指定lib目录
- Spring中log4j的使用
- 钓鱼比赛(百度笔试)
- 合并两个排序的链表