双向链表的代码实现

一.双向链表的分类

1不带头双向链表

 2.带头双向链表

 二.带头双向循环链表VS无头单向非循环链表

1.带头双向链表
1.1优点
  1. 简化边界条件处理:由于存在哑节点(哨兵节点),它本身不存储有效数据,但在链表中扮演了重要角色,特别是在插入和删除操作时,可以极大地简化对头部和尾部边界条件的处理。这使得代码更加简洁和统一。
  2. 提高代码可读性:通过哑节点,可以使得插入和删除操作更加直观,提高了代码的可读性和可维护性。
  3. 减少空链表判断:在带头双向链表中,由于哑节点的存在,当链表为空时,哑节点的prevnext都指向自己,这样可以减少空链表的判断逻辑。
1.2缺点
  1. 轻微的空间开销:相比不带头双向链表,带头双向链表需要额外维护一个哑节点,这在一定程度上增加了空间开销。然而,这种开销通常是可以接受的,因为哑节点所占用的空间相对较小。
  2. 实现复杂度:虽然哑节点简化了插入和删除操作,但在实现时需要注意哑节点的正确处理,包括在初始化、插入和删除操作中对哑节点的维护。这可能会增加一定的实现复杂度。
 2.无头单向非循环链表
2.1优点
  1. 节省空间:相比带头双向链表,不带头双向链表不需要维护哑节点,因此可以节省一定的空间开销。这对于空间敏感的应用场景来说是一个优势。
  2. 实现简单:不带头双向链表的实现相对简单,不需要处理哑节点的相关逻辑。这使得初学者更容易理解和掌握双向链表的基本概念和操作。
2.2缺点
  1. 边界条件处理复杂:在不带头双向链表中,对头部和尾部的插入和删除操作需要特殊处理,因为头部和尾部没有哑节点来简化边界条件的处理。这可能会增加代码的复杂度和出错的可能性。
  2. 空链表判断繁琐:在不带头双向链表中,当链表为空时,需要特别注意头部指针和尾部指针的指向问题。这可能会使得空链表的判断逻辑变得繁琐和复杂。

三.双向链表的代码实现

 在双向链表的代码块中都有相关的注释。
首先先看一下双向链表的插入及删除过程

 3.1有关双向链表的头文件

 

#pragma once

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

#include<assert.h>

typedef int LTDataType;

typedef struct ListNode
{
	LTDataType* prev;
	LTDataType* next;
	LTDataType* data;
}LTNode;

//链表初始化
void LTInit(LTNode** phead);

//链表的销毁
void LTDestroy(LTNode* phead);

//链表在某个位置前插入
void LTInsert(LTNode* pos, LTDataType x);

//void LTEarse(LTNode* pos);
//链表的删除
void LTEarse(LTNode* phead,LTDataType x);

//链表的查找
LTNode* LTFind(LTNode* phead, LTDataType x);

//链表的打印
void LTPrintf(LTNode* phead);

//链表的尾插
void LTPushBack(LTNode* pos, LTDataType x);

//链表的尾删
void LTPopback(LTNode* phead);

//链表的头插
void LTPushFront(LTNode* phead, LTDataType x);

//链表的头删
void LTPopFront(LTNode* phead);
 3.2有关双向链表代码的.c文件

 

#define _CRT_SECURE_NO_WARNINGS 1

#include"List.h"

//通过插入的x创建一个newnodde
LTNode* BuyListNode(LTDataType x)
{
	LTNode* phead = (LTNode*)malloc(sizeof(LTNode));//为x扩容空间
	if (phead == NULL)//判断是否扩容成功
	{
		perror("malloc");
		return NULL;
	}
	phead->prev = NULL;
	phead->next = NULL;
	phead->data = x;

	return phead;
}

void LTInit(LTNode** pphead)
{
	*pphead = BuyListNode(-1);//头指针,哨兵位结点
	
	(*pphead)->prev = *pphead;//哨兵位结点的prev指向自己
	(*pphead)->next = *pphead;//next也指向自己
}

void LTDestroy(LTNode* phead)
{
	assert(phead);

	LTNode* cur = phead->next;
	while (cur != phead)
	{
		LTNode* tmp = cur->next;//保存要销毁的下一个结点,若不保存当该结点free释放之后,cur->next将属于野指针
		free(cur);
		cur = tmp;
	}
}

void LTInsert(LTNode* pos,LTDataType x)
{
	assert(pos);

	LTNode* newnode = BuyListNode(x);//为插入的x开辟空间
	LTNode* pre = pos->prev;//保存要删除的上一个结点

	pos->prev = newnode;
	newnode->next = pos;

	pre->next = newnode;
	newnode->prev = pre;

}


void LTEarse(LTNode* phead, LTDataType x)
{
	assert(phead);

	LTNode* cur = phead->next;
	while (cur != phead)//哨兵位不能被删除,所以从哨兵位的下一个结点开始寻找
	{
		if (cur->data == x)//找到该结点
		{
			LTNode* pre = cur->prev;
			LTNode* tail = cur->next;
			
			pre->next = tail;
			tail->prev = pre;
			
			free(cur);
			return;
		}

		cur = cur->next;
	}

}

LTNode* LTFind(LTNode* phead, LTDataType x)
{
	assert(phead);

	LTNode* cur = phead->next;
	while (cur != phead)
	{
		if (cur->data == x)
		{
			return cur;
		}

		cur = cur->next;
	}

	return NULL;
}

void LTPrintf(LTNode* phead)
{
	LTNode* cur = phead->next;
	while (cur != phead)
	{
		printf("%d->", cur->data);
		cur = cur->next;
	}
	printf("\n");
}


void LTPushBack(LTNode* phead, LTDataType x)
{
	assert(phead);

	LTNode* newnode = BuyListNode(x);
	LTNode* tail = phead->prev;

	newnode->prev = tail;
	newnode->next = phead;

	phead->prev = newnode;
	tail->next = newnode;
}

void LTPopback(LTNode* phead)
{
	assert(phead);
	if (phead->next == phead)
		return;

	LTNode* tail = phead->prev;
	LTNode* ptail = tail->prev;

	ptail->next = phead;
	phead->next = ptail;

	free(tail);

}

void LTPushFront(LTNode* phead, LTDataType x)
{
	assert(phead);

	LTNode* newnode = BuyListNode(x);
	LTNode* first = phead->next;
	phead->next = newnode;
	newnode->prev = phead;

	newnode->next = first;
	first->prev = newnode;

    //另一种不需要保存phead->next的解法
	/*newnode->next = phead->next;
	phead->next->prev = newnode;

	phead->next = newnode;
	newnode->prev = phead;*/
}

void LTPopFront(LTNode* phead)
{
	assert(phead);
	if (phead->next == phead)
		return;

	LTNode* tail = phead->next;
	LTNode* Ntail = tail->next;

	phead->next = Ntail;
	Ntail->prev = phead;
	
	free(tail);
}

结尾

如果有什么建议和疑问,或是有什么错误,希望大家可以在评论区提一下。
希望大家以后也能和我一起进步!!
如果这篇文章对你有用的话,希望能给我一个小小的赞!

 

  • 15
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 7
    评论
以下是一个简单的双向链表的示例代码实现了基本的插入、删除和遍历功能。请注意,这只是一个基本示例,你可以根据自己的需求进行修改和扩展。 ```c #include <stdio.h> #include <stdlib.h> // 双向链表节点 typedef struct Node { int data; struct Node* prev; struct Node* next; } Node; // 创建一个新节点 Node* createNode(int data) { Node* newNode = (Node*)malloc(sizeof(Node)); if (newNode == NULL) { printf("内存分配失败\n"); exit(1); } newNode->data = data; newNode->prev = NULL; newNode->next = NULL; return newNode; } // 在链表的头部插入节点 void insertAtHead(Node** head, int data) { Node* newNode = createNode(data); if (*head == NULL) { *head = newNode; } else { (*head)->prev = newNode; newNode->next = *head; *head = newNode; } } // 在链表的尾部插入节点 void insertAtTail(Node** head, int data) { Node* newNode = createNode(data); if (*head == NULL) { *head = newNode; } else { Node* current = *head; while (current->next != NULL) { current = current->next; } current->next = newNode; newNode->prev = current; } } // 删除链表中指定值的节点 void deleteNode(Node** head, int data) { if (*head == NULL) { printf("链表为空\n"); return; } Node* current = *head; while (current != NULL) { if (current->data == data) { if (current == *head) { *head = current->next; if (*head != NULL) { (*head)->prev = NULL; } } else { current->prev->next = current->next; if (current->next != NULL) { current->next->prev = current->prev; } } free(current); return; } current = current->next; } printf("节点 %d 未找到\n", data); } // 遍历链表并打印节点值 void printList(Node* head) { if (head == NULL) { printf("链表为空\n"); } else { Node* current = head; printf("链表节点值: "); while (current != NULL) { printf("%d ", current->data); current = current->next; } printf("\n"); } } // 释放链表内存 void freeList(Node** head) { Node* current = *head; while (current != NULL) { Node* temp = current; current = current->next; free(temp); } *head = NULL; } int main() { Node* head = NULL; insertAtHead(&head, 3); insertAtHead(&head, 2); insertAtHead(&head, 1); printList(head); insertAtTail(&head, 4); insertAtTail(&head, 5); printList(head); deleteNode(&head, 3); deleteNode(&head, 6); printList(head); freeList(&head); return 0; } ```
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

鲨鱼吃橘子

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

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

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

打赏作者

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

抵扣说明:

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

余额充值