单链表的操作

【1】概念

  1. 单向链表:简称单链表 。当前结点可以找到后一个结点,但是后一个结点无法找到当前结点。

  1. 链表的头结点:放在第一个有效结点之前,其数据域一般没有意义,或者存放链表长度。
    1. 头结点不是链表的必要元素
    2. 目的:为了操作统一和方便设立的,有了头结点后,在第一个元素前插入数据和删除第一个结点,就与其他结点操作一致了。
  2. 链表的头指针:指向链表第一个结点的指针。
    1. 头指针是链表的必要元素,只有知道头指针,才能找到链表。
    2. 若有头结点,则头指针指向头结点。

【2】操作单链表

1、单链表的初始化函数

注意:单链表的头结点存储这单链表的有效长度,由于单链表是数据结点的时候,单链表数据域存储数据,由此,单链表的结点是头结点还是数据结点,都是其中一种,所以在定义存储单链表结点的数据体时,使用union(联合体)定义数据域

​
/*
  4  * function:   创建一个空的单链表                                                                                     
  5  * @param [ in] 
  6  * @param [out] 
  7  * @return 返回单链表的头节点
  8  */
  9 linklist *linklist_init(void){
 10     linklist* head = (linklist*)malloc(sizeof(linklist));
 11     if(NULL == head){
 12         printf("头结点创建失败\n");
 13     }
 14     head->next = NULL;
 15     head->data.size = 0;      //链表的有效长度赋值为0
 16     return head;
 17 }

2、单链表的头插函数

 /*
  * function:    单链表的头插函数                                  
  * @param [ in] 
  * @param [out] 
  * @return      
  */
 void linklist_headadd(linklist* list, datatype e){
     linklist* pnew_node = (linklist*)malloc(sizeof(linklist));
     if(NULL == pnew_node){
         printf("申请新节点失败\n");
         return;
     }
     pnew_node->next = NULL;
     pnew_node->data.element = e;
     list->data.size++;
     pnew_node->next = list->next;
     list->next = pnew_node;
 }

3、单链表的尾插函数

在进行尾插时,我们不能直接找到尾结点,每次插入都需要循环遍历找到单链表的尾结点,时间复杂度O(n)

/*
 * function:    单链表中插入数据:尾插
 * @param [ in] 
 * @param [out] 
 * @return      
 */
void linklist_tailadd(linklist* head, datatype e){
    linklist* pnew_node = (linklist*)malloc(sizeof(linklist));
    if(NULL == pnew_node){
        printf("申请结点失败\n");
        return;
    }
    linklist* tail = head;
    for(; tail->next != NULL; tail=tail->next);
    pnew_node->next = NULL;
    pnew_node->data.element = e;
    tail->next = pnew_node;
    tail = pnew_node;
    head->data.size++;
}

4、单链表的遍历函数

当链表为空的时候,这是遍历单链表就没哟意义,遍历就是循环打印,直到遍历指针p指向空

                                     
/*
 * function:    遍历单链表
    linklist* pnode;
 * @param [ in] 
 * @param [out] 
 * @return      
 */
void linklist_traverse(linklist* head){
    if(linklist_isempty(head)){
        printf("单链表为空\n");
        return;
    }
    linklist* p = head->next;
    for(;p != NULL; p = p->next){
        printf(":%d\n", p->data.element);
    }
}

5、单链表的判空

由于在创建单链表时,创建了头结点,所以当头结点的next为NULL时,此时单链表为空。

/*
 * function:    单链表判空
 * @param [ in] 
 * @param [out] 
 * @return      
 */
int linklist_isempty(linklist* list){
    return list->next == NULL;
}

6、单链表的删除结点  头删

在删除结点时,要将头结点存储的有效数据长度减一

/*  
 * function:    单链表删除节点 头删
 * @param [ in] 
 * @param [out] 
 * @return      
 */
datatype linklist_headdel(linklist* list){
    if(linklist_isempty(list)){
        printf("单链表为空,不能删除结点\n");
        return -1;
    }
    linklist* pnode = list->next;   //待删除结点
    int res = pnode->data.element;
    list->next = pnode->next;
    pnode->next = NULL;
    list->data.size--;         //单链表有效数据-1
    free(pnode);
    return res;

7、单链表的删除结点  尾删

要尾删除结点,找到尾结点是无法就删除的,我们需要找到尾结点的前驱结点,通过前驱结点对尾结点进行删除。所以我们需要两个指针,一个遍历指针遍历单链表,找到尾结点,另一个遍历指针的前驱结点,到找到尾结点时,就知道其前驱

/*                                                                            
 * function:   单链表删除尾结点 
 * @param [ in] 
 * @param [out] 
 * @return      
 */
datatype linklist_taildel(linklist* head){
    if(linklist_isempty(head)){
        printf("单链表为空,不能删除结点\n");
        return -1;
    }
    int res;
    linklist* pretail = head;
    linklist* tail = pretail->next;
    for(; tail->next != NULL; tail = tail->next, pretail = pretail->next);
    pretail->next = NULL;
    res = tail->data.element;
    head->data.size--;
    free(tail);
    return res;
}

8、单链表的按位置插入

找到插入位置的前驱结点,当要插入结点的位置为n时,到n-1的时后就是插入位置的前驱结点。

(前置++,先加一,然后在返回其值)

/*
 * function:    按位置插入
 * @param [ in] 
 * @param [out] 
 * @return      
 */
void linklist_insertIndex(linklist* head, datatype e, int index){
    if(index < 1 || index > head->data.size + 1){  //要添加的位置超过了单链表的有效长度加1
        printf("插入位置不合法\n");
        return;
    }
    linklist* pnew_node = (linklist*)malloc(sizeof(linklist));
    pnew_node->data.element = e;
    linklist* p = head;   //插入位置前驱结点
    while(--index){       //找到插入位置前驱结点
        p = p->next;
    }
    pnew_node->next = p->next;
    p->next = pnew_node;
    head->data.size++;
}

9、单链表的按位置删除

同按位置插入逻辑相同,需要找到删除位置的前驱结点

/*
 * function:   单链表删除尾结点 
 * @param [ in] 
 * @param [out] 
 * @return                                                                 
 */
datatype linklist_taildel(linklist* head){
    if(linklist_isempty(head)){
        printf("单链表为空,不能删除结点\n");
        return -1;
    }
    int res;
    linklist* pretail = head;
    linklist* tail = pretail->next;
    for(; tail->next != NULL; tail = tail->next, pretail = pretail->next);
    pretail->next = NULL;
    res = tail->data.element;
    head->data.size--;
    free(tail);
    return res;

【3】单链表总代码

   1.  linklist.c

#include "linklist.h"

/*
 * function:   创建一个空的单链表
 * @param [ in] 
 * @param [out] 
 * @return 返回单链表的头节点
 */
linklist *linklist_init(void){
    linklist* head = (linklist*)malloc(sizeof(linklist));
    if(NULL == head){
        printf("头结点创建失败\n");
    }
    head->next = NULL;
    head->data.size = 0;      //链表的有效长度赋值为0
    return head;
}

/*
* function:    单链表的头插函数
   //linklist_traverse(phead);
* @param [ in] 
* @param [out] 
* @return      
*/
void linklist_headadd(linklist* list, datatype e){
   linklist* pnew_node = (linklist*)malloc(sizeof(linklist));
   if(NULL == pnew_node){
       printf("申请新节点失败\n");
       return;
   }
   pnew_node->next = NULL;
   pnew_node->data.element = e;
   list->data.size++;
   pnew_node->next = list->next;
   list->next = pnew_node;
}

/*
 * function:    单链表中插入数据:尾插
 * @param [ in] 
 * @param [out] 
 * @return      
 */
void linklist_tailadd(linklist* head, datatype e){
    linklist* pnew_node = (linklist*)malloc(sizeof(linklist));
    if(NULL == pnew_node){
        printf("申请结点失败\n");
        return;
    }
    linklist* tail = head;
    for(; tail->next != NULL; tail=tail->next);
    pnew_node->next = NULL;
    pnew_node->data.element = e;
    tail->next = pnew_node;
    tail = pnew_node;
    head->data.size++;
}

/*
 * function:    遍历单链表
    linklist* pnode;
 * @param [ in] 
 * @param [out] 
 * @return      
 */
void linklist_traverse(linklist* head){
    if(linklist_isempty(head)){
        printf("单链表为空\n");
        return;
    }
    linklist* p = head->next;
    for(;p != NULL; p = p->next){
        printf(":%d\n", p->data.element);
    }

}

/*
 * function:    单链表判空
 * @param [ in] 
 * @param [out] 
 * @return      
 */
int linklist_isempty(linklist* list){
    return list->next == NULL;
}

/*                                                     
 * function:    单链表删除节点 头删
 * @param [ in] 
 * @param [out] 
 * @return      
 */
datatype linklist_headdel(linklist* list){
    if(linklist_isempty(list)){
        printf("单链表为空,不能删除结点\n");
        return -1;
    }
    linklist* pnode = list->next;   //待删除结点
    int res = pnode->data.element;
    list->next = pnode->next;
    pnode->next = NULL;
    list->data.size--;         //单链表有效数据-1
    free(pnode);
    return res;
}

datatype linklist_taildel(linklist* head){
    if(linklist_isempty(head)){
        printf("单链表为空,不能删除结点\n");
        return -1;
    }
    int res;
    linklist* pretail = head;
    //linklist_traverse(phead);
    linklist* tail = pretail->next;
    for(; tail->next != NULL; tail = tail->next, pretail = pretail->next);
    pretail->next = NULL;
    res = tail->data.element;
    head->data.size--;
    free(tail);
    return res;                                                                 
}

 /*
  * function:    按位置插入
  * @param [ in] 
  * @param [out] 
  * @return                                                                                  
  */
 void linklist_insertIndex(linklist* head, datatype e, int index){
     if(index < 1 || index > head->data.size + 1){  //要添加的位置超过了单链表的有效长度加1
         printf("插入位置不合法\n");
         return;
     }
     linklist* pnew_node = (linklist*)malloc(sizeof(linklist));
     pnew_node->data.element = e;
     linklist* p = head;   //插入位置前驱结点
     while(--index){       //找到插入位置前驱结点
         p = p->next;
     }
     pnew_node->next = p->next;
     p->next = pnew_node;
     head->data.size++;
 }

/*
 * function:    按位置删除
 * @param [ in] 
 * @param [out] 
 * @return      
 */
datatype linklist_Indexdel(linklist* head, int index){
    if(linklist_isempty(head)){
        printf("单链表为空,不能删除结点\n");
        return -1;
    }
    if(index < 1 ||index > head->data.size)   //要删除的位置超过了单链表的有效长度
    {
        printf("单链表插入位置不合法\n");
        return -2;
    }
    linklist* p = head;
    while(--index){
        p = p->next;
    }
    linklist* temp = p->next;
    datatype val = temp->data.element;
    p->next = temp->next;
    free(temp);
    head->data.size--;
    return val;
}









2. linklist.h

 #ifndef __LINKLIST_H__
 #define __LINKLIST_H__
 
 #include <stdio.h>
 #include <stdlib.h>
 
 typedef int datatype;
 
 
 union dataNode{
     int size;   //头结点是存储单链表长度
     datatype element;  //当结点为有效结点时,存储数据  
 };
 
 typedef struct node{
     struct node* next;    //指针域:存储下一个节点的首地址
     union dataNode data;
 }linklist;
                                                                                 
 linklist* linklist_init();                   //初始化单链表
 void linklist_headadd(linklist*, datatype);      //单链表添加元素
 void linklist_tailadd(linklist*, datatype);   //尾插法
 int linklist_isempty(linklist*);             //单链表判空
 datatype linklist_headdel(linklist*);            //单链表元素的删除
 datatype linklist_taildel(linklist*);         //尾删
 void linklist_traverse(linklist*);           //单链表遍历
 void linklist_insertIndex(linklist*, datatype, int);  //按位置插入
 datatype linklist_Indexdel(linklist*, int);  //按位置删除
 #endif

3. main.c

#include <stdio.h>
#include "linklist.h"

int main(int argc, const char *argv[])
{
    linklist* phead = linklist_init();



    //头插向单链表中添加元素
    linklist_headadd(phead, 1);
    linklist_headadd(phead, 2);
    linklist_headadd(phead, 3);
    linklist_headadd(phead, 4);
    //找到尾结点
    linklist_tailadd(phead,  10);
    //printf("size=%d\n", phead->data.size);

    //printf("val=%d\n", phead->next->data.element);
    //printf("del:%d\n", linklist_headdel(phead));
    //printf("del:%d\n", linklist_headdel(phead));
    //printf("del:%d\n", linklist_headdel(phead));
    //printf("del:%d\n", linklist_headdel(phead));

    //linklist_traverse(phead);
    //尾删除
    //printf("tail del:%d\n",linklist_taildel(phead));
    //printf("tail del:%d\n",linklist_taildel(phead));
    //printf("tail del:%d\n",linklist_taildel(phead));

    //按位置删除
    //printf("del 3:%d\n", linklist_Indexdel(phead, 5));
    //printf("del 3:%d\n", linklist_Indexdel(phead, 0));           
    //printf("size=%d\n", phead->data.size);

    //按位置插入
    linklist_insertIndex(phead, 20, 3);
    linklist_insertIndex(phead, 21, 7);
    //linklist_insertIndex(phead, 22, 9);  //插入位置不合法
    linklist_insertIndex(phead, 66, 1);


    linklist_traverse(phead);

    return 0;
}
                                                                   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值