1、链表的相关概念
链表作为一种十分常见的数据结构,其应用范围也是极其广泛的,书上,网上我们能看到许多的链表程序,但是自己用着是不习惯的,并且很多地方可能不完善,所以通过自己来实现这个程序,链表是一种线性表,但是并不会按线性的链表是一种物理存储单元上非连续、非顺序表的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的,。链表由一系列结点(链表中的每个元素称为结点)组成,结点可以在运行时动态生成,每个节点包括两部分:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域,相比于线性顺序表结构,操作复杂。由于不必须按顺序存储,链表在插入的时候可以达到O(1)的复杂度,比另一种线性表顺序表快得多,但是查找一个节点或者访问特定编号节点则需要O(n)的时间,而线性表顺序表相应的时间复杂度是O(logn)和O(1)
链表总共有八种:我们根据是否有头结点、有环无环,单链表、双链表排列组合之后重点内容就得到了8种,我们今天就来实现下这种单向的不带环不带头的单链表。
test.h的相关代码如下
#define _CRT_SECURE_NO_WARNINGS
#pragma once
//单向的不带环不带头链表
#include <stdio.h>
#include <stdlib.h>
//单向链表
//1、next=NULL
//2、next=
typedef char LinkNodeType;
typedef struct LinkNode{
LinkNodeType data;
struct LinkNode* next;//只有一个指针指向下一个元素,单向链表
}LinkNode;
typedef LinkNode* PLinkNode;
void LinkListInit(LinkNode* node);
//void LinkListDestroy(LinkNode** phead);
void LinkListPushBack(LinkNode** phead, LinkNodeType value);//尾插
void LinkListPrintChar(LinkNode* head,const char* msg);//打印链表函数
void LinkListPopBack(LinkNode** head);//尾删
void LinkListPushFront(LinkNode** phead, LinkNodeType value);//头插
void LinkListPopFront(LinkNode** phead);//头删
LinkNode* LinkListFind(LinkNode* head, LinkNodeType to_find);//查找元素在链表中的位置,
//to_find要查找的值,return这个值对应的节点的地址
void LinkListInsert(LinkNode** head, LinkNode* pos,LinkNodeType value);//在pos之前插入元素
void LinkListInsertAfter(LinkNode** head, LinkNode* pos, LinkNodeType value);//在pos之后插入元素
//void LinkListErase(LinkNode** head, LinkNode* pos);//删除指定位置的元素
//void LinkListErase2(LinkNode** head, LinkNode** pos);//删除指定位置的元素
void LinkListRemove(LinkNode** head, LinkNodeType to_delete);//删除指定值的元素
//void LinkListRemoveAll(LinkNode** head, LinkNodeType to_delete);//删除指定值的所有元素都删掉
int LinkListEmpty(LinkNode* head);//判断链表是否为空,链表为空返回1,否则返回0
size_t LinkListSize(LinkNode* head);//求链表中元素的个数
test.c 的代码如下:
#define _CRT_SECURE_NO_WARNINGS
#include <stdlib.h>
#include "test.h"
void LinkListInit(LinkNode** node){
*node = NULL;
}
void LinkListPrintChar(LinkNode* head, const char* msg){
//打印链表函数
printf("[%s]\n", msg);
LinkNode* cur = head;
for (; cur != NULL; cur = cur->next){
printf("[%c|%p] ", cur->data, cur);
}
printf("\n");
}
LinkNode* CreateNode(LinkNodeType value){
LinkNode* new_node = (LinkNode*)malloc(sizeof(LinkNode));
new_node ->data = value;
new_node->next = NULL;
return new_node;
}
void LinkListPushBack(LinkNode **phead, LinkNodeType value){
if (phead == NULL){
//非法输入
return;
}
if (*phead == NULL){
//空链表
*phead = CreateNode(value);
return;
}
//链表非空时候代码如下:
LinkNode* cur = *phead;
while (cur->next != NULL){
cur = cur->next;//便利之后,取下一个元素的地址
}
LinkNode* new_node = CreateNode(value);
cur->next = new_node;
return;
}
void Destroynode(LinkNode *node){
free(node);
}
void LinkListPopBack(LinkNode** phead){//因为在删除的时候要考虑只有一个元素时,头指针的指向会改变的
if (phead == NULL){
//非法输入
return;
}
if (*phead == NULL){
//空链表,不能删除
return;
}
if ((*phead ==NULL)){
//只有一个元素
Destroynode(*phead);//销毁函数,可提高代码的维护性
*phead = NULL;//必须要进行释放,不然会造成内存泄漏,所以必须用free释放
//在这里为了代码的美观,所以必须要调用Destroynode函数
}
//先定义一个指针cur,要知道删哪个,还得知道前一个,让它的next指向空,再定义一个pre指针,指向cur的前一个元素。
//很多节点时,初始状态先让pre指向空指针,cur指向第一个元素,判断cur是不是最后一个结点,不是的话,让pre指向cur
//,cur立即指向下一个元素,最后cur指向最后一个元素,pre指向倒数第二个元素,然后cur释放,pre的next指向NULL
LinkNode* cur = *phead;
LinkNode* pre = NULL;
while (cur->next != NULL){
pre = cur;
cur = cur->next;
}
//当循环结束pre指向倒数第二个结点,cur指向最后一个结点
pre->next = NULL;
Destroynode(cur);//为了代码的扩展性,万一在写代码的过程中,万一结点保存的不是int,char....,
//而是由malloc开辟出来的,在Destroy中就必须free(node->data);
return;
}
void LinkListPushFront(LinkNode** phead, LinkNodeType value){
//头插
if (phead == NULL){
//非法输入
return;
}
LinkNode* new_node = CreateNode(value);
new_node->next = *phead;
*phead = new_node;
}
void LinkListPopFront(LinkNode** phead){
//头删
if (phead == NULL){
return;
}
if (*phead == NULL){
return;
}
LinkNode* to_earse = *phead;
*phead = (*phead)->next;
Destroynode(to_earse);
return;
}
LinkNode* LinkListFind(LinkNode* head,LinkNodeType to_find)//查找元素在链表中的位置,
//to_find)要查找的值,return这个值对应的节点的地址
{
if (head == NULL) //空链表
{
return;
}
else
{
LinkNode* cur = head;
while (cur) //遍历链表
{
if (cur->data == to_find)
{
return cur; //当cur结点的元素等于我们要寻找的元素时,返回cur结点的地址
}
cur = cur->next;
}
return;
}
}
void LinkListInsert(LinkNode** head, LinkNode* pos, LinkNodeType value)//在pos之前插入元素
//pos 指的是节点指针,pos为空表明的是没有这个节点
{
if (head == NULL||pos==NULL){
return;
}
if (*head == pos){//出现一个元素
LinkListPushFront(head,value);
return;
}
LinkNode* cur = *head;
while (cur!=NULL)
{
if (cur ->next== pos)
{
LinkNode* new_node = CreateNode(value);
cur->next = new_node;
new_node->next = pos;
return;
}
cur = cur->next;
}
}
//在这里还有一种版本的实现那就是创造一个新的节点,将pos里的值给他,调用 LinkListInsert(pos,pos->data)
//pos->data = value;
void LinkListInsertAfter(LinkNode** head, LinkNode* pos, LinkNodeType value)//在pos之后插入元素
{
if (head == NULL || pos == NULL){
return;
}
LinkNode* new_node = CreateNode(value);//LinkNode* new_node = CreateNode(pos->data);
new_node->next = pos->next; // new_node->next = pos->next;
pos->next = new_node; //pos->next=new_node ;
//pos->data=value
//在这里还有一种版本的实现那就是创造一个新的节点,将pos里的值给他,调用 LinkListInsert(pos,pos->data)
//pos->data = value;在pos之前插入元素
}
void LinkListErase(LinkNode** head, LinkNode* pos)//删除指定位置的元素
{
if (head == NULL || pos== NULL){//非法输入
return;
}
if (*head == NULL)
{ //空链表不能删除
return;
}
LinkNode* cur = *head;
for (; cur != NULL; cur = cur->next)
{
if (cur->next == pos){
break;
}
}
//循环结束之后,要取判定到底是找到了退出的,换是没找到退出的
cur->next = pos->next;
Destroynode(pos);
return;
}
void LinkListErase2(LinkNode** head, LinkNode* pos)//删除指定位置的元素
{
//删除后面的元素,将后面的值给指定pos处,再将后面的值删除,
if (head == NULL || pos == NULL){//非法输入
return;
}
if (*head == NULL)
{ //空链表不能删除
return;
}
if (pos->next == NULL){//要删除的是最后一个元素
LinkListPopBack(head);
return;
}
pos->data = pos->next->data;
LinkNode* to_erase = pos->next;
pos->next = to_erase->next;
Destroynode(to_erase);
}
void LinkListRemove(LinkNode** head, LinkNodeType to_delete)//删除指定值的元素
{
if (head == NULL){
return;
}
if (*head == NULL){
return;
}
LinkNode* pos = LinkListFind(*head, to_delete);
LinkListErase(head, pos);
}
void LinkListRemoveAll(LinkNode** head, LinkNodeType to_delete)
//删除指定值的所有元素都删掉
{
if (head == NULL){
return;
}
if (*head == NULL){
return;
}
LinkNode* cur = *head;
while (cur!= NULL)
{
LinkListErase(head, (LinkListFind(*head, to_delete)));
cur = cur->next;
if (cur->next==NULL)//如果最后一个要删除的元素,cur->next 也为空的话,最后一个元素就不能被删除了,需要重新
//判断这个元素是不是要删除的元素。
{
LinkListErase(head, (LinkListFind(*head, to_delete)));
return;
}
}
return;
}
int LinkListEmpty(LinkNode* head)//判断链表是否为空,链表为空返回1,否则返回0
{
return (head->next == NULL) ? 0 : 1;
}
size_t LinkListSize(LinkNode* head)// 求链表中元素的个数
{
if (head == NULL){
return;
}
size_t count = 0;
LinkNode* cur = head;
while (cur != NULL){
++count;
cur = cur->next;
}
return count;
}
测试代码如下:
#define _CRT_SECURE_NO_WARNINGS
#include "test.h"
//////////////////////////////////////////////
////////////////以下为测试代码///////////////
////////////////////////////////////////////
#if 1
#include <stdio.h>
#define TEST_HEADER printf("\n===============%s================\n",__FUNCTION__);
void testinit(){
TEST_HEADER;
LinkNode* head;
LinkListInit(&head);
}
void test1(){
TEST_HEADER;
LinkNode* head;
LinkListInit(&head);
LinkListPushBack(&head, 'a');
LinkListPushBack(&head, 'b');
LinkListPushBack(&head, 'c');
LinkListPushBack(&head, 'd');
LinkListPushBack(&head, 'e');
LinkListPrintChar(head,"尾插四个元素");//用head是因为不需要修改头指针的指向
LinkListPopBack(&head);
LinkListPopBack(&head);
LinkListPrintChar(head, "删除空链表");
LinkListPrintChar(head,"删除两个元素");//不需要修改头指针指向
}
void test2(){
TEST_HEADER;
LinkNode* head;
LinkListInit(&head);
LinkListPushFront(&head, 'a');
LinkListPushFront(&head, 'b');
/*LinkListPushFront(&head, 'c');
LinkListPushFront(&head, 'd');
LinkListPushFront(&head, 'e');*/
LinkListPrintChar(head,"插2");//用head是因为不需要修改头指针的指向
}
void test3(){
TEST_HEADER;
LinkNode* head;
LinkListInit(&head);
//LinkListPushBack(&head, 'a');
//LinkListPushBack(&head, 'b');
//LinkListPushBack(&head, 'c');
//LinkListPushBack(&head, 'd');
//LinkListPushBack(&head, 'e');
LinkListPopFront(&head);//头删
LinkListPrintChar(head, "删除一个元素");//不需要修改头指针指向
}
void test4(){
TEST_HEADER;
LinkNode* head;
LinkListInit(&head);
LinkListPushBack(&head, 'a');
LinkListPushBack(&head, 'b');
LinkListPushBack(&head, 'c');
LinkListPushBack(&head, 'd');
LinkListPushBack(&head, 'e');
LinkNode* pos_x = LinkListFind(head,'x');
printf("pox_x expect is NULL,actual %p\n", pos_x);
LinkNode* pos_e = LinkListFind(head, 'e');
printf("找到结点的地址为:%p\n", head->next,pos_e);
}
void test5(){
TEST_HEADER;
LinkNode* head;
LinkListInit(&head);
LinkListPushBack(&head, 'a');
LinkListPushBack(&head, 'b');
LinkListPushBack(&head, 'c');
LinkListPushBack(&head, 'd');
LinkListPushBack(&head, 'e');
LinkListPrintChar(head, "不插入元素");
LinkNode* pos = head->next->next;
LinkListInsert(&head, pos, ' x');
LinkListPrintChar(head, "插入一个元素");
}
void test6(){
TEST_HEADER;
LinkNode* head;
LinkListInit(&head);
LinkListPushBack(&head, 'a');
LinkListPushBack(&head, 'b');
LinkListPushBack(&head, 'c');
LinkListPushBack(&head, 'd');
LinkListPushBack(&head, 'e');
LinkListPrintChar(head, "不插入元素");
LinkNode* pos = head->next;
LinkListInsertAfter(&head, pos, ' x');
LinkListPrintChar(head, "插入一个元素");
}
void test7(){
TEST_HEADER;
LinkNode* head;
LinkListInit(&head);
LinkListErase(&head,(LinkNode*)0x11);
LinkListPrintChar(head, "删除空链表");
LinkListPushBack(&head, 'a');
LinkListPushBack(&head, 'b');
LinkListPushBack(&head, 'c');
LinkListPushBack(&head, 'd');
LinkListPushBack(&head, 'e');
LinkNode* pos = head->next;
LinkListErase(&head, pos);
LinkListPrintChar(head, "删除一个元素b");
/*LinkNode* pos = head->next->next;
LinkListErase(&head, pos);
LinkListPrintChar(head, "删除一个元素c");*/
}
void test8(){
TEST_HEADER;
LinkNode* head;
LinkListInit(&head);
LinkListPushBack(&head, 'a');
LinkListPushBack(&head, 'b');
LinkListPushBack(&head, 'c');
LinkListPushBack(&head, 'd');
LinkListPushBack(&head, 'e');
LinkNode* pos = head->next;
LinkListErase2(&head, pos);
LinkListPrintChar(head, "删除一个元素b");
}
void test9(){
TEST_HEADER;
LinkNode* head;
LinkListInit(&head);
LinkListRemove(&head, 'a');
printf("删除a\n");
LinkListPushBack(&head, 'a');
LinkListPushBack(&head, 'b');
LinkListPushBack(&head, 'c');
LinkListPushBack(&head, 'd');
LinkListPushBack(&head, 'e');
LinkListPrintChar(head, "当前链表");
LinkListRemove(&head, 'x');
LinkListPrintChar(head, "删除x");
}
void test10(){
TEST_HEADER;
LinkNode* head;
LinkListInit(&head);
LinkListPushBack(&head, 'a');
LinkListPushBack(&head, 'b');
LinkListPushBack(&head, 'c');
LinkListPushBack(&head, 'b');
LinkListPushBack(&head, 'b');
LinkListPrintChar(head, "当前链表");
LinkListRemoveAll(&head, 'b');
LinkListPrintChar(head, "删除b");
}
void test11(){
TEST_HEADER;
LinkNode* head;
LinkListInit(&head);
LinkListEmpty(&head);
printf("%d\n");
LinkListPushBack(&head, 'a');
LinkListPushBack(&head, 'b');
LinkListPushBack(&head, 'c');
LinkListPushBack(&head, 'd');
LinkListPushBack(&head, 'e');
LinkListEmpty(&head);
printf("%d\n");
}
void test12(){
TEST_HEADER;
LinkNode* head;
LinkListInit(&head);
LinkListPushBack(&head, 'a');
LinkListPushBack(&head, 'b');
LinkListPushBack(&head, 'c');
LinkListPushBack(&head, 'd');
LinkListPushBack(&head, 'e');
LinkListPrintChar(head, "删除元素");
size_t i=LinkListSize(head);
printf("链表的长度为%d\n",i);
}
int main(){
///*testinit();
//test1();*/
//test2();
//test3();
// test4();
//test5();
//test6();
//test7();
//test8();
//test9();
test10();
//test11();
//test12();
getchar();
return 0;
}
#endif
这里就是我所有的链表实现,相关的一些重要的会在链表面试题中写出来的。