数据结构 C 代码 2.2: 单链表

学习目标:

学会单链表的创建, 添加, 插入, 删除.

学习指导:帆神的代码


学习任务:

  1. 抄代码.
  2. 进行 insertElement 等函数进一步的测试.
  3. 挑本贴 bug 并留言.
  4. 写 CSDN 博客. 必须有图示, 可以用工具画, 或者手绘拍照.
  5. 学习成果目录

    1.准备工作

    2.插入操作     时间复杂度O(n)

    3.删除操作     时间复杂度O(n)

    4.两个测试类及测试结果

    5.全部代码


学习时间:

2022.4.28


1.准备工作

1.1链表的结构体 

/**
 * 先把链表定义好
 */
typedef struct LinkNode {
	char data;
	struct LinkNode *next;
} LNode, *LinkList, *NodePtr;

1.2创建头节点并初始化时间复杂度O(1)

/**
 * @brief 初始化链表头节点
 *
 * @return tempHeader
 */
LinkList initLinkList() {
	NodePtr tempHeader = (NodePtr)malloc(sizeof(LNode));
	tempHeader->data = '\0';
	tempHeader->next = NULL;
	return tempHeader;
}

1.3打印链表时间复杂度O(n)

/**
 * @brief 打印链表
 *
 * @param paraHeader
 */
void printList(NodePtr paraHeader) {
	NodePtr p = paraHeader->next;
	while (p != NULL) {
		printf("%C", p->data);
		p = p->next;
	}
	printf("\n");
}

1.4尾插法插入元素时间复杂度O(n)

 

/**
 * @brief 尾插法添加一个节点
 *
 * @param paraChar
 * @param paraHeader
 */
void appendElement(NodePtr paraHeader, char paraChar) {
	NodePtr p, q;

	//1创建一个新节点
	q = (NodePtr)malloc(sizeof(LNode));
	q->data = paraChar;
	q->next = NULL;

	//2找到尾节点
	p = paraHeader;
	while (p->next != NULL) {
		p = p->next;
	}

	//3把新节点插入尾节点后面
	p->next = q;

}

2.插入操作     时间复杂度O(n)

 

/**
 * @brief 在指定位置插入一个节点
 *
 * @param paraChar
 * @param paraHeader
 * @param paraPosition
 */
void insertElement(NodePtr paraHeader, char paraChar, int paraPosition) {
	NodePtr p, q;

	//1创建新节点
	q = (NodePtr)malloc(sizeof(LNode));
	q->data = paraChar;

	//2找到插入节点位置
	p = paraHeader;
	for (int i = 0; i < paraPosition; i++) {
		p = p->next;
		if (p == NULL) {
			//未找到节点位置,报错
			printf("位置%d超出了链表长度范围", paraPosition);
			return ;
		}
	}

	//3把新建节点插入链表
	//为什么要打印个linking??????
	printf("插入节点%c成功\r\n",paraChar);
	q->next = p->next;
	p->next = q;
}

3.删除操作     时间复杂度O(n)

 

/**
 * @brief 根据元素的数据删除链表节点
 *
 * @param paraChar
 * @param paraHeader
 */
void deleteElement(NodePtr paraHeader, char paraChar) {
	NodePtr p, q;
	//1找到要删除节点的前一个节点
	p = paraHeader;
	while ((p->next != NULL) && (p->next->data != paraChar)) {
		p = p->next;
	}
	if (p->next == NULL) {
		printf("无法找到所需要删除的数据%c\r\n", paraChar);
		return;
	}
	//释放所需要删除节点
	//把删除节点的前一个节点指向删除节点后一个节点
	q = p->next;
	p->next = p->next->next;
	free(q);
}

4.两个测试类及测试结果

4.1增删测试类


/**
 * @brief 增删测试类
 */
void appendInsertDeleteTest() {
	printf("----appendInsertDeleteTest开始测试-----\n");
	//1初始化空链表
	LinkList tempList = initLinkList();
	printList(tempList);

	//2加入一些节点
	appendElement(tempList, 'H');
	appendElement(tempList, 'e');
	appendElement(tempList, 'l');
	appendElement(tempList, 'l');
	appendElement(tempList, 'o');
	appendElement(tempList, '!');
	printList(tempList);

	//3删除节点
	deleteElement(tempList, 'e');
	deleteElement(tempList, 'a');
	deleteElement(tempList, 'o');
	printList(tempList);

	//4在位置1初插入一个o
	insertElement(tempList, 'o', 1);
	printList(tempList);
	printf("--------------**********-------------\n\n");
}

4.2地址分配观察测试类

/**
 * @brief 地址分配观察测试类
 */
void basicAddressTest() {
	printf("------basicAddressTest开始测试-------\n");
	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;
	printf("--------------**********-------------\n\n");
}

4.3测试结果

----appendInsertDeleteTest开始测试-----

Hello!
无法找到所需要删除的数据a
Hll!
插入节点o成功
Holl!
--------------**********-------------

------basicAddressTest开始测试-------
The first  node: -2080376352, -2080376352, -2080376344
The second node: -2080376368, -2080376368, -2080376360
--------------**********-------------

--------------------------------
Process exited after 0.03241 seconds with return value 0
Press ANY key to continue...

5.全部代码

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

/**
 * 先把链表定义好
 */
typedef struct LinkNode {
	char data;
	struct LinkNode *next;
} LNode, *LinkList, *NodePtr;

/**
 * @brief 初始化链表头节点
 *
 * @return tempHeader
 */
LinkList initLinkList() {
	NodePtr tempHeader = (NodePtr)malloc(sizeof(LNode));
	tempHeader->data = '\0';
	tempHeader->next = NULL;
	return tempHeader;
}

/**
 * @brief 打印链表
 *
 * @param paraHeader
 */
void printList(NodePtr paraHeader) {
	NodePtr p = paraHeader->next;
	while (p != NULL) {
		printf("%C", p->data);
		p = p->next;
	}
	printf("\n");
}

/**
 * @brief 尾插法添加一个节点
 *
 * @param paraChar
 * @param paraHeader
 */
void appendElement(NodePtr paraHeader, char paraChar) {
	NodePtr p, q;

	//1创建一个新节点
	q = (NodePtr)malloc(sizeof(LNode));
	q->data = paraChar;
	q->next = NULL;

	//2找到尾节点
	p = paraHeader;
	while (p->next != NULL) {
		p = p->next;
	}

	//3把新节点插入尾节点后面
	p->next = q;

}

/**
 * @brief 在指定位置插入一个节点
 *
 * @param paraChar
 * @param paraHeader
 * @param paraPosition
 */
void insertElement(NodePtr paraHeader, char paraChar, int paraPosition) {
	NodePtr p, q;

	//1创建新节点
	q = (NodePtr)malloc(sizeof(LNode));
	q->data = paraChar;

	//2找到插入节点位置
	p = paraHeader;
	for (int i = 0; i < paraPosition; i++) {
		p = p->next;
		if (p == NULL) {
			//未找到节点位置,报错
			printf("位置%d超出了链表长度范围", paraPosition);
			return ;
		}
	}

	//3把新建节点插入链表
	//为什么要打印个linking??????
	printf("插入节点%c成功\r\n",paraChar);
	q->next = p->next;
	p->next = q;
}

/**
 * @brief 根据元素的数据删除链表节点
 *
 * @param paraChar
 * @param paraHeader
 */
void deleteElement(NodePtr paraHeader, char paraChar) {
	NodePtr p, q;
	//1找到要删除节点的前一个节点
	p = paraHeader;
	while ((p->next != NULL) && (p->next->data != paraChar)) {
		p = p->next;
	}
	if (p->next == NULL) {
		printf("无法找到所需要删除的数据%c\r\n", paraChar);
		return;
	}
	//释放所需要删除节点
	//把删除节点的前一个节点指向删除节点后一个节点
	q = p->next;
	p->next = p->next->next;
	free(q);
}

/**
 * @brief 增删测试类
 */
void appendInsertDeleteTest() {
	printf("----appendInsertDeleteTest开始测试-----\n");
	//1初始化空链表
	LinkList tempList = initLinkList();
	printList(tempList);

	//2加入一些节点
	appendElement(tempList, 'H');
	appendElement(tempList, 'e');
	appendElement(tempList, 'l');
	appendElement(tempList, 'l');
	appendElement(tempList, 'o');
	appendElement(tempList, '!');
	printList(tempList);

	//3删除节点
	deleteElement(tempList, 'e');
	deleteElement(tempList, 'a');
	deleteElement(tempList, 'o');
	printList(tempList);

	//4在位置1初插入一个o
	insertElement(tempList, 'o', 1);
	printList(tempList);
	printf("--------------**********-------------\n\n");
}


/**
 * @brief 地址分配观察测试类
 */
void basicAddressTest() {
	printf("------basicAddressTest开始测试-------\n");
	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;
	printf("--------------**********-------------\n\n");
}


int main() {
	appendInsertDeleteTest();
	basicAddressTest();
	return 0;
}

摘要:
只需要定义一个节点类型, 空间只受内存堆空间的限制.
抓住了头节点, 就抓住了整个世界.
可以根据循环语句, 分析时间复杂度.
这里必须用图示来分析 append, insert, delete 操作.
basicAddressTest 用于观察地址分配. 多写几个这样的函数, 就更理解操作系统的机制.


 

猜你喜欢

转载自blog.csdn.net/qq_61649579/article/details/124478892
今日推荐