Java实现线性表的顺序和链式存储

2019-07-22  本文已影响0人  一只变强的Hacker

什么是线性表?

​ 线性表(Linear List),是由同类型数据元素构成有序序列的线性结构,表中元素个数称为线性表的长度;线性表没有元素时,称为空表;表的起始位置称为表头,表的结束位置称为表尾

抽象数据类型描述

​ 类型名称:线性表(List)

​ 数据对象集:线性表是n(n>=0)个元素构成的有序序列(a,a2,...,an)

​ 操作集:Item 表示元素类型,整数 i 表示位置

顺序存储实现(固定大小)

​ 创建一个泛型类List:

public class List<Item>{
    private Item[] data; // 泛型数组存储数据
    private int last = -1; // last表示最后一个元素的下标
}

​ 构造函数:

public List(int size){
        data = (Item[]) new Object[size]; // java不允许创建泛型数组,所以需要用到类型转换
}

插入数据

​ 在这里,我们插入数据的位置范围是1n+1,即第一个位置到最后一个位置+1(这里的n就表示当前List中的元素个数)。所以我们就会发现我们在插入数据后,这些数据都是连续的。比如:在创建了List之后,数组中没有元素,元素个数为0,那么此时插入的范围就是10+1,只有1这个位置可以插入,之后再插入第二个元素,这时元素的个数已经变成了1,此时的范围是1~1+1,以此类推,存储在这个线性表中的数据总会是连续的。

image

​ 当我们向线性表中插入数据时,首先要看看这个线性表满了没有,如果满了不能插入,判断满的条件就是:last==data.length-1如果最后一个元素的下标等于数组的最后一个下标,说明满了。

​ 然后,要判断插入的位置是否合法:i < 1 || i > last+2(n+1 >= i >= 1)。

​ 之前说过,List中存储的数据是连续的,所以我们插入时不能直接插入,需要将元素往后挪出一个空位才能够在特定的位置进行插入:

// 挪动的顺序毫无疑问是从最后一个元素挪,否则你从第i个元素挪的话就会覆盖掉后面的元素
for (int j=last; j>=i-1; j--){
    data[j+1] = data[j];
}
// 插入数据
data[i-1] = item;
// last加1
last = last + 1;

删除元素

​ 和插入元素类似,只是挪动元素是往前挪动元素,填补删除后留下的空位。直接上代码:

// 如果List空,不能删除
if (isEmpty()){
    System.out.println("List为空,不能删除");
}
// 规定的删除位置在1~n+1
if (i < 1 || i > last+2){
    System.out.println("删除位置非法,无法插入!");
}
// 在删除之后需要先将数据往前挪
for (int j=i-1; j<last; j++){
    data[j] = data[j+1];
}
last = last - 1;

完整代码:

public class List<Item> {
    // Java不允许创建泛型数组,所以做强制类型转换
    private Item[] data;
    private int last = -1; // 表示List中最后一个元素的下标

    // 构造函数-创建List时指定大小
    public List(int size){
        data = (Item[]) new Object[size];
    }

    // List是否为空
    public boolean isEmpty(){
        return last==-1;
    }

    // 返回List的大小
    public int size(){
        return last+1;
    }

    // 在第i个位置插入数据(即插入到数组下标为i-1这个位置)
    public void insert(Item item, int i){
        // 如果List满了,不能插入
        if (last==data.length-1){
            System.out.println("数组满,无法插入!");
        }
        // 规定的插入位置在1~n+1之间
        if (i < 1 || i > last+2){
            System.out.println("插入位置非法,无法插入!");
        }
        // 在插入之前需要先将数据往后挪
        for (int j=last; j>=i-1; j--){
            data[j+1] = data[j];
        }
        data[i-1] = item;
        last = last + 1;
    }

    // 在第i个位置删除数据(即删除数组下标为i-1这个位置)
    public void delete(int i){
        // 如果List空,不能删除
        if (isEmpty()){
            System.out.println("List为空,不能删除");
        }
        // 规定的删除位置在1~n+1
        if (i < 1 || i > last+2){
            System.out.println("删除位置非法,无法插入!");
        }
        // 在删除之后需要先将数据往前挪
        for (int j=i-1; j<last; j++){
            data[j] = data[j+1];
        }
        last = last - 1;
    }

    // 查找某个元素,如果查找到,返回下标,否则返回-1
    public int find(Item item){
        for (int i=0; i<last+1; i++){
            if (data[i]==item){
                return i;
            }
        }
        return -1;
    }

    public void printData(){
        for (Item item:data){
            System.out.print(" " + item);
        }
        System.out.println("");
    }

    // 测试
    public static void main(String[] args) {
        List<Integer> testList = new List(5);
        testList.insert(1,1);
        testList.insert(2,2);
        testList.insert(3,3);
        testList.insert(4,4);
        testList.insert(5,5);
        testList.printData();
        testList.delete(1);
        testList.printData();
        System.out.println(testList.find(3));
    }
}

链式存储实现

​ 创建一个泛型类 LinkedList:

public class LinkedList<Item> {
    private Node head; // 线性表的表头(链表的入口)
    private int N; // 结点的数量(线性表中的元素数量)

    // node结点类
    private class Node{
        Item item;
        Node next;
        // 构造器
        public Node(Item item, Node next) {
            this.item = item;
            this.next = next;
        }
    }
}

​ 求线性表的长度和是否为空:

public int length(){
    return N;
}

public boolean isEmpty(){
    return head==null; // 当head为null或N为0
}

​ 查找相应的位置的结点:

public Node findXth(int X){
    Node temp = head;
    int i = 1;
    // 当当前结点不为空且位置小于X时
    while (temp != null && i < X){
        temp = temp.next;
        i = i+1;
    }
    // 跳出循环有两种可能,当前结点为空或i==X
    if (i == X){
        return temp;
    } else{
        return null;
    }
}

插入数据

​ 因为是链式存储,所以不必关心空间的问题,只需care插入问题。

​ 要向链表中插入结点,首先要分插入的位置。如果是插到链表的头,需要做特殊处理:让插入结点的next的值变为head,head的值置为新插入的结点;如果插入到其他位置,只需要先找到插入位置的前一个结点p,然后是两步(顺序不能更改):1、设置插入的结点的next的值为p的next。2、改变p的next的值为插入的结点。

public void insert(int i, Item item){
    Node temp, p;
    if (i == 1){
        temp = new Node(item, head);
        head = temp;
        N = N+1;
        return;
    }
    p = findXth(i-1);
    if (p == null){
        System.out.println("参数i错误!");
        return;
    } else{
        temp = new Node(item, p.next);
        p.next = temp;
        N = N+1;
        return;
    }
}

删除数据

​ 和插入类似,区分删除的是头结点还是其他位置,删除头结点:只需head置为head的next;删除其他结点:找到前一个结点p,使其next置为p.next.next即可,java会自动进行垃圾回收。

public void delete(int i){
    Node p;
    if (i == 1){
        if (head == null){
            System.out.println("链表为空!");
            return;
        } else{
            head = head.next;
            N = N-1;
            return;
        }
    }
    p = findXth(i-1);
    if (p == null || p.next == null){
        System.out.println("第i个结点不存在!");
    } else {
        p.next = p.next.next;
        N = N-1;
    }
}
上一篇下一篇

猜你喜欢

热点阅读