鏈表的基本操作(單鏈表、雙向鏈表、循環鏈表)

本週第一次講座,學長給我們簡單的概述了數據結構和算法,然後對鏈表的一些操作進行了講解,下來之後,我把原來書上的一些

鏈表的基本操作與鏈表的逆置,排序等操作結合起來,整理出來

  1. 鏈表是由結點構成的,關鍵是定義結點
  2. C語言程序設計上兩大特例:①鏈表節點的定義②遞歸函數的定義。這兩個違反了先定義再使用。

一、鏈表的分類     

        3.靜態鏈表:各結點在程序中定義,不是臨時開闢的(不是用malloc函數或者calloc函數動態開闢的空間),始終佔着內存不放。

struct student
{
	int number;                     //數據區域 
	struct student *next;          // 指針區域 
};                                //定義結構體類型 
int main()
{
	struct student a,b,c,*head,*p;             //定義結點、頭指針、和進行遍歷的指針 
	a.number=1;
	b.number=2;
	c.number=3;
	head=&a;
	a.next=&b;b.next=&c;c.next=NULL;
	p=head;
	do
	{
		printf("d",p->number);             //進行遍歷 
		p=p->next;
	}
	while(p!NULL);

}

  二、鏈表的基本操作

  4.動態鏈表的創建

(1)創建鏈表:從無到有建立起一個鏈表,即往空鏈表中一次插入若干結點,並保持結點之間的前驅和後繼關係。顧名思義,鏈表就像一條鐵環串成的鏈子,每個鐵環是結點,連接下一個鐵環,也可以理解爲老師帶領手拉手小朋友排隊,頭指針是老師,小朋友與小朋友之間拉着手。

第一次看的時候沒有看懂,應該在紙上把鏈表的圖畫一畫就可以明白。

struct num
{
	int number;
	struct num *next;
 };
 int icount;                            //全局變量標記結點的順序 
 struct *create()
 {
 	struct *pHead=NULL,*pEnd,*pNew;
 	icount=0;
 	pNew=pEnd=(struct*)malloc(sizeof(struct num));
 	scanf("%d",&pNew->number);
 	while(pNew->number!=0)             //while(pNew->number>0)作用相同
	{
		icount++;
		if(icount==1)                  //判斷是否是第一次加入結點也可以用if(pHead==NULL)進行判斷 
		{
		    pNew->next=pHead;           //使指針指向爲空,也可以pNew->next=NULL 
			pEnd=pNew;
			pHead=pNew;
		}
		else
		{
			pNew->next=NULL;
			pEnd->next=pNew;
			pEnd=pNew;
		}
		pNew=(struct*)malloc(sizeof(struct num));//再次分配結點的內存空間,爲下一個結點插入做準備 
		scanf("%d",&pNew->number);
	 } 
	 free(pNew);
	 return pHead;
 }

         5.單鏈表的遍歷輸出

(2)檢索操作:按給定的結點引導或檢索條件,查找某個結點。如果找到指定的結點則稱爲檢索成功;否則檢索失敗。自己理解是從鏈表的開頭按照順序一個一個檢查,然後找到自己想要找到的結點,如果找到則檢索成功,如果沒找到就是檢索失敗。

 void print(struct num *pHead) 
 {
 	struct num *pTemp;     
 	int n=1;                            //表示鏈表中結點的序號 
 	pTemp=pHead;
 	while(pTemp!=NULL)
 	{
 		printf("%d",pTemp->num);
 		pTemp=pTemp->next;                 //遍歷的關鍵,移動臨時指針到下一個結點 
 		n++;
	}
 }

        6.單鏈表的插入

(3)插入操作:在結點N(i)和N(i+1)之間插入一個新的結點N,使線性表的長度增加1,且N(i)和N(i+1)的邏輯關係發生變化:

插入前N(i)是N(i+1)的前驅,N(i+1)是N(i)的後繼;插入後新的結點N成爲N(i)的後繼,N(i+1)的前驅。

插入原則:

①插入操作不應破壞原鏈接關係

②插入的結點應該在它該在的位置(按照順序排好,如果不要求順序,則可以任意插入)

實現方法:

應該有一個插入位置的查找子過程

共有3中插入情況:

①插入結點在鏈表最前端

 struct num *Insert(struct num *pHead)
 {
 	struct num *pNew;
 	pNew=(struct*)malloc(sizeof(struct num));
 	pNew->next=pHead;                 //新結點指針指向原來的首結點 
 	pHead=pNew;                       //頭指針指向新節點 
 	icuont++;                         //增加鏈表節點數量 
 	return pHead; 
 }

②插入節點在鏈表中間

 struct num *Insert(struct num *pHead,int n)
{
	struct num *p=pHead,*PNew;             //兩個指針,*P是插入結點的前一個結點 
	while(p&&p->number!=n)                 //查找要插入的位置 
	p=p->next;                             //下移到下一個結點處 
	pNew=(struct*)malloc(sizeof(struct num));
	scanf("%d",&pNew->number);
	pNew->next=p->next ;                      //新結點指向下一個結點 
	p->next=pNew;                            // 上一個結點指向新結點 
	icount++;                                //鏈表長度增加 
	return pHead;
}

③插入節點在鏈表尾端

struct num *Insert(struct num *pHead)
{
	struct num *p=pHead,*pNew;
	while(p&&p->next!=NULL)
	p=p->next;
	pNew=(struct*)malloc(sizeof(struct num));
	scanf("%d",&pNew->number);
	p->next=pNew;                         // 尾結點指向新結點 
	pNew->next=NULL;                      //新結點指向空指針,變爲尾結點 
	icount++;
	return pHead;
}


         7.單鏈表的刪除

(4)刪除操作:刪除結點N(i)後,使線性表的長度減1,且N(i-1)N(i)N(i+1)的邏輯關係發生變化:

刪除前,N(i)是N(i+1)的前驅,N(i-1)的後繼;刪除後,N(i-1)成爲N(i+1)的前驅,N(i+1)成爲N(i-1)的後繼。

刪除的原則:

不改變原來排列的順序,只是從鏈表中分離開來,撤銷原來的鏈接關係。

兩種情況:

①要刪的結點是頭指針所指的結點則直接操作;

②不是頭結點,要依次往下找。另外考慮:空表和找不到要刪除的結點

需要兩個臨時指針:
P1:判斷指向的結點是不是要刪除的結點(用於尋找);

P2:始終指向P1前面的一個結點;

 void Delete(struct num *pHead,int n)
 {
 	int i;
 	struct num *pTemp;               //臨時指針,用於查找所要刪除的結點 
 	struct num *pPre;               //表示要刪除的結點前的結點  
 	pTemp=pHead;                    //得到鏈表的頭結點 
 	for(i=1;i<n;i++)
 	{
 		pPre=pTemp;                 //pPre跟進pTemp 
 		pTemp=pTemp->next;          //pTemp前進下移到下一個結點 
	 }
 	pPre->next=pTemp->next;          // 將要刪除的結點兩邊的結點鏈接 
 	free(pTemp);
 	icount--;                             //減少鏈表中結點的個數 
 }

 三、循環鏈表

單鏈表最後一個結點的指針指向NULL,循環鏈表的最後一個結點的指針指向鏈表頭結點,首尾相連,形成數據鏈。

與單鏈表的不同點:

(1)鏈表的建立:單鏈表需要創建一個頭結點,專門存放第一個結點的地址,單鏈表的尾指針的指針域指向NULL,;而循環鏈表的建立,不需要專門的頭結點,讓最後一個結點的指針域指向鏈表的頭結點即可。

(2)鏈表表尾的判斷:單鏈表判斷結點是否爲表尾結點,只需判斷結點的指針域是否爲NULL,如果是,則爲尾結點,否則不是。而循環鏈表判斷是否尾結點,則是判斷該節點的指針域是否指向鏈表頭結點。

四、雙向鏈表

雙向兩鏈表也是基於單鏈表,單鏈表有一個頭結點,一個尾結點,雙鏈表有兩個指針域,一個指向左邊一個指向右邊,

一個存儲直接後繼結點地址,一般稱爲右鏈域,一個存儲直接前驅結點地址,一般成爲左鏈域。

13.雙向循環鏈表創建

struct num
{
	int number;
	struct num *rlink;
	struct num *llink;
};
struct num *creat(int n)
{
	struct num *p,*h,*s;    //h代表頭結點,S代表新結點,p則儲存新結點的前驅 
	int i;
	if(h=(struct*)malloc(sizeof(struct num))==NULL)     //判斷開闢空間是否成功 
	 exit(0);
	 h->llink=NULL;         //頭結點初始化 
	 h->rlink=NULL;          //頭結點初始化 
	 p=h;
	 for(i=0;i<n;i++)
	 {
	 	if(s=(struct*)malloc(sizeof(struct num))==NULL) //開闢新結點 
	 	exit(0);
	 	p->rlink=s;          //新結點前驅向右指向新結點 
	 	printf("請輸入第%d個人的姓名",i+1);
	 	scanf("%d",s->number);      //輸入新結點裏的信息 
	 	s->llink=p;                  //新結點向左指向前驅 
	 	s->rlink=NULL;               //新結點向右指向空 
		p=s;                         //將創建好的新結點作爲下一個新結點的前驅 
	 }
	 h->llink=s;                     //頭結點向左指向最後一個結點 
	 p->rlink=h;                     //最後一個結點向右指向頭結點,連接完成 
	 return(h);
}

14.雙向鏈表查找

從表頭結點往後依次比較各結點數據域的值,若是該特定值。則返回結點的指針,否則繼續往後查,直到表尾

struct num *search(struct num *pHead,int n)
{
	struct num *p;           //行走指針,一個一個結點比較 
	int n;                   //要得到的那個數 
	p=pHead->rlink;          //從第一個結點開始 
	while(p!=h)              //當P走完一圈 
	{
		y=p->number;          
		if(n==y)             //進行比較 
		return(p);
		else
		p=p->rlink;         //繼續遍歷 
	}
}

15.雙向列表插入

雙向鏈表插入結點和單鏈表插入結點方法基本相同,只是指針域要分左右,比如要在p,q之間插入結點s,只需把p右鏈域指向s,s的左鏈域指向p,s的右鏈域指向q,q的左鏈域指向s即可

void Intset(struct num *p)
{
	int n;
	struct num *s;
	if(s=(struct*)malloc(sizeof(struct num))==NULL) //開闢空間 
	exit(0);
	scanf("%d",&n); 
	s->number=n;
	s->rlink=p->rlink;                //將新結點的向右指向後一個結點 
	p->rlink=s;                       //前一個結點指向新結點 
	s->llink=p;                       //新結點指向前一個結點 
	(s->rlink)->llink=s;              //後一個結點向左指向新結點 
} 

16.雙鏈表的刪除

雙鏈表的刪除和單鏈表的刪除也類似,將要刪除的鏈表跳過即可,需要注意的是在刪除的時候分清左右鏈域

比如:s、p、q三個連續結點,要刪除p,只需將s的右鏈域指向q,q的左鏈域指向s,並釋放p結點就可以了

void del(struct num *p)
{
	(p->rlink)->llink=p->llink;//要刪除結點P的後繼向左指向P的前驅 
	(p->llink)->rlink=p->rlink;//要刪除結點P的前驅向右指向P的後繼 
	free(p);                   //將P開闢的空間釋放 
} 

以上是鏈表的一些操作,如果以上內容有錯誤,歡迎大家指出,謝謝大家吐舌頭


發佈了27 篇原創文章 · 獲贊 59 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章