线性表

1、线性表

线性结构是最简单也是最常用的数据结构之一。线性数据结构的特点是:在数据元素的有限集中,除了第一个元素没有直接前驱,最后一个元素没有直接后继外,其他元素有且仅有一个前驱和一个后继。

线性表有两种存储实现方式,分别是顺序存储和链式存储,本文主要介绍两种方式实现线性表,并对他们的优缺点做一简单的比较。

1.1 线性表的顺序存储实现

import java.util.Arrays;

public class SequenceList {

private int DEFAULT_SIZE = 16;
//保存数组的长度
private int capacity;
//定义以一个线性表用于保存顺序表中的元素
private Object[] elementData;
//保存顺序表中元素的个数
private int size = 0;
//以默认数组长度创建空线性表
public SequenceList(){
    capacity = DEFAULT_SIZE;
    elementData = new Object[capacity];
}
//以一个初始化元素创建一个线性表
public SequenceList(T element){
    this();
    elementData[0] = element;
    size++;
}
/**
 * 已制定长度的数组来初始化线性表
 * @param element 线性表的第一个元素
 * @param initSize 指定顺序表底层的数组长度
 */
public SequenceList(T element, int initSize){
    capacity = 1;
    while(capacity < initSize){
        capacity <<= 1;
    }
    elementData = new Object[capacity];
    elementData[0] = element;
    size++;
}
/**
 * 获取线性表的长度
 * @return 返回线性表的数据长度
 */
public int length(){
    return size;
}
/**
 * 获取索引为i的元素
 * @param i 索引
 * @return 返回索引处的元素
 */
@SuppressWarnings("unchecked")
public T get(int i){
    if(i < 0 || i > size -1){
        throw new IndexOutOfBoundsException("线性表索引越界");
    }
    return (T) elementData[i];
}
/**
 * 查找顺序表中指定元素的索引
 * @param element
 * @return
 */
public int locate(T element){
    for(int i = 0;i < size;i++){
        if(elementData[i] == element){
            return i;
        }
    }
    return -1;
}
/**
 * 在线性表的指定位置插入元素
 * @param element 需要插入的元素
 * @param index 插入元素的位置
 */
public void insert(T element,int index){
    if(index < 0 || index > size){
        throw new IndexOutOfBoundsException("线性表索引越界");
    }
    ensureCapacity(size + 1);
    //将指定索引处的元素都向后移动一格
    System.arraycopy(elementData, index, elementData, index + 1, size - index);
    elementData[index] = element;
    size++;
}
/**
 * 在线性表的末尾添加一格元素
 * @param element
 */
public void insertAtFirst(T element){
    insert(element,0);
}
/**
 * 确保线性表的最小容量
 * @param minCapacity 指定最小容量
 */
private void ensureCapacity(int minCapacity) {
    if(capacity < minCapacity){
        while(capacity < minCapacity){
            capacity <<= 1;
        }
        elementData = Arrays.copyOf(elementData, capacity);
    }
}
/**
 * 删除指定索引出的元素
 * @param index 索引
 * @return 返回被删除的元素
 */
public T delete(int index){
    if(index < 0 || index > size -1){
        throw new IndexOutOfBoundsException("数组越界异常 ");
    }
    @SuppressWarnings("unchecked")
    T oldValue = (T) elementData[index];
    int numMoved = size - index - 1;
    if(numMoved > 0){
        System.arraycopy(elementData, index + 1, elementData, index, numMoved);
    }
    //清空最后一个元素
    elementData[--size] = null;
    return oldValue;
}
/**
 * 删除最后一个元素
 * @return 返回最后一个元素
 */
public T remove(){
    return delete(size -1);
}
/**
 * 判断线性表是否为空
 * @return 根据size的是否为零来判断
 */
public boolean isEmpty(){
    return size == 0;
}
/**
 * 清空线性表
 */
public void clear(){
    //将底层数组的所有元素全部置为空
    Arrays.fill(elementData, null);
    size = 0;
}

public String toString(){
    if(size == 0){
        return "[]";
    }else{
        StringBuilder sb = new StringBuilder("[");
        for (int i = 0; i < size; i++) {
            sb.append(elementData[i].toString()+",");
        }
        int len = sb.length();
        return sb.delete(len -2, len).append("]").toString();
    }
}

}
1.2 线性表的链式存储实现——单链表

public class LinkList {

/**
 * 定义一个内部内,实例表示链表的节点
 * 
 * @author Administrator
 *
 */
private class Node {
    // 保存节点数据
    private T data;
    // 指向下一个节点
    private Node next;

    // 无参构造器
    public Node() {

    }

    // 带参构造器
    public Node(T data, Node next) {
        this.data = data;
        this.next = next;
    }
}

// 保存链表的头节点
private Node header;
// 保存链表的尾节点
private Node tail;
// 保存节点的个数
private int size;

// 创建空链表
public LinkList() {
    header = null;
    tail = null;
}

// 已制定元素来创建链表
public LinkList(T element) {
    header = new Node(element, null);
    tail = header;
    size++;
}

// 返回链表的长度
public int length() {
    return size;
}

// 获取链表指定索引处的元素
public T get(int index) {
    return getNodeByIndex(index).data;
}

// 根据索引获取指定位置的节点
private Node getNodeByIndex(int index) {
    if (index < 0 || index > size - 1) {
        throw new IndexOutOfBoundsException("数组越界异常 ");
    }
    // 从header节点开始
    Node current = header;
    for (int i = 0; i < size; i++) {
        if (current != null) {
            if (i == index) {
                return current;
            }
            current = current.next;
        }
    }
    return null;
}

// 查找线性表中指定元素的索引
public int getIndex(T element) {
    // 从header节点开始
    Node current = header;
    for (int i = 0; i < size; i++) {
        if (current != null) {
            if (current.data.equals(current)) {
                return i;
            }
        }
    }
    return -1;
}

// 向链式表的 指定位置插入元素
public void insert(T element, int index) {
    if (index < 0 || index > size - 1) {
        throw new IndexOutOfBoundsException("数组越界异常 ");
    }
    // 如果是空链表
    if (header == null) {
        add(element);
    } else {
        // 在标头插入
        if (index == 0) {
            addAtHeader(element);
        } else {
            // 获取插入节点的前一个节点
            Node pre = getNodeByIndex(index - 1);
            // 新节点指向前一个节点的下一个节点,前一个节点的下一个节点指向新节点
            pre.next = new Node(element, pre.next);
            size++;
        }
    }
}

// 头插法
private void addAtHeader(T element) {
    // 创建新节点,并且让新节点指向header
    header = new Node(element, header);
    if (tail == null) {
        tail = header;
    }
    size++;
}

// 采用尾插法插入一个元素
private void add(T element) {
    // 如果为空链表
    if (header == null) {
        header = new Node(element, null);
        tail = header;
    }
    // 链表不为空
    else {
        Node newNode = new Node(element, null);
        tail.next = newNode;
        tail = newNode;
    }
    size++;
}

// 删除指定索引处的节点
public T delete(int index) {
    if (index < 0 || index > size - 1) {
        throw new IndexOutOfBoundsException("数组越界异常 ");
    }
    Node del = null;
    // 如果删除的是第一个节点
    if (index == 0) {
        del = header;
        header = header.next;
    } else {
        // 获取删除节点的前一个节点
        Node pre = getNodeByIndex(index - 1);
        // 获取将要被删除的节点
        del = pre.next;
        pre.next = del.next;
        // 将被删除节点的next置为空
        del.next = null;
    }
    size--;
    return del.data;
}

// 删除链表的最后一个节点元素
public T remove() {
    return delete(size - 1);
}

/**
 * 判断线性表是否为空
 * 
 * @return 根据size的是否为零来判断
 */
public boolean isEmpty() {
    return size == 0;
}

/**
 * 清空线性表
 */
public void clear() {
    header = null;
    tail = null;
    size = 0;
}

public String toString() {
    if (size == 0) {
        return "[]";
    } else {
        StringBuilder sb = new StringBuilder("[");
        for (Node current = header; current != null; current = current.next) {
            sb.append(current.data.toString() + ",");
        }
        int len = sb.length();
        return sb.delete(len - 2, len).append("]").toString();
    }
}

}

1.3 线性表的链式存储实现——双链表

public class DuLinkList {

/**
 * 定义一个内部内,实例表示链表的节点
 * 
 * @author Administrator
 *
 */
private class Node {
    // 保存节点数据
    private T data;
    // 指向下一个节点
    private Node next;
    // 指向前一个节点
    private Node pre;

    // 无参构造器
    public Node() {
    }

    // 含参构造器
    public Node(T data, Node next, Node pre) {
        this.data = data;
        this.next = next;
        this.pre = pre;
    }
}

// 保存链表的头节点
private Node header;
// 保存链表的尾节点
private Node tail;
// 保存节点的个数
private int size;

// 创建空链表
public DuLinkList() {
    header = null;
    tail = null;
}

// 以指定数据元素来创建链表
public DuLinkList(T element) {
    header = new Node(element, null, null);
    tail = header;
    size++;
}

// 根据索引获取指定位置的节点
private Node getNodeByIndex(int index) {
    if (index < 0 || index > size - 1) {
        throw new IndexOutOfBoundsException("数组越界异常 ");
    }
    if (index <= size / 2) {
        // 从header开始遍历
        Node current = header;
        for (int i = 0; i <= size / 2 && current != null; i++, current = current.next) {
            if (i == index) {
                return current;
            }
        }
    } else {
        // 从结尾开始遍历
        Node current = tail;
        for (int i = 0; i > size / 2 && current != null; i++, current = current.pre) {
            if (i == index) {
                return current;
            }
        }
    }
    return null;
}

// 返回链表的长度
public int length() {
    return size;
}

// 获取链表中索引为index的元素
public T get(int index) {
    return getNodeByIndex(index).data;
}

// 查找指定元素在线性表中的索引
public int locate(T element) {
    // 从头节点开始遍历
    Node current = header;
    for (int i = 0; i < size && current != null; i++, current = current.next) {
        if (current.data.equals(element)) {
            return i;
        }
    }
    return -1;
}

// 采用尾插法为链表插入一个元素
public void inserAtLast(T element) {
    // 如果链表为空链表
    if (header == null) {
        header = new Node(element, null, null);
        tail = header;
    } else {
        // 创建新节点新节点的pre指向原tail
        Node newNode = new Node(element, tail, null);
        // 让尾节点的next指向新节点
        tail.next = newNode;
        tail = newNode;
    }
    size++;
}

// 采用头插法为链表插入一个元素
public void insertAtFirst(T element) {
    // 创建新节点,新节点的next指向原来的header,并且让新节点作为新的header
    header = new Node(element, null, header);
    // 插入之前为空链表
    if (tail == null) {
        tail = header;
    }
    size++;
}

// 向链表的指定位置插入元素
public void insert(T element, int index) {
    if (index < 0 || index > size - 1) {
        throw new IndexOutOfBoundsException("数组越界异常 ");
    }
    // 插入之前为空链表
    if (header == null) {
        insertAtFirst(element);
    } else {
        if (index == 0) {
            insertAtFirst(element);
        } else {
            // 获取插入节点的前一个节点
            Node prev = getNodeByIndex(index - 1);
            // 新建要插入的检点
            Node newNode = new Node(element, prev, prev.next);
            // 让prev的next指向新节点
            prev.next = newNode;
            // 让原来的index节点的pre指向新节点
            prev.next.pre = newNode;
            size++;
        }
    }
}

// 删除指定索引处的元素
public T remove(int index) {
    if (index < 0 || index > size - 1) {
        throw new IndexOutOfBoundsException("数组越界异常 ");
    }
    Node del = null;
    // 如果删除的是头结点
    if (index == 0) {
        header = header.next;
        // 释放对原来头节点的引用
        header.pre = null;
    } else {
        // 获取删除节点的前一个节点
        Node prev = getNodeByIndex(index - 1);
        // 获取被删除的节点
        del = prev.next;
        // 让被删除节点的前一个节点的next指向删除节点的next
        prev.next = del.next;
        // 被删除节点的下一个节点的next指向被删除节点的前一个节点
        if (del.next != null) {
            del.next.pre = prev.next;
        }
        // 将被删除节点的next和pre引用置为空
        del.pre = null;
        del.next = null;
    }
    size--;
    return del.data;
}

// 删除链表的最后一个元素
public T delete() {
    return remove(size - 1);
}

// 将链表置为空
public void clear() {
    header = null;
    tail = null;
    size = 0;
}

public String toString() {
    if (size == 0) {
        return "[]";
    } else {
        StringBuilder sb = new StringBuilder("[");
        for (Node current = header; current != null; current = current.next) {
            sb.append(current.data.toString() + ",");
        }
        int len = sb.length();
        return sb.delete(len - 2, len).append("]").toString();
    }
}

public String reverseToString() {
    if (size == 0) {
        return "[]";
    } else {
        StringBuilder sb = new StringBuilder("[");
        for (Node current = tail; current != null; current = current.pre) {
            sb.append(current.data.toString() + ",");
        }
        int len = sb.length();
        return sb.delete(len - 2, len).append("]").toString();
    }
}

}

1.4 顺序表和链表的比较

线性表的常用操作主要有查找、插入、删除,下面就这三个方面分别从时间复杂度和空间复杂度进行比较分析。

1.4.1 基于时间的比较

  1. 顺序表的读取性能优于链表:顺序表中元素的逻辑顺序与物理存储顺序一致,而且支持随机存取,因此顺序表在查找、读取时性能很好;而链表必须从头开始遍历查找元素,无法在O(1)的时间内完成。
  2. 基于元素的插入删除操作:顺序表中插入和删除操作需要移动大量的元素;相比较而言链表只需要在查找到位置后修改几个指针的引用即可,因此对于插入和删除较多的操作,应该选择链式存储结构的链表。

1.4.2 基于空间的比较

  1. 当线性表的长度范围变化比较大时,应该考虑采用链表:顺序表,其存储空间是预先静态分配的,虽然在存储期间可以扩展数组的容量,但是如果线性表的长度范围变化范围较大,则在使用中会存在大量的空闲空间,空间的利用率不高;而链表的存储空间是动态分配的,不会存在空间浪费的问题。
  2. 当线性表的长度范围变化不大时,应该考虑采用顺序表。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值