Skip to content

LV070-单链表进阶操作

一、单链表反转

1. 头插法

头插法反转链表,是指在原有链表的基础上,依次将位于原链表头部的节点取出来,然后采用从头部插入的方式生成一个新链表,则新生成的链表即为原链表的反转链表啦。

1.1 头插法实现步骤

1-1

1.2 函数实现

c
/**
 * @Function   : slinkedlist_reverse_insertH
 * @brief      : 头插法反转单链表
 * @param H    : _slinkedlink 类型,表示要反转的单链表的地址
 * @return     : int 类型, 链表不存在则返回-1; 反转完成返回 0
 * @Description:
 */
int slinkedlist_reverse_insertH(_slinkedlink H)
{
    /* 定义两个单链表结构体指针变量 */
    _slinkedlink p;
    _slinkedlink q;

    /* 1. 判断单链表是否存在 */
    if (H == NULL)
    {
        printf("[WARN ]Single linked list is not existed!\n");
        return -1;
    }
    /* 2. 判断链表仅有头节点或者只有两个节点 */
    if (H->next == NULL || H->next->next == NULL)
    {
        return 0;
    }
    /* 3. 拆分链表为两部分 */
    p = H->next->next;
    H->next->next = NULL;
    /* 4. 开始反转 */
    while (p != NULL)
    {
        q = p;
        p = p->next;

        q->next = H->next;
        H->next = q;
    }

    return 0;
}

2. 迭代法

迭代法反转就是从当前链表的首元节点开始,一直遍历至链表的最后一个节点,这期间会逐个改变所遍历到的节点的指针域,令其指向前一个节点,最后再令原始的头节点指向反转后的第一个节点。

2.1 迭代法实现步骤

1-2

2.2 函数实现

c
/**
 * @Function   : slinkedlist_reverse_iteration
 * @brief      : 迭代法反转链表
 * @param H    : _slinkedlink 类型,表示要反转的单链表的地址
 * @return     : _slinkedlink 类型,反转成功则返回反转后的链表地址; 反转失败则返回 NULL
 * @Description: 需要处理头节点,在函数内已经处理。
 */
_slinkedlink slinkedlist_reverse_iteration(_slinkedlink H)
{
    /* 定义三个单链表结构体指针变量 */
    _slinkedlink p1;
    _slinkedlink p2;
    _slinkedlink p3;
    /* 1. 判断单链表是否存在 */
    if (H == NULL)
    {
        printf("Single linked list is not existed!\n");
        return NULL;
    }
    /* 2. 判断是否为空表 */
    if (H->next == NULL)
    {
        printf("Single linked list H is empty!\n");
        return H;
    }
    /* 3. 开始反转操作, */
    p1 = NULL;
    /* 跳过链表头,H 会一直指向链表头 */
    p2 = H->next;
    p3 = H->next->next;
    while (p2 != NULL)
    {
        /* 反转后一个节点指向前一个节点 */
        p2->next = p1;

        /* 三个指针向后移动一个节点 */
        p1 = p2;
        p2 = p3;
        /* p3 会先到 NULL, 此时 p3 已经不可以再进行 p3 = p3-> next; 操作了,此时的 p2 是位于最后一个节点,即 p2-> next = NULL*/
        if (p3 != NULL)
            p3 = p3->next;
    }
    /* 4.加上原来的头节点 */
    /* 反转完成后,p1 指向原先的随后一个节点, p2 和 p3 指向 NULL, 反转链表的最后一个节点是原来的头节点	*/
    H->next = p1;
    return H;
}

3. 递归法

和迭代反转法的思想恰好相反,递归反转法的实现是从链表的尾节点开始,依次向前遍历,遍历过程依次改变各节点的指向,即令其指向前一个节点。

3.1 递归法实现步骤

递归法有些复杂了,用 ppt 做的动图不怎么反应的出来,所以这里就省略了,简单通过代码理解吧。(个人觉得涉及到递归的,似乎都不怎么好理解)。

3.2 函数实现

c
/**
 * @Function   : slinkedlist_reverse_recursiveH
 * @brief      : 递归法反转单链表
 * @param H    : _slinkedlink 类型,表示要反转的单链表的地址
 * @return     : _slinkedlink 类型,反转成功则返回反转后的链表地址; 反转失败则返回 NULL
 * @Description: 处理递归反转时的头节点问题。
 */
_slinkedlink slinkedlist_reverse_recursiveH(_slinkedlink H)
{
    /* 定义一个单链表结构体指针变量 */
    _slinkedlink p1;
    /* 1. 判断单链表是否存在 */
    if (H == NULL)
    {
        printf("Single linked list is not existed!\n");
        return NULL;
    }
    /* 2. 去掉头节点 */
    p1 = H->next;
    /* 3.去掉头节点的部分进行反转 */
    p1 = slinkedlist_reverse_recursive(p1);
    /* 4.头节点指向反转后的无节点链表 */
    H->next = p1;

    return H;
}

/**
 * @Function   : slinkedlist_reverse_recursive
 * @brief      : 递归法反转单链表递归实现
 * @param H    : _slinkedlink 类型,表示要反转的单链表的地址
 * @return     : _slinkedlink 类型,反转成功则返回反转后的链表地址; 反转失败则返回 NULL
 * @Description: 
 * @Attention  : 原来的尾节点会成为头节点,头节点会成为尾节点,递归函数内无法处理,重新定义一个函数进行处理。
 */
_slinkedlink slinkedlist_reverse_recursive(_slinkedlink H)
{
    /* 定义一个单链表结构体指针变量 */
    _slinkedlink p1;

    /* 1. 判断单链表是否存在 */
    if (H == NULL)
    {
        printf("Single linked list is not existed!\n");
        return NULL;
    }
    /* 2. 递归结束的条件 */
    if (H->next == NULL)
    {
        return H;
    }
    /* 3. 开始反转操作, */
    p1 = slinkedlist_reverse_recursive(H->next);
    H->next->next = H;
    H->next = NULL;
    return p1;
}

4. 本地逆置法

本地逆置法和头插法的实现思想类似,唯一的区别在于,头插法是通过建立一个新链表实现的,而就地逆置法则是直接对原链表做修改,从而实现将原链表反转。

4.1 本地逆置法实现步骤

1-4

4.2 函数实现

c
/**
 * @Function   : slinkedlist_reverse_local
 * @brief      : 本地逆置法反转单链表
 * @param H    : _slinkedlink 类型,表示要反转的单链表的地址
 * @return     : _slinkedlink 类型,反转成功则返回反转后的链表地址; 反转失败则返回 NULL
 * @Description:
 * @Attention  : 需要处理头节点, 函数内已处理。
 */
_slinkedlink slinkedlist_reverse_local(_slinkedlink H)
{
    /* 定义两个单链表结构体指针变量 */
    _slinkedlink p;
    _slinkedlink q;

    /* 1. 判断单链表是否存在 */
    if (H == NULL)
    {
        printf("Single linked list is not existed!\n");
        return NULL;
    }
    /* 2. 判断链表仅有头节点或者只有两个节点 */
    if (H->next == NULL || H->next->next == NULL)
    {
        return H;
    }

    /* 3. 开始反转操作 */
    p = H->next;
    q = H->next->next;
    while (q != NULL)
    {
        p->next = q->next;
        q->next = H->next;
        H->next = q;
        q = p->next;
    }

    return H;
}

二、单链表排序

1. 排序方法

这里使用冒泡法实现链表的排序。

2. 函数实现

c
/**
 * @Function   : slinkedlist_bubble_sort
 * @brief      : 单链表排序(冒泡法排序)
 * @param H    : _slinkedlink 类型,表示要排序的单链表的地址
 * @param mode :int 类型,排序模式 0, 降序; 1, 升序
 * @return     : _slinkedlink 类型,排序完成则返回反转后的链表地址; 否则则返回 NULL
 * @Description:
 */
_slinkedlink slinkedlist_bubble_sort(_slinkedlink H, int mode)
{
	/* 定义三个链表结构体指针变量 */
	_slinkedlink p, q, temp;
	int i, j;
	int len = 0;

	/* 1.判断链表是否为存在, 不存在则直接返回 */
	if (H == NULL)
	{
		printf("H is NULL(not exist)!\n");
		return NULL;
	}
	/* 2.求链表长度 */
	len = slinkedlist_length(H);

	/* 3.冒泡排序 */
	for(i = 0; i < len-1; i++)/* 外层循环, 进行 len-1 趟数据比较 */
	{
		/* 进行完一轮后,从头开始进行下一轮 */
		q = H->next;/* 令 q 指向第一个结点 */
		p = q->next;/* 令 p 指向后一个结点 */
		temp = H;   /* 让 temp 始终指向 q 前一个结点,方便交换 */
		for(j = 0; j < len-1-i; j++)    /* 每一趟进行 len-1-i 次比较 */
		{
			if(mode == 1 && (q->data > p->data))/* 如果该结点的值大于后一个结点,则交换*/
			{

				q->next = p->next;
				p->next = q;
				temp->next = p;
			}
			else if(mode == 0 && (q->data < p->data))/* 如果该结点的值小于后一个结点,则交换*/
			{

				q->next = p->next;
				p->next = q;
				temp->next = p;
			}
			/* 移动到下一个节点 */
			temp = temp->next;
			q = temp->next;
			p = q->next;
		}
	}
	return H;
}

三、链表相交判定

1. 判定方法

链表相交?既然链表之间的节点是指针联系起来的,那么他们就完全有可能会有共同的部分,只考虑相交的话,可能的情况有以下三种:

image-20221022162040962

但是根据单链表中每个节点的指针只能指向一个地址,所以情况 2 和情况 3 是不可能存在的。既然是只有情况 1 ,那么这也就意味着它们的尾节点地址必然是相同的,都遍历到结尾,比较尾节点就可以知道他们是否相交啦。

2. 函数实现

c
/**
 * @Function   : slinkedlist_intersect
 * @brief      : 判断两链表是否相交
 * @param H1   : _slinkedlink 类型,链表 H1 的首地址
 * @param H2   : _slinkedlink 类型,链表 H2 的首地址
 * @return     : int 类型,1, 两个链表相交; 0, 两个链表不相交;-1, 链表不存在
 * @Description:
 */
int slinkedlist_intersect(_slinkedlink H1, _slinkedlink H2)
{
    /* 定义两个个链表结构体指针变量 */
    _slinkedlink p;
    _slinkedlink q;

    /* 1. 判断链表是否为存在, 不存在则直接返回 */
    if (H1 == NULL || H2 == NULL)
    {
        printf("Single linked list H1 or H2 is not existed!\n");
        return -1;
    }

    /* 2. 寻找尾节点 */
    p = H1;
    q = H2;
    while (p->next)
    {
        p = p->next;
    }
    while (q->next)
    {
        q = q->next;
    }
    /* 3. 判断是否相交 */
    if (p == q)
        return 1;
    else
        return 0;
}

四、相邻节点和最大

1. 实现思路

问题:设节点 data 域为整型,求链表中相邻两结点 data 值之和为最大的第一结点的指针。

思路:设 p , q 分别为链表中相邻两节点指针,求 p-> data+q-> data 为最大的那一组值,返回其相应的指针 p 。

2. 函数实现

c
/**
 * @Function   : slinkedlist_adjmax
 * @brief      : 求相邻两节点和最大时的第一个节点指针
 * @param H    : _slinkedlink 类型,链表 H 的首地址
 * @param value:data_t * 类型,存放相邻两节点和的最大值,需要传入地址
 * @return     : _slinkedlink 类型,链表不存在返回 NULL; 否则返回相邻两节点和最大时的第一个节点指针
 * @Description:
 */
_slinkedlink slinkedlist_adjmax(_slinkedlink H, data_t *value)
{
    /* 定义单个结构体变量指针, r 用于记录此次比较的目标节点 */
    _slinkedlink p, q, r;
    data_t sum;
    /* 1.判断链表是否为存在, 不存在则直接返回 */
    if (H == NULL)
    {
        printf("H is NULL(not exist)!\n");
        return NULL;
    }
    /* 2. 判断是否至少有三个节点,若小于三个节点,则直接返回 H 即可 */
    if (H->next == NULL || H->next->next == NULL || H->next->next->next == NULL)
    {
        return H;
    }
    /* 3. 开始比较相邻两节点和的值,找出最大值 */
    q = H->next;
    p = H->next->next; /* p = q-> next; */
    r = q;
    sum = q->data + p->data;

    while (p->next != NULL)
    {
        p = p->next;
        q = q->next;
        if (sum < q->data + p->data)
        {
            sum = q->data + p->data;
            r = q;
        }
    }
    /* 4. 传回最大值 */
    *value = sum;
    return r;
}

五、完整程序

1. slinkedlist.c

c
/** ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== =
 * Copyright © hk. 2022-2025. All rights reserved.
 * File name  : slinkedlist.c
 * Author     : qidaink
 * Date       : 2022-10-21
 * Version    :
 * Description:
 * Others     :
 * Log        :
 * ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==
 */

#include <stdio.h>
#include <stdlib.h>
#include "slinkedlist.h"

/**
 * @Function   : slinkedlist_create
 * @brief      : 单链表创建
 * @param arg  : none
 * @return     : _slinkedlink 类型,NULL, 内存申请失败; H, 单链表地址
 * @Description: 其实就是创建一个节点
 */
_slinkedlink slinkedlist_create(void)
{
    /* 1.定义一个单链表结构体指针变量 */
    _slinkedlink H;
    /* 2.申请内存空间 */
    H = (_slinkedlink)malloc(sizeof(_slinkednode));
    /* 3.判断是否申请成功 */
    if (H == NULL)
    {
        printf("[ERROR]Single linked list malloc failed!\n");
        return H;
    }
    /* 4.初始化内存空间 */
    H->data = 0;
    H->next = NULL;

    return H;
}

/**
 * @Function   : slinkedlist_free
 * @brief      : 单链表空间释放(释放整个单链表占用的内存空间)
 * @param H    : _slinkedlink 类型,表示要释放的单链表的地址
 * @return     : _slinkedlink 类型,NULL, 表示释放完成
 * @Description:
 */
_slinkedlink slinkedlist_free(_slinkedlink H)
{
    /* 定义一个单链表结构体指针变量 */
    _slinkedlink p;

    /* 1. 判断单链表是否存在 */
    if (H == NULL)
    {
        printf("[WARN ]Single linked list is not existed!\n");
        return NULL;
    }
    /* 2. 开始释放内存 */
    p = H;
    printf("Single linked list free:");
    while (H != NULL)
    {
        p = H;
        printf("%d ", p->data);
        /* p 和 H 指向同一个地址空间,要先移动 H, 再释放,否则后边的节点地址就丢掉了 */
        H = H->next;
        free(p);
    }
    puts(""); /* 自带换行 */

    return NULL;
}

/**
 * @Function   : slinkedlist_show
 * @brief      : 单链表数据显示
 * @param H    : _slinkedlink 类型,表示要释放的单链表的地址
 * @return     : int 类型,0, 显示成功;-1, 单链表不存在
 * @Description:
 */
int slinkedlist_show(_slinkedlink H)
{
    /* 定义一个单链表结构体指针变量 */
    _slinkedlink p;

    /* 1. 判断单链表是否存在 */
    if (H == NULL)
    {
        printf("[WARN ]Single linked list is not existed!\n");
        return -1;
    }
    /* 2. 遍历所有节点 */
    p = H;
    printf("Single linked list:");
    while (p->next != NULL)
    {
        printf("%d ", p->next->data);
        p = p->next;
    }
    puts(""); /* 自带换行 */

    return 0;
}

/**
 * @Function   : slinkedlist_node_get
 * @brief      : 单链表节点查找(按位置查找)
 * @param H    : _slinkedlink 类型,表示要查找的单链表的地址
 * @param pos  : int 类型,表示要查找的节点位置,pos 大于等于 -1
 * @return     : _slinkedlink 类型,查找成功则返回查找到的节点的地址,否则返回 NULL
 * @Description:
 */
_slinkedlink slinkedlist_node_get(_slinkedlink H, int pos)
{
    /* 定一个单链表结构体指针变量 */
    _slinkedlink p;
    int i = 0;

    /* 1. 判断单链表是否存在 */
    if (H == NULL)
    {
        printf("[WARN ]Single linked list is not existed!\n");
        return NULL;
    }
    /* 2. 检查参数是否合法 */
    if (pos == -1) /* pos = -1 表示查找的是头节点 */
    {
        return H;
    }
    /* 这里可以防止参数非法,而返回 H 的情况,可以防止删除节点函数误删 */
    if (pos < -1)
    {
        printf("[ERROR]get pos is invalid\n");
        return NULL;
    }
    /* 3. 开始遍历节点,查找所需节点 */
    p = H;
    i = -1; /* 一共需要循环 pos + 1 次 */
    while (i < pos)
    {
        p = p->next;
        if (p == NULL) /* 已经遍历到单链表尾部了 */
        {
            printf("[ERROR]get pos is invalid\n");
            return NULL;
        }
        i++;
    }

    return p;
}

/**
 * @Function   : slinkedlist_tail_insert
 * @brief      : 单链表尾部插入数据
 * @param H    : _slinkedlink 类型,表示要插入节点的单链表的地址
 * @param value: data_t 类型,表示要插入的数据
 * @return     : int 类型,0, 插入成功;-1, 插入失败
 * @Description:
 */
int slinkedlist_tail_insert(_slinkedlink H, data_t value)
{
    /* 定义两个个单链表结构体指针变量 */
    _slinkedlink p;
    _slinkedlink q;

    /* 1. 判断单链表是否存在 */
    if (H == NULL)
    {
        printf("[WARN ]Single linked list is not existed!\n");
        return -1;
    }
    /* 2. 创建新节点并初始化 */
    if ((p = (_slinkedlink)malloc(sizeof(_slinkednode))) == NULL)
    {
        printf("[ERROR]The new node malloc failed!\n");
        return -1;
    }
    p->data = value;
    p->next = NULL;
    /* 3. 寻找尾节点 */
    q = H;
    while (q->next != NULL)
    {
        q = q->next;
    }
    /* 3. 插入新节点 */
    q->next = p;

    return 0;
}

/**
 * @Function   : slinkedlist_insert
 * @brief      : 单链表在指定位置插入节点
 * @param H    : _slinkedlink 类型,表示要插入节点的单链表的地址
 * @param value: data_t 类型,彪悍死要插入的数据
 * @param pos  : int 类型,表示要插入的节点位置,pos 大于等于 0
 * @return     : int 类型,0, 插入成功;-1, 插入失败
 * @Description:
 */
int slinkedlist_insert(_slinkedlink H, data_t value, int pos)
{
    /* 定义两个单链表结构体指针变量 */
    _slinkedlink p;
    _slinkedlink q;

    /* 1. 判断单链表是否存在 */
    if (H == NULL)
    {
        printf("[WARN ]Single linked list is not existed!\n");
        return -1;
    }
    /* 2. 获取插入位置的前一个节点 */
    p = slinkedlist_node_get(H, pos - 1);
    if (p == NULL)
    {
        printf("[WARN ]pos-1 = %d position is not existed!\n", pos - 1);
        return -1;
    }
    /* 3. 创建新节点并初始化 */
    if ((q = (_slinkedlink)malloc(sizeof(_slinkednode))) == NULL)
    {
        printf("[ERROR]The new node malloc failed\n");
        return -1;
    }
    q->data = value;
    q->next = NULL;
    /* 4. 插入新节点 */
    q->next = p->next;
    p->next = q;

    return 0;
}

/**
 * @Function   : slinkedlist_delete
 * @brief      : 删除指定位置节点
 * @param H    : _slinkedlink 类型,表示要删除节点的单链表的地址
 * @param pos  : int 类型,要删除的节点位置(pos 大于等于 0)
 * @return     : int 类型,0, 删除成功;-1, 删除失败
 * @Description:
 */
int slinkedlist_delete(_slinkedlink H, int pos)
{
    /* 定义两个单链表结构体指针变量 */
    _slinkedlink p;
    _slinkedlink q;

    /* 1. 判断单链表是否存在 */
    if (H == NULL)
    {
        printf("[WARN ]Single linked list is not existed!\n");
        return -1;
    }
    /* 2. 获取删除位置的前一个节点 */
    /* 注意在这里,不合法的 pos 范围会在获取节点函数中被处理 */
    p = slinkedlist_node_get(H, pos - 1);
    if (p == NULL)
    {
        printf("[WARN ]pos-1 = %d position is not existed!\n", pos - 1);
        return -1;
    }
    if (p->next == NULL)
    {
        printf("delete pos is invalid!pos-1 = %d position is the last node!\n", pos - 1);
        return -1;
    }

    /* 3. 更新节点 */
    q = p->next;
    p->next = q->next; /* p-> next = p-> next-> next; */

    /* 4.释放删除的节点 */
    printf("Single linked list node free:%d\n", q->data);
    free(q);
    q = NULL;
    return 0;
}

/**
 * @Function   : slinkedlist_node_update
 * @brief      : 更新指定位置的节点数据
 * @param H    : _slinkedlink 类型,表示要更新数据的单链表的地址
 * @param value: data_t 类型,表示要更新的数据
 * @param pos  : int 类型,要更新的节点位置(pos 大于等于 0)
 * @return     : int 类型, 0, 更新成功;-1, 更新失败
 * @Description:
 */
int slinkedlist_node_update(_slinkedlink H, data_t value, int pos)
{
    /* 定义一个单链表结构体指针变量 */
    _slinkedlink p;

    /* 1. 判断单链表是否存在 */
    if (H == NULL)
    {
        printf("Single linked list is not existed!\n");
        return -1;
    }
    if (pos < 0)
    {
        printf("update pos is invalid!\n");
        return -1;
    }
    /* 2. 获取更新节点位置 */
    p = slinkedlist_node_get(H, pos);
    if (p == NULL)
    {
        printf("pos position is not existed!\n");
        return -1;
    }

    /* 3. 更新节点 */
    p->data = value;

    return 0;
}

/**
 * @Function   : slinkedlist_length
 * @brief      : 获取单链表长度(不包含头节点)
 * @param H    : _slinkedlink 类型,表示要获取长度的单链表的地址
 * @return     : int 类型, 链表不存在则返回-1; 否则返回单链表长度
 * @Description:
 */
int slinkedlist_length(_slinkedlink H)
{
    /* 定义一个链表结构体指针变量 */
    _slinkedlink p;
    int len = 0;

    /* 1.判断链表是否为存在, 不存在则直接返回 */
    if (H == NULL)
    {
        printf("[WARN ]Single linked list is not existed!\n");
        return -1;
    }

    /* 2.遍历链表 */
    p = H;
    while (p->next != NULL)
    {
        len++;
        p = p->next;
    }
    return (len);
}

/**
 * @Function   : slinkedlist_reverse_insertH
 * @brief      : 头插法反转单链表
 * @param H    : _slinkedlink 类型,表示要反转的单链表的地址
 * @return     : int 类型, 链表不存在则返回-1; 反转完成返回 0
 * @Description:
 */
int slinkedlist_reverse_insertH(_slinkedlink H)
{
    /* 定义两个单链表结构体指针变量 */
    _slinkedlink p;
    _slinkedlink q;

    /* 1. 判断单链表是否存在 */
    if (H == NULL)
    {
        printf("[WARN ]Single linked list is not existed!\n");
        return -1;
    }
    /* 2. 判断链表仅有头节点或者只有两个节点 */
    if (H->next == NULL || H->next->next == NULL)
    {
        return 0;
    }
    /* 3. 拆分链表为两部分 */
    p = H->next->next;
    H->next->next = NULL;
    /* 4. 开始反转 */
    while (p != NULL)
    {
        q = p;
        p = p->next;

        q->next = H->next;
        H->next = q;
    }

    return 0;
}

/**
 * @Function   : slinkedlist_reverse_iteration
 * @brief      : 迭代法反转链表
 * @param H    : _slinkedlink 类型,表示要反转的单链表的地址
 * @return     : _slinkedlink 类型,反转成功则返回反转后的链表地址; 反转失败则返回 NULL
 * @Description: 需要处理头节点,在函数内已经处理。
 */
_slinkedlink slinkedlist_reverse_iteration(_slinkedlink H)
{
    /* 定义三个单链表结构体指针变量 */
    _slinkedlink p1;
    _slinkedlink p2;
    _slinkedlink p3;
    /* 1. 判断单链表是否存在 */
    if (H == NULL)
    {
        printf("Single linked list is not existed!\n");
        return NULL;
    }
    /* 2. 判断是否为空表 */
    if (H->next == NULL)
    {
        printf("Single linked list H is empty!\n");
        return H;
    }
    /* 3. 开始反转操作, */
    p1 = NULL;
    /* 跳过链表头,H 会一直指向链表头 */
    p2 = H->next;
    p3 = H->next->next;
    while (p2 != NULL)
    {
        /* 反转后一个节点指向前一个节点 */
        p2->next = p1;

        /* 三个指针向后移动一个节点 */
        p1 = p2;
        p2 = p3;
        /* p3 会先到 NULL, 此时 p3 已经不可以再进行 p3 = p3-> next; 操作了,此时的 p2 是位于最后一个节点,即 p2-> next = NULL*/
        if (p3 != NULL)
            p3 = p3->next;
    }
    /* 4.加上原来的头节点 */
    /* 反转完成后,p1 指向原先的随后一个节点, p2 和 p3 指向 NULL, 反转链表的最后一个节点是原来的头节点	*/
    H->next = p1;
    return H;
}

/**
 * @Function   : slinkedlist_reverse_recursiveH
 * @brief      : 递归法反转单链表
 * @param H    : _slinkedlink 类型,表示要反转的单链表的地址
 * @return     : _slinkedlink 类型,反转成功则返回反转后的链表地址; 反转失败则返回 NULL
 * @Description: 处理递归反转时的头节点问题。
 */
_slinkedlink slinkedlist_reverse_recursiveH(_slinkedlink H)
{
    /* 定义一个单链表结构体指针变量 */
    _slinkedlink p1;
    /* 1. 判断单链表是否存在 */
    if (H == NULL)
    {
        printf("Single linked list is not existed!\n");
        return NULL;
    }
    /* 2. 去掉头节点 */
    p1 = H->next;
    /* 3.去掉头节点的部分进行反转 */
    p1 = slinkedlist_reverse_recursive(p1);
    /* 4.头节点指向反转后的无节点链表 */
    H->next = p1;

    return H;
}

/**
 * @Function   : slinkedlist_reverse_recursive
 * @brief      : 递归法反转单链表递归实现
 * @param H    : _slinkedlink 类型,表示要反转的单链表的地址
 * @return     : _slinkedlink 类型,反转成功则返回反转后的链表地址; 反转失败则返回 NULL
 * @Description:
 * @Attention  : 原来的尾节点会成为头节点,头节点会成为尾节点,递归函数内无法处理,重新定义一个函数进行处理。
 */
_slinkedlink slinkedlist_reverse_recursive(_slinkedlink H)
{
    /* 定义一个单链表结构体指针变量 */
    _slinkedlink p1;

    /* 1. 判断单链表是否存在 */
    if (H == NULL)
    {
        printf("Single linked list is not existed!\n");
        return NULL;
    }
    /* 2. 递归结束的条件 */
    if (H->next == NULL)
    {
        return H;
    }
    /* 3. 开始反转操作, */
    p1 = slinkedlist_reverse_recursive(H->next);
    H->next->next = H;
    H->next = NULL;
    return p1;
}

/**
 * @Function   : slinkedlist_reverse_local
 * @brief      : 本地逆置法反转单链表
 * @param H    : _slinkedlink 类型,表示要反转的单链表的地址
 * @return     : _slinkedlink 类型,反转成功则返回反转后的链表地址; 反转失败则返回 NULL
 * @Description:
 * @Attention  : 需要处理头节点, 函数内已处理。
 */
_slinkedlink slinkedlist_reverse_local(_slinkedlink H)
{
    /* 定义两个单链表结构体指针变量 */
    _slinkedlink p;
    _slinkedlink q;

    /* 1. 判断单链表是否存在 */
    if (H == NULL)
    {
        printf("Single linked list is not existed!\n");
        return NULL;
    }
    /* 2. 判断链表仅有头节点或者只有两个节点 */
    if (H->next == NULL || H->next->next == NULL)
    {
        return H;
    }

    /* 3. 开始反转操作 */
    p = H->next;
    q = H->next->next;
    while (q != NULL)
    {
        p->next = q->next;
        q->next = H->next;
        H->next = q;
        q = p->next;
    }

    return H;
}

/**
 * @Function   : slinkedlist_bubble_sort
 * @brief      : 单链表排序(冒泡法排序)
 * @param H    : _slinkedlink 类型,表示要排序的单链表的地址
 * @param mode :int 类型,排序模式 0, 降序; 1, 升序
 * @return     : _slinkedlink 类型,排序完成则返回反转后的链表地址; 否则则返回 NULL
 * @Description:
 */
_slinkedlink slinkedlist_bubble_sort(_slinkedlink H, int mode)
{
    /* 定义三个链表结构体指针变量 */
    _slinkedlink p, q, temp;
    int i, j;
    int len = 0;

    /* 1.判断链表是否为存在, 不存在则直接返回 */
    if (H == NULL)
    {
        printf("H is NULL(not exist)!\n");
        return NULL;
    }
    /* 2.求链表长度 */
    len = slinkedlist_length(H);

    /* 3.冒泡排序 */
    for (i = 0; i < len - 1; i++) /* 外层循环, 进行 len-1 趟数据比较 */
    {
        /* 进行完一轮后,从头开始进行下一轮 */
        q = H->next;                      /* 令 q 指向第一个结点 */
        p = q->next;                      /* 令 p 指向后一个结点 */
        temp = H;                         /* 让 temp 始终指向 q 前一个结点,方便交换 */
        for (j = 0; j < len - 1 - i; j++) /* 每一趟进行 len-1-i 次比较 */
        {
            if (mode == 1 && (q->data > p->data)) /* 如果该结点的值大于后一个结点,则交换*/
            {

                q->next = p->next;
                p->next = q;
                temp->next = p;
            }
            else if (mode == 0 && (q->data < p->data)) /* 如果该结点的值小于后一个结点,则交换*/
            {

                q->next = p->next;
                p->next = q;
                temp->next = p;
            }
            /* 移动到下一个节点 */
            temp = temp->next;
            q = temp->next;
            p = q->next;
        }
    }
    return H;
}

/**
 * @Function   : slinkedlist_intersect
 * @brief      : 判断两链表是否相交
 * @param H1   : _slinkedlink 类型,链表 H1 的首地址
 * @param H2   : _slinkedlink 类型,链表 H2 的首地址
 * @return     : int 类型,1, 两个链表相交; 0, 两个链表不相交;-1, 链表不存在
 * @Description:
 */
int slinkedlist_intersect(_slinkedlink H1, _slinkedlink H2)
{
    /* 定义两个个链表结构体指针变量 */
    _slinkedlink p;
    _slinkedlink q;

    /* 1. 判断链表是否为存在, 不存在则直接返回 */
    if (H1 == NULL || H2 == NULL)
    {
        printf("Single linked list H1 or H2 is not existed!\n");
        return -1;
    }

    /* 2. 寻找尾节点 */
    p = H1;
    q = H2;
    while (p->next)
    {
        p = p->next;
    }
    while (q->next)
    {
        q = q->next;
    }
    /* 3. 判断是否相交 */
    if (p == q)
        return 1;
    else
        return 0;
}

/**
 * @Function   : slinkedlist_adjmax
 * @brief      : 求相邻两节点和最大时的第一个节点指针
 * @param H    : _slinkedlink 类型,链表 H 的首地址
 * @param value:data_t * 类型,存放相邻两节点和的最大值,需要传入地址
 * @return     : _slinkedlink 类型,链表不存在返回 NULL; 否则返回相邻两节点和最大时的第一个节点指针
 * @Description:
 */
_slinkedlink slinkedlist_adjmax(_slinkedlink H, data_t *value)
{
    /* 定义单个结构体变量指针, r 用于记录此次比较的目标节点 */
    _slinkedlink p, q, r;
    data_t sum;
    /* 1.判断链表是否为存在, 不存在则直接返回 */
    if (H == NULL)
    {
        printf("H is NULL(not exist)!\n");
        return NULL;
    }
    /* 2. 判断是否至少有三个节点,若小于三个节点,则直接返回 H 即可 */
    if (H->next == NULL || H->next->next == NULL || H->next->next->next == NULL)
    {
        return H;
    }
    /* 3. 开始比较相邻两节点和的值,找出最大值 */
    q = H->next;
    p = H->next->next; /* p = q-> next; */
    r = q;
    sum = q->data + p->data;

    while (p->next != NULL)
    {
        p = p->next;
        q = q->next;
        if (sum < q->data + p->data)
        {
            sum = q->data + p->data;
            r = q;
        }
    }
    /* 4. 传回最大值 */
    *value = sum;
    return r;
}

/* 测试函数实现 */
int test_slinkedlist_tail_insert()
{
    _slinkedlink H;
    int a[5] = {1, 3, 4, 5, 6};
    int i = 0;
    H = slinkedlist_create();
    if (H == NULL)
        return -1;
    for (i = 0; i < 5; i++)
    {
        slinkedlist_tail_insert(H, a[i]);
    }
    slinkedlist_show(H);
    printf("H=%p\n", H);
    H = slinkedlist_free(H);
    printf("H=%p\n", H);
    return 0;
}

void test_slinkedlist_node_get()
{
    _slinkedlink H;
    _slinkedlink p;
    int a[5] = {1, 3, 4, 5, 6};
    int i = 0;
    H = slinkedlist_create();
    if (H == NULL)
        return;
    for (i = 0; i < 5; i++)
    {
        slinkedlist_tail_insert(H, a[i]);
    }
    slinkedlist_show(H);
    for (i = -2; i < 7; i++)
    {
        p = slinkedlist_node_get(H, i);
        if (p != NULL)
            printf("i = %d,value=%d\n", i, p->data);
    }

    printf("H=%p\n", H);
    H = slinkedlist_free(H);
    printf("H=%p\n", H);
}

void test_slinkedlist_insert()
{
    _slinkedlink H;
    int a[5] = {1, 3, 4, 5, 6};
    int i = 0;
    H = slinkedlist_create();
    if (H == NULL)
        return;
    slinkedlist_insert(H, a[0], 0);
    for (i = 1; i < 5; i++)
    {
        slinkedlist_insert(H, a[i], 1);
    }
    slinkedlist_show(H);

    slinkedlist_insert(H, 100, 2);
    slinkedlist_insert(H, 200, 50);
    slinkedlist_insert(H, 300, -9);
    slinkedlist_show(H);
    printf("H=%p\n", H);
    H = slinkedlist_free(H);
    printf("H=%p\n", H);
}

void test_slinkedlist_delete()
{
    _slinkedlink H;
    int a[5] = {1, 3, 4, 5, 6};
    int i = 0;
    H = slinkedlist_create();
    if (H == NULL)
        return;
    for (i = 0; i < 5; i++)
    {
        slinkedlist_tail_insert(H, a[i]);
    }
    slinkedlist_show(H);

    slinkedlist_delete(H, -4);
    slinkedlist_delete(H, 50);
    slinkedlist_delete(H, 2);
    slinkedlist_delete(H, 3);
    slinkedlist_delete(H, 3);
    slinkedlist_show(H);
    printf("H=%p\n", H);
    H = slinkedlist_free(H);
    printf("H=%p\n", H);
}

void test_slinkedlist_node_update()
{
    _slinkedlink H;
    int a[5] = {1, 3, 4, 5, 6};
    int i = 0;
    H = slinkedlist_create();
    if (H == NULL)
        return;
    for (i = 0; i < 5; i++)
    {
        slinkedlist_tail_insert(H, a[i]);
    }
    slinkedlist_show(H);
    slinkedlist_node_update(H, 100, 2);
    slinkedlist_show(H);
    slinkedlist_node_update(H, 100, 0);
    slinkedlist_show(H);
    slinkedlist_node_update(H, 100, 4);
    slinkedlist_show(H);
    slinkedlist_node_update(H, 100, 5);
    slinkedlist_show(H);
    slinkedlist_node_update(H, 100, -1);
    slinkedlist_show(H);
    printf("H=%p\n", H);
    H = slinkedlist_free(H);
    printf("H=%p\n", H);
}

void test_slinkedlist_length()
{
    _slinkedlink H;

    int a[6] = {1, 3, 7, 5, 6, 8};
    int i = 0;
    H = slinkedlist_create();
    if (H == NULL)
        return;
    for (i = 0; i < 6; i++)
    {
        slinkedlist_tail_insert(H, a[i]);
    }
    slinkedlist_show(H);

    printf("len=%d\n", slinkedlist_length(H));

    printf("H=%p\n", H);
    H = slinkedlist_free(H);
    printf("H=%p\n", H);
}

void test_slinkedlist_reverse_iteration()
{
    _slinkedlink H;

    int a[6] = {1, 3, 4, 5, 6, 7};
    int i = 0;
    H = slinkedlist_create();
    if (H == NULL)
        return;
    for (i = 0; i < 6; i++)
    {
        slinkedlist_tail_insert(H, a[i]);
    }
    slinkedlist_show(H);

    printf("H=%p\n", H);
    H = slinkedlist_reverse_iteration(H);
    slinkedlist_show(H);
    printf("H=%p\n", H);
    H = slinkedlist_free(H);
    printf("H=%p\n", H);
}
void test_slinkedlist_reverse_recursiveH()
{
    _slinkedlink H;

    int a[6] = {1, 3, 4, 5, 6, 7};
    int i = 0;
    H = slinkedlist_create();
    if (H == NULL)
        return;
    for (i = 0; i < 6; i++)
    {
        slinkedlist_tail_insert(H, a[i]);
    }
    slinkedlist_show(H);

    printf("H=%p\n", H);
    H = slinkedlist_reverse_recursiveH(H);
    slinkedlist_show(H);
    printf("H=%p\n", H);
    H = slinkedlist_free(H);
    printf("H=%p\n", H);
}
void test_slinkedlist_reverse_insertH()
{
    _slinkedlink H;

    int a[6] = {1, 3, 4, 5, 6, 7};
    int i = 0;
    H = slinkedlist_create();
    if (H == NULL)
        return;
    for (i = 0; i < 6; i++)
    {
        slinkedlist_tail_insert(H, a[i]);
    }
    slinkedlist_show(H);

    printf("H=%p\n", H);
    slinkedlist_reverse_insertH(H);
    slinkedlist_show(H);
    printf("H=%p\n", H);
    H = slinkedlist_free(H);
    printf("H=%p\n", H);
}

void test_slinkedlist_reverse_local()
{
    _slinkedlink H;

    int a[6] = {1, 3, 7, 5, 6, 8};
    int i = 0;
    H = slinkedlist_create();
    if (H == NULL)
        return;
    for (i = 0; i < 6; i++)
    {
        slinkedlist_tail_insert(H, a[i]);
    }
    slinkedlist_show(H);

    printf("H=%p\n", H);
    H = slinkedlist_reverse_local(H);
    slinkedlist_show(H);
    printf("H=%p\n", H);
    H = slinkedlist_free(H);
    printf("H=%p\n", H);
}

void test_slinkedlist_bubble_sort()
{
	_slinkedlink H;

	int a[] = {1, 5, 3, 2, 9, 10, 7};
	int i = 0;
	H = slinkedlist_create();
	if (H == NULL)
		return;
	for(i = 0; i < sizeof(a)/sizeof(int); i++)
	{
		slinkedlist_tail_insert(H, a[i]);
	}
	slinkedlist_show(H);

	H = slinkedlist_bubble_sort(H, 1);
	slinkedlist_show(H);
	H = slinkedlist_bubble_sort(H, 0);
	slinkedlist_show(H);

	printf("H=%p\n", H);
	H = slinkedlist_free(H);
	printf("H=%p\n", H);

}

void test_slinkedlist_adjmax()
{
	_slinkedlink H;
	_slinkedlink r;
	data_t sum;

	int a[] = {1, 5, 3, 2, 9, 10, 7};
	int i = 0;
	H = slinkedlist_create();
	if (H == NULL)
		return;
	for(i = 0; i < sizeof(a)/sizeof(int); i++)
	{
		slinkedlist_tail_insert(H, a[i]);
	}
	slinkedlist_show(H);

	r = slinkedlist_adjmax(H, &sum);
	if (r != NULL && r != H)
	{
		printf("data=%d, sum=%d\n", r->data, sum);
	}
	slinkedlist_show(H);

	printf("H=%p\n", H);
	H = slinkedlist_free(H);
	printf("H=%p\n", H);
}

void test_slinkedlist_intersect()
{
	_slinkedlink H1;
	_slinkedlink H2;
	_slinkedlink H3;
	_slinkedlink p1;
	_slinkedlink p2;
	_slinkedlink temp;

	int a[] = {1, 2, 3};
	int b[] = {4, 5, 6, 7};
	int c[] = {8, 9, 10, 11, 12};
	int i = 0;
	H1 = slinkedlist_create();
	H2 = slinkedlist_create();
	H3 = slinkedlist_create();
	if (H1 == NULL || H2 == NULL || H3 == NULL)
		return;
	for(i = 0; i < sizeof(a)/sizeof(int); i++)
	{
		slinkedlist_tail_insert(H1, a[i]);
	}
	for(i = 0; i < sizeof(b)/sizeof(int); i++)
	{
		slinkedlist_tail_insert(H2, b[i]);
	}
	for(i = 0; i < sizeof(c)/sizeof(int); i++)
	{
		slinkedlist_tail_insert(H3, c[i]);
	}
	slinkedlist_show(H1);
	slinkedlist_show(H2);
	slinkedlist_show(H3);
	p1 = slinkedlist_node_get(H1, 2);
	p2 = slinkedlist_node_get(H2, 3);
	temp = p1->next;/* 保护后边的节点 */
	p1->next = p2;
	printf("=========intersect1 test=========\n");
	slinkedlist_show(H1);
	slinkedlist_show(H2);
	slinkedlist_show(H3);
	printf("H1,H2:%d\n", slinkedlist_intersect(H1, H2));
	printf("H1,H3:%d\n", slinkedlist_intersect(H1, H3));
	printf("H2,H3:%d\n", slinkedlist_intersect(H2, H3));
	printf("========end==========\n");
	/* 注意释放的时候相交链表可能会有问题,这里要让他们不再相交 */
	p1->next = temp;

	slinkedlist_show(H1);
	slinkedlist_show(H2);
	slinkedlist_show(H3);
	printf("========free==========\n");
	H1 = slinkedlist_free(H1);
	H2 = slinkedlist_free(H2);
	H3 = slinkedlist_free(H3);
}

2. slinkedlist.h

c
/** ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== =
 * Copyright © hk. 2022-2025. All rights reserved.
 * File name  : slinkedlist.h
 * Author     : qidaink
 * Date       : 2022-10-21
 * Version    :
 * Description:
 * Others     :
 * Log        :
 * ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==
 */
#ifndef __SLINKEDLIST_H__
#define __SLINKEDLIST_H__

/* 自定义数据类型 */
typedef int data_t;

typedef struct __node
{
    data_t data;
    struct __node *next;
} _slinkednode, *_slinkedlink;

/* 函数声明 */

_slinkedlink slinkedlist_create(void);                              /* 单链表创建 */
_slinkedlink slinkedlist_free(_slinkedlink H);                      /* 单链表空间释放(释放整个单链表占用的内存空间) */
int slinkedlist_show(_slinkedlink H);                               /* 单链表数据显示 */
_slinkedlink slinkedlist_node_get(_slinkedlink H, int pos);         /* 单链表节点查找(按位置查找) */
int slinkedlist_tail_insert(_slinkedlink H, data_t value);          /* 单链表尾部插入数据 */
int slinkedlist_insert(_slinkedlink H, data_t value, int pos);      /* 单链表在指定位置插入节点 */
int slinkedlist_delete(_slinkedlink H, int pos);                    /* 删除指定位置节点 */
int slinkedlist_node_update(_slinkedlink H, data_t value, int pos); /* 更新指定位置的节点数据 */
int slinkedlist_length(_slinkedlink H);                             /* 获取单链表长度(不包含头节点) */
int slinkedlist_reverse_insertH(_slinkedlink H);                    /* 头插法反转单链表 */
_slinkedlink slinkedlist_reverse_iteration(_slinkedlink H);         /* 迭代法反转链表 */
_slinkedlink slinkedlist_reverse_recursiveH(_slinkedlink H);        /* 递归法反转单链表 */
_slinkedlink slinkedlist_reverse_recursive(_slinkedlink H);         /* 递归法反转单链表递归实现 */
_slinkedlink slinkedlist_reverse_local(_slinkedlink H);             /* 本地逆置法反转单链表 */
_slinkedlink slinkedlist_bubble_sort(_slinkedlink H, int mode);     /* 单链表排序(冒泡法排序) */
int slinkedlist_intersect(_slinkedlink H1, _slinkedlink H2);        /* 判断两链表是否相交 */
_slinkedlink slinkedlist_adjmax(_slinkedlink H, data_t *value);     /* 求相邻两节点和最大时的第一个节点指针 */

int test_slinkedlist_tail_insert();  /* 单链表尾部插入测试 */
void test_slinkedlist_node_get();    /* 节点查找测试 */
void test_slinkedlist_insert();      /* 指定位置插入测试 */
void test_slinkedlist_delete();      /* 按位置删除节点测试 */
void test_slinkedlist_node_update(); /* 更新节点数据测试 */
void test_slinkedlist_length();      /* 获取链表长度测试 */

void test_slinkedlist_reverse_iteration();  /* 迭代法反转单链表测试 */
void test_slinkedlist_reverse_recursiveH(); /* 递归法反转单链表测试 */
void test_slinkedlist_reverse_insertH();    /* 头插法反转单链表测试 */
void test_slinkedlist_reverse_local();      /* 本地逆置法反转单链表测试 */

void test_slinkedlist_bubble_sort(); /* 冒泡法链表排序测试 */
void test_slinkedlist_adjmax();      /* 求相邻两节点和最大时的第一个节点指针测试 */
void test_slinkedlist_intersect();   /* 判断两链表是否相交测试 */

#endif

3. main.c

c
#include <stdio.h>
#include "slinkedlist.h"

int main(int argc, char *argv[])
{
	// test_slinkedlist_tail_insert();
	// test_slinkedlist_node_get();
	// test_slinkedlist_insert();
	// test_slinkedlist_delete();
	// test_slinkedlist_node_update();
	// test_slinkedlist_length();

	// test_slinkedlist_reverse_iteration();
	// test_slinkedlist_reverse_recursiveH();
	// test_slinkedlist_reverse_insertH();
	// test_slinkedlist_reverse_local();

	test_slinkedlist_bubble_sort();
	// test_slinkedlist_adjmax();
	// test_slinkedlist_intersect();
	return 0;
}

4. Makefile

makefile
##============================================================================#
# Copyright © hk. 2022-2025. All rights reserved.
# File name  : Makefile
# Author     : qidaink
# Date       : 2022-09-29
# Version    : 
# Description: 
# Others     : 
# Log        : 
##============================================================================#
##

TARGET          := main
#============================================================================#
## 工具设置
# 设置交叉编译器名称
CROSS_COMPILE   :=
# 设置交叉编译器
CC              := $(CROSS_COMPILE)gcc
# 编译选项
CFLAGS          += -g -Wall
#============================================================================#
## 目录路径设置
# 头文件路径设置(.h)
INCDIRS 		:= .

# 源文件路径设置(.S .C)
SRCDIRS			:= .

# 生成的中间 .o 文件的位置
OBJDIRS         := .
#============================================================================#
# 生成编译器的 -I dir_path 参数 
INCLUDE			:= $(patsubst %, -I %, $(INCDIRS))

# 获取所有的 .C 文件名称(包含路径)
CFILES			:= $(foreach dir, $(SRCDIRS), $(wildcard $(dir)/*.c))

# 获取所有的 .C 文件名称(不包含路径)
CFILENDIR		:= $(notdir  $(CFILES))

# 指定所有的 .C 生成的 .o 文件名(包含将要存放的路径)
COBJS			:= $(patsubst %, $(OBJDIRS)/%, $(CFILENDIR:.c=.o))

# 将所有要生成的 .o 文件合并到一个变量中(都包含路径)
OBJS			:= $(COBJS)

# 设置源文件路径
VPATH			:= $(SRCDIRS)

#============================================================================#			
# 生成目标文件	   
$(TARGET): $(OBJS)
	$(CC) $(CFLAGS) $^ -o $(TARGET)

# 隐含规则推导相关中间文件
$(COBJS) : $(OBJDIRS)/%.o : %.c
	$(CC) $(CFLAGS) -c $(INCLUDE) $<  -o $@ 
	
#============================================================================#	
.PHONY: clean clean_o
# 删除所有编译链接生成的文件命令
clean: clean_o
	@rm -vf $(TARGET)

# 删除所有的 .o 文件命令
clean_o:
	@rm -vf $(COBJS)

printf:
	@echo "CFLAGS    = $(CFLAGS)"
	@echo "INCDIRS   = $(INCDIRS)"
	@echo "SRCDIRS   = $(SRCDIRS)"
	@echo "INCLUDE   = $(INCLUDE)"
	@echo "CFILES    = $(CFILES)"
	@echo "CFILENDIR = $(CFILENDIR)"
	@echo "COBJS     = $(COBJS)"
	@echo "OBJS      = $(OBJS)"
	@echo "VPATH     = $(VPATH)"