日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

java数据接口之链表_Java数据结构和算法之链表

發布時間:2024/7/19 java 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java数据接口之链表_Java数据结构和算法之链表 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

三、鏈表

鏈結點

在鏈表中,每個數據項都被包含在‘點“中,一個點是某個類的對象,這個類可認叫做LINK。因為一個鏈表中有許多類似的鏈結點,所以有必要用一個不同于鏈表的類來表達鏈結點。每個LINK對象中都包含一個對下一個點引用的字段(通常叫做next)但是本身的對象中有一個字段指向對第一個鏈結點的引用。

單鏈表

用一組地址任意的存儲單元存放線性表中的數據元素。

以元素(數據元素的映象) ?+ 指針(指示后繼元素存儲位置) ?= 結點(表示數據元素 或 數據元素的映象)

以“結點的序列”表示線性表,稱作線性鏈表(單鏈表)

單鏈表是一種順序存取的結構,為找第 i 個數據元素,必須先找到第 i-1 個數據元素。

因此,查找第 i 個數據元素的基本操作為:移動指針,比較 j 和 i

1、鏈接存儲方法 ?鏈接方式存儲的線性表簡稱為鏈表(Linked List)。

鏈表的具體存儲表示為:

① 用一組任意的存儲單元來存放線性表的結點(這組存儲單元既可以是連續的,也可以是不連續的)

② 鏈表中結點的邏輯次序和物理次序不一定相同。為了能正確表示結點間的邏輯關系,在存儲每個結點值的同時,還必須存儲指示其后繼結點的地址(或位置)信息(稱為指針

(pointer)或鏈(link))

注意: ?鏈式存儲是最常用的存儲方式之一,它不僅可用來表示線性表,而且可用來表示各種非線性的數據結構。

2、鏈表的結點結構

┌──--┬──--┐

│data │next│

└──---┴─--─┘

data域--存放結點值的數據域

next域--存放結點的直接后繼的地址(位置)的指針域(鏈域)

注意:

①鏈表通過每個結點的鏈域將線性表的n個結點按其邏輯順序鏈接在一起的。

②每個結點只有一個鏈域的鏈表稱為單鏈表(Single Linked List)。

【例】線性表(bat,cat,eat,fat,hat,jat,lat,mat)的單鏈表示如示意圖

3、頭指針head和終端結點指針域的表示 ?單鏈表中每個結點的存儲地址是存放在其前趨結點next域中,而開始結點無前趨,故應設頭指針head指向開始結點。

注意: ?鏈表由頭指針唯一確定,單鏈表可以用頭指針的名字來命名。

【例】頭指針名是head的鏈表可稱為表head。 ?終端結點無后繼,故終端結點的指針域為空,即NULL。

4、單鏈表的一般圖示法 ?由于我們常常只注重結點間的邏輯順序,不關心每個結點的實際位置,可以用箭頭來表示鏈域中的指針,線性表(bat,cat,fat,hat,jat,lat,mat)的單鏈表就可以表示為下圖形式。 ?bat->cat->fat->hat->jat->lat->mat

5、單鏈表類型描述

typedef char DataType; //假設結點的數據域類型為字符

typedef struct node{ //結點類型定義

DataType data; //結點的數據域

struct node *next;//結點的指針域

}ListNode

typedef ListNode *LinkList;

ListNode *p;

LinkList head;

注意:

①*LinkList和ListNode是不同名字的同一個指針類型(命名的不同是為了概念上更明確)

②*LinkList類型的指針變量head表示它是單鏈表的頭指針

③ListNode類型的指針變量p表示它是指向某一結點的指針

6、指針變量和結點變量

指針變量????????????? ?│??????????? ?結點變量

│ 定義??? ?│在變量說明部分顯式定義? │在程序執行時,通過標準函數malloc生成

│ 取值???? │ 非空時,存放某類型結點 │實際存放結點各域內容的地址

│操作方式│ 通過指針變量名訪問????? ?│ 通過指針生成、訪問和釋放

①生成結點變量的標準函數 ?p=( ListNode *)malloc(sizeof(ListNode)); ?//函數malloc分配一個類型為ListNode的結點變量的空間,并將其首地址放入指針變量p中

②釋放結點變量空間的標準函數 ?free(p);//釋放p所指的結點變量空間

③結點分量的訪問 ?利用結點變量的名字*p訪問結點分量

方法一:(*p).data和(*p).next

方法二:p-﹥data和p-﹥next

④指針變量p和結點變量*p的關系

指針變量p的值——結點地址

結點變量*p的值——結點內容

(*p).data的值——p指針所指結點的data域的值

(*p).next的值——*p后繼結點的地址

*((*p).next)——*p后繼結點

注意:

① 若指針變量p的值為空(NULL),則它不指向任何結點。此時,若通過*p來訪問結點就意味著訪問一個不存在的變量,從而引起程序的錯誤。

② 有關指針類型的意義和說明方式的詳細解釋 ?可見,在鏈表中插入結點只需要修改指針。但同時,若要在第i個結點之前插入元素,修改的是第i-1個結點的指針。

因此,在單鏈表中第i個結點之前進行插入的基本操作為: 找到線性表中第i-1個結點,然后修改其指向后繼的指針。

雙端鏈表

雙端鏈表與傳統的鏈表非常相似,但是它有一個新增的特性:即對最后一個鏈結點的引用,就像對第一個鏈結點的引用一樣。

對最后一個鏈結點的引用允許像在表頭一樣,在表尾直接插入一個鏈結點。當然,仍然可以在普通的單鏈表的表尾插入一個鏈結點,方法是遍歷整個鏈表直到到達表尾,但是這種方法效率很低。

對最后一個鏈結點的引用允許像在表頭一樣,在表尾直接插入一個鏈結點。當然,仍然可以在普通的單鏈表的表尾插入一個鏈結點,方法是遍歷整個鏈表直到到達表尾,但是這種方法效率很低。

像訪問表頭一樣訪問表尾的特性,使雙端鏈表更適合于一些普通鏈表不方便操作的場合,隊列的實現就是這樣一個情況。

下面是一個雙端鏈表的例子。

class Link3{

public long dData;

public Link3 next;

public Link3(long d){

dData=d;

}

public void displayLink(){

System.out.print(dData+" ");

}

}

//

class FirstLastList{

private Link3 first;

private Link3 last;

public FirstLastList(){

first=null;

last=null;

}

public boolean isEmpty(){

return first==null;

}

public void insertFirst(long dd){

Link3 newLink=new Link3(dd); ??.

if(isEmpty()){

last=newLink;

newLink.next=first;

first=newLink;

}

}

public void insertLast(long dd){

Link3 newLink=new Link3(dd);

if(isEmpty()){

first=newLink;

}else{

last.next=newLink;

last=newLink;

}

}

public long deleteFirst(){

long temp=first.dData;

if(first.next==null){

last=null;

first=first.next;

return temp;

}

}

public void displayList(){

System.out.print("List (first-->last): ");

Link3 current=first;

while(current!=null){

current.displayLink();

current=current.next;

}

System.out.println("");

}

}

//

public class FirstLastApp {

public static void main(String[] args){

FirstLastList theList=new FirstLastList();

theList.insertFirst(22);

theList.insertFirst(44);

theList.insertFirst(66);

theList.insertLast(11);

theList.insertLast(33);

theList.insertLast(55);

theList.displayList();

theList.deleteFirst();

theList.deleteFirst();

theList.displayList();

}

}

為了簡單起見,在這個程序中,把每個鏈結點中的數據字段個數從兩個壓縮到一個。這更容易顯示鏈結點的內容。(記住,在一個正式的程序中,可能會有非常多的數據字段,或者對另外一個對象的引用,那個對象也包含很多數據字段。)

這個程序在表頭和表尾各插入三個鏈點,顯示插入后的鏈表。然后刪除頭兩個鏈結點,再次顯示。

注意在表頭重復插入操作會顛倒鏈結點進入的順序,而在表尾的重復插入則保持鏈結點進入的順序。

雙端鏈表類叫做FirstLastList。它有兩個項,first和last,一個指向鏈表中的第一個鏈結點,另一個指向最后一個鏈結點。如果鏈表中只有一個鏈結點,first和last就都指向它,如果沒有鏈結點,兩者都為Null值。

這個類有一個新的方法insertLast(),這個方法在表尾插入一個新的鏈結點。這個過程首先改變last.next,使其指向新生成的鏈結點,然后改變last,使其指向新的鏈結點。 ? 插入和刪除方法和普通鏈表的相應部分類似。然而,兩個插入方法都要考慮一種特殊情況,即插入前鏈表是空的。如果isEmpty()是真,那么insertFirst()必須把last指向新的鏈結點,insertLast()也必須把first指向新的鏈結點。

如果用insertFirst()方法實現在表頭插入,first就指向新的鏈結點,用insertLast()方法實現在表尾插入,last就指向新的鏈結點。如果鏈表只有一個鏈結點,那么多表頭刪除也是一種特殊情況:last必須被賦值為null值。

不幸的是,用雙端鏈表也不能有助于刪除最后一個鏈結點,因為沒有一個引用指向倒數第二個鏈結點。如果最后一個鏈結點被刪除,倒數第二個鏈結點的Next字段應該變成Null值。為了方便的刪除最后一個鏈結點,需要一個雙向鏈表。(當然,也可以遍歷整個鏈表找到最后一個鏈結點,但是那樣做效率不是很高。) ?有序鏈表 ?在有序鏈表中,數據是按照關鍵值有序排列的。有序鏈表的刪除常常是只限于刪除在鏈表頭部的最小鏈結點。不過,有時也用Find()方法和Delete()方法在整個鏈表中搜索某一特定點。

一般,在大多數需要使用有序數組的場合也可以使用有序鏈表。有序鏈表優于有序數組的地方是插入的速度,另外鏈表可以擴展到全部有效的使用內存,而數組只能局限于一個固定的大小中。但是,有序鏈表實現起來比有序數組更困難一些。

后而將看到一個有序鏈表的應用:為數據排序。有序鏈表也可以用于實現優先級隊列,盡管堆是更常用的實現方法。 ? 在有序鏈表中插入一個數據項的Java代碼,為了在一個有序鏈表中插入數據項,算法必須首先搜索鏈表,直到找到合適的位置:它恰好在第一個比它大的數據項的前面。

當算法找到了要插入的位置,用通常的方式插入數據項:把新鏈結點的Next字段指向下一個鏈結點,然后把前一個鏈結點的Next字段改為指向新的鏈結點。然而,需要考慮一些特殊情況:鏈結點有可以插在表頭,或者插在表尾。看一下這段代碼:

Public void insert(long key){

Link newLink= new Link(key);

Link previous= null;

Link current=first;

While(current!= null&&key>current.dData){

Previous= current;

Current=current.next;

}

if(previous== null){

First=newLink;

}else{

Previous.next= newLink;

newLink.next=current;

}

在鏈表上移動時,需要用一個previous引用,這樣才能把前一個鏈結點的Next字段指向新的鏈結點。創建新鏈結點后,把current變量設為first,準備搜索正確的插入點。這時也把previous設為Null值,這步操作很重要,因為后面要用這個Null值判斷是否仍在表頭。

While循環和以前用來搜索插入點的代碼類似,但是有一個附加的條件。如果當前檢查的鏈結點的關鍵值不再小于待插入的鏈結點的關鍵值,則循環結束;這是最常見的情況,即新關鍵值插在鏈表中部的某個地方。 然而,如果current為Null值,while循環也會停止。這種情況發生在表尾,或者鏈表為空時。

如果current在表頭或者鏈表為空,previous將為Null值;所以讓first指向新的鏈結點。否則current處在鏈表中部或結尾,就使previous的next字段指向新的鏈結點。 不論哪種情況、都讓新鏈結點的Next字段指向current。如果在表尾,current為Null值,則新鏈結點的Next字段也本應該設為這個值(Null)。

下面是有序鏈表的程序 ?SortedList.java程序實現了一個SortedList類,它擁有insert()、remove()和displayList()方法。只有insert()方法與無序鏈表中的insert()方法不同。

package 有序鏈表;

class Link{

public long dData;

public Link next;

public Link(long dd){

dData= dd;

}

//.........................

public void displayLink(){

System.out.print(dData+" ");

}

}

class SortedList{

private Link first;

//..........................

public SortedList(){

first=null;

}

//.........................

public boolean isEmpty(){

return (first== null);

}

//..........................

public void insert(long key){

Link newLink=new Link(key);

Link previous= null;

Link current= first;

while(current!=null&&key>current.dData){

previous=current;

current=current.next;

}

if(previous== null){

first=newLink;

} else{

previous.next= newLink;

newLink.next=current;

}

//................................

public Link remove(){

Link temp=first;

first= first.next;

return temp;

}

//................................

public void displayList(){

System.out.print("List (first-->last): ");

Link current= first;

while(current!=null){

current.displayLink();

current= current.next;

}

System.out.println("");

}

}

public class SortedLinkApp{

public static void main(String[] args){

SortedList theSortedList=new SortedList();

theSortedList.insert(20);

theSortedList.insert(40);

theSortedList.displayList();

theSortedList.insert(10);

theSortedList.insert(30);

theSortedList.insert(50);

theSortedList.displayList();

theSortedList.remove();

theSortedList.displayList();

System.exit(0);

}

}

在Main()方法中,插入值為20和40的兩個鏈結點。然后再插入三個鏈結點,分別是10、30和50。這三個值分別插在表頭、表中和表尾。這說明insert()方法正確地處理了特殊情況。最后刪除了一個鏈結點,表現出刪除操作總是從表頭進行。每一步變化后,都顯示整個鏈表。

雙向鏈表

雙向鏈表也叫雙鏈表,是鏈表的一種,它的每個數據結點中都有兩個指針,分別指向直接后繼和直接前驅。所以,從雙向鏈表中的任意一個結點開始,都可以很方便地訪問它的前驅結點和后繼結點。一般我們都構造雙向循環鏈表。

/* 線性表的雙向鏈表存儲結構 */

typedef struct DuLNode{

ElemType data;

struct DuLNode *prior,*next;

}DuLNode,*DuLinkList;

/*帶頭結點的雙向循環鏈表的基本操作(14個) */

void InitList(DuLinkList *L){/* 產生空的雙向循環鏈表L */

*L=(DuLinkList)malloc(sizeof(DuLNode)); ??if(*L){

(*L)->next=(*L)->prior=*L;

}else{

exit(OVERFLOW);

}

void DestroyList(DuLinkList *L){

/* 操作結果:銷毀雙向循環鏈表L */

DuLinkList q,p=(*L)->next;/* p指向第一個結點 */

while(p!=*L){

/* p沒到表頭 */

q=p->next;

free(p);

p=q;

}

free(*L);

*L=NULL;

}

void ClearList(DuLinkList L) {/* 不改變L */

/* 初始條件:L已存在。操作結果:將L重置為空表 */

DuLinkList q,p=L->next; /* p指向第一個結點 */

while(p!=L) {/* p沒到表頭 */

q=p->next;

free(p);

p=q;

}

L->next=L->prior=L; /* 頭結點的兩個指針域均指向自身 */

}

Status ListEmpty(DuLinkList L){ /* 初始條件:線性表L已存在。操作結果:若L為空表,則返回TRUE,否則返回FALSE */

if(L->next==L&&L->prior==L){

return TRUE;

}else{

return FALSE;

}

int ListLength(DuLinkList L){ ?/* 初始條件:L已存在。操作結果:返回L中數據元素個數 */

int i=0;

DuLinkList p=L->next; /* p指向第一個結點 */

while(p!=L) {/* p沒到表頭 */

i++;

p=p->next;

}

return i;

}

Status GetElem(DuLinkList L,int i,ElemType *e){

/* 當第i個元素存在時,其值賦給e并返回OK,否則返回ERROR */

int j=1; /* j為計數器 */

DuLinkList p=L->next; /* p指向第一個結點 */

while(p!=L&&jnext){

j++;

}

if(p==L||j>i) /* 第i個元素不存在?{ */

return ERROR;

*e=p->data; /* 取第i個元素 */

return OK;

}

int LocateElem(DuLinkList L,ElemType e,Status(*compare)(ElemType,ElemType)){

/* 初始條件:L已存在,compare()是數據元素判定函數 */

/* 操作結果:返回L中第1個與e滿足關系compare()的數據元素的位序。 */

/* 若這樣的數據元素不存在,則返回值為0 */

int i=0;

DuLinkList p=L->next;/* p指向第1個元素 */

while(p!=L){

i++;

if(compare(p->data,e)){ /* 找到這樣的數據元素 */

return i;

p=p->next;

}

return 0;

}

Status PriorElem(DuLinkList L,ElemType cur_e,ElemType *pre_e) ? { /* 操作結果:若cur_e是L的數據元素,且不是第一個,則用pre_e返回它的前驅, */

/* 否則操作失敗,pre_e無定義 */

DuLinkList p=L->next->next; /* p指向第2個元素 */

while(p!=L){ /* p沒到表頭*/

if(p->data==cur_e){

*pre_e=p->prior->data;

return TRUE;

}

p=p->next;

}

return FALSE;

}

Status NextElem(DuLinkList L,ElemType cur_e,ElemType *next_e){

/* 操作結果:若cur_e是L的數據元素,且不是最后一個,則用next_e返回它的后繼, */

/* 否則操作失敗,next_e無定義 */

DuLinkList p=L->next->next; /* p指向第2個元素 */

while(p!=L){ /* p沒到表頭 */

if(p->prior->data==cur_e) ?? {

*next_e=p->data;

return TRUE;

}

p=p->next;

}

return FALSE;

}

DuLinkList GetElemP(DuLinkList L,int i) {

/* 另加 */

/* 在雙向鏈表L中返回第i個元素的地址。i為0,返回頭結點的地址。若第i個元素不存在,*/

/* 返回NULL */

int j;

DuLinkList p=L; /* p指向頭結點 */

if(i<0||i>ListLength(L)){ /* i值不合法 */

return NULL;

for(j=1;j<=i;j++){

p=p->next;

return p;

}

Status ListInsert(DuLinkList L,int i,ElemType e) ? { /* 在帶頭結點的雙鏈循環線性表L中第i個位置之前插入元素e,i的合法值為1≤i≤表長+1 */

/* 改進算法2.18,否則無法在第表長+1個結點之前插入元素 */

DuLinkList p,s;

if(i<1||i>ListLength(L)+1){ /* i值不合法 */

return ERROR;

p=GetElemP(L,i-1); /* 在L中確定第i個元素前驅的位置指針p */

if(!p) {/* p=NULL,即第i個元素的前驅不存在(設頭結點為第1個元素的前驅) */

return ERROR;

s=(DuLinkList)malloc(sizeof(DuLNode));

if(!s){

return OVERFLOW;

s->data=e;

s->prior=p; /* 在第i-1個元素之后插入 */

s->next=p->next;

p->next->prior=s;

p->next=s;

return OK;

}

Status ListDelete(DuLinkList L,int i,ElemType *e){ /* 刪除帶頭結點的雙鏈循環線性表L的第i個元素,i的合法值為1≤i≤表長 */

DuLinkList p;

if(i<1){ /* i值不合法 */

return ERROR;

}

p=GetElemP(L,i); /* 在L中確定第i個元素的位置指針p */

if(!p) {/* p=NULL,即第i個元素不存在 */

return ERROR;

}

*e=p->data;

p->prior->next=p->next;

p->next->prior=p->prior;

free(p);

return OK;

}

void ListTraverse(DuLinkList L,void(*visit)(ElemType)) ? { /* 由雙鏈循環線性表L的頭結點出發,正序對每個數據元素調用函數visit() */

DuLinkList p=L->next; /* p指向頭結點 */

while(p!=L)?? {

visit(p->data);

p=p->next;

}

printf("\n");

}

void ListTraverseBack(DuLinkList L,void(*visit)(ElemType)) ? { /* 由雙鏈循環線性表L的頭結點出發,逆序對每個數據元素調用函數visit()。另加 */

DuLinkList p=L->prior; /* p指向尾結點 */

while(p!=L) ?? {

visit(p->data);

p=p->prior;

}

printf("\n");

}

迭代器

迭代器是一種對象,它能夠用來遍歷STL容器中的部分或全部元素,每個迭代器對象代表容器中的確定的地址。迭代器修改了常規指針的接口,所謂迭代器是一種概念上的抽象:那些行為上象迭代器的東西都可以叫做迭代器。然而迭代器有很多不同的能力,它可以把抽象容器和通用算法有機的統一起來。

迭代器提供一些基本操作符:*、++、==、!=、=。這些操作和C/C++“操作array元素”時的指針接口一致。不同之處在于,迭代器是個所謂的smart pointers,具有遍歷復雜數據結構的能力。其下層運行機制取決于其所遍歷的數據結構。因此,每一種容器型別都必須提供自己的迭代器。事實上每一種容器都將其迭代器以嵌套的方式定義于內部。因此各種迭代器的接口相同,型別卻不同。這直接導出了泛型程序設計的概念:所有操作行為都使用相同接口,雖然它們的型別不同。

功能 ? 迭代器使開發人員能夠在類或結構中支持foreach迭代,而不必整個實現IEnumerable或者IEnumerator接口。只需提供一個迭代器,即可遍歷類中的數據結構。當編譯器檢測到迭代器時,將自動生成IEnumerable接口或者IEnumerator接口的Current,MoveNext和Dispose方法。

特點

1.迭代器是可以返回相同類型值的有序序列的一段代碼;

2.迭代器可用作方法、運算符或get訪問器的代碼體;

3.迭代器代碼使用yield return語句依次返回每個元素,yield break將終止迭代;

4.可以在類中實現多個迭代器,每個迭代器都必須像任何類成員一樣有惟一的名稱,并且可以在foreach語句中被客戶端代碼調用;

5.迭代器的返回類型必須為IEnumerable和IEnumerator中的任意一種;

6.迭代器是產生值的有序序列的一個語句塊,不同于有一個 或多個yield語句存在的常規語句塊;

7.迭代器不是一種成員,它只是實現函數成員的方式,理解這一點是很重要的,一個通過迭代器實現的成員,可以被其他可能或不可能通過迭代器實現的成員覆蓋和重載;

8.迭代器塊在C#語法中不是獨特的元素,它們在幾個方面受到限制,并且主要作用在函數成員聲明的語義上,它們在語法上只是語句塊而已;

與50位技術專家面對面20年技術見證,附贈技術全景圖

總結

以上是生活随笔為你收集整理的java数据接口之链表_Java数据结构和算法之链表的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。