資料結構——單連結串列介面實現(C語言)

五顆粒粒發表於2020-12-12

github程式碼下載

github程式碼:
https://github.com/Kyrie-leon/Data_Structures/tree/main/ListNode

一、連結串列的概念及結構

1.1 概念

連結串列是一種物理結構上非連續、非順序的儲存結構,資料元素的邏輯順序是通過連結串列中的指標連結次序實現的。

1.2 結構

在這裡插入圖片描述

二、連結串列的介面實現

2.1 連結串列的儲存定義

typedef int SLTDataType;

//單連結串列的定義
typedef struct SListNode
{
	SLTDataType data;	
	struct SListNode * next; //next指向當前節點的下一個節點
}SListNode;

2.2 動態申請一個節點

申請一個節點首先使用malloc開闢一塊結構記憶體
然後將資料元素x放入data資料區
將next置空
在這裡插入圖片描述


//2.2 動態申請一個節點
SListNode * BuyListNode(SLTDataType x)
{
	
	SListNode * plist=(SListNode *)malloc(sizeof(SListNode));
	//判斷申請是否成功
	if (plist==NULL)
	{
		printf("申請新節點失敗\n");
		exit(-1);
	}
	plist->data = x;
	plist->next = NULL;
	return plist;
}

2.3 單連結串列列印

//2.3 單連結串列列印
void SListPrint(SListNode * plist)
{

	//列印連結串列
	while (plist)
	{
		printf("%d->", plist->data);
		plist = plist->next;	//節點指向下一個
	}
	printf("NULL\n");
}

2.4 單連結串列尾插

  • 首先申請一個新節點
    在這裡插入圖片描述

  • 判斷連結串列是否為空,如果為空則直接將新節點插入

  • 不是空的話,遍歷連結串列找到尾節點
    在這裡插入圖片描述

  • 找到後,申請新節點,讓原連結串列尾節點指向新節點
    在這裡插入圖片描述
    注意:由於尾插可能存在空連結串列的情況,因此當連結串列為空時傳入一級指標不會改變連結串列的狀態,所以傳入二級指標。

//2.4 單連結串列尾插
void SListPushBack(SListNode** pplist, SLTDataType x)
{
	//申請一個新節點
	SListNode* NewNode = BuyListNode(x);

	//判斷連結串列是否為空,如果為空則申請節點直接插入,否則找到尾節點插入
	if ((*pplist) == NULL)
	{
		*pplist = NewNode;
	}
	else
	{
		//定義一個當前節點tail去找連結串列的尾節點
		SListNode* tail = *pplist;

		//遍歷節點找尾節點
		while (tail->next)
		{
			tail = tail->next;
		}

		//尾節點的next指向新節點
		tail->next = NewNode;
	}

}

2.5 單連結串列頭插

  • 首先申請新節點
  • 判斷連結串列是否為空,若為空直接插入
  • 不為空,讓NewNode的next指向Plist,然後讓Plist指向NewNode
    在這裡插入圖片描述
//2.5 單連結串列頭插
void SListPushFront(SListNode** pplist, SLTDataType x)
{
	//申請新節點
	SListNode * NewNode = BuyListNode(x);
	//判斷連結串列是否空
	if (*pplist == NULL)
	{
		//連結串列為空直接插入
		*pplist = NewNode;

	}
	else 
	{
		NewNode->next = *pplist;	//新節點next指向pplist
		*pplist=NewNode;			//更新頭結點
	}

}

2.6 單連結串列尾刪

尾刪有三種情況:

  • 單連結串列為空則返回

  • 單連結串列只有一個節點,直接刪除
    在這裡插入圖片描述

  • 單連結串列有一個以上節點,定義兩個指標,一個指向尾節點,一個指向尾節點前的一個節點。首先找到尾節點刪除,
    在這裡插入圖片描述
    然後將prev的next置空
    在這裡插入圖片描述

//2.6 單連結串列尾刪
void SListPopBack(SListNode** pplist)
{
	//1.單連結串列為空
	if (*pplist == NULL)
	{
		return;
	}
	//2.單連結串列只有一個節點
	else if((*pplist)->next==NULL)
	{
		free(*pplist);
		*pplist = NULL;
	}
	//3.單連結串列有一個以上節點
	else
	{
		//tali指向第1個節點,prev為空
		SListNode * prev = NULL;
		SListNode * tail = *pplist;

		
		//遍歷連結串列找到尾節點
		while (tail->next!=NULL)
		{
			//注意先後順序,prev先走才不會丟失節點
			//prev向後走一個節點
			prev = tail;
			//tail向後走一個節點
			tail = tail->next;
		}
		//刪除尾節點
		free(tail);
		//prev作為新的尾節點
		prev->next = NULL;

	}
}

2.7 單連結串列頭刪

連結串列三種情況同上
首先找到頭結點,
在這裡插入圖片描述
讓*pplist指向下一個節點
刪除頭結點
在這裡插入圖片描述

//2.7 單連結串列頭刪
void SListPopFront(SListNode** pplist)
{
	//1.空單連結串列
	if (*pplist == NULL)
	{
		return;
	}
	//2.一個節點
	else if ((*pplist)->next == NULL)
	{
		free(*pplist);
		*pplist = NULL;
	}
	//3.一個以上
	else
	{

		SListNode * phead = *pplist;
		//現移動刪除後的頭結點
		*pplist = phead->next;
		//再釋放原來的頭結點
		free(phead);
	}
}

2.8 單連結串列查詢

//2.8 單連結串列查詢
SListNode * SListFind(SListNode * plist, SLTDataType x)
{
	SListNode * cur = plist;
	//1.空連結串列直接返回
	//遍歷連結串列
	while (cur)
	{
		//如果找節點值等於x則返回
		if (x == cur->data)
		{
			return cur;
		}
		//沒找到繼續遍歷
		cur = cur->next;
	}
	return NULL;
}

2.9 單連結串列任意位置之後插入

在pos位置之後插入新節點容易實現,首先讓新節點指向當前節點cur的下一個節點,然後讓cur當前節點指向新節點
在這裡插入圖片描述

//2.9 單連結串列在任意位置之後插入x
void SListInsertAfter(SListNode* pos, SLTDataType x)
{
	assert(pos);
	SListNode * newNode = BuyListNode(x);
	newNode->next = pos->next;
	pos->next = newNode;
}

2.10 單連結串列刪除任意位置之後的值

在這裡插入圖片描述

//2.10 單連結串列刪除任意位置之後的值
void SListEraseAfter(SListNode* pos)
{
	assert(pos);
	if (pos->next)
	{
		SListNode * next = pos->next;	//找到pos位置後面的節點儲存起來,防止刪除後丟失
		pos->next = next->next;			//pos位置節點指向next後的節點
		free(next);		//釋放pos後的節點
	}
}

三、多檔案實現

3.1 ListNode.h

#ifndef _LISTNODE_H
#define _LISTNODE_H

#include<stdio.h>
#include<windows.h>
#include<stdlib.h>
#include<assert.h>

typedef int SLTDataType;

//單連結串列的定義
typedef struct SListNode
{
	SLTDataType data;	//data
	struct SListNode * next; //next指向當前節點的下一個節點
}SListNode;


//動態申請一個節點
SListNode * BuyListNode(SLTDataType x);
//單連結串列列印
void SListPrint(SListNode * plist);
//單連結串列尾插
void SListPushBack(SListNode** pplist, SLTDataType x);
//單連結串列頭插
void SListPushFront(SListNode** pplist, SLTDataType x);
//單連結串列尾刪
void SListPopBack(SListNode** pplist);
//單連結串列頭刪
void SListPopFront(SListNode** pplist);
//單連結串列查詢
SListNode * SListFind(SListNode * plist, SLTDataType x);
//單連結串列在任意位置之後插入x
void SListInsertAfter(SListNode* pos, SLTDataType x);
//單連結串列刪除任意位置之後的值
void SListEraseAfter(SListNode* pos);
//單連結串列的銷燬
void SListDestory(SListNode* plist);


#endif

3.2 ListNode.c

#include "ListNode.h"

//2.2 動態申請一個節點
SListNode * BuyListNode(SLTDataType x)
{
	
	SListNode * plist=(SListNode *)malloc(sizeof(SListNode));
	//判斷申請是否成功
	if (plist==NULL)
	{
		printf("申請新節點失敗\n");
		exit(-1);
	}
	plist->data = x;
	plist->next = NULL;
	return plist;
}

//2.3 單連結串列列印
void SListPrint(SListNode * plist)
{

	//列印連結串列
	while (plist)
	{
		printf("%d->", plist->data);
		plist = plist->next;	//節點指向下一個
	}
	printf("NULL\n");
}

//2.4 單連結串列尾插
void SListPushBack(SListNode** pplist, SLTDataType x)
{
	//申請一個新節點
	SListNode* NewNode = BuyListNode(x);

	//判斷連結串列是否為空,如果為空則申請節點直接插入,否則找到尾節點插入
	if ((*pplist) == NULL)
	{
		*pplist = NewNode;
	}
	else
	{
		//定義一個當前節點tail去找連結串列的尾節點
		SListNode* tail = *pplist;

		//遍歷節點找尾節點
		while (tail->next)
		{
			tail = tail->next;
		}

		//尾節點的next指向新節點
		tail->next = NewNode;
	}

}

//2.5 單連結串列頭插
void SListPushFront(SListNode** pplist, SLTDataType x)
{
	//申請新節點
	SListNode * NewNode = BuyListNode(x);
	//判斷連結串列是否空
	if (*pplist == NULL)
	{
		//連結串列為空直接插入
		*pplist = NewNode;

	}
	else 
	{
		NewNode->next = *pplist;	//新節點next指向pplist
		*pplist=NewNode;			//更新頭結點
	}

}

//2.6 單連結串列尾刪
void SListPopBack(SListNode** pplist)
{
	//1.單連結串列為空
	if (*pplist == NULL)
	{
		return;
	}
	//2.單連結串列只有一個節點
	else if((*pplist)->next==NULL)
	{
		free(*pplist);
		*pplist = NULL;
	}
	//3.單連結串列有一個以上節點
	else
	{
		//tali指向第1個節點,prev為空
		SListNode * prev = NULL;
		SListNode * tail = *pplist;

		
		//遍歷連結串列找到尾節點
		while (tail->next!=NULL)
		{
			//注意先後順序,prev先走才不會丟失節點
			//prev向後走一個節點
			prev = tail;
			//tail向後走一個節點
			tail = tail->next;
		}
		//刪除尾節點
		free(tail);
		//prev作為新的尾節點
		prev->next = NULL;

	}
}

//2.7 單連結串列頭刪
void SListPopFront(SListNode** pplist)
{
	//1.空單連結串列
	if (*pplist == NULL)
	{
		return;
	}
	//2.一個節點
	else if ((*pplist)->next == NULL)
	{
		free(*pplist);
		*pplist = NULL;
	}
	//3.一個以上
	else
	{

		SListNode * phead = *pplist;
		//現移動刪除後的頭結點
		*pplist = phead->next;
		//再釋放原來的頭結點
		free(phead);
	}
}

//2.8 單連結串列查詢
SListNode * SListFind(SListNode * plist, SLTDataType x)
{
	SListNode * cur = plist;
	//1.空連結串列直接返回
	//遍歷連結串列
	while (cur)
	{
		//如果找節點值等於x則返回
		if (x == cur->data)
		{
			return cur;
		}
		//沒找到繼續遍歷
		cur = cur->next;
	}
	return NULL;
}

//2.9 單連結串列在任意位置之後插入x
void SListInsertAfter(SListNode* pos, SLTDataType x)
{
	assert(pos);
	SListNode * newNode = BuyListNode(x);
	newNode->next = pos->next;
	pos->next = newNode;
}

//2.10 單連結串列刪除任意位置之後的值
void SListEraseAfter(SListNode* pos)
{
	assert(pos);
	if (pos->next)
	{
		SListNode * next = pos->next;	//找到pos位置後面的節點儲存起來,防止刪除後丟失
		pos->next = next->next;			//pos位置節點指向next後的節點
		free(next);		//釋放pos後的節點
	}
}

3.3 test.c

#include "ListNode.h"


//測試單連結串列的頭尾刪除
void TestListNode1()
{
	SListNode * Plist=NULL;	//定義一個單連結串列

	//測試申請一個節點
	Plist = BuyListNode(1);

	//列印連結串列測試
	SListPrint(Plist);

	//2.4 單連結串列尾插測試
	SListPushBack(&Plist, 2);
	SListPushBack(&Plist, 3);
	SListPushBack(&Plist, 4);
	SListPushBack(&Plist, 5);
	SListPrint(Plist);

	//2.5 單連結串列頭插
	SListPushFront(&Plist, -1);
	SListPrint(Plist);

	//單連結串列尾刪
	//SListPopBack(&Plist);
	//SListPopBack(&Plist);
	//SListPopBack(&Plist);
	//SListPopBack(&Plist);
	//SListPopBack(&Plist);
	//SListPopBack(&Plist);
	//SListPopBack(&Plist);
	//SListPopBack(&Plist);	
	//SListPopBack(&Plist);	
	//SListPopBack(&Plist);
	//SListPrint(Plist);

	//單連結串列頭刪
	//SListPopFront(&Plist);
	//SListPopFront(&Plist);
	//SListPopFront(&Plist);
	//SListPopFront(&Plist);
	//SListPopFront(&Plist);
	//SListPopFront(&Plist);
	//SListPopFront(&Plist);
	//SListPopFront(&Plist);
	//SListPrint(Plist);

	//2.8 單連結串列查詢
	SListNode * pos = SListFind(Plist, 3);
	SListPrint(pos);

	//2.9 單連結串列在任意位置之後插入x
	SListInsertAfter(pos, 30);
	SListPrint(Plist);

	//2.10 單連結串列刪除任意位置之後的值
	SListEraseAfter(pos);
	SListPrint(Plist);


}



int main()
{
	TestListNode1();
	system("pause");
	return 0;
}

相關文章