单链表

单链表

1. 基本概念

将线性表\(L=(a_0,...a_{i-1},a_i,a_{i+1}...a_{n-1})\)中的各元素分布在存储器的不同存储块,称每个元素为节点,通过地址或指针建立元素之间的联系。节点数据结构定义如下:

typedef struct node
{
	data_t data;		// 节点的数据域
	struct node *next;	// 节点的后继指针
}listnode,*linklist;

节点的data域存放数据元素\(a_i\),而next域是一个指针,指向\(a_i\)的直接后继\(a_{i+1}\)所在的节点。

优缺点:
​ 1.优点,链表的存储结构不需要联系的地址空间,节点的插入和删除不需要成片移动;

​ 2.缺点,不支持随机存储,因为空间不连续;

2. 实现

image-20250130191306384

linklist.h

typedef int data_t;	// 节点数据类型

typedef struct node 
{
	data_t data;		// 节点数据域
	struct node * next;	// 节点指针域
}listnode, * linklist;	

linklist list_create();									// 创建链表,返回值为头结点指针
int list_empty(linklist H);								// 判断链表是否为空,返回值 0非空表,-1空表
int list_tail_insert(linklist H, data_t value);			// 链表尾部插入函数, 返回值 0插入成功,-1插入失败
int list_head_insert(linklist H, data_t value);			// 链表头部插入函数, 返回值 0插入成功,-1插入失败
int list_show(linklist H);								// 遍历链表,返回值 0遍历成功,-1遍历失败
int list_length(linklist H);							// 获取链表长度,返回值 链表长度,-1则链表不存在
linklist list_get(linklist H, int pos);					// 获取链表第pos个节点,返回值 链表第pos个节点地址,NULL则链表不存在
int list_insert(linklist H, int pos, data_t value);		// 链表插入函数,返回值 0插入成功,-1插入失败
int list_delete(linklist H, int pos);					// 链表删除函数,返回值 0删除成功,-1删除失败
void list_free(linklist *H);							// 释放链表函数

int list_reverse(linklist H);							// 链表反转函数,返回值 0反转成功,-1反转失败
linklist list_adjmax(linklist H,int *max);				// 链表相邻最大值函数,返回值 链表头结点指针,NULL则链表不存在
linklist list_merge(linklist H1, linklist H2);			// 链表合并函数(两个有序链表),返回值 合并后新链表头结点指针,NULL则链表不存在

linklist.c

#include "linklist.h"
#include <stdlib.h>	// malloc()函数头文件
#include <stdio.h>

/**
  * @name 	list_create()
  * @brief	创建链表函数
  * @param  无
  * @retval 表地址
  */
linklist list_create()
{
    linklist H;
    // 1. 申请头结点内存
    H = (linklist)malloc(sizeof(listnode));
    if (H == NULL)
    {
        printf("malloc fail\n");
        return H;
    }

    // 2. 赋初值
    H->data = 0;
    H->next = NULL;

    return H;
}
/**
  * @name 	int list_empty(linklist H)
  * @brief	链表尾部插入函数
  * @param  H 表地址,value 插入的值
  * @retval 0-链表不是空表,-1-链表为空表
  */
int list_empty(linklist H)
{
    if (H == NULL)
    {
        printf("list is not exist\n");
        return -1;
    }
    else if (H->next == NULL)
    {
        return -1;
    }
    else
    {
        return 0;
    }
}
/**
  * @name 	list_tail_insert()
  * @brief	链表尾部插入函数
  * @param  H 表地址,value 插入的值
  * @retval 0-成功插入,-1-插入失败
  */
int list_tail_insert(linklist H, data_t value)
{
    linklist NewNode;   // 新节点
    linklist tem;       // 临时节点,记录遍历过程节点地址

    // 1. 判断链表H是否存在
    if (H == NULL)
    {
        printf("list is not exist\n");
        return -1;
    }
    
    // 2. 创建一个新节点p,赋初值
    if ((NewNode = (linklist)malloc(sizeof(listnode))) == NULL)
    {
        printf("malloc fail\n");
        return -1;
    }
    NewNode->data = value;
    NewNode->next = NULL;

    // 3. 查找链表的尾结点q,将尾节点q的next指向新节点p
    tem = H;
    while (tem->next != NULL)
    {
        tem = tem->next;
    }
    tem->next = NewNode;

    return 0; 
} 

/**
  * @name 	list_head_insert(linklist H, data_t value)
  * @brief	链表头部插入函数
  * @param  H 表地址,value 插入的值
  * @retval 0-成功插入,-1-插入失败
  */
int list_head_insert(linklist H, data_t value)
{
    linklist NewNode;   // 新节点
    
    // 1. 判断链表H是否存在
    if (H == NULL)
    {
        printf("list is not exist\n");
        return -1;
    }
    
    // 2. 创建一个新节点p,赋初值
    if ((NewNode = (linklist)malloc(sizeof(listnode))) == NULL)
    {
        printf("malloc fail\n");
        return -1;
    }
    NewNode->data = value;
    NewNode->next = NULL;

    // 3. 将头节点q的next指向新节点p
    NewNode->next = H->next;
    H->next = NewNode;

    return 0;
}

/**
  * @name 	list_show()
  * @brief	链表遍历函数
  * @param  H 表地址
  * @retval 0-成功遍历,-1-遍历失败
  */
int list_show(linklist H)
{
    linklist tem;   // 临时节点,记录遍历过程节点地址

    // 1. 判断链表H是否存在
    if (H == NULL)
    {
        printf("list is not exist\n");
        return -1;
    }

    // 2. 遍历链表,打印出所有节点的值
    tem = H->next;
    while (tem != NULL)
    {
        printf("%d ", tem->data);
        tem = tem->next;
    }
    puts("");

    return 0;
}

/**
  * @name 	list_length()
  * @brief	获取链表长度函数
  * @param  H 表地址
  * @retval 链表长度,-1则链表不存在
  */
int list_length(linklist H)
{
    linklist tem;   // 临时节点,记录遍历过程节点地址
    int i;

    // 1. 判断链表H是否存在
    if (H == NULL)
    {
        printf("list is not exist\n");
        return -1;
    }

    // 2. 遍历链表,统计链表长度
    tem = H;
    i = 0;
    while (tem->next != NULL)
    {
        i++;
        tem = tem->next;
    }
    return i;
}

/**
  * @name 	list_get()
  * @brief	获取链表第pos个节点
  * @param  H 表地址
  * @retval 要获取的节点地址
  */
linklist list_get(linklist H, int pos)
{
    linklist tem;   // 临时节点,存放pos节点地址
    int i;

    // 1. 判断链表H是否存在,以及pos是否合法
    if (H == NULL)
    {
        printf("list is not exist\n");
        return H;
    }
    if (pos < -1 || pos >= list_length(H))	// pos不合法
    {
        printf("pos is invalid\n");
        return NULL;
    }
    
    // 2. 遍历链表,找到第pos个节点
    tem = H;
    for (i = -1; i < pos; i++)
    {
        tem = tem->next;
    }

    return tem;
}

/**
  * @name 	list_insert()
  * @brief	链表插入函数
  * @param  H 表地址,pos 插入的位置,value 插入的值
  * @retval 0-成功插入,-1-插入失败
  */
int list_insert(linklist H, int pos, data_t value)
{
    linklist NewNode;
    linklist tem;        // 临时节点,存放pos-1个节点

    // 1. 创建一个新节点q,赋初值
    if ((NewNode = (linklist)malloc(sizeof(listnode))) == NULL)
    {
        printf("malloc fail\n");
        return -1;
    }
    NewNode->data = value;
    NewNode->next = NULL;

    // 2. 查找链表的第pos-1个节点,并将其next给到新节点q,其自身next改为q节点地址

    tem = list_get(H, pos-1);
    if (tem == NULL)
    {
        return -1;
    }
    NewNode->next = tem->next;
    tem->next = NewNode;

    return 0;
}

/**
  * @name 	list_delete()
  * @brief	链表删除函数
  * @param  H 表地址,pos 删除的链表位置
  * @retval 0-成功删除,-1-删除失败
  */
 int list_delete(linklist H, int pos)
{
    linklist tem1;   // 临时节点tem1,存放pos-1的节点
    linklist tem2;   // 临时节点tem1,存放pos的节点
    // 1. 判断链表H是否存在,以及pos是否合法
    if (H == NULL)
    {
        printf("list is not exist\n");
        return -1;
    }
    if (pos < 0 || pos > list_length(H))	// pos不合法
    {
        printf("pos is invalid\n");
        return -1;
    }

    // 2. 遍历链表,找到第pos个节点的前一个节点,并删除该节点
    tem1 = list_get(H, pos-1);  // 找到第pos-1个节点
    tem2 = tem1->next;          // 找到第pos个节点
    //printf("delete %d\n", tem2->data);  
    tem1->next = tem1->next->next;  // 将第pos-1个节点的next指向第pos+1个节点
    free(tem2);                     // 释放第pos个节点的内存
    tem2 = NULL;
    return 0;
}

/**
  * @name 	list_free()
  * @brief	链表销毁函数,使用时注意将返回值赋值给链表
  * @param  H 表地址
  * @retval NULL
  */
void list_free(linklist *H)
{
    linklist tem;   // 临时节点,记录遍历过程节点地址

    // 1. 判断链表H是否存在
    if (*H == NULL)
    {
        printf("list is not exist\n");
        return;
    }

    // 2.遍历链表,释放所有节点
    while (*H != NULL)
    {
        tem = (*H)->next;
    // printf("free %d\n", tem->data);
        free(*H);
        *H = tem;
    }
    *H = NULL;
}

/**
  * @name 	list_reverse(linklist H)
  * @brief	链表反转函数
  * @param  H 表地址
  * @retval 0-成功反转,-1-反转失败
  */
int list_reverse(linklist H)
{
    int length = list_length(H);

    // 1. 判断链表H是否存在
    if (H == NULL)
    {
        printf("list is not exist\n");
        return -1;
    }

    // 2. 创建新链表,并遍历链表,将链表反转
    printf("length:%d\n",length);
    while (length)
    {
        list_insert(H, length--,H->next->data);
        list_delete(H, 0);
    } 
    return 0;
}

/**
  * @name 	linklist list_adjmax(linklist H)
  * @brief	查找链表相邻两节点最大值函数
  * @param  H 表地址
  * @retval 两节点中的前一节点地址
  */
linklist list_adjmax(linklist H,int *max)
{
    linklist tem;
    int temp;
    // 1. 判断链表H是否存在,以及链表H内元素是否大于2
    if (H == NULL || H->next == NULL || H->next->next == NULL) 
    {
        printf("list is invalid\n");
        return NULL;
    }

    while (H->next->next != NULL)
    {
        temp = H->next->data + H->next->next->data;
        if (*max < temp)
        {
            //  printf("a1:%d\n", *max);
            *max = temp;
            // printf("a2:%d\n", max);
            tem = H->next;
            //  printf("bb:%p\n", tem);
        }

        H = H->next;
        //  printf("***%d**\n",H->data);
    }
    //  printf("***llll**\n");

    return tem;
}

/**
  * @name   list_merge(linklist H)
  * @brief	链表合并函数(两个有序链表)
  * @param  H1 表1地址,H2 表2地址
  * @retval 合并后新链表头结点指针,NULL则链表不存在
  */
linklist list_merge(linklist H1, linklist H2)
{   
    linklist tem1, tem2, H3;
    tem1 = H1->next;
    tem2 = H2->next;
    // 1. 判断链表H1和H2是否存在
    while (H1 == NULL || H2 == NULL)
    {
        printf("list is invalid\n");
        return NULL;
    }
    
    // // 2. 创建新链表H3,并遍历链表,将链表合并
    H3 = list_create();
    while (tem1 != NULL && tem2 != NULL)
    {
        // printf("tem1:%d\n", tem1->data);
        // printf("tem2:%d\n", tem2->data);
        // scanf("%d",&i);
        if (tem1->data < tem2->data)
        {
            list_tail_insert(H3, tem1->data);
            tem1 = tem1->next;
        }
        else
        {
            list_tail_insert(H3, tem2->data);
            tem2 = tem2->next;
        }
        // printf("tem3:%d\n", tem3->data);
    }
    if (tem1 ==  NULL)
    {
        while (tem2 != NULL)
        {
            list_tail_insert(H3, tem2->data);
            tem2 = tem2->next;
        }
    }
    else
    {
        while (tem1 != NULL)
        {
            list_tail_insert(H3, tem1->data);
            tem1 = tem1->next;
        }
    }
    return H3; 

    // 该方法会影响链表H1和H2,通过改变每个节点的next地址生成H3
    // while (tem1 != NULL && tem2 != NULL)
    // {
    //     // printf("tem1:%d\n", tem1->data);
    //     // printf("tem2:%d\n", tem2->data);
    //     // scanf("%d",&i);
    //     if (tem1->data < tem2->data)
    //     {
    //         tem3->next = tem1;
    //         tem1 = tem1->next;
    //     }
    //     else
    //     {
    //         tem3->next = tem2;
    //         tem2 = tem2->next;
    //     }
    //     tem3 = tem3->next;
    //     // printf("tem3:%d\n", tem3->data);
    // }
    // if (tem1 ==  NULL)
    // {
    //     tem3->next = tem2;
    // }
    // else
    // {
    //     tem3->next = tem1;
    // }
    // return H3;
}

main.c

#include <stdio.h>
#include "linklist.h"
#include <stdlib.h>
#include <time.h>

void test_create();
void test_delete();
void test_reverse();
void test_adjmax();
void test_merge();

int main(int argc, const char *argv[])
{
	// test_create();
	// test_delete();
	// test_reverse();
	// test_adjmax();
	// test_merge();
	return 0;
}
void test_create()
{
	linklist H;
	int value;
	int pos,length;

	H = (linklist)list_create();
	printf("**********test_create*******\n");
	printf("list_tail_insert:\n");
	while (1)
	{
		printf("input:");
		scanf("%d", &value);
		if (value == -1)
		{
			break;
		}
		list_tail_insert(H, value);
	}
	printf("list_insert:\n");
	while (1)
	{
		printf("input:[pos],[value]\n");
		scanf("%d,%d", &pos, &value);
		if (pos == -1)
		{
			break;
		}
		list_insert(H, pos, value);
	}
	printf("**********lsit_show*******\n");
	list_show(H);
	length = list_length(H);
	printf("length:%d\n",length);

	printf("get pos:");
	scanf("%d", &pos);
	printf("**********test_get*******\n");
	if (list_get(H, pos) == NULL)
	{
		return;
	}
	printf("value:%d\n",list_get(H, pos)->data);
	list_free(&H);
	return;
}
void test_delete(void)
{
	linklist H;
	int value, pos;
	int length=5;

	H = (linklist)list_create();
	printf("**********test_delete*******\n");
	srand((unsigned)time(NULL)); 
	while (length)
	{
		value = rand()%100+1;
		list_tail_insert(H, value);
		length--;
	}
	printf("******before_delete******\n");
	list_show(H);
	printf("delete pos:");
	scanf("%d", &pos);
	list_delete(H, pos);
	printf("******after_delete******\n");
	list_show(H);
	list_free(&H);
	printf("*****after_free*******\n");
	list_show(H);
	return;
}

void test_reverse()
{
	linklist H;
	int value;
	int length = 7;

	H = (linklist)list_create();
	printf("**********test_reverse*******\n");
	srand((unsigned)time(NULL)); 
	while (length)
	{
		value = rand()%100+1;
		list_tail_insert(H, value);
		length--;
	}
	printf("******before_reverse******\n");
	list_show(H);
	printf("******after_reverse******\n");
	list_reverse(H);
	list_show(H);
	return;
}

void test_adjmax()
{
	linklist H;
	linklist p;
	int value;
	int *max = malloc(sizeof(int));
	int length;

	H = (linklist)list_create();
	printf("**********test_adjmax*******\n");
	printf("input length:");
	scanf("%d", &length);
	srand((unsigned)time(NULL)); 
	while (length)
	{
		value = rand()%100+1;
		list_tail_insert(H, value);
		length--;
	}
	printf("******before_adjmax******\n");
	list_show(H);
	printf("******after_adjmax******\n");
	p = list_adjmax(H, max);
	printf("value:%d\n",p->data);
	printf("max:%d\n", *max);
	list_free(&H);
	list_show(H);
	return;
}

void test_merge()
{
	linklist H1, H2, H3;
	int a[] = {1, 3, 5, 9, 17};
	int b[] = {2, 6, 8, 13, 19};

	H1 = (linklist)list_create();
	H2 = (linklist)list_create();
	printf("**********test_merge*******\n");
	printf("befor_merge:\n");
	for (int i = 0; i < sizeof(a)/sizeof(int); i++)
	{
		list_tail_insert(H1, a[i]);
	}
	for (int j = 0; j < sizeof(b)/sizeof(int); j++)
	{
		list_tail_insert(H2, b[j]);
	}
	
	list_show(H1);
	list_show(H2);

	H3 = list_merge(H1, H2);
	printf("after_merge:\n");
	list_show(H3);
	list_show(H1);
	list_show(H2);

	list_free(&H1);
	list_free(&H2);
	list_free(&H3);

	list_show(H1);
	list_show(H2);
	list_show(H3);
	return;
}
posted @ 2025-01-26 23:51  Arsun  阅读(18)  评论(0)    收藏  举报