數據結構那些事(二)線性表及其連續存儲的實現

   1、基本概念java

   首先,仍是來回顧線性結構的基本概念。數組

  • 線性結構:在這些數據元素中有一個能夠被稱爲「第一個」(元素01)的數據元素;還有一個能夠被稱爲「最後一個」(元素04)的數據元素;除第一個元素之外每一個數據元素有且僅有一個直接前驅元素,除最後一個元素之外每一個數據元素有且僅有一個直接後續元素。這種數據結構的特色是數據元素之間是11 的聯繫,即線性關係。數據結構

  • 二元組表示:一種數據結構的二元組表示爲 linearity = (K,R),其中
    K = {01, 02, 03, 04, 05}
    R = {<02,04>, <03,05>, <05,02>, <01,03>} 
    ide

    接下來我必須強調的一點,咱們研究的線性表是類型相同有限序列學習

  2、線性表和數組的區別this

    還有一點,線性表和數組要區別一下,具體區別以下:spa

  1. 從概念上區別:線性表是抽象數據結構,數組是一種具體的數據類型。code

  2. 從邏輯關係上區別:線性表描述的是元素和元素之間一對一的關係,而數組是下表索引和數組元素的一一對應的關係。orm

  3. 從物理存儲上區別:線性表相鄰的元素存儲在內存中能夠是連續的,也能夠是不連續的;而數組中相鄰的元素存儲在連續的內存空間中。對象

  3、線性表抽象數據類型(ADT List)

   抽象數據類型是描述啥東西,有啥關係,幹啥。關於啥東西。

  啥東西:數據元素在Java裏面就是對象,什麼對象呢?爲了兼容全部對象,可使用Object,在這裏,我使用泛型佔位符E表示對象的類型。

  啥關係:數據元素的邏輯關係是線性表

  幹啥:線性表能夠完成對其中的元素訪問、增長、刪除等操做,還應該可以判斷是不是空的,此額外還應該判斷是否包含某個元素。

  好的,說了再多也不如用代碼來的那麼明瞭。

ackage com.util.linear;
/***
 * <p>List是線性表的抽象定義ADT。<br/>
 * List 定義了一個線性表對象所擁有的行爲準則,List支持範型,可以在編譯器期肯定容器中元素的類型。
 * </P>
 * @author huangqian(huangqian866@163.com)
 */
public interface List<E> {
	/***
	 * 返回線性表中元素的個數
	 * @return
	 */
	public int getSize();
	/***
	 * 若是線性表爲空返回true,若是線性表非空返回false
	 * @return
	 */
	public boolean isEmpty();
	/***
	 * 判斷線性表中是否包含元素e。若是包含元素e,返回true,不然返回false
	 * @param e
	 * @return
	 */
	public boolean containes( E e);
	/***
	 * <p>獲取元素e在線性表中索引,<br/>
	 * 若是線性表中不存在e,則返回-1,<br/>
	 * 不然返回e所在位置的索引
	 * </p>
	 * @param e
	 * @return 若是線性表中不存在e,則返回-1,<br/>
	 * 不然返回e所在位置的索引
	 */
	public int indexOf(E e);
	/***
	 * 在指定的位置(index)處插入元素e
	 * @param index
	 * @param e
	 */
	public void insert(int index,E e)throws OutOfBoundaryException;
	
	/***
	 * 將元素e插入到e1前面
	 * @param e1 參考元素
	 * @param e 插入的元素
	 * @return
	 */
	public boolean insertBefore(E e1,E e );
	/***
	 * 將元素e插入到e1後面
	 * @param e1 參考元素
	 * @param e 插入的元素
	 * @return
	 */
	public boolean insertAfter(E e1, E e);
	
	/***
	 * 刪除線性表中索引爲index的元素,並將其返回
	 * @param index
	 * @return 返回下表索引爲index的元素
	 * @throws OutOfBoundaryException
	 */
	public  E remove(int index) throws OutOfBoundaryException;
	/***
	 * 從集合中刪除元素e
	 * @param e
	 * @return 若是成功刪除則返回true,不然返回false。
	 */
	public boolean remove(E e);
	/***
	 * 用元素e替換線性表中索引位置index處的元素,並將替換的元素返回。
	 * @param index 目標索引
	 * @param e 新元素
	 * @return 若是成功替換,將被替換的元素返回。
	 * @throws OutOfBoundaryException 索引越界異常
	 */
	public E replace(int index,E e)throws OutOfBoundaryException;
	/***
	 * 獲取位置爲index處的元素
	 * @param index 目標元素的位置
	 * @return 若是index未超出線性表索引範圍,則返回index處的元素。
	 * @throws OutOfBoundaryException 索引位置越界異常
	 */
	public E get(int index) throws OutOfBoundaryException;
	

}

 

 4、線性表的順序存儲的實現

   線性表的順序存儲是用一組連續的存儲單元存儲數據元素,假如每一個元素佔k個存儲單元,那麼

    location(a(n+1)) = location(a(n)) + k

  所以:

    location(a(n)) = location(a(0)) + n*k

   由於數組的物理內存單元是連續的,在這裏我能夠藉助數組,去實現線性表的順序存儲。

package com.util.linear;


/****
 * 現行表順序存儲的實現
 * @author huangqian
 *
 * @param <E>
 */
public class ArrayList<E> implements List<E> {
   //順訓表種元素的個數
	private int size;
	private Object [] elements ;
	private  int capacity;
	
	

	public ArrayList() {
		 //capacity默認值爲8
		this(8);
	}
	
	public ArrayList(int initCapacity){
		this.capacity = initCapacity;
		elements = new Object[initCapacity];
	}

	@Override
	public int getSize() {
		return size;
	}

	@Override
	public boolean isEmpty() {
		return size==0 || elements.length ==0;
	}

	/***
	 * 若是包含對應的元素,返回true,不然返回false
	 */
	@Override
	public boolean containes(E e) {
		return indexOf(e) >-1;
	}

	/***
	 * 查詢到了返回對應的索引值,不然返回-1
	 */
	@Override
	public int indexOf(E e) {
		if(e ==null){
			for(int i=0;i<elements.length;i++){
				if(elements[i]==null){
					return i;
				}
			}
		}else{
			for(int i=0;i<elements.length;i++){
				if(e.equals(elements[i])){
					return i;
				}
			}
		}
		return -1;
	}

	@Override
	public void insert(int index, E e) throws OutOfBoundaryException {
          if(index >size){
        	  throw new OutOfBoundaryException("插入的索引超過了數組的範圍");
          }
          //空間不足,須要從新開闢空間
          if(size == elements.length){
        	  //倍增可以提升效率
        	  extendSpace(elements.length*2);
          }
		for (int i = size; i > index; i--) {
			elements[i] = elements[i - 1];
		}
		elements[index] = e;
		size++;
	}
	/***
	 * 擴展存儲空間
	 * @param newLen
	 */
	private void extendSpace(int newLen){
		   Object[] tmp = elements;
		   elements = new Object[newLen];
		   for(int i=0;i<tmp.length;i++){
			   elements[i] = tmp[i];
		   }
		   tmp = null;
	}
	/***
	 * 若是實際內存利用不超過當前空間得一半,而且當前存儲空間的長度爲capacity的2倍以上,則回收一半的存儲空間。不然,不會回收存儲空間
	 */
	private void free(){
		if(size * 2 < elements.length&& capacity * 2 < elements.length){
			Object[] tmp = elements;
			int newLen;
			newLen = elements.length % 2 == 0 ? elements.length % 2  : (elements.length % 2) +1;
			elements = new Object[newLen];
			for(int i = 0; i < size ; i++){
				elements[i] = tmp[i];
			}
			tmp = null;
		}
	}

	/***
	 * 在e1後面插入元素e.若是找到了e1,而且插入元素e返回true,不然返回false
	 */
	@Override
	public boolean insertBefore(E e1, E e) {
		int index = indexOf(e1);
		if(index == -1){
			return false;
		}else{
			insert(index+1,e);
			return true;
		}
	}

	@Override
	public boolean insertAfter(E e1, E e) {
		int index = indexOf(e1);
		if(index < 0){
			return false;
		}else {
			insert(index,e);
			return true;
		}
	}

	@Override
	public E remove(int index) throws OutOfBoundaryException {
	     if(index < 0 || index >= size){
	    	 throw new OutOfBoundaryException("指定的下表索引越界");
	     }
	     E returnObj = (E)elements[index];
	     for(int i=index;i < size-1;i++){
	    	 elements[i] = elements[i+1];
	     }
	     size--;
	     //檢查是否回收空間
	     free();
		return returnObj;
	}

	@Override
	public boolean remove(E e) {
		int index = indexOf(e);
		if(index < 0){
			return false;
		}else{
			remove(index);
			return true;
		}
	}

	@Override
	public E replace(int index, E e) throws OutOfBoundaryException {
		if(index < 0 || index >= size){
			 throw new OutOfBoundaryException("指定的下標索引越界");
		}
		@SuppressWarnings("unchecked")
		E rst =(E)elements[index];
		elements[index] = e;
		return rst;
	}

	@SuppressWarnings("unchecked")
	@Override
	public E get(int index) throws OutOfBoundaryException {
		if(index < 0 || index >= size){
			throw new OutOfBoundaryException("指定的下標索引越界");
		}
		return ((E)elements[index]);
	}

}

在線性表的操做中確定會遇到內存不足,須要擴展內存空間的。我依然還記得大學學習C語言的時候,每次定義一個宏INC_SIZE。每次存儲空間不足的時候,就增長INC_SIZE。若是每次咱們增長的空間都是一個固定值,會有一個問題,好比個人INC_SIZE是100,我忽然須要插入1000元素,這個時候就作10次內存空間的從新開闢的工做,換句話說,我須要插入n個元素,我花在開闢空間的時間是n/INC_SIZE,我再次花銷的時間複雜度爲o(n)。比較友好一點的作法是倍增,時間複雜度變爲了log2(n)。

   還有一點,當咱們的線性表的存儲空間使用不到一半的時候,應該考慮回收空間。個人代碼代碼裏面free在下一次GC以前,我其實是增長了內存。

相關文章
相關標籤/搜索