c语言双向循环链表

双向循环链表

1.双向链表的创建

这里我使用的是创建一个头结点(哨兵节点),所以在创建链表的时候先创建一个头结点,这个头结点next指针prev指针都先指向NULL,除此之外,这个节点并不存储其他任何值。
如果需要在后面添加新的值,需要创建一个新的节点,与头结点不同的是后续创建的节点都需要存储准确的值。

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


//创建一个新的节点
ListNode* BuyListNode(LTDataType x)
{
    ListNode* node = (ListNode*)malloc(sizeof(ListNode));
    if (node == NULL)
    {
        perror("malloc fail");
        exit(-1);
    }
    node->data = x;
    node->next = NULL;
    node->prev = NULL;
    return node;
}


// 创建返回链表的头结点.
ListNode* ListCreate()
{
    ListNode* pHead = BuyListNode(-1);
    pHead->next = pHead;
    pHead->prev = pHead;

    return pHead;
}

2.双向链表的销毁

任何链表再使用完成后都需要进行销毁,因为双向链表是在堆上创建的,如果不及时进行销毁,会在成内存泄漏,可能会造成严重的损失。虽然现在的编译器能够尽量减少这种损失,但在后续使用完之后最好是及时销毁,减少不必要的损失。
注:这里创建的链表有头结点,所以最后只需要判断cur是否指向头结点即可

// 双向链表销毁
void ListDestory(ListNode* pHead)
{
    assert(pHead);

    ListNode* cur = pHead->next;
    //因为有头结点,所以这里直接判断cur是否会访问到头结点即可
    while (cur != pHead)
    {
        ListNode* next = cur->next;
        free(cur);

        cur = next;
    }

    free(pHead);
}

3.双向链表的头插头删

(1)头插

这里采用三指针的方法,这样可以防止没有除头结点以外的节点所发生的错误,如果在有节点的情况下next就指向该节点。

  1. 在创建newnode的同时,创建一个next节点,并将phead->next指向next
  2. 将next->prev指向newnode,phead->next指向newnode,newnode->next指向next即可
// 双向链表头插
void ListPushFront(ListNode* pHead, LTDataType x)
{
    assert(pHead);

    ListNode* newnode = BuyListNode(x);
    ListNode* next = pHead->next;

    //这里直接插入即可,没有单向链表那么麻烦
    pHead->next = newnode;
    newnode->next = next;
    next->prev = newnode;

}

(2)头删

头删的方法跟头插的方法类似,因为这里使用了哨兵位头结点,所以最后只需要判断要删除的节点不是哨兵位节点即可

// 双向链表头删
void ListPopFront(ListNode* pHead)
{
    assert(pHead);
    assert(pHead->prev != pHead); //防止陷入循环

    ListNode* first = pHead->next;
    ListNode* second = first->next;

    free(first);

    pHead->next = second;
    second->prev = pHead;
}

4.双向链表的查找

这里判断的方法就是判断查找的节点最后是否为哨兵位头节点,如果是则已经找了一遍整个链表,如果中途找到了就返回该节点,没找到就打印没有找到并返回NULL。

// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x)
{
    assert(pHead);
    ListNode* cur = pHead->next;
    //因为带头结点,所以直接判断是否是哨兵位
    while (cur != pHead)
    {
        if (cur->data == x)
        {
            return cur;
        }
        cur = cur->next;
    }
    printf("没有找到\n");
    return NULL;
}

5.双向链表在指定位置的插入

这里直接创建一个新的节点,并先保存要插入位置的前一个位置的节点,因为这里是双向链表,这里直接连接前后节点即可。

// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x)
{
    assert(pos);
    ListNode* prev = pos->prev;
    ListNode* newnode = BuyListNode(x);

    //因为是双向链表,直接连接前后节点即可
    prev->next = newnode;
    pos->prev = newnode;

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

6.双向链表在指定位置删除

  1. 先创建两个节点分别保存该节点的前后节点
  2. 再将这个节点的前后指针连接即可
  3. 最后释放该位置节点
// 双向链表删除pos位置的节点
void ListErase(ListNode* pos)
{
    assert(pos);
    ListNode* prev = pos->prev;
    ListNode* next = pos->next;
    
    free(pos);

    prev->next = next;
    next->prev = prev;
}

代码总结

ListNode.h

#pragma once

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

// 带头+双向+循环链表增删查改实现
typedef int LTDataType;
typedef struct ListNode
{
	LTDataType data;
	struct ListNode* next;
	struct ListNode* prev;
}ListNode;
 
// 创建返回链表的头结点.
ListNode* ListCreate();
// 双向链表销毁
void ListDestory(ListNode* pHead);
// 双向链表打印
void ListPrint(ListNode* pHead);
// 双向链表尾插
void ListPushBack(ListNode* pHead, LTDataType x);
// 双向链表尾删
void ListPopBack(ListNode* pHead);
// 双向链表头插
void ListPushFront(ListNode* pHead, LTDataType x);
// 双向链表头删
void ListPopFront(ListNode* pHead);
// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x);
// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x);
// 双向链表删除pos位置的节点
void ListEra

ListNode.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "ListNode.h"

//创建一个新的节点
ListNode* BuyListNode(LTDataType x)
{
	ListNode* node = (ListNode*)malloc(sizeof(ListNode));
	if (node == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	node->data = x;
	node->next = NULL;
	node->prev = NULL;
	return node;
}


// 创建返回链表的头结点.
ListNode* ListCreate()
{
	ListNode* pHead = BuyListNode(-1);
	pHead->next = pHead;
	pHead->prev = pHead;

	return pHead;
}

// 双向链表销毁
void ListDestory(ListNode* pHead)
{
	assert(pHead);

	ListNode* cur = pHead->next;
	//因为有头结点,所以这里直接判断cur是否会访问到头结点即可
	while (cur != pHead)
	{
		ListNode* next = cur->next;
		free(cur);

		cur = next;
	}

	free(pHead);
}

// 双向链表打印
void ListPrint(ListNode* pHead)
{
	assert(pHead);

	ListNode* cur = pHead->next;
	while (cur != pHead)
	{
		printf("%d ", cur->data);
		cur = cur->next;
	}
	printf("\n");
}

// 双向链表尾插
void ListPushBack(ListNode* pHead, LTDataType x)
{
	assert(pHead);

	//这里直接创建节点插入即可
	ListNode* newnode = BuyListNode(x);
	ListNode* tail = pHead->prev;

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

	newnode->next = pHead;
	pHead->prev = newnode;
}

// 双向链表尾删
void ListPopBack(ListNode* pHead)
{
	assert(pHead);
	assert(pHead->prev != pHead);

	ListNode* tail = pHead->prev;
	ListNode* tailprev = tail->prev;

	pHead->prev = tailprev;
	tailprev->next = pHead;
	free(tail);
}

// 双向链表头插
void ListPushFront(ListNode* pHead, LTDataType x)
{
	assert(pHead);

	ListNode* newnode = BuyListNode(x);
	ListNode* next = pHead->next;

	//这里直接插入即可,没有单向链表那么麻烦
	pHead->next = newnode;
	newnode->next = next;
	next->prev = newnode;

}

// 双向链表头删
void ListPopFront(ListNode* pHead)
{
	assert(pHead);
	assert(pHead->prev != pHead); //防止陷入循环

	ListNode* first = pHead->next;
	ListNode* second = first->next;

	free(first);

	pHead->next = second;
	second->prev = pHead;
}

// 双向链表查找
ListNode* ListFind(ListNode* pHead, LTDataType x)
{
	assert(pHead);
	ListNode* cur = pHead->next;
	//因为带头结点,所以直接判断是否是哨兵位
	while (cur != pHead)
	{
		if (cur->data == x)
		{
			return cur;
		}
		cur = cur->next;
	}
	printf("没有找到\n");
	return NULL;
}

// 双向链表在pos的前面进行插入
void ListInsert(ListNode* pos, LTDataType x)
{
	assert(pos);
	ListNode* prev = pos->prev;
	ListNode* newnode = BuyListNode(x);

	//因为是双向链表,直接连接前后节点即可
	prev->next = newnode;
	pos->prev = newnode;

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

// 双向链表删除pos位置的节点
void ListErase(ListNode* pos)
{
	assert(pos);
	ListNode* prev = pos->prev;
	ListNode* next = pos->next;
	
	free(pos);

	prev->next = next;
	next->prev = prev;
}

test.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "ListNode.h"

int main()
{
	ListNode* pHead = ListCreate();
	ListPushBack(pHead, 1);
	ListPushBack(pHead, 2);
	ListPushBack(pHead, 3);
	ListPushBack(pHead, 4);
	ListPushBack(pHead, 5);
	ListPopBack(pHead);
	ListPushFront(pHead, 6);
	ListNode* pos = ListFind(pHead, 2);
	if (pos)
	{
		pos->data *= 5;
	}
	ListInsert(pos, 5);
	ListErase(pos);
	ListPrint(pHead);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值