单链表的实现

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

这里就是我所有的链表实现,相关的一些重要的会在链表面试题中写出来的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值