数据结构-线性表的两种实现方式:顺序表和链表

  • 顺序表就是在内存中按顺序连续开辟一段空间来存储数据的结构,在java中就是数组,如a所示
  • 链表就是在内存中随机开辟内存一段段存储数据的结构,如图b所示
    在这里插入图片描述

线性表的接口

使用一个接口表明基本操作的需求:

public interface Ilist {
    //清空线性表
    public void clear();
    //判断线性表是否为空
    public boolean isEmpty();
    //获取线性表的长度
    public int length();
    //获取线性表中的第i个值
    public Object get(int i) throws Exception;
    //往线性表中第i个位置插入一个值
    public void insert(int i,Object x) throws Exception;
    //移除掉线性表中的第i个值
    public void remove(int i) throws Exception;
    //找到线性表中第一次出现x的索引
    public int indexOf(Object x);
    //打印整个线性表
    public void display();
}

顺序表基本操作代码实现

使用顺序表来实现Ilist接口,重写接口中所有的抽象类方法,注释就能看懂大部分,这里需要注意的是插入和删除两个操作,下面图解:

插入操作

从下图可以看出,想要在i-1出插入x,那么就要将该位置后面所有的元素向后移动,并且应该从an开始移动,再a(n-1),最后把ai从i-1移动到i位置,再插入x

在这里插入图片描述

删除操作

删除某个地方的元素后,该位置就会空出来,那么我们只需要把后面的元素依次往前移动补上去就行
在这里插入图片描述

public class SqList implements Ilist{
  	//对象数组,存放数据
    private Object[] listElem;
    //用于记录当前顺序表的长度
    private int curLen;

    //构造函数初始化,置长度为0,开辟一个maxSize大小的数组
    public SqList(int maxSize) {
        curLen = 0;
        listElem = new Object[maxSize];
    }

    @Override
    public void clear() {
        //直接置当前数组长度为0即可,不用释放,下次用这一块内存会直接覆盖
        curLen = 0;
    }

    @Override
    public boolean isEmpty() {
        return curLen == 0;
    }

    @Override
    public int length() {
        return curLen;
    }

    @Override
    public Object get(int i) throws Exception{
        if(i < 0 || i >= curLen){
            throw new Exception("索引越界");
        }
        return listElem[i];
    }

    @Override
    public void insert (int i, Object x) throws Exception{
        if(curLen == listElem.length){
            throw new Exception("数组满了,无法插入新元素");
        }
      	//注意比较这里i > curLen,上面的get()方法是i >= curLen,因为这里是插入数据,可以在最后插入,因此可以等于curLen
        if(i < 0 || i > curLen){
            throw new ArrayIndexOutOfBoundsException("索引越界");
        }
      	//要想在某个地方插入,必须先把该位置后面的数组往后移动一下,把该位置空出来才能插入数据
        for (int j = curLen; j > i ; j--) {
            listElem[j] = listElem[j-1];
        }
        listElem[i] = x;
        curLen++;
    }

    @Override
    public void remove(int i) throws Exception{
        if(i < 0 || i >= curLen){
            throw new Exception("索引错误");
        }
      	//从i后面的数据一次往前补上来就行,注意这里j<curLen-1
        for (int j = i; j < curLen-1; j++) {
            listElem[i] = listElem[i+1];
        }
        curLen--;
    }

    @Override
    public int indexOf(Object x) {
        for (int i = 0; i < curLen; i++) {
            if (listElem == x){
                return i;
            }
        }
        return -1;
    }

    @Override
    public void display() {
        for (int i = 0; i < curLen; i++) {
            System.out.print(listElem[i]+"");
        }
        System.out.println();
    }
}

单链表基本操作代码实现

链表需要先定义一个节点类Node用来存放数据以及指向下一个节点的指针,这里链表的插入和删除操作需要单独进行图解:

插入

先使p指向head节点,往后移动到i-1的位置,第一步使得q.next指向p.next.next,再把p.next连接到q上。注意,这里顺序不能反了,如果反了,链表后半部分就找不到了
在这里插入图片描述

删除

在这里插入图片描述

import java.util.Scanner;

public class LinkList implements Ilist{
  	//这里书上并没有定义一个记录长度的curLen,书上的链表的长度是从head遍历计算的,目前还没有想清楚
    private int curLen;
    public Node head;

    public LinkList() {

        curLen = 0;
        head = new Node();
    }

    public LinkList(int n, boolean order) throws Exception {
        //order为0时尾插,为1时头插
        this();
        System.out.println("请输入"+n+"个字符串,以回车分割");
        if(!order){
            creatTail(n);
        }
        else{
            creatHead(n);
        }
    }

    @Override
    public void clear() {
        head.data = null;
        head.next = null;
        curLen = 0;
    }

    @Override
    public boolean isEmpty() {
        return head.next == null;
    }

    @Override
    public int length() {
        return curLen;
    }

    @Override
    public Object get(int i) throws Exception {
        if (i < 0 || i >= curLen){
            throw new Exception("索引错误");
        }
        Node p = head.next;
        for (int j = 0; j < i; j++) {
            p = p.next;
        }
        return p.data;
    }

    @Override
    public void insert(int i, Object x) throws Exception {
        if (i > curLen){
            throw new Exception("索引错误");
        }
        Node p = head;
        Node q = new Node(x);
        for (int j = -1; j < i - 1; j++) {
            p = p.next;
        }
        q.next = p.next;
        p.next = q;
        curLen++;
    }

    @Override
    public void remove(int i) throws Exception {
        if (i < 0 || i >= curLen){
            throw new Exception("索引错误");
        }
        Node p = head.next;
        for (int j = 0; j < i - 1; j++) {
            p = p.next;
        }
        p.next = p.next.next;
        curLen--;
    }

    @Override
    public int indexOf(Object x) {
        Node p = head.next;
        int j = 0;
        if (p != null){
            if (p.data == x)
                return j;
            j++;
            p = p.next;
        }
        return -1;
    }

    @Override
    public void display() {
        Node p = head.next;
        while (p != null){
            System.out.print(p.data+" ");
            p = p.next;
        }
        System.out.println();
    }

    public void creatHead(int n) throws Exception {
        Scanner sc = new Scanner(System.in);
        for (int i = 0; i < n; i++) {
            insert(0,sc.next());
        }
    }

    public void creatTail(int n) throws Exception {
        Scanner sc = new Scanner(System.in);
        for (int i = 0; i < n; i++) {
            insert(length(),sc.next());
        }
    }

}

链表和数组优缺点比较

  • 数组实现比较简单,查找效率高,插入和删除效率低,需要预分配空间,一旦分配尺寸就固定了

  • 链表比较灵活,插入和删除的效率比较高,可以动态分配空间,但是查找效率低

  • 所以红黑树就结合了两者的特点,使得查找效率十分高效,后面再学习红黑树

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值