目录
先细节讲解头插与尾插的写法
尾插
void ListPushBack(LTNode* phead, LTDataType x)
{
assert(phead);
//只需让newnode指向前一个指针,前一个指针指向newnode
//newnode作新的尾结点,newnode->next指向头
//定义一个新的结点
LTNode* newnode = BuyListNode(x);
//prev是指前一个指针
//双向链表的头指针的prev指向尾,所以不用遍历去找尾指针
LTNode* tail = phead->prev;
//链接
tail->next = newnode;
newnode->prev = tail;
newnode->next = phead;
phead->prev = newnode;
//ListInsert(phead, x);
}
相比单链表带来的结构优势
- 已经存在链表结点的尾插
- 如果链表中一个结点都没有的时候,也可以使用这个尾插
头插
也是两种情况都适用,不用分情况去写代码
注意:head是不动的,它一直是哨兵位的头结点,头插也是要插在她之后的
void ListPushFront(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* newnode = BuyListNode(x);
//定义一个next,保存原来哨兵位之后的结点
LTNode* next = phead->next;
// phead newnode next 三者直接的关系
//链接
//以下代码位置没有关系,可以随意颠倒
phead->next = newnode;
newnode->prev = phead;
newnode->next = next;
next->prev = newnode;
//ListInsert(phead->next, x);
}
//如果不写next的方法:
//void ListPushFront(LTNode* phead, LTDataType x)
//{
// assert(phead);
//
// LTNode* newnode = BuyListNode(x);
//
//这两句一定得放在前
// phead->next->prev = newnode;
// newnode->next = phead->next;
//
// phead->next = newnode;
// newnode->prev = phead;
//}
双向链表的实现
List.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
//定义一个双向链表的结点
//与单链表相比多了一个前驱指针
typedef int LTDataType;
typedef struct ListNode
{
struct ListNode* next;
struct ListNode* prev;
LTDataType data;
}LTNode;
//比较麻烦
//struct List
//{
// LTNode* phead;
// int size;
//};
//也可以定义一个全局的size
//链表建议少调用size
//链表结构
//初始化
//void ListInit(LTNode** pphead);
LTNode* ListInit();
//打印
void ListPrint(LTNode* phead);
//尾插
void ListPushBack(LTNode* phead, LTDataType x);
//头插
void ListPushFront(LTNode* phead, LTDataType x);
//尾删
void ListPopBack(LTNode* phead);
//头删
void ListPopFront(LTNode* phead);
//判断是否为空链表
bool ListEmpty(LTNode* phead);
// 在pos位置之前插入x
void ListInsert(LTNode* pos, LTDataType x);
// 删除pos位置的节点
void ListErase(LTNode* pos);
//求链表的长度
int ListSize(LTNode* phead);
//链表的销毁
void ListDestory(LTNode* phead);
List.c
#include"List.h"
//创建一个结点
LTNode* BuyListNode(LTDataType x)
{
LTNode* node = (LTNode*)malloc(sizeof(LTNode));
if (node == NULL)
{
//调系统的接口可以使用perror
perror("malloc fail");
exit(-1);
}
node->data = x;
node->next = NULL;
node->prev = NULL;
return node;
}
//传参用二级指针
//void ListInit(LTNode** pphead)
//{
// *pphead = BuyListNode(-1);
// (*pphead)->next = *pphead;
// (*pphead)->prev = *pphead;
//}
//用其他方法解决(使用返回值)
//此类不需要二级指针
LTNode* ListInit()
{
LTNode* phead = BuyListNode(-1);
phead->next = phead;
phead->prev = phead;
return phead;
}
void ListPrint(LTNode* phead)
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
printf("%d ", cur->data);
cur = cur->next;
}
printf("\n");
}
void ListPushBack(LTNode* phead, LTDataType x)
{
assert(phead);
//只需让newnode指向前一个指针,前一个指针指向newnode
//newnode作新的尾结点,newnode->next指向头
//定义一个新的结点
LTNode* newnode = BuyListNode(x);
//prev是指前一个指针
//双向链表的头指针的prev指向尾,所以不用遍历去找尾指针
//定义一个tail保存原链表的尾结点
LTNode* tail = phead->prev;
//链接
tail->next = newnode;
newnode->prev = tail;
newnode->next = phead;
phead->prev = newnode;
//ListInsert(phead, x);
}
void ListPushFront(LTNode* phead, LTDataType x)
{
assert(phead);
LTNode* newnode = BuyListNode(x);
//定义一个next,保存原来哨兵位之后的结点
LTNode* next = phead->next;
// phead newnode next 三者直接的关系
//链接
//以下代码位置没有关系,可以随意颠倒
phead->next = newnode;
newnode->prev = phead;
newnode->next = next;
next->prev = newnode;
//ListInsert(phead->next, x);
}
//如果不写next的方法:
//void ListPushFront(LTNode* phead, LTDataType x)
//{
// assert(phead);
//
// LTNode* newnode = BuyListNode(x);
//
//这两句一定得放在前
// phead->next->prev = newnode;
// newnode->next = phead->next;
//
// phead->next = newnode;
// newnode->prev = phead;
//}
void ListPopBack(LTNode* phead)
{
assert(phead);
//链表为空的时候不能再删了(只剩哨兵位)
//1.
assert(phead->next != phead);
//2.
//assert(!ListEmpty(phead));
/*LTNode* tail = phead->prev;
LTNode* tailPrev = tail->prev;
free(tail);
tailPrev->next = phead;
phead->prev = tailPrev;*/
ListErase(phead->prev);
}
void ListPopFront(LTNode* phead)
{
assert(phead);
assert(phead->next != phead);
ListErase(phead->next);
}
bool ListEmpty(LTNode* phead)
{
assert(phead);
return phead->next == phead;
}
// 在pos位置之前插入x
void ListInsert(LTNode* pos, LTDataType x)
{
assert(pos);
LTNode* prev = pos->prev;
LTNode* newnode = BuyListNode(x);
// prve newnode pos
prev->next = newnode;
newnode->prev = prev;
newnode->next = pos;
pos->prev = newnode;
}
// 删除pos位置的节点
void ListErase(LTNode* pos)
{
assert(pos);
LTNode* prev = pos->prev;
LTNode* next = pos->next;
prev->next = next;
next->prev = prev;
free(pos);
}
int ListSize(LTNode* phead)
{
//遍历一遍进行计数
assert(phead);
LTNode* cur = phead->next;
int size = 0;
while (cur != phead)
{
++size;
cur = cur->next;
}
return size;
}
void ListDestory(LTNode* phead)
{
assert(phead);
LTNode* cur = phead->next;
while (cur != phead)
{
LTNode* next = cur->next;
ListErase(cur);
cur = next;
}
free(phead);
//phead = NULL;
}
Test.c
#include"List.h"
void TestList1()
{
//LTNode* plist = NULL;
//ListInit(&plist);//
LTNode* plist = ListInit();
ListPushBack(plist, 1);
ListPushBack(plist, 2);
ListPushBack(plist, 3);
ListPushBack(plist, 4);
ListPushBack(plist, 5);
ListPrint(plist);
}
void TestList2()
{
LTNode* plist = ListInit();
ListPushFront(plist, 1);
ListPushFront(plist, 2);
ListPushFront(plist, 3);
ListPushFront(plist, 4);
ListPushFront(plist, 5);
ListPrint(plist);
ListPopBack(plist);
ListPrint(plist);
ListPopBack(plist);
ListPrint(plist);
ListPopBack(plist);
ListPrint(plist);
ListPopBack(plist);
ListPrint(plist);
ListPopBack(plist);
ListPrint(plist);
// ListPopBack(plist);
// ListPrint(plist);
Listdestory(plist);
plist = NULL;
}
int main()
{
TestList2();
return 0;
}