java基础:14.3 线性表的手动实现

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/L20902/article/details/89005826


实现线性表的方式有两种。

一种是使用数组( array) 存储线性表的元素。数组大小是固定的。如果元素个数超过了数组的容量,就创建一个更大的新数组,并将当前数组中的元素复制到新数组中。

另一种是使用链式结构( linked structure) 。链式结构由结点组成,每个结点都是动态创建的,用来存储一个元素。所有的结点链接成一个线性表。

这样,就可以给线性表定义两种类。为了方便起见,分别称这两种类为MyArrayList 和MyLinkedList 。这两种类具有相同的操作,但是具有不同的实现。

在这里插入图片描述
MyList 定义了MyAbstractList 、MyArrayList 和MyLinkedList 的通用接口

通用的操作可以归纳为一个接口或者一个抽象类。一个好的策略就是在设计中提供接口和便利抽象类,以整合接口和抽象类的优点,这样用户可以认为哪个方便就用哪个。抽象类提供了接口的骨架实现,可以更有效地实现接口。

两种方式复杂度的比较
在这里插入图片描述

第一步:在设计中提供接口。

首先,定义一个接口MyList 继承自Iterable。在接口里定义线性表会用到的方法。

public interface MyList<E> extends Iterable<E> {
	
	public void add(E e);
	
	public void add(int dex,E e);
	
	public void clear();
	
	public boolean contains(E e);
	
	public E get(int index);
	
	public int indexOf(E e);
	
	public boolean isEmpty();
	
	public int lastIndexOf(E e);
	
	public boolean remove(E e);
	
	public E remove(int index);
	
	public Object set(int index,E e);
	
	public int size();

}

第二步:实现通用类MyAbstractList。

MyAbstractList 声明变量size ,表示线性表中元素的个数。
MyList定义了操作线性表的许多方法。 MyAbstractList 提供 MyList 接口的【部分实现】

public abstract  class MyAbstractList<E> implements MyList<E>{

	protected int size = 0;
	
	protected MyAbstractList() {
	}
	
	protected MyAbstractList(E[] objects) {
		for(int i = 0; i < objects.length;i++)
			add(objects[i]);
	}
	
	@Override
	public void add(E e) {
		add(size,e);
	}
	
	@Override
	public boolean isEmpty() {
		return size==0;
	}
	
	@Override
	public int size() {
		return size;
	}
	
	@Override
	public boolean remove(E e) {
		if(indexOf(e) >= 0) {
			remove(indexOf(e));
			return true;
		}
		else 
			return false;
	}	
}

.

第三步(1):MyArrayList 的实现。

被保护的数据域一般很少使用,但是,将MyAbstractList 类中的size 数据域设直为被保护的是一个很好的选择。MyAbstractL;st 的子类可以访问s;ze ,但是,在不同包中的MyAbstractList 的非子类不能访问它。作为一个常用规则,可以将抽象类中的数据域声明为被保护的。

数据长度可扩:数组是一种大小固定的数据结构。数组一旦创建之后,它的大小就无法改变。尽管如此,仍然可以使用数组来实现动态的数据结构。处理的方法是,**当数组不能再存储线性表中的新元素时,创建一个更大的新数组来替换当前数组。**初始化时,用默认大小创建一个类型为E []的数组data 。向数组中插人一个新元素时,首先确认数组是否有足够的空间。若数组的空间不够,则创建大小为当前数组两倍的新数组,然后将当前数组中的元素复制到新的数组中。

指定下标插入元素:在指定下标处插入一个新元素之前,必须将指定下标后面的所有元素都向右移动一个位置并且将该线性表的大小增加1

指定下标删除元素:删除指定下标处的一个元素时,应该将该下标后面的元素都向左移动一个位置,并将线性表的大小减1

等方法…

package ReWrite;

import java.util.Iterator;

public class MyArrayList<E> extends MyAbstractList<E>{
	
	public static final int INITIAL_CAPACITY = 16;  // 初始数组大小16
	
	private E[] data = (E[]) new Object[INITIAL_CAPACITY];  
	//由于泛型消除,所以不能使用语法new e[INITIAL_CAPACITY] 创建泛型数组。
	//创建了一个Object 类型的数组,并将它转换为E[]类型
	
	public MyArrayList() {
	}

	public MyArrayList(E[] objects) {
		for (int i=0; i < objects.length; i++)
			add(objects[i]);
	}
	
	
	/**
	 * 用于确保数组中是否有存储新元素的空间。
	 * 若空间已满,则创建容量x2的新数组,并把所有元素复制到新数组
	 */
	private void ensureCapacity() {
		if(size >= data.length) {
			E[] newData = (E[])(new Object[size*2+1]);
			System.arraycopy(data, 0, newData, 0, size);
			data = newData;
		}
	}

	
	/**
	 * 添加元素方法。
	 */
	@Override
	public void add(int index, E e) {
		ensureCapacity();  	
		for(int i = size-1 ; i>= index; i--)
			data[i+1] = data[i];		
		data[index] = e;
		size++;  		// 在MyAbstractList里定义的
	}
	

	/**
	 * 清空线性表
	 * 通过创建一个新数组井且将其赋值给data ,
	 * 老的数组和保存在老数组中的元素变成了垃圾,将自动被JVM 所收集
	 */
	@Override
	public void clear() {
		data = (E[]) new Object[INITIAL_CAPACITY];
		size = 0;
		
	}

	/**
	 * 使用equals 方法将元素e 与数组中的所有元素逐一比较,以判断数组中是否包含元素e 。
	 */
	@Override
	public boolean contains(E e) {
		for(int i=0; i< size ;i++)
			if(e.equals(data[i]))  return true;
		
		return false;
	}

	/**
	 * 作用:取值。
	 * 首先检查index 是否在范围内,并且如果index在范围内, 则返问data[indexJ 。
	 */
	@Override
	public E get(int index) {
		checkIndex(index);
		return data[index];
	}
	
	private void checkIndex(int index) {
		if (index < 0 || index >= size)
			throw new IndexOutOfBoundsException("index"+ index + " out of bounds");
	}

	/**
	 * 从第一个元素开始,将元素e 与数组中的每一个元素逐一比较。
	 * 如果匹配, 则返问匹配元素的下标;否则,返回一1 。
	 */
	@Override
	public int indexOf(E e) {
		for (int i = 0; i < size; i++)
			if(e.equals(data[i]))  return i;
			
			return -1;
	}
	
	

	@Override
	public int lastIndexOf(E e) {
		for(int i = size - 1; i >= 0 ; i--)
			if(e.equals(data[i]))  return i;
		
		return -1;
	}
	

	/**
	 * 删除并返回指定下标的元素。
	 * 将指定下标之后所有元素向左移动一个位置,并将数组大小size 减i。
	 */
	@Override
	public E remove(int index) {
		checkIndex(index);
		E e = data[index];
		for(int j = index; j < size - 1; j++)
			data[j] = data [j+1];
		
		data[size-1] =null;
		size--;
		return e;
	}

	/**
	 * 修改执行下标元素的值,并返回修改前的值
	 */
	@Override
	public Object set(int index, E e) {
		checkIndex(index);
		E old = data[index];
		data[index] = e;
		return old;
	}

	/**
	 * 覆盖Object 类中的toString 方法,返回一个表示线性表中所有元素的字符串。
	 */
	@Override
	public String toString() {
		StringBuilder result = new StringBuilder("[");
		for(int i=0; i < size; i++) {
			result.append(data[i]);
			if(i < size-1) result.append(",");
		}
		
		return result.toString() + "]";
	}
	
	/**
	 * 创建一个新数组,它的大小与当前数组线性表的大小匹配。
	 * 将当前数组复制到新的数组中,,然后将新数组设置为当前数组
	 */
	public void trimToSize() {
		if(size != data.length) {
			E[] newData = (E[])(new Object[size]);
			System.arraycopy(data, 0, newData, 0, size);
			data = newData;
		}
	}

	
	@Override
	public Iterator<E> iterator() {
		return new ArrayListIterator();
	}
	
	public class ArrayListIterator implements Iterator<E>{
		private int current = 0;
		
		@Override
		public boolean hasNext() {
			return (current<size);
		}
		
		@Override
		public E next() {
			return data[current++];
		}
		
		@Override
		public void remove() {
			MyArrayList.this.remove(current);
		}
	}

}

第三步(2):测试MyArrayList。

package ReWrite;

public class Test {
	public static void main(String[] args) {
		MyList<String> list1 = new MyArrayList<String>();
		list1.add("America");
		list1.add("China");
		list1.add("Germany");
		list1.add(1,"Canada");
		System.out.print(list1);
		list1.remove(3);
		System.out.print(list1);
		list1.clear();
		System.out.print(list1);
	}
}

[America,Canada,China,Germany][America,Canada,China][]

第四步:链式结构MyLinkedList

链表采用链接结构实现。

链表中的每个元素都包含一个称为结点( node) 的结构。当向链表中加人一个新的元素时,就会产生一个包含它的结点。每个结点都和它的相邻结点相链接.

结点可以按如下方式定义为一个类:
class Node {
------- E element;
------- Node next;
------- public Node(E e) {
------- element = e;
--------}
}

在这里插入图片描述
变量head 指向线性表的第一个结点,而变量tail指向最后一个结点。如果线性表为空, head 和tail 这两个变量均为null。

创建一个存储三个结点的链表的例子。

步骤1 :声明head 和tail 。
Node head = null;
Node tail = null;

head 和tail 都为null 。该线性表为空。

步骤2. 创建第一个结点并将它追加到线性表中,在将第一个结点插入线性表之后, head 和tail都指向这个结点。

head = new Node<>(“Chicage”);
tail = head;

在这里插入图片描述
步骤3 :创建第二个结点并将它追加到线性表中。为了将第二个结点追加到线性表中,需要将新结点和第一个结点链接起来。现在,新结点就是尾结点。所以,应该移动tail ,使它指向该新结点。

tail.next= new Node<>(“Denver”);
tail = tail.next;

在这里插入图片描述

步骤4: 创建第三个结点并将它追加到线性表中。为了向线性表追加新的结点,链接新结点和线性表当前的最后一个结点。现在, 新结点就是尾结点。所以,应该移动tail,使它指向该新结点。

tail.next = new Node<>(“England”);
tail = tail.next;

在这里插入图片描述
每个结点都包含元素和一个名为next 的数据域, next 指向下一个元素。
如果结点是线性表中的最后一个, 那么它的指针数据域next 所包含的值是null。可以使用这个特性来检测某结点是否是最后的结点。

.
.

第四步(1):MyLinkedList 的实现。

package ReWrite;

import java.util.Iterator;

public class MyLinkedList<E> extends MyAbstractList<E>{

	private Node<E> head,tail;  
	
	public MyLinkedList() {
	}
	
	public MyLinkedList(E[] objects) {
		super(objects);
	}
	
	/**
	 * addFirst: 创建一个包含元素e的新结点。该新结点就成为链表的第一个结点
	 * @param e
	 */
	public void addFirst(E e) {
		Node<E> newNode = new Node<>(e);
		newNode.next = head;
		head = newNode;
		size++;
		
		if(tail == null)
			tail = head;
	}
	
	/**
	 * addLast: 创建一个包含元素的新结点,并将它插到链表的末尾
	 * @param e
	 */
	public void addLast(E e) {
		Node<E> newNode = new Node<>(e);
		if(tail == null)
			head = tail = newNode;
		else {
			tail.next = newNode;
			tail = tail.next;
		}		
		size++;
	}
	
	
	/**
	 * add:指定index位置插入
	 */
	@Override
	public void add(int index, E e) {
		if(index == 0) addFirst(e);
		else if(index >= size) addLast(e);
		else {
			Node<E> current = head;
			for(int i = 1; i < index; i++)
				current = current.next;
			Node<E> temp = current.next;
			current.next = new Node<E>(e);
			(current.next).next = temp;
			size++;
		}
		
	}
	
	/**
	 * removeFirst:删除并返回第一个结点
	 * @return
	 */
	public E removeFirst() {
		if(size == 0) return null;
		else {
			Node<E> temp = head;
			head = head.next;
			size--;
			if( head == null) tail =null;
			return temp.elemet;
		}
	}
	
	/**
	 * removeLast:移除并返回最后一个结点
	 * @return
	 */
	public E removeLast() {
		if(size==0) return null;
		else if(size == 1){
			Node<E> temp = head;
			head = tail = null;
			size = 0;
			return temp.elemet;
		}
		else {
			Node<E> current = head;
			for(int i=0; i < size-2 ; i++)
				current = current.next;
			Node<E> temp = tail;
			tail = current;
			tail.next = null;
			size--;
			return temp.elemet;
		}
	}
	
	
	/**
	 * remove:移除并发返回指定index的结点
	 */
	@Override
	public E remove(int index) {
		if( index<0 || index>=size) return null;
		else if(index==0) return removeFirst();
		else if(index==size-1) return removeLast();
		else {
			Node<E> previous = head;
			for(int i=0; i<index-1; i++)
				previous = previous.next;
			Node<E> current = previous.next;
			previous.next = current.next;
			size--;
			return current.elemet;
		}
	}

	public E getFirst() {
		if( size == 0) return null;
		else return head.elemet;
	}
	
	public E getLast() {
		if(size == 0) return null;
		else return tail.elemet;
	}
	
	
	@Override
	public void clear() {
		size = 0;
		head = tail = null;
		
	}

	
	@Override
	public boolean contains(E e) {
		Node<E> current = head;
		for(int i=0; i<size; i++) {
			if(e.equals(current.elemet)) return true;
			current = current.next;
		}
		return false;
	}

	@Override
	public E get(int index) {
		Node<E> current = head;
		for(int i=0; i<index ;i++) 
			current = current.next;
		return current.elemet;
		
	}
	
	
	
	/**
	 * 从第一个元素开始,将元素e 与数组中的每一个元素逐一比较。
	 * 如果匹配, 则返问匹配元素的下标;否则,返回一1 。
	 */
	@Override
	public int indexOf(E e) {
		Node<E> current = head;
		for (int i = 0; i < size; i++)
			if(e.equals(current.elemet))  return i;	
			return -1;
	}

	@Override
	public int lastIndexOf(E e) {
		Node<E> current = head;
		int index=-1;
		for (int i = 0; i < size; i++)
			if(e.equals(current.elemet))  index=i;	
		if(index != -1) return index;
		else return -1;
	}



	@Override
	public Object set(int index, E e) {
		Node<E> current = head;
		for(int i=0 ; i< index; i++)
			current =current.next;
		E temp = current.elemet;
		current.elemet = e;
		return temp;
	}

	@Override
	public Iterator<E> iterator() {
		return new LinkedListIterator();
	}
	
	private class LinkedListIterator implements Iterator<E>{
		private Node<E> current = head;
		
		@Override
		public boolean hasNext() {
			return (current!=null);
		}
		
		@Override
		public E next() {
			E e = current.elemet;
			current = current.next;
			return e;
		}
		
		@Override
		public void remove() {
			MyLinkedList.this.removeLast();
		}
		
	}
	
	
	
	
	@Override
	public String toString() {
		StringBuilder result = new StringBuilder("[");
		Node<E> current = head;
		for(int i=0; i < size; i++) {
			result.append(current.elemet);
			current = current.next;
			if(current != null)
				result.append(".");
			else
				result.append("]");
		}
		return result.toString();
	}
	
	
	
	
	private static class Node<E>{
		E elemet;
		Node<E> next;
		public Node(E element) {
			this.elemet = element;
		}
	}

}

第四步(2):测试。

package ReWrite;

public class Test {
	public static void main(String[] args) {
		
		
		MyLinkedList<String> list1 = new MyLinkedList<>();
		list1.add("America");      // [America]	
		list1.add(0,"Cannada");    // [Cannada.America]
		list1.add("Russia");       // [Cannada.America.Russia]	
		list1.addFirst("France");  // [France.Cannada.America.Russia]	
		list1.add(2,"Germay");     // [France.Cannada.Germay.America.Russia]
		list1.add(5,"Norway");     // [France.Cannada.Germay.America.Russia.Norway]
		list1.add(0,"Poland");     // [Poland.France.Cannada.Germay.America.Russia.Norway]	
		list1.removeFirst();       // [France.Cannada.Germay.America.Russia.Norway]
		list1.removeLast();        // [France.Cannada.Germay.America.Russia]
		list1.remove(1);           // [France.Germay.America.Russia] 	
		list1.remove(2);           // [France.Germay.Russia] 

		for(String s:list1)
		System.out.print(s.toUpperCase()+"  ");

	}
}

猜你喜欢

转载自blog.csdn.net/L20902/article/details/89005826
今日推荐