带头双向循环链表的实现

带头双向循环链表的实现

1.定义结构体

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

2.申请链表的一个节点

LTNode* BuyLTNode(LTDataType x) {
    LTNode* newhead = (LTNode*)malloc(sizeof(LTNode));
    if (newhead == NULL)
    {
        perror("malloc failed");
        exit(1);
    }
    newhead->data = x;
    
    return newhead;
}

LTNode* LTInit(LTNode* phead) {
    phead = BuyLTNode(-1);
    phead->next = phead;
    phead->prev = phead;
    return phead;
}

注意,在链表初始化的时候,要获得链表头节点,此时需要将头节点的next和prev都设置为头节点自己

3.尾插

尾插
示意图如上,需要修改四个指针。可以记录尾节点以方便写代码。首先将尾部节点和新节点进行链接,然后将新的尾节点和头节点进行链接。

void LTPushBack(LTNode* phead, LTDataType data) {
    assert(phead);
    LTNode* tail = phead->prev;
    
    LTNode* newnode = BuyLTNode(data);

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

    //LTInsert(phead, data);
}

4. 尾删

尾删
尾删如上图,可以记录删除节点的前一个节点,链接前一个节点和头节点的连接,然后free掉删除节点。这里需注意链表为空的时候无法尾删

void LTPopBack(LTNode* phead) {
    assert(phead);
    assert(phead->next != phead);

    LTNode* del = phead->prev;

    LTNode* tail = del->prev;

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

    free(del);

    //LTErase(phead->prev);
}

5.头插

头插
头插如上图,可以先对新节点和head->next进行链接,然后head和新节点进行链接。

void LTPushFront(LTNode* phead, LTDataType data) {
    assert(phead);
    LTNode*  newnode = BuyLTNode(data);
    newnode->next = phead->next;
    phead->next->prev = newnode;

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

    //LTInsert(phead->next, data);
}

6.头删

头删
头删如上图,记录head的next,然后链接head和del->next,最后free掉del。注意头删的时候链表不能为空。

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

    LTNode* del = phead->next;
    phead->next = del->next;
    del->next->prev = phead;

    free(del);

    //LTErase(phead->next);
}

7.查找

注意查找的停止条件是 cur != head

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;
}

8.pos位置的前面进行插入

pos为位置前插入
示意图如上,可以先记录pos的前一个节点,然后将新节点和pos链接,最后前一个节点和新节点链接,注意顺序。

void LTInsert(LTNode* pos, LTDataType x) {
    assert(pos);
    LTNode* newnode = BuyLTNode(x);
    
    LTNode* posPrev = pos->prev;

    newnode->prev = posPrev;
    posPrev->next = newnode;

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

9.删除pos位置节点

删除pos位置节点
可以记录pos的前一个节点,然后链接pos前一个节点和pos后一个节点

void LTErase(LTNode* pos) {
    assert(pos);

    LTNode* posPrev = pos->prev;
    posPrev->next = pos->next;
    pos->next->prev = posPrev;

    free(pos);
}

10.销毁

void ListDestory(ListNode* pHead) {
    assert(pHead);
    ListNode* cur = pHead->_next;
    ListNode* next = cur->_next;
    while (cur != pHead)
    {
        free(cur);
        cur = next;
        next = cur->_next;
    }
    free(pHead);
}

11.打印

// 双向链表打印
void ListPrint(ListNode* pHead) {
	assert(pHead);
	ListNode* cur = pHead->_next;
	while (cur != pHead)
	{
		printf("%d ", cur->_data);
		cur = cur->_next;
		
	}
	printf("\n");
}

打印要注意头节点不算,然后注意停止条件

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值