【数据结构--单链表】

单链表

一、单链表的定义

​ 由于顺序表的插入删除操作需要移动大量的元素,影响了运行效率,因此引入了线性表的链式存储——单链表

单链表示意图

在这里插入图片描述

二、线性表的链式存储结构

线性表的链式存储结构:线性表中数据元素(结点)在存储器中的位置是任意的,即逻辑上相邻的数据元素在物理位置上不一定相邻。

单链表的链式存储结构

在这里插入图片描述

三、单链表的实现

单链表存储结构代码
typedef struct Node
{
    char data;
    struct Node* next;
} Node,*LinkList;
初始化单链表
//初始化单链表
LinkList initLinkList()
{
    Node* LinkHead = (Node*)malloc(sizeof(Node));
    LinkHead->data = '\0';
    LinkHead->next = NULL;
    return LinkHead;
}
打印单链表
//打印单链表
void printList(Node* LinkHead)
{
    Node* p = LinkHead->next;
    while(p != NULL)
    {
        printf("%c",p->data);
        p = p->next;
    }
    printf("\n\n");
}
向单链表中添加元素
//向单链表中添加元素
void addElement(Node* LinkHead,char addChar)
{
    Node* p,*q;
    q = (Node*)malloc(sizeof(Node));
    q->data = addChar;
    q->next = NULL;
    p = LinkHead;
    while(p->next != NULL)
    {
        p = p->next;
    }
    p->next = q;
}
单链表的读取

思路:

(1)声明一个指针 p 指向链表第一个节点,初始化 j 从 1 开始;

(2)当j < i时,遍历链表,让p的指针向后移动,不断指向下一节点,j 累加1;

(3)若到链表末尾 p 为空,则说明第 i 个节点不存在;

(4)否则查找成功,打印节点 p 的数据。

代码如下:

//单链表的读取(打印单链表中第i个数据元素的值)
void GetElement(Node* LinkHead, int i)
{
    Node* p;
    p = LinkHead->next;
    int j = 1;
    while(p != NULL && j < i)
    {
        p = p->next;
        ++j;
    }
    if(p == NULL || j > i)
    {
        printf("Can't get it !\n\n");
        return ;
    }
    printf("The element at its %d-th position is %c\n\n",i,p->data);
}
单链表的插入

​ 设储存元素 e 的节点为 q ,若要实现将节点 q 插入到节点 p 和 p->next 之间,只需要让 q->next和 p->next 的指针做一点改变即可。

单链表的插入

单链表的插入
在这里插入图片描述

代码如下:

//单链表的插入
void ListInsert(Node* LinkHead, int position, char InsertChar)
{
    if(position <= 0)
    {
        printf("The position %d out of range of linked list!\n\n",position);
        return ;
    }
    Node* p, *q;
    p = LinkHead; 
    for(int i = 0; i < position; ++i)
    {
        p = p->next;
        if(p == NULL)
        {
            printf("The position %d out of range of linked list!\n\n ",position);
            return ;
        }
    }
    q = (Node*)malloc(sizeof(Node));
    q->data = InsertChar;
    q->next = p->next;
    p->next = q;
}
单链表的删除

​ 设存储元素 ai 的节点为 q,要实现将节点 q 删除单链表的操作,其实就是将它的前继节点的指针绕过,指向它的后继节点即可。

单链表的删除

在这里插入图片描述

代码如下:

//单链表的删除(删除第i个数据元素)
void ListDelete(Node* LinkHead, int i)
{
    int j = 1;
    Node *p, *q;
    p = LinkHead;
    while(p->next != NULL && j < i)
    {
        p = p->next;
        ++j;
    }
    if(p->next == NULL || j > i)
    {
        printf("Can't delete it!\n\n");
        return ;
    }
    q = p->next;
    p ->next = q->next;
    free(q);
}
单链表的整表删除

​ 当我们不打算使用这个单链表时,我们需要把它销毁,即在内存中将其释放,思路如下:

(1)声明一指针 p 和 q

(2)将第一个节点赋值给 p

(3)循环:

​ ① 将下一节点赋值给 q ;

​ ② 释放 p ;

​ ③ 将 q 赋值给 p 。

代码如下:

//单链表的整表删除
void ClearList(Node* LinkHead)
{
    Node *q,*p;
    p = LinkHead->next;
    while(p != NULL)
    {
        q = p->next;
        free(p);
        p = q;
    }
    LinkHead->next = NULL;
}
完整代码
#include <stdio.h>
#include <stdlib.h>

typedef struct Node
{
    char data;
    struct Node* next;
} Node,*LinkList;

//初始化单链表
LinkList initLinkList()
{
    Node* LinkHead = (Node*)malloc(sizeof(Node));
    LinkHead->data = '\0';
    LinkHead->next = NULL;
    return LinkHead;
}

//打印单链表
void printList(Node* LinkHead)
{
    Node* p = LinkHead->next;
    while(p != NULL)
    {
        printf("%c",p->data);
        p = p->next;
    }
    printf("\n\n");
}

//向单链表中添加元素
void addElement(Node* LinkHead,char addChar)
{
    Node* p,*q;
    q = (Node*)malloc(sizeof(Node));
    q->data = addChar;
    q->next = NULL;
    p = LinkHead;
    while(p->next != NULL)
    {
        p = p->next;
    }
    p->next = q;
}

//单链表的读取(打印单链表中第i个数据元素的值)
void GetElement(Node* LinkHead, int i)
{
    Node* p;
    p = LinkHead->next;
    int j = 1;
    while(p != NULL && j < i)
    {
        p = p->next;
        ++j;
    }
    if(p == NULL || j > i)
    {
        printf("Can't get it !\n\n");
        return ;
    }
    printf("The element at its %d-th position is %c\n\n",i,p->data);
}

//单链表的插入
void ListInsert(Node* LinkHead, int position, char InsertChar)
{
    if(position <= 0)
    {
        printf("The position %d out of range of linked list!\n\n",position);
        return ;
    }
    Node* p, *q;
    p = LinkHead; 
    for(int i = 0; i < position; ++i)
    {
        p = p->next;
        if(p == NULL)
        {
            printf("The position %d out of range of linked list!\n\n ",position);
            return ;
        }
    }
    q = (Node*)malloc(sizeof(Node));
    q->data = InsertChar;
    q->next = p->next;
    p->next = q;
}

//单链表的删除(删除第i个数据元素)
void ListDelete(Node* LinkHead, int i)
{
    int j = 1;
    Node *p, *q;
    p = LinkHead;
    while(p->next != NULL && j < i)
    {
        p = p->next;
        ++j;
    }
    if(p->next == NULL || j > i)
    {
        printf("Can't delete it!\n\n");
        return ;
    }
    q = p->next;
    p ->next = q->next;
    free(q);
}

//单链表的整表删除
void ClearList(Node* LinkHead)
{
    Node *q,*p;
    p = LinkHead->next;
    while(p != NULL)
    {
        q = p->next;
        free(p);
        p = q;
    }
    LinkHead->next = NULL;
}

//测试
void test()
{
    LinkList tempLinkList = initLinkList();
    printList(tempLinkList);

    //添加元素测试
    printf("--------------addElementTest--------------\n\n");
    addElement(tempLinkList,'I');
    addElement(tempLinkList,' ');
    addElement(tempLinkList,'l');
    addElement(tempLinkList,'o');
    addElement(tempLinkList,'v');
    addElement(tempLinkList,'e');
    addElement(tempLinkList,' ');
    addElement(tempLinkList,'d');
    addElement(tempLinkList,'a');
    addElement(tempLinkList,'t');
    addElement(tempLinkList,'a');
    addElement(tempLinkList,'!');
    printList(tempLinkList);

    //读取测试
    printf("--------------GetElementTest--------------\n\n");
    GetElement(tempLinkList,1);

    //插入测试
    printf("--------------ListInsertTest--------------\n\n");
    ListInsert(tempLinkList,12,'!');
    ListInsert(tempLinkList,14,'!');
    ListInsert(tempLinkList,-1,'!');
    printList(tempLinkList);

    //删除测试
    printf("--------------ListDeleteTest--------------\n\n");
    ListDelete(tempLinkList,13);
    ListDelete(tempLinkList,15);
    printList(tempLinkList);

    //清空测试
    printf("--------------ClearListTest--------------\n\n");
    ClearList(tempLinkList);
    printList(tempLinkList);
}

int main()
{
    test();
}
运行截图

在这里插入图片描述

闵版代码(略改)
#include <stdio.h>
#include <malloc.h>

/*
	Linked list of characters. The key is data.
*/

typedef struct LinkNode{
	char data;
	struct LinkNode* next;
} LNode,*LinkList,*NodePtr;

/*
	Initialize the list with a header.
	@return The pointer to the header.
*/

LinkList initLinkList()
{
	NodePtr tempHeader = (NodePtr)malloc(sizeof(LNode));
	tempHeader->data = '\0';
	tempHeader->next = NULL;
	return tempHeader;
}	// Off initLinkList

/*
	Print the list.
	@param paraHeader The header of the list.
*/

void printList(NodePtr paraHeader)
{
	NodePtr p = paraHeader->next;
	while(p != NULL)
	{
		printf("%c",p->data);
		p = p->next;
	}printf("\r\n");
}	//Off printList

/*
	Add an element to the tail.
	@param paraHeader The header of the list.
	@param paraChar the given char.
*/

void appendElement(NodePtr paraHeader, char paraChar)
{
	NodePtr p,q;

	// Step 1. Construct a new node.
	q = (NodePtr)malloc(sizeof(LNode));
	q->data = paraChar;
	q->next = NULL;

	// Step 2. Search to the tail.
	p = paraHeader;
	while (p->next != NULL)
	{
		p = p->next;
	}	//Off while 
	
	// Step 3. Now add/link.
	p->next = q;
}	//Off appendElement

/*
	Insert an element to the given position.
	@param paraHeader The header of the list.
	@param paraChar The given char.
	@param paraPosition The given position.
*/

void insertElement(NodePtr paraHeader,char paraChar,int paraPositon)
{
	NodePtr p,q;

	// Step 1. Search to the position.
	p = paraHeader;
	for(int i = 0; i < paraPositon; ++i)
	{
		p = p->next;
		if(p == NULL)
		{
			printf("The position %d is beyond the scope of the list.",paraPositon);
			return ;
		}	//Off if
	}	//Off for i

	// Step 2. Construct a new node.
	q = (NodePtr)malloc(sizeof(LNode));
	q->data = paraChar;

	// Step 3. Now link.
	printf("linking\r\n");
	q->next = p->next;
	p->next = q;
}	//Off insertElement

/*
	Delete an element from the list.
	@param paraHeader The header of the list.
	@param paraChar The given char.
*/

void deleteElement(NodePtr paraHeader,char paraChar)
{
	NodePtr p,q;
	 p = paraHeader;
	 while((p->next != NULL) && (p->next->data != paraChar))
	 {
		 p = p->next;
	 }	//Off while

	 if(p->next == NULL)
	 {
		 printf("Can't delete %c\r\n",paraChar);
		 return ;
	 }	//Off if

	 q = p->next;
	 p->next = q->next;
	 free(q);
}	//Off deleteElement

/*
	Unit test
*/

void appendInsertDeleteTest()
{
	// Step 1. Initialize an empty list.
	LinkList tempList = initLinkList();
	printList(tempList);

	// Step 2. Add some characters.
	appendElement(tempList,'H');
	appendElement(tempList,'e');
	appendElement(tempList,'l');
	appendElement(tempList,'l');
	appendElement(tempList,'o');
	appendElement(tempList,'!');
	printList(tempList);

	//Step 3. Delete some characters(the first occurence).
	deleteElement(tempList,'e');
	deleteElement(tempList,'a');
	deleteElement(tempList,'o');
	printList(tempList);

	// Step 4. Insert to a given position.
	insertElement(tempList,'o',1);
	printList(tempList);
}	//Off appendInsertDeleteTest

/*
	Address test: beyond the book.
*/

void basicAddressTest()
{
	LNode tempNode1,tempNode2;
	tempNode1.data = 4;
	tempNode1.next = NULL;
	tempNode2.data = 6;
	tempNode2.next = NULL;
	printf("The first node: %p, %p, %p\r\n",&tempNode1,&tempNode1.data,&tempNode1.next);
	printf("The second node: %p, %p, %p\r\n",&tempNode2,&tempNode2.data,&tempNode2.next);
}	//Off basicAddressTest

/*
	The entrance.
*/

int main()
{
	appendInsertDeleteTest();
	basicAddressTest();
}	//Off main

四、总结

​ 单链表与顺序表的简单对比:

  • 若线性表需要频繁查找,很少进行插入及删除操作时,宜采用顺序表

  • 当线性表中元素格式变化较大或不知道有多大时,最好采用单链表

总之,单链表和顺序表各有其优缺点,不能简单评判其好坏,应当根据实际情况,综合考虑使用哪种数据结构更优。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

黎子想写好代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值