1. 程式人生 > >朱有鵬C語言高階---4.9.8-單鏈表--逆序(單鏈表的完整程式)(8)

朱有鵬C語言高階---4.9.8-單鏈表--逆序(單鏈表的完整程式)(8)

朱有鵬C語言高階---4.9.8-單鏈表--逆序(8)

 

什麼是連結串列的逆序?

連結串列的逆序又叫反向,意思就是把連結串列中所有的有效節點在連結串列中的順序給反過來。

 

單鏈表逆序演算法分析

(1)當我們對一個數據結構進行一個操作時,我們就需要一套演算法。這就是資料結構和演算法的關係。

(2)我總結:演算法有2個層次。第一個層次是數學和邏輯上的演算法;第二個層次是用程式語言來實現演算法

(3)從邏輯上來講,連結串列的逆序有很多種方法。這些方法都能實現最終的需要,但是效率是不一樣的。彼此的可擴充套件性、容錯性等不同。

(4)思路:首先遍歷原連結串列,然後將原連結串列中的頭指標和頭結點作為新連結串列的的頭指標和頭結點。原連結串列中的有效節點挨個取出來,採用頭插入的方法插入到新連結串列中即可。

(5)連結串列的逆序 = 遍歷+頭插入

 

 

程式設計實戰(核心函式:reverse_linkedlist())

#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;	
}

//將pH指向的連結串列逆序
void reverse_linkedlist(struct node *pH)
{
	struct node *p = pH->pNext;	//pH指向頭節點,p指向第1個有效節點
	struct node *pBack;		//儲存當前節點的後一個節點地址	

	//單鏈表沒有有效節點或者只有一個有效節點時,逆序不用做任何操作,直接返回
	if ((NULL == p) || (NULL == p->pNext))
		return ;
	
	//當連結串列有2個及2個以上節點時,才需要真正進行逆序操作
	while (NULL != p->pNext)	//是不是最後一個節點
	{
		//原連結串列中第一個有效節點將是逆序後新連結串列的尾節點,尾節點的pNext指向NULL
		pBack = p->pNext;	//儲存p節點後面一個節點地址
		if (p == pH->pNext)
		{
			// 原連結串列第一個有效節點
			p->pNext = NULL;
			
		}
		else
		{
			// 原連結串列的非第1個有效節點
			p->pNext = pH->pNext;//後端掛接
		}
		pH->pNext = p;//前端掛接
		
		//p = p->pNext;	//這樣已經不行了,因為p->pNext已經被改過了
		p = pBack;	//走到下一個節點	
	}
	// 迴圈結束後,最後一個節點仍然缺失
	insert_head(pH, p);	
}

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

	insert_tail(pHeader, create_node(11));
	insert_tail(pHeader, create_node(12));
	insert_tail(pHeader, create_node(13));
	insert_tail(pHeader, create_node(14));
	
	bianli2(pHeader);
	
	reverse_linkedlist(pHeader);
	printf("-----逆序後-----\n");
	bianli2(pHeader);

	return 0;
}