1. 程式人生 > >朱有鵬C語言高階---4.9.7--單鏈表--刪除節點(7)

朱有鵬C語言高階---4.9.7--單鏈表--刪除節點(7)

朱有鵬C語言高階---4.9.7--單鏈表--刪除節點(7)

 

(1)連結串列到底用來幹嘛的?

連結串列是用來存資料的,連結串列和陣列是一類東西!!

(2)為什麼要刪除節點?

有時候連結串列節點中的資料不想要了,因此要刪除這個節點。

(3)刪除節點的2個步驟

第一步:找到要刪除的節點

第二步:刪除這個節點。

(4)如何找到待刪除的節點

通過遍歷來查詢節點。從頭指標+頭結點開始,順著連結串列依次將各個節點拿出來,按照一定的方法比對,找到我們要刪除的那個節點。

(5)如何刪除一個節點

1、待刪除的節點不是尾節點的情況:

         首先把待刪除的節點的前一個節點的pNext指標指向待刪除的節點的後一個節點的首地址(這樣就把這個節點從連結串列

中摘出來了),然後再將這個摘出來的節點free掉介面。

2、待刪除的節點是尾節點的情況:

       首先把待刪除的尾節點的前一個節點的pNext指標指向NULL(這時候就相當於原來尾節點前面的一個節點變成了新的

尾節點),然後將摘出來的節點free掉。

(6)注意堆記憶體的釋放

當程式都結束了的情況下,那些沒有free的堆記憶體也被釋放了。

有時候我們的程式執行時間很久,這時候malloc的記憶體沒有free,會一直被佔用直到你free釋放它或者整個程式終止。

 

 

完整程式碼:(核心函式:delete_node())

#include <stdio.h>
#include <strings.h>
#include <stdlib.h>

//構建一個連結串列節點
struct node
{
	int data;		//有效資料
	struct node *pNext;	//指向下一個節點的指標
};

//作用:建立一個連結串列的節點
//返回:指標,指標指向我們本函式新建立的一個節點的首地址
struct node * create_node(int data)
{
	struct node *p = (struct node *)malloc(sizeof(struct node));
	if (NULL == p)
	{
		printf("malloc error.\n");
		return NULL;
	}
	//清理申請到的堆記憶體
	bzero(p, sizeof(struct node));
	//填充節點
	p->data = data;
	p->pNext = NULL;//將來要指向下一個節點的首地址
			//實際操作時將下一個節點malloc返回的指標複製給這個
	return p;
}

//思路:由頭指標向後遍歷,直到走到原來的最後一個節點。原來最後一個節點裡面的pNext是NULL,
//現在我們將它改成new就可以了。添加了之後新節點就變成了最後一個。
//pH:頭指標,有連結串列的頭指標才能找到連結串列。new是一個新的節點
//計算添加了新的節點後共有多少個節點,然後把這個數寫進頭結點中
void insert_tail(struct node *pH, struct node *new)
{
	int cnt = 0;
	//分兩布來完成插入
	//第一步,先找到連結串列中最後一個節點
	struct node *p = pH;
	while (NULL != p->pNext)
	{
		p = p->pNext;//往後走一個節點
		cnt++;
	}
	
	//第二部,將新節點插入到最後一個節點尾部		
	p->pNext = new;
	pH->data = cnt + 1;
}

//思路:
void insert_head(struct node *pH, struct node *new)
{
	//第1步: 新節點的next指向原來的第一個節點
	new->pNext = pH->pNext;	

	//第2部: 頭節點的next指向新節點的地址
	pH->pNext = new;

	//第3步: 頭節點中的計數要加1
	pH->data += 1;
}

//遍歷單鏈表,pH為指向單鏈表的頭指標,遍歷的節點資料打印出來
void bianli(struct node *pH)
{
	
	//pH->data	//頭節點的資料,不是連結串列的常規資料,不要算進去了	
	//struct node *p = pH;//錯誤,因為頭指標後面是頭節點,頭節點的資料域是節點個數
	struct node *p = pH->pNext;//直接跨過了頭節點,p直接走到第一個節點
	printf("-----開始遍歷-----\n");
	while (NULL != p->pNext)//是不是最後一個節點
	{
		printf("node data: %d.\n", p->data);		
		p = p->pNext;	//走到下一個節點,也就是迴圈增量
	}
	printf("node data: %d.\n", p->data);		
	printf("-----完了-----\n");
}


//遍歷單鏈表,pH為指向單鏈表的頭指標,遍歷的節點資料打印出來
void bianli2(struct node *pH)
{
	
	//pH->data	//頭節點的資料,不是連結串列的常規資料,不要算進去了	
	struct node *p = pH;//錯誤,因為頭指標後面是頭節點,頭節點的資料域是節點個數
	printf("-----開始遍歷-----\n");
	while (NULL != p->pNext)//是不是最後一個節點
	{
		p = p->pNext;	//走到下一個節點,也就是迴圈增量
		printf("node data: %d.\n", p->data);		
	}
	printf("-----完了-----\n");
}

// 從連結串列pH中刪除節點,待刪除的節點的特徵是資料區等於data
// 返回值:當找到並且成功刪除了節點則返回0,當未找到節點時返回-1
int delete_node(struct node *pH, int data)//引數:頭指標,節點的資料
{
	//找到這個待刪除的節點,通過遍歷連結串列來查詢
	struct node *p = pH; 	   //頭指標後面是頭節點,用來指向當前節點
	struct node *pPrev = NULL; //用來指向當前節點的前一個節點
	
	while (NULL != p->pNext) //是不是最後一個節點
	{
		pPrev = p;    //在p走向下一個節點前先將其儲存
		p = p->pNext; //走到下一個節點,也就是迴圈增量
		//判斷這個節點是不是我們要找的那個節點
		if (p->data == data)
		{
			// 找到了節點,處理這個節點
			// 分為2種情況,一個是找到的是普通節點,另一個是找到的是尾節點
			
			// 刪除的節點的困難點在於:通過連結串列的遍歷依次訪問各個節點,找到這個節點
			// 後p指向了這個節點,但是要刪除這個節點關鍵要操作前一個節點,但是這
			// 時候已經沒有指標指向前一個節點了,所以沒法操作。解決方案就是增加
			// 一個指標指向當前節點的前一個節點
			if (NULL == p->pNext)
			{
				//尾節點
				pPrev->pNext = NULL;	//原來尾節點的前一個節點變成新尾節點
				free(p);		//釋放原來的尾節點的記憶體
			}
			else
			{
				//普通節點
				//要刪除的節點的前一個節點和它的後一個節點相連,這樣就把要刪除的節點給摘出來了
				pPrev->pNext = p->pNext;
				free(p);
			}			
			// 處理完成之後退出程式
			return 0;
			
		}
	}
	// 到這裡還沒找到,說明連結串列中沒有我們想要的節點
	printf("沒找到這個節點.\n");
	return -1;	
}

int main(void)
{
	//不能指向NULL,因為尾插法時首先會判斷頭指標的值p->pNext
	//struct node *pHeader = NULL;	
	//定義頭指標,建立頭節點。頭節點的資料域傳的是節點的個數
	struct node *pHeader = create_node(0);//資料定義0表示沒放資料	

	insert_head(pHeader, create_node(11));
	insert_head(pHeader, create_node(12));
	insert_head(pHeader, create_node(13));
	insert_head(pHeader, create_node(12));
	
	bianli2(pHeader);
	
	delete_node(pHeader, 12);
	printf("-----刪除後-----\n");
	bianli2(pHeader);

	return 0;
}