【数据结构】使用Java实现链表类LinkList

目录

  • 链表介绍
  • Java->链表API
  • Java->链表代码
  • 使用链表示例

链表介绍

如果想了解Java实现的顺序表,请戳这里:【数据结构】使用Java实现顺序表类 SeqList

上述文章介绍的顺序表作为数据存储结构有很多优点,最重要的就是顺序表的元素是随机存取的。但是顺序表也有很多缺点,主要的缺点有两个,一是插入和删除的效率低下,因为插入和删除操作需要变化部分元素的位置,时间复杂度是O(n)。二是数据一旦创建就不能改变大小,如果想扩大数组的容量,那么应该创建一个新的更大的新数组,将原数组复制到新数组中。这是十分不方便的。

于是为了解决上述问题,链表就出现了,链表的主要特点是:1、链表的大小是动态的,随着数据的增加或者删除动态改变长度。2、链表的插入、删除十分高效,对于经常需要插入和删除数据的应用,非常适合使用链表作为数据存储结构。

顺序表和链表都属于线性表。

Java->链表API

在看代码之前,我们先看看链表需要完成什么任务,也就是链表的API,这样有助于理解代码,也有助于快速查找自己想实现的功能。

  • LinkList(): 构造函数
  • clear(): 删除整个列表
  • removeAll(): 删除整个列表,调用clear函数来实现
  • getNode(int i): 获得逻辑上i号节点
  • get(int i): 获得逻辑上i号节点的值
  • set(int i,T x): 修改i号节点的值
  • add(int i,T x): 将值为x的节点插入到i号位置
  • add(T key): 在链表尾部插入元素key
  • addBack(T key): 在链表尾部插入元素key,和add(T key)函数的作用一样
  • addFront(T key): 在链表首部插入元素key
  • remove(int i): 删除i号节点,并返回i号节点对应的值
  • remove(): 重载remove,删除头节点
  • removeFront(): 删除链表头节点,与remove()函数同义
  • removeBack(): 删除链表尾节点
  • addSort(T value): 将值value按从小到大的排序方式插入链表
  • sort(): 对链表按照从小到大的顺序进行排序
  • indexOf(int begin,int end,T key): 在索引begin和end之间查找值key,返回逻辑编号
  • search(T key): 功能同indexOf,遍历整个链表,一般不使用,主要用于实现字典
  • contains(T key): 判断链表中是否存在值为key节点
  • toString(): 将链表中的值转换成字符串
  • toArray(): 将链表转换成Object数组
  • toArray(E[] a): 将单链表转化为特定类型的数组,使用了函数泛型
  • iterator(): 返回迭代器对象

Java->链表代码

可能要注意的点:
1、使用代码的时候记得把package改成自己的文件所在的包名。
2、代码中LinkList继承的父类AbsList在这里没有写,因为写代码时,我在顺序表SeqList中实现了AbsList类,这个AbsList默认是default,也就是包访问权限(default修饰的类是包访问的),然后我把SeqList和LinkList两个类放在一个包中了。所以其实LinkList使用的AbsList其实是SeqList类中的AbsList,所以这里就使用的是上篇文章中的SeqList中的AbsList,所以如果想让这个类可以使用,去上面文章中找AbsList类copy一下就好了。或者点击---->传送门也能到达

/*
 * created on April 16 14:40 2019
 * 
 * @author:lhy
 */
package DS;

import java.util.Iterator;


//创建节点类Lnode
class Lnode<T> implements Comparable<Lnode<T>> {
	public T data;	// 节点值
	public Lnode<T> next;	// 保存下个节点

	// Lnode构造函数
	public Lnode(T key) {
		data = key;
		next = null;
	}

	public Lnode(T key, Lnode<T> next) {
		data = key;
		this.next = next;
	}

	// 判断节点值是否相等
	public boolean equals(Object e) {
		Lnode<T> node = (Lnode<T>) e;
		return data.equals(node.data);
	}

	// 实现Comparable的compareTo方法,用于比较链表节点比较大小
	public int compareTo(Lnode<T> e) {
		Comparable<T> x;
		if(data instanceof Comparable) {
			x=(Comparable<T>)data;
			return (int)x.compareTo(e.data);
		}
		else {
			throw new ClassCastException("类型无法比较");
		}
	}
	
	//将该节点的值变成字符串格式
	public String toString() {
		return data.toString();
	}
}

/*
 * LinkList API介绍
 * 
 * LinkList(): 构造函数
 * clear(): 删除整个列表
 * removeAll(): 删除整个列表,调用clear函数来实现
 * getNode(int i): 获得逻辑上i号节点
 * get(int i): 获得逻辑上i号节点的值
 * set(int i,T x): 修改i号节点的值
 * add(int i,T x): 将值为x的节点插入到i号位置
 * add(T key): 在链表尾部插入元素key
 * addBack(T key): 在链表尾部插入元素key,和add(T key)函数的作用一样
 * addFront(T key): 在链表首部插入元素key
 * remove(int i): 删除i号节点,并返回i号节点对应的值
 * remove(): 重载remove,删除头节点
 * removeFront(): 删除链表头节点,与remove()函数同义
 * removeBack(): 删除链表尾节点
 * addSort(T value): 将值value按从小到大的排序方式插入链表
 * sort(): 对链表按照从小到大的顺序进行排序
 * indexOf(int begin,int end,T key): 在索引begin和end之间查找值key,返回逻辑编号
 * search(T key): 功能同indexOf,遍历整个链表,一般不使用,主要用于实现字典
 * contains(T key): 判断链表中是否存在值为key节点
 * toString(): 将链表中的值转换成字符串
 * toArray(): 将链表转换成Object数组
 * toArray(E[] a): 将单链表转化为特定类型的数组,使用了函数泛型
 * iterator(): 返回迭代器对象
 */
public class LinkList<T> extends AbsList<T> implements Iterable<T> {
	//LinkList继承了SeqList中的AbsList抽象类,继承了Iterable接口
	Lnode<T> first=null,last=null; //头指针和尾指针
	Iterator<T> iterator=null; //指向当前节点的迭代器
	//构造函数
	public LinkList() {
		first=last=null;
		length=0;
		this.iterator=new LinkIterator();
	}
	
	//比较器,供内部使用
	private int compare(Lnode<T> a,Lnode<T> b) {
		return a.compareTo(b);
	}
	//删除整个列表
	public void clear() {
		first=last=null;
		length=0;
	}
	
	//删除整个列表,调用clear函数来实现
	public void removeAll() {
		clear();
	}
	
	//获得逻辑上的i号节点
	public Lnode<T> getNode(int i){
		//判断i号节点是否越界
		if(i<0||i>length-1) {
			return null;
		}
		//判断i号是否为头节点(其实可以不用判断)
		if(i==0) {
			return first;
		}
		else {
			Lnode<T> p=first;
			int j=0;
			while (p!=null&&j<i) {
				p=p.next;
				j++;
			}
			return p;
		}
		
	}
	
	//获得i号节点的值,调用getNode完成具体工作
	public T get(int i) {
		Lnode<T> pLnode=getNode(i);
		if(pLnode==null)
			return null;
		else 
			return pLnode.data;
	}
	
	//修改i号节点的值
	public boolean set(int i,T x) {
		Lnode<T> p=getNode(i);
		if(p==null) {
			return false;
		}
		else {
			p.data=x;
			return true;
		}
	}
	
	//将值为x的节点插入到i号位置
	public void add(int i,T x) {
		Lnode<T> p,s;
		int j=i-1;
		s=new Lnode<T>(x,null);
		//在空链表中插入节点s
		if(first==null||length==0) {
			first=s;
			last=s;
		}
		
		//在头节点之前插入节点s,即i=0时
		else if(j<0) {
			s.next=first;
			first=s;
		}
		//在链表尾部插入节点s
		else if(j>=length-1) {
			last.next=s;
			last=s;
		}
		//在链表中间插入节点s
		else {
			p=getNode(j);
			s.next=p.next;
			p.next=s;
		}
		length++;
	}
	
	//重载add()函数,在链表尾部插入元素key
	public void add(T key) {
		add(length,key);
	}
	
	//在链表尾部插入元素key,和add(T key)函数的作用一样
	public void addBack(T key) {
		add(length,key);
	}
	
	//在链表首部插入元素key
	public void addFront(T key) {
		add(0,key);
	}
	
	//删除i号节点,并返回i号节点对应的值,通过调用removeNode实现
	public T remove(int i) {
		Lnode<T> p=removeNode(i);
		if(p!=null)
			return p.data;
		else
			return null;
	}
	
	//核心算法,删除逻辑上的i号节点,内部使用,返回Lnode
	protected Lnode<T> removeNode(int i){
		Lnode<T> p,q;
		//判断链表是否为空
		if(first==null) {
			return null;
		}
		//删除头节点
		if(i==0) {
			p=first;
			first=first.next;
			length-=1;
			return p;
		}
		//删除中间节点或尾节点
		if(i>=1 && i<=length-1) {
			//首先获得i-1位置所在的节点
			p=getNode(i-1);
			//获得i节点位置上的节点
			q=p.next;
			p.next=q.next;
			if(q==last) {
				last=p;
			}
			length--;
			return q;
		}
		return null;
	}
	
	//重载remove,删除头节点
	public T remove() {
		return removeNode(0).data;
	}
	
	//删除头节点,与remove()函数同义
	public T removeFront() {
		return removeNode(0).data;
	}
	
	//删除尾节点
	public T removeBack() {
		return removeNode(length-1).data;
	}
	
	//将值value按从小到大的排序方式插入链表,调用insertOrder实现
	public void addSort(T value) {
		Lnode<T> s=new Lnode(value);
		insertOrder(s);
	}
	
	//有序插入的核心算法
	private void insertOrder(Lnode<T> s) {
		Lnode<T> p1,p2;
		length++;
		//在空链表中插入节点
		if(first==null) {
			first=s;
			last=first;
			return ;
		}
		//小于头节点的值,将节点插入到头节点之前
		if(compare(s, first)<0) {
			s.next=first;
			first=s;
			return ;
		}
		//大于最后一个节点值,将节点在链表尾部插入
		if(compare(s, last)>0) {
			last.next=s;
			last=s;
			return ;
		}
		//被插入的节点p在p1和p2之间,p1在前,p2在后
		p2=first;
		p1=p2;
		while(p2!=null) {
			//s节点比p2节点大时,将p1等于p2,p2后移一个位置,直到s小于等于p2时停止循环,这时s节点的值在p1和p2之间
			if(compare(s, p2)>0) {
				p1=p2;
				p2=p2.next;
			}
			else {
				break;
			}
		}
		s.next=p2;
		p1.next=s;
		return;
	}
	
	//对链表排序
	public void sort() {
		LinkList<T> sl=new LinkList<T>();//创建一个存放有序序列的链表
		Lnode<T> p;
		p=this.removeNode(0);//取出无序链表的头节点
		while(p!=null) {
			sl.addSort(p.data);
			p=this.removeNode(0);
		}
		Lnode<T> q=sl.first;
		while(q!=null) {
			this.add(q.data);
			q=q.next;
		}
	}
	
	//在索引begin和end之间查找值key,返回逻辑编号
	public int indexOf(int begin,int end,T key) {
		Lnode<T> p=getNode(begin);//获取开始节点
		int i=begin;
		while(p!=null&&i<end) {
			if(p.data.equals(key))
				return i;
			p=p.next;
			i++;
		}
		return -1;
	}
	
	//功能同indexOf,一般不使用,主要用于实现字典
	public T search(T key) {
		Lnode<T> p=getNode(0);
		while(p!=null) {
			if(p.data.equals(key)) {
				return p.data;
			}
			p=p.next;
		}
		return null;
	}
	
	//判断链表中是否存在值为key节点
	public boolean contains(T key) {
		if(indexOf(0,length,key)==-1) return false;
		else return true;
	}
	
	//将链表中的值转换成字符串
	public String toString() {
		String string;
		Lnode<T> pLnode;
		pLnode=first;
		string="(";
		while(pLnode!=null) {
			string+=pLnode.data.toString()+" ";
			pLnode=pLnode.next;
		}
		return string+")";
	}
	
	//将链表转换成Object数组
	public Object[] toArray() {
		Object[] a=new Object[length];
		Lnode<T> pLnode=first;
		for(int i=0;i<length;i++) {
			a[i]=pLnode.data;
			pLnode=pLnode.next;
		}
		return a;
	}
	
	//将单链表转化为特定类型的数组,使用了函数泛型
	public <E> E[] toArray(E[] a) {
		if(a.length<length) {
			//创建一个长度为length(和链表长度相等),类型为数组a的元素类型的数组,并强制转换成E[]类型
			a=(E[])java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), length);
		}
		int i=0;
		Object[] result=a;
		Lnode<T> xLnode=this.first;
		for(i=0;i<length;i++) {
			result[i]=xLnode.data;
			xLnode=xLnode.next;
		}
		if(a.length>length) {
			a[length]=null;
		}
		return a;
	}
	
	//返回迭代器对象
	public Iterator<T> iterator(){
		return new LinkIterator();
	}
	
	//内部类,实现迭代器
	private class LinkIterator implements Iterator<T>{
		private int index=0;
		private Lnode<T> current=first;
		public boolean hasNext() {
			//在调用next()之后,index自增,确保index不等于person的长度
			return (index!=length() && current!=null);
		}
		
		public T next() {
			T temp=current.data;
			current=current.next;
			index++;
			return temp;
		}
		
		public int nextIndex() {
			return index++;//先返回index的值,后加1
		}
		public void remove() {
			//未实现本方法
		}
	}
}

使用链表示例

创建一个LinkList链表对象,对LinkList类中各个函数进行实现检验,代码如下:

/*
 * created on April 16 15:40 2019
 * 
 * @author:lhy
 */
package DS;

import java.util.Iterator;

/*
 * LinkList API介绍
 * 
 * LinkList(): 构造函数
 * clear(): 删除整个列表
 * removeAll(): 删除整个列表,调用clear函数来实现
 * get(int i): 获得逻辑上i号节点的值
 * set(int i,T x): 修改i号节点的值
 * add(int i,T x): 将值为x的节点插入到i号位置
 * add(T key): 在链表尾部插入元素key
 * addBack(T key): 在链表尾部插入元素key,和add(T key)函数的作用一样
 * addFront(T key): 在链表首部插入元素key
 * remove(int i): 删除i号节点,并返回i号节点对应的值
 * remove(): 重载remove,删除头节点
 * removeFront(): 删除链表头节点,与remove()函数同义
 * removeBack(): 删除链表尾节点
 * addSort(T value): 将值value按从小到大的排序方式插入链表
 * sort(): 对链表按照从小到大的顺序进行排序
 * indexOf(int begin,int end,T key): 在索引begin和end之间查找值key,返回逻辑编号
 * search(T key): 功能同indexOf,遍历整个链表,一般不使用,主要用于实现字典
 * contains(T key): 判断链表中是否存在值为key节点
 * toString(): 将链表中的值转换成字符串
 * toArray(): 将链表转换成Object数组
 * toArray(E[] a): 将单链表转化为特定类型的数组,使用了函数泛型
 * iterator(): 返回迭代器对象
 */

public class Test_LinkList {
	public static void main(String[] args) {
		LinkList<Integer> linkList=new LinkList<Integer>();
		//对linkList赋值
		for(int i=0;i<10;i++) {
			linkList.add((int)(Math.random()*1000));
		}
		System.out.println("由系统随机数生成的链表为:"+linkList.toString());
		System.out.println("获取7号节点的链表元素为:"+linkList.get(7));
		//将下标为1的节点值修改为2333
		linkList.set(1,2333);
		System.out.println("将1号节点值修改为2333,然后输出:"+linkList.toString());
		//向3号位置插入值为5555的节点
		linkList.add(3,5555);
		System.out.println("向3号节点位置插入值为5555的节点,输出链表:"+linkList.toString());
		//向链表尾部插入值为9999的节点
		linkList.add(9999);
		System.out.println("向链表尾部插入值为9999的节点,输出链表:"+linkList.toString());
		//向链表首部插入值为12345的节点
		linkList.addFront(12345);
		System.out.println("向链表首部插入值为12345的节点,输出链表:"+linkList.toString());
		System.out.println("删除4号节点,返回值为:"+linkList.remove(4));
		System.out.println("删除4号节点后,输出链表"+linkList.toString());
		//删除尾节点
		linkList.removeBack();
		System.out.println("删除尾节点后,输出链表:"+linkList.toString());
		//对linkList进行排序
		linkList.sort();
		System.out.println("排序后的链表为:"+linkList.toString());
		//向链表中有序插入节点值为666的节点
		linkList.addSort(666);
		System.out.println("向链表中有序插入节点值为666的节点后,输出链表:"+linkList.toString());
		System.out.println("查找值为666的节点在链表中位置为:"+linkList.indexOf(0, linkList.length,666));
		System.out.println("判断链表中是否有值为665的节点:"+linkList.contains(665));
		//获得一个链表迭代器对象
		Iterator<Integer> iterator=linkList.iterator();
		//使用迭代器输出链表元素
		System.out.print("使用迭代器输出链表元素:");
		while(iterator.hasNext()) {
			System.out.print(iterator.next()+" ");
		}
		//清空链表
		linkList.clear();
		System.out.println("\n输出清空后的链表:"+linkList.toString());

	}
}

输出:

由系统随机数生成的链表为:(834 738 152 568 863 608 495 975 742 475 )
获取7号节点的链表元素为:9751号节点值修改为2333,然后输出:(834 2333 152 568 863 608 495 975 742 475 )3号节点位置插入值为5555的节点,输出链表:(834 2333 152 5555 568 863 608 495 975 742 475 )
向链表尾部插入值为9999的节点,输出链表:(834 2333 152 5555 568 863 608 495 975 742 475 9999 )
向链表首部插入值为12345的节点,输出链表:(12345 834 2333 152 5555 568 863 608 495 975 742 475 9999 )
删除4号节点,返回值为:5555
删除4号节点后,输出链表(12345 834 2333 152 568 863 608 495 975 742 475 9999 )
删除尾节点后,输出链表:(12345 834 2333 152 568 863 608 495 975 742 475 )
排序后的链表为:(152 475 495 568 608 742 834 863 975 2333 12345 )
向链表中有序插入节点值为666的节点后,输出链表:(152 475 495 568 608 666 742 834 863 975 2333 12345 )
查找值为666的节点在链表中位置为:5
判断链表中是否有值为665的节点:false
使用迭代器输出链表元素:152 475 495 568 608 666 742 834 863 975 2333 12345 
输出清空后的链表:()

你可能感兴趣的:(【数据结构】使用Java实现链表类LinkList)