使用java模拟实现LinkedList单向链表

1. 基础知识

线性表常见的分为顺序表(vector和ArrayList)和链表(LinkedList)
单向链表只能由前驱找到后继,每个节点有数据和指向下一个节点的指针
使用java模拟实现LinkedList单向链表_第1张图片

优缺点:增删快,查找满
增加节点:新节点指针指向后一个节点,新节点的前一个节点指针指向新节点.
删除节点:被删除的节点的前一个节点指向被删除节点指向的后一个节点.

为了使操作一致,使用增加使用头结点(哑元节点)的方法.
使用java模拟实现LinkedList单向链表_第2张图片

2. 编写接口List

package com.cqc.LinkedList;

/**
 * 线性表接口
 * 和存储结构无关
 * @author Administrator
 *
 */
public interface List {
    //返回线性表的大小,即数据元素的个数
    public int size();

    //返回线性表中序号为i的数据元素
    public Object get(int i);

    //判断线性表是否为空,空为true,否则为false
    public boolean isEmpty();

    //判断线性表是否包含数据元素node
    public boolean contains(Node node);

    //返回数据元素e在线性表中序号
    public int indexOf(Node node);

    //将数据元素e插入到线性表的i号位置
    public void add(int i,Object e);

    //将数据元素e插入到线性表末尾
    public void add(Object e);

    //删除线性表中序号为i的元素,并返回之
    public Object remove(int i);

    //删除线性表中第一个与node相同的元素
    public boolean remove(Node node);

    //替换线性表中序号为i的数据元素为e,返回原数据元素
    public Object replace(int i,Object e);

}



3. 编写接口Node,节点类

/**
 * 节点类,包含指针和数据
 */
public class Node {
    public Object data; //数据
    public Node next; //指向下一个节点的指针

    //构造方法和重写toSting
    public Node(Object data, Node next) {
        this.data = data;
        this.next = next;
    }
    public Node(Object data) {
        this.data = data;
    }
      public Node() {

    }
    @Override
    public String toString() {
        return "Node{" +
                "data=" + data +
                '}';
    }
}

4. 编写LinkedList实现List接口

package com.cqc.LinkedList;

/**
 * 查找,删除,增加核心方法是找到找到要操作节点的前一个节点,第0个的前一个节点为head节点,用head赋值即可得到第0个节点的前一个节点
 */
public class LinkedList implements List {

    private Node head = new Node(); //头结点,不存储数据,为了使用方便,head为指向头结点的指针
    private int size; //一共有几个节点

    @Override
    public int size() {
        return size;
    }

    /**
     * 需要从头结点开始查找
     * @param i
     * @return
     */
    @Override
    public Object get(int i) {
        //找到i前一个节点,p初始值指向头结点
        Node  p = head;
        //最终p指向第i-1个节点(从第0个开始,循环i次)
        for(int j=0;j<i;j++){
            p = p.next;
        }
        return p.next;
    }

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

    /**
     * 查询是否包含此节点
     * @param node
     * @return
     */
    @Override
    public boolean contains(Node node) {
        return indexOf(node)>=0;
    }

    @Override
    public int indexOf(Node node) {
        //p初始值指向头结点
        Node  p = head;
        //最终p指向第size-1个节点(从第0个开始,循环size次)
        for(int j=0;j<size;j++){
            p = p.next;
            if(p.next == node){ //p指向要查找的元素
                return j;
            }
        }
        return -1;
    }

    @Override
    public void add(int i, Object e) {
        //找到i前一个节点,p初始值指向头结点
        Node  p = head;
        //最终p指向第i-1个节点(从第0个开始,循环i此)
        for(int j=0;j<i;j++){
            p = p.next;
        }
        //新创建一个节点,构造给了数据,指针未赋值
        Node newNode = new Node(e);

        //指明新节点的直接后继
        //指针由前一个节点(i-1)得到
        newNode.next= p.next;

        //指明新节点的直接前驱,改变前一个节点,第(i-1)的指针域
        p.next = newNode;
        //添加完成后size++
        size++;


    }

    @Override
    public void add(Object e) {
        this.add(size,e);
    }


    @Override
    public Object remove(int i) {
        //找到i前一个节点,p初始值指向头结点
        Node  p = head;
        //最终p指向第i-1个节点(从第0个开始,循环i此)
        for(int j=0;j<i;j++){
            p = p.next;
        }
        //保存第i个节点对象
        Object o = p.next;
        //将第i个节点的指针域赋值给第i-1个节点的指针域
        p.next = p.next.next;

        //元素个数减一
        size--;

        return o;
    }

    /**
     * 删除一个节点
     * @param node
     * @return
     */
    public boolean remove(Node node) {
        //找到i前一个节点,p初始值指向头结点
        Node  p = head;
        Node beforeP; //beforeP指向p指向的节点的前一个节点
        //最终p指向第size-1个节点(从第0个开始,循环size次)
        for(int j=0;j<size;j++){
            beforeP = p;
            p = p.next;
            if(p.next == node){ //p指向要删除的元素
                beforeP.next = p.next;
                //元素个数减一
                size--;
                return true;
            }
        }
        return false;
    }

    @Override
    public String toString() {

        if(size == 0){
            return "[]";
        }
        StringBuilder stringBuilder = new StringBuilder("["); //字符串开始符号为[
        //定义一个Node,指向head
        Node p = head;
        for(int i = 0 ;i<size;i++){
            p = p.next; //p指向第i个节点
            if(i !=size-1){
                stringBuilder.append(p.data+",");
            }else {
                stringBuilder.append(p.data);
            }
        }
        stringBuilder.append("]"); //字符串结尾符号
        return stringBuilder.toString();
    }

}

5. 重要方法实现过程

5.1 增加一个节点的方法

增加一个节点newNode(数据和指针域),需要知道该节点之前的节点,则获取该节点之前的节点是核心内容第一个节点的前一个节点是头结点,则获取指向头结点的指针是前提

获取指向头结点指针的方法:

① 由无参构造器new出LinkedList时,同时new出了一个节点head和size变量
在这里插入图片描述
② head是指向头结点的指针
在这里插入图片描述
③流程图
使用java模拟实现LinkedList单向链表_第3张图片

获取指向要操作的节点的前一个节点,:

①总体流程
使用java模拟实现LinkedList单向链表_第4张图片

②示意图
使用java模拟实现LinkedList单向链表_第5张图片
使用p指针指向头结点,根据参数i可以获取到要添加节点的前一个节点,通过指向新节点指针和指向第2个节点指针的引用和替换,借壳完成增加操作.

5.2 由索引找到一个节点的方法

  public Object get(int i) {
        //找到i前一个节点,p初始值指向头结点
        Node  p = head;
        //最终p指向第i-1个节点(从第0个开始,循环i次)
        for(int j=0;j<i;j++){
            p = p.next;
        }
        return p.next;
    }

核心方法还是找到头结点,由p指针依次按照i的值循环查找到i节点的前一个节点

你可能感兴趣的:(单链表,数据结构)