1、链表的设计思想:
2、链表及操作链表的方法:(使用泛型是为了避免转型的异常)
package com.demo;
// 设置泛型避免安全隐患;接口定义标准
interface ILink<E> {
public void add(E e); //增加数据
public int size() ; //获取数据的个数
public boolean isEmpty() ; //空集合判断
public Object[] toArray() ; //将集合元素以数组的形式返回
public E get(int index) ; //获取指定索引数据
public void set(int index, E data) ; //修改指定索引的数据
public boolean contains(E data) ; //判断数据是否存在
public void remove(E data) ; //数据删除
public void clean() ; //清空链表
}
// 实现接口
class LinkImpl<E> implements ILink<E> {
//私有内部类-保存节点的数据关系
private class Node {
private E data ; //保存的数据
private Node next ;
public Node(E data) { //有数据才有意义
this.data = data ;
}
//保存新的Node数据
public void addNode(Node newNode) {
if(this.next == null) { //第一次调用该方法,this.next即为根节点的下一个节点
this.next = newNode ;
}else {
this.next.addNode(newNode); //在此处调用时即为下一个节点的下一个节点
}
}
//将集合中的数据保存到数组中(递归定义)
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 containNode(E data) {
//此处注意,由于整个链表没有null数据的存在,所以程序在判断的时候直接使用每一个节点数据发出equals()方法即可,但是如果链表中有空数据时,必须由待判断的数据data发出,因为在调用此方法之前,已经为data做了空值判断。
//if(this.data.equals(data)) {
if(data.equals(this.data)) {
return true ;
}else {
if(this.next == null) { //找不到
return false ;
}else {
return this.next.containNode(data) ; //向后继续比较
}
}
}
//删除非根节点
public void removeNode(Node previous, E data) {
if(this.data.equals(data)) {
previous.next = this.next ; //空出当前节点
}else {
if(this.next != null) { //有后续节点
this.next.removeNode(this, data); //向后继续删除
}
}
}
}
// Link类中定义的结构
private Node root ; //保存根元素
private int count ; //保存数据的个数
private int foot ; //操作数组的脚标
private Object[] returnData ; //返回的数据保存数组
public void add(E e) {
if(e == null) {
return ; //方法调用直接结束
}
// 数据本身不具有关联特性;要想实现关联处理必须将数据包装在Node类中
Node newNode = new Node(e) ; //创建一个新的节点
if(this.root == null) { //当前没有根节点
this.root = newNode ; //第一个节点作为根节点
}else {
// this.root.next = newNode ; 不能这样写,否则root的next永远在被替换
//将新节点保存在合适的位
this.root.addNode(newNode);
}
this.count ++ ; //数据增加成功数据个数加一
}
//返回链表中数据个数的方法
public int size() {
return this.count;
}
//空集合判断的方法
public boolean isEmpty() {
return this.root == null ;
}
//将集合元素以数组的形式返回
public Object[] toArray() {
if(this.isEmpty()) {
return null;
}
this.foot = 0 ; //脚标清零
this.returnData = new Object[this.count] ; //根据已有长度开辟数组
//在Node类中根据递归获取数据
this.root.toArrayNode() ;
return this.returnData ;
}
//获取指定索引数据-这一特点和数组相似,但是数组获取一个数据的时间复杂度为1,而链表获取数据的时间复杂度为n
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); //修改数据,具体方法在Node类中
}
//判断数据是否存在
public boolean contains(E data) {
if(data == null) {
return false ; //没有数据
}
return this.root.containNode(data) ; //交个Node类判断
}
//数据删除-需要数据比较的支持(两种情况:删除的根节点(与LinkImpl有关)-让root指向root的next节点、删除的不是根节点(由node类负责))
public void remove(E data) {
if(this.contains(data)) { //判断数据是否存在
if(this.root.data.equals(data)) { //根节点为要删除的节点
this.root = this.root.next ;
}else {
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>() ;
all.add("Hello");
all.add("一只瓶子a");
all.set(0,"Hi");
System.out.println("【数据集合的长度】" + all.size());
Object result [] = all.toArray() ;
if(result != null) {
for(Object obj : result) {
System.out.println("【遍历数据】:" + obj);
}
}
System.out.println("【根据索引获取数据】:" + all.get(1));
System.out.println("【判断数据是否存在】:" + all.contains("一只瓶子a"));
}
}