`
zhouYunan2010
  • 浏览: 207662 次
  • 性别: Icon_minigender_1
  • 来自: 湖南
社区版块
存档分类

用动态数组模拟双向循环链表

阅读更多
简单来说其实使用数组模拟LinkedList。同LinkedList的操作基本相似。
基本原理为:数组存放Entry对象,包含数据部分,指针部分(数组下标)
添加,删除基本操作改变指针。数组包含两个链表,一个备用链表(空数据,仅含指针)与
实际存放数据的链表(即保存存入的数)。添加先从备用链表中获取一个空闲节点,
移除把节点重新放入备用链表等待获取。采用ArrayList的数组自动扩张的方法。
具体代码如下:
package com.utils;

import java.util.AbstractSequentialList;
import java.util.Arrays;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.List;
import java.util.ListIterator;
import java.util.NoSuchElementException;




/**
 * 用数组模拟双向循环列表
 */
public class ArrayLinkedList<E> extends AbstractSequentialList<E> implements 
		List<E>{
	
	//空闲链表头结点,索引为0的位置设置头结点
	private transient Entry<E> freeHeader;	
	//实际链表头结点,索引为1的位置设置头结点
	private transient Entry<E> header;
	private transient int size = 0;		//数组中存储的Entry对象个数
	
	private transient Object[] data;	//Object数组,因为java不支持泛型数组,即Entry<E>[] data = new Entry<E>[10] //error
										//遍历时进行强制类型转换即可
	/**
	 * 构造方法一,初始化化数组 
	 */
	public ArrayLinkedList(int initialCapacity) {
		super();
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        initArray(initialCapacity);
	}
	
	/**
	 * 构造方法二,构造含有指定集合c中所有元素的链表
	 * 具体见addAll方法
	 */
	public ArrayLinkedList(Collection<? extends E> c) {
		this();
		addAll(c);
	}
	
	/**
	 * 默认数组长度
	 */
	public ArrayLinkedList(){
		this(10);
	}
	
	/**
	 * 初始化数组.
	 * 除header,其他都是备用链表的元素
	 * 实际链表此时为空。数据项都为空
	 */
	private void initArray(int initialCapacity) {
		data = new Object[initialCapacity];
		for(int i = 0 ;i < data.length;i++){
			if(freeHeader==null){
				freeHeader = new Entry<E>(null, i, i+2, data.length-1);
				data[i] = freeHeader;
				continue;
			}
			if(header == null){
				header = new Entry<E>(null, i, i, i);	//初始实际链表头结点,此链表为空
				data[i] = header;
				continue;
			}
			Entry<E> e = new Entry<E>(null, i, 
					i+1==data.length ? 0 : i+1, i-1 == 1 ? 0 : i-1);
			data[i] = e;
		}
	}
	
	/**
	 * 获取一个备用链表的节点
	 * */
	public int getFreeNode(){
		int i = freeHeader.next;
		if(i > 0 ){
			Entry e = (Entry)data[i];
			freeHeader.next = e.next;
			((Entry)data[e.next]).pre = freeHeader.cur;
		}
		return i;
	}
	
	/**
	 * 将空闲节点回收到备用链表
	 * */
	public void free(Entry<E> e){
		e.next  = freeHeader.next;
		e.pre = freeHeader.cur;
		freeHeader.next = e.cur;
		e.element = null;
	}
	
	public void ensureCapacity(int minCapacity) {
		modCount++;
		int oldCapacity = data.length;
		if (minCapacity > oldCapacity) {
			int newCapacity = (oldCapacity * 3) / 2 + 1;
			if (newCapacity < minCapacity)
				newCapacity = minCapacity;
			data = Arrays.copyOf(data, newCapacity);
			//初始化增加的节点
			for(int i = oldCapacity; i < newCapacity;i++){
				Entry<E> e = new Entry<E>(null, i, 
						i+1==data.length ? 0 : i+1, i-1 == 1 ? 0 : i-1);
				data[i] = e;
				//修改备用头结点
				freeHeader.pre = newCapacity - 1;
				freeHeader.next = oldCapacity;
			}
		}
	}

	public E getFirst(){
		if(size == 0)
			throw new NoSuchElementException();
		return ((Entry<E>)data[header.next]).element;
	}
	
	public E getLast(){
		if(size == 0)
			throw new NoSuchElementException();
		return  ((Entry<E>)data[header.pre]).element;
	}
	
	/**
	 * 移除第一个元素,具体见remove()方法
	 */
	public E removeFirst() {
		return remove((Entry<E>)data[header.next]);
	}

	/**
	 * 移除最后一个元素
	 */
	public E removeLast() {
		return remove((Entry<E>)data[header.pre]);
	}
	
	/**
	 * 
	 * 在链表开始位置添加一个元素
	 * 详情见addBefore()
	 */
	public void addFirst(E e) {
		addBefore(e, (Entry<E>)data[header.next]);
	}

	/**
	 * 在链表最后位置添加一个元素
	 * 详情见addBefore()
	 */
	public void addLast(E e) {
		addBefore(e, header);
	}
	
	/**
	 * 查看链表是否包含元素o
	 * 详细见indexOf()方法
	 */
	public boolean contains(Object o) {
		return indexOf(o) != -1;
	}
	
	/**
	 * 
	 * 返回o第一次出现的位置,如果在List中找不到o返回-1
	 */
	public int indexOf(Object o) {
		int index = 0;	//链表的索引也是从0开始
		if (o == null) {
			for (Entry e = (Entry)data[header.next]; e != header; e = (Entry)data[e.next]) {	//从头结点开始,依次遍历
				if (e.element == null)
					return index;
				index++;
			}
		} else {
			for (Entry e = (Entry)data[header.next]; e != header; e = (Entry)data[e.next]) {
				if (o.equals(e.element))
					return index;
				index++;
			}
		}
		return -1;
	}
	
	/**
	 * 双向循环链表,从后面开始遍历即可
	 */
	public int lastIndexOf(Object o) {
		int index = size;
		if (o == null) {
			for (Entry e = (Entry)data[header.pre]; e != header; e = (Entry)data[e.pre]) {
				index--;
				if (e.element == null)
					return index;
			}
		} else {
			for (Entry e = (Entry)data[header.pre]; e != header; e = e = (Entry)data[e.pre]) {
				index--;
				if (o.equals(e.element))
					return index;
			}
		}
		return -1;
	}

	/**
	 * 跟addLast()方法类似,添加成功返回true
	 */
	public boolean add(E e) {
		addBefore(e, header);
		return true;
	}
	
	/**
	 * 假如链表中含有一个或多个o对象,移除第一次出现的o
	 * 如果找不到o返回false
	 */
	public boolean remove(Object o) {
		if (o == null) {
			for (Entry<E> e = (Entry<E>)data[header.next]; e != header; e = (Entry<E>)data[e.next]) {	//从第一个元素开始遍历,没一个Entry对象都包含它下一个元素的信息
				if (e.element == null) {
					remove(e);
					return true;
				}
			}
		} else {
			for (Entry<E> e = (Entry<E>)data[header.next]; e != header; e = (Entry<E>)data[e.next]) {
				if (o.equals(e.element)) {
					remove(e);
					return true;
				}
			}
		}
		return false;
	}
	
	/**
	 * 
	 * 把c中所有元素按顺序添加到链表尾部
	 */
	public boolean addAll(Collection<? extends E> c) {
		return addAll(size, c);
	}
	
	/**
	 * 
	 * 在指定位置按顺序添加c中所有元素带List中
	 * 
	 */
	public boolean addAll(int index, Collection<? extends E> c) {
		if (index < 0 || index > size)	//检查是否越界,=size表示添加到最后
			throw new IndexOutOfBoundsException("Index: " + index + ", Size: "
					+ size);
		Object[] a = c.toArray();
		int numNew = a.length;
		if (numNew == 0)
			return false;
		ensureCapacity(size + numNew + 2);

		Entry<E> successor = (index == size ? header : entry(index));
		Entry<E> predecessor = (Entry<E>)data[successor.pre];
		for (int i = 0; i < numNew; i++) {	//这里不难,画一个图就出来了,主要是初始化c和修改指针
			//暂时使其next为successor,因为e会赋给前驱,而每次遍历都要修改其前驱的next
			int node = getFreeNode();
			Entry<E> e = (Entry<E>)data[node];
			e.next = successor.cur;
			e.pre = predecessor.cur;
			e.element = (E)a[i];
			predecessor.next = e.cur;   //重新设置前驱的next指针
			predecessor = e;	//让e变为前驱
		}
		successor.pre = predecessor.cur;	//successor的前驱为c中最后一个元素的引用

		size += numNew;		//长度加
		return true;
	}
	
	/**
	 * 移除链表中所有元素
	 */
	public void clear() {
		Entry<E> e = (Entry<E>)data[header.next];
		while (e != header) {	//表示不是只有一个头结点的空链表
			Entry<E> next = (Entry<E>)data[e.next];
			free(e);
			e = next;
		}
		header.next = header.pre = header.cur = 1;	//初始头结点
		size = 0;
		modCount++;
	}
	
	/**
	 * 返回指定位置的元素时
	 */
	public E get(int index) {
		return entry(index).element;
	}

	/**
	 * 设置指定位置的元素
	 */
	public E set(int index, E element) {
		Entry<E> e = entry(index);
		E oldVal = e.element;
		e.element = element;
		return oldVal;
	}
	
	/**
	 * 
	 * 把指定元素添加到指定位置,需先定位到此位置的节点
	 * 详情见addBefore()
	 */
	public void add(int index, E element) {
		addBefore(element, (index == size ? header : entry(index)));
	}

	/**
	 * 移除指定位置的元素
	 */
	public E remove(int index) {
		return remove(entry(index));
	}
	
	/**
	 * 
	 * 返回指定索引位置的Entry对象,需要依次遍历得到。
	 * 这里稍做了一下优化,如果index < size/2 从前面开始遍历
	 * 如果index >= size/2 从后面开始遍历
	 */
	private Entry<E> entry(int index) {
		if (index < 0 || index >= size)	//index在0(包含)到size(不包含)之间,索引从0开始
			throw new IndexOutOfBoundsException("Index: " + index + ", Size: "
					+ size);
		Entry<E> e = header;
		if (index < (size >> 1)) {
			for (int i = 0; i <= index; i++)
				e = (Entry<E>)data[e.next];	//依次调用e.next才能得到,需调用index+1次,因为它是从头结点开始的
		} else {
			for (int i = size; i > index; i--)
				e = (Entry<E>)data[e.pre]; //依次调用e.previous才能得到
		}
		return e;
	}
	
	
	private Entry<E> addBefore(E e,Entry<E> entry){
		ensureCapacity(size + 1 + 2);
		int node = getFreeNode();
		Entry<E> newEntry = (Entry<E>)data[node];
		newEntry.element = e;
		newEntry.cur = node;
		newEntry.pre = entry.pre;
		newEntry.next = entry.cur;
		
		((Entry<E>)data[newEntry.pre]).next = node;
		((Entry<E>)data[newEntry.next]).pre = node;
		size++;
		return newEntry;
	}
	
	private E remove(Entry<E> e){
		if(e == header)
			throw new NoSuchElementException();
		E result = e.element;
		//改变实际链表的节点指针
		((Entry<E>)data[e.pre]).next = e.next;
		((Entry<E>)data[e.next]).pre = e.pre;
		free(e);
		size--;		//size--
		modCount++;
		return result;
	}
	
	@Override
	public ListIterator<E> listIterator(int index) {
		return new ListItr(index);
	}

	@Override
	public int size() {
		// TODO Auto-generated method stub
		return size;
	}
	

	private static class Entry<E>{
		E element;
		int next;
		int pre;
		int cur;
		
		Entry(E element,int cur,int next,int pre){
			this.element = element;
			this.next = next;
			this.pre = pre;
			this.cur = cur;
		}
		public String toString(){
			return "element:"+ element +" cur:"+cur+" next:"+next+" pre:"+pre;
		}
	}
	

	private class ListItr implements ListIterator<E> {
		private Entry<E> lastReturned = header;		//上一次调用next或previous返回的元素,没有调用next则为头结点
		private Entry<E> next;	//下一次调用next方法返回的元素
		private int nextIndex;	//下一次调用next返回的元素的索引
		private int expectedModCount = modCount;	//用来检测遍历过程中是否产生了并发操作

		ListItr(int index) {	//构造器,是迭代器定位到index位置,要返回index位置的元素需调用一次next()方法时
			if (index < 0 || index > size)	
				throw new IndexOutOfBoundsException("Index: " + index
						+ ", Size: " + size);
			if (index < (size >> 1)) {	//从前面开始遍历
				next = (Entry<E>)data[header.next];	 //这是index为0的元素
				for (nextIndex = 0; nextIndex < index; nextIndex++)
					next = (Entry<E>)data[next.next];	//最终next为第index个元素,index从0开始
			} else {		//从后面开始遍历
				next = header;
				for (nextIndex = size; nextIndex > index; nextIndex--)
					next = (Entry<E>)data[next.pre];	//最终next为第index个元素,index从0开始
			}
		}

		public boolean hasNext() {	//size位置可没有元素
			return nextIndex != size;
		}

		public E next() {
			checkForComodification();
			if (nextIndex == size)
				throw new NoSuchElementException();

			lastReturned = next;
			next = (Entry<E>)data[next.next];	//这里与ArrayList中的cursor何其相似
			nextIndex++;
			return lastReturned.element;
		}

		public boolean hasPrevious() {
			return nextIndex != 0;
		}

		public E previous() {
			if (nextIndex == 0)
				throw new NoSuchElementException();

			lastReturned = next = (Entry<E>)data[next.pre];
			nextIndex--;
			checkForComodification();
			return lastReturned.element;
		}

		public int nextIndex() {	//返回下一次调用next返回的元素的索引
			return nextIndex;
		}

		public int previousIndex() {	//返回下一次调用previous返回的元素的索引
			return nextIndex - 1;
		}

		public void remove() {
			checkForComodification();
			Entry<E> lastNext = (Entry)data[lastReturned.next];
			try {
				ArrayLinkedList.this.remove(lastReturned);	//移除上一层调用next()或previous返回的元素
			} catch (NoSuchElementException e) {
				throw new IllegalStateException();
			}
			if (next == lastReturned)	//表明是调用previous后才调用remove方法
				next = lastNext;
			else
				nextIndex--;		//元素减少。nextIndex--
			lastReturned = header;	//重置lastReturned
			expectedModCount++;
		}

		public void set(E e) {
			if (lastReturned == header)
				throw new IllegalStateException();
			checkForComodification();
			lastReturned.element = e;
		}

		public void add(E e) {
			checkForComodification();
			lastReturned = header;
			addBefore(e, next);	//在上一次调用next返回的元素之后,在上次调用previous返回的元素之前添加e
			nextIndex++;	//元素增加,索引增加,保证下次调用next()不是返回添加的元素
			expectedModCount++;
		}

		final void checkForComodification() {
			if (modCount != expectedModCount)
				throw new ConcurrentModificationException();
		}
	}
}




下面给出的是测试的代码,所有方法我都测试过了。
public class TestMyList {
	public static void main(String[] args) {
		ArrayLinkedList<String> al  = new ArrayLinkedList<String>();
		al.add("My");
		al.add("Array");
		al.add("LinkedList");
		al.add("end");
		al.remove("My");
		al.add("new");
		al.remove(1);
		al.add("GG");
		al.add("haha");
		al.removeFirst();
		al.removeLast();
		al.addFirst("zhis");
		al.addLast("is");
		al.remove(2.3);
		ArrayList<String> ar = new ArrayList<String>();
		ar.add("123");ar.add("456");
		al.addAll(1, ar);
		Object[] data = al.data;
		for(int i=0;i<data.length;i++){
			System.out.println(data[i]);
		}
		System.out.println("size:"+al.size());
		ListIterator<String> it = al.listIterator(0);
		Iterator<String> it2 = al.iterator();
		while(it2.hasNext()){
			System.out.print(it2.next()+" ");
		}
		System.out.println();
		System.out.print(it.next()+" ");
		System.out.print(it.next()+" ");
		it.remove();
		System.out.print(it.next()+" " );
		System.out.print(it.previousIndex()+" ");
		System.out.print(it.previous()+" ");
		System.out.print(it.previous()+" ");
		System.out.print(it.nextIndex()+" ");
		System.out.println();
		System.out.println(al.lastIndexOf("GG"));
	}
}
分享到:
评论

相关推荐

    双向循环链表C++源代码

    双向的循环链表的C++源代码 实现正逆序遍历,链表归并,插入,删除,查询等基本操作

    用Java实现模拟双向循环链表LinkedList

    在Java编程语言中,`LinkedList`是一种常用的线性数据结构,它通过节点(Node)对象存储数据,并且每个节点都包含指向前后节点的引用,从而形成一个双向循环链表。这个链表的特点在于,它的首节点和尾节点相互连接,...

    c++编的双线循环链表

    双向循环链表作为一种动态数据结构,可以随着需要添加或删除元素,不同于静态数组,后者在创建时就需要确定大小。 **C++编程环境** 本示例能在两种Visual Studio版本下运行:VS2005和VS2010。Visual Studio是一款...

    双链表-循环链表-静态链表.pdf

    循环链表特别适用于需要重复访问链表中的元素的情况,如模拟循环队列、管理循环缓冲区等。循环链表的操作,如插入和删除节点,都需保持最后一个节点的指针始终指向头节点,以保持链表的循环特性。 静态链表则是采用...

    C语言相关练习题 数组指针链表

    4. **其他数据结构和算法**:"杨辉三角"和"金字塔杨辉三角"是典型的递归问题,可以用数组或链表实现,涉及到组合数学和二项式系数。"螺旋数组"是数组的一种特殊排列,可以通过多维数组和循环实现。"魔方数组"可能是...

    C++语言约瑟夫环,双向链表

    此外,约瑟夫环问题有多种解决方案,如使用数组和位运算等,但使用双向链表能更好地模拟实际问题,对初学者理解链表和循环结构有很好的实践价值。通过学习这个案例,不仅可以提升C++编程技巧,还能加深对链表数据...

    C#完整的链表应用源码

    循环链表在处理需要“无限”循环或周期性行为的问题时特别有用,例如模拟时间序列或播放列表。 C#源码提供了这些链表结构的实现,这将帮助开发者理解链表的工作原理,以及如何在实际项目中应用它们。通过阅读和分析...

    各种形式的链表源码

    在给定的“各种形式的链表源码”压缩包中,重点是单链表、循环链表、双端链表的实现,以及可能使用数组或特定数据结构实现的链表变体。下面将详细介绍这些链表类型及其相关知识点。 **单链表**是最简单的一种链表...

    特殊的链表——双向、循环、静态

    总结来说,双向链表提供了双向遍历的能力,循环链表形成了一个可循环的链,而静态链表则是利用数组实现链表功能,各有优缺点。这些特殊链表结构在特定场景下有着广泛的应用,例如在搜索算法、文件系统、图形处理等...

    数据结构和算法必知必会的50个代码实现.zip

    实现单链表、循环链表、双向链表,支持增删操作 实现单链表反转 实现两个有序的链表合并为一个有序链表 实现求链表的中间结点 栈 用数组实现一个顺序栈 用链表实现一个链式栈 编程模拟实现一个浏览器的前进、...

    数据结构和算法必知必会的50个代码实现

    实现单链表、循环链表、双向链表,支持增删操作 实现单链表反转 实现两个有序的链表合并为一个有序链表 实现求链表的中间结点 栈 用数组实现一个顺序栈 用链表实现一个链式栈 编程模拟实现一个浏览器的前进、后退...

    数据结构和算法必知必会的50个代码实现源码.zip

    支持动态增删改操作实现两个有序数组合并为一个有序数组链表实现单链表、循环链表、双向链表,支持增删操作实现单链表反转实现两个有序的链表合并为一个有序链表实现求链表的中间结点栈用数组实现一个顺序栈用链表...

    【最强悍的链表】Linux 内核链表源码

    这个结构体包含两个指针成员,`next`和`prev`,分别指向后继节点和前驱节点,形成了双向循环链表。双向链表的优势在于可以从任一方向遍历,同时支持双向插入和删除操作。 接下来是链表操作的API。Linux内核提供了...

    链表辅助函数_链表辅助函数_

    - 使用数组实现链表:虽然链表通常用指针实现,但在某些场景下,可以使用数组模拟链表。每个数组元素代表一个节点,通过下标表示节点间的连接关系。 - 错误处理:辅助函数需要考虑边界条件和错误情况,如插入或...

    DTLib-LinkQueue

    总结来说,DTLib的LinkQueue和StaticQueue为程序员提供了两种不同的队列实现方式,分别基于双向循环链表和顺序循环数组。它们各有优缺点,适应不同的场景需求。熟悉和掌握这两种数据结构,能够帮助开发者更好地应对...

    数据结构3学习.pptx

    静态链表的存储结构通常使用一维数组来模拟链表,每个元素包含数据域和指针域,但无需动态分配或释放内存。在静态链表中,插入和删除操作不涉及元素的物理移动,只改变指针即可,这保持了链式存储结构的优势。例如,...

    约瑟夫环问题 (只是做了个简单的版本)

    解决约瑟夫环问题有多种方法,包括使用链表、数组、栈和队列等数据结构,以及模运算来实现循环计数。 首先,我们可以使用数组来存储每个人的位置。数组的索引代表人的编号,值可以用来记录这个人是否被淘汰。初始化...

    链表相关的算法程序

    - 静态链表:使用数组模拟链表,节省了内存分配和回收的时间开销。 - 压缩链表:通过合并相邻的空节点来减少内存使用。 - 哈希表与链表结合:哈希表的键对应链表的头节点,实现快速查找。 总结来说,链表是一种强大...

    c语言实列 图形 链表 相当完整

    此外,该资源可能还涵盖了链表的一些高级话题,如双向链表、循环链表、链表的合并排序等。双向链表每个节点包含两个指针,一个指向前一个节点,一个指向后一个节点,这提供了更灵活的插入和删除操作。循环链表的最后...

    用队列和栈判断回文_赫夫曼数_双向链表_内部排序(8种).zip

    在C语言中,可以利用数组模拟栈和队列,通过push和pop操作实现这一过程。 2. **赫夫曼数**: 赫夫曼编码(Huffman Coding)是一种用于无损数据压缩的算法,其核心思想是建立一棵具有最小带权路径长度的二叉树,...

Global site tag (gtag.js) - Google Analytics