单向链表基本功能实现

package demo7;
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 e);				//数据删除
	
	public void clean();					//清空集合
 	
}

class LinkImpl<E> implements ILink<E>{

	private class Node{		//保存节点的数据关系
		
		private E data;		//保存的数据
		
		private Node next; 	//保存下一个引用
		
		private int count; 	//保存数据的个数
		
		public Node(E data) {	//有数据的情况下才有意义
			this.data = data;
		}
		
		//在现在定义的Node类之中并没有出现setter与getter方法,
		//是因为内部类中的私有属性也方便外部类直接访问
		
		//第一次调用: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);		//如果不是继续往后判断
			}
		}
		
		//在Node类中递归获取数据
		//第一次调用: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();
			 }
		}
		
		//在Node中索引数据
		public E getNode(int index) {
			if(LinkImpl.this.foot ++ == index) {   //索引相同
				return this.data;			//返回当前数据
			}else {	
				return this.next.getNode(index);  //返回下一条索引数据
			}
			//这一特点和数组是很相似的,但是需要注意的是,数组获取一个数据的时间复杂度为1
			//而链表获取数据的时间复杂度为n;
		}
		//在Node中修改指定索引数据
		public void setNode(int index, E data) {
			if(LinkImpl.this.foot ++ == index) {   //索引相同
				this.data = data;			//返回当前数据
			}else {	
				this.next.setNode(index, data);  //返回下一条索引数据
			}
			//这种操作的时间复杂度也是n,因为依然需要进行数据的遍历处理。
		}
		
		//在Node中进行一次判断数据是否存在
		 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);		//继续向下判断
				 }
			 }
			 //由于整个链表没有null数据的存在,所以整个的程序在判断的时候直接使用每一个的节点数据发出equals的方法调用即可。
		 }
		 
		 //在Node中进行数据的删除(非根节点)
		 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);		//向后继续删除
				 }
			 }
			 //完善LinkImpl中子类的remove方法
		 }
	}
	
	//------------------------------以下为Link类中定义的成员-----------------------
	
	private Node root;		//保存根元素
	
	private int count; 	//保存数据的个数
	
	private int foot = 0;		 		//操作数组的脚标
	
	private Object[] returnData; 		//返回的数据保存
	
	
	//------------------------------以下为Link类中定义的方法-----------------------
	
	@Override
	public void add(E e) {	
		if(e == null) {		//方法调用直接结束
			return ; 
		}else {			
			//数据本身是不具有关联特性的,只有Node类有,要想实现关联处理就必须将数据包装在Node类中
			Node newNode = new Node(e);
			
			if(this.root == null) {		//现在没有根节点
				this.root = newNode;		//第一个节点作为根节点
			}else {
				this.root.addNode(newNode);		//将新节点保存在合适的位置
			}
			this.count ++;			//对数据计数
		}
	}
	
	@Override
	public int size() {			//数据统计功能
		return this.count;
	}
	//只是对于数据保存中的一个辅助功能

	@Override
	public boolean isEmpty() {		//判断链表是否为空集合

		//return this.root == null;			//判断根节点是否为空
		
		return this.count == 0; 			//判断计数器是否为空
		
		//使用根节点或者是长度判断其本质相同
	}

	@Override
	public Object[] toArray() {			//将数据以数组的形式返回
		if(this.isEmpty()) {		//空集合
			return null;			//没有数据
		}
		this.foot = 0; 			//脚标清零
		
		this.returnData = new Object[this.count];	//根据已有长度开辟数组长度
		
		this.root.toArrayNode();			//利用Node类进行递归数据获取
		
		return this.returnData;
		//集合的数据一般要返回时肯定要以对象数组的形式返回
		
	}

	@Override
	public E get(int index) {			//引用索引查询数据
		
		if(index >= this.count) {		//索引应该在指定的范围之内
			return null;
		}	//索引数据的获取应该由Node类完成
		
		this.foot = 0;  //重置索引的下标
		
		return this.root.getNode(index);
	}

	@Override
	public void set(int index, E data) {
		if(index >= this.count) {		//索引应该在指定的范围之内
			return ;		//方法结束
		}	//索引数据的获取应该由Node类完成
		
		this.foot = 0;  //重置索引的下标
		this.root.setNode(index, data);
	}

	@Override
	public boolean contains(E data) {		//在LinkImpl子类里面实现此方法
		if(data ==null) {
			return false;	//没有数据
		}
		return this.root.containsNode(data);		//把数据交给Node类判断
	}
	
	@Override				//删除指定数据
	public void remove(E data) {			//在LinkImpl子类里面实现根节点的判断
		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 --;
		}
		//根节点数据删除
		//如果现在根节点并不是要删除的节点,那么就需要进行后续节点判断,
		//但是请一定要注意此时的根节点已经判断完成,判断应该从根节点的下一个开始判断
		//在Node类中追加删除处理
		//删除的逻辑是依靠引用来完成的
	}

	@Override
	public void clean() {				//根据根节点清空集合
		
		this.root = null;		//后续的所有节点都没有了
		
		this.count = 0;			//个数清零
	}
}
public class JavaDemo1 {
	public static void main(String[] args) {
		
		ILink<String> all = new LinkImpl<>();
		
		System.out.println("【数据追加之前】 数据个数:" + all.size() + "、是否为空集合:" + all.isEmpty());
		
		//添加数据
		all.add("Hello");
		all.add("World");
		all.add("fire");
		
		//修改数据
		all.set(1, "世界");
		
		//删除数据
		//all.remove("Hello");				//删除根节点数据
		all.remove("世界");					//删除非根节点数据
		
		//清空集合
		//all.clean();
		
		System.out.println("【数据追加之后】 数据个数:" + all.size() + "、是否为空集合:" + all.isEmpty() );

		//获取链表中的所有数据
		Object[] result = all.toArray();
		if(result != null) {
			for(Object temp : result) {
				System.out.println(temp);
			}
		}
		
		System.out.println("----------------- 数据获取的分割线 --------------------------------------");
		System.out.println(all.get(0));
		System.out.println(all.get(1));
		System.out.println(all.get(4));
		
		System.out.println("----------------- 数据获取的分割线 --------------------------------------");
		System.out.println(all.contains("火"));
		System.out.println(all.contains("Hello"));
	
	}
	
	//这些就是链表的基本功能,当然这只是一个最简单最基础的单向链表实现。
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值