zoukankan      html  css  js  c++  java
  • Linux C 数据结构 ->单向链表<-(~千金散尽还复来~)

    之前看到一篇单向链表的博文,代码也看着很舒服,于是乎记录下来,留给自己~,循序渐进,慢慢

    延伸到真正的内核链表~(敢问路在何方?路在脚下~

    1. 简介

    链表是Linux 内核中最简单,最普通的数据结构。链表是一种存放和操作可变数量元素(常称为节点)

    的数据结构,链表和静态数组的不同之处在于,它所包含的元素都是动态创建并插入链表的,在编译

    时不必知道具体需要创建多少个元素,另外也因为链表中每个元素的创建时间各不相同,所以它们在

    内存中无须占用连续内存区。正是因为元素不连续的存放,所以各个元素需要通过某种方式被链接在

    一起,于是每个元素都包含一个指向下一个元素的指针,当有元素加入链表或从链表中删除元素时,

    简单调整一下节点的指针就可以了。

    根据它的特性,链表可分为:单链表双链表单向循环链表和双向循环链表,今天总结记录的就是

    最简单的单链表,

    1.1 节点类型描述

    1 typedef struct node_t {
    2     data_t data;            /* 节点数据域 */
    3     struct node_t *next;    /* 节点的后继指针域 */
    4 }linknode_t, *linklist_t;

    另一种写法

    1 struct node_t {
    2     data_t data;
    3     struct node_t *next;
    4 }
    5 typedef struct node_t linknode_t;
    6 typedef struct node_t* linklist_t;

    细看说明:

    * linknode_t A;
    * linklist_t p = &A;
    *
    * 结构变量A为所描述的节点,而指针变量p为指向此类型节点的指针(p值为节点的地址)
    * 这样看来 linknode_t 和 linklist_t 的作用是一样的,那么为什么我们要定义两个数据类
    * 型(同一种)呢?  答曰:主要为了代码的可读性,我们要求标识符要望文识义,便于理解
    *
    * linknode_t *pnode  指向一个节点
    * linklist_t list  指向一个整体

    1.2 头节点 head (~黄河之水天上来~)

    在顺序存储线性表,如何表达一个空表{ },是通过list->last = -1来表现的,所谓的空表就是

    数据域为NULL,而链表有数据域和指针域,我们如何表现空链表呢?这时,就引入了头结点

    的概念,头结点和其他节点数据类型一样,只是数据域为NULL,head->next = NULL,下面

    我们看一个创建空链表的函数,如何利用头结点来创建一个空链表,只要头节点在,链表就还在~

     1 // 创建一个空链表
     2 linklist_t 
     3 CreateEmptyLinklist()
     4 {
     5     linklist_t list;
     6     list = (linklist_t)malloc(sizeof(linknode_t));
     7     
     8     if(NULL != list) {
     9         list->next = NULL;
    10     }
    11     
    12     return list;
    13 }

    2. 链表基本运算的相关"算法"操作 or 操刀(~烹羊宰牛且为乐,会须一饮三百杯~)

     链表的运算除了上面的创建空链表,还有数据的插入,删除,查找等函数,链表的运算有各种实现方

    法,如何写出一个高效的,封装性较好的函数是我们要考虑的,比如数据插入函数,我们就要尽可能

    考虑所有能出现的结果,比如:1)如果需插入数据的链表是个空表;2)所插入的位置超过了链表的

    长度;如果我们的函数能包含所有能出现的情况,不仅能大大提高我们的开发效率,也会减少代码的

    错误率。下面看一个可用性较强的链表插入操作的函数实现~

    int 
    InsertLinklist(linklist_t list, int at, data_t x)
    {
        linknode_t * node_prev, * node_at, * node_new;
        int pos_at;
        int found = 0;
    
        if (NULL == list) return -1;
    
        /* at must >= 0  */
        if (at < 0) return -1;
        
        /*第一步、新节点分配空间*/
        node_new = (linklist_t)malloc(sizeof(linknode_t));
        if (NULL == node_new) {
            return -1;
        }
        node_new->data = x; /* assigned value */
        /*
         *节点如果插入超过链表长度的位置,会接到尾节点后面,
         *这样,node_new成了尾节点,node_new->next = NULL 
        */
        node_new->next = NULL; 
    
        /*第二步、定位*/
        node_prev = list; //跟随指针,帮助我们更好的定位
        node_at = list->next; //遍历指针
        pos_at = 0;
        while(NULL != node_at) {
            if(pos_at == at){
                found = 1; //找到正确的位置,跳出循环
                break;            
            }
            /* move to the next pos_at */
            node_prev = node_at;        //跟随指针先跳到遍历指针的位置
            node_at = node_at->next;    //遍历指针跳到下一个节点的位置
            pos_at++;
        }
    
        /*第三步、插入*/    
        if(found) {
            /* found = 1,找到正确的位置,插入  */
            node_new->next = node_at;   //插入的节点next指向node_at
            node_prev->next = node_new; //插入节点的前一个节点
        } 
        else {
            /*若是没找到正确的位置,即所插入位置超越了链表的长度,
             *则接到尾节点的后面,同样,这样适用于{ }即空链表,这样
             *我们可以建立一个空链表,利用这个函数,实现链表的初始化
             */
            node_prev->next = node_new;
        }
        
        return 0;
    }

    3. 正文开始 Demo(~与君歌一曲,请君为我倾耳听~)

    listlink.h

     1 #ifndef _LIST_LINK_H_
     2 #define _LIST_LINK_H_
     3 
     4 typedef int data_t;
     5 
     6 typedef struct node_t {
     7     data_t data;            /* 节点数据域 */
     8     struct node_t * next;    /* 节点的后继指针域 */
     9 }linknode_t, * linklist_t;
    10 
    11 
    12 
    13 /* 链表操作函数*/
    14 
    15 // 创建一个空链表
    16 linklist_t CreateEmptyLinklist();
    17 
    18 // 销毁链表
    19 void DestroyLinklist(linklist_t list);
    20 
    21 // 清空链表
    22 void ClearLinklist(linklist_t list);
    23 
    24 // 是否为空链表
    25 int IsEmptyLinklist(linklist_t list);
    26 
    27 // 链表长度
    28 int LengthLinklist(linklist_t list);
    29 
    30 // 获去链表节点数据
    31 int GetLinklist(linklist_t list, int at, data_t * x);
    32 
    33 // 设置链表节点数据
    34 int SetLinklist(linklist_t list, int at, data_t x);
    35 
    36 // 插入节点
    37 int InsertLinklist(linklist_t list, int at, data_t x);
    38 
    39 // 删除节点
    40 int DeleteLinklist(linklist_t list, int at);
    41 
    42 // 链表转置
    43 linklist_t ReverseLinklist(linklist_t list);
    44 
    45 // 打印链表
    46 int Display(linklist_t list);
    47 
    48 
    49 #endif // _LIST_LINK_H_

    listlink.c

      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include "listlink.h"
      4 
      5 // 创建一个空链表
      6 linklist_t 
      7 CreateEmptyLinklist()
      8 {
      9     linklist_t list;
     10     list = (linklist_t)malloc(sizeof(linknode_t));
     11     
     12     if(NULL != list) {
     13         list->next = NULL;
     14     }
     15     
     16     return list;
     17 }
     18 
     19 // 销毁链表
     20 void 
     21 DestroyLinklist(linklist_t list)
     22 {
     23     if(NULL != list) {
     24         ClearLinklist(list);
     25         free(list);
     26     }   
     27 }
     28 
     29 // 清空链表
     30 void 
     31 ClearLinklist(linklist_t list)
     32 {
     33     linknode_t * node; /* pointer to the node to be remove */
     34     if(NULL == list) return;
     35     
     36     while(NULL != list->next) {
     37         node = list->next;
     38         list->next = node->next; //此时node->next是第二node节点元素依次往后
     39         free(node);
     40     }
     41     return;
     42 }
     43 
     44 // 是否为空链表
     45 int 
     46 IsEmptyLinklist(linklist_t list)
     47 {
     48     if(NULL != list) {
     49         if(NULL == list->next)  // 只有头节点
     50             return 1; // 返回1,是个空链表
     51         else 
     52             return 0; // 返回0,链表非空
     53         
     54     } else 
     55         return -1;    //  返回-1, 错误的类型
     56 }
     57 
     58 // 链表长度
     59 int 
     60 LengthLinklist(linklist_t list)
     61 {
     62     int len = 0;
     63     linknode_t * node; // 遍历指针
     64     
     65     if(NULL == list) return -1;
     66     
     67     node = list->next; // node指针指向第一个节点
     68     while(NULL != node) {
     69         len++;
     70         node = node->next;
     71     }
     72     
     73     return len;
     74 }
     75 
     76 // 获去一个链表指定节点数据域的数据值
     77 int 
     78 GetLinklist(linklist_t list, int at, data_t * x)
     79 {
     80     linknode_t *node;   // 遍历节点的指针
     81     int pos;            // 用于遍历比较
     82     
     83     if(NULL == list) return -1;
     84     /*at 必须要 >= 0*/
     85     if(at < 0) return -1;
     86     
     87     /* 从第一个元素开始 */
     88     node = list->next; // node指针指向一个元素
     89     pos = 0;
     90     while(NULL != node) {
     91         if(at == pos) {
     92             if(NULL != x) 
     93                 *x = node->data;
     94             return 0;
     95         }
     96         // 下一个元素
     97         node = node->next;
     98         pos++;
     99     }
    100     return -1;
    101 }
    102 
    103 // 设置一个指定链表节点的数据域值
    104 int 
    105 SetLinklist(linklist_t list, int at, data_t x)
    106 {
    107     linknode_t * node; // 遍历链表
    108     int pos;
    109     int found = 0;
    110     
    111     if(!list) return -1;
    112     /*at 必须 >= 0*/
    113     if(at < 0) return -1;
    114     
    115     /* node指针指向第一个元素 */
    116     node = list->next;
    117     pos = 0;
    118     while(NULL != node) {
    119         if(at == pos) {
    120             found = 1; // 找到了位置
    121             node->data = x;
    122             break;
    123         }
    124         /*往后移动元素*/
    125         node = node->next;
    126         pos++;
    127     }
    128     if(1 == found)
    129         return 0;
    130     else
    131         return -1;
    132 }
    133 
    134 // 插入节点
    135 int 
    136 InsertLinklist(linklist_t list, int at, data_t x)
    137 {
    138     /* 
    139      * node_at and pos_at are used to locate the position of node_at.
    140      * node_prev follows the node_at and always points to previous node 
    141      *    of node_at.
    142      * node_new is used to point to the new node to be inserted.
    143      */
    144     linknode_t      * node_prev, * node_at, * node_new;
    145     int             pos_at;
    146     int             found = 0;
    147     
    148     if(NULL == list) return -1;
    149     
    150     /* at 必须 >= 0 */
    151     if(at < 0) return -1;
    152     
    153     node_new = malloc(sizeof(linknode_t));
    154     if(NULL == node_new)
    155         return -1;
    156     node_new->data = x; // assigned value
    157     node_new->next = NULL;
    158     
    159     node_prev = list; // head
    160     node_at = list->next; //node_at指针指向第一元素
    161     pos_at = 0;
    162     while(NULL != node_at) {
    163         if(pos_at == at) {
    164             found = 1; // found the node ‘at'
    165             break;
    166         }
    167         /* move to the next pos_at */
    168         node_prev = node_at;
    169         node_at = node_at->next;
    170         pos_at++;    
    171     }
    172     
    173     if(found) {
    174         /* insert */
    175         node_new->next = node_at;
    176         node_prev->next = node_new;
    177     } else{
    178         /*
    179          * If not found,means the provided 'at'
    180          * exceeds the upper limit of the list, just
    181          * append the new node to the end of the list
    182          */
    183         node_prev->next = node_new;   
    184     }
    185     
    186     return 0;
    187 }
    188 
    189 // 删除节点
    190 int 
    191 DeleteLinklist(linklist_t list, int at)
    192 {
    193    /* 
    194     * node_at and pos_at are used to locate the position of node_at.
    195     * node_prev follows the node_at and always points to previous node 
    196     *    of node_at.
    197     */ 
    198     linknode_t      * node_prev, * node_at;
    199     int             pos_at;
    200     int             found = 0;
    201     
    202     if(!list) return -1;
    203     if(at < 0) return -1;
    204     
    205     node_prev = list; // node_prev指针指向链表头
    206     node_at = list->next; // node_at指针指向第一元素
    207     pos_at = 0;
    208     
    209     while(NULL != node_at) {
    210         if(pos_at == at) {
    211             // found the node 'at'
    212             found = 1;
    213             break;
    214         }
    215         // move to the next pos_at
    216         node_prev = node_at;
    217         node_at = node_at->next;
    218         pos_at++;    
    219     }
    220     if(found) {
    221         // remove 
    222         node_prev->next = node_at->next;
    223         free(node_at); 
    224         return 0;
    225     }else
    226         return -1;   
    227 }
    228 
    229 // 链表转置
    230 linklist_t 
    231 ReverseLinklist(linklist_t list)
    232 {
    233     linknode_t * node;       // iterator
    234     linknode_t * node_prev;  // previous node of iterator
    235     linknode_t * node_next;  /* next node of iterator
    236                              * used to backup next of iterator
    237                              */
    238     if(NULL == list) return NULL;
    239     node_prev = NULL;
    240     node = list->next; // node指针指向第一个元素
    241     while(NULL != node) {
    242         /*
    243          * step1: backup node->next
    244          * due to the next of iterator will be
    245          * modified in step2
    246          */
    247         node_next = node->next;
    248         /* 
    249          * when iterator reaches the last node 
    250          * of original list, make the list head
    251          * point to the last node, so the original
    252          * last one becomes the first one.
    253          */
    254         if(NULL == node_next)
    255             list->next = node;
    256         /* 
    257          * step2: reverse the linkage between nodes
    258          * make the node pointer to the previous node,
    259          * not the next node
    260          */        
    261         node->next = node_prev;
    262         /* 
    263          * step3: move forward 
    264          */
    265         node_prev = node;
    266         node = node_next;
    267     }
    268     
    269     return list;
    270 }
    271 
    272 // 打印链表
    273 int
    274 Display(linklist_t list)
    275 {
    276     linknode_t * node;
    277     
    278     if(NULL == list) return -1;
    279     
    280     node = list->next;
    281     while(node != NULL) {
    282         printf(" %d ", node->data);
    283         node = node->next;
    284     } 
    285     printf("
    ");
    286     
    287     return 0;
    288 }
    289 
    290 
    291 int main(int argc, char * argv[])
    292 {
    293     int i;
    294     data_t x;
    295     linklist_t p;
    296     
    297     /*创建链表*/
    298     p = CreateEmptyLinklist();
    299     Display(p);
    300     data_t a[10] = {1,3,5,7,9,11,13,15,17,19};
    301     
    302     for(i = 0; i < 10; i++) {
    303         /*插入链表*/
    304         InsertLinklist(p, i, a[i]);
    305     }
    306     Display(p);
    307     
    308     /*链表转置*/
    309     ReverseLinklist(p);
    310     /*链表长度*/
    311     printf("The length of the list is [%d]
    ", LengthLinklist(p));
    312     Display(p);
    313     
    314     /*获取特定节点值*/
    315     GetLinklist(p, 4, &x);
    316     printf("The No.4 of this list is [%d]
    ", x);
    317     
    318     /*设置特定节点的值*/
    319     SetLinklist(p, 4, 100);
    320     GetLinklist(p, 4, &x);
    321     printf("After updating! The No.4 of this list is [%d]
    ", x);
    322     Display(p);
    323     
    324     /*删除节点*/
    325     DeleteLinklist(p,5);
    326     printf("After delete!The length of list is [%d]
    ", LengthLinklist(p));
    327     Display(p);
    328     
    329     /*清空链表*/
    330     ClearLinklist(p);
    331     if(IsEmptyLinklist(p)) 
    332         printf("This list is empty!
    ");
    333     /*销毁链表*/
    334     DestroyLinklist(p);
    335     printf("This list is destroyed!
    ");
    336     
    337    
    338     return 0;
    339     
    340 }

    运行

     

    4. 鸣谢你动了谁的奶酪? ^_^)

    感谢下面博主的共享,本文的基石,谢谢!!
    
    感谢:https://blog.csdn.net/zqixiao_09/article/details/50402523

    5. 后记

    行歌

    在草长莺飞的季节里喃喃低唱 

    到处人潮汹涌还会孤独

    怎么

    在灯火阑珊处竟然会觉得荒芜

    从前轻狂绕过时光        

  • 相关阅读:
    pku 1330 LCA
    最近公共祖先(LCA)的Tarjan算法
    字典数模板
    字典树
    RMQ问题ST算法与模板
    RMQ(Range Minimum/Maximum Query)问题:
    罗穗骞的dc3和倍增法
    多重背包问题
    tarjan LCA 算法
    终极工程师的造诣 – 不迷信高科技的乔布斯给我们的启示
  • 原文地址:https://www.cnblogs.com/zhaoosheLBJ/p/9277744.html
Copyright © 2011-2022 走看看