資料結構單鏈表的定義(Java)
阿新 • • 發佈:2019-02-14
定義一個介面
public interface ILinarList<E> {
public abstract boolean add(E item); //新增元素
public abstract boolean add(int i,E item); //插入元素
public abstract E remove(int i); //刪除元素
public abstract int indexOf(E item); //定位元素
public abstract E get(int i); //取表元素
public abstract int size(); //求線性表長度
public abstract void clear(); //清空線性表
public abstract boolean isEmpty(); //判斷線性表是否為空
}
定義一個結點類
/*
* 單鏈表的的原理就在結點這裡,在每一個結點中都包含一個下一個結點的地址,
* 如果想要訪問下一個結點,就要通過上一個結點來呼叫他的成員變數next,以此類推
*/
public class Node<E> {
E item;//建立一個泛型類item
Node<E> next;//建立一個結點類型別
public Node(E element,Node <E> next) {
this.item=element;
this.next=next;
}
}
建立一個SLinkList類來實現介面
public class SLinkList<E> implements ILinarList<E>{
private Node<E> start;//1 新建一個空的帶頭節點
int size;
public SLinkList(){
start=null;
size=0;
}
/*一個節點中的成員變數指向一個新的Node,從例項物件的角度,整條連結串列中有例項化的物件為start,
* 詳見1、2,可以看出,private Node<E> start=new Node<E>(item,null),例項化了一個start
* 接下來add方法中,Node<E> current=start 為引用了start 的地址,start 的成員變數next的型別是Node<E>的引用型別,
* 裡面包含一個E型別的item,和一個Node<E>型別的next
* 也就是說每新增一個節點都是沒有例項化物件的,直接從start 的next成員指向一個新的Node<E>,
* 再新增就是從新的Node<E>中的next成員指向新的Node<E>
* 如下面的語句
*/
//新增元素到連結串列的末尾
public boolean add(E item) {
if(start==null)//如果帶頭節點為空,將新建一個帶頭節點
start=new Node<E>(item,null);//2
else {//如果帶頭節點不為空的話,新建一個current指向start的地址
Node<E> current=start;
while(current.next!=null){//依次訪問每一個節點,當一個節點的next為空時,說明此節點為最後一個節點
current=current.next;//注意,這裡的賦值,每次都是賦予地址,並不是修改
}
current.next=new Node<E>(item,null);//引用最後一個節點的next,建立一個新的節點
}
size++;
return true;
}
//插入元素到i索引位置前插入一個數據元素
public boolean add(int i, E item) {
if(i<0||i>size)//等於size時候即為在末尾調加一個元素
throw new IndexOutOfBoundsException("Index:"+i+",Size:"+size);//丟擲越界異常
Node<E> newnode=new Node<E>(item, null);
if(i==0){//第一個元素前插入一個元素
newnode.next=start;//newnode.next指向start的地址,此時原頭元素有了一個指向他的地址
start=newnode;//start的地址指向newnode的地址,所以此時有兩個例項物件指向帶頭元素,分別是start和newnode
size++;
}else{
Node<E> current=start;//將current指向頭元素的地址
Node<E> previous=null;
int number=0;//定義一個number用來計算訪問節點的次數
while(number<i){//從0開始自加,加到i時候,此時current指向i的索引位置,previous指向前一個
previous=current;//previous始終儲存current前一個節點的地址
current=current.next;//迴圈訪問current的next的地址
number++;//每訪問一次number自加一次
}
previous.next=newnode;//將前一個節點的地址的next指向修改為newnode
newnode.next=current;//將newnode的next指向當前的節點元素
size++;
}
return true;
}
//清楚索引的元素
public E remove(int i) {
E oidValue=null;//先賦值為null,兩個迴圈都沒有進入時,此方法返回一個null
if(i<0||i>size-1)
throw new IndexOutOfBoundsException("Index:"+i+",Size:"+size);//丟擲越界異常
else if(!isEmpty()){//當整個連結串列不為空時,進去此語句
if(i==0){//當要清楚頭元素時
oidValue=start.item;//將返回元素賦值
start=start.next;
size--;
}else{//以下方法同add重寫的第二次方法
Node<E> previous=null;
Node<E> current=start;
int number=0;
while(number<i){
previous=current;
current=current.next;
number++;
}
oidValue=current.item;
previous.next=current.next;//將指定元素的前一個元素的next指向指定元素的下一個元素
current.next=null;//釋放當前元素的next
current=null;//同上(個人覺得不做這兩步好像也沒有什麼問題?)
size--;
}
}
return oidValue;
}
public int indexOf(E item) {
int number=0;
if(item==null){//需要單獨對item==null的情況判定,因equals沒有比較null,空指標錯誤
for(Node<E> x=start;x!=null;x=x.next){
if(x.item==null)
return number;
number++;
}
}else{
for(Node<E>x=start;x!=null;x=x.next){
if(item.equals(x.item))//判定是否為所要的元素
return number;
number++;
}
}
return -1;//當遍歷完整個連結串列之後沒有所要的元素,說明此連結串列不存在此元素,返回-1
}
public E get(int i) {
E item=null;
if(i<0||i>size-1)
throw new IndexOutOfBoundsException("Index:"+i+",Size:"+size);//丟擲越界異常
else if(!isEmpty()){
Node<E> current=start;
for(int j=0;j<i;j++)//當知道要取第i個元素的時候,用for迴圈執行i次
current=current.next;
item=current.item;
}
return item;
}
public int size() {
return size;
}
public void clear() {
for(Node<E> x=start;x!=null;){//當x為空時停止
Node<E> next=x.next;//儲存x.next的地址
x.item=null;//將x中的元素清空
x.next=null;//將x的指向清空,注意,x.next不代表是下一個節點,僅僅是指向下一個節點的地址
x=next;
}
start =null;//start也是指向一片地址,如果沒有清空的話,依然可以訪問,只不過start.item和start.next都為空罷了
size=0;
}
public boolean isEmpty() {
return size==0;
}
}