目录
一、双向链表初始化
二、尾插
问题1:什么时候传一级指针,什么时候传二级指针呢?
三、打印
四、尾删
五、头插
六、头删
七、pos之前插入
八、删除pos位置的节点
九、销毁链表
总代码:
List.h
List.c
test.c
图解:phead链表的最后一个节点要指向第一个节点,第一个节点要指向最后一个节点,而第一个节点和最后一个节点都是第一个节点,所以phead->next = phead->prev = phead;
代码:
LTNode* InitList() { //带头节点,循环链表 //哨兵位头结点 LTNode* phead = (LTNode*)malloc(sizeof(LTNode)); phead->next = phead->prev = phead; return phead; }
图解:
当有多个节点:
当只有头结点:
代码:
//创建新节点 LTNode* BuyNode(int x) { LTNode* newnode = (LTNode*)malloc(sizeof(LTNode)); if (newnode == NULL) { printf("newnode failed\n"); exit(-1); } newnode->data = x; newnode->next = newnode->prev = NULL; return newnode; }
//尾插 void ListPushBack(LTNode* phead,int x) { assert(phead); LTNode* newnode = BuyNode(x); LTNode* tail = phead->prev; tail->next = newnode; newnode->prev = tail; newnode->next = phead; phead->prev = newnode; }
1.比如:(传一级指针)
此时传的是一级指针,因为该链表是带有头结点的链表,不会改变头指针所指向的方向,传过去的plist和phead所指向的地址是一样的,就相当于phead = plist;
2.比如:(传二级指针)
此时传的是二级指针,因为需要改变头指针的指向,传过去的&s1和接收的SL** phead的地址是一样的,就相当于SL** phead = &s1;
总结:大部分带头节点就不会改变头指针的方向,就不需要传二级指针
图解:
代码:
//打印 void ListPrint(LTNode* phead) { assert(phead);//如果phead没有初始化,则会为NULL,而带头结点的指针不会为NULL,所以在此断言 LTNode* cur = phead->next; while (cur != phead) { printf("%d",cur->data); cur = cur->next; } printf("\n"); }
图解:
代码:
//尾删 void ListPopBack(LTNode* phead) { assert(phead); LTNode* tail = phead->prev; LTNode* tailprev = tail->prev; 暴力判定:头指针的下一个结点还是头结点,那么说明只有头结点 assert(tail != phead); tailprev->next = phead; phead->prev = tailprev; //温柔的判定:链表为空,也就是只剩头结点时,就不会再删除的情况 //if (tail != phead) //{ // free(tail); //} //tail = NULL; }
图解:
代码:
//头插 void ListPushFront(LTNode* phead, int x) { assert(phead); LTNode* Next = phead->next; LTNode* newnode = BuyNode(x); phead->next = newnode; newnode->prev = phead; newnode->next = Next; Next->prev = newnode; }
图解:
代码:
//头删 void ListPopFront(LTNode* phead) { assert(phead); LTNode* tail = phead->next; LTNode* tailnext = tail->next; //暴力判断:说明只有一个头结点 assert(phead->next != phead); phead->next = tailnext; tailnext->prev = phead; free(tail); tail = NULL; }
代码:
//pos位置之前插入 void ListInsert(LTNode* pos, DataType x) { assert(pos); LTNode* posprev = pos->prev; LTNode* newnode = BuyNode(x); posprev->next = newnode; newnode->prev = posprev; newnode->next = pos; pos->prev = newnode; }
该函数的好处就是可以替代头插与尾插:
图解:
//尾插 void ListPushBack(LTNode* phead, DataType x) { assert(phead); //LTNode* newnode = BuyNode(x); //LTNode* tail = phead->prev; //tail->next = newnode; //newnode->prev = tail; //newnode->next = phead; //phead->prev = newnode; //改写: ListInsert(phead,x); }
//头插 void ListPushFront(LTNode* phead, DataType x) { assert(phead); /*LTNode* Next = phead->next; LTNode* newnode = BuyNode(x); phead->next = newnode; newnode->prev = phead; newnode->next = Next; Next->prev = newnode;*/ //改写: ListInsert(phead->next,x); }
效果图:
代码:
//删除pos位置结点 void ListErase(LTNode* pos) { assert(pos); //因为phead结点不存数据,所以pos不会指向phead,所以不用判断 LTNode* posnext = pos->next; LTNode* posprev = pos->prev; posprev->next = posnext; posnext->prev = posprev; free(pos); pos = NULL; }
该函数的好处就是可以替代头删和尾删:
//头删 void ListPopFront(LTNode* phead) { assert(phead); //LTNode* tail = phead->next; //LTNode* tailnext = tail->next; // 暴力判断:说明只有一个头结点 //assert(phead->next != phead); // //phead->next = tailnext; //tailnext->prev = phead; //free(tail); //tail = NULL; //改写 ListErase(phead->next); }
//尾删 void ListPopBack(LTNode* phead) { assert(phead); //暴力判定:头指针的下一个结点还是头结点,那么说明只有头结点 assert(phead->prev != phead); // //LTNode* tail = phead->prev; //LTNode* tailprev = tail->prev; //tailprev->next = phead; //phead->prev = tailprev; //温柔判定:链表为空,也就是只剩头结点时,就不会再删除的情况 //if (tail != phead) //{ // free(tail); //} //tail = NULL; //改写: ListErase(phead->prev); }
//销毁链表
void ListDestroy(LTNode** phead)
{
LTNode* cur = (*phead)->next;
while (cur != (*phead))
{
LTNode* Next = cur->next;
free(cur);
cur = Next;
}
//头结点也是malloc出来的,也要释放
free(*phead);
*phead = NULL;
//方法一:如果传一级,那么phead的置空并不会影响外面的plist,头结点被释放掉了,但是没有置空,所以在外面置空也可以
//方法二:如果传二级,这时候*phead置空,才会影响到plist,因为这是传址,改变里面的内容
}
#pragma once
#include
#include
#include
#include
typedef int DataType;
typedef struct ListNode
{
DataType data;
struct ListNode* next;
struct ListNode* prev;
}LTNode;
//这次不传二级指针,用返回值
LTNode* InitList();
//尾插
void ListPushBack(LTNode* phead, DataType x);
//尾删
void ListPopBack(LTNode* phead);
//头插
void ListPushFront(LTNode* phead, DataType x);
//头删
void ListPopFront(LTNode* phead);
//查找
LTNode* ListFind(LTNode* phead, DataType x);
//pos位置之前插入
void ListInsert(LTNode* pos, DataType x);
//删除pos位置结点
void ListErase(LTNode* pos);
//销毁链表
void ListDestroy(LTNode** phead);
#define _CRT_SECURE_NO_WARNINGS 1
#include"List.h"
//增加节点
LTNode* BuyNode(DataType x)
{
LTNode* newnode = (LTNode*)malloc(sizeof(LTNode));
if (newnode == NULL)
{
printf("newnode failed\n");
exit(-1);
}
newnode->data = x;
newnode->next = newnode->prev = NULL;
return newnode;
}
//初始化
LTNode* InitList()
{
//带头节点,循环链表
//哨兵位头结点
LTNode* phead = (LTNode*)malloc(sizeof(LTNode));
phead->next = phead->prev = phead;
return phead;
}
//尾插
void ListPushBack(LTNode* phead, DataType x)
{
assert(phead);
//LTNode* newnode = BuyNode(x);
//LTNode* tail = phead->prev;
//tail->next = newnode;
//newnode->prev = tail;
//newnode->next = phead;
//phead->prev = newnode;
//改写:
ListInsert(phead,x);
}
//打印
void ListPrint(LTNode* phead)
{
assert(phead);//如果phead没有初始化,则会为NULL,而带头结点的指针不会为NULL,所以在此断言
LTNode* cur = phead->next;
while (cur != phead)
{
printf("%d ",cur->data);
cur = cur->next;
}
printf("\n");
}
//尾删
void ListPopBack(LTNode* phead)
{
assert(phead);
//暴力判定:头指针的下一个结点还是头结点,那么说明只有头结点
assert(phead->prev != phead);
//
//LTNode* tail = phead->prev;
//LTNode* tailprev = tail->prev;
//tailprev->next = phead;
//phead->prev = tailprev;
温柔判定:链表为空,也就是只剩头结点时,就不会再删除的情况
if (tail != phead)
{
free(tail);
}
tail = NULL;
//改写:
ListErase(phead->prev);
}
//头插
void ListPushFront(LTNode* phead, DataType x)
{
assert(phead);
/*LTNode* Next = phead->next;
LTNode* newnode = BuyNode(x);
phead->next = newnode;
newnode->prev = phead;
newnode->next = Next;
Next->prev = newnode;*/
//改写:
ListInsert(phead->next,x);
}
//头删
void ListPopFront(LTNode* phead)
{
assert(phead);
//LTNode* tail = phead->next;
//LTNode* tailnext = tail->next;
//
暴力判断:说明只有一个头结点
//assert(phead->next != phead);
//
//phead->next = tailnext;
//tailnext->prev = phead;
//free(tail);
//tail = NULL;
//改写
ListErase(phead->next);
}
//查找
LTNode* ListFind(LTNode* phead, DataType x)
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
//pos位置之前插入
void ListInsert(LTNode* pos, DataType x)
{
assert(pos);
LTNode* posprev = pos->prev;
LTNode* newnode = BuyNode(x);
posprev->next = newnode;
newnode->prev = posprev;
newnode->next = pos;
pos->prev = newnode;
}
//删除pos位置结点
void ListErase(LTNode* pos)
{
assert(pos);
//因为phead结点不存数据,所以pos不会指向phead,所以不用判断
LTNode* posnext = pos->next;
LTNode* posprev = pos->prev;
posprev->next = posnext;
posnext->prev = posprev;
free(pos);
pos = NULL;
}
//销毁链表
void ListDestroy(LTNode** phead)
{
LTNode* cur = (*phead)->next;
while (cur != (*phead))
{
LTNode* Next = cur->next;
free(cur);
cur = Next;
}
//头结点也是malloc出来的,也要释放
free(*phead);
*phead = NULL;
//方法一:如果传一级,那么phead的置空并不会影响外面的plist,头结点被释放掉了,但是没有置空,所以在外面置空也可以
//方法二:如果传二级,这时候*phead置空,才会影响到plist,因为这是传址,改变里面的内容
}
#define _CRT_SECURE_NO_WARNINGS 1
#include"List.h"
testList1()
{
LTNode* plist = InitList();
ListPushBack(plist,1);//因为有头结点,所以不用改变头指针,总结:不改变头指针传一级,改变传二级
ListPushBack(plist,2);
ListPushBack(plist,3);
ListPushBack(plist,4);
ListPushBack(plist,5);
ListPrint(plist);
ListPopBack(plist);
//ListPrint(plist);
ListPopBack(plist);
ListPrint(plist);
//ListPopBack(plist);
//ListPushFront(plist,4);
//ListPrint(plist);
ListPopFront(plist);
//ListPrint(plist);
ListPopFront(plist);
ListPrint(plist);
//ListPopFront(plist);
//ListPopFront(plist);
//ListPopFront(plist);
//LTNode* pos = ListFind(plist,3);
//if (pos != NULL)
//{
// /*printf("%d\n",pos->data);*/
// ListInsert(pos, 20);
// ListErase(pos);
//}
//ListPrint(plist);
ListDestroy(&plist);
//传一级指针的话,可以将头指针在此置空即可,因为头结点已经释放,但是没有置空,
//以下为传一级指针的操作
/*plist = NULL;*/
}
int main()
{
testList1();
return 0;
}
本文为带头节点双循坏链表,如有问题,请评论区多多评论^_^