離散存儲【鏈表】
1.定義:
n個結點離散分配彼此通過指針相連
每個結點只有一個前驅結點,每個結點只有一個後續結點
首結點沒有前驅結點,尾結點沒有後續結點
2.專業術語:
首結點:第一個存放有效數據的結點
尾結點:最有一個存放有效數據的結點
頭結點:頭結點的數據類型和首結點的類型是一樣的
首結點之前的結點// 第一個有效結點之前的那個指針
頭結點並不存放有效數據,也沒有存放整個鏈表中 結點的個數。
加頭結點的目的是爲了方便對鏈表的操作
頭指針:指向頭結點的指針變量 鏈表的首位置
尾指針:指向尾結點的指針變量
示例1:如何定義一個結點(每一個結點的類型如何表示)
typedef struct Node
{
int data; //數據域
struct Node * pNext; //指針域
//指針存儲的地址只能是 struct Node 類型。
//指針域指向的 是與 本身結點數據類型相同的下一個結點 所以數據類型是 struct Node *;
}NODE,*PNODE;
//NODE 等價於 struct Node,
//PNODE 等價於 struct Node *;
3.如果希望一個函數來對鏈表進行處理,我們至少需要接受鏈表的哪些信息:
只需要一個參數:頭指針以爲我們通過頭指針可以推算出鏈表的其他所有參數
4.分類:
單鏈表:每個結點的指針域只指向後面的結點雙鏈表:每一個結點有兩個指針(指前指後)
循環鏈表:能通過任何一個結點找到其他所有的結點
非循環鏈表
5.算法:
遍歷查找
清空
銷燬
求長度
排序
刪除結點
插入結點
插入結點:(僞算法)
//第1種寫法
r = p->pNext;
p->pNext = q;
q->pNext = r;
//第2種寫法
q->pNext = p->pNext;
p->pNext = q;//(這兩行代碼不能倒過來)
/*
q 本身就是結點的地址 q 指向的就是 結點
q-> pNext 表示的就是結點的指針域
至於指針域中存放的下一個結點的地址是誰 稍加判斷就好了。
最重要的!p->pNext //p所指向的結構體變量中 pNext 成員本身。
*/
刪除結點:(僞算法)
r = p->pNext;
p->pNext = p->pNext->pNext;
free(r);//這裏很重要 一定要記得釋放內存,防止內存 溢出
示例2:創建並遍歷一個鏈表,實現判斷是否爲空,求鏈表的長度查找刪除
仿JAVA中LinkedList對象部分功能的實現
#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>
typedef struct Node
{
//數據域
int data;
//指針域
struct Node * pNext;
//指針域指向的 是與 本身結點數據類型相同的下一個結點 所以數據類型是 struct Node *;
}NODE,*PNODE;
//NODE 等價於 struct Node,
//PNODE 等價於 struct Node *;
//函數聲明
PNODE create_list(void);
void traverse_list(PNODE pHead);
bool is_empty(PNODE pHead);
int length_list(PNODE);
bool insert_list(PNODE,int,int);
bool delete_list(PNODE,int,int *);
void sort_list(PNODE);
int main(void)
{
PNODE pHead = NULL;//等價於 struct Node * pHead = NULL;
//create_list()的功能:創建一個非循環單鏈表,並將該鏈表的頭結點的地址賦給 pHead
pHead = create_list();
traverse_list(pHead);//遍歷
if(is_empty(pHead))
printf("鏈表爲空\n");
else
printf("鏈表不空\n");
int len = length_list(pHead);
printf("鏈表的長度是%d \n",len);
sort_list(pHead);
traverse_list(pHead);//遍歷
insert_list(pHead,2,33);
traverse_list(pHead);//遍歷
int val;
if(delete_list(pHead,4,&val))
{
printf("您刪除的元素是%d\n",val);
}
else
{
printf("刪除失敗%d\n",val);
}
return 0;
}
//創建一個鏈表,創建一個非循環單鏈表,並將該鏈表的頭結點的地址賦給 pHead
PNODE create_list(void)
{
//C語言裏的寫法是把所有的變量都定義在前面
int len;//用來存放有效結點的個數
int i;
int val;//用來存放用戶輸入的有效結點的值
//升成一個頭結點 頭結點的數據類型與 其他結點是相同的。
//分配了一個不存放有效數據的頭結點
PNODE pHead = (PNODE)malloc(sizeof(NODE));
if(NULL == pHead)
{
printf("分配失敗,程序終止。\n");
exit(-1);
}
//pTail 永遠指向 尾結點
PNODE pTail = pHead;
pTail -> pNext = NULL;
printf("請輸入您需要生成的鏈表的結點的個數:len =");
scanf("%d",&len);
for(i =0;i<len;++i)
{
printf("請輸入第%d個結點的值:",i+1);
scanf("%d",&val);
PNODE pNew = (PNODE)malloc(sizeof(NODE));
if(NULL == pNew)
{
printf("分配失敗,程序終止。\n");
exit(-1);
}
pNew ->data = val;
//新生成的結點要掛到整個鏈表的最後的一個位置。
pTail -> pNext = pNew;
pNew ->pNext = NULL;
pTail = pNew;
}
return pHead;
}
//遍歷這個鏈表
void traverse_list(PNODE pHead)
{
//p 指向的是鏈表的第一個有效結點
PNODE p = pHead->pNext;
while(p!= NULL)
{
printf(" %d",p->data);
p = p->pNext;
}
printf("\n");
return;
}
//判斷鏈表是否爲空
bool is_empty(PNODE pHead)
{
if(pHead->pNext == NULL)
return true;
else
return false;
}
//返回鏈表長度
int length_list(PNODE pHead)
{
PNODE p = pHead -> pNext;
int len = 0;
while(p != NULL)
{
len++;
p = p->pNext;
}
return len;
}
//鏈表排序
void sort_list(PNODE pHead)
{
//實際上算法都是類似
//數組和鏈表同樣都是 線性結構
int i,j,t;
PNODE p, q;
int len = length_list(pHead);
//i 是數組中第一個有效元素的下標 p 是鏈表中第一個有效元素的 地址。
for(i = 0,p = pHead ->pNext;i<len - 1;++i,p = p->pNext)
{
// q 就應該是p 的下一個元素
for(j = i+1,q = p ->pNext;j<len;++j,q = q->pNext)
{
/*
請參照數組中的方式來理解 鏈表
if(a[i] > a[j])
{
t = a[i];
a[i] = a[j];
a[j] = t;
}
*/
if((p->data )> (q->data))
{
t = p->data;
p->data = q->data;
q->data= t;
}
}
}
return;
}
//在pHead 所指向鏈表的第pos 個結點的前面插入一個新的結點,該結點的值是 val並且 pos 的值是從1 開始。
bool insert_list(PNODE pHead,int pos,int val)
{
int i = 0;
PNODE p = pHead;
while(p != NULL && i<pos -1)
{
p = p->pNext;
i++;
}
if(i>pos-1||p== NULL)
return false;
PNODE pNew = (PNODE)malloc(sizeof(PNODE));
if(pNew == NULL)
{
printf("動態分配內存失敗");
exit(-1);
}
pNew ->data = val;
//定義了一個臨時結點
PNODE q = p->pNext;
p->pNext = pNew;
pNew ->pNext = q;
return true;
}
//刪除指定位置的元素,並用*pVal記錄被刪除的元素
bool delete_list(PNODE pHead,int pos,int * pVal)
{
int i = 0;
PNODE p = pHead;
while(p ->pNext!= NULL && i<pos -1)
{
p = p->pNext;
i++;
}
if(i>pos-1||p ->pNext== NULL)
return false;
PNODE q = p ->pNext;
*pVal = q ->pNext;
//刪除p結點後面的結點
p->pNext = p->pNext->pNext;
free(q);
q = NULL;
return true;
}