自行使用Java数组实现链表数据结构

这几年一直做企业ERP基础架构,对于算法领域的知识使用的较少,前两天被他人问及链表如何实现,草草在白板上写了一个一维字符串数组,用来表示链表,数组里面包含了一个长度为2的字符串数组,用来表示节点,节点的第一个元素保存值,第二个元素保存下个节点的引用,非常简单,但当时由于时间关系,没有仔细思考,草草作答了。

今天晚上,觉得有点时间,仔细想了下,应该采用面向对象的思想,对节点和链表高度抽象,节点应可以是任意对象。

首先让我们来复习下什么是链表?我们用一个简单的示意图表示(用画图板画了个,将就看吧:)):

自行使用Java数组实现链表数据结构

从上头可以看出,链表有如下特点:

1、有一个头和一个尾,一个有多个节点组成的非封闭的链

2、每个节点都有一个指针指向下一个节点

嗯,那我们要抽象一个链表的类,还要有一个节点的类,来实现上述链表的特点。

代码如下:

package com.iteye.redhacker.test;

/**
 * 链表节点类
 * 
 * @author JackDou
 * @since 2013-08-11
 */
public class Node {

	private Object value;

	private Node next;

	private int index;

	public Node(Object value) {
		this.value = value;
	}

	/**
	 * @return the value
	 */
	public Object getValue() {
		return value;
	}

	/**
	 * @param value
	 *            the value to set
	 */
	public void setValue(Object value) {
		this.value = value;
	}

	/**
	 * @return the index
	 */
	public int getIndex() {
		return index;
	}

	/**
	 * @param index
	 *            the index to set
	 */
	public void setIndex(int index) {
		this.index = index;
	}

	/**
	 * @return the next
	 */
	public Node getNext() {
		return next;
	}

	/**
	 * @param next
	 *            the next to set
	 */
	public void setNext(Node next) {
		this.next = next;
	}

	@Override
	public String toString() {
		return "Node [index=" + index + "," + "value=" + value + "]";
	}
}


package com.iteye.redhacker.test;

/**
 * 链表类
 * 
 * @author JackDou
 * @since 2013-08-11
 */
public class LinkedList {
	
	/** 默认容量大小及链表容量满的时候每次自动增加的大小 */
	private static final int DEFAULT_CAPACITY_SIZE = 12;
	
	/** 链表中的节点集合 */
	private Node[] nodes;
	
	/** 指针,指向链表末端待添加的位置,默认指向0的位置 */
	private int endPos = 0;
	
	/** 当前链表容量 */
	private int capacity = 0;

	public LinkedList() {
		nodes = new Node[DEFAULT_CAPACITY_SIZE];
		capacity = DEFAULT_CAPACITY_SIZE;
	}
	
	public LinkedList(int capacity) {
		nodes = new Node[capacity];
		this.capacity = capacity;
	}
	
	/**
	 * 链表大小
	 * 
	 * @return 链表大小
	 */
	public int size() {
		return endPos;
	}
	
	public int capacity() {
		return this.nodes.length;
	}
	
	/**
	 * 向链表中加入一个节点
	 * 
	 * @param node 新加入的节点
	 */
	public void add(Node node) {
		if (this.endPos >= this.capacity() - 1) {
			capacity = this.endPos + 1 + DEFAULT_CAPACITY_SIZE;
			Node[] nodesTemp = new Node[capacity];
			System.arraycopy(nodes, 0, nodesTemp, 0, this.endPos);
			nodes = nodesTemp;
		}
		if (endPos == 0) {
			node.setIndex(0);
			nodes[endPos] = node;
		} else if (endPos > 0 && endPos <= this.capacity() - 1) {
			node.setIndex(endPos);
			nodes[endPos] = node;
			nodes[endPos-1].setNext(nodes[endPos]);
		}
		endPos++;
	}
	
	/**
	 * 删除链表元素
	 * 
	 * @param index 要删除链表中节点的位置
	 */
	public void remove(int index) {
		if (index > this.capacity() - 1 || index >= this.endPos || index < 0) {
			throw new RuntimeException("没有位置索引" + index + "存在");
		}
		if (index == endPos-1) {
			nodes[index] = null;
			return;
		}
		for (; 0 <= index && index < endPos-1; index++) {
			if (index == 0) {
				nodes[index] = nodes[index+1];
				nodes[index].setIndex(index);
			} else {
				nodes[index] = nodes[index+1];
				nodes[index].setIndex(index);
				nodes[index-1].setNext(nodes[index]);
			}
		}
		endPos--;
	}
	
	/**
	 * 更新指定链表位置节点的值
	 * 
	 * @param index 指定的链表位置
	 * @param value 指点链表位置上的节点的值
	 */
	public void update(int index, Object value) {
		if (index > this.capacity() - 1 || index >= this.endPos || index < 0) {
			throw new RuntimeException("没有位置索引" + index + "存在");
		}
		nodes[index].setValue(value);
	}
	
	/**
	 * 获取指点链表位置上的节点的值
	 * 
	 * @param index 指定的链表位置
	 * @return 指点链表位置上的节点
	 */
	public Node getNode(int index) {
		if (index > this.capacity() - 1 || index >= this.endPos || index < 0) {
			throw new RuntimeException("没有位置索引" + index + "存在");
		}
		return nodes[index];
	}
	
	/**
	 * 在链表指定位置插入节点
	 * 
	 * @param index 指定的链表位置
	 * @param value 节点
	 */
	public void insert(int index, Node node) {
		if (index > this.capacity() - 1 || index >= this.endPos || index < 0) {
			throw new RuntimeException("没有位置索引" + index + "存在");
		}
		if (this.endPos >= this.capacity() - 1) {
			capacity = this.endPos + 1 + DEFAULT_CAPACITY_SIZE;
			Node[] nodesTemp = new Node[capacity];
			System.arraycopy(nodes, 0, nodesTemp, 0, this.endPos);
			nodes = nodesTemp;
		}
		Node[] nodesTemp = new Node[size()- index];
		System.arraycopy(nodes, index, nodesTemp, 0, nodesTemp.length);

		for (int i = 0, len= nodesTemp.length; i< len; i++) {
			nodesTemp[i].setIndex(nodesTemp[i].getIndex() + 1);
		}
		endPos++;
		node.setIndex(index);
		System.arraycopy(nodesTemp, 0, nodes, index+1, nodesTemp.length);
			node.setNext(nodes[0]);
		nodes[index] = node;
		nodes[index-1].setNext(nodes[index]);
		nodes[index].setNext(nodes[index+1]);
	}
	
	/**
	 * 获取链表的第一个节点
	 * 
	 * @return 链表的第一个节点
	 */
	public Node getFirst() {
		return nodes[0];
	}
	
	/**
	 * 获取链表的最后一个节点
	 * 
	 * @return 链表的最后一个节点
	 */
	public Node getLast() {
		return nodes[endPos-1];
	}
	
	/**
	 * 链表反序
	 */
	public LinkedList reverse() {
		Node[] nodesTemp = new Node[capacity];
		for (int i = 0, j = endPos - 1; i < endPos &&  j >= 0; i++, j--) {
			nodesTemp[i] = nodes[j];
			nodesTemp[i].setIndex(i);
			if (j > 0) {
				nodesTemp[i].setNext(nodes[j-1]);
			}
		}
		nodes = nodesTemp;
		return this;
	}
	
	public String toString() {
		StringBuilder strBui = new StringBuilder();
		for (int  i = 0; i < endPos; i++) {
			strBui.append(nodes[i].toString());
			if (i < endPos) {
				strBui.append(",");
			}
		}
		return strBui.toString();
	}

	/**
	 * 获取当前链表容量
	 * 
	 * @return the capacity
	 */
	public int getCapacity() {
		return capacity;
	}
}


package com.iteye.redhacker.test;

/**
 * 测试类
 * 
 * @author JackDou
 * @since 2013-08-11
 */
public class Test {

	public static void main(String[] args) {
		// 创建默认大小的LinkedList
		LinkedList list1 = new LinkedList();
		list1.add(new Node(12));
		list1.add(new Node(10));
		list1.add(new Node(11));
		list1.add(new Node(8));
		list1.add(new Node(9));
		
		System.out.println("链表默认容量为12");
		System.out.println("链表长度:" + list1.size());
		System.out.println("链表当前容量:" + list1.getCapacity());
		System.out.println();
		
		list1.add(new Node(2));
		list1.add(new Node(4));
		list1.add(new Node(5));
		list1.add(new Node(7));
		list1.add(new Node(6));
		list1.add(new Node(1));
		list1.add(new Node(3));
		
		System.out.println("链表容量满时会自动扩容,默认为扩大12个节点容量");
		System.out.println("链表:" + list1.toString());
		System.out.println("链表第一个节点:" + list1.getFirst());
		System.out.println("链表最后一个节点节点:" + list1.getLast());
		System.out.println("链表长度:" + list1.size());
		System.out.println("链表当前容量:" + list1.getCapacity());
		System.out.println();
		
		list1.remove(3);
		System.out.println("删除索引为3的节点后的情况:");
		System.out.println("链表:" + list1.toString());
		System.out.println("链表第一个节点:" + list1.getFirst());
		System.out.println("链表最后一个节点节点:" + list1.getLast());
		System.out.println("链表长度:" + list1.size());
		System.out.println("链表当前容量:" + list1.getCapacity());
		System.out.println();
		
		list1.add(new Node(80));
		list1.add(new Node(90));
		System.out.println("新增两个节点的情况(新增:80,90两个数字):");
		System.out.println("链表:" + list1.toString());
		System.out.println("链表第一个节点:" + list1.getFirst());
		System.out.println("链表最后一个节点节点:" + list1.getLast());
		System.out.println("链表长度:" + list1.size());
		System.out.println("链表当前容量:" + list1.getCapacity());
		System.out.println();
		
		System.out.println("获取第8个节点的信息:" + list1.getNode(8));
		System.out.println("获取第8个节点的下一个节点信息是:" + list1.getNode(8).getNext());
		System.out.println();
		
		System.out.println("测试反序链表");
		System.out.println("原链表:" + list1.toString());
		System.out.println("反序链表:" + list1.reverse().toString());
		System.out.println();
		
		System.out.println("链表长度:" + list1.size());
		System.out.println("链表当前容量:" + list1.getCapacity());
		System.out.println("测试在第3个索引的位置连续插入12个值为45的节点,使得链表长度超过当前容量,测试链表的自增长容量");
		System.out.println("原链表:" + list1.toString());
		list1.insert(3, new Node(45));
		list1.insert(3, new Node(45));
		list1.insert(3, new Node(45));
		list1.insert(3, new Node(45));
		list1.insert(3, new Node(45));
		list1.insert(3, new Node(45));
		list1.insert(3, new Node(45));
		list1.insert(3, new Node(45));
		list1.insert(3, new Node(45));
		list1.insert(3, new Node(45));
		list1.insert(3, new Node(45));
		list1.insert(3, new Node(45));
		System.out.println("插入后链表:" + list1.toString());
		System.out.println("链表长度:" + list1.size());
		System.out.println("链表当前容量:" + list1.getCapacity());
	}
}


测试类打印出来的结果是:

引用

链表默认容量为12
链表长度:5
链表当前容量:12

链表容量满时会自动扩容,默认为扩大12个节点容量
链表:Node [index=0,value=12],Node [index=1,value=10],Node [index=2,value=11],Node [index=3,value=8],Node [index=4,value=9],Node [index=5,value=2],Node [index=6,value=4],Node [index=7,value=5],Node [index=8,value=7],Node [index=9,value=6],Node [index=10,value=1],Node [index=11,value=3],
链表第一个节点:Node [index=0,value=12]
链表最后一个节点节点:Node [index=11,value=3]
链表长度:12
链表当前容量:24

删除索引为3的节点后的情况:
链表:Node [index=0,value=12],Node [index=1,value=10],Node [index=2,value=11],Node [index=3,value=9],Node [index=4,value=2],Node [index=5,value=4],Node [index=6,value=5],Node [index=7,value=7],Node [index=8,value=6],Node [index=9,value=1],Node [index=10,value=3],
链表第一个节点:Node [index=0,value=12]
链表最后一个节点节点:Node [index=10,value=3]
链表长度:11
链表当前容量:24

新增两个节点的情况(新增:80,90两个数字):
链表:Node [index=0,value=12],Node [index=1,value=10],Node [index=2,value=11],Node [index=3,value=9],Node [index=4,value=2],Node [index=5,value=4],Node [index=6,value=5],Node [index=7,value=7],Node [index=8,value=6],Node [index=9,value=1],Node [index=10,value=3],Node [index=11,value=80],Node [index=12,value=90],
链表第一个节点:Node [index=0,value=12]
链表最后一个节点节点:Node [index=12,value=90]
链表长度:13
链表当前容量:24

获取第8个节点的信息:Node [index=8,value=6]
获取第8个节点的下一个节点信息是:Node [index=9,value=1]

测试反序链表
原链表:Node [index=0,value=12],Node [index=1,value=10],Node [index=2,value=11],Node [index=3,value=9],Node [index=4,value=2],Node [index=5,value=4],Node [index=6,value=5],Node [index=7,value=7],Node [index=8,value=6],Node [index=9,value=1],Node [index=10,value=3],Node [index=11,value=80],Node [index=12,value=90],
反序链表:Node [index=0,value=90],Node [index=1,value=80],Node [index=2,value=3],Node [index=3,value=1],Node [index=4,value=6],Node [index=5,value=7],Node [index=6,value=5],Node [index=7,value=4],Node [index=8,value=2],Node [index=9,value=9],Node [index=10,value=11],Node [index=11,value=10],Node [index=12,value=12],

链表长度:13
链表当前容量:24
测试caruso元素到第3个索引的位置连续插入12个值为45的节点,使得链表长度超过当前容量,测试链表的自增长容量
原链表:Node [index=0,value=90],Node [index=1,value=80],Node [index=2,value=3],Node [index=3,value=1],Node [index=4,value=6],Node [index=5,value=7],Node [index=6,value=5],Node [index=7,value=4],Node [index=8,value=2],Node [index=9,value=9],Node [index=10,value=11],Node [index=11,value=10],Node [index=12,value=12],
插入后链表:Node [index=0,value=90],Node [index=1,value=80],Node [index=2,value=3],Node [index=3,value=45],Node [index=4,value=45],Node [index=5,value=45],Node [index=6,value=45],Node [index=7,value=45],Node [index=8,value=45],Node [index=9,value=45],Node [index=10,value=45],Node [index=11,value=45],Node [index=12,value=45],Node [index=13,value=45],Node [index=14,value=45],Node [index=15,value=1],Node [index=16,value=6],Node [index=17,value=7],Node [index=18,value=5],Node [index=19,value=4],Node [index=20,value=2],Node [index=21,value=9],Node [index=22,value=11],Node [index=23,value=10],Node [index=24,value=12],
链表长度:25
链表当前容量:36


关于程序的详细实现这里不做详细的解释了,大家可以直接从文后下到源码,到如到eclipse里自行学习。

值得要说明的是:

1、我在链表里实现了一个反序,这是当时被问及的关键点,现在看起来似乎很简单的;
2、我在这个实现里使用了System.arraycopy()这个高级的API,或许还不够原生,有系统的同学可以实现自己的数组拷贝方法用以代替。

我的算法的复习因以此契机还需继续,希望有更多的时间去思考这些问题。加油!

你可能感兴趣的:(java数组)