JAVA數據結構--------線性表

1、線性表定義:
java

線性表是由n(n>=0)個類型相同的數據元素組成的有限序列,第一個元素無前驅元素,最後一個無後繼元素,其餘元素有且僅有一個前驅和一個後繼。數組

線性表接口LList的定義:app

package com.list;

public interface LList<T> {
	
	boolean isEmpty();  //判斷線性表是否爲空
	int length();       //返回線性表的長度
	T get(int i);       //返回第i個元素
	void set(int i,T x); //設置第i個元素爲x
	void inset(int i,T x);//插入x做爲第i個元素
	void append(T x);    //在線性表最後插入x元素
	T remove(int i);     //刪除第i個元素並返回被刪除的對象
	void removeAll();    //刪除線性表的全部元素
	T search(T key);     //查找,返回首次出現的關鍵字爲key元素

}

2、線性表的順序表示和實現函數

一、線性表的順序存儲結構:用一組連續的內存單元依次存放線性表的數據元素,元素內存的物理存儲次序與他們在線性表中的邏輯次序相同。順序存儲的線性表也稱爲順序表。this

    順序表是一種隨機存取結構。存取任何一個元素的時間複雜度是O(1)。順序表一般採用數組存儲數據。指針

二、順序表實現類及其操做code

    分析:順序表採用數組存儲數據元素,因此要有一個成員變量elements存放線性表的一維數組。同時還要有成員變量len表示順序表的長度,且len<=elements.length。對象

package com.list.impl;

import com.list.LList;

public class SeqList<T> implements LList<T> {
	/****************************初始化*************************************************/

	private Object[] elements;  //順序表採用數據存放數據元素
	private int len;            //順序表的長度,記錄實際元素個數
	//無參的構造函數
	public SeqList(){
		this(64);        //建立默認容量的數組
	}
	//有參構造函數
	public SeqList(int size){
		this.elements=new Object[size];  //初始化存儲數組
		this.len=0;                     //設順序表的初始長度爲0
	}
	
	/***************************操做***********************************************/
	
	/**
	 * 判斷順序表是否爲空
	 */
	public boolean isEmpty() {
		//根據線性表的實際長度來判斷
		return this.len==0;
	}
	
	public int length() {
		return this.len;
	}
	
	/**
	 * 取第i個元素在數組中是[i-1]
	 */
	public T get(int i) {
		if(i>=1 && i<=this.len){
			return (T) elements[i-1];
		}
		return null;
	}
	
	/**
	 * 設置第i個元素的值爲x
	 */
	public void set(int i, T x) {
		if(x==null){
			return;
		}
		if(i>=1 && i<=this.len){
			elements[i-1]=x;
		}
		else{
			throw new IndexOutOfBoundsException(i+"");
		}
	}
	/**
	 * 返回順序表全部元素的描述字符串,形式爲(,)覆蓋Object中的toString()方法
	 */
	public String toString(){
		String str="(";
		if(this.len>0){
			str+=this.elements[0].toString();
		}
		for(int i=1;i<this.len;i++){
			str+=","+elements[i].toString();
		}
		return str+")";
	}
	/**
	 * 插入x做爲第i個元素
	 */
	public void insert(int i, T x) {
		if(x==null){
			return;
		}
		//若是數組已滿,則擴充順序表容量
		if(this.len==elements.length){
			Object[] temp=this.elements;     //建立臨時temp變量引用elements數組
			this.elements=new Object[temp.length*2];  //從新申請一個容量更大的數組
			//複製數組元素
			for(int j=1;j<=temp.length;j++){
				this.elements[j-1]=temp[j-1];
			}
		}
		//若是i小於1,則設i爲1
		if(i<1){
			i=1;
		}
		//若是插入位置大於線性表的長度,至關於在最後插入數據,則就設i爲len
		if(i>this.len){
			i=this.len+1;
		}
		//元素後移,len+1
	/*	for(int j=i;j<=this.len-1;j++){
			elements[j]=elements[j-1];
		}*/
		for(int j=this.len-1;j>=i;j--){
			this.elements[j+1]=this.elements[j];
		}
		this.elements[i-1]=x;
		this.len++;
		
	}
	//在順序表的最後插入元素x
	public void append(T x) {
		this.insert(this.len, x);
	}
	//刪除順序表的第i個元素,並返回此元素
	public T remove(int i) {
		// TODO Auto-generated method stub
		return null;
	}
	//刪除線性表中的全部元素
	public void removeAll() {
		this.len=0;
	}

	public T search(T key) {
		// TODO Auto-generated method stub
		return null;
	}
	

}

   三、順序表操做的效率分析接口

      ①、存取任何一個元素的時間複雜度爲O(1)。
內存

      ②、插入刪除效率很低,在等機率狀況下時間複雜度爲O(n)。

    四、順序表的深拷貝和淺拷貝

        拷貝構造方法:一個類的構造方法,若是其參數是該類對象,則稱爲拷貝構造方法。器主要做用是複製對象。

      (1)、順序表的淺拷貝

              將當前對象的各成員變量賦值爲實際參數對應各成員變量值,稱爲淺拷貝。

              當成員變量的數據類型爲基本數據類型時,淺拷貝可以實現對象複製功能。當成員變量的數據類型時引用類型時,淺拷貝只複製了對象引用,並無真正實現對象複製功能。

    public SeqList(SeqList<T> list){
        
        this.elements=list.elements;
        this.len=list.len;
    }

        兩個對象擁有同一個數組,形成修改、刪除、插入等操做結果相互影響。

      (2)、順序表的深拷貝

              當一個類包含引用類型的成員變量時,該類聲明的拷貝構造函數,不只要複製對象的全部基本類型成員變量值,還要從新申請引用類型變量佔用的動態存儲空間,並複製其中全部對象,這種複製方式成爲深拷貝。

public SeqList(SqeList<T> list){
    
    this.len=list.len;
    this.elements=new Object[list.elements.length];
    for(int i=0;i<list.elements.length;i++){
        this.elements[i]=list.elements[i];
    }
}

    五、順序表比較相等

        比較兩個對象順序表是否相等,是指他們的長度相同而且各對應元素相等。覆蓋Object的equals()方法:

public boolean equals(Object obj){

    if(this==obj){
      return true;
    }
    if(obj instanceof SeqList){
        SeqList<T> list=(SeqList)obj;
        if(this.length==list.length){
            for(int i=0;i<this.length;i++)
                if(!(this.get(i)).equals(list.get(i)))
                return false;
          return true;
        }
    }
    return false;
}

3、線性表的鏈式表示和實現

一、線性表的鏈式存儲:用若干地址分散的存儲單元存儲數據元素,邏輯上相鄰的數據元素在物理位置上不必定相鄰,必須採用附加信息表示數據元素之間的順序關係。所以,存儲一個數據元素的存儲單元至少包含兩部分:數據域和地址域。

二、單鏈表類實現及其操做:

分析:由於線性表的鏈式存儲結構,邏輯上相鄰的物理位置上不必定相鄰,因此存儲單元至少要包含數據域和地址域。

結點Node實現:

package com.list.impl;

public class Node<T> {
	
	private T data; //數據域,保存數據元素
	private Node<T> next;//地址與,引用後繼結點
	public Node(){
		this(null,null);
	}
	public Node(T data,Node<T> next){
		this.data=data;
		this.next=next;
	}

}

Node類時「自引用類」,指一個類聲明包含一個引用當前類的對象的成員變量。Node類的一個對象表示單鏈表中的一個結點,經過next鏈,將兩個結點連接到一塊兒。

創建連接:

Node<String> p=new Node<String>("A",null);
Node<String> q=new Node<String>("B",null);
p.next=q;

或者

Node<String> q=new Node<String>("B",null);
Node<String> p=new Node<String>("A",q);

head存儲線性鏈表第一結點的地址,稱爲頭指針。head==null時,表示空鏈表。

單鏈表的遍歷:遍歷單鏈表是指從第一個結點開始,沿着結點的next鏈,依次訪問單鏈表中的每一個結點,而且每一個結點只訪問一次。遍歷單鏈表操做不能改變頭指針head,所以須要聲明一個變量p指向當前訪問結點。p從head指向的結點開始訪問,再沿着next鏈到達後繼結點,逐個訪問。

Node<T> p=head;
while(p!=null){
    System.out.println(p.data.toString());
    p=p.next;
}

三、帶頭結點的單鏈表

帶頭結點的單鏈表是指,在單鏈表的第一個結點以前增長一個特殊的結點,稱爲頭結點。頭結點的做用是使全部鏈表(包括空表)的頭指針非空,並使單鏈表的插入、刪除操做不須要區分是否爲空表或是否在第一個位置進行,從而與其餘位置的插入、刪除操做一致。

package com.list.impl;

import com.list.LList;


/**
 * 構造帶頭結點的單鏈表
 * @author wangning
 *
 * @param <T>
 */
public class LinkList<T> implements LList<T>{
	/****************構造鏈表**************************/
	
	private Node<T> head; //頭指針,指向單鏈表的頭結點
	//默認構造方法,構造空鏈表,建立頭結點,data和next均爲null
	public LinkList(){
		this.head=new Node<T>();
	}
	//由指定的數組中的多個對象構造單鏈表,採用尾插法
	public LinkList(T[] elements){
		this();//建立空鏈表
		Node<T> rear=this.head;    //rear指向單鏈表的最後一個結點
		for(int i=0;i<elements.length;i++){
			rear.next=new Node<T>(elements[i],null);  //尾插入,建立結點鏈入rear結點以後
			rear=rear.next;                //rear指向新的鏈尾結點
		}
	}
	/*
	 * 頭查法構造鏈表
	public LinkList(T[] elements){
		this();
		for(int i=0;i<elements.length;i++){
			Node<T> p=new Node<T>(elements[i],null);//新增結點
			p.next=head.next;           //帶頭結點的寫法
			head.next=p;               //帶頭結點的寫法
		}
	}
	*/
	
	/**
	 * 判斷單鏈表是否爲空
	 */
	public boolean isEmpty() {
		return this.head.next==null;
	}
	/**
	 * 返回單鏈表的長度
	 */
	public int length() {
		int i=0;
		Node<T> p=this.head.next;
		while(p!=null){
			i++;
			p=p.next;
		}
		return i;
	}
	public String toString(){
		String str="(";
		Node<T> p=this.head.next;
		while(p!=null){
			str+=p.data.toString();
			if(p.next!=null){
				str+=",";
			}
			p=p.next;
		}
		return str+")";
	}
	/**
	 * 返回第i個元素
	 */
	public T get(int i) {
		if(i>=0){
			Node<T> p=this.head.next;  //第一個結點
			for(int j=0;p!=null && j<i; j++){
				p=p.next;
			}
			if(p!=null){
				return p.data;
			}
		}
		return null;
	}
	//設置第i個元素值爲x
	public void set(int i, T x) {
			if(x==null){
				return;
			}
			if(i>=0){
				Node<T> p=this.head.next; //第一個結點
				for(int j=0;p!=null && j<i;j++){
					p=p.next;
				}
				if(p!=null){
					p.data=x;
				}
			}
			else
				throw new IndexOutOfBoundsException();
	}
	//將x對象插入在序號爲i結點前
	public void insert(int i, T x) {
		if(x==null){
			return;
		}
		Node<T> p=this.head;  //投加點
		for(int j=0;p!=null && j<i;j++){
			p=p.next;          //i結點的前驅
		}
		p.next=new Node<T>(x,p.next);
	}

	public void append(T x) {
		this.insert(Integer.MAX_VALUE, x);
		
	}

	public T remove(int i) {
		if(i>=0){
			Node<T> p=this.head;   //頭結點
			for(int j=0;p!=null && j<i; j++){
				p=p.next;             //定位到待刪除結點的前去結點
			}
			if(p.next!=null){
				T d=p.next.data;   
				p.next=p.next.next;
				return d;
			}
		}
		return null;
	}

	public void removeAll() {
		this.head.next=null;
	}

	public T search(T key) {
		// TODO Auto-generated method stub
		return null;
	}

}

四、    單鏈表操做的效率分析

         isEmpty()方法的時間複雜度是O(1);length()方法要遍歷單鏈表,時間複雜度爲O(n)。

        單鏈表是一種順序存取結構,不是隨機存取結構。insert(p,x)。方法在單鏈表指定p結點以後插入一個結點,時間複雜度爲O(1)。insert(i,x)方法插入x做爲第i個結點,時間複雜度爲O(n),其花費時間視插入位置而定,若在單鏈表最後插入,則時間複雜度爲O(n)。

         對單鏈表進行插入和刪除操做只要改變少許結點的鏈,不須要移動數據元素。單鏈表中的結點在刪除和插入過程當中,是動態釋放和申請的,不須要預先給單鏈表分配存儲空間,從而避免了順序表因存儲空間不足擴充空間和複製元素的過程,提供了運行效率和存儲空間利用率。

       提升單鏈表操做效率的措施:

       因爲單鏈表長度的length()方法須要遍歷整個單鏈表,因此在某些時候須要使用長度的狀況下,經歷避免兩次遍歷單鏈表。

public boolean append(T x){
return insert(this.length(),x);
}

   insert將遍歷單鏈表2次,改成rentrun insert(Integer.MAX_VALUE,x);則只需遍歷一次就將x結點插入在單鏈表以後。若是在單例表中添加某些私有成員變量,則可提升某些操做的效率,例如添加len變量表示單鏈表長度,添加rear做爲單鏈表的尾指針。

相關文章
相關標籤/搜索