数据结构2.带控制信息的链表

来源:互联网 发布:汽车行业数据库 编辑:程序博客网 时间:2024/06/06 07:25

引言

在上一篇博客数据结构1.单链表中,我们对链表相对数组的优缺点进行了比较。
而且,我们发现,链表是一种物理存储单元上非连续、非顺序的存储结构,所以,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。
若是想要对链表中的其中一个元素进行操作,通常都需要p_node = p_node->next;的循环进行。

计算机科学领域的任何问题,都可以通过增加一个间接中间层来解决。
Any problems in computer science can be soved by another layer of indirection.

所以,我们在定义这个链表的时候,可以给链表加上一层控制信息。如下:

* 本文的所有源程序可以在single_control_list上得到。*

一、链表数据类型的定义

/*链表数据类型定义*/struct List_node;    //声明一个类型//链表控制信息typedef struct List{    struct List_node *head;    //指向链表头部    struct List_node *tail;    //指向链表尾部    int              count;    //链表节点数量}List;//链表节点信息typedef struct List_node{    int               data;    //数据域    struct List_node *next;    //指针域}List_node;

这样我们可以看到,已经可以直接获取到链表节点个数以及指向尾部节点的指针。我们可以用来做什么呢?

二、链表的接口:

/********链表的接口********/List      *init_list(void)                                 ;    //1.  链表的初始化void      destroy_list(List **list)                        ;    //2.  链表的销毁Boolean   push_front(List *list, int value)                ;    //3.1 头部插入Boolean   push_back(List  *list, int value)                ;    //3.2 尾部插入Boolean   pop_front(List *list)                            ;    //4.1 头部删除Boolean   pop_back(List *list)                             ;    //4.2 尾部删除List_node *find_node(List *list, int value)                ;    //5.1 链表的查找List_node *find_revise_node(List *list, int num)           ;    //5.2 找到链表的倒数第num个节点List_node *find_mid_node(List *list)                       ;    //5.3 找到链表的中间节点void      modify_node(List *list, int value)               ;    //6.1 链表节点的修改Boolean   insert_node(List *list, int index, int value)    ;    //6.2 链表节点的插入(下标)void      delete_one_node(List *list, List_node *node)     ;    //6.3 在O(1)的时间复杂度删除节点void      sort_list_ascend(List *list)                     ;    //7.1 升序void      sort_list_descent(List *list)                    ;    //7.2 降序排列void      show_list(List *list)                            ;    //8.1 显示链表信息void      reverse_show_list(List *list)                    ;    //8.2 逆序输出链表信息int       get_list_count(List *list)                       ;    //9.  得到链表节点数量List      *merge_two_lists(List *list1, List *list2)       ;    //10.  合并两个有序链表List      *merge_two_lists_recure(List *list1, List *list2);    //11.  合并两个有序链表(递归)List      *reverse_list(List *list)                        ;    //12.  逆置一个链表List      *list_dump(List *list)                           ;    //13.  链表的拷贝Boolean   is_list_intersect(List *list1, List *list2)      ;    //14.  判断链表是否有交点List_node *get_first_common_node(List *list1, List *list2) ;    //15.  得到第一个交点Boolean   has_circle(List *list)                           ;    //16.  判断一个链表是否有环List_node *find_circle_first_node(List *list)              ;    //17.  找到带环链表的环入口节点

我们将这些都放入ctl_list.h文件当中,包括链表的定义,链表的接口,一些用到的宏定义。

为了能够方便的进行程序的设计,我们仍旧采用包裹函数的方式对某些操作进行简化:

/*接口包裹函数定义*/static void *Malloc(size_t size);static List_node *Create_node(void); static void Swap(void *a, void *b, int length);static void Rev_show_list(List_node *node);

我们常用到的操作函数定义如下:

/*接口包裹函数实现*/static void *Malloc(size_t size){    void *result = malloc(size);    if(result == NULL){        fprintf(stderr, "the memory is full\n");        exit(1);    }    return result;}static List_node *Create_node(void)    //创建链表节点{    List_node *node = (List_node *)Malloc(sizeof(List_node));    bzero(node, sizeof(List_node));    return node;}static void Swap(void *a, void *b, int length){    void *temp = Malloc(length);    memcpy(temp, a, length);    memcpy(a, b, length);    memcpy(b, temp, length);    free(temp);}static void Rev_show_list(List_node *node){   //要打印当前节点,先打印其后续节点   if(node != NULL){       rev_show_list(node->next);       printf("%d ", node->data);       }}

宏定义如下:

/*控制链表宏定义*/#define TRUE               (1)#define FALSE              (0)#define ZERO               (0)#define ONLY_ONE           (1)#define TWO                (2)#define get_data_size()    ((unsigned long)&(((List_node *)0)->next)) 

三、链表接口的实现

//1.链表的初始化//      | head | tail | count |//          \      /      0//           \    ///            NULLList      *init_list(void)                            {    List *list = (List *)Malloc(sizeof(List));    bzero(list, sizeof(List));    //全部清零    return list; }
//2. 链表的销毁//              list//             ///         list_head  //          ///      | head | tail | count |//          \         \   n + 1//             //            \          \//            node->node2->node3void      destroy_list(List **list)                   {    if(list == NULL || *list == NULL){        return ;    }    //  链表删除步骤:    //  1.删除链表节点信息;    while((*list)->count){   //头部删除链表节点        pop_front(*list);    }    //  2.删除链表控制信息.    free(*list);    *list = NULL;}
//3.1 头部插入Boolean   push_front(List *list, int value)           {    List_node *node = NULL;    if(list == NULL){         return FALSE;    }       //创建节点并赋值    node = create_node();    node->data = value;    //   case1:    //      | head | tail | count |    //          \      /      1    //           \    /    //            node    if(list->count == ZERO){        list->tail = node;    }else{    //   case2:    //      | head |              tail | count |    //          \                 /       n + 1    //           \               /    //            node->node2->node3        node->next = list->head;    }    list->head = node;    list->count++;    return TRUE;}
//3.2 尾部插入Boolean   push_back(List  *list, int value)           {    List_node *node = NULL;    if(list == NULL){        return FALSE;    }    node = create_node();    node->data = value;    //   case1:    //      | head | tail | count |    //          \      /      1    //           \    /    //            node    if(list->count == ZERO){       list->head = list->tail = node;    }else{        //   case2:        //      | head |              tail | count |        //          \                 /       n + 1        //           \               /        //            node1->node2->node        list->tail->next = node;        list->tail = node;    }    list->count++;    return TRUE;}
//4.1 头部删除Boolean   pop_front(List *list)                       {    List_node *p_node = NULL;    if(list == NULL || list->count == ZERO){        return FALSE;    }    //   case1:    //      | head | tail | count |    //          \      /      1->0    //           \    /    //            node    //   case2:    //      | head |              tail | count |    //          \                 /       n-> n - 1    //           \               /    //            node1->node2->node3    p_node = list->head;    if(list->count == ONLY_ONE){        list->head = list->tail = NULL;     }else{        list->head = list->head->next;    }    free(p_node);    list->count--;    return TRUE;}
//4.2 尾部删除Boolean   pop_back(List *list)                        {    List_node *p_node = NULL;    if(list == NULL || list->count == ZERO){         return FALSE;    }    //   case1:    //      | head | tail | count |    //          \      /      1->0    //           \    /    //            node    //   case2:    //      | head |              tail | count |    //          \                 /       n-> n - 1    //           \               /    //            node1->node2->node3    p_node = list->head;     if(list->count == ONLY_ONE){            list->head = list->tail = NULL;        free(p_node);    }else{        //判断倒数第二个?        //   p_node->next == list->tail        while(p_node->next != list->tail){            p_node = p_node->next;        }        free(list->tail);        list->tail = p_node;        p_node->next = NULL;    }    list->count--;    return TRUE;}
//5.1 链表的查找List_node *find_node(List *list, int value){    if(list == NULL)    {        return NULL;    }    List_node *p = list->head;    while(p != NULL)    {        if(p->data == value)        {            printf("found the value: %d\n", value);            return p;        }        p = p->next;    }    printf("No found!");    return NULL;}
//5.2 找到链表的倒数第num个节点List_node *find_revise_node(List *list, int num)   {    List_node *move = NULL;    int move_count = 0;    //move + num == count    if(list == NULL || num <= 0 || num > list->count){        return NULL;    }        move = list->head;    //移动的步长    move_count = list->count - num;    while(move_count--){        move = move->next;    }    return move;}
//5.3 找到链表的中间节点    // 1.) 快慢指针:    //    //       f  每次移动2步, s 每次移动1步    //       10  23  5  15  50  67  45  32  82    //    // 2.)   10 /  2 == 5    //       10 >> 1 == 5    //    //       0000 1010   10    //            10 >> 1    //       0000 0101   5List_node *find_mid_node(List *list)    {    List_node *move = NULL;    int move_count = 0;    if(list == NULL){        return NULL;    }    move = list->head;    move_count = list->count >> 1;    while(move_count--){        move = move->next;    }    return move;}
//6.1 链表节点的修改void modify_node(List *list, int index, int value){    int count = index;    List_node *p = list->head;    if(list == NULL || index >= list->count || index < 0)    {        return ;    while(count--)    {        p = p->next;    }    p->data = value;}
//6.2链表节点的插入Boolean insert_node(List *list,int index, int value){    if(list == NULL || index > list->count || index < 0)    {        return FALSE;    }    //在最后则尾部插入    if(index == list->count)    {        return push_back(list, value);    }    //在最前则头部插入    if(index == 0)    {        return push_front(list, value);    }    int count =  index -1;    List_node *p_node = list->head;    //创建新节点    List_node *node = create_node();    node->data = value;    //寻找需插入的节点位置    while(count--)    {        p_node = p_node->next;    }    //进行插入    node->next = p_node->next;    p_node->next = node;    list->count++;    return TRUE;}
//6.3 在O(1)的时间复杂度删除节点    //由于是单链表,无法获取到前一个节点的位置,    //所以,我们在这里用后一个节点代替我们所需的节点。void      delete_one_node(List *list, List_node *node)   {    if(list == NULL || node == NULL)    {        return ;    }    //尾部删除    if(node == list->tail)    {        pop_back(list);        return ;    }    List_node *p = node->next;//保存下一个节点位置    node->data = node->next->data; //这个节点作为后一个的替代    node->next = node->next->next; //直接指向下一个节点的后面    free(p);    list->count--;}
//7.1 升序排列 //我们在这里用到了一种新的办法求我们的结构体大小,定义在宏里: // ((unsigned long)&(((List_node *)0)->next)) //     //                    (List_node *)0          //指向首地址在0的List_node的指针 //                   ((List_node *)0)->next)  //取next则指向了它的下一个 //                 &(((List_node *)0)->next)) //取地址,得到的是这个地址,由于首地址,则正好是大小 // ((unsigned long)&(((List_node *)0)->next)) //unsigned long 类型转换,得到就是大小 // sizeof(List_node) - sizeof(List_node *);   //内存分配,结构体里内存对齐现象:    // int               data;  // 0   1   2   3    //                          // 4   5   6   7    // struct List_Node *next;  // 8   9  10  11    //                            12  13  14  15void      sort_list_ascend(List *list)                {    List_node *p_node = NULL;    List_node *q_node = NULL;    unsigned long data_size = 0;    if(list == NULL || list->count < TWO){        return ;    }    data_size = get_data_size();    //求得数据区域得大小    for(p_node = list->head; p_node->next; p_node = p_node->next){        for(q_node = p_node->next; q_node ; q_node = q_node->next){            if(p_node->data > q_node->data){                 swap(p_node, q_node, data_size);                            }        }    }}
//7.2 降序排列  //和升序类似void      sort_list_descend(List *list)               {    List_node *p_node = NULL;    List_node *q_node = NULL;    unsigned long data_size = 0;    if(list == NULL || list->count < TWO){        return ;    }    data_size = get_data_size();    //求得数据区域得大小    for(p_node = list->head; p_node->next; p_node = p_node->next){        for(q_node = p_node->next; q_node ; q_node = q_node->next){            if(p_node->data < q_node->data){                 swap(p_node, q_node, data_size);                            }        }    }}
//8.1 显示链表信息void      show_list(List *list)                       {    List_node *p_node = NULL;    if(list != NULL && list->count != ZERO){         for(p_node = list->head; p_node; p_node = p_node->next){             printf("%d ", p_node->data);        }        printf("\n");    }}
//8.2 逆序输出链表信息void      reverse_show_list(List *list)    {    if(list == NULL || list->count == ZERO){        return ;    }      // .h中定义的递归(先递归再输出)    Rev_show_list(list->head);    printf("\n");}
//9 得到链表节点数量  //这是一个有控制信息的链表,有关于节点数量的变量int       get_list_count(List *list)                  {    if(list == NULL){         return -1;    }    return list->count;}
///10 合并两个有序链表    //l1    | head |              tail | count |    //          \                 /       n-> n - 1    //           \               /    //            node1->node2->node3    //    //    //l2    | head |              tail | count |    //          \                 /       n-> n - 1    //           \               /    //            node1->node2->node3    //List      *merge_two_lists(List *list1, List *list2)    {    List *result = NULL;    List_node *list1_move = NULL;       List_node *list2_move = NULL;       if(list1 == NULL || list2 == NULL){        return result;    }    result = init_list();   //结果链表得初始化    list1_move = list1->head;    list2_move = list2->head;    //如果两个链表都没有遍历完,进行比较    while(list1_move != NULL && list2_move != NULL){        if(list1_move->data <= list2_move->data){            push_back(result, list1_move->data);            list1_move = list1_move->next;        }else{            push_back(result, list2_move->data);            list2_move = list2_move->next;        }    }    //当两个链表中任何一个遍历结束,则把另外一个进行尾部添加    while(list2_move != NULL){        push_back(result, list2_move->data);        list2_move = list2_move->next;    }    while(list1_move != NULL){        push_back(result, list1_move->data);        list1_move = list1_move->next;    }    return result;}
//11//合并两个有序链表(递归)    //       //l1            node1->node2->node3    //    //     l->node1->node4...    //        //l2            node4->node5->node6    //List      *merge_two_lists_recure(List *list1, List *list2)    {    List *node = NULL;    List *l  = init_list();    List *l1 = list1;    List *l2 = list2;    if(l1->count == 0)    {        return list2;    }    if(l2->count == 0)    {        return list1;    }    //若l1当前head满足较小情况,将l指向l1,让l1指向l1的下一个节点,进入递归比较l1->head->next形成的链表与l2再进行比较,返回的node链表是后面节点的新链表;    if(l1->head->data < l2->head->data)    {        l->head = l1->head;        l1->head = l1->head->next;        l1->count --;        node = merge_two_lists_recure(l1, l2);    }    else    {        l->head = l2->head;        l2->head = l2->head->next;        l2->count--;        node = merge_two_lists_recure(l1, l2);    }    //完成递归后,l指向node组成的新链表;    l->head->next = node->head;    l->count = node->count +1;    return l;}
//12 逆置一个链表    //因为会改变结构,为了不失去对后面的节点的信息,使用了三个指针,交换前两个,不丢失第三个 。    //    //      | head |              tail | count |    //          \                   \      n-> n - 1    //           \                   \    //            node1->node2->node3->node4    //             |      |       |    //             p      q       m    //    //            node1<-node2 node3->node4    //             |      |       |      |    //             p      q       m      |    //                    p       q      m       //List      *reverse_list(List *list)    {    List_node *p_node = NULL;    List_node *q_node = NULL;    List_node *m_node = NULL;    if(list == NULL || list->count < TWO){        return list;    }     //两个节点    if(list->count == TWO){        list->tail->next = list->head;        list->head->next = NULL;    }else{    //三个节点        p_node = list->head;        q_node = p_node->next;        m_node = q_node->next;        p_node->next = NULL;        do{            q_node->next = p_node;   //让中间指针的next指向前一个            p_node = q_node;    //指针整体向后搬移            q_node = m_node;            m_node = m_node->next;        }while(m_node != NULL);        q_node->next = p_node;    }    //交换头尾指针    swap(&(list->head), &(list->tail), sizeof(List_node *));    return list;}
//13 链表的拷贝  //使用了尾插法依次将元素插入List      *list_dump(List *list)    {    List * new_list = init_list();    List_node * node = list->head;    for(node = list->head; node; node = node->next)    {        push_back(new_list, node->data);    }    return new_list;}
//14 判断链表是否有交点  //若两个链表有交点,则尾节点一定是同一个。  //  //  l1-> => => =>  //                \  //                 \       //                 /  => => => => => =>  //  l2-> => => =>   //Boolean   is_list_intersect(List *list1, List *list2)    {    if(list1 == NULL || list2 == NULL){        return FALSE;    }//        if( list1->tail == list2->tail)    {        printf("intersect\n");        return TRUE;    }    else    {        printf("nothing\n");        return FALSE;    }}
//15 得到第一个交点  //             l1 =>  //                    \       //                    /   => => => => => =>  //     l2 => => => =>   //     长度不等,先获得两个链表都离相遇点长度相等的地方开始依次比较。List_node *get_first_common_node(List *list1, List *list2)    {    int list1_len = 0;    int list2_len = 0;    int distance = 0;    List_node *p_node = NULL;    List_node *q_node = NULL;    if(!is_list_intersect(list1, list2)){    //判断两个链表是否有交点        return NULL;    }    list1_len = list1->count;    list2_len = list2->count;    p_node = list1->head;    q_node = list2->head;    //判断较长链表并首先进行移动    if(list1_len >= list2_len){        distance = list1_len - list2_len;        while(distance--){             p_node = p_node->next;        }    }else{        distance = list2_len - list1_len;        while(distance--){            q_node = q_node->next;        }    }    //依次对对应节点进行判断是否相等,如果相等则是第一个相交节点    while(p_node != q_node){        p_node = p_node->next;        q_node = q_node->next;    }    return p_node;}
//16 判断一个链表是否有环  //  //   - - - 、    //          \    - - - - 、  //           \ /          |  //            \          /  //             ` - - - -  //Boolean has_circle(List *list)                                {    List_node *p = list->head;  //slow    List_node *q = list->head;  //fast    while(p != NULL && q != NULL && q->next !=NULL)    {        p = p->next;        //一次一步        q = q->next->next;  //一次两步        //如果有环,则一定会相遇        if(p == q)        {            printf("we found circle!\n");            return TRUE;        }    }    printf("no circle!\n");    return FALSE;}
//17 找到带环链表的环入口节点  //  //   - - - 、    //          \    - - - - 、  //           \ /          |  //            \          /  //        入口点 ` - -*- -  //                   \  //                     相遇点  //            |<-x->|  //  |<---a--->|<-----r---->|  //  |<----------L--------->|  // 我们设整个链表长L,入口点与相遇点长x,起点到入口点长度为a;  // 快指针走的长度是慢指针的2倍,由于快指针可能不止走了一圈;  // 慢指针走了s步,即快指针走了2s步。  //  // ∵ 2s = s + nr ;  // ∴ s = nr;  //  //   a + x = nr  //   a + x = nr = (n-1)r + r =  (n-1)r + L -a  //   a = (n-1)r + (L -a -x)  //   链表从头到环入口点长度 = (n -1)环长 + 相遇点到环入口长度  //   所以, 从链表头和相遇点各设一个一步长的指针,必定会在相遇点回合。  //List_node *find_circle_first_node(List *list)                  {    List_node * entry = NULL;    if(!has_circle(list))    {        return NULL;    }    List_node *p = list->head;  //slow    List_node *q = list->head;  //fast    while(p != NULL && q != NULL && q->next !=NULL)    {        p = p->next;        q = q->next->next;        if(p == q)        {            break;    //相遇点p和q        }    }    //p为相遇点开始    q = list->head;   //q从链表头开始    while(q != p)    {        p =p->next;        q =q->next;    }    printf("we found the node is %d\n", q->data);    return q;}

四、测试功能

测试代码:

#include "list.h"int main(){    int i = 0;    int num = 0;    List *list = NULL;    List *list1 = NULL;    List *list2 = NULL;    List_node * node = NULL;    printf("1.init_list:\n");    list = init_list();    list1 = init_list();    list2 = init_list();    printf("3.1 list1 push_front :\n");    for(i = 0; i < 6; i++)    {        push_front(list1, rand()%100);    }    show_list(list1);    printf("3.2 list2 push_back:\n");    for(i = 0; i < 4; i++)    {        push_back(list2, rand()%100);    }    show_list(list2);    printf("4.1 list1 pop_front:\n");    pop_front(list1);    show_list(list1);    printf("4.2 list2 pop_back:\n");    pop_back(list2);    show_list(list2);    printf("7.1 sort_list1_ascend:\n");                       sort_list_ascend(list1);                      show_list(list1);    printf("7.2 sort_list2_descend:\n");                       sort_list_descend(list2);                      show_list(list2);    printf("12 reverse_list2:\n");    reverse_list(list2);    show_list(list2);#if 0    printf("10. list : merge_two_lists(list1, list2):\n");    list = merge_two_lists(list1, list2);    show_list(list);#endif#if 1    printf("11. list : merge_two_lists_recure(list1, list2):\n");    list = merge_two_lists_recure(list1, list2);    show_list(list);#endif    printf("8.1 show_list(list);\n");          show_list(list);    printf("8.2 reverse_show_list(list);\n");    reverse_show_list(list);#if 1     printf("find_node:\n");    printf("which one?(value to find)\n");     scanf("%d",&num);    printf("found num of list is :%d \n",find_node(list, num)->data);#endif#if 1    printf("find_revise_node:\n");         printf("which one index ?(reverse)\n");     scanf("%d",&num);    printf("reverse num of list is :%d \n",find_revise_node(list, num)->data);#endif    printf("5.3 find_mid_node(list):\n") ;    printf("%d \n",find_mid_node(list)->data);    printf("9. get_list_count:\n");     printf("the count of list is :%d\n", get_list_count(list));#if 1      printf("6.3 delete_one_node:");    printf("which one?(to delete)\n");     scanf("%d",&num);    node = create_node();    node = find_node(list,num);    delete_one_node(list, node);    show_list(list);#endif#if 1    printf("6.1 modify_node :\n");    modify_node(list, 1, 26);    show_list(list);#endif#if 1    printf("6.2 insert_node: \n");    insert_node(list, 2, 18);    show_list(list);#endif #if 1    printf("16. has_circle:\n");    //list->head->next->next->next->next->next = list->head->next;    if(has_circle(list))    {        printf("17 find_circle_first_node:\n");        find_circle_first_node(list);    }#endif    printf("13. list1:list_dump(list)\n");    list1 = list_dump(list);    printf("list1: ");    show_list(list1);    printf("14. is_list_intersect:\n");    if(is_list_intersect(list1, list))    {        printf("the common node: %d\n",get_first_common_node(list1, list));    }    printf("2. destroy_list:\n");    destroy_list(&list1);    destroy_list(&list2);    destroy_list(&list);    return 0;}

运行结果:

root@aemonair:/ctl_list# ./bin/list 1.init_list:3.1 list1 push_front :35 93 15 77 86 83 3.2 list2 push_back:86 92 49 21 4.1 list1 pop_front:93 15 77 86 83 4.2 list2 pop_back:86 92 49 7.1 sort_list1_ascend:15 77 83 86 93 7.2 sort_list2_descend:92 86 49 12 reverse_list2:49 86 92 11. list : merge_two_lists_recure(list1, list2):15 49 77 83 86 86 92 93 8.1 show_list(list);15 49 77 83 86 86 92 93 8.2 reverse_show_list(list);93 92 86 86 83 77 49 15 find_node:which one?(value to find)92found the value: 92found num of list is :92 find_revise_node:which one index ?(reverse)2reverse num of list is :92 5.3 find_mid_node(list):86 9. get_list_count:the count of list is :86.3 delete_one_node:which one?(to delete)92found the value: 9215 49 77 83 86 86 93 6.1 modify_node :15 26 77 83 86 86 93 6.2 insert_node: 15 26 18 77 83 86 86 93 16. has_circle:no circle!13. list1:list_dump(list)list1: 15 26 18 77 83 86 86 93 14. is_list_intersect:nothing2. destroy_list:

总结

至此。我们完成了对带控制信息链表的定义,实现,测试;
我们会发现加上控制信息之后,一些操作被封装起来,有可能更加便捷。
这是一种思路,一种方法。
以上解法也不一定是最简便快捷的,只是其中一种实现而已。
我们还将继续对数据结构进行进一步的探讨。

0 0
原创粉丝点击