java数据结构与算法学习_链表

链表

本质是一个动态的对象数组,它可以实现若干个对象的存储。

  • 传统的数组应用有限,最大的缺点在于长度的固定
  • 利用引用的逻辑关系来实现类似于数组的数据处理操作,以一种保存"多"方数据的形式,实现数组类似的功能

思想分析
链表的每个节点作为一个类,该类需要有存储数据的数据,有存储下一个节点引用的节点区。

链表本身可以作为一个类,存放包括增加数据,删除数据等各种链表操作。

保存的数据与链表类无关联,与Node节点类有关联,
而链表类与Node节点类也有关联,因此将Node类作为链表类的内部类,可以实现数据和链表类的关联。

代码实现

(1)定义一个链表类的标准接口

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 int isExist(E data);//判断数据是否存在 存在返回下标,不存在返回-1
 	public void remove(E data);//删除指定数据内容
 	public void clean();//链表数据整体清空
}

(2)定义一个实现接口的链表类

class LinkImpl<E> implements ILink<E>{
	//Node作为内部类,作为链表类的成员
	private class Node{
  	private E data; // 保存数据
  	private Node next; // 保存下一个引用
  	public Node(E data) { //Node构造函数必须需要一个数据,否则Node实例化对象无意义
   		this.data = data;
  	}
  	//链表类的其他属性成员
  	private Node root; // 保存根元素(即头结点,指向第一个有效链表节点)
	private int count; //记录有效节点个数
 	private Object[] returnData; //将链表转化为数组
 	private int foot; //脚标,可以理解为记录第几个节点
}

(3)添加数据方法实现

//定义在链表类中
@Override
public void add(E e) {
	if(e == null) { // 保存的数据为空
   		return; 
  	}
  	//数据有效时,创建新节点
  	Node newNode = new Node(e); // 创建一个新节点
  	if(this.root == null) {
  		this.root = newNode; //如果根节点为空,将新节点赋值给根节点
  	}
  	else {
   		this.root.addNode(newNode); //利用递归遍历找到最后一个有数据节点
  	}
  	this.count++; //每次增加一个节点,count++
 }

//定义在内部类Node类中
  //通过递归将新节点保存在合适的位置
  //第一次调用:LinkImpl.root.addNode(),this = LinkImpl.root 判断root.next是否为空,也就是判断root的下一个节点如果为空,就将新节点挂在root上(root.this = newNode)
  //第二次调用:root.next.addNode() this = root.next 判断root.nex.next是否为空。
  public void addNode(Node newNode) {
   	if(this.next == null) {
    		this.next = newNode;
   	}
   	else {
    		this.next.addNode(newNode);
   	}
}

(4)获得链表长度

//定义在链表类中
 @Override
 public int size() {
  	return this.count;
 }

(5)判断链表是否为空

 @Override
 public boolean isEmpty() {
  	return this.count == 0;
 }

(6)将链表转为数组返回

//定义在链表类中
 @Override
 public Object[] toArray() {
  	if(isEmpty()) {
   		System.out.println("数据为空");
  	 	return null;
  	}
  	else {
   		this.foot = 0; //数组脚标重置
   		this.returnData = new Object[this.count];  //根据有效数据创建数组
   		this.root.toArrayNode();  //利用Node进行递归数据获取
   		return this.returnData;
  	}
 }

//定义在内部类Node类中
  public void toArrayNode(){
  		//先将root的值赋给数组的[0],如果next不为null,说明还有下一个节点,递归调用重复赋值操作。
   		LinkImpl.this.returnData[LinkImpl.this.foot++] = this.data;
   		if(this.next != null) {
   		this.next.toArrayNode();
   	}
  }

(7)根据下标获取链表数据

//定义在链表类中 ,index从0开始,和数组下标一致
@Override
 //数据获取一个数据的时间复杂度为1,链表获取数据的时间复杂度为n
 public E get(int index) {
  	this.foot = 0;//脚标先清0
  	if(index < 0 || index >= count) {
   		System.out.println("下标错误");
   		return null ;
  	}
  	else {
   		return this.root.traverseNode(index);//有Node类判断index是第几个节点
     }
 }
//定义在内部类Node中
public E traverseNode(int index) {
	if(index == LinkImpl.this.foot) {
     		return this.data;
    	}
   	else {
     		LinkImpl.this.foot++;//判断下一个节点,因此脚标+1
        	return this.next.traverseNode(index);
    }
}

(8)根据脚标改变数据

//定义在链表类中
 @Override
 public void set(int index, E data) {
	this.foot = 0; //脚标清0
  	if(index < 1 || index > count) {
   		System.out.println("下标错误");
   	return;
  	}
  	else {
   		this.root.setNode(index,data);
  	}
}

//定义在内部类Node中,同获取数据一致
  public void setNode(int index, E data) {
   	if(index == LinkImpl.this.foot) {
    		this.data = data;
   	}
   	else{
    		LinkImpl.this.foot++;
    		this.next.setNode(index, data);
   	}
 }

(9)判断一个数据是否存在,返回脚标

//定义在链表类中
 @Override
 public int isExist(E data) {
  	if(isEmpty()) {
   		System.out.println("链表为空");
   		return -1;
  	}
  	else {
   		this.foot = 0;
   		return this.root.ExistNode(data);
  	}
}

//定义在内部类Node中
public int ExistNode(E data) {
   	if(this.data == data) {
    		return LinkImpl.this.foot;
   	}
  	else {
    		LinkImpl.this.foot++;
    		return this.next.ExistNode(data);
   	}
}

(9)传入一个数据,判断是否存在,存在即删除
要删除的节点的前一个节点的next指向要删除节点的next即可

//定义在链表类中
//考虑两种情况 
 //1 要删除的是根节点数据
 //2 要删除的不是根节点数据
public void remove(E data) {
  	if(this.isExist(data) == 0) { //先判断数据是否存在,如果存在返回的是0,这说明要删除的是根节点,直接让根节点指向根节点的下一个节点。
   		this.root = this.root.next;
  	}
  	//如果不存在该数据,则返回-1
  	else if(this.isExist(data) == -1){
   		return;
  	}
  	//要删除的不是根节点数据,传入要删除节点的前一个节点引用
  	else{
   		this.root.next.removeNode(this.root, data);
  	}
  	this.count--; //有效数据-1
 }

//定义在内部类Node中
  //第一次使用,previous是root,this是root.next
  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); //向后继续删除
    		}
   	}
   }

(10)清空链表

 @Override
 public void clean() {
  	this.root = null;
  	count = 0;
 }

测试代码

public class ListDemo {
 	public static void main(String[] args) {
  		ILink<String> all = new LinkImpl<String>();
  		System.out.println(all.size());
  		all.add("hello");
  		all.add("world");
 		all.add("java");
  		System.out.println(all.size()); //3
  		Object[] result = all.toArray();

		//遍历数组
  		for(Object temp : result) {
   			System.out.println(temp);
  		}
  		
		System.out.println("------------根据下标获得数据--------");
		System.out.println(all.get(2));
		System.out.println("---------替换数据------------");
		all.set(2, "nihao");
		System.out.println(all.get(2));
		System.out.println("---------是否存在------------");
		System.out.println(all.isExist("world"));
  		System.out.println("---------删除根节点------------");
  		all.remove("world");
  
  		Object[] result2 = all.toArray();
  		for(Object temp : result2) {
   			System.out.println(temp);
  		}
 	}
}

java实现链表结构与C语言不用,隐藏了指针没有办法用循环遍历节点,因此使用递归方法遍历节点。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值