[データ構造] C 言語で書かれたリンク リスト。リンク リストを定義し、新しいノードの作成、ノードの挿入、リンク リストのクリア、リンク リストの出力、ノードの検索などのいくつかの基本的なリンク リスト操作を実装します。

#include <stdio.h>    // 引入标准输入输出库,用于输入输出操作  
#include <stdlib.h>   // 引入标准内存分配库,用于动态内存分配  
#include <time.h>     // 引入标准时间库,用于使用随机数生成函数  
  
// 定义常量DL为3,未在后续代码中使用,可能为误提交或者待使用  
#define DL 3  
// 定义宏STR,将参数转换为对应的字符串表示  
#define STR(n) #n  
// 定义宏DIGIT_LEN_STR,将参数作为格式字符串与一个整数结合,生成对应的格式化字符串  
#define DIGIT_LEN_STR(n) "%" STR(n) "d"  
  
// 定义一个结构体Node,表示链表中的一个节点  
typedef struct Node {  
    int data;          // 节点的数据部分,此处为整型  
    struct Node *next;  // 节点的下一个节点的指针  
} Node;  
  
// 定义一个函数getNewNode,用于创建一个新的节点并返回其指针  
Node *getNewNode(int val) {  
    // 通过malloc动态分配内存,创建新节点  
    Node *p = (Node *)malloc(sizeof(Node));  
    // 设置新节点的数据部分  
    p->data = val;  
    // 设置新节点的下一个节点为NULL  
    p->next = NULL;  
    // 返回新节点的指针  
    return p;  
}  
  
// 定义一个函数insert,用于在链表的指定位置插入一个新的节点  
Node *insert(Node *head, int pos, int val) {  
    // 创建一个新的头节点new_head,用于保存插入新节点后的链表头  
    Node new_head, *p = &new_head, *node = getNewNode(val);  
    // 将new_head的next指向原链表的头节点head  
    new_head.next = head;  
    // 在链表中找到指定位置的前一个节点,此处使用了一个for循环  
    for (int i = 0; i < pos; i++) p = p->next;  
    // 将新节点插入到指定位置,此处涉及链表的操作  
    node->next = p->next;  
    p->next = node;  
    // 返回新链表的头节点new_head的next指针,即插入新节点后的链表头  
    return new_head.next;  
}  
  
// 定义一个函数clear,用于清空链表中的所有节点,释放其内存空间  
void clear(Node *head) {  
    // 如果链表为空,则直接返回,无需进行任何操作  
    if (head == NULL) return ;  
    // 遍历链表,逐个释放节点的内存空间,直至链表末尾  
    for (Node *p = head, *q; p; p = q) {  
        q = p->next;   // q保存下一个节点,为下一次循环准备  
        free(p);       // 释放当前节点的内存空间  
    }  
    return ;           // 所有节点已经释放完毕,函数结束返回  
}
// 输出链表的函数,参数为链表的头节点和标记flag,默认为0  
void output_linklist(Node *head, int flag = 0) {  
    // 初始化节点计数为0  
    int n = 0;  
    // 遍历链表,计算链表的长度  
    for (Node *p = head; p; p = p->next) n += 1;  
    // 根据DL宏定义的长度,打印数字表示的索引和空格  
    for (int i = 0; i < n; i++) {  
        printf(DIGIT_LEN_STR(DL), i);  
        printf("  ");  
    }  
    // 打印换行符  
    printf("\n");  
    // 遍历链表,打印每个节点的值和箭头符号  
    for (Node *p = head; p; p = p->next) {  
        printf(DIGIT_LEN_STR(DL), p->data);  
        printf("->");  
    }  
    // 再打印换行符  
    printf("\n");  
    // 如果flag为0,打印额外的换行符  
    if (flag == 0) printf("\n\n");  
    // 函数结束并返回  
    return ;  
}  
  
// 在链表中查找值的函数,参数为链表的头节点和要查找的值  
int find(Node *head, int val) {  
    // 初始化指针p为链表的头节点  
    Node *p = head;  
    // 初始化计数为0  
    int n = 0;  
    // 遍历链表,查找值为val的节点  
    while (p) {  
        // 如果找到值为val的节点  
        if (p->data == val) {  
            // 输出链表,标记为1,表示找到了值  
            output_linklist(head, 1);  
            // 计算打印的空格长度,为节点计数乘以每个节点的长度加上两个空格的长度再加两个空格的长度  
            int len = n * (DL + 2) + 2;  
            // 在打印的结果下方打印len个横线" -"  
            for (int i = 0; i < len; i++) printf(" ");  
            // 在打印的横线上方打印"^"符号  
            printf("^\n");  
            // 在打印的横线下方再打印len个横线" |"  
            for (int i = 0; i < len; i++) printf(" ");  
            // 在最下方打印"|"符号  
            printf("|\n");  
            // 返回1,表示找到了值为val的节点  
            return 1;  
        }  
        // 如果没找到,计数加1并移动到下一个节点  
        n += 1;  
        p = p->next;  
    }  
    // 如果遍历完链表都没找到值为val的节点,返回0,表示没找到  
    return 0;  
}
// 程序的主函数开始  
int main() {  
  
    // 使用当前时间作为随机数生成器的种子,这样每次运行程序时,生成的随机数都会不同  
    srand(time(0));  
  
    // 定义常量MAX_OP为7,这可能是一个操作的上限或者是要插入的节点数量  
    #define MAX_OP 7  
  
    // 创建一个指向Node类型的指针head,并将其初始化为NULL,这是链表的头节点  
    Node *head = NULL;  
  
    // 循环MAX_OP次,进行MAX_OP次操作  
    for (int i = 0; i < MAX_OP; i++) {  
  
        // 随机生成一个在0到i之间的位置,pos代表要插入新节点的位置  
        int pos = rand() % (i + 1);  
  
        // 随机生成一个0到99之间的值,val代表要插入的新节点的值  
        int val = rand() % 100;  
  
        // 打印出要进行的操作,即将val插入到pos位置  
        printf("insert %d at %d to linklist\n", val, pos);  
  
        // 调用insert函数,将新节点插入到链表的指定位置,并更新head为新的头节点  
        head = insert(head, pos, val);  
  
        // 调用output_linklist函数,打印出当前的链表状态  
        output_linklist(head);  
    }  
  
    // 从标准输入读取一个整数val,如果读取失败(输入的不是整数),则返回-1,所以~scanf("%d", &val)会变成-1的补码,是一个很大的数  
    int val;  
    while (~scanf("%d", &val)) {  
  
        // 调用find函数在链表中查找val,如果没找到,则返回0  
        if (!find(head, val)) {  
  
            // 如果没找到,打印"not found"  
            printf("not found\n");  
        }  
    }  
  
    // 调用clear函数,清除链表中的所有节点,释放其内存空间  
    clear(head);  
  
    // 返回0,表示程序正常结束  
    return 0;  
}

このコードは C 言語で書かれており、リンク リストを定義し、新しいノードの作成、ノードの挿入、リンク リストのクリア、リンク リストの出力、ノードの検索などのいくつかの基本的なリンク リスト操作を実装します。各コード部分の詳細な説明は次のとおりです。

  1. ファイルコメント:

    • このコードはファイルのコメントです
  2. ヘッダー ファイルをインクルードします。

    • #include <stdio.h> および #include <stdlib.h>: これら 2 つのヘッダー ファイルには、標準入出力ライブラリと、入出力やメモリ割り当てなどの操作のための標準ライブラリが含まれています。
    • #include <time.h>srand(time(0)) : このヘッダー ファイルには、乱数発生器のシードを設定するために使用される、時間に関連する関数が含まれています。
  3. マクロ定義:

    • #define DL 3DL: このマクロは、値が 3 の定数を定義します 。
    • #define STR(n) #n および #define DIGIT_LEN_STR(n) "%" STR(n) "d": これら 2 つのマクロは、文字列で表される整数定数を生成するために使用されます。たとえば、DIGIT_LEN_STR(3) は に置き換えられます "%3d"
  4. 構造体 の定義Node :

    • typedef struct Node { int data; struct Node *next; } Node;: これは Node という名前の構造体型を定義します。これには 2 つのメンバーがあり、1 つは data ノード データを格納するためのもので、もう 1 つは next 次のノードを指すためのものです。
  5. 関数 getNewNode 定義:

    • Node *getNewNode(int val) { Node *p = (Node *)malloc(sizeof(Node)); p->data = val; p->next = NULL; return p; }: この関数は、新しいノードを作成するために使用され、整数パラメータを受け取り 、val新しい タイプNode のオブジェクト を作成し、val それを新しいノードのメンバーに割り当て data 、新しいノードの値を next に 設定しNULL、そのポインタを返します。新しく作成されたノード。
  6. 関数 insert 定義:

    • Node *insert(Node *head, int pos, int val) { Node new_head, *p = &new_head, *node = getNewNode(val); new_head.next = head; for (int i = 0; i < pos; i++) p = p->next; node->next = p->next; p->next = node; return new_head.next; }: この関数は、リンク リストの指定された位置に新しいノードを挿入するために使用され、リンク リストの先頭ノード、挿入される位置、および挿入される値の 3 つのパラメーターを head受け取り pos ます valまず新しいヘッド ノードを作成し new_head、次に を呼び出して新しいノードを作成し getNewNode 、新しいノードの値を に設定します val次に、ループによって挿入する位置を見つけ、最後にリンク リストの指定された位置に新しいノードを挿入し、新しい先頭ノードのポインタを返します。
  7. 関数 clear 定義:

    • void clear(Node *head) { if (head == NULL) return ; for (Node *p = head, *q; p; p = q) { q = p->next; free(p); } return ; }: この関数はリンク リストをクリアするために使用され、リンク リストのヘッド ノードであるパラメータを受け取ります headまず、リンクされたリストが空かどうかを確認し、空であれば直接返します。そして、リンクリストをループすることで、各ノードのメモリ空間が解放されます。
  8. 関数 output_linklist 定義:

    • void output_linklist(Node *head, int flag = 0) { int n = 0; for (Node *p = head; p; p = p->next) n += 1; for (int i = 0; i < n; i++) { printf(DIGIT_LEN_STR(DL), i); printf(" "); } printf("\n"); for (Node *p = head; p; p = p->next) { printf(DIGIT_LEN_STR(DL), p->data); printf("->"); } printf("\n"); if (flag == 0) printf("\n\n"); return ; }: この関数はリンク リストを出力するために使用され、リンク リストのヘッド ノード head とマークの 2 つのパラメーターを受け取りますflagまずリンク リストの長さを計算し、次に長さに応じて対応する数の数字とスペースを出力し、次にリンク リスト内のすべての要素と矢印記号を出力します。0の場合 flag 、リンクリストを印刷した後

おすすめ

転載: blog.csdn.net/dsafefvf/article/details/132717970