数据结构 —— 线性表——顺序表和单链表的增删改查(java实现版)
前言:最近在复习数据结构,进行分章总结复习到的知识点,当作笔记以加深记忆。同时以java语言进行具体的操作实现(eclipse实现),需要的同学可以适当作为参考,有不正确的地方欢迎指正。
#一:线性表的定义
什么是线性表呢?首先我们得明白,线性表是一种用来存储数据的线性结构!
一般我们这样来定义线性表:线性表是n(n>=0)个数据特性相同的元素的组成有限序列。
这里我们应该注意:线性表内的数据元素类型应该是相同的!
二:线性表的特点
从线性表的定义中其实我们就可以看出线性表的大概特点(非空线性表中,即n>0.n=0时称为空表):
1.“有限”——说明有头有尾 专业术语为:存在唯一一个被称为“第一个”的数据元素,存在唯一一个被称为“最后一个”的数据元素
2.“序列“——在此我简单的将它理解为站好队,因此也就有前后的问题 专业术语为:除第一个元素外,每个元素都有唯一的一个”直接前驱“。除最后一个元素外,每一个元素都有唯一的”直接后继“(说的就是麻烦哈,其实就是前面有人和后面有人呗)
三:线性表的存储方式(最重要)
线性表是存储数据的一种数据结构,那么它到底是怎样存储的呢?这是我们学习线性表务必要掌握的知识。
线性表的存储——分为顺序存储结构(顺序表)和链式存储结构(链表)。(画重点)
1:顺序表
定义:顺序表,一般使用数组实现。也就是在内存中找个初始地址,然后数据元素依次排队,数组大小有两种方式指定——静态分配和是动态扩展。(在此我把它比喻为操场上排队,带头的找个位置站好,其他的人依次往后排)
(图片百度找的)
特点:在存储空间中连续(紧挨着),数据有长度(此处为5)
2.链表
定义:用一组任意的存储单元存储线性表的数据元素(这组存储单元可以是连续的,也可以是不连续的),包括数据域和指针域,数据域存数据,指针域指示其后继的信息。
(图片百度找的)
可以抽象为:
特点:存储空间中不连续(满天飞哈)具有数据域和指针域(茫茫人海中,知道要去牵谁滴手)
3:顺序表和链表对数据的操作及优缺点(非常重要)
用线性表干嘛?肯定是用来操作数据啦。线性表对数据最多的操作就是——查找,插入和删除啦。(此处先用文字描述,具体实现在代码里,包括注释,很多东西一看到代码就懂了)
1:查找
顺序表的查找:(依然用这个图)
现在我要查3 知道了1的位置 那么从一开始 查3次(查1,2,3)第三次得到是3停止查找(因为数据在内存中是连续的,所以可以这样查找)
单链表的查找:
依旧是查3号,现在直到头节点1号的位置,然后就开始往后依次往后找,中间隔了多少个空间就得查多少次!(真累,至于为啥不能用索引呢?因为存储的不连续,满天飞的下场)
由此可以看出,在查找方面,一般来讲顺序表是比单链表要有优势的。
2:插入
顺序表的插入:
说到这个就不得不说上面的排队,这就是插队啊!有人插队了咋办?揍他一顿呗,是领导揍不起那没办法,排在领导要插队的位置后面的人都像后挪一个位。当然如果最后一个人后面是墙了那不好意思,这队差不了(大致就是这意思)
单链表的插入:领导要要插队,没关系,您随意。(反正再胖空位都能容下你),前面的人知道后面的人变成了领导,后面的人知道前面的现在是领导了就ok了;
3:删除
删除和插入差不多,都是位置相同移动的问题。
由此可以看出,在插入和删除方面,一般来讲,单链表是比较有优势的。
四:顺序表与线性表的优缺点
下面对顺序表和线性表进行简单的总结
顺序表优缺点
优点:
(1)空间利用率高(理由:连续存放)
(2)存取速度高效,通过下标直接进行存储和读取。(想想排队哈)
(3)顺序存储,随机读取(不算是优点,算是特点)
缺点:
(1)插入和删除比较慢。(想想插队,后面的全部需要往后移一个位)
(2)顺序表开始时就要定义一个长度,因此是存在空间限制的。当需要存储的元素个数可能多于顺序表元素时,可能出现“溢出”问题。(想想那个领导插队最后一个人靠墙的,总不能把人给挤扁了)。当然当你放的元素个数远远小于预先分配的空间是,就
产生浪费
单链表优缺点
优点:
(1)插入和删除的速度快,保留原有的物理顺序(只改变下指针。想想上述的插队问题就明白了)。
(2)没有空间限制,基本只与内存空间大小有关。(满天飞)
(3)随机存储,顺序读取(算是特点)
缺点:
(1)存取元素比较慢(不能进行索引访问,只能从头结点开始顺序查找)
(2)占用额外的空间用来存储指针(不是连续存放的(满天飞了解下),空间碎片多)
造成空间浪费。
五.代码实现!(最关键的点来了!)
为了节省空间,所以我将操作都封装成一个函数,用哪个直接调用那个就行了。
(当然jdk本身是提供这些方法的,不过我们是学习原理而不是学习使用,所以这些操作还是要自己写滴)
先贴出我的整体的项目结构
一:首先定义一个接口MyList
package com.wang.first.LinearTable;
/*
*首先 创建顺序表的接口
*/
public interface MyList {
//1:返回线性表大小
public int getSize();
//2:判断线性表是否为空:false:true
public boolean isEmpty();
//3:判断线性表是否含有元素e
public boolean contains(Object e);
//4: 查找 返回e元素所在的位置
public int indexOf(Object e);
//4.1:查找 返回第i个位置的元素
public Object get(int i);
//5:将元素e插入到顺序表的第i个位置
public void add(int i,Object e) throws Exception;
public void add(Object e) throws Exception;
//6:将元素e插入到obj之前
public boolean addBefore(Object obj,Object e) throws Exception;
//7:将元素e插入到obj之后
public boolean addAfter(Object obj,Object e) throws Exception;
//8:删除第i个位置元素
public void remove(int i) throws Exception;
//9:删除线性表中第一个为e的元素
public boolean removed (Object e) throws Exception;
//10:替换第i个元素为e
public Object replace(int i,Object e) throws Exception;
//11:输出线性表
public void display();
}
二、顺序表的代码实现(增删改查)
1 首先实例化接口
package com.wang.first.LinearTable;
/*
* 顺序表
* 执行MyList接口
*/
public class MyArrayList implements MyList {
//创建一个存储空间为maxSize的顺序表
private Object[] myList;//顺序表的存储空间
private int size;//顺序表的当前长度
private int maxSize;
//构建顺序表
public MyArrayList(int maxsize) {
size=0;
maxSize=maxsize;
myList=new Object[maxSize];
}
@Override
public int getSize() {//获取顺序表大小
// TODO Auto-generated method stub
return this.size;
}
@Override
public boolean isEmpty() { //空表
// TODO Auto-generated method stub
return size==0;
}
@Override
public boolean contains(Object e) {//判断是否含有元素e
// TODO Auto-generated method stub
for (int i=0;i<size;i++){//遍历
if(myList[i].equals(e)){
return true;
}
}
return false;
}
@Override
public int indexOf(Object e) { //返回元素e的位置
// TODO Auto-generated method stub
for (int i=0;i<size;i++){
if(myList[i].equals(e)){
return i;
}
}
return -1;
}
public Object get(int i){//返回第i个位置的元素
return myList[i];
//return -1;
}
@Override
public void add(int i, Object e) throws Exception { //插入到i位置
// TODO Auto-generated method stub
if(size==maxSize)
throw new Exception("顺序表已满,不可插入");//throw为手动抛出异常
if(i<0||i>size)
throw new Exception("插入位置不合法");
for(int j=size;j>i;j--){
myList[j]=myList[j-1];//i之后的数据依次后移
}
myList[i]=e;
size++;
}
@Override
public void add(Object e) throws Exception {
// TODO Auto-generated method stub
this.add(size,e);
}
@Override
public boolean addBefore(Object obj, Object e) throws Exception {//obj前插入e
// TODO Auto-generated method stub
//找到obj所在的位置 调用插入就行
int i=indexOf(obj);
if(i<0) return false;
add(i,e);
return true;
}
@Override
public boolean addAfter(Object obj, Object e) throws Exception { //obj后插入e
// TODO Auto-generated method stub
int i=indexOf(obj); //获取obj元素位置
if(i<0) return false; //没找到obj就会返回-1 此时i=-1;
add(i+1,e); //找到了调用插入即可
return true;
}
@Override
public void remove(int i) throws Exception {//移除第i个位置的元素
// TODO Auto-generated method stub
if(i<0||i>size-1) //判断是否越界
throw new Exception("删除位置不合法");
for(int j=i;j<size-1;j++){
myList[j]=myList[j+1]; //后面的依次向前移一个位置
}
size--;//移除了一个 所以长度-1
}
@Override
public boolean removed(Object e) throws Exception {//直接移除元素e(第一个匹配的e)移除所有的没有写
// TODO Auto-generated method stub
//找到e的位置调用上面的删除 (先判断有没有)
int i=indexOf(e);
if(i<0) return false; //判断有没有e
remove(i);
return true;
}
@Override
public Object replace(int i, Object e) throws Exception { //替换第i位置的元素为e
// TODO Auto-generated method stub
if(i<0||i>size)
throw new Exception("位置不合法");
Object obj=myList[i];
myList[i]=e;
return obj;
}
@Override
public void display() {//输出顺序表
// TODO Auto-generated method stub
for(int i=0;i<size;i++){
System.out.print(myList[i]+" ");
}
System.out.println("\n");
}
}
2、小程序调用具体实现
package com.wang.first.LinearTable;
/*
* 小测试1: 在0——9中作查看,插入 ,删除,和修改
*
* @:北城
*/
public class arraylistTest01 extends MyArrayList { //继承
public arraylistTest01(int maxsize) {
super(maxsize);
// TODO Auto-generated constructor stub
}
public static void main(String[] args) throws Exception {
arraylistTest01 t1=new arraylistTest01(100); //申请空间 注意此处如果是10插入的时候会抛出手写的那个异常
for(int i=0;i<10;i++){
t1.add(i, i); //调用insert依次插入构成顺序表
}
System.out.print("初始化的顺序表为:");
t1.display();//显示插入的顺序表
//1:查看
System.out.print("元素4位置为:"+t1.indexOf(4));//查看元素4的位置 从第0个开始
System.out.println("\n");
//2:插入
t1.add(2, 50);//第二个位置插入元素50
System.out.print("插入元素后的顺序表为:");
t1.display();
//3:删除某个位置的元素
t1.remove(5);
System.out.print("删除第5个元素后的顺序表为:");
t1.display();
//4:删除某个元素 :看着函数一样但是参数属性不一样 调用要注意
t1.removed(5);
System.out.print("删除元素5后的顺序表为:");
t1.display();
//修改
t1.replace(8,100);
System.out.print("将第8个元素替换成100后的顺序表为:");
t1.display();
}
}
3、结果如下图
二、单链表的实现
1、首先定义结点类
为了便于理解与使用,此处Node中的data和next暂时定义为public型
package com.wang.first.LinearTable;
/*
* 单链表采用了结点,所以需要定义下结点
* 主要是两个数据:内容和指向下一个结点的指针
*/
public class Node {
// private Object data;
// private Node next;
//为了便于理解与使用,此处暂时定义为public型
Object data;
Node next;
//生成方法
public Node() {
super();
}
public Node(Object data, Node next) {
super();
this.data = data;
this.next = next;
}
public Node(Object data) {
super();
this.data = data;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
}
2、实例化接口方法
package com.wang.first.LinearTable;
public class MyLinkList implements MyList {
private Node head=new Node();//定义一个头结点 不存数据,为了操作方便
private int size;//定义有几个结点
@Override
public int getSize() {
return size;
}
@Override
public boolean isEmpty() {
// TODO Auto-generated method stub
return size==0;
}
@Override
public boolean contains(Object e) {
// TODO Auto-generated method stub
return false;
}
@Override
public int indexOf(Object e) {
// TODO Auto-generated method stub
Node p=head;
for(int j=0;j<size;j++){
if(p.data==e)
return j;
p=p.next;
}
return -1;
}
public Object get(int i){
Node p=head;
for(int j=0;j<i;j++){
p=p.next;
}
return p.data;
}
@Override
public void add(int i, Object e) throws Exception {
// TODO Auto-generated method stub
if(i<0||i>size){
throw new Exception("插入位置异常"+i);
}
Node p=head;
for(int j=0;j<i-1;j++){
p=p.next;
}
Node newNode=new Node();
newNode.data=e;
newNode.next=p.next;
p.next=newNode;
size++;
}
public void add(Object e) throws Exception{ //上面的一种特殊情况
this.add(size, e);
}
@Override
public boolean addBefore(Object obj, Object e) throws Exception {
// TODO Auto-generated method stub
return false;
}
@Override
public boolean addAfter(Object obj, Object e) throws Exception {
// TODO Auto-generated method stub
return false;
}
@Override
public void remove(int i) throws Exception {
// TODO Auto-generated method stub
if(i<0||i>size){
throw new Exception("删除位置异常"+i);
}
Node p=head;
for(int j=0;j<i-1;j++){
p=p.next;
}
p.next=p.next.next;
size--;
}
@Override
public boolean removed(Object e) throws Exception {
// TODO Auto-generated method stub
int i=indexOf(e);
remove(i);
return true;
}
@Override
public Object replace(int i, Object e) throws Exception {
// TODO Auto-generated method stub
if(i<0||i>size){
throw new Exception("替换位置异常"+i);
}
Node p=head;
for(int j=0;j<i;j++){
p=p.next;
}
p.data=e;
return p;
}
@Override
public void display() {
Node p=head;
for(int j=0;j<size;j++){
p=p.next;
System.out.print(p.data+" ");
}
System.out.println("\n");
}
}
3、具体程序实现
package com.wang.first.LinearTable;
/*
* 小测试2: 在10——19中作查看,插入 ,删除,和修改
*
* @:北城
*/
public class linklistTest01 extends MyLinkList { //继承
public static void main(String[] args) throws Exception {
MyList t1=new MyLinkList(); //申请空间
//t1.add(0, 1);
for(int i=10;i<20;i++){
t1.add(i); //调用add依次插入 构成单链表
}
System.out.print("初始化的顺序表为:");
t1.display();//显示插入的顺序表
//1:查看
t1.indexOf(14);
System.out.print("元素14位置为:");//查看元素14的位置 从第0个开始
t1.display();
System.out.println("第10个位置的元素为: "+t1.get(10)+"\n");
//2:插入
t1.add(2, 50);//第二个位置插入元素50
System.out.print("插入元素后的顺序表为:");
t1.display();
//3:删除某个位置的元素
t1.remove(2);
System.out.print("删除第2个元素后的顺序表为:");
t1.display();
//4:删除某个元素 :看着函数一样但是参数属性不一样 调用要注意
t1.removed(18);
System.out.print("删除元素18后的顺序表为:");
t1.display();
//修改
t1.replace(2,100);
System.out.print("将第2个元素替换成100后的顺序表为:");
t1.display();
}
}
4、结果如下
注意我设置的下标从0开始计数
到此基本要实现的功能算是完成啦,下一篇博客将会写出复习数据结构第二章——栈和队列的原理与java实现