不帶頭不循環單鏈表

單鏈表的操作比較簡單靈活,存放在地址任意的存儲單元中

單鏈表是用每一個結點存放一個數據,然後將所有的結點連接起來

當然,每個結點的位置都是隨機的,並不會連續

每個結點也是在堆上申請的

//一個結點中包含數據和指向下一個結點的指針
typedef struct sListNode
{
	DataType data;
	struct sListNode *pNext;
}sListNode;

在單鏈表中,我們實現了一下的基本操作

//單鏈表的初始化
void sListInit(sListNode **ppHead);

//單鏈表的銷燬
void sListDestory(sListNode **ppHead);

//單鏈表的打印
void sListPrint(sListNode *pHead);

//單鏈表的插入:尾插、頭插、插入指定位置
void sListPushBack(sListNode **ppHead, DataType data);
void sListPushFront(sListNode **ppHead, DataType data);
void sListInsert(sListNode **ppHead, sListNode *pPos, DataType data);

//單鏈表的查詢
sListNode *sListFind(sListNode *ppHead, DataType data);

//單鏈表的刪除:頭刪、尾刪、刪除指定位置、刪除指定數據、刪除所有指定數據
void sListPopFront(sListNode **ppHead); 
void sListPopBack(sListNode **ppHead);
void sListErase(sListNode **ppHead, sListNode *pPos);
void sListRemove(sListNode **ppHead, DataType data);
void sListRemoveAll(sListNode **ppHead, DataType data);

1)初始化

void sListInit(sListNode **ppHead)
{
	assert(ppHead != NULL);
	//開始鏈表中沒有數據,那麼該鏈表指向空
	*ppHead = NULL;
}

2)銷燬

void sListDestory(sListNode **ppHead)
{
	assert(ppHead != NULL);
	sListNode *pFollow = NULL;
	//依次遍歷鏈表,將鏈表中的每個結點均刪除
	while (*ppHead != NULL)
	{
		//刪除當前結點前得先記錄下一個結點的位置
		pFollow = (*ppHead)->pNext;
		free(*ppHead);
		*ppHead = pFollow;
	}
}

3)打印

void sListPrint(sListNode *pHead)
{
	//依次遍歷每一個結點將其打印
	while (pHead != NULL)
	{
		printf("%d -> ", pHead->data);
		pHead = pHead->pNext;
	}
	printf("NULL\n");
}

4)插入

//寫一個函數進行結點的創建
sListNode *createNewNode(int data)
{
	sListNode *spList = (sListNode *)malloc(sizeof(sListNode));
	if (spList != NULL)
	{
		spList->data = data;
		spList->pNext = NULL;
		return spList;
	}
	return NULL;
}

void sListInsert(sListNode **ppHead, sListNode *pPos, DataType data)
{
	/* 限制:pPos一定存在於鏈表中 */
	assert(ppHead != NULL);
	sListNode *newNode = createNewNode(data);//創建一個新結點
	sListNode *psList = *ppHead;
	if (newNode != NULL)
	{
		//特殊情況:*ppHead就是想找的位置,即pPos就是第一個結點的位置
		//也就是頭插了
		if (pPos == *ppHead)
		{
			sListPushFront(ppHead, data);
			return;
		}

		while (psList->pNext != pPos)
		{
			psList = psList->pNext;
		}
		//當前結點的下一個結點就是指定位置了
		newNode->pNext = pPos;
		psList->pNext = newNode;
		//以上兩步操作可將結點插入指定位置的前一個位置(注意順序不能改變)
	}
}

void sListPushFront(sListNode **ppHead, DataType data)
{
	assert(ppHead != NULL);
	sListNode *newNode = createNewNode(data);
	if (newNode != NULL)
	{
		//特殊情況:*ppHead爲NULL
		if (*ppHead == NULL)
		{
			*ppHead = newNode;
			return;
		}

		newNode->pNext = *ppHead;
		*ppHead = newNode;
	}
}

void sListPushBack(sListNode **ppHead, DataType data)
{
	assert(ppHead != NULL);
	sListNode *psList = *ppHead;
	sListNode *newNode = createNewNode(data);
	if (newNode != NULL)
	{
		//特殊情況:*ppHead爲NULL
		if (psList == NULL)
		{
			*ppHead = newNode;
			return;
		}
		//循環找到最後一個結點的位置在其後插入結點
		while (psList->pNext)
		{
			psList = psList->pNext;
		}
		psList->pNext = newNode;
	}
}

5)查詢(找到返回該節點的地址,沒找到返回NULL)

sListNode *sListFind(sListNode *pHead, DataType data)
{
	assert(pHead != NULL);
	sListNode *psList;
	psList = pHead;
	while (psList != NULL)
	{
		if (psList->data == data)
		{
			return psList;
		}
		psList = psList->pNext;
	}
	return NULL;
}

6)刪除

void sListRemoveAll(sListNode **ppHead, DataType data)
{
	assert(ppHead != NULL);
	assert(*ppHead != NULL);
	//先找到第一個指定數據的結點位置
	sListNode *psList = sListFind(*ppHead, data);
	while (psList != NULL)
	{
		//刪除指定數據,查找下一個指定數據
		sListErase(ppHead, psList);
		psList = sListFind(*ppHead, data);
	}
}

void sListRemove(sListNode **ppHead, DataType data)
{
	assert(ppHead != NULL);
	assert(*ppHead != NULL);
	sListNode *psList = sListFind(*ppHead, data);
	sListErase(ppHead, psList);
}

void sListErase(sListNode **ppHead, sListNode *pPos)
{
	assert(ppHead != NULL);
	assert(*ppHead != NULL);
	sListNode *psList = *ppHead;
	//特殊情況:首結點就是pPos,或者pPos爲NULL
	if (psList == pPos)
	{
		//此時相當於頭刪
		sListPopFront(ppHead);
		return;
	}
	else if (pPos == NULL)
	{
		//空位置不存在刪除直接返回
		return;
	}
	//找到指定位置
	while (psList->pNext != pPos)
	{
		psList = psList->pNext;
	}
	//將鏈表與指定位置的下一個結點連接起來
	psList->pNext = pPos->pNext;
	//釋放指定位置的結點
	free(pPos);
}

void sListPopBack(sListNode **ppHead)
{
	assert(ppHead != NULL);
	//當前不許至少有一個結點讓你刪
	assert(*ppHead != NULL);
	sListNode *psList = *ppHead;
	//特殊情況:只有一個結點
	if (psList->pNext == NULL)
	{
		free(psList);
		*ppHead = NULL;
		return;
	}
	//找到最後一個結點
	while (psList->pNext->pNext != NULL)
	{
		psList = psList->pNext;
	}
	free(psList->pNext);
	psList->pNext = NULL;
}

void sListPopFront(sListNode **ppHead)
{
	assert(ppHead != NULL);
	assert(*ppHead != NULL);
	sListNode *psList = *ppHead;
	*ppHead = (*ppHead)->pNext;
	free(psList);
}

總結:單鏈表的操作就更加靈活了。切記單鏈表中對結點的操作順序有的是絕對不能改變的,否則會丟失結點造成一系列問題。還有,如果你想知道一個結點就得先知道它前一個結點,你想看最後一個結點就得遍歷整個鏈表,這樣相對於繁瑣,所以又提出了帶頭循環雙鏈表的概念(下篇博客中^-^)

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