• 单链表


    下图展示了单链表的基本结构:

    head指针是链表的头指针,指向第一个节点,每个节点的next指针域指向下一个节点,最后一个节点的next指针域为NULL,在图中用0表示。

    下面先来看程序(栈的链式存储实现,另外一个实现点这里)和对应的输出(注意输出前进行了链表反转(见《单链表反转》,否则程序后面的while循环输出的顺序是250,200,100),接着来分析程序:

     C++ Code 
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
     

    /* linkedlist.h */

    #ifndef LINKEDLIST_H
    #define LINKEDLIST_H

    typedef struct node *link;
    struct node
    {
        unsigned char item;
        link next;
    };

    link make_node(unsigned char item);
    void free_node(link p);
    link search(unsigned char key);
    void insert(link p);
    void deletep(link p);
    void traverse(void (*visit)(link));
    void reverse(void);
    void destroy(void);
    void push(link p);
    link pop(void);

    #endif
     C++ Code 
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
     

    /* linkedlist.c */
    #include <stdlib.h>
    #include <stdio.h>
    #include "linkedlist.h"

    static link head = NULL;

    link make_node(unsigned char item)
    {
        link p = malloc(sizeof(*p));
        p->item = item;
        p->next = NULL;
        printf("make node from Item %d ", item);
        return p;
    }

    void free_node(link p)
    {
        printf("free node ... ");
        free(p);
    }

    link search(unsigned char key)
    {
        link p;
        printf("search by key %d ", key);
        for (p = head; p; p = p->next)
            if (p->item == key)
                return p;
        return NULL;
    }

    void insert(link p)
    {
        printf("insert node from head ... ");
        p->next = head;
        head = p;
    }

    /*
    void delete(link p)
    {
        link pre;
        printf("delete node from ptr ... ");
        if (p == head) {
            head = p->next;
            return;
        }
        for (pre = head; pre; pre = pre->next) 
            if (pre->next == p) {
                pre->next = p->next;
                return;
         }
    }
    */

    void deletep(link p)
    {
        link *pnext;
        printf("delete node from ptr ... ");
        for (pnext = &head; *pnext; pnext = &(*pnext)->next)
            if (*pnext == p)
            {
                *pnext = p->next;
                return;
            }
    }

    void traverse(void (*visit) (link))
    {
        link p;
        printf("linkedlist traverse ... ");
        for (p = head; p; p = p->next)
            visit(p);
    }

    void reverse(void)
    {
        link pnode = head;
        link pprev = NULL;
        printf("reverse linkedlist ... ");
        while (pnode != NULL)
        {
            // get the next node, and save it at pNext
            link pnext = pnode->next;
            // reverse the linkage between nodes
            pnode->next = pprev;
            // move forward on the the list
            pprev = pnode;
            pnode = pnext;

        }
        head = pprev;
    }

    void destroy(void)
    {
        link q, p = head;
        printf("destory linkedlist ... ");
        head = NULL;
        while (p)
        {
            q = p;
            p = p->next;
            free_node(q);
        }
    }

    void push(link p)
    {
        printf("push item from head ... ");
        insert(p);
    }

    link pop(void)
    {
        if (head == NULL)
            return NULL;
        else
        {
            link p = head;
            printf("pop item from head ... ");
            head = head->next;
            return p;
        }
    }


     C++ Code 
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
     
    /*************************************************************************
        > File Name: main.c
        > Author: Simba
        > Mail: dameng34@163.com
        > Created Time: Fri 28 Dec 2012 01:22:24 PM CST
     ************************************************************************/

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

    void print_item(link p)
    {
        printf("print item %d  ", p->item);
    }

    int main(void)
    {
        link p = make_node(10);
        insert(p);
        p = make_node(5);
        insert(p);
        p = make_node(90);
        insert(p);
        p = search(5);
        deletep(p);
        free_node(p);
        traverse(print_item);
        destroy();
        printf(".................. ");
        p = make_node(100);
        push(p);
        p = make_node(200);
        push(p);
        p = make_node(250);
        push(p);

        reverse();//链表反转

        while ((p = pop()))
        {

            print_item(p);
            free_node(p);
        }

        return 0;
    }



    输出为:

    分析:

    在初始化时把头指针head初始化为NULL,表示空链表(不带头结点)。然后main函数调用make_node创建几个节点,分别调用insert插入到链表中。

    链表的插入操作如下图:

    正如上图所示,insert函数虽然简单,其中也隐含了一种特殊情况(Special Case)的处理,当head为NULL时,执行insert操作插入第一个节点之后,head指向第一个节点,而第一个节点的next指针域成为NULL,这很合理,因为它也是最后一个节点。所以空链表虽然是一种特殊情况,却不需要特殊的代码来处理,和一般情况用同样的代码处理即可,这样写出来的代码更简洁,但是在读代码时要想到可能存在的特殊情况。当然,insert函数传进来的参数p也可能有特殊情况,传进来的p可能是NULL,甚至是野指针,本章的函数代码都假定调用者的传进来的参数是合法的,不对参数做特别检查。事实上,对指针参数做检查是不现实的,如果传进来的是NULL还可以检查一下,如果传进来的是野指针,根本无法检查它指向的内存单元是不是合法的,C标准库的函数通常也不做这种检查,例如strcpy(p, NULL)就会引起段错误。

    接下来main函数调用search在链表中查找某个节点,如果找到就返回指向该节点的指针,找不到就返回NULL。

    search函数其实也隐含了对于空链表这种特殊情况的处理,如果是空链表则循环体一次都不执行,直接返回NULL。

    然后main函数调用delete从链表中摘除用search找到的节点,最后调用free_node释放它的存储空间。

    链表的删除操作如下图:


    从上图可以看出,要摘除一个节点需要首先找到它的前趋然后才能做摘除操作,而在单链表中通过某个节点只能找到它的后继而不能找到它的前趋,所以删除操作要麻烦一些,需要从第一个节点开始依次查找要摘除的节点的前趋。delete操作也要处理一种特殊情况,如果要摘除的节点是链表的第一个节点,它是没有前趋的,这种情况要用特殊的代码处理,而不能和一般情况用同样的代码处理。这样很不爽,能不能把这种特殊情况转化为一般情况呢?可以把delete函数改成上述程序那样:

    消除特殊情况的链表删除操作如下图:


    定义一个指向指针的指针pnext,在for循环中pnext遍历的是指向链表中各节点的指针域,这样就把head指针和各节点的next指针统一起来了,可以在一个循环中处理。(其实增加一个头节点也可以消除delete的特殊情况《线性表的链式存储结构》)
    然后main函数调用traverse函数遍历整个链表,调用destroy函数销毁整个链表。


    参考:《linux c 编程一站式学习》

  • 相关阅读:
    现代JVM内存管理方法的发展历程,GC的实现及相关设计概述(转)
    jvm对大对象分配内存的特殊处理(转)
    用java字节码解释i++和++i(转)
    Git 常用命令手记 及 Github协同流程(转)
    经常使用git命令集
    Android手机分辨率基础知识(DPI,DIP计算)
    软件測试自学指南---从入门到精通
    惊!从一场离奇的命案说起
    java设计模式演示样例
    浅谈UML的概念和模型之UML九种图
  • 原文地址:https://www.cnblogs.com/alantu2018/p/8471629.html
Copyright © 2020-2023  润新知