链表的定义与使用

2020-11-13  本文已影响0人  曾梦想仗剑天涯

链表实现简介

此图来源于李兴华老师 此图来源于李兴华老师 此图来源于李兴华老师
//直接操作Node很麻烦
class Node<E> {
  private E data;
  private Node next;
  public Node(E data) {
    this.data = data;
  }
  public void setNext(Node next) {
    this.next = next;
  }
  public E getData() {
    return this.data;
  }
  public Node getNext() {
    return this.next;
  }
}
public class LinkDemo {
  public static void main(String [] args) {
    Node<String> n1 = new Node<String>("火车头");
    Node<String> n2 = new Node<String>("车厢一");
    Node<String> n3 = new Node<String>("车厢二");
    Node<String> n4 = new Node<String>("车厢三");
    Node<String> n5 = new Node<String>("车厢四");
    n1.setNext(n2);
    n2.setNext(n3);
    n3.setNext(n4);
    n4.setNext(n5);
    print(n1);
  }
  public static void print(Node<?> node) {
    if(node != null) {
      System.out.println(node.getData());
      print(node.getNext());
    }
  }
}
此图来源于李兴华老师

数据保存:public void add(E e)

//基本结构
interface ILink<E> {    //设置泛型避免安全隐患
  public abstract void add(E e);
}
class LinkImpl<E> implements ILink<E> {
  private class Node {    //保存节点的数据类型
    private E data;   //保存的数据
    private Node next;    //保存下一个引用
    public Node(E data) {   //有数据的情况下才有意义
      this.data = data;
    }
  }
  public void add(E e) {
  
  }
}
//实现数据增加
interface ILink<E> {    //设置泛型避免安全隐患
  public abstract void add(E e);
}
class LinkImpl<E> implements ILink<E> {
  private class Node {    //保存节点的数据类型
    private E data;   //保存的数据
    private Node next;    //保存下一个引用
    public Node(E data) {   //有数据的情况下才有意义
      this.data = data;
    }
    //第一次调用: this = LinkImpl.root
    //第二次调用:  this = LinkImpl.root.next
    //第三次调用:  this = LinkImpl.root.next.next
    public void addNode(Node newNode) {   //保存新的node数据
      if(this.next == null) {   //当下一个节点为null时
        this.next = newNode;    //保存当前节点
      } else {
        this.next.addNode(newNode);
      }
    }
  }
  //-------------------以下是ILink类中定义的成员
  private Node root;    //根元素
  //-------------------以下是ILink类中定义的方法
  public void add(E e) {
    if(e == null) {   //保存的数据为null
      return;   //方法调用直接结束
    }
    //数据本身并不具有关联特性,只有Node类有,那么想实现关联处理就必须将数据包装在Node类之中
    Node newNode = new Node(e);   //创建一个新的节点
    if(this.root == null) {   //现在没有根节点
      this.root = newNode;    //第一个节点作为根节点
    } else {
      this.root.addNode(newNode);//将新节点保存在合适的位置
    }
  }
}
public class LinkDemo {
  public static void main(String [] args) {
    ILink<String> all = new LinkImpl<String>();
    all.add("Hello");
    all.add("World");
  }
}

获取数据长度:public int size()

public abstract int size();   //获取数据的个数
private int count;
public void add(E e) {
  if(e == null) {   //保存的数据为null
    return;   //方法调用直接结束
  }
  //数据本身并不具有关联特性,只有Node类有,那么想实现关联处理就必须将数据包装在Node类之中
  Node newNode = new Node(e);   //创建一个新的节点
  if(this.root == null) {   //现在没有根节点
    this.root = newNode;    //第一个节点作为根节点
  } else {
    this.root.addNode(newNode);//将新节点保存在合适的位置
  }
  this.count ++;
}
public int size() {
  return this.count;
}

空集合判断:public boolean isEmpty()

public abstract boolean isEmpty();   //判断是否为空集合
public boolean isEmpty() {
  //return this.root == null;
  return this.count == 0;
}

返回集合数据:public Object [] toArray()

此图来源于李兴华老师
public abstract Object [] toArray();    //将元素以数组的形式返回
private int foot;   //操作数组的脚标
private Object [] returnData;   //返回的数据保存
//第一次调用:this = LinkImpl.root
//第二次调用:  this = LinkImpl.root.next
//第三次调用:  this = LinkImpl.root.next.next
public void toArrayNode() {
  LinkImpl.this.returnData[LinkImpl.this.foot++] = this.data;
  if(this.next != null) {   //还有下一个数据
    this.next.toArrayNode();
  }
}
public Object [] toArray() {
  if(this.isEmpty()) {    //空集合
    return null;    //没有数据
  }
  this.foot = 0;    //脚标清零
  this.returnData = new Object [this.count];    //根据已有长度开辟数组
  this.root.toArrayNode();    //利用Node类递归获取数据
  return this.returnData;
}

获取指定索引数据:public E get(int index)

此图来源于李兴华老师
public abstract E get(int index);   //根据索引获取数据
public E getNode(int index) {
  if(LinkImpl.this.foot++ == index) {
    return this.data;
  } else {
    return this.next.getNode(index);
  }
}
public E get(int index) {
    if(index >= this.count) {   //所以应该在指定范围之内
      return null;
    }
    this.foot = 0;    //重制索引的下标
    return this.root.getNode(index);
  }

*这一特点和数组是很相似的,但是需要注意的是,数组获取一个数据的时间复杂度为1,而链表获取数据的时间复杂度为n;

修改指定索引数据:public void set(int index, E data)

public void set(int index, E data)
public void setNode(int index, E data) {
  if(LinkImpl.this.foot++ == index) {   //索引相同
    this.data = data;   //修改数据
  } else {
    this.next.setNode(index, data);
  }
}
public void set(int index, E data) {
  if(index >= this.count) {
    return;
  }
  this.foot = 0;
  this.root.setNode(index, data);
}

判断指定数据是否存在:public boolean contains(E data)

public abstract boolean contains(E data);   //判断数据是否存在
public boolean containsNode(E data) {
  if(data.equals(this.data)) {
    return true;
  } else {
    if(this.next == null) {
      return false;
    } else {
      return this.next.containsNode(data);
    }
  }
}
public boolean contains(E data) {
  if(data == null) {
    return false;
  }
  return this.root.containsNode(data);
}

数据的删除:public void remove(E data)

此图来源于李兴华老师 此图来源于李兴华老师
public abstract void remove(E data);    //删除指定数据
public void remove(E data) {
  if(this.contains(data)) {   //判断数据是否存在
    if(this.root.data.equals(data)) {   //根节点为要删除的节点
      this.root = this.root.next;   //根节点的下一个节点
    }
    this.count--;
  }
}
public void removeNode(Node previous, E data) {
  if(data.equals(this.data)) {
    previous.next = this.next;    //空出当前节点
  } else {
    if(this.next != null) {   //有后续节点
      this.next.removeNode(this, data);   //向后继续删除
    }
  }
}
public void remove(E data) {
  if(this.contains(data)) {   //判断数据是否存在
    if(this.root.data.equals(data)) {   //根节点为要删除的节点
      this.root = this.root.next;   //根节点的下一个节点
    } else {    //如果不是根节点,交由Node类进行处理
      this.root.next.removeNode(this.root, data);
    }
    this.count--;
  }
}

清空链表:public void clean()

public abstract void clean();   //清空集合
public void clean() {
  this.root = null;   //后续的所有节点都没了
  this.count = 0;   //个数清零
}
interface ILink<E> {    //设置泛型避免安全隐患
  public abstract void add(E e);    //增加数据
  public abstract int size();   //获取数据的个数
  public abstract boolean isEmpty();   //判断是否为空集合
  public abstract Object [] toArray();    //将元素以数组的形式返回
  public abstract E get(int index);   //根据索引获取数据
  public abstract void set(int index, E data);   //修改索引数据
  public abstract boolean contains(E data);   //判断数据是否存在
  public abstract void remove(E data);    //删除指定数据
  public abstract void clean();   //清空集合
}
class LinkImpl<E> implements ILink<E> {
  private class Node {    //保存节点的数据类型
    private E data;   //保存的数据
    private Node next;    //保存下一个引用
    public Node(E data) {   //有数据的情况下才有意义
      this.data = data;
    }
    //第一次调用: this = LinkImpl.root
    //第二次调用:  this = LinkImpl.root.next
    //第三次调用:  this = LinkImpl.root.next.next
    public void addNode(Node newNode) {   //保存新的node数据
      if(this.next == null) {   //当下一个节点为null时
        this.next = newNode;    //保存当前节点
      } else {
        this.next.addNode(newNode);
      }
    }
    //第一次调用:this = LinkImpl.root
    //第二次调用:  this = LinkImpl.root.next
    //第三次调用:  this = LinkImpl.root.next.next
    public void toArrayNode() {
      LinkImpl.this.returnData[LinkImpl.this.foot++] = this.data;
      if(this.next != null) {   //还有下一个数据
        this.next.toArrayNode();
      }
    }
    public E getNode(int index) {
      if(LinkImpl.this.foot++ == index) {   //索引相同
        return this.data;   //返回当前数据
      } else {
        return this.next.getNode(index);
      }
    }
    public void setNode(int index, E data) {
      if(LinkImpl.this.foot++ == index) {   //索引相同
        this.data = data;   //修改数据
      } else {
        this.next.setNode(index, data);
      }
    }
    public boolean containsNode(E data) {
      if(data.equals(this.data)) {
        return true;
      } else {
        if(this.next == null) {
          return false;
        } else {
          return this.next.containsNode(data);
        }
      }
    }
    public void removeNode(Node previous, E data) {
      if(data.equals(this.data)) {
        previous.next = this.next;    //空出当前节点
      } else {
        if(this.next != null) {   //有后续节点
          this.next.removeNode(this, data);   //向后继续删除
        }
      }
    }
  }
  //-------------------以下是ILink类中定义的成员
  private Node root;    //根元素
  private int count;    //保存数据个数
  private int foot;   //操作数组的脚标
  private Object [] returnData;   //返回的数据保存
  //-------------------以下是ILink类中定义的方法
  public void add(E e) {
    if(e == null) {   //保存的数据为null
      return;   //方法调用直接结束
    }
    //数据本身并不具有关联特性,只有Node类有,那么想实现关联处理就必须将数据包装在Node类之中
    Node newNode = new Node(e);   //创建一个新的节点
    if(this.root == null) {   //现在没有根节点
      this.root = newNode;    //第一个节点作为根节点
    } else {
      this.root.addNode(newNode);//将新节点保存在合适的位置
    }
    this.count ++;
  }
  public int size() {
    return this.count;
  }
  public boolean isEmpty() {
    //return this.root == null;
    return this.count == 0;
  }
  public Object [] toArray() {
    if(this.isEmpty()) {    //空集合
      return null;    //没有数据
    }
    this.foot = 0;    //脚标清零
    this.returnData = new Object [this.count];    //根据已有长度开辟数组
    this.root.toArrayNode();    //利用Node类递归获取数据
    return this.returnData;
  }
  public E get(int index) {
    if(index >= this.count) {   //所以应该在指定范围之内
      return null;
    }
    this.foot = 0;    //重制索引的下标
    return this.root.getNode(index);
  }
  public void set(int index, E data) {
    if(index >= this.count) {
      return;
    }
    this.foot = 0;
    this.root.setNode(index, data);
  }
  public boolean contains(E data) {
    if(data == null) {
      return false;
    }
    return this.root.containsNode(data);
  }
  public void remove(E data) {
    if(this.contains(data)) {   //判断数据是否存在
      if(this.root.data.equals(data)) {   //根节点为要删除的节点
        this.root = this.root.next;   //根节点的下一个节点
      } else {    //如果不是根节点,交由Node类进行处理
        this.root.next.removeNode(this.root, data);
      }
      this.count--;
    }
  }
  public void clean() {
    this.root = null;   //后续的所有节点都没了
    this.count = 0;   //个数清零
  }
}
public class LinkDemo {
  public static void main(String [] args) {
    ILink<String> all = new LinkImpl<String>();
    System.out.println("数据增加之前size = " + all.size() + "、是否为空集合:" + all.isEmpty());
    all.add("Hello");
    all.add("World");
    all.add("!");
    all.remove("World");
    all.clean();
    System.out.println("数据增加之后size = " + all.size() + "、是否为空集合:" + all.isEmpty());
    System.out.println("----------------输出集合数据--------------------");
    Object [] result = all.toArray();
    if(result != null) {
      for(Object obj : result) {
        System.out.println(obj);
      }
    }
    System.out.println("----------------根据索引获取数据----------------");
    System.out.println(all.get(0));
    System.out.println(all.get(1));
    System.out.println(all.get(2));
    System.out.println("----------------根据索引修改数据----------------");
    all.set(0, "你好");
    System.out.println(all.get(0));
    System.out.println(all.get(1));
    System.out.println("----------------判断数据是否存在----------------");
    System.out.println(all.contains("Hello"));
    System.out.println(all.contains("World"));
  }
}
上一篇下一篇

猜你喜欢

热点阅读