数据结构 C 代码 2.3: 双向链表

总结与反思: 

1,优点:与单链表相比双链表提供了反向遍历的能力,每个节点都环环相扣

2,缺点:比起单链表要操作更多,得把*previous指向前一节点,所以结构体也多了一个指向前一节点的指针,更占空间

3,我的建议是单链表就够用了

学习目标:

学会双向链表. 主要是为了练习链表的变种, 寻找更多设计的感觉. 这样在面对实际问题的时候, 就具有一定的建模能力.

学习指导:帆神的代码

学习任务:

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

     1抄写代码

    1.1双链表结构体

    1.2初始化链表

     1.3打印链表

    1.4插入节点

    1.5删除节点

    2.定位节点

    3.单元测试

    3.1增删

    3.2定位

    3.3地址分配

    3.4运行结果

    4全部代码

3. 代码说明

  1. 实现了打印、插入、定位、删除.
  2. 每个相关指针都要改, 必须画个图来辅助写程序.
  3. 尾结点删除时需要注意. 需要进行边界测试.

学习时间:

2022.5.3

 1抄写代码

1.1双链表结构体

/**
 * 双链表结构体,存储字符串数据
 */
typedef struct DoubleLinkeNdode {
	char data;
	struct DoubleLinkeNdode *previous;
	struct DoubleLinkeNdode *next;
} DLNode, *DLNodePtr;

1.2初始化链表

/**
 * @brief 初始化链表,创建一个头节点
 *
 * @return 返回头节点
 */
DLNodePtr initLinkList() {
	//申请空间
	DLNodePtr tempHeader = (DLNodePtr)malloc(sizeof(struct DoubleLinkeNdode) * 1);
	//初始化
	tempHeader->data = '\0';
	tempHeader->previous = NULL;
	tempHeader->next = NULL;
	return tempHeader;
}

 1.3打印链表

/**
 * @brief 打印链表
 *
 * @param paraHeader 传入头节点
 */
void printList(DLNodePtr paraHeader) {
	DLNodePtr p = paraHeader->next;
	//节点不为空,则打印数据
	while (p != NULL) {
		printf("%c", p->data);
		p = p->next;
	}
	printf("\n");
}

1.4插入节点

/**
 * @brief 插入一个节点操作
 *
 * @param paraChar
 * @param paraHeader
 * @param paraPosition
 */
void insertElement(DLNodePtr paraHeader, char paraChar, int paraPosition) {
	//创建三个结构体变量:p查找查找插入链表位置,q申请新空间,r插入节点的后一节点,辅助插入
	DLNodePtr p, q, r;
	//1查找插入链表位置
	p = paraHeader;
	for (int i = 0; i < paraPosition; i++) {
		p = p->next;
		if (p == NULL) {
			printf("位置%d已经超出链表范围\n", paraPosition);
			return;
		}
	}

	//2申请空间
	q = (DLNodePtr)malloc(sizeof(struct DoubleLinkeNdode) * 1);
	//赋值
	q->data = paraChar;

	//3把新节点插入双链表
	r = p->next;
	q->next = p->next;
	q->previous = p;
	p->next = q;
	//如果不是尾节点,才需要把插入节点的后一节点指向插入节点(非常巧妙)
	if (r != NULL) {
		r->previous = q;
	}
	printf("插入元素%c成功!\n", paraChar);
}

1.5删除节点

/**
 * @brief 删除一个节点操作
 * 
 * @param paraChar 
 * @param paraHeader 
 */
void deleteElement(DLNodePtr paraHeader, char paraChar) {
	//创建三个结构体变量:p为查找传入数据的前节点
	DLNodePtr p, q, r;
	//1查找传入数据的前节点
	p = paraHeader;
	while ((p->next != NULL) && (p->next->data != paraChar)) {
		p = p->next;
	}
	//2找不到数据,提示并结束
	if (p->next == NULL) {
		printf("节点%c不存在\n", paraChar);
		return;
	}
	//3找到数据,删除并提示
	q = p->next;
	r = q->next;
	p->next = r;
	if (r != NULL) {
		r->previous = p;
	}
	printf("你已经删除了节点%c", q->data);
	free(q);

}

2.定位节点

/**
 * @brief 定位节点
 * 
 * @param paraChar 
 * @param paraHeader 
 * 
 * @return 
 */
int locateElement(DLNodePtr paraHeader, char paraChar) {
	DLNodePtr p = paraHeader;
	for (int i = 0; p != NULL; i++) {
		if (p->data == paraChar) return i;
		p = p->next;
	}
	return -1;
}

3.单元测试

3.1增删

/**
 * 单元测试
 */
void insertDeleteTest() {
	printf("----insertDeleteTest开始测试-----\n");
	DLNodePtr tempList = initLinkList();
	printList(tempList);

	insertElement(tempList, 'H', 0);
	insertElement(tempList, 'e', 1);
	insertElement(tempList, 'l', 2);
	insertElement(tempList, 'l', 3);
	insertElement(tempList, 'o', 4);
	insertElement(tempList, '!', 5);
	printList(tempList);

	deleteElement(tempList, 'e');
	deleteElement(tempList, 'a');
	deleteElement(tempList, 'o');
	printList(tempList);

	insertElement(tempList, 'o', 1);
	printList(tempList);
	printf("--------------**********-------------\n\n");

}

3.2定位

/**
 * 单元测试2
 */
void insertLocateTest() {
	printf("----insertLocateTest开始测试-----\n");
	int location, data;
	DLNodePtr tempList = initLinkList();
	printList(tempList);

	insertElement(tempList, 'H', 0);
	insertElement(tempList, 'e', 1);
	insertElement(tempList, 'l', 2);
	insertElement(tempList, 'l', 3);
	insertElement(tempList, 'o', 4);
	insertElement(tempList, '!', 5);
	printList(tempList);

	data = 'e';
	location = locateElement(tempList, data);
	if (location == -1) {
		printf("找不到元素%c!!!\n", data);
	} else {
		printf("元素%c的位置是%d\n", data, location);
	}
	data = 'a';
	location = locateElement(tempList, data);
	if (location == -1) {
		printf("找不到元素%c!!!\n", data);
	} else {
		printf("元素%c的位置是%d\n", data, location);
	}
	data = 'o';
	location = locateElement(tempList, data);
	if (location == -1) {
		printf("找不到元素%c!!!\n", data);
	} else {
		printf("元素%c的位置是%d\n", data, location);
	}
	printList(tempList);

	insertElement(tempList, 'o', 1);
	printList(tempList);
	printf("--------------**********-------------\n\n");

}

3.3地址分配

/**
 * 地址分配测试
 */
void basicAddressTest() {
	printf("------basicAddressTest开始测试-------\n");
	DLNode tempNode1, tempNode2;

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

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

	printf("第一个节点,data,next: %d, %d, %d\r\n",
	       &tempNode1, &tempNode1.data, &tempNode1.next);
	printf("第二个节点,data,next: %d, %d, %d\r\n",
	       &tempNode2, &tempNode2.data, &tempNode2.next);

	tempNode1.next = &tempNode2;
	printf("--------------**********-------------\n\n");

}

3.4运行结果

----insertDeleteTest开始测试-----

插入元素H成功!
插入元素e成功!
插入元素l成功!
插入元素l成功!
插入元素o成功!
插入元素!成功!
Hello!
你已经删除了节点e节点a不存在
你已经删除了节点oHll!
插入元素o成功!
Holl!
--------------**********-------------

----insertLocateTest开始测试-----

插入元素H成功!
插入元素e成功!
插入元素l成功!
插入元素l成功!
插入元素o成功!
插入元素!成功!
Hello!
元素e的位置是2
找不到元素a!!!
元素o的位置是5
Hello!
插入元素o成功!
Hoello!
--------------**********-------------

------basicAddressTest开始测试-------
第一个节点,data,next: -834668016, -834668016, -834668000
第二个节点,data,next: -834668048, -834668048, -834668032
--------------**********-------------


--------------------------------
Process exited after 0.06356 seconds with return value 0

Press ANY key to continue...

4全部代码

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

/**
 * 双链表结构体,存储字符串数据
 */
typedef struct DoubleLinkeNdode {
	char data;
	struct DoubleLinkeNdode *previous;
	struct DoubleLinkeNdode *next;
} DLNode, *DLNodePtr;

/**
 * @brief 初始化链表,创建一个头节点
 *
 * @return 返回头节点
 */
DLNodePtr initLinkList() {
	//申请空间
	DLNodePtr tempHeader = (DLNodePtr)malloc(sizeof(struct DoubleLinkeNdode) * 1);
	//初始化
	tempHeader->data = '\0';
	tempHeader->previous = NULL;
	tempHeader->next = NULL;
	return tempHeader;
}

/**
 * @brief 打印链表
 *
 * @param paraHeader 传入头节点
 */
void printList(DLNodePtr paraHeader) {
	DLNodePtr p = paraHeader->next;
	//节点不为空,则打印数据
	while (p != NULL) {
		printf("%c", p->data);
		p = p->next;
	}
	printf("\n");
}

/**
 * @brief 插入一个节点操作
 *
 * @param paraChar
 * @param paraHeader
 * @param paraPosition
 */
void insertElement(DLNodePtr paraHeader, char paraChar, int paraPosition) {
	//创建三个结构体变量:p查找查找插入链表位置,q申请新空间,r插入节点的后一节点,辅助插入
	DLNodePtr p, q, r;
	//1查找插入链表位置
	p = paraHeader;
	for (int i = 0; i < paraPosition; i++) {
		p = p->next;
		if (p == NULL) {
			printf("位置%d已经超出链表范围\n", paraPosition);
			return;
		}
	}

	//2申请空间
	q = (DLNodePtr)malloc(sizeof(struct DoubleLinkeNdode) * 1);
	//赋值
	q->data = paraChar;

	//3把新节点插入双链表
	r = p->next;
	q->next = p->next;
	q->previous = p;
	p->next = q;
	//如果不是尾节点,才需要把插入节点的后一节点指向插入节点(非常巧妙)
	if (r != NULL) {
		r->previous = q;
	}
	printf("插入元素%c成功!\n", paraChar);
}

/**
 * @brief 删除一个节点操作
 *
 * @param paraChar
 * @param paraHeader
 */
void deleteElement(DLNodePtr paraHeader, char paraChar) {
	//创建三个结构体变量:p为查找传入数据的前节点
	DLNodePtr p, q, r;
	//1查找传入数据的前节点
	p = paraHeader;
	while ((p->next != NULL) && (p->next->data != paraChar)) {
		p = p->next;
	}
	//2找不到数据,提示并结束
	if (p->next == NULL) {
		printf("节点%c不存在\n", paraChar);
		return;
	}
	//3找到数据,删除并提示
	q = p->next;
	r = q->next;
	p->next = r;
	if (r != NULL) {
		r->previous = p;
	}
	printf("你已经删除了节点%c", q->data);
	free(q);

}

/**
 * @brief 定位节点
 * 
 * @param paraChar 
 * @param paraHeader 
 * 
 * @return 
 */
int locateElement(DLNodePtr paraHeader, char paraChar) {
	DLNodePtr p = paraHeader;
	for (int i = 0; p != NULL; i++) {
		if (p->data == paraChar) return i;
		p = p->next;
	}
	return -1;
}

/**
 * 单元测试
 */
void insertDeleteTest() {
	printf("----insertDeleteTest开始测试-----\n");
	DLNodePtr tempList = initLinkList();
	printList(tempList);

	insertElement(tempList, 'H', 0);
	insertElement(tempList, 'e', 1);
	insertElement(tempList, 'l', 2);
	insertElement(tempList, 'l', 3);
	insertElement(tempList, 'o', 4);
	insertElement(tempList, '!', 5);
	printList(tempList);

	deleteElement(tempList, 'e');
	deleteElement(tempList, 'a');
	deleteElement(tempList, 'o');
	printList(tempList);

	insertElement(tempList, 'o', 1);
	printList(tempList);
	printf("--------------**********-------------\n\n");

}

/**
 * 单元测试2
 */
void insertLocateTest() {
	printf("----insertLocateTest开始测试-----\n");
	int location, data;
	DLNodePtr tempList = initLinkList();
	printList(tempList);

	insertElement(tempList, 'H', 0);
	insertElement(tempList, 'e', 1);
	insertElement(tempList, 'l', 2);
	insertElement(tempList, 'l', 3);
	insertElement(tempList, 'o', 4);
	insertElement(tempList, '!', 5);
	printList(tempList);

	data = 'e';
	location = locateElement(tempList, data);
	if (location == -1) {
		printf("找不到元素%c!!!\n", data);
	} else {
		printf("元素%c的位置是%d\n", data, location);
	}
	data = 'a';
	location = locateElement(tempList, data);
	if (location == -1) {
		printf("找不到元素%c!!!\n", data);
	} else {
		printf("元素%c的位置是%d\n", data, location);
	}
	data = 'o';
	location = locateElement(tempList, data);
	if (location == -1) {
		printf("找不到元素%c!!!\n", data);
	} else {
		printf("元素%c的位置是%d\n", data, location);
	}
	printList(tempList);

	insertElement(tempList, 'o', 1);
	printList(tempList);
	printf("--------------**********-------------\n\n");

}

/**
 * 地址分配测试
 */
void basicAddressTest() {
	printf("------basicAddressTest开始测试-------\n");
	DLNode tempNode1, tempNode2;

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

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

	printf("第一个节点,data,next: %d, %d, %d\r\n",
	       &tempNode1, &tempNode1.data, &tempNode1.next);
	printf("第二个节点,data,next: %d, %d, %d\r\n",
	       &tempNode2, &tempNode2.data, &tempNode2.next);

	tempNode1.next = &tempNode2;
	printf("--------------**********-------------\n\n");

}



int main() {
	insertDeleteTest();
	insertLocateTest();
	basicAddressTest();
	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值