完整代码链接:DataStructure: 基本数据结构的实现。 (gitee.com)
目录
一、结构体设计:
typedef int ListNodeDataType;
typedef struct ListNode
{
struct ListNode* next;//存储后一个结点的地址
struct ListNode* prev;//存储前一个结点的地址
ListNodeDataType data;
}ListNode;
二、创建新结点:
ListNode* CreateListNode(ListNodeDataType x)
{
ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
assert(newNode);//当开辟新结点失败时,打印诊断消息并中止程序
newNode->next = NULL;
newNode->prev = NULL;
newNode->data = x;
return newNode;
}
三、初始化:
对于初始化就是要让头结点的prev指针与next指针都指向自己。
void ListInit(ListNode** pphead)//链表的头指针会受影响,要传二级指针
{
*pphead = CreateListNode(0);
(*pphead)->next = *pphead;
(*pphead)->prev = *pphead;
}
初始化测试:
void TestInit()
{
ListNode* phead = NULL;
ListInit(&phead);
printf("%d\n", phead->data);
printf("%p\n", phead->prev);
printf("%p\n", phead->next);
}
测试结果:
四、清空:
void ListClear(ListNode* phead)
{
//清理所有的数据结点,保留头结点,以便可以继续使用
assert(phead);
ListNode* cur = phead->next;
while (cur != phead)
{
ListNode* next = cur->next;
free(cur);
cur = next;
}
//虚拟头结点的prev与next重新指向自己
phead->next = phead;
phead->prev = phead;
}
清空测试:
TestClear()
{
ListNode* phead = NULL;
ListInit(&phead);
ListPushBack(phead, 1);
ListPushBack(phead, 2);
ListPushBack(phead, 3);
ListPushBack(phead, 4);
ListPrint(phead);
ListClear(phead);
printf("%d\n", phead->data);
printf("%d\n", phead->prev->data);
printf("%d\n", phead->next->data);
}
测试结果:
五、销毁:
void ListDestory(ListNode** pphead)
{
assert(*pphead);
ListClear(*pphead);
free(*pphead);//释放虚拟头结点所占有的空间
*pphead = NULL;
}
六、打印:
void ListPrint(ListNode* phead)
{
assert(phead);
ListNode* cur = phead->next;
while (cur != phead)
{
printf("%d ", cur->data);
cur = cur->next;
}
printf("\n");
}
七、插入:
1.头插:
void ListPushFront(ListNode* phead, ListNodeDataType x)
{
assert(phead);
ListNode* first = phead->next;//虚拟结点后的第一个结点
ListNode* newNode = CreateListNode(x);//创建一个新结点
first->prev = newNode;//1
phead->next = newNode;//2
newNode->next = first;//3
newNode->prev = phead;//4
//1,2,3,4顺序可以交换
}
测试头插:
TestPushFront()
{
ListNode* phead = NULL;
ListInit(&phead);
ListPushFront(phead, 1);
ListPushFront(phead, 2);
ListPushFront(phead, 3);
ListPushFront(phead, 4);
ListPrint(phead);
}
测试结果:
2.尾插:
void ListPushBack(ListNode* phead, ListNodeDataType x)
{
assert(phead);
ListNode* tail = phead->prev;//链表的尾结点是虚拟头结点的前一个结点
ListNode* newNode = CreateListNode(x);
tail->next = newNode;//1
newNode->prev = tail;//2
phead->prev = newNode;//3
newNode->next = phead;//4
//1,2,3,4顺序可以交换
}
测试尾插:
TestPushBack()
{
ListNode* phead = NULL;
ListInit(&phead);
ListPushBack(phead, 1);
ListPushBack(phead, 2);
ListPushBack(phead, 3);
ListPushBack(phead, 4);
ListPrint(phead);
}
测试结果:
3.目标结点前插入:
void ListInsert(ListNode* pos, ListNodeDataType x)
{
assert(pos);
ListNode* posPrev = pos->prev;
ListNode* newNode = CreateListNode(x);
newNode->next = pos;
newNode->prev = posPrev;
posPrev->next = newNode;
pos->prev = newNode;
}
测试目标结点前插入:
TestInsert()
{
ListNode* phead = NULL;
ListInit(&phead);
ListPushBack(phead, 1);
ListPushBack(phead, 2);
ListPushBack(phead, 3);
ListPushBack(phead, 4);
ListPrint(phead);
ListNode* pos = ListFind(phead, 2);//找到数据为2的结点
ListInsert(pos, 6);//在数据为2的结点前插入一个数据为6的结点
ListPrint(phead);
}
测试结果:
4.头插与尾插的简化:
void ListPushFront(ListNode* phead, ListNodeDataType x)
{
assert(phead);
ListInsert(phead->next, x);
}
void ListPushBack(ListNode* phead, ListNodeDataType x)
{
assert(phead);
ListInsert(phead, x);
}
八、删除:
1.头删:
void ListPopFront(ListNode* phead)
{
assert(phead);
assert(phead->next != phead);
ListNode* first = phead->next;//要删除的结点
ListNode* second = first->next;//要删除结点的后一个结点
phead->next = second;//1
second->prev = phead;//2
free(first);
first = NULL;
}
测试头删:
void TestPopFront()
{
ListNode* phead = NULL;
ListInit(&phead);
ListPushBack(phead, 1);
ListPushBack(phead, 2);
ListPushBack(phead, 3);
ListPushBack(phead, 4);
ListPrint(phead);
ListPopFront(phead);
ListPrint(phead);
ListPopFront(phead);
ListPopFront(phead);
ListPopFront(phead);
ListPrint(phead);
}
测试结果:
2.尾删:
void ListPopBack(ListNode* phead)
{
assert(phead);
assert(phead->next != phead);//只有虚拟头结点时
ListNode* tail = phead->prev;//找到尾结点
ListNode* tailPrev = tail->prev;//尾结点的前一个结点
phead->prev = tailPrev;
tailPrev->next = phead;
free(tail);
tail = NULL;
}
测试尾删:
void TestPopBack()
{
ListNode* phead = NULL;
ListInit(&phead);
ListPushBack(phead, 1);
ListPushBack(phead, 2);
ListPushBack(phead, 3);
ListPushBack(phead, 4);
ListPrint(phead);
ListPopBack(phead);
ListPrint(phead);
ListPopBack(phead);
ListPopBack(phead);
ListPopBack(phead);
ListPrint(phead);
}
测试结果:
3.删除目标结点:
void ListErase(ListNode* pos)
{
assert(pos);
ListNode* posPrev = pos->prev;//要删除结点的前一个结点
ListNode* posNext = pos->next;//要删除结点的后一个结点
posPrev->next = posNext;
posNext->prev = posPrev;
free(pos);
pos = NULL;
}
测试删除目标结点:
void TestErase()
{
ListNode* phead = NULL;
ListInit(&phead);
ListPushBack(phead, 1);
ListPushBack(phead, 2);
ListPushBack(phead, 3);
ListPushBack(phead, 4);
ListPrint(phead);
ListNode* pos = ListFind(phead, 2);//找到数据为2的结点
ListErase(pos);
ListPrint(phead);
}
测试结果:
4.头删与尾删的简化:
void ListPopFront(ListNode* phead)
{
assert(phead);
assert(phead->next != phead);
ListErase(phead->next);
}
void ListPopBack(ListNode* phead)
{
assert(phead);
assert(phead->next != phead);
ListErase(phead->prev);
}
九、查找:
ListNode* ListFind(ListNode* phead, ListNodeDataType x)
{
assert(phead);
ListNode* cur = phead->next;
while (cur!=phead)
{
if (cur->data == x)
return cur;
else
cur = cur->next;
}
return NULL;
}
十、修改:
void ListModify(ListNode* phead, ListNodeDataType pos, ListNodeDataType x)
{
assert(phead);
ListNode* ret = ListFind(phead, pos);
ret->data = x;
}
测试修改:
void TestModify()
{
ListNode* phead = NULL;
ListInit(&phead);
ListPushBack(phead, 0);
ListPushBack(phead, 1);
ListPushBack(phead, 2);
ListPushBack(phead, 3);
ListPrint(phead);
ListModify(phead, 0, 2003);//把数据为4的结点的数据改为2003
ListPrint(phead);
}