Java关于简易数据单链表的实现

本文就链表在Java平台上的定义与应用,欢迎评论讨论~

       关于链表想必大家都很了解了,由于其在插入等方面具有极小的时间复杂性而被广泛应用。链表由节点组成,单链表中每个节点只包括两个部分:一个是存储数据元素,另一个是存储下一个节点是什么节点。相比于线性表顺序结构,链表由与不必须按顺序存储,在插入的时候可以达到O(1)的复杂度,比线性表和顺序表快很多。但是查找一个节点或是访问特定编号的节点则需要O(n)的时间,而线性表和顺序表相应的时间复杂度分别是O(logn)和O(1)。

       在Java中,由于没有指针,因此在定义节点类的时候,我们需要定义它的两个属性,即数据(data)与后节点(nextnode),而定义节点必须要给定节点的值,否则节点无意义,因此重构一下构造函数,具体的节点类代码如下:

public class Node<E> {
	public E data;
	public Node<E> nextnode;
	public Node(E data){
	this.data=data;
	}
}

       节点类的两个属性我并没有作封装处理,方便后面定义链表方法时对节点属性进行修改。类名后面加<字母>,表示泛型,它可以根据我们建立它时定义的数据类型表示不同的数据类型,如Node<String> node1 = new Node<String>("a")表示node1节点里的数据类型是字符串型。

       通常我们应用链表时,主要应用的就是对数据处理最常用的几种方法,即“增删改查”,因此定义一个链表类时至少要有这几种最简单方法。首先我们建立链表类,需要用到的属性有链表的长度size(节点数)、头结点head和尾节点tail:

public class MyLinkList<E> {
	private int size;
	private Node<E> head, tail;
}

       为了防止调用类时从外部改变类的属性,要对属性进行封装,将其全部定义为private类型。当我们未加说明时,size默认值为0,head和tail默认null。

       接下来我们定义几个简单的方法。首先要从外部调用链表长度,需要定义调用函数:

public int Size() {
	return size;
}

       接下来定义往链表里添加新的数据的方法。具体的方法就是新建一个节点保存新的数据,将尾节点的后节点定义成这个新的节点即可。值得注意的是,如果尾节点尚未定义时(即为null时),调用其后节点会出现空指针异常,因此考虑特殊情况:链表为空时,将新的节点定义成链表的头结点和尾节点。添加成功后,链表的长度增加1。具体代码如下:

 
public void add(E e) {
	Node<E> temp = new Node<E>(e);
	//链表为空,将新的节点定义成链表的头结点和尾节点
	if (size == 0) {
		head = temp;
		tail = head;
	} 
	//链表不为空,让尾节点的后节点为新节点
	else {
		tail.nextnode = temp;
		tail = temp;
	} 
	size++;
}

       然后定义另一个比较简单的方法:查找。和顺序表和线性表不同,链表不能用类似于下标一类的指标直接找到指定数据,因此正如第一段所说,链表的查找算法复杂度较高,需要我们遍历一遍所有的数据,从头结点开始,取指定数目个后节点后得到指定位置的节点,返回对应的数据。注意考虑溢出异常。具体代码如下:

 
public E get(int index) {
	//预防溢出异常
	if (index < 0 || index >= size)  {
		System.out.println("超出范围!");
		//有返回值,需要return一个null
		return null;
	}
	//定义一个节点来存储查找过程中遍历到的节点
	Node<E> temp = head;
	for (int i = 0; i < index; i++) {
		temp = temp.nextnode;
	}
	return temp.data;
}

       接下来定义删除和修改方法,因为两种方法用到的思路相近,这里我连起来一起讲。首先定义清空,这个方法很简单,由于Java删除可以不用释放空间,因此我们直接将size置零即可:

public void removeAll() {
	size = 0;
}

       接下来定义删除或修改指定位置节点信息。由于不能通过下标直接导到指定位置,我们还得像查找一样遍历链表中所有节点。与查找不同的是,我们不光要找到指定位置,还要修改指定位置的节点,而用另一个节点(temp)来存储查找过程中遍历到的节点时,对temp的修改并不会影响链表中节点的属性,因此我们要用temp获得要修改的节点的前节点,用前节点的后节点属性来得到要修改的节点,再修改即可。再删除中要改的节点是指定位置的前节点,而在修改中要改的节点就是指定位置节点。在遍历的过程中同样要注意溢出异常,而且不止像查找中的后溢出,因为要获得前节点,也有可能会造成前溢出,需要考虑的情况比较多。

       执行删除时,要修改指定index节点的前节点,则需要查找到指定index节点的前节点的前节点。需要考虑前溢出的特殊情况有index=0和index=1两种情况。具体删除代码如下:

public void remove(int index) {
	//预防溢出异常
	if (index < 0 || index >= size) {
		System.out.println("超出范围!");
		//没有返回值,直接return即可
		return;
	} else {
		//删除的是头结点,直接让头结点等于其后节点即可
		if (index == 0) {
			head = head.nextnode;
		}
		//删除的是头结点的后节点,让头结点的后节点为头结点的后节点的后节点即可
		else if (index == 1) {
			head.nextnode = head.nextnode.nextnode;
		} else {
			//定义一个节点来存储查找过程中遍历到的节点
			Node<E> temp = head;
			for (int i = 0; i < index - 2; i++) {
				temp = temp.nextnode;
			}
			//删除的是尾节点,直接让尾节点的前节点的后节点为null即可
			if (index == size) {
				temp.nextnode.nextnode = null;
			}
			//删除的是中间的节点,让其前节点的后节点为其后节点即可
			else {
				temp.nextnode.nextnode = temp.nextnode.nextnode.nextnode;
			}
		}
		size--;
	}
}

       执行修改时,直接修改指定index的节点即可,即遍历到指定index的前节点即可,只需要考虑index=0的特殊情况导致的前溢出。具体修改代码如下:

 
public void change(int index, E e) {
	//预防溢出异常
	if (index < 0 || index >= size) {
		System.out.println("超出范围!");
		return;
	} else {
		//修改的是头结点,直接重定义节点的data属性即可
		if (index == 0)
			head.data = e;
		else {
			//定义一个节点来存储查找过程中遍历到的节点
			Node<E> temp = head;
			for (int i = 0; i < index - 1; i++) {
				temp = temp.nextnode;
			}
			//重定义指定节点的data属性
			temp.nextnode.data = e;
		}
	}
}

       以上便是我对链表的Java编程思路与代码,可以直接实例化调用使用。简单的调用小程序代码如下:

 
public class test {
	public static MyLinkList<Integer> linklist = new MyLinkList<Integer>();
	//实例化一个链表类对象,保存的类型为整形。方括号里必须使用类名,int型对应的类是Integer类
	public static void main(String[] args){
		System.out.print("原链表:");
		//保存10个数进链表
		for (int i=0;i<10;i++){
			linklist.add(i);
			System.out.print(linklist.get(i)+" ");
		}
		linklist.remove(0);//删除头节点
		linklist.remove(3);//删除中间节点
		linklist.remove(linklist.Size()-1);//删除尾节点
		System.out.println();
		System.out.print("删除后:");
		for (int i=0;i<linklist.Size();i++)
			System.out.print(linklist.get(i)+" ");
		linklist.change(0, 10);//修改头节点
		linklist.change(3, 11);//修改中间节点
		linklist.change(linklist.Size()-1, 12);//修改尾节点
		System.out.println();
		System.out.print("修改后:");
		for (int i=0;i<linklist.Size();i++){
			System.out.print(linklist.get(i)+" ");
		}
	}
}

       执行代码后,获得的结果如下:

 

       原链表:0 1 2 3 4 5 6 7 8 9 

       删除后:1 2 3 5 6 7 8 

       修改后:10 2 3 11 6 7 12 

 

       至此,链表的简单功能和应用已介绍完毕,如有不足望指出,谢谢~

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值