单链表的基本操作

单链表(含头结点)

一、单链表

  • 1.1 关于单链表
    单链表是一种采用链式存储的线性结构,其数据元素是由结点结构体组成,结点包括数据域和指针域。
    相较于顺序表来说,单链表解脱了需要整片存储空间和存储空间大小固定的束缚;解决了插入、删除操作时对其他元素的操作问题。
    相应的对于顺序表来说,单链表的整体存储空间大小需求更多;同时查询数据元素的方法也较单一。
  • 2.1 单链表的结构体
    单链表的结构体是由数据域和指针域组成的,数据域存放要保存的数据,指针域保存下一个结点的地址(下一个结点为空,保存NULL)。
typedef struct LNode
{
    int data;
    struct LNode *next;
}linklist_t, LNode_t;       

二、单链表的创建(有头结点)

  • 2.1 传参的方式创建单链表
void Create_Linklist(linklist_t **L){
    //先判断传入的参数是否正确
    if(NULL == L){
        printf("Parameter error\n");
        return;
    }
    
    //创建头结点
    *L = (linklist_t *)malloc(sizeof(linklist_t);
    if(NULL == *L){
        printf("Space request error\n");
        return;
    }
    (*L)->data = -1;
    (*L)->next = NULL;
    return;
}
  • 2.2 指针函数的方式创建单链表
linklist *Create_Linklist(void){
    linklist *L = (linklist *)malloc(sizeof(linklist));
    if(NULL == L){
        printf("Space request error\n");
        return NULL;
    }
    L->data = -1;
    L->next = NULL;
    return L;
}
  • 上述两种方法的区别
    在这里插入图片描述
  • 上述的两种方法仅仅是创建了带有头结点的单链表,想要得到想要的单链表还需要使用单链表的插入函数。

三、单链表的结点查找(按位置查找)

  • 3.1 单链表中元素的个数
    功能:统计单链表中元素个数
    参数:单链表的头结点
    返回值:成功返回单链表中元素个数;失败返回-1
//用于记录单链表中元素的个数
int Length_Linklist(linklist_t *L){
    //判断参数是否正确
    if(NULL == L){
        printf("Parameter error\n");
        return -1;
    }

    //num记录元素个数
    int num = 0;
    if(NULL == L->next){
        return num;
    }
    LNode_t *Q = L->next; 
    num = 1;
    while(NULL != Q->next){
        Q = Q->next;
        num++;
    }
    return num;
}
  • 3.2 单链表的查找
    功能:查找单链表的指定结点的前一个结点
    参数:单链表的头指针、需要查找结点的位置参数
    返回值:成功返回的为该结点的前一个结点的结点指针;失败返回NULL
//如果第一个有效结点算作第 0 位则pos不用改动,如果第一个有效结点算作第 1 位,则pos = pos - 1;
//返回的为该结点的前一个结点的结点指针,主要也是为了方便之后的插入和删除操作
LNode_t *PosFind_Linklist(linklist_t *L, int pos){
    //判断参数是否正确
    if(NULL == L){
        printf("Parameter error\n");
        return NULL;
    }
    //注意这里是 "pos > num",可能会返回的是尾结点(pos = num),\
    这样写也主要是为了便于之后插入的方便,若只想查找元素数据可在该函数前加一个判断
    int Length_Linklist(L);
    if((pos < 0) || (pos > num)){
        printf("Parameter error\n");
        return NULL;
    }
    num = 0;
    LNode_t *N = L;
    while(num != pos){
        num++;
        N = N->next;
    }
    return N;
}

四、单链表的插入操作

  • 4.1 头插法
int HeadInsert_Linklist(linklist_t *L, int value){
    //判断参数是否正确
    if(NULL == L){
        printf("Parameter error\n");
        return 0;
    }
    
    //创建结点
    LNode_t *N = (LNode *)malloc(sizeof(LNode_t));
    N->data = value;
    N->next = NULL;

    //插入到头结点之后
    N->next = L->next;
    L->next = N;
    
    return 0;
}
  • 4.2 尾插法
int TailInsert_Linklist(linklist_t *L, int value){
    //判断参数是否正确
    if(NULL == L){
        printf("Parameter error\n");
        return -1;
    }

    //创建结点
    LNode_t *N = (LNode *)malloc(sizeof(LNode_t));
    N->data = value;
    N->next = NULL;

    //创建尾指针
    LNode_t *Q = L;

    //遍历找到链尾
    while(NULL != Q->next){
        Q = Q->next;
    }

    //链尾元素之后插入新结点
    Q->next = N;
    Q = Q->next;
}
  • 4.3 指定位置插入
    功能:将新结点插入到指定位置
    参数:单链表的头指针、新结点的位置、新结点数据域的值
    返回值:成功返回0;失败返回-1
int PosInsert_Linklist(linklist_t *L, int pos, int value){
    //如果第一个有效结点算作第 0 位则pos不用改动,如果第一个有效结点算作 1 位,则pos = pos - 1;
    //寻找插入位置的前一个结点
    LNode_t *Q = PosFind_Linklist(L, pos);

    //创建结点
    LNode_t *N = (LNode_t *)malloc(sizeof(LNode_t));
    if(NULL == N){
        printf("Space request error\n");
        return -1;
    }
    N->data = value;
    N->next = NULL;


    //插入新结点
    N->next = Q->next;
    Q->next = N;
    return 0;
}

如果要是一次性成型的话可以将链表的创建和插入写在一起,如果需要后续进行增删改建议还是分开写。然后设置 mode_flag 进行快速插入、删除或者单个插入、删除。

五、链表的删除操作

  • 5.1 链表的头删法
int HeadDelete_Linklist(linklist_t *L){
    //判断参数是否正确
    if(NULL == L){
        printf("Parameter error\n");
        return -1;
    }
    if(0 == Length_Linklist(L)){           // Length_Linklist() 函数在上文“3.1 单链表中元素的个数”中
        printf("linklist is empty\n");
        return -1;
    }
    
    //头删
    int value = 0;
    LNode_t *N = L->next;
    L->next = N->next;
    N->next = NULL;
    value = N->data;
    free(N);
    N = NULL;

    return value;
}
  • 5.2 链表的尾删法
int TailDelete_Linklist(linklist_t *L){
    //判断参数是否正确
    if(NULL == L){
        printf("Parameter error\n");
        return -1;
    }
    if(0 == Length_Linklist(L)){            /// Length_Linklist() 函数在上文“3.1 单链表中元素的个数”中
        printf("linklist is empty\n");
        return -1;
    }

    //每次都要遍历到尾部结点的前一个结点
    int i = 0int num = Length_Linklist(L);          
    LNode_t *N = L;
    while(i != num-1){
        i++;
        N = N->next;
    }

    //尾删
    int value = 0;
    value = N->next->data;
    free(N->next);
    N = NULL;
    
    return value;
}
  • 5.3 任意位置删除
int PosDelete_Linklist(linklist *L, int pos){
    //如果第一个有效结点算作第 0 位则pos不用改动,如果第一个有效结点算作 1 位,则pos = pos - 1;
    //判断参数是否正确
    int num = Length_Linklist(L);           // Length_Linklist() 函数在上文“3.1 单链表中元素的个数”中
    if((pos < 0) || (pos >= num)){
        printf("Parameter error\n");
        return -1;
    }

    //遍历找到 pos 位置的前一个结点
    LNode_t *N = PosFind_Linklist(L, pos);

    //删除
    int value = 0;
    LNode_t *P = N->next;
    value = P->data;
    N->next = p->next;
    P->next = NULL;
    free(P);
    P = NULL;
    return value;
}

六、链表的逆置

归根结底就是将单链表头删/尾删的方式出单链表表,然后再头插/尾插的方式入另一个单链表。
当然一个单链表也是可以实现链表的逆置的,逆置的时候第1个结点要到表尾,可以设置一个指针一直指向这个结点,对该结点之后的结点进行删除然后头插,直到后续无结点逆置完成。

  • 6.1 单一链表
int Reverse_Linklist(linklist_t *L){
    if(NULL == L){
        printf("Parameter error\n");
        return -1;
    }
    if(NULL == L->next){
        printf("linklist is empty\n");
        return 0;
    }
    LNode_t *N = L->next;
    LNode_t *Q = N;
    while(NULL != N->next){
        Q = N->next;
        N->next = Q->next;
        Q->next = L->next;
        L->next = Q;
    }
    return 0;
}
  • 6.2 两个链表
int Reverse_Linklist(linklist **L){
    if(NULL == L || NULL == *L){
        printf("Parameter error\n");
        return -1;
    }
    if(NULL == (*L)->next){
        printf("linklist is empty\n");
        return 0;
    }
    LNode_t *N = L;
    linklist *Q = NULL;
    while(NULL != L->next){
        N = L->next;
        L->next = N->next;
        N->next = Q->next;
        Q->next = N;
    }
    (*L) = Q;
    Q = NULL;
    return 0;
}

七、链表的置空

简单来说就是将单链表除头结点之外的结点全部删除,其实现方式无非就是当链表不为空的时候就循环头删结点,直到链表为空。

int SetEmpty_Linklist(linklist_t *L){
    if(NULL == L){
        printf("Parameter error\n");
        return -1;
    }
    if(NULL == L->next){
        printf("Linklist is empty\n");
        return 0;
    }
    while(NULL != L->next){
        HeadDelete_Linklist(L);
    }
    return 0;
}

八、链表的销毁

有了上述的基础,离链表的销毁其实只差删除头结点和将头结点指针指向NULL。

int Free_Linklist(linklist_t **L){
    if(NULL == *L){
        printf("Parameter error\n");
        return -1;
    }
    if(NULL != (*L)->next){
        SetEmpty_Linklist(*L);
    }
    free(*L);
    (*L) = NULL;
    return 0; 
}
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值