【数据结构】单链表

单链表是一种数据结构,它由一个节点序列组成,每个节点包含两个部分:数据和指向下一个节点的指针。单链表中只能向一个方向遍历,即从头节点开始,依次向后遍历。该数据结构可以用于实现栈、队列等数据结构,也可用于解决一些具有类似链式结构的问题。由于单链表的节点只包含一个指向下一个节点的指针,因此在插入、删除节点时较为方便,但是在查找节点时需要遍历整个链表。

在链式存储中,结点之间的存储单元地址是不连续的。链式存储中每个结点都包含两个部分:存储元素本身的数据域和存储结点地址的指针域。

单链表的元素插入与删除

#include<stdio.h>
#include<malloc.h>

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


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


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


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;
	}// Of while

	//Step 3: Now add/link
	p->next = q;
}// Of appendElement


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

	//Step 1: Search to the position
	p = paraHeader;
	for (int i = 0; i < paraPosition; i++)
	{
		p = p->next;
		if (p == NULL)
		{
			printf("The position %d is beyond the scope of the list.", paraPosition);
			return;
		}// Of if
	}// Of 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;
}// Of insertElement


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

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

	q = p->next;
	p->next = p->next->next;
	free(q);
}// Of 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 charcters(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);
}// Of appendInsertDeleteTest

int main()
{
	appendInsertDeleteTest();
	return 0;
}// Of main

在这里插入图片描述

  1. 初始化LinkList initLinkList();
    单链表的第一个结点之前附加一个结点,称为头结点;
    单链表的初始化操作就是申请一个头结点,将指针域置空
typedef struct LinkNode
{
	char data;
	struct LinkNode *next;
} LNode, *LinkList, *NodePtr;

定义了一个单链表的节点结构体,其中包含一个字符类型的数据成员和一个指向下一个节点的指针成员。同时,通过typedef关键字,将该结构体类型定义为三个别名:LNode、LinkList和NodePtr。
LNode代表单链表的节点类型,可以用来定义单链表的节点变量
LinkList代表单链表的类型,可以用来定义单链表的头指针变量
NodePtr代表单链表节点指针类型,可以用来定义指向单链表节点的指针变量

  1. 遍历单链表void printList(NodePtr paraHeader);
    声明一个指针p,从头结点指向的第一个结点开始,如果p不为空,那么就输出当前结点的值,并将p指向下一个结点即p = p->next;,直到遍历到最后一个结点为止。

  2. 尾部添加元素void appendElement(NodePtr paraHeader, char paraChar);
    先创建一个结点,然后找到链表的尾部,然后将该结点添加到链表的尾部
    在这个操作中不能忘了q->next = NULL;
    该函数通过动态内存分配的方式创建了新节点q,并在单链表尾部添加了该节点。需要注意的是,在使用完动态分配的内存后,需要使用free函数将该内存释放,否则会造成内存泄漏。

  3. 插入元素 void insertElement(NodePtr paraHeader, char paraChar, int paraPosition);
    从表头开始遍历,查找指定位置的前一个结点,即插入位置的前驱结点p,然后令新结点q的指针域指向p的后继结点,再令结点p的指针域指向新结点q。

核心代码:

q->next = p->next;
p->next = q;
  1. 删除元素void deleteElement(NodePtr paraHeader, char paraChar);
    函数首先从头节点开始遍历单链表,查找要删除的元素。具体来说,函数使用while循环遍历单链表,直到找到一个节点,该节点的下一个节点的data成员等于paraChar或者遍历到了单链表的末尾为止。如果遍历到了单链表的末尾,则说明要删除的元素不存在,函数打印出错误信息并返回。否则,函数将要删除的节点q从单链表中删除,并用free释放其占用的内存空间。

核心代码:

q = p->next;
p->next = p->next->next;

下面是测试单链表节点的地址:

/**
 * Address test
 */
void basicAddressTest()
{
	LNode tempNode1, tempNode2;

	tempNode1.data = 4;
	tempNode1.next = NULL;

	tempNode2.data = 6;
	tempNode2.next = NULL;

	printf("The first node: %d, %d, %d\r\n",
		&tempNode1, &tempNode1.data, &tempNode1.next);
	printf("The second node: %d, %d, %d\r\n",
		&tempNode2, &tempNode2.data, &tempNode2.next);
	
	tempNode1.next = &tempNode2;
}// Of basicAddressTest

在该函数中,使用&运算符获取了tempNode1和tempNode2的内存地址、数据成员地址和next指针地址,并通过printf函数打印出来。
分析:
第一个结点的地址以及其data的地址都是895415608
而结点的next则比895415608多了8,说明单链表中各个节点的内存地址一般会相差一个固定的值,这个值通常是节点结构体的大小(sizeof(LNode)).因为在单链表中每个节点都包含一个指向下一个节点的指针,所以每个节点在内存中的地址应该是连续的。在单链表中,每个节点包含一个数据成员和一个指向下一个节点的指针成员,因此一个节点在内存中的大小通常是8字节

在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值