不带头结点不带环的单链表
链表按照:单向链表、双向链表;带环链表、不带环链表;带头结点、不带头结点;共有八种,最常见的即不带头结点不带环的单链表的相关操作实现。
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,"销毁链表"); }