目录
一、链表
链表所需要的功能:
- 初始化
- 创建新节点
- 插入
- 删除
- 查询
- 链表的销毁(释放包括头结点在内的空间)
- 链表的清空(释放除了头结点以外的空间)
链表的优缺点:
- 优点:链表不需要初始化容量,可以任意加减元素,并且添加与删除元素十分快捷,只需要改变指针域指向的内容即可,内存利用率高,
- 缺点:查找元素,需要通过遍历链表来查找,十分耗时
- 适用于:需要频繁添加或者删除操作的场景
二、单向链表与双向链表的区别
单向链表与双向(循环)链表的区别:
- 在存储空间方面:单链表需要的存储空间比双向链表的要少,因为双向链表不仅保存有指向下一个节点的指针,还保存有指向上一个节点的指针,需要较大的空间来存储双向链表的指针域。
- 在处理时间方面:双向链表的插入与删除操作比单链表的时间要快很多。在最末尾插入的时候,单链表需要找到需要插入位置的前一个节点,需要遍历整个链表,时间复杂度为O(n),而双向链表只需要head->tail,就可以得到最后一个节点的位置,然后就可以进行插入操作,时间复杂度为O(1)。在删除操作方面,单链表需要遍历到需要删除的节点的前一个节点,双向链表需要遍历到需要删除的节点,时间复杂度同为O(n)。
一个提高单链表尾部插入效率的小方法:
- 定义一个指针tail,专门指向单链表的最末尾。如图所示
三、单链表的实现
注意:在处理单链表的插入的时候,优先处理新节点!
- 链表的定义
/*结构体*/
typedef int datatype;
typedef struct node
{
datatype data;
struct node *next;
}node,*list;
/*单链表的尾指针*/
struct node *tail;
- 链表的初始化
/*
创建头节点
*/
list initNode()
{
list head = malloc(sizeof(node));
if(head != NULL)
{
head->next = NULL;
}
tail = head;//尾指针指向头结点
return head;
}
- 创建新的节点
/*
创建新节点
*/
list newNode(datatype n)
{
list new = malloc(sizeof(node));
if(new != NULL)
{
new->data = n;
new->next = NULL;
}
return new;
}
- 尾插法:插入节点
/*
尾插法,添加节点(包含尾指针)
插入需要注意:优先处理新节点、不先动旧的链表
*/
bool insertNode_tail(list new)
{
/*判断新的节点是否为空*/
if (new == NULL)
{
printf("new point to NULL\n");
return false;
}
new->next = NULL;
tail->next = new;
tail = new;//tail指向最后一个节点
return true;
}
/*
不含有尾指针
*/
bool insertNode_tail(list new,list head)
{
/*判断新的节点是否为空*/
if (new == NULL)
{
printf("new point to NULL\n");
return false;
}
list p = head;
while(p->next != NULL)//搜索到最后的节点
{
p = p->next;
}
new->next = NULL;//优先处理新节点
p->next = new;
return true;
}
- 删除节点
/*
删除节点:输入对应数字的负数形式,则会删除对应数字
*/
bool delNode(datatype data, list head)
{
//数据处理
data = -data;
list p = head;
while(p->next != NULL)//找到要删除的节点的前一个节点
{
if(p->next->data == data)break;
p = p->next;
}
list del_p = p->next;//要删除的节点
p->next = del_p->next;
free(del_p);//释放空间
tail = head;//将tail指针指向最末尾
while(tail->next != NULL)
{
tail = tail->next;
}
return true;
}
- 查询链表
/*
查询节点
*/
void showNode(list head)
{
list p = head->next;
do
{
printf("%d\t", p->data);
p = p->next;
}while(p != NULL);
printf("\n");
}
- 销毁与清空链表
- 销毁链表:释放包括头节点的所有空间
- 清空链表:释放除了头节点的其他节点空间
/*
销毁整个链表
*/
void destoryAllNode(list head)
{
list p = head;
list next_p = NULL;
while(p != NULL)
{
next_p = p->next;//存储下一个节点指针
free(p);//释放当前节点
p = next_p;//指向销毁后的下一个节点
}
}
/*
清空链表
*/
void clearNode(list head)
{
list p = head->next;//p指向第一个节点
list next_p = NULL;
while(p != NULL)
{
next_p = p->next;
free(p);
p = next_p;
}
}
四、双向(循环)链表的实现
- 双向链表的定义
/*结构体*/
typedef int datatype;
typedef struct node
{
datatype data;
struct node *next, *pre;
}node,*list;
- 双向链表的初始化
/*
创建头节点
*/
list initNode()
{
list head = malloc(sizeof(node));
if(head != NULL)
{
head->next = head;
head->pre = head;
}
return head;
}
- 创建新节点
/*
创建新节点
*/
list newNode(datatype n)
{
list new = malloc(sizeof(node));
if(new != NULL)
{
new->data = n;
new->next = NULL;
new->pre = NULL;
}
return new;
}
- 尾插法:插入节点
/*
尾插法,添加节点
插入需要注意:优先处理新节点、不先动旧的链表
*/
bool insertNode_weicha(list new, list head)
{
/*判断新的节点是否为空*/
if (new == NULL)
{
printf("new point to NULL\n");
return false;
}
//优先处理新的节点
new->pre = head->pre;//head->pre 为最后一个节点
new->next = head;
head->pre->next = new;//倒数第二个节点指向新的节点
head->pre = new;//此时new为最后一个
return true;
}
- 删除节点
/*
删除节点:输入对应数字的负数形式,则会删除对应数字
*/
bool delNode(datatype data, list head)
{
//数据处理
data = -data;
list p = head->next;//p为第一个节点
while(p->next != head)
{
if(p->data == data)break;//p指向要删除的节点
p = p->next;
}
p->next->pre = p->pre;
p->pre->next = p->next;
p->pre = NULL;
p->next = NULL;
free(p);//释放空间
return true;
}
- 查询节点
/*
查询节点
*/
void showNode(list head)
{
list p = head->next;//p指向第一个节点
do
{
printf("%d\t", p->data);
p = p->next;
}while(p != head);
printf("\n");
}
- 销毁与清空链表
/*
销毁整个链表
*/
void destoryAllNode(list head)
{
list p = head->next;
list next_p = NULL;
while(p != head)
{
next_p = p->next;//存储下一个节点指针
free(p);//释放当前节点
p = next_p;//指向销毁后的下一个节点
}
free(head);
}
/*
清空链表
*/
void clearNode(list head)
{
list p = head->next;
list next_p = NULL;
while(p != head)
{
next_p = p->next;//存储下一个节点指针
free(p);
p = next_p;
}
}