Java 数据结构_线性表

Java 数据结构_线性表


本文由 Luzhuo 编写,转发请保留该信息.
原文: http://blog.csdn.net/rozol/article/details/78446966


分类

顺序表(一维数组)

  • 顺序表:
    • 线性表是n个数据元素的有限序列
  • 优缺点
    • 优点
      • 存取速度快(随机存取)
    • 缺点
      • 增删元素时,造成大量的数据元素移动
      • 存储空间是静态分配,定义太大浪费空间,太小不够用,难以估计存储规模时采用链式表为好

Code

顺序表实现代码

顺序表实现代码

/**
 * ArrayList, 顺序表,一元数组
 * @author Luzhuo
 */
public class ArrayList {
    private int[] array; // 数组内存指针
    private int size; // 数组的容量
    private int length; // 已存数据的数量

    public ArrayList(){
        init(1024);
    }

    public ArrayList(int size){
        init(size);
    }

    /**
     * 创建数组<br>
     * 设置数组容量, 创建数组空间, 设置指针偏移量0
     * @param size 创建数组的大小
     */
    private void init(int size){
        this.size = size;
        this.array = new int[size]; // 创建数组内存空间
        this.length = 0;
    }

    /**
     * 删除数组<br>
     * 释放数组内存, 数组容量和指针偏移量将在创建时重新设置
     */
    public void destroy(){
        this.array = null; // 释放数组内存空间
    }

    /**
     * 清空数据<br>
     * 设置指针偏移量0, 添加数据时, 数组中的旧数据将被覆盖
     */
    public void clear(){
        this.length = 0;
    }

    /**
     * 数组是否为空<br>
     * 指针偏移量不为0则表示有数据
     * @return true 为空 / false 不为空 
     */
    public boolean isEmpty(){
        // 如果已存数据数量为0,则表示数组为空
        return this.length == 0 ? true : false;
    }

    /**
     * List的长度(已存数据的数量)<br>
     * 指针偏移量为已存储数据的数量
     * @return 返回数组中已存储的数据数
     */
    public int length(){
        return this.length;
    }

    /**
     * 获取数据<br>
     * 通过索引直接获取数组中对应的数据
     * @param index 索引
     * @return 数据
     */
    public int getData(int index){
        // 判断传入参数是否合法
        if(index < 0 || index >= this.size){
            throw new ArrayIndexOutOfBoundsException("索引越界.");
        }

        return array[index];
    }

    /**
     * 获取指定元素的(第一个)索引<br>
     * 遍历寻找指定元素, 找到该元素则返回其对应的索引值
     * @param data 数据
     * @return 找到指定元素返回元素索引, 否则返回-1
     */
    public int getIndex(int data){
        for(int i = 0; i < this.length; i++){
            if(this.array[i] == data) return i;
        }
        return -1;
    }

    /**
     * 获取指定元素的直接前驱<br>
     * 遍历查找指定的元素, 找到该元素则获取对应的索引值, 数组中该索引值-1位置的元素便是其直接前驱
     * @param data 指定元素
     * @return 返回指定元素的直接前驱; 没有找到该元素 或者 是第一个元素, 直接返回-1
     */
    public int priorData(int data){
        // 获取元素的位置
        int tempIndex = getIndex(data);

        // 没有找到该元素 或者 是第一个元素, 直接返回-1
        if(tempIndex == -1 || tempIndex == 0) return -1;

        return array[tempIndex - 1];
    }

    /**
     * 获取指定元素的直接后继<br>
     * 遍历查找指定的元素, 找到该元素则获取对应的索引值, 数组中该索引值+1位置的元素便是其直接后继
     * @param data 指定元素
     * @return 返回指定元素的直接后继, 没有找到该元素 或者 是最后一个元素, 直接返回-1
     */
    public int nextData(int data){
        // 获取元素的位置
        int tempIndex = getIndex(data);

        // 没有找到该元素 或者 是最后一个元素, 直接返回-1
        if(tempIndex == -1 || tempIndex == this.length - 1) return -1;

        return array[tempIndex + 1];
    }

    /**
     * 遍历List
     */
    public void traverse(){
        for(int i = 0; i < this.length; i++){
            System.out.println(this.array[i]);
        }
    }

    /**
     * 插入数据<br>
     * 先将指定位置及其所有后继元素全部向后平移一位(从最末尾开始移), 然后插入到指定位置, 指针偏移量+1
     * @param index 插入的位置
     * @param data 插入的数据
     * @return 插入成功返回true; 索引越界将返回false
     */
    public boolean insert(int index, int data){
        if(index < 0 || index > this.length){
            throw new ArrayIndexOutOfBoundsException("索引越界.");
        }

        // 将index位置及其所有后继元素全部向后平移一位,从最末尾的元素开始移
        for(int i = this.length -1; i >= index; i--){
            this.array[i + 1] = this.array[i];
        }
        // 移完数据后,在插入数据
        this.array[index] = data;
        this.length++;
        return true;
    }

    /**
     * 删除数据<br>
     * 将指定位置及其所有后继元素向前平移一位, 指针偏移量-1
     * @param index 删除指定的元素
     * @return 返回被删除的元素
     */
    public int delete(int index){
        if(index < 0 || index >= this.length){
            throw new ArrayIndexOutOfBoundsException("索引越界.");
        }

        // 先将要删除的元素拷贝
        int temp = this.array[index];

        // 拷贝完之后,将index位置与之后所有后继元素向前移动一位
        for(int i = index + 1; i < this.length; i++){
            this.array[i-1] = this.array[i];
        }
        this.length--;
        return temp;
    }
}

顺序表测试代码

public class ArrayListTest {
    public static void main(String[] args) {
        // ArrayList的测试
        ArrayList list = new ArrayList();

        for(int x = 0; x < 10; x++){
            list.insert(x, x);
        }
        list.traverse();

        list.delete(3);
        list.traverse();

        System.out.println(list.length());
        System.out.println(list.getData(5));
        System.out.println(list.getIndex(5));
        System.out.println(list.priorData(5));
        System.out.println(list.nextData(5));
        System.out.println(list.isEmpty());

        list.traverse();
        list.clear();
        System.out.println(list.isEmpty());
        list.traverse();

        list.destroy();
    }
}

链式表

  • 静态链表
    • 数组形式,插入和删除时无需移动元素,仅修改指针即可
    • 最后结点的指针指向0
  • 单链表
    • 只能沿着一个方向查找,不能反向查找,最后一个结点指针域的值是null
  • 单向循环链表
    • 将单链表的最后一个结点指针域的null指向了第一个结点
    • 以末尾结点指针为已知条件
  • 双向链表
    • 双向列表保存了直接前趋结点和直接后继结点的指针
    • 末尾结点后继指针域为null
    • 头指针的前趋指针域为null
  • 双向循环链表
    • 末尾结点的后继指针域直线给第一个结点
    • 头结点的前趋指针域指向最后一个结点
  • 优缺点
    • 优点
      • 增删元素快,不会造成大量的元素移动,但是需要遍历寻找
    • 缺点
      • 存取速度慢(顺序存取,需要遍历查找)

Code

单链表实现代码

单链表实现代码

/**
 * LinkedList, 单链表
 * @author Luzhuo
 */
public class LinkedList {
    public Node node; // 头结点
    public int length; // 已存的数据数量

    public LinkedList(){
        init();
    }

    /**
     * 创建单链表<br>
     * 创建头结点, 空数据, 头结点指针=>null, 指针偏移量0
     */
    private void init(){
        // 初始化头结点
        this.node = new Node();
        node.data = 0;
        node.next = null;
        length = 0;
    }

    /**
     * 删除单链表<br>
     * 遍历删除所有结点(包括头结点), 头结点指针=>null, 指针偏移量0
     */
    public void destroy(){
        // 释放所有结点
        // 删除头结点的所有后继结点
        clear();
        // 删除头结点
        this.node = null;
    }

    /**
     * 清空数据
     * 遍历删除头结点的所有后继结点, 头结点指针=>null, 指针偏移量0
     */
    public void clear(){
        // 保留头结点,释放所有后继结点
        // 通过头结点找到下个结点
        Node curN = this.node.next;
        // 该结点的指针域不为null
        while(curN != null){
            Node tempN = curN.next;
            // 已通过该结点找到下个结点,该结点无利用价值,将其删除
            curN = null;
            curN = tempN;
        }
        // 删除完数据后,将头结点的指针域置为null
        this.node.next = null;
        this.length = 0;
    }

    /**
     * LinkedList是否为空<br>
     * 指针偏移量不为0, 则表示有数据
     * @return LinkedList为空返回true,否则返回false
     */
    public boolean isEmpty(){
        return this.length == 0 ? true : false;
    }

    /**
     * 获取LinkedList的长度(已存储数据的数量)<br>
     * 指针偏移量为已存储数据的数量
     * @return 数据长度
     */
    public int length(){
        return this.length;
    }

    /**
     * 插入数据到头部(头结点后)<br>
     * 交换指针, 头结点指针=>newNode, newNode=>tempNode, 指针偏移量+1
     * @param data 要插入的数据
     */
    public boolean insertFrist(Node data){
        // 临时保存头结点的指针域里的指针
        Node tempN = this.node.next;
        // 创建新的结点,并赋值数据
        Node newN = new Node();
        newN.data = data.data;
        // 交换结点,将新结点的指针给头结点, 将临时保存的指针赋值到新节点的指针域
        this.node.next = newN;
        newN.next = tempN;
        this.length++;
        return true;
    }

    /**
     * 插入数据到末尾<br>
     * 寻找末尾结点, Node=>NewNode, NewNode=>null, 指针偏移量+1
     * @param data
     * @return
     */
    public boolean insertLast(Node data){
        // 寻找最后一个结点
        Node curN = this.node;
        while(curN.next != null){
            curN = curN.next;
        }
        // 创建新的结点, 并将指针交给上边找出来的最后一个结点.
        Node newN = new Node();
        newN.data = data.data;
        newN.next = null;
        curN.next = newN;
        this.length++;
        return true;
    }

    /**
     * 插入数据<br>
     * 找到指定位置的结点(IndexNode), 交换指针, NewNode=>AfterNode, IndexNode=>NewNode, 指针偏移量+1
     * @param index 插入位置
     * @param data 插入的数据
     * @return 插入数据是否成功
     */
    public boolean insert(int index, Node data){
        if(index < 0 || index > this.length){
            throw new ArrayIndexOutOfBoundsException("索引越界.");
        }

        // 找到index位置的结点
        Node curN = this.node;
        for(int i = 0; i < index; i++){
            curN = curN.next;
        }
        // 创建新的结点并交换指针,先将index结点指针域里的指针交给新结点,再将新结点指针赋值到index结点的指针域
        Node newN = new Node();
        newN.data = data.data;
        newN.next = curN.next;
        curN.next = newN;
        this.length++;
        return true;
    }

    /**
     * 删除元素<br>
     * 找到指定位置元素的结点(IndexNode), 交换指针, BeforeNode=>AfterNode, 指针偏移量-1
     * @param index
     * @return 返回被删除的元素
     */
    public Node delete(int index){
        if(index < 0 || index >= this.length) {
            throw new ArrayIndexOutOfBoundsException("索引越界.");
        }

        Node curN = this.node;
        Node curNBefore = null;
        for(int i = 0; i <= index; i++){
            curNBefore = curN; // 保存index的直接前趋结点,再获取index结点
            curN = curN.next; // 获取index结点
        }
        // 交换结点;将index结点指针域里的指针交给直接前趋结点
        curNBefore.next = curN.next;
        Node temp = curN;
        temp.next = null;
        this.length--;
        return temp;
    }

    /**
     * 获取数据<br>
     * 遍历获取指定位置的元素
     * @param index 位置
     * @return
     */
    public Node getData(int index){
        if(index < 0 || index >= this.length) {
            throw new ArrayIndexOutOfBoundsException("索引越界.");
        }

        Node curN = this.node;
        for(int i = 0; i <= index; i++){
            curN = curN.next;
        }

        return curN;
    }

    /**
     * 查找数据的(第一个)索引<br>
     * 遍历查找指定元素
     * @param data 查找的数据
     * @return 找到返回索引,未找到返回-1
     */
    public int getIndex(Node data){
        Node curN = this.node;
        int count = 0;
        while(curN.next != null){
            curN = curN.next;
            if(curN.data == data.data){
                return count;
            }
            count++;
        }
        return -1;
    }

    /**
     * 查找数据的直接前趋结点的数据<br>
     * 遍历查找指定元素(同时缓存上个结点), 返回缓存的上个结点
     * @param data
     * @return
     */
    public int priorData(Node data){
        Node curN = this.node;
        Node tempN = null;
        while(curN.next != null){
            tempN = curN;
            curN = curN.next;
            if(curN.data == data.data){
                if(tempN == this.node) return -1; // 第一个元素没有前趋 元素
                int priData = tempN.data;
                return priData;
            }
        }
        return -1;
    }

    /**
     * 查找数据的直接后继结点的数据<br>
     * 遍历查找指定结点, 返回下个结点
     * @param data
     * @return
     */
    public int nextData(Node data){
        Node curN = this.node;
        while(curN.next != null){
            curN = curN.next;
            if(curN.data == data.data){
                if(curN.next == null) return -1; // 最后一个结点没有后继结点
                return curN.next.data;
            }
        }
        return -1;
    }

    /**
     * 遍历<br>
     * 根据结点的next指针域里的指针, 依次向下遍历结点
     */
    public void traverse(){
        Node curN = this.node;
        while(curN.next != null){
            curN = curN.next;
            curN.print();
        }
    }

}

/**
 * 结点
 * @author Luzhuo
 */
class Node{
    public int data; // 数据域
    public Node next; // 指针域(指向下个结点)

    public Node(){ }

    public Node(int data){
        this.data = data;
    }

    public void print() {
        System.out.println(("Next: ".concat(next == null ? "null" : next.toString()).concat("; Data: ").concat(String.valueOf(data))));
    }
}

单链表测试代码

public class LinkedListTest {
    public static void main(String[] args) {
        // 测试
        LinkedList list = new LinkedList();

        for(int x = 0; x < 10; x++){
            list.insert(x, new Node(x));
        }

        list.traverse();

        list.delete(3);
        list.insertFrist(new Node(11));
        list.insertLast(new Node(99));
        list.traverse();

        System.out.println(list.isEmpty());
        System.out.println(list.length());
        System.out.println(list.getData(3).data);
        System.out.println(list.getIndex(new Node(5)));
        System.out.println(list.priorData(new Node(5)));
        System.out.println(list.nextData(new Node(5)));


        list.clear();
        System.out.println(list.isEmpty());
        System.out.println(list.length());
        list.traverse();
        list.destroy();
    }
}
双向循环链表

LinkedHashMap是java中自带双向循环链表数据结构
LinkedHashMap源码分析

特殊的线性表

栈 和 队列 属于线性表中较特殊的一种, 因为 栈 被限制为后进先出, 队列被限制为 先进先出
栈 和 队列 的实现方式可以为顺序表 或者 链式表.

栈属于线性表中特殊表之一, 后进先出

Code

栈的数组实现代码

/**
 * 栈
 * @author Luzhuo
 */
public class Stack {
    private Elem[] buffer; // 栈空间指针
    private int size; // 栈容量
    private int length; // 栈中元素个数

    public Stack(){
        init(1024);
    }

    public Stack(int size){
        init(size);
    }

    /**
     * 创建栈<br>
     * 创建数组, 偏移量指针0
     * @param size
     */
    private void init(int size){
        this.size = size;
        this.buffer = new Elem[size];
        this.length = 0;
    }

    /**
     * 删除栈<br>
     * 删除数组, 偏移量指针将在创建时重新设置
     */
    public void destory(){
        this.buffer = null;
    }

    /**
     * 栈是否为空<br>
     * 指针偏移量不为0, 则不为空
     * @return true 没有数据, false 有数据
     */
    public boolean isEmpty(){
        return this.length == 0 ? true : false;
    }

    /**
     * 栈是否已满<br>
     * 指针偏移量>=数组容量, 则栈已存满
     * @return
     */
    public boolean isFull(){
        return this.length >= this.size ? true : false;
    }

    /**
     * 清空栈<br>
     * 指针偏移量0, 设置数据将直接覆盖数组里的旧数据
     */
    public void clear(){
        this.length = 0;
    }

    /**
     * 获取栈中元素的个数<br>
     * 指针偏移量为已存数据量
     * @return
     */
    public int length(){
        return this.length;
    }

    /**
     * 入栈:添加元素到栈顶<br>
     * 添加到数组末尾, 指针偏移量+1
     * @param elem 元素
     * @return
     */
    public boolean push(Elem elem){
        if(isFull()) return false;

        this.buffer[this.length] = elem;
        this.length++;

        return true;
    }

    /**
     * 出栈:从栈顶移除第一个元素<br>
     * 将数组末尾的数据返回(不删除数据), 指针偏移量-1, 添加数据时直接将其覆盖
     * @return 有元素返回元素,没有元素返回null
     */
    public Elem pop(){
        if(isEmpty()) return null;

        this.length--;
        return this.buffer[this.length];
    }

    /**
     * 遍历(栈顶到栈底)<br>
     */
    public void traverse(){
        for(int i = this.length - 1; i >= 0; i--){
            System.out.print(this.buffer[i] + " ");
        }
    }

}

/**
 * 存放栈中的元素
 * @author Luzhuo
 *
 */
class Elem{
    public char data;
    public Elem(){}
    public Elem(char data){
        this.data = data;
    }
    @Override
    public String toString() {
        return String.valueOf(data);
    }
}

栈测试代码

public class StackTest {
    public static void main(String[] args) {
        // 测试
        Stack stack = new Stack(10);

        System.out.println(stack.isEmpty());
        System.out.println(stack.isFull());
        System.out.println(stack.length());

        for(int i = 0; i < 10; i++){
            stack.push(new Elem((char) (65+i)));
        }


        stack.push(new Elem('B'));
        stack.push(new Elem('C'));

        System.out.println(stack.isEmpty());
        System.out.println(stack.isFull());

        System.out.println(stack.pop());
        System.out.println(stack.length());

        stack.traverse();

        stack.clear();
        System.out.println(stack.length());

        stack.destory();
    }

}

队列

队列属于线性表中特殊表之一, 先进先出

普通队列

环形队列: 弥补了普通队列的处理完任务后内存浪费的情况

Code

环形队列的数组实现代码

public class Queue {
    private Elem[] queque; // 队列数组
    private int length; // 队列元素个数
    private int size; // 队列的容量 
    private int head; // 队头
    private int tail; // 队尾

    public Queue(){
        init(1024);
    }

    public Queue(int size){
        init(size);
    }

    /**
     * 创建队列<br>
     * 创建数组, 头指针0, 尾指针0, 指针偏移量0
     * @param size
     */
    private void init (int size){
        this.size = size;
        this.queque = new Elem[size];
        clear();
    }

    public void destory(){
        this.queque = null;
    }

    /**
     * 清空队列<br>
     * 重置 头指针0, 尾指针0, 指针偏移量0
     */
    public void clear(){
        this.head = 0;
        this.tail = 0;
        this.length = 0;
    }

    /**
     * 是否为空<br>
     * 如果指针偏移量为0 则说明没有数据
     * @return
     */
    public boolean isEmpty(){
        return this.length == 0 ? true : false;
    }

    /**
     * 是否满了<br>
     * 如果偏移量 >= 容量, 说明已经满了
     * @return
     */
    public boolean isFull(){
        return this.length >= this.size ? true : false;
    }

    /**
     * 获取长度<br>
     * 指针偏移量就是已存数据量
     * @return
     */
    public int length(){
        return this.length;
    }

    /**
     * 插入元素(插入到队列末尾)<br>
     * 插入到队尾, 尾指针位置由 this.tail % this.size 计算获得
     * @param elem
     * @return
     */
    public boolean addElem(Elem elem){
        if(isFull()) return false;
        if(elem == null) throw new NullPointerException();

        this.queque[this.tail] = elem;
        this.tail++;
        this.tail = this.tail % this.size;
        this.length++;
        return true;
    }

    /**
     * 获取元素<br>
     * 获取头指针处的元素, 然后头指针+1, 指针偏移量-1
     * @return
     */
    public Elem getElem(){
        if(isEmpty()) return null;

        Elem elem = this.queque[this.head];
        this.head++;
        this.head = this.head % this.size;
        this.length--;
        return elem;
    }

    /**
     * 遍历
     */
    public void traverse(){
        for(int i = this.head; i < this.length + this.head; i++){
            System.out.print(this.queque[i%this.size] + " ");
        }
    }
}

class Elem{
    public char data;

    public Elem(){}
    public Elem(char data){
        this.data = data;
    }

    @Override
    public String toString() {
        return String.valueOf(this.data);
    }
}

环形队列测试代码

public class QueueTest {
    public static void main(String[] args) {
        // 测试
        Queue que = new Queue(3);

        System.out.println(que.isEmpty());
        System.out.println(que.isFull());
        System.out.println(que.length());

        for(int i = 0; i < 5; i++){
            que.addElem(new Elem((char)(65+i)));
        }

        que.traverse();

        System.out.println();
        System.out.println(que.getElem());

        que.traverse();
        System.out.println();

        que.addElem(new Elem('O'));
        que.traverse();
        System.out.println();

        System.out.println(que.isEmpty());
        System.out.println(que.isFull());
        System.out.println(que.length());


        que.clear();
        System.out.println(que.length());

        que.destory();
    }
}
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值