不带头结点不带环的单链表
链表按照:单向链表、双向链表;带环链表、不带环链表;带头结点、不带头结点;共有八种,最常见的即不带头结点不带环的单链表的相关操作实现。
1. 不带头结点:用NULL表示空链表;
2. 用第一个结点表示整个链表。因为知道第一个结点,从而通过其next域可以找到单链表的其他结点;
3. 最后一个结点的next指向NULL;
4. 单链表除首元素结点外的每个结点都有直接前驱,除最后一个结点外的每个结点都有直接后继;
Linklist.h
首先,我们先将单链表的结点的结构体表示出来,一个结点包括它的数值,再有一个指向下一个结点的指针,所以定义结构体结点如下:
//单向不带环不带头结点链表实现
#pragma once
#include <stdio.h>
#include <stdlib.h>//malloc头文件
#include <stddef.h>//sizeof
#define SHOW_NAME printf("\n==========%s==========\n",__FUNCTION__)
typedef char LinkNodeType;
typedef struct LinkNode//单链表的结点
{
LinkNodeType data;
struct LinkNode* next;
}LinkNode;//*pLinkNode;
1. 链表的初始化
void LinklistInit(LinkNode** head)//初始化
{
*head = NULL;
}
2.创建一个新结点
LinkNode* CreateNode(LinkNodeType value)//创建新结点
{
LinkNode* new_node = (LinkNode* )malloc(sizeof(LinkNode));//sizeof是一个运算符
new_node->data = value;
new_node->next = NULL;
return new_node;
}
3.单链表的尾插
void LinklistPushBack(LinkNode** head,LinkNodeType value)//尾插
{
if(head == NULL)//说明head结点不存在
return;
if(*head == NULL)//空链表
{
*head = CreateNode(value);
return;
}
LinkNode* cur = *head;//链表非空
while(cur->next != NULL)
{
cur = cur->next;
}
LinkNode* new_node = CreateNode(value);
cur->next = new_node;
//此处不用将new_node的next置空,因为CreateNode中已实现
}
4.单链表的尾删
void LinklistPopBack(LinkNode** head)//尾删
{
if(head == NULL)//非法操作
return;
if(*head == NULL)//空链表
return;
if((*head)->next == NULL)//只有一个元素
{
DestroyNode(*head);
*head = NULL;
return;
}
LinkNode* pre = NULL;//pre指向倒数第二个结点
LinkNode* cur = *head;
while(cur->next != NULL)
{
pre = cur;
cur = cur->next;
}
pre->next = NULL;
DestroyNode(cur);
}
5.单链表的头插
void LinklistPushFront(LinkNode** head,LinkNodeType value)//头插
{
if(head == NULL)//非法操作
return;
LinkNode* new_node = CreateNode(value);//链表是否为空都适用
new_node->next = *head;
*head = new_node;
return;
}
6.单链表的头删
void LinklistPopFront(LinkNode** head)//头删
{
if(head == NULL)
return;
if(*head == NULL)
return;
LinkNode* cur = *head;//不论链表里有多少个结点,都适用
*head = (*head)->next;
DestroyNode(cur);
return;
}
7.给定一个值,在单链表中查找对应结点
LinkNode* LinklistFind(LinkNode* head,LinkNodeType value)//给定一个值,在单链表中查找对应结点
{
if(head == NULL)//空链表
return;
LinkNode* cur = head;
while(cur != NULL)
{
if(cur->data == value)
return cur;
cur = cur->next;
}
return NULL;
}
8.在单链表的任意位置之后插入结点
void LinklistInsertAfter(LinkNode* pos,LinkNodeType value)//在pos位置之后插入元素
{
if(pos == NULL)//非法操作,pos不存在
return;
LinkNode* new_node = CreateNode(value);
new_node->next = pos->next;
pos->next = new_node;
return;
}
9.
在单链表的任意位置之后前入结点
void LinklistInsertBefore(LinkNode** head,LinkNode* pos,LinkNodeType value)//在pos位置之前插入元素
{
if(head == NULL || pos == NULL)//非法操作
return;
if(*head == pos)//插入位置为头结点之前
{
LinklistPushFront(head,value);
return;
}
LinkNode* cur = *head;
for(; cur!=NULL; cur=cur->next)
{
if(cur->next == pos)
break;
}
if(cur == NULL)//没找到pos
return;
LinklistInsertAfter(cur,value);
}
10.删除指定结点
方法一:
void LinklistErase(LinkNode** head,LinkNode* pos)//删除pos结点的O(n)算法
{
if(head == NULL || pos == NULL)
return;
if(*head == NULL)
return;
LinkNode* cur = *head;
if(cur == pos)
{
LinkNode* to_delete = cur;
*head = cur->next;
DestroyNode(to_delete);
return;
}
for(; cur->next!= NULL; cur=cur->next)
{
if(cur->next == pos)
break;
}
if(cur == NULL)//未找到pos结点
return;
cur->next = pos->next;
DestroyNode(pos);
}
方法二:
void LinklistErase1(LinkNode** head,LinkNode* pos)
{
if(head == NULL || pos == NULL)
return;
if(*head == NULL)
return;
if(pos->next == NULL)//要删结点为最后一个结点
{
LinklistPopBack(head);
return;
}
if(pos == *head)
{
LinklistPopFront(head);
return;
}
pos->data = pos->next->data;//将pos后结点的值赋给pos,再删除pos后的结点即可
LinkNode* cur = pos->next;
pos->next = cur->next;
DestroyNode(cur);//此代码不适用删除非链表中的有效元素
}
11.给定一个值,删除对应结点(若有相同的,仅删除第一个结点)
void LinklistRemove(LinkNode** head,LinkNodeType value)//删除指定值的第一个结点
{
if(head == NULL)
return;
if(*head == NULL)
return;
LinkNode* cur = *head;
for(; cur!=NULL; cur=cur->next)
{
if(cur->data == value)
{
LinklistErase(head,cur);
return;
}
}
}
12.给定一个值,删除对应结点(若有相同的,删除所有结点)
void LinklistRemoveAll(LinkNode** head,LinkNodeType value)//删除指定值的所有结点
{
if(head == NULL)
return;
if(*head == NULL)
return;
LinkNode* cur = *head;
LinkNode* pre = NULL;
while(cur != NULL)
{
if(cur->data == value)
{
if(cur == *head)//要删的为首元素结点
{
LinkNode* to_delete = cur;
cur = *head = cur->next;
DestroyNode(to_delete);
}
else if(cur->next == NULL)//要删的为最后一个结点
{
pre->next = NULL;
DestroyNode(cur);
}
else
{
LinkNode* to_delete = cur;
cur = cur->next;
pre->next = cur;
DestroyNode(to_delete);
}
}
else
{
pre = cur;
cur = cur->next;
}
}
return;
}
13.单链表置空
int LinklistEmpty(LinkNode* head)
{
if(head == NULL)
return 1;
return 0;
}
14.销毁结点
void DestroyNode(LinkNode* head)//销毁一个结点
{
free(head);
}
15.求链表的大小
size_t LinklistSize(LinkNode* head)
{
if(head == NULL)
return 0;
LinkNode* cur = head;
size_t count = 0;
while(cur != NULL)
{
count++;
cur = cur->next;
}
return count;
}
16.单链表的打印
void LinklistPrintChar(LinkNode* head,const char* msg)//打印函数
{
printf("[%s]\n",msg);
if(head == NULL)//空链表
{
printf("链表为空\n");
return;
}
LinkNode* cur = head;
for(; cur!=NULL; cur=cur->next)
{
printf("[%c|%p] ",cur->data,cur);//打印值和地址
}
printf("\n");
}
17.单链表的销毁
void LinklistDestroy(LinkNode** head)//销毁链表
{
if(head == NULL)
return;
if(*head == NULL)
return;
LinkNode* cur = *head;
while(cur != NULL)
{
LinkNode* to_delete = cur;
cur = cur->next;
DestroyNode(to_delete);
}
*head = NULL;
//检查内存泄漏的方法:程序执行前查看程序占用内存的情况,多次调用该函数后再次检查内存
}
以下为以上函数的测试代码:
void TestInit()
{
SHOW_NAME;
LinkNode* head;
LinklistInit(&head);
}
void TestPushBack()
{
SHOW_NAME;
LinkNode* head;
LinklistInit(&head);
LinklistPushBack(&head,'a');
LinklistPushBack(&head,'b');
LinklistPushBack(&head,'c');
LinklistPushBack(&head,'d');
LinklistPrintChar(head,"尾插四个元素");
}
void TestPopBack()
{
SHOW_NAME;
LinkNode* head;
LinklistInit(&head);
LinklistPushBack(&head,'a');
LinklistPushBack(&head,'b');
LinklistPushBack(&head,'c');
LinklistPushBack(&head,'d');
LinklistPrintChar(head,"尾插四个结点");
LinklistPopBack(&head);
LinklistPrintChar(head,"尾删一个结点");
}
void TestPushFront()
{
SHOW_NAME;
LinkNode* head;
LinklistInit(&head);
LinklistPushFront(&head,'a');
LinklistPushFront(&head,'b');
LinklistPushFront(&head,'c');
LinklistPushFront(&head,'d');
LinklistPrintChar(head,"头插四个结点");
}
void TestPopFront()
{
SHOW_NAME;
LinkNode* head;
LinklistInit(&head);
LinklistPushFront(&head,'a');
LinklistPushFront(&head,'b');
LinklistPushFront(&head,'c');
LinklistPushFront(&head,'d');
LinklistPrintChar(head,"头插四个结点");
LinklistPopFront(&head);
LinklistPrintChar(head,"头删一个结点");
}
void TestFind()
{
SHOW_NAME;
LinkNode* head;
LinklistInit(&head);
LinklistPushFront(&head,'a');
LinklistPushFront(&head,'b');
LinklistPushFront(&head,'c');
LinklistPushFront(&head,'d');
LinklistPrintChar(head,"头插四个结点");
LinkNode* ret = LinklistFind(head,'c');
printf("[c的地址为%p]\n",ret);
}
void TestInsertAfter()
{
SHOW_NAME;
LinkNode* head;
LinklistInit(&head);
LinklistPushBack(&head,'a');
LinklistPushBack(&head,'b');
LinklistPushBack(&head,'c');
LinklistPushBack(&head,'d');
LinklistPrintChar(head,"尾插四个元素");
LinkNode* cur = head->next;
LinklistInsertAfter(cur,'x');
LinklistPrintChar(head,"向b后插入x");
}
void TestInsertBefore()
{
SHOW_NAME;
LinkNode* head;
LinklistInit(&head);
LinklistPushBack(&head,'a');
LinklistPrintChar(head,"尾插一个元素");
LinklistInsertBefore(&head,head,'h');
LinklistPrintChar(head,"在a前插入h");
LinklistPushBack(&head,'b');
LinklistPushBack(&head,'c');
LinklistPushBack(&head,'d');
LinklistPrintChar(head,"再尾插三个元素");
LinkNode* cur = head->next->next;
LinklistInsertBefore(&head,cur,'x');
LinklistPrintChar(head,"向b前插入x");
}
void TestErase()
{
SHOW_NAME;
LinkNode* head;
LinklistInit(&head);
LinklistPushBack(&head,'a');
LinklistPushBack(&head,'b');
LinklistPushBack(&head,'c');
LinklistPushBack(&head,'d');
LinklistPrintChar(head,"尾插四个元素");
LinkNode* cur = head->next->next;
LinklistErase(&head,cur);
LinklistPrintChar(head,"删除c");
cur = head;
LinklistErase(&head,cur);
LinklistPrintChar(head,"删除a");
}
void TestErase1()
{
SHOW_NAME;
LinkNode* head;
LinklistInit(&head);
LinklistPushBack(&head,'a');
LinklistPushBack(&head,'b');
LinklistPrintChar(head,"尾插两个元素");
LinkNode* cur = head->next;
LinklistErase1(&head,cur);
LinklistPrintChar(head,"删除b");
LinklistPushBack(&head,'c');
LinklistPushBack(&head,'d');
LinklistPushBack(&head,'f');
LinklistPrintChar(head,"再尾插三个元素");
cur = head->next;
LinklistErase1(&head,cur);
LinklistPrintChar(head,"删除c");
cur = head;
LinklistErase1(&head,cur);
LinklistPrintChar(head,"删除a");
}
void TestRemove()
{
SHOW_NAME;
LinkNode* head;
LinklistInit(&head);
LinklistPushBack(&head,'a');
LinklistPushBack(&head,'b');
LinklistPushBack(&head,'c');
LinklistPushBack(&head,'d');
LinklistPrintChar(head,"尾插四个元素");
LinklistRemove(&head,'c');
LinklistPrintChar(head,"删除c");
LinklistRemove(&head,'x');
LinklistPrintChar(head,"删除x");
}
void TestRemoveAll()
{
SHOW_NAME;
LinkNode* head;
LinklistInit(&head);
LinklistPushBack(&head,'d');
LinklistPushBack(&head,'d');
LinklistPushBack(&head,'d');
LinklistPushBack(&head,'a');
LinklistPushBack(&head,'d');
LinklistPushBack(&head,'f');
LinklistPrintChar(head,"尾插六个元素");
LinklistRemoveAll(&head,'d');
LinklistPrintChar(head,"删除d");
}
void TestEmpty()
{
SHOW_NAME;
LinkNode* head;
LinklistInit(&head);
size_t ret = LinklistEmpty(head);
if(ret == 1)
printf("链表为空\n");
else
printf("链表不为空\n");
LinklistPushBack(&head,'c');
LinklistPushBack(&head,'d');
LinklistPrintChar(head,"尾插两个元素");
ret = LinklistEmpty(head);
if(ret == 1)
printf("链表为空\n");
else
printf("链表不为空\n");
}
void TestSize()
{
SHOW_NAME;
LinkNode* head;
LinklistInit(&head);
size_t ret = LinklistSize(head);
printf("expected 0,actual is %u\n",ret);
LinklistPushBack(&head,'a');
LinklistPushBack(&head,'b');
LinklistPushBack(&head,'c');
LinklistPushBack(&head,'d');
LinklistPrintChar(head,"尾插四个元素");
ret = LinklistSize(head);
printf("expected 4,actual is %u\n",ret);
}
void TestDestroy()//销毁链表
{
SHOW_NAME;
LinkNode* head;
LinklistInit(&head);
LinklistPushBack(&head,'a');
LinklistPushBack(&head,'b');
LinklistPushBack(&head,'c');
LinklistPushBack(&head,'d');
LinklistPrintChar(head,"尾插四个元素");
LinklistDestroy(&head);
LinklistPrintChar(head,"销毁链表");
}