單鏈表的學習

鏈表是一種很重要的數據結構,它由兩部分組成,第一個部分是我們要儲存的數據,第二個部分是指向下一個儲存單元的指針。鏈表在使用中有順序表無法比擬的靈活性,免去了儲存空間不夠,又有可能浪費的尷尬。


單鏈表有一個頭指針pHead,當我們沒有數據要儲存的時候它指向NULL,當我們有數據的時候它指向第一塊儲存單元。儲存單元裏面有兩個部分,前面的部分是我們要儲存的數據data,後面的部分是指向下一個儲存單元的指針pNext,當後面沒有儲存單元的時候就指向NULL。那麼我們儲存的數據在內存中並不是連續儲存的,而是在內存中跳躍式儲存的。要使用的時候再直接申請一塊空間。


下面是鏈表的定義

typedef struct ListNode
{
	DataType data;
	struct ListNode *pNext;
}SListNode, *PSListNode;

可以看到單鏈表的兩個成員。爲了使用方便我們直接typedef重命名。


單鏈表有幾種基本操作,比如插入數據,刪除數據,那我下面實現了一下。


首先,我寫了一個申請新單元的函數

PSListNode ByeNode(DataType data)
{
	PSListNode pNewNode = (PSListNode)malloc(sizeof(SListNode));
	if (NULL != pNewNode)
	{
		pNewNode->data = data;
		pNewNode->pNext = NULL;
	}
	return pNewNode;
}

它可以爲我們直接在內存中申請一塊新的空間並且返回它的地址。


第一個實現就是我們從尾部插入數據

void PushBack(PSListNode* pHead, DataType data)
{
	PSListNode pNode = NULL;
	PSListNode pNewNode = NULL;

	assert(pHead);

	if (NULL == *pHead)
	{
		*pHead = ByeNode(data);
	}
	else
	{
		pNode = *pHead;
		while (NULL != pNode->pNext)
		{
			pNode = pNode->pNext;
		}
		pNewNode = ByeNode(data);
		pNode->pNext = pNewNode;
	}
}

這裏我們傳的參數是二級指針,因爲我們是要改變它指針的指向。假如我們直接傳遞一級指針,那麼我們並不能改變它的指向,相當於我們函數中操作了半天,其實都是在操作一個臨時的指針變量,只不過他跟我們的頭指針的指向是一樣的,最後什麼也沒有返回,頭指針什麼變化都沒有。


接下來就是我們從尾部刪除數據的實現

void PopBack(PSListNode* pHead)
{
	/*PSListNode pPerNode = NULL;
	PSListNode pCurNode = NULL;
	assert(pHead);

	if (NULL == *pHead)
	{
		return;
	}
	else
	{
		pCurNode = *pHead;
		pPerNode = pCurNode;

		while (NULL != pCurNode->pNext)
		{
			pPerNode = pCurNode;
			pCurNode = pCurNode->pNext;
		}
		if (pCurNode==pPerNode)
		{
			*pHead = NULL;
			free(pCurNode);
			pCurNode = NULL;
			pPerNode = NULL;
		}
		else
		{
			pPerNode->pNext = NULL;
			free(pCurNode);
			pCurNode = NULL;
		}	
	}*/

	PSListNode pPerNOde = *pHead;
	PSListNode pCurNode = *pHead;
	assert(pHead);

	if (NULL == *pHead)
	{
		return;
	}
	else
	{
		if (NULL == pCurNode->pNext)
		{
			return;
		}
		else
		{
			while (NULL!=pCurNode->pNext)
			{
				pPerNOde = pCurNode;
				pCurNode = pCurNode->pNext;
			}

			pPerNOde->pNext = NULL;
			free(pCurNode);
			pCurNode = NULL;
		}
	}

}

註釋中的代碼是我剛開始的時候寫的,我發現他的邏輯不是很清晰,在第二個部分中我把鏈表中只有一個節點的情況單列了出來,邏輯比之前清晰了很多。


那我們也可以在鏈表的頭部插入數據

void PushFront(PSListNode* pHead, DataType data)
{
	PSListNode NewNode = NULL;
	assert(pHead);

	if (NULL == *pHead)
	{
		*pHead = ByeNode(data);
	}
	else
	{
		NewNode = ByeNode(data);
		if (NULL == NewNode)
		{
			return;
		}
		else
		{
			NewNode->pNext = (*pHead);
			*pHead = NewNode;
		}
	}
}

思路有了之前的兩個函數做鋪墊想起來並不難。申請一塊新的空間之後,讓它的pNext指向我們之前的第一塊空間。然後改變我們的頭指針的指向,讓它指向我們的新空間。這裏注意我們申請空間是有可能失敗的,所以要判斷一下。


當然還有從頭部刪除

void PopFront(PSListNode* pHead)
{
	assert(pHead);

	if (NULL == *pHead)
	{
		return;
	}
	else
	{
		PSListNode pCurNode = *pHead;

		pCurNode = pCurNode->pNext;
		free(*pHead);
		*pHead = pCurNode;
		pCurNode = NULL;
	}
}

千萬不要忘記free空間之後要給指針賦空,否則會形成野指針。


還有就是尋找我們鏈表中的元素

PSListNode Find(PSListNode pHead, DataType data)
{
	if (NULL == pHead)
	{
		return NULL;
	}
	else
	{
		PSListNode pNode = pHead;
		/*while (data != pNode->data)
		{
			if (NULL == pNode->pNext)
			{
				return NULL;
			}
			pNode = pNode->pNext;
		}
		return pNode;*/

		while (NULL != pNode)
		{
			if (data == pNode->data)
				return pNode;

			pNode = pNode->pNext;
		}
		return NULL;
	}
}

註釋掉的代碼是我第一次寫的,後來我發現它的邏輯有點問題,我可以更簡單的實現它的功能。

最後返回我要找的數據的位置,假如沒有找到那麼就返回空。


打印我鏈表中的元素

void PrintList(PSListNode pHead)
{
	PSListNode pNode = pHead;

	while (NULL!=pNode)
	{
		printf("%d ", pNode->data);
		pNode = pNode->pNext;
	}

	printf("\n");
}


刪除我的任意位置的節點

void  Erase(PSListNode* pHead, PSListNode pos)
{
	PSListNode pCurNode = pos;
	PSListNode pPerNode = NULL;
	assert(pHead);

	if (NULL == *pHead)
	{
		return;
	}
	else
	{
		pPerNode = *pHead;
		while (pPerNode->pNext != pCurNode)
		{
			pPerNode = pPerNode->pNext;
		}
		pPerNode->pNext = pCurNode->pNext;
		free(pCurNode);
		pCurNode = NULL;
	}
}


在我的鏈表的任意位置插入一個節點

void  Insert(PSListNode* pHead, PSListNode pos, DataType data)
{
	PSListNode ptmpNode = *pHead;
	PSListNode pNode = *pHead;
	assert(pHead);

	if (NULL == *pHead)
	{
		*pHead = ByeNode(data);
	}
	else
	{
		while (pos != pNode)
		{
			if (NULL == pNode)
			{
				return;
			}
			pNode = pNode->pNext;
		}
		ptmpNode = pNode->pNext;
		pNode->pNext = ByeNode(data);
		pNode = pNode->pNext;
		pNode->pNext = ptmpNode;
	}
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章