3DM速報:《NBA 2K22》正式公佈 《FIFA 22》Steam國區被退款
阿新 • • 發佈:2021-07-15
1、定義一個結構體
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef int ElemType; // 自定義連結串列的資料元素為整數。
typedef struct DNode
{
ElemType data; // 存放結點的資料元素。
struct DNode *prior,*next; // 前驅和後繼結點的指標。
}DNode,*LinkList;
int main()
{
return 0;
}
2、初始化連結串列
// 初始化連結串列LL,返回值:失敗返回NULL,成功返回頭結點的地址。 DNode *InitList1(); int main() { LinkList LL=NULL; // 宣告連結串列指標變數。 LL=InitList1(); // 初始化連結串列。 printf("LL=%p\n",LL); return 0; } // 初始化連結串列LL,返回值:失敗返回NULL,成功返回頭結點的地址。 DNode *InitList1() { DNode *head = (DNode *)malloc(sizeof(DNode)); // 分配頭結點。 if (head == NULL) return NULL; // 記憶體不足,返回失敗。 head->prior=head->next=NULL; // 前驅後繼結點都置為空。 return head; }
3、建立一個數據元素。
int main()
{
LinkList LL=NULL; // 宣告連結串列指標變數。
LL=InitList1(); // 初始化連結串列。
printf("LL=%p\n",LL);
ElemType ee; // 建立一個數據元素。
return 0;
}
4、插入元素
int InsertList(LinkList LL, unsigned int ii, ElemType *ee); int main() { LinkList LL=NULL; // 宣告連結串列指標變數。 LL=InitList1(); // 初始化連結串列。 printf("LL=%p\n",LL); ElemType ee; // 建立一個數據元素。 printf("在表中插入元素(1、2、3、4、5、6、7、8、9、10)。\n"); ee=1; InsertList(LL, 1, &ee); ee=2; InsertList(LL, 1, &ee); ee=3; InsertList(LL, 1, &ee); ee=4; InsertList(LL, 1, &ee); ee=5; InsertList(LL, 1, &ee); ee=6; InsertList(LL, 1, &ee); ee=7; InsertList(LL, 1, &ee); ee=8; InsertList(LL, 1, &ee); ee=9; InsertList(LL, 1, &ee); ee=10; InsertList(LL, 1, &ee); return 0; } // 在連結串列LL的第ii個位置插入元素ee,返回值:0-失敗;1-成功。 int InsertList(LinkList LL, unsigned int ii, ElemType *ee) { if ( (LL == NULL) || (ee == NULL) ) { printf("連結串列LL或元素ee不存在。\n"); return 0; } // 判斷表和元素是否存在。 // 判斷插入位置是否合法 if (ii < 1) { printf("插入位置(%d)不合法,應該在大於0。\n",ii); return 0; } // 要在位序ii插入結點,必須找到ii-1結點。 DNode *pp=LL; // 指標pp指向頭結點,逐步往後移動,如果為空,表示後面沒結點了。 int kk=0; // kk指向的是第幾個結點,從頭結點0開始,pp每向後移動一次,kk就加1。 while ( (pp != NULL) && (kk < ii-1) ) { pp=pp->next; kk++; // printf("pp=%p,kk=%d\n",pp,kk); } if ( pp==NULL ) { printf("位置(%d)不合法,超過了表長。\n",ii); return 0; } DNode *tmp = (DNode *)malloc(sizeof(DNode)); // 分配一個結點。 if (tmp == NULL) return 0; // 記憶體不足,返回失敗。 // 考慮資料元素為結構體的情況,這裡採用了memcpy的方法而不是直接賦值。 memcpy(&tmp->data,ee,sizeof(ElemType)); // 處理前驅後續結點的指標。 tmp->next=pp->next; tmp->prior=pp; if (tmp->next != NULL) tmp->next->prior=tmp; // 特殊處理,如果是在尾部插入,tmp->next根本不存在。 pp->next=tmp; return 1; /////////////////////////////////////// // 以上程式碼可以用以下程式碼代替。 // DNode *pp=LocateNode(LL,ii-1); // return InsertNextNode(pp,ee); /////////////////////////////////////// }
5、打印表中全部的元素
void PrintList(LinkList LL); int main() { LinkList LL=NULL; // 宣告連結串列指標變數。 LL=InitList1(); // 初始化連結串列。 printf("LL=%p\n",LL); ElemType ee; // 建立一個數據元素。 printf("在表中插入元素(1、2、3、4、5、6、7、8、9、10)。\n"); ee=1; InsertList(LL, 1, &ee); ee=2; InsertList(LL, 1, &ee); ee=3; InsertList(LL, 1, &ee); ee=4; InsertList(LL, 1, &ee); ee=5; InsertList(LL, 1, &ee); ee=6; InsertList(LL, 1, &ee); ee=7; InsertList(LL, 1, &ee); ee=8; InsertList(LL, 1, &ee); ee=9; InsertList(LL, 1, &ee); ee=10; InsertList(LL, 1, &ee); PrintList(LL); return 0; } // 列印連結串列中全部的元素。 void PrintList(LinkList LL) { if (LL == NULL) { printf("連結串列LL不存在。\n"); return; } // 判斷連結串列是否存在。 DNode *pp=LL->next; // 從第1個結點開始。 while (pp != NULL) { printf("%-3d", pp->data); // 如果元素ee為結構體,這行程式碼要修改。 pp=pp->next; } printf("\n"); /* // 以下程式碼用於顯示全部結點的地址和元素的值。 DNode *pp=LL; // 從第0個結點開始。 while (pp != NULL) { printf("pp=%p,prior=%p,next=%p,data=%-3d\n",pp,pp->prior,pp->next,pp->data); pp=pp->next; } */ }
6、尾部插入元素
int PushBack(LinkList LL, ElemType *ee);
int main()
{
LinkList LL=NULL; // 宣告連結串列指標變數。
LL=InitList1(); // 初始化連結串列。
printf("LL=%p\n",LL);
ElemType ee; // 建立一個數據元素。
printf("在表中插入元素(1、2、3、4、5、6、7、8、9、10)。\n");
ee=1; InsertList(LL, 1, &ee);
ee=2; InsertList(LL, 1, &ee);
ee=3; InsertList(LL, 1, &ee);
ee=4; InsertList(LL, 1, &ee);
ee=5; InsertList(LL, 1, &ee);
ee=6; InsertList(LL, 1, &ee);
ee=7; InsertList(LL, 1, &ee);
ee=8; InsertList(LL, 1, &ee);
ee=9; InsertList(LL, 1, &ee);
ee=10; InsertList(LL, 1, &ee);
PrintList(LL);
printf("表尾插入元素(12)。\n");
ee=12; PushBack(LL, &ee);
PrintList(LL);
return 0;
}
// 在連結串列LL的尾部插入元素ee,返回值:0-失敗;1-成功。
int PushBack(LinkList LL, ElemType *ee)
{
if ( (LL == NULL) || (ee == NULL) ) { printf("連結串列LL或元素ee不存在。\n"); return 0; } // 判斷表和元素是否存在。
DNode *pp=LL; // 從頭結點開始。
// 找到最後一個結點。
while (pp->next != NULL) pp=pp->next;
DNode *tmp = (DNode *)malloc(sizeof(DNode)); // 分配一個結點。
if (tmp == NULL) return 0; // 記憶體不足,返回失敗。
// 考慮資料元素為結構體的情況,這裡採用了memcpy的方法而不是直接賦值。
memcpy(&tmp->data,ee,sizeof(ElemType));
// 處理前驅後續結點的指標。
tmp->next=pp->next;
tmp->prior=pp;
if (tmp->next != NULL) tmp->next->prior=tmp; // 特殊處理,如果是在尾部插入,tmp->next根本不存在。
pp->next=tmp;
return 1;
}
7、刪除某個節點
int DeleteNode(LinkList LL, unsigned int ii);
int main()
{
LinkList LL=NULL; // 宣告連結串列指標變數。
LL=InitList1(); // 初始化連結串列。
printf("LL=%p\n",LL);
ElemType ee; // 建立一個數據元素。
printf("在表中插入元素(1、2、3、4、5、6、7、8、9、10)。\n");
ee=1; InsertList(LL, 1, &ee);
ee=2; InsertList(LL, 1, &ee);
ee=3; InsertList(LL, 1, &ee);
ee=4; InsertList(LL, 1, &ee);
ee=5; InsertList(LL, 1, &ee);
ee=6; InsertList(LL, 1, &ee);
ee=7; InsertList(LL, 1, &ee);
ee=8; InsertList(LL, 1, &ee);
ee=9; InsertList(LL, 1, &ee);
ee=10; InsertList(LL, 1, &ee);
PrintList(LL);
printf("刪除表中第7個結點。\n");
DeleteNode(LL,7); PrintList(LL);
return 0;
}
// 刪除連結串列LL中的第ii個結點,返回值:0-位置ii不合法;1-成功。
int DeleteNode(LinkList LL, unsigned int ii)
{
if (LL == NULL) { printf("連結串列L不存在。\n"); return 0; } // 判斷連結串列是否存在。
// 判斷刪除位置是否合法
if (ii < 1) { printf("刪除位置(%d)不合法,應該在大於0。\n",ii); return 0; }
// 要刪除位序ii結點,必須找到ii-1結點。
DNode *pp=LL; // 指標pp指向頭結點,逐步往後移動,如果為空,表示後面沒結點了。
int kk=0; // kk指向的是第幾個結點,從頭結點0開始,pp每向後移動一次,kk就加1。
while ( (pp != NULL) && (kk < ii-1) )
{
pp=pp->next; kk++;
}
// 注意,以下行的程式碼與視訊中的不一樣,視訊中的是 if ( pp==NULL ),有bug。
if ( pp->next==NULL ) { printf("位置(%d)不合法,超過了表長。\n",ii); return 0; }
DNode *tmp=pp->next; // tmp為將要刪除的結點。
// 處理前驅後續結點的指標。
pp->next=tmp->next;
if (tmp->next != NULL) tmp->next->prior=pp; // 特殊處理,如果tmp是尾結點,tmp->next根本不存在。
free(tmp);
return 1;
}
8、查詢節點地址
DNode *LocateNode(LinkList LL, unsigned int ii);
int main()
{
LinkList LL=NULL; // 宣告連結串列指標變數。
LL=InitList1(); // 初始化連結串列。
printf("LL=%p\n",LL);
ElemType ee; // 建立一個數據元素。
printf("在表中插入元素(1、2、3、4、5、6、7、8、9、10)。\n");
ee=1; InsertList(LL, 1, &ee);
ee=2; InsertList(LL, 1, &ee);
ee=3; InsertList(LL, 1, &ee);
ee=4; InsertList(LL, 1, &ee);
ee=5; InsertList(LL, 1, &ee);
ee=6; InsertList(LL, 1, &ee);
ee=7; InsertList(LL, 1, &ee);
ee=8; InsertList(LL, 1, &ee);
ee=9; InsertList(LL, 1, &ee);
ee=10; InsertList(LL, 1, &ee);
PrintList(LL);
DNode *tmp;
if ( (tmp=LocateNode(LL,3)) != NULL)
printf("第3個結點的地址是=%p,ee=%d\n",tmp,tmp->data);
return 0;
}
/ 獲取連結串列中第ii個結點,成功返回結點的地址,失敗返回空。
// 注意,ii可以取值為0,表示頭結點。
DNode *LocateNode(LinkList LL, unsigned int ii)
{
if ( LL == NULL ) { printf("連結串列LL不存在。\n"); return NULL; } // 判斷表和元素是否存在。
DNode *pp=LL; // 指標pp指向頭結點,逐步往後移動,如果為空,表示後面沒結點了。
int kk=0; // kk指向的是第幾個結點,從頭結點0開始,pp每向後移動一次,kk就加1。
while ( (pp != NULL) && (kk < ii) )
{
pp=pp->next; kk++;
}
if ( pp==NULL ) { printf("位置(%d)不合法,超過了表長。\n",ii); return NULL; }
return pp;
}
9、查詢元素地址
DNode *LocateElem(LinkList LL, ElemType *ee);
int main()
{
LinkList LL=NULL; // 宣告連結串列指標變數。
LL=InitList1(); // 初始化連結串列。
printf("LL=%p\n",LL);
ElemType ee; // 建立一個數據元素。
printf("在表中插入元素(1、2、3、4、5、6、7、8、9、10)。\n");
ee=1; InsertList(LL, 1, &ee);
ee=2; InsertList(LL, 1, &ee);
ee=3; InsertList(LL, 1, &ee);
ee=4; InsertList(LL, 1, &ee);
ee=5; InsertList(LL, 1, &ee);
ee=6; InsertList(LL, 1, &ee);
ee=7; InsertList(LL, 1, &ee);
ee=8; InsertList(LL, 1, &ee);
ee=9; InsertList(LL, 1, &ee);
ee=10; InsertList(LL, 1, &ee);
PrintList(LL);
DNode *tmp;
ee=8;
if ( (tmp=LocateElem(LL,&ee)) != NULL)
printf("元素值為8的結點的地址是=%p\n",tmp);
else
printf("元素值為8的結點的地址是NULL,沒找著。\n");
return 0;
}
// 查詢元素ee在連結串列LL中的結點地址,如果沒找到返回NULL,否則返回結點的地址。
DNode *LocateElem(LinkList LL, ElemType *ee)
{
DNode *pp=LL->next; // 從第1個數據結點開始。
while (pp != NULL)
{
// 如果資料元素是結構體,以下程式碼要修改。
if (pp->data == *ee) return pp;
pp = pp->next;
}
return NULL;
}
10、在某個節點之後插入元素
int InsertNextNode(DNode *pp, ElemType *ee);
int main()
{
LinkList LL=NULL; // 宣告連結串列指標變數。
LL=InitList1(); // 初始化連結串列。
printf("LL=%p\n",LL);
ElemType ee; // 建立一個數據元素。
printf("在表中插入元素(1、2、3、4、5、6、7、8、9、10)。\n");
ee=1; InsertList(LL, 1, &ee);
ee=2; InsertList(LL, 1, &ee);
ee=3; InsertList(LL, 1, &ee);
ee=4; InsertList(LL, 1, &ee);
ee=5; InsertList(LL, 1, &ee);
ee=6; InsertList(LL, 1, &ee);
ee=7; InsertList(LL, 1, &ee);
ee=8; InsertList(LL, 1, &ee);
ee=9; InsertList(LL, 1, &ee);
ee=10; InsertList(LL, 1, &ee);
PrintList(LL);
DNode *tmp;
ee=8;
if ( (tmp=LocateElem(LL,&ee)) != NULL)
printf("元素值為8的結點的地址是=%p\n",tmp);
else
printf("元素值為8的結點的地址是NULL,沒找著。\n");
printf("在結點%p之後插入66\n",tmp);
ee=66;
InsertNextNode(tmp,&ee); PrintList(LL);
return 0;
}
// 在指定結點pp之後插入元素ee,返回值:0-失敗;1-成功。
int InsertNextNode(DNode *pp, ElemType *ee)
{
if (pp == NULL) { printf("結點pp不存在。\n"); return 0; }
DNode *tmp = (DNode *)malloc(sizeof(DNode));
if (tmp == NULL) return 0;
memcpy(&tmp->data,ee,sizeof(ElemType));
// 處理前驅後續結點的指標。
tmp->next=pp->next;
tmp->prior=pp;
if (tmp->next != NULL) tmp->next->prior=tmp; // 特殊處理,如果是在尾部插入,tmp->next根本不存在。
pp->next=tmp;
return 1;
}
11、在某個節點之前插入元素
int InsertPriorNode(DNode *pp, ElemType *ee);
int main()
{
LinkList LL=NULL; // 宣告連結串列指標變數。
LL=InitList1(); // 初始化連結串列。
printf("LL=%p\n",LL);
ElemType ee; // 建立一個數據元素。
printf("在表中插入元素(1、2、3、4、5、6、7、8、9、10)。\n");
ee=1; InsertList(LL, 1, &ee);
ee=2; InsertList(LL, 1, &ee);
ee=3; InsertList(LL, 1, &ee);
ee=4; InsertList(LL, 1, &ee);
ee=5; InsertList(LL, 1, &ee);
ee=6; InsertList(LL, 1, &ee);
ee=7; InsertList(LL, 1, &ee);
ee=8; InsertList(LL, 1, &ee);
ee=9; InsertList(LL, 1, &ee);
ee=10; InsertList(LL, 1, &ee);
PrintList(LL);
DNode *tmp;
ee=8;
if ( (tmp=LocateElem(LL,&ee)) != NULL)
printf("元素值為8的結點的地址是=%p\n",tmp);
else
printf("元素值為8的結點的地址是NULL,沒找著。\n");
printf("在結點%p之前插入55\n",tmp);
ee=55;
InsertPriorNode(tmp,&ee); PrintList(LL);
return 0;
}
// 在指定結點pp之前插入元素ee,返回值:0-失敗;1-成功。
int InsertPriorNode(DNode *pp, ElemType *ee)
{
if (pp == NULL) { printf("結點pp不存在。\n"); return 0; }
// 在單鏈表中,如果要在指定結點pp之前插入元素,採用的是偷樑換柱的方法。
// 在雙鏈表中,偷不偷都行,因為pp->prior有前驅結點的地址。
DNode *tmp = (DNode *)malloc(sizeof(DNode));
if (tmp == NULL) return 0;
// 把待插入的元素存入tmp中。
memcpy(&tmp->data,ee,sizeof(ElemType));
// 處理前驅後繼指標。
tmp->prior=pp->prior;
pp->prior->next=tmp;
tmp->next=pp;
pp->prior=tmp;
return 1;
}
12、銷燬連結串列
void DestroyList1(LinkList LL);
int main()
{
LinkList LL=NULL; // 宣告連結串列指標變數。
LL=InitList1(); // 初始化連結串列。
printf("LL=%p\n",LL);
ElemType ee; // 建立一個數據元素。
printf("在表中插入元素(1、2、3、4、5、6、7、8、9、10)。\n");
ee=1; InsertList(LL, 1, &ee);
ee=2; InsertList(LL, 1, &ee);
ee=3; InsertList(LL, 1, &ee);
ee=4; InsertList(LL, 1, &ee);
ee=5; InsertList(LL, 1, &ee);
ee=6; InsertList(LL, 1, &ee);
ee=7; InsertList(LL, 1, &ee);
ee=8; InsertList(LL, 1, &ee);
ee=9; InsertList(LL, 1, &ee);
ee=10; InsertList(LL, 1, &ee);
PrintList(LL);
DestroyList1(LL); LL=NULL; // 銷燬連結串列,LL置為空。
return 0;
}
// 銷燬連結串列LL。
void DestroyList1(LinkList LL)
{
// 銷燬連結串列LL是指釋放連結串列全部的結點,包括頭結點。
DNode *tmp;
while(LL!=NULL)
{
tmp=LL->next; // tmp儲存下一結點的地址。
free(LL); // 釋放當前結點。
LL=tmp; // LL指標移動到下一結點。
}
// LL=NULL; // LL在本函式中相當於區域性變數,就算置空了也不會影響呼叫者傳遞的LL,所以LL=NULL沒有意義。
return;
}
13、完整的程式碼
/*
* 程式名:linklist4.c,此程式演示帶頭結點的雙鏈表的實現,資料元素是整數。
* 作者:C語言技術網(www.freecplus.net) 日期:20201230
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef int ElemType; // 自定義連結串列的資料元素為整數。
typedef struct DNode
{
ElemType data; // 存放結點的資料元素。
struct DNode *prior,*next; // 前驅和後繼結點的指標。
}DNode,*LinkList;
// 初始化連結串列LL,返回值:失敗返回NULL,成功返回頭結點的地址。
DNode *InitList1();
// 傳入指標變數的地址的方法。
// 初始化連結串列,返回值:0-失敗;1-成功。
int InitList2(LinkList *LL);
// C++引用的方法,在Linux下,需要用g++編譯。
// 初始化連結串列,返回值:0-失敗;1-成功。
// int InitList3(LinkList &LL);
// 如果引數採用轉指標LL的值,LL的值只能傳進去,無法返回,這種方法是不行的。
int InitList4(LinkList LL);
// 銷燬連結串列LL。
void DestroyList1(LinkList LL);
// 銷燬連結串列LL。
// 傳入指標的地址的方法。
void DestroyList2(LinkList *LL);
// C++引用的方法,在Linux下,需要用g++編譯。
// 傳入指標的地址的方法。
// void DestroyList3(LinkList &LL);
// 清空連結串列。
void ClearList(LinkList LL);
// 在連結串列LL的第ii個位置插入元素ee,返回值:0-失敗;1-成功。
int InsertList(LinkList LL, unsigned int ii, ElemType *ee);
// 列印連結串列中全部的元素。
void PrintList(LinkList LL);
// 在連結串列LL的頭部插入元素ee,返回值:0-失敗;1-成功。
int PushFront(LinkList LL, ElemType *ee);
// 在連結串列LL的尾部插入元素ee,返回值:0-失敗;1-成功。
int PushBack(LinkList LL, ElemType *ee);
// 刪除連結串列LL中的第ii個結點,返回值:0-位置ii不合法;1-成功。
int DeleteNode(LinkList LL, unsigned int ii);
// 刪除連結串列LL中第一個結點,返回值:0-位置不合法;1-成功。
int PopFront(LinkList LL);
// 刪除連結串列LL中最後一個結點,返回值:0-位置不合法;1-成功。
int PopBack(LinkList LL);
// 求連結串列的長度,返回值:>=0-表LL結點的個數。
int LengthList(LinkList LL);
// 判斷連結串列是否為空,返回值:0-非空或失敗,1-空。
int IsEmpty(LinkList LL);
// 獲取連結串列中第ii個結點,成功返回結點的地址,失敗返回空。
// 注意,ii可以取值為0,表示頭結點。
DNode *LocateNode(LinkList LL, unsigned int ii);
// 查詢元素ee在連結串列LL中的結點地址,如果沒找到返回NULL,否則返回結點的地址。
DNode *LocateElem(LinkList LL, ElemType *ee);
// 在指定結點pp之後插入元素ee,返回值:0-失敗;1-成功。
int InsertNextNode(DNode *pp, ElemType *ee);
// 在指定結點pp之前插入元素ee,返回值:0-失敗;1-成功。
int InsertPriorNode(DNode *pp, ElemType *ee);
// 刪除指定結點。
int DeleteNode1(DNode *pp);
int main()
{
LinkList LL=NULL; // 宣告連結串列指標變數。
LL=InitList1(); // 初始化連結串列。
// 如果要在函式中對變數進行賦值,必須把變數的地址傳入函式。
// 指標變數簡稱指標,如果要在函式中對指標變數賦值,也必須把指標的地址傳入函式。
// LL是指標,在InitList2函式中,需要把頭結點的地址賦值給LL,所以要傳入LL的地址。
// 指標是變數,用於存放變數的地址,指標不是地址,指標裡存放的內容才是地址。
// 所以,這裡要把指標變數LL的地址傳給InitList2()函式。
// 各位菜雞,明白鳥嗎?
// InitList2(&LL); // 初始化連結串列,傳入指標變數LL的地址。
// InitList3(LL); // 初始化連結串列,C++的引用。
// 如果引數採用轉指標LL的值,LL的值只能傳進去,無法返回,這種方法是不行的。
// InitList4(LL);
printf("LL=%p\n",LL);
ElemType ee; // 建立一個數據元素。
printf("在表中插入元素(1、2、3、4、5、6、7、8、9、10)。\n");
ee=1; InsertList(LL, 1, &ee);
ee=2; InsertList(LL, 1, &ee);
ee=3; InsertList(LL, 1, &ee);
ee=4; InsertList(LL, 1, &ee);
ee=5; InsertList(LL, 1, &ee);
ee=6; InsertList(LL, 1, &ee);
ee=7; InsertList(LL, 1, &ee);
ee=8; InsertList(LL, 1, &ee);
ee=9; InsertList(LL, 1, &ee);
ee=10; InsertList(LL, 1, &ee);
printf("length=%d\n",LengthList(LL));
PrintList(LL);
printf("在第5個位置插入元素(13)。\n");
ee=13; InsertList(LL, 5, &ee);
PrintList(LL);
printf("在表頭插入元素(11),表尾插入元素(12)。\n");
ee=11; PushFront(LL, &ee);
ee=12; PushBack(LL, &ee);
PrintList(LL);
printf("刪除表中第7個結點。\n");
DeleteNode(LL,7); PrintList(LL);
printf("刪除表中第一個結點。\n");
PopFront(LL); PrintList(LL);
DNode *tmp;
if ( (tmp=LocateNode(LL,3)) != NULL)
printf("第3個結點的地址是=%p,ee=%d\n",tmp,tmp->data);
ee=8;
if ( (tmp=LocateElem(LL,&ee)) != NULL)
printf("元素值為8的結點的地址是=%p\n",tmp);
else
printf("元素值為8的結點的地址是NULL,沒找著。\n");
printf("在結點%p之後插入66\n",tmp);
ee=66;
InsertNextNode(tmp,&ee); PrintList(LL);
printf("在結點%p之前插入55\n",tmp);
ee=55;
InsertPriorNode(tmp,&ee); PrintList(LL);
// 找到第10個節點並刪除它。
if ( (tmp=LocateNode(LL,10)) != NULL)
printf("第10個結點的地址是=%p,ee=%d\n",tmp,tmp->data);
printf("刪除結點%p\n",tmp);
DeleteNode1(tmp); PrintList(LL);
DestroyList1(LL); LL=NULL; // 銷燬連結串列,LL置為空。
// DestroyList2(&LL); // 銷燬連結串列,傳入指標的地址,LL在函式中會置為空。
// DestroyList3(LL); // 銷燬連結串列,C++的引用,LL在函式中會置為空。
printf("LL=%p\n",LL);
return 0;
}
// 如果引數採用轉指標LL的值,LL的值只能傳進去,無法返回,這種方法是不行的。
int InitList4(LinkList LL)
{
DNode *head = (DNode *)malloc(sizeof(DNode)); // 分配頭結點。
if (head == NULL) return 0; // 記憶體不足,返回失敗。
head->next=NULL; // 頭結點的下一結點暫時不存在,置空。
LL=head;
printf("LL1=%p\n",LL);
return 1;
}
// 初始化連結串列LL,返回值:失敗返回NULL,成功返回頭結點的地址。
DNode *InitList1()
{
DNode *head = (DNode *)malloc(sizeof(DNode)); // 分配頭結點。
if (head == NULL) return NULL; // 記憶體不足,返回失敗。
head->prior=head->next=NULL; // 前驅後繼結點都置為空。
return head;
}
// 傳入指標變數的地址的方法。
// 初始化連結串列,返回值:0-失敗;1-成功。
int InitList2(LinkList *LL)
{
// 在本函式中,LL是指標的指標,用於存放指標的地址。
if ( *LL != NULL ) { printf("連結串列LL已存在,在初始化之前請先釋放它。\n"); return 0; }
DNode *head = (DNode *)malloc(sizeof(DNode)); // 分配頭結點。
if (head == NULL) return 0; // 記憶體不足,返回失敗。
head->prior=head->next=NULL; // 前驅後繼結點都置為空。
*LL=head;
return 1;
}
/*
// C++引用的方法。
// 初始化連結串列,返回值:0-失敗;1-成功。
int InitList3(LinkList &LL)
{
if ( LL != NULL ) { printf("連結串列L已存在,在初始化之前請先釋放它。\n"); return 0; }
DNode *head = (DNode *)malloc(sizeof(DNode)); // 分配頭結點。
if (head == NULL) return 0; // 記憶體不足,返回失敗。
head->prior=head->next=NULL; // 前驅後繼結點都置為空。
LL=head;
return 1;
}
*/
// 銷燬連結串列LL。
void DestroyList1(LinkList LL)
{
// 銷燬連結串列LL是指釋放連結串列全部的結點,包括頭結點。
DNode *tmp;
while(LL!=NULL)
{
tmp=LL->next; // tmp儲存下一結點的地址。
free(LL); // 釋放當前結點。
LL=tmp; // LL指標移動到下一結點。
}
// LL=NULL; // LL在本函式中相當於區域性變數,就算置空了也不會影響呼叫者傳遞的LL,所以LL=NULL沒有意義。
return;
}
// 銷燬連結串列LL。
void DestroyList2(LinkList *LL)
{
// 如果函式的引數是指標的指標,可以啟用以下程式碼。
DNode *tmp1,*tmp2;
tmp1=*LL;
while(tmp1!=NULL)
{
tmp2=tmp1->next; // tmp儲存下一結點的地址。
free(tmp1); // 釋放當前結點。
tmp1=tmp2; // LL指標移動到下一結點。
}
*LL=NULL; // 把連結串列的指標置為空,表示連結串列不存在了。
return;
}
/*
// C++引用的方法。
// 傳入指標的地址的方法。
void DestroyList3(LinkList &LL)
{
// 銷燬連結串列LL是指釋放連結串列全部的結點,包括頭結點。
DNode *tmp;
while(LL!=NULL)
{
tmp=LL->next; // tmp儲存下一結點的地址。
free(LL); // 釋放當前結點。
LL=tmp; // LL指標移動到下一結點。
}
LL=NULL; // 把連結串列的指標置為空,表示連結串列不存在了。
return;
}
*/
// 清空連結串列。
void ClearList(LinkList LL)
{
// 清空連結串列LL是指釋放連結串列全部的結點,但不包括頭結點。
if (LL == NULL) { printf("連結串列LL不存在。\n"); return; } // 判斷連結串列是否存在。
DNode *tmp1;
DNode *tmp2=LL->next; // 保留頭結點,從頭結點的下一個結點開始釋放。
while(tmp2!=NULL)
{
tmp1=tmp2->next;
free(tmp2);
tmp2=tmp1;
}
LL->next=NULL; // 這行程式碼一定不能少,否則會留下野指標。
return;
}
// 在連結串列LL的第ii個位置插入元素ee,返回值:0-失敗;1-成功。
int InsertList(LinkList LL, unsigned int ii, ElemType *ee)
{
if ( (LL == NULL) || (ee == NULL) ) { printf("連結串列LL或元素ee不存在。\n"); return 0; } // 判斷表和元素是否存在。
// 判斷插入位置是否合法
if (ii < 1) { printf("插入位置(%d)不合法,應該在大於0。\n",ii); return 0; }
// 要在位序ii插入結點,必須找到ii-1結點。
DNode *pp=LL; // 指標pp指向頭結點,逐步往後移動,如果為空,表示後面沒結點了。
int kk=0; // kk指向的是第幾個結點,從頭結點0開始,pp每向後移動一次,kk就加1。
while ( (pp != NULL) && (kk < ii-1) )
{
pp=pp->next; kk++;
// printf("pp=%p,kk=%d\n",pp,kk);
}
if ( pp==NULL ) { printf("位置(%d)不合法,超過了表長。\n",ii); return 0; }
DNode *tmp = (DNode *)malloc(sizeof(DNode)); // 分配一個結點。
if (tmp == NULL) return 0; // 記憶體不足,返回失敗。
// 考慮資料元素為結構體的情況,這裡採用了memcpy的方法而不是直接賦值。
memcpy(&tmp->data,ee,sizeof(ElemType));
// 處理前驅後續結點的指標。
tmp->next=pp->next;
tmp->prior=pp;
if (tmp->next != NULL) tmp->next->prior=tmp; // 特殊處理,如果是在尾部插入,tmp->next根本不存在。
pp->next=tmp;
return 1;
///////////////////////////////////////
// 以上程式碼可以用以下程式碼代替。
// DNode *pp=LocateNode(LL,ii-1);
// return InsertNextNode(pp,ee);
///////////////////////////////////////
}
// 刪除連結串列LL中的第ii個結點,返回值:0-位置ii不合法;1-成功。
int DeleteNode(LinkList LL, unsigned int ii)
{
if (LL == NULL) { printf("連結串列L不存在。\n"); return 0; } // 判斷連結串列是否存在。
// 判斷刪除位置是否合法
if (ii < 1) { printf("刪除位置(%d)不合法,應該在大於0。\n",ii); return 0; }
// 要刪除位序ii結點,必須找到ii-1結點。
DNode *pp=LL; // 指標pp指向頭結點,逐步往後移動,如果為空,表示後面沒結點了。
int kk=0; // kk指向的是第幾個結點,從頭結點0開始,pp每向後移動一次,kk就加1。
while ( (pp != NULL) && (kk < ii-1) )
{
pp=pp->next; kk++;
}
// 注意,以下行的程式碼與視訊中的不一樣,視訊中的是 if ( pp==NULL ),有bug。
if ( pp->next==NULL ) { printf("位置(%d)不合法,超過了表長。\n",ii); return 0; }
DNode *tmp=pp->next; // tmp為將要刪除的結點。
// 處理前驅後續結點的指標。
pp->next=tmp->next;
if (tmp->next != NULL) tmp->next->prior=pp; // 特殊處理,如果tmp是尾結點,tmp->next根本不存在。
free(tmp);
return 1;
}
// 在連結串列LL的頭部插入元素ee,返回值:0-失敗;1-成功。
int PushFront(LinkList LL, ElemType *ee)
{
return InsertList(LL,1,ee);
}
// 在連結串列LL的尾部插入元素ee,返回值:0-失敗;1-成功。
int PushBack(LinkList LL, ElemType *ee)
{
if ( (LL == NULL) || (ee == NULL) ) { printf("連結串列LL或元素ee不存在。\n"); return 0; } // 判斷表和元素是否存在。
DNode *pp=LL; // 從頭結點開始。
// 找到最後一個結點。
while (pp->next != NULL) pp=pp->next;
DNode *tmp = (DNode *)malloc(sizeof(DNode)); // 分配一個結點。
if (tmp == NULL) return 0; // 記憶體不足,返回失敗。
// 考慮資料元素為結構體的情況,這裡採用了memcpy的方法而不是直接賦值。
memcpy(&tmp->data,ee,sizeof(ElemType));
// 處理前驅後續結點的指標。
tmp->next=pp->next;
tmp->prior=pp;
if (tmp->next != NULL) tmp->next->prior=tmp; // 特殊處理,如果是在尾部插入,tmp->next根本不存在。
pp->next=tmp;
return 1;
}
// 刪除連結串列LL中第一個結點,返回值:0-位置不合法;1-成功。
int PopFront(LinkList LL)
{
return DeleteNode(LL, 1);
}
// 刪除連結串列LL中最後一個結點,返回值:0-位置不合法;1-成功。
int PopBack(LinkList LL)
{
if ( LL == NULL ) { printf("連結串列LL不存在。\n"); return 0; } // 判斷表和元素是否存在。
// 必須加上這個判斷,不要誤刪了頭結點。
if ( LL->next == NULL) { printf("連結串列LL為空,沒有尾結點。\n"); return 0; } // 判斷表是否為空。
// 如果是單鏈表,要刪除最後一個結點,必須找到最後一個結點的前一個結點。
// 如果是雙鏈表,要刪除最後一個結點,找到最後一個結點或最後一個結點的前一個結點都可以,但程式碼實現略有不同。
DNode *pp=LL; // 從第0個結點開始。
// 找到最後一個結點。
while (pp->next != NULL) pp=pp->next;
pp->prior->next=NULL; // 把最後一個節點的前一結點的next指標置為空。
// 釋放最後一個結點。
free(pp);
return 1;
}
// 列印連結串列中全部的元素。
void PrintList(LinkList LL)
{
if (LL == NULL) { printf("連結串列LL不存在。\n"); return; } // 判斷連結串列是否存在。
DNode *pp=LL->next; // 從第1個結點開始。
while (pp != NULL)
{
printf("%-3d", pp->data); // 如果元素ee為結構體,這行程式碼要修改。
pp=pp->next;
}
printf("\n");
/*
// 以下程式碼用於顯示全部結點的地址和元素的值。
DNode *pp=LL; // 從第0個結點開始。
while (pp != NULL)
{
printf("pp=%p,prior=%p,next=%p,data=%-3d\n",pp,pp->prior,pp->next,pp->data);
pp=pp->next;
}
*/
}
// 求連結串列的長度,返回值:>=0-表LL結點的個數。
int LengthList(LinkList LL)
{
if (LL == NULL) { printf("連結串列LL不存在。\n"); return 0; } // 判斷連結串列是否存在。
DNode *pp=LL->next; // 頭結點不算,從第1個結點開始。
int length=0;
while (pp != NULL) { pp=pp->next; length++; }
return length;
// 不使用臨時變數,如何計算連結串列(包括頭結點)的長度?
// if (LL==NULL) return 0;
// return LengthList(LL->next)+1;
}
// 判斷連結串列是否為空,返回值:0-非空或失敗,1-空。
int IsEmpty(LinkList LL)
{
if (LL == NULL) return 0;
if (LL->next == NULL) return 1;
return 0;
}
// 獲取連結串列中第ii個結點,成功返回結點的地址,失敗返回空。
// 注意,ii可以取值為0,表示頭結點。
DNode *LocateNode(LinkList LL, unsigned int ii)
{
if ( LL == NULL ) { printf("連結串列LL不存在。\n"); return NULL; } // 判斷表和元素是否存在。
DNode *pp=LL; // 指標pp指向頭結點,逐步往後移動,如果為空,表示後面沒結點了。
int kk=0; // kk指向的是第幾個結點,從頭結點0開始,pp每向後移動一次,kk就加1。
while ( (pp != NULL) && (kk < ii) )
{
pp=pp->next; kk++;
}
if ( pp==NULL ) { printf("位置(%d)不合法,超過了表長。\n",ii); return NULL; }
return pp;
}
// 查詢元素ee在連結串列LL中的結點地址,如果沒找到返回NULL,否則返回結點的地址。
DNode *LocateElem(LinkList LL, ElemType *ee)
{
DNode *pp=LL->next; // 從第1個數據結點開始。
while (pp != NULL)
{
// 如果資料元素是結構體,以下程式碼要修改。
if (pp->data == *ee) return pp;
pp = pp->next;
}
return NULL;
}
// 在指定結點pp之後插入元素ee,返回值:0-失敗;1-成功。
int InsertNextNode(DNode *pp, ElemType *ee)
{
if (pp == NULL) { printf("結點pp不存在。\n"); return 0; }
DNode *tmp = (DNode *)malloc(sizeof(DNode));
if (tmp == NULL) return 0;
memcpy(&tmp->data,ee,sizeof(ElemType));
// 處理前驅後續結點的指標。
tmp->next=pp->next;
tmp->prior=pp;
if (tmp->next != NULL) tmp->next->prior=tmp; // 特殊處理,如果是在尾部插入,tmp->next根本不存在。
pp->next=tmp;
return 1;
}
// 在指定結點pp之前插入元素ee,返回值:0-失敗;1-成功。
int InsertPriorNode(DNode *pp, ElemType *ee)
{
if (pp == NULL) { printf("結點pp不存在。\n"); return 0; }
// 在單鏈表中,如果要在指定結點pp之前插入元素,採用的是偷樑換柱的方法。
// 在雙鏈表中,偷不偷都行,因為pp->prior有前驅結點的地址。
DNode *tmp = (DNode *)malloc(sizeof(DNode));
if (tmp == NULL) return 0;
// 把待插入的元素存入tmp中。
memcpy(&tmp->data,ee,sizeof(ElemType));
// 處理前驅後繼指標。
tmp->prior=pp->prior;
pp->prior->next=tmp;
tmp->next=pp;
pp->prior=tmp;
return 1;
}
// 刪除指定結點。
int DeleteNode1(DNode *pp)
{
if (pp == NULL) { printf("結點pp不存在。\n"); return 0; }
// 在單鏈表中,刪除指定結點根本不可行,也不能採用偷樑換柱的方法。
// 但是,在雙鏈表中,刪除指定結點是可行的。
pp->prior->next=pp->next;
if (pp->next != NULL) pp->next->prior=pp->prior; // 特殊處理,如果pp是尾結點,pp->next->prior根本不存在。
free(pp);
return 1;
}