`

《Java数据结构和算法》学习笔记(4)——链表

阅读更多
每篇笔记都会附上随书的Applet演示程序,有算法看不明白的,可以下载Applet运行起来(直接打开html文件即可),可以很容易地看清楚算法的每一步。
链表

链表简单的说就是个对象链,一个对象里包含另一个对象的引用。链表类的成员只有一个:这个链表的第一个链接点。只要通过第一个链接点,就能得到链表里所有的其它链接点。下面是一个链表的实现:
package dsaa.array;
/**
 * @(#)LinkList.java 2008-12-27 下午07:02:17
 * 
 * @author Qiu Maoyuan
 * Link List
 */
public class LinkList<E> {

	private Link first;
	
	public void addFirst(E value){
		Link newElement = new Link(value);
		newElement.next = first;
		first = newElement;
	}
	
	public E getFirst(){
		if(isEmpty())
			throw new IllegalStateException("Linklist is empty");
		return first.element;
	}
	
	public boolean isEmpty(){
		return first == null;
	}
	
	public E removeFirst(){
		if(isEmpty())
			throw new IllegalStateException("Linklist is empty");
		E value = first.element;
		first = first.next;
		return value;
	}
	
	public boolean delete(E element){
		if(isEmpty())
			return false;
		Link current = first;
		Link previous = first;
		while(!element.equals(current.element)){
			if(current.next == null){
				return false;
			} else {
				previous = current;
				current = current.next;
			}
		}
		if(current == first)
			first = first.next;
		else
			previous.next = current.next;
		return true;
	}

	@Override
	public String toString(){
		StringBuilder buffer = new StringBuilder();
		Link current = first;
		while(current != null){
			buffer.append(current.toString());
			current = current.next;
		}
		return buffer.toString();
	}
	
	private class Link{
		
		private E element;
		
		private Link next;
		
		public Link(E element){
			this.element = element;
		}

		@Override
		public String toString(){
			return "[" + element.toString() + "]";
		}
	}
}

双端链表与普通链表的区别就是:双端链表多了一个对链表尾部元素的引用以及一个用于在尾部增加链拉点的addLast()方法。
package dsaa.array;
/**
 * @(#)LinkList.java 2008-12-27 下午07:02:17
 * 
 * @author Qiu Maoyuan
 * Link List
 */
public class LinkList<E> {

	private Link first;
	private Link last;
	
	public void addFirst(E value){
		Link newElement = new Link(value);
		if(isEmpty()){
			last = newElement;
		}
		newElement.next = first;
		first = newElement;
	}
	
	public E getFirst(){
		if(isEmpty())
			throw new IllegalStateException("Linklist is empty");
		return first.element;
	}
	
	public boolean isEmpty(){
		return first == null;
	}
	
	public E removeFirst(){
		E value = getFirst();
		first = first.next;
		return value;
	}
	
	public boolean delete(E element){
		if(isEmpty())
			return false;
		Link current = first;
		Link previous = first;
		while(!element.equals(current.element)){
			if(current.next == null){
				return false;
			} else {
				previous = current;
				current = current.next;
			}
		}
		if(current == first)
			first = first.next;
		else
			previous.next = current.next;
		if(current == last)
			last = previous;
		return true;
	}
	
	public void addLast(E value){
		Link newElement = new Link(value);
		if(isEmpty()){
			first = newElement;
		}else{
			last.next = newElement;
		}
		last = newElement;
	}

	@Override
	public String toString(){
		StringBuilder buffer = new StringBuilder();
		Link current = first;
		while(current != null){
			buffer.append(current.toString());
			current = current.next;
		}
		return buffer.toString();
	}
	
	private class Link{
		
		private E element;
		
		private Link next;
		
		public Link(E element){
			this.element = element;
		}

		@Override
		public String toString(){
			return "[" + element.toString() + "]";
		}
	}
}

链表的效率
链表在表头的插入和删除操作都很快,只需要O(1)级别的时间。删除指定链接点的操作和数组一样,需要O(N)级别的时间。但是链表仍然比数组快得多,因为不需要像数组那样删除元素后还要移动元素。
链表比数组优越的另一个地方是:链表需要多少长度就可以分配多少长度,而数组的长度在创建时就是固定了的。尽管有Vector或者ArrayList这样可扩展长度的数组,但是它们只允许以固定的增量扩展,在内存的使用效率上还是比链表低。
下面是一个用链表实现的栈
package dsaa.array;

import java.util.EmptyStackException;

/**
 * @(#)LinkStack.java 2008-12-28 上午04:21:08
 * 
 * @author Qiu Maoyuan
 * Stack
 */
public class LinkStack<E> {
	
	private LinkList<E> stackLinkList;
	
	public LinkStack(){
		stackLinkList = new LinkList<E>();
	}
	
	public void push(E value){
		stackLinkList.addFirst(value);
	}
	
	public E peek(){
		E element = null;
		try{
			element = stackLinkList.getFirst();



		}catch(IllegalStateException ex){
			throw new EmptyStackException();
		}
		return element;
	}
	
	public E pop(){
		E element = null;
		try{
			element = stackLinkList.removeFirst();
		}catch(IllegalStateException ex){
			throw new EmptyStackException();
		}
		return element;
	}
	
	public boolean isEmpty(){
		return stackLinkList.isEmpty();
	}
}

链表实现的队列
package dsaa.array;
/**
 * @(#)LinkQueue.java 2008-12-28 上午04:27:07
 * 
 * @author Qiu Maoyuan
 * Link Queue
 */
public class LinkQueue<E> {
	
	private LinkList<E> queueLinkList;
	
	public LinkQueue(int size){
		queueLinkList = new LinkList<E>();
	}
	
	public E peek(){
		return queueLinkList.getFirst();
	}
	
	public void insert(E value){
		queueLinkList.addLast(value);
	}
	
	public E remove(){
		return queueLinkList.removeFirst();
	}
	
	public boolean isEmpty(){
		return queueLinkList.isEmpty();
	}
}

一写完代码发现链表是个好东西,Stack和Queue的实现变得很容易
在写这些笔记的代码时,我都采用TDD的方式,上一章写Stack和Queue的测试代码完全可以直接用在LinkStack和LinkQueue上。。。针对接口测试……嗯,也许一开始就应该对接口进行测试。

有序链表,就不多说了,看名字就知道意思了 插入元素的实现就是先遍历找准位置,然后把新增链接点的next指向previous的next(也就是current),再把previous的next指向新增的这个链接点。然后注意处理一下特殊情况(比如正好插入位置是在链表头)。有序链表的效率还是比数组高:插入时和数组一样只需要O(N)级别的查找时间,但插入之前不需要像数组那样移动元素。
下面是一个有序链表的实现:
package dsaa.array;
/**
 * @(#)SortedLinkList.java 2008-12-28 下午02:16:35
 * 
 * @author Qiu Maoyuan
 * Sorted LinkList
 */
public class SortedLinkList<E extends Comparable<E>> {

	private Link first;
	
	public void add(E value){
		Link newElement = new Link(value);
		if(isEmpty()){
			first = newElement;
		}else{
			Link current = first;
			Link previous = null;
			while(!endOfLink(current) && current.element.compareTo(value)>=0){
				previous = current;
				current = current.next;
			}
			if(!endOfLink(current)){
				newElement.next = current;
			}
			if(current == first)
				first = newElement;
			else
				previous.next = newElement;
		}
	}
	
	public E getFirst(){
		if(isEmpty())
			throw new IllegalStateException("Linklist is empty");
		return first.element;
	}
	
	public boolean isEmpty(){
		return first == null;
	}
	
	public E remove(){
		E value = getFirst();
		first = first.next;
		return value;
	}
	
	public boolean delete(E element){
		if(isEmpty())
			return false;
		Link current = first;
		Link previous = first;
		while(!element.equals(current.element)){
			if(endOfLink(current.next)){
				return false;
			} else {
				previous = current;
				current = current.next;
			}
		}
		if(current == first)
			first = first.next;
		else
			previous.next = current.next;
		return true;
	}

	private boolean endOfLink(Link link) {
		return link == null;
	}

	@Override
	public String toString(){
		StringBuilder buffer = new StringBuilder();
		Link current = first;
		while(current != null){
			buffer.append(current.toString());
			current = current.next;
		}
		return buffer.toString();
	}
	
	private class Link{
		
		private E element;
		
		private Link next;
		
		public Link(E element){
			this.element = element;
		}

		@Override
		public String toString(){
			return "[" + element.toString() + "]";
		}
	}
}

给有序列表添加一个简单的带数组参数的构造方法,就可以用这个链表给一个数组进行排序,这种排序方法叫表插入排序法。表插入排序法的效率比数组插入排序法(第2章的插入排序法)还高些:构造有序链表时平均需要N^2/2次比较,时间复杂度为O(N^2),跟数组插入排序法花在“比较”上的时间级别一样,但数组插入排序法花在复制上的时间级别为O(N),而表插入排序法花在复制上的时间级别为O(N)。
package dsaa.array;
/**
 * @(#)SortedLinkList.java 2008-12-28 下午02:16:35
 * 
 * @author Qiu Maoyuan
 * Sorted LinkList
 */
public class SortedLinkList<E extends Comparable<E>> {

	private Link first;
	
	public SortedLinkList(){}
	
	public SortedLinkList(E[] array){
		for(E element : array){
			add(element);
		}
	}
	
	public void add(E value){
		Link newElement = new Link(value);
		if(isEmpty()){
			first = newElement;
		}else{
			Link current = first;
			Link previous = null;
			while(!endOfLink(current) && current.element.compareTo(value)>=0){
				previous = current;
				current = current.next;
			}
			if(!endOfLink(current)){
				newElement.next = current;
			}
			if(current == first)
				first = newElement;
			else
				previous.next = newElement;
		}
	}
	
	public E getFirst(){
		if(isEmpty())
			throw new IllegalStateException("Linklist is empty");
		return first.element;
	}
	
	public boolean isEmpty(){
		return first == null;
	}
	
	public E remove(){
		E value = getFirst();
		first = first.next;
		return value;
	}
	
	public boolean delete(E element){
		if(isEmpty())
			return false;
		Link current = first;
		Link previous = first;
		while(!element.equals(current.element)){
			if(endOfLink(current.next)){
				return false;
			} else {
				previous = current;
				current = current.next;
			}
		}
		if(current == first)
			first = first.next;
		else
			previous.next = current.next;
		return true;
	}

	private boolean endOfLink(Link link) {
		return link == null;
	}

	@Override
	public String toString(){
		StringBuilder buffer = new StringBuilder();
		Link current = first;
		while(current != null){
			buffer.append(current.toString());
			current = current.next;
		}
		return buffer.toString();
	}
	
	private class Link{
		
		private E element;
		
		private Link next;
		
		public Link(E element){
			this.element = element;
		}

		@Override
		public String toString(){
			return "[" + element.toString() + "]";
		}
	}
}

可以这样使用这个有序链表:
	/**
	 * 表插入排序法
	 * @param <T>
	 * @param array
	 */
	@SuppressWarnings("unchecked")
	public static <T extends Comparable<? super T>> void listInsertionSort(T[] array){
		SortedLinkList linkList = new SortedLinkList(array);
		T[] newArray = (T[])new Comparable[array.length];
		for(int i=0; i<newArray.length; i++){
			newArray[i] = (T)linkList.remove();
		}
		array = newArray;
	}

[Java的泛型真恶心...... ]
还有一种链表叫双向链表,就是既可以向前遍历,又可以向后遍历的链表。这种链表的链接点内部有两个成员:previous和next。插入和删除的时候需要多操作一个引用。
分享到:
评论

相关推荐

    数据结构实验——链表

    数据结构实验——链表 数据结构实验——链表

    Java数据结构和算法(第二版).zip

    《Java数据结构和算法》(第2版)介绍了计算机编程中使用的数据结构和算法,对于在计算机应用中如何操作和管理数据以取得最优性能提供了深入浅出的讲解。全书共分为15章,分别讲述了基本概念、数组、简单排序、堆和...

    java数据结构和算法

    java 数据结构和算法, 排序算法, 数组,链表,二叉树

    Java数据结构和算法

    (1)数据结构与算法概念解析 (2)数据结构之数组 (3)数据结构之栈 (4)数据结构之队列 (5)数据结构之链表 (6)数据结构之二叉树 (7)数据结构之霍夫曼树 (8)数据结构之红黑树(一)——基础分析 ...

    java数据结构与算法.pdf

    包含了各种数据结构和算法(java)的实现方式和详解(图解),包括单双链表、环形链表(约瑟夫问题)、栈、后缀表达式、中缀表达式转后缀表达式、迷宫问题、八大排序算法、多种查找算法、哈希表、二叉树实现以及操作...

    java数据结构和算法.(第二版)

    《Java数据结构和算法》(第2版)介绍了计算机编程中使用的数据结构和算法,对于在计算机应用中如何操作和管理数据以取得最优性能提供了深入浅出的讲解。全书共分为15章,分别讲述了基本概念、数组、简单排序、堆和...

    Java数据结构和算法笔记.doc

    Java数据结构和算法 第0讲 综述 参考教材:Java数据结构和算法(第二版),[美] Robert lafore 1. 数据结构的特性 "数据结构"优点 "缺点 " "数组 "插入快;如果知道下标,可以非常快"查找慢,删除慢,大小固定" " ...

    Java数据结构与算法编程基础全面系统教程

    JAVA数据结构与算法课程第05课双端链表和双向链表.mp4JAVA数据结构与算法课程第06课递归的应用.mp4JAVA数据结构与算法课程第07课递归的高级应用.mp4JAVA数据结构与算法课程第08课希尔排序.mp4JAVA数据结构与算法课程...

    算法和数据结构——链表.pdf

    #资源达人分享计划#

    Java数据结构与算法

    介绍了计算机编程中使用的...《Java数据结构和算法》(第2版)提供了学完一门编程语言后进一步需要知道的知识。本书所涵盖的内容通常作为大学或学院中计算机系二年级的课程,在学生掌握了编程的基础后才开始本书的学习。

    基本数据结构和算法学习笔记

    基本数据结构和算法学习笔记(持续更新中...)。慢慢滴~ 包括基本的数据结构和算法,如数组、链表、字符串、树、图、dp等等... 还有很多算法刷题代码,目前我和我女朋友一起开发。欧拉拉~欧拉拉~.zip

    Java数据结构和算法中文第二版

    Java数据结构和算法介绍了计算机编程中使用的数据结构和算法,对于在计算机应用中如何操作和管理数据以取得最优性能提供了深入浅出的讲解。全书共分为15章,分别讲述了基本概念、数组、简单排序、堆和队列、链表、...

    java数据结构和算法实践

    以下是关于Java数据结构和算法的一些介绍: Java作为一种流行的编程语言,在数据结构和算法的实现方面有着广泛的应用。数据结构指的是在计算机中组织和存储数据的方式,算法则是明确定义的解决特定问题的规则和步骤。...

    Data Structure and Algorithm Learning —— 数据结构与算法学习笔记.zip

    数据结构是计算机存储、组织数据的方式,它涉及到数据的逻辑结构、物理结构以及对数据的基本操作。...通过对数据结构的理解和运用,以及对算法的学习和研究,可以帮助我们更有效地解决实际问题,提升编程能力。

    数据结构——循环链表的操作2

    数据结构——循环链表的操作1 数据结构——循环链表的操作1 数据结构——循环链表的操作1 数据结构——循环链表的操作1

    数据结构——循环链表的操作1

    数据结构——循环链表的操作1 数据结构——循环链表的操作1 数据结构——循环链表的操作1 数据结构——循环链表的操作1

    数据结构——链表的实现

    数据结构课程的链表类的C++实现,搜索,删除,插入,查找等函数

Global site tag (gtag.js) - Google Analytics