带头双向循环链表增删查改实现
实现功能:
//初始化
void ListInit(List* plist);//申请结点
ListNode* BuyListNode();//销毁
void ListDestory(List* plist);//尾插
void ListPushBack(List* plist, LTDataType x);//尾删
void ListPopBack(List* plist);//头插
void ListPushFront(List* plist, LTDataType x);//头删
void ListPopFront(List* plist);//查找
ListNode* ListFind(List* plist, LTDataType x);// 在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x);// 删除pos位置的节点
void ListErase(List*plist,ListNode* pos);//打印
void ListPrint(List* plist);
//List.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
//带头双向循环链表增删查改实现
typedef int LTDataType;
typedef struct ListNode
{
LTDataType _data;
struct ListNode* _next;
struct ListNode* _prev;
}ListNode;
typedef struct List
{
ListNode* _head;
}List;
//初始化
void ListInit(List* plist);
//申请结点
ListNode* BuyListNode();
//销毁
void ListDestory(List* plist);
//尾插
void ListPushBack(List* plist, LTDataType x);
//尾删
void ListPopBack(List* plist);
//头插
void ListPushFront(List* plist, LTDataType x);
//头删
void ListPopFront(List* plist);
//查找
ListNode* ListFind(List* plist, LTDataType x);
// 在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x);
// 删除pos位置的节点
void ListErase(List*plist,ListNode* pos);
//打印
void ListPrint(List* plist);
//List.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "List.h"
//初始化
void ListInit(List* plist)
{
plist->_head = BuyListNode(-1);
plist->_head->_next = plist->_head;
plist->_head->_prev = plist->_head;
}
//申请新的结点
ListNode* BuyListNode(LTDataType x)
{
ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
//判断分配是否成功
assert(newNode);
newNode->_data = x;
newNode->_next = NULL;
newNode->_prev = NULL;
return newNode;
}
//销毁
void ListDestory(List* plist)
{
assert(plist);
ListNode* tmp;
tmp = plist->_head->_next;
//不能先释放头结点,防止二次释放
while (tmp!=plist->_head)
{
ListNode* next = tmp->_next;
tmp = next;
}
//最后释放
free(plist->_head);
plist->_head = NULL;
}
// 在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x)
{
assert(pos);
ListNode* newnode = BuyListNode(x); // 申请新结点
ListNode* prev = pos->_prev; // pos前一个结点
prev->_next = newnode; // 连接pos前一个结点和新结点
newnode->_prev = prev;
pos->_prev = newnode; // 连接pos和新结点
newnode->_next = pos;
}
//头插
void ListPushFront(List* plist, LTDataType x)
{
assert(plist);
//头插和在第一个结点前面插入一样
ListInsert(plist->_head->_next, x);
//ListNode* next;
//ListNode* newNode = BuyListNode(x);
//next = plist->_head->_next;
//plist->_head->_next = newNode;
//newNode->_prev = plist->_head;
//newNode->_next = next;
//next->_prev = newNode;
}
//尾插
void ListPushBack(List* plist, LTDataType x)
{
assert(plist);
尾插和在头结点前面插入一个结点一样
ListInsert(plist->_head, x);
//ListNode* tail;
//ListNode* newNode =BuyListNode(x) ;
//tail = plist->_head->_prev;
//tail->_next = newNode;
//newNode->_prev = tail;
//newNode->_next = plist->_head;
//plist->_head->_prev = newNode;
}
// 删除当前pos位置的节点
void ListErase(List*plist,ListNode* pos)
{
ListNode*prev, *next;
assert(pos && plist);
//注意空链表
if (plist->_head == pos)
return;
prev = pos->_prev;
next = pos->_next;
prev->_next = next;
next->_prev = prev;
free(pos);
pos = NULL;
}
//头删
void ListPopFront(List* plist)
{
//assert(plist);
//等于删除头结点后面的结点
ListErase(plist,plist->_head->_next);
//ListNode* ret,*next;
不能删掉头节点
//if (plist->_head = plist->_head->_next)
// return;
//next = plist->_head->_next;
//ret = next->_next;
//plist->_head->_next = ret;
//ret->_prev = plist->_head;
//free(next);
//next = NULL;
}
//尾删
void ListPopBack(List* plist)
{
//assert(plist);
//等于删除头结点前面的结点
ListErase(plist,plist->_head->_prev);
//ListNode* ret,*tail;
注意不能删掉头节点
//if (plist->_head == plist->_head->_next)
// return;
//tail = plist->_head->_prev;
//ret = tail->_prev;
//ret->_next = plist->_head;
//plist->_head->_prev = ret;
//free(tail);
//tail = NULL;
}
//查找
ListNode* ListFind(List* plist, LTDataType x)
{
ListNode* NewNode;
assert(plist);
NewNode = plist->_head;
while (NewNode->_next!=plist->_head)
{
NewNode = NewNode->_next;
if(NewNode->_data==x)
{
return NewNode;
}
}
printf("不存在此结点\n");
return NULL;
}
//打印
void ListPrint(List* plist)
{
assert(plist);
//头不存放数据,所以直接指向头的下一个结点
ListNode* ret=plist->_head->_next;
while (ret!=plist->_head)
{
printf("%d ", ret->_data);
ret = ret->_next;
}
printf("\n");
}
注意:实现功能部分进行了必要的代码复用,注释中包含了不用进行复用时的写法
//ListTest.c
#define _CRT_SECURE_NO_WARNINGS 1
#include "List.h"
int main()
{
List phead;
//ListNode* ret;
ListInit(&phead);
ListPushFront(&phead, 3);
ListPushFront(&phead, 2);
ListPushFront(&phead, 1);
ListPushBack(&phead, 4);
ListPushBack(&phead, 5);
//ListPrint(&phead);//1 2 3 4 5
ListPopBack(&phead);
ListPopBack(&phead);
ListPopBack(&phead);
ListPopBack(&phead);
ListPopBack(&phead);
ListPopBack(&phead);
//ListPopFront(&phead);
//ListPopFront(&phead);
//ListPopFront(&phead);
//ListPopFront(&phead);
//ListPopFront(&phead);
//ListPopFront(&phead);
//ret = ListFind(&phead, 3);
//printf("%d\n", ret->_data);
//ListPopBack(&ret);
ListPrint(&phead);//2 4
ListDestory(&phead);
system("pause");
return 0;
}
通过练习带头双向循环链表,我们不难发现这种结构进行操作时十分方便,因为无论给出哪一个节点我们都可以通过prev或者next指针直接拿到我们所需要的数据,操作时间复杂度为O(1),避免了对链表进行遍历.
图片来源于: https://blog.csdn.net/belonghwl/article/details/100835387
对比前面的顺序表,单链表,还有今天看的双链表来看,如果我们有要去完成一个任务,假设以上三种数据结构我们都可以使用并完成任务,那么个人觉得使用顺序:顺序表>双链表>单链表. 因为现代计算机已经在存储大小方面远远优胜于之前的计算机,因此内存占用大小(空间复杂度)已经没有那么重要,更重要的关注的是效率(时间复杂度).