单链表 之c代码

我们知道数据结构就是数据及其相互关系,包括逻辑结构和物理结构。单链表的逻辑结构是一种一对一的线性关系,物理结构是利用节点把数据结合起来,在计算机中体现这种一对一的数据关系。单链表节点包括包含数据本身信息的数据域和体现数据一对一关系的指针域。因为单链表只有一个指向后一节点的单一指针域next 所以单链表只能从前往后遍历,而不能从后向前遍历,这就意味着一旦单链表的某一节点丢失 ,后面所有的数据信息都会丢失,并且单链表有头指针唯一确定,要想查找单链表的

某一数据只能从头开始遍历,最坏时间复杂度为O(n),但因为数据间是通过指针来确定数据的前后关系的,所以单链表在内存中是不一定要连续分配的内存,可以更有效的利用内存空间,同时可以快速的在一个节点后插入和删除一个数据元素。在某一节点后插入和删除一个节点的最坏时间复杂度O(1).在编写程序时,特别是面向过程的c语言程序一定牢记 程序 =数据结构+ 算法。数据在内存中有两种存储方式,一种就是连续存储的数组,另一种就是结构体。算法就是对数据的操作。同时 数据结构 和定义在数据结构上的算法 组成了抽象数据类型,注意这里是类型,类型不只是数据,还有定义在该数据的相关算法。

单链表 是n个相同节点组成的,每一个节点又包含了两个信息 ,包含数据信息的数据域,包含数据间关系的指针域所以要声明一个单链表结构体之前我们必须声明一个节点结构体单链表节点结构体ListNode。

1.头文件list.h

//单链表List.h
#ifndef LIST_H
#define LIST_H

//单链表节点的数据结构
typedef struct ListNode{
	void * data;//因为 不知道单链表中存储的是何种数据 ,为了抽象所以用一个泛型指针 void* 指向数据
	ListNode *next;//指向下一节点的指针 next
}ListNode;

//可针对单链表节点的相关操作(1)用一个数据初始化一个节点 (2)读取节点的数据信息 (3)销毁一个节点
//init_Node
ListNode* init_node(void *data); //初始化一个节点 最后生成的是一个节点
void *get_data(const ListNode * list_node);
void destroy_node(ListNode * list_node);

//单链表抽象类型
//单链表数据结构 (1)单链表的大小size (2)头节点 head (3)尾节点 tail (4)添加一个新节点 (5)销毁节点
typedef struct List{
	int size;//大小
	ListNode *head;//头节点指针
	ListNode *tail;//尾节点指针
	ListNode* (*init_node)(void *data);//添加一个节点
	int (*destroy_node)(ListNode *list_node);//销毁一个节点
}List;

//单链表的相关操作:(1)初始化单链表 init_list (2)在一个节点后插入一个新的节点 ins_next_list
//(3)在一个节点后删除一个节点rem_next_list (4)销毁单链表 dstroy_list
//(5)单链表大小 size_list (6) 获取头节点 head_list (7)获取尾节点 tail_list (8)判断是否为头节点 is_head
//判断是否为尾节点 is_tail
void init_list(List *list);
int ins_next_list(List *list,ListNode *now_node,int(*init_node)(void *data));
int rem_next_list(List *list,ListNode *now_node,int(*destroy_node)(ListNode *list_node));
void destroy_list(List *list);
int size_list(const List *list);
ListNode* head_list(const List *list);
ListNode* tail_list(const List *list);
int is_head(const List *list);
int is_tail(const List *list);

#endif

2.单链表实现文件list.cpp

//list.cpp
#include<stdlib.h>
#include<stdio.h>

#include"list.h"

//生成一个单链表节点:init_node
//功能:动态分配内存,生成一个单链表节点,数据域中的数据为 data, 指针域为NULL 为单链表的插入做准备
//返回值:内存中动态分配的单链表节点指针 ListNode
//时间复杂度:O(1)
ListNode* init_node(void *data){ 

	ListNode* new_node ;//声明一个单链表节点类型的指针

		//在堆中为新的节点分配内存空间
	if( ( new_node = (ListNode*)malloc( sizeof(ListNode) ) ) == NULL )
		return NULL;//分配失败

	//分配成功
	//将数据封装为节点
	new_node->data = data;
	new_node->next = NULL;

	//将指向该节点的指针返回
	return new_node;

}

//destroy_node:释放节点list_node 持有的内存空间
//返回值 :void
//参数; list_node 为单链表节点
//时间复杂度:O(1)
void destroy_node(ListNode * list_node){
	free(list_node);
	return;
}

//初始化单链表 init_list
// 功能:对链表的其余操作都是在此初始化之后,如果不调用init_list,编译器会报错:list 未被初始化。
//返回值 void
//时间复杂度O(1)
void init_list(List *list){
	list->size = 0;
	list->head = NULL;
	list->tail = NULL;
}

//在链表中插入一个新的节点:ins_next_list
//功能:在一个节点now_node后插入一个数据项为data的新的节点,如果now_node为 NULL 则在链表的头部插入
//返回值:当插入成功返回0,插入失败 返回-1
//时间复杂度:O(1)
//参数:list 为空, list 只有一个节点,list有多个节点 , now_node 为空, now_node 为中间节点
//now_node 为尾部节点
//测试用例 {list == NULL , now_node == NULL , [1,2,3,4,5],now_node->next == NULL}
int ins_next_list(List *list,ListNode *now_node, void *data){
	//把数据封装为一个新的节点,这里需要动态分配内存空间,已经在 int(*init_node)(void *data)
	//中封装好了
	if( list == NULL )
		return -1;

	else
	{
	ListNode *new_node = (*init_node)(data);//封装好的节点类型

	//将节点插入到相应位置
	//查看链表list 中有哪些元素发生了改变
	//size自增
	if(now_node == NULL)//新节点 作为头节点进行插入
	{
		if(list->size == 0)//当链表为空时
		{ 

			list->head = new_node;
			list->tail = new_node;
			++list->size;
		}
		else//链表非空时,
		{
			new_node->next = list->head;
			list->head = new_node;
			++list->size;
		}
		//这里可以把共有的代码提取出来
		//list->head = new_node;
		//++list->size;
	}

	//新节点 非头节点(now_node 为中间节点,now_node 为链表中最后第二个节点,now_node 为最后的一个节点)
	else
	{
		if(now_node == list->tail)//当前节点为尾节点
		{
			new_node->next = now_node->next;
			now_node->next = new_node;
			list->tail = new_node;
			++list->size;
		}
		else//当前节点为中间节点
		{
			new_node->next = now_node->next;
			now_node->next = new_node;
			++list->size;
		}
		//这里可以把插入节点的共有代码提取出来:
		// new_node->next = now_node->next;
		// now_node->next = new_node;

	}
	//插入都有链表长度加1
	// ++list->szie

	return 0;

}

}

//rem_next_list:从单链表list当前节点 now_node  后删除一个节点,list必须已经初始化,当传入实参now_node
//为NULL时 ,删除头节点
//函数返回值: 当删除成功返回0,当删除失败 返回 -1
//参数说明:list 必须已经初始化,list 不能为空,now_node == NULL 删除头节点,now_node 为尾节点时,返回-1,
//int(*destroy_node)(ListNode *list_node);为函数指针,指向节点的析构函数,也就是说在堆中动态释放该节点所占有的内存空间。
//时间复杂度:O(1)
//测试用例说明:1.list 未初始化(return -1),2.list 为空2.list 只有一个节点(now_node == NULL(删除头节点,head =tail = NULL,size =0) ,now_node == head (删除失败,return-1))
//3.list 有多个节点(now_node == NULL(删除头节点,head =head->next), now_node == head, now_node == 中间节点(pnode),(正常删除,1.保存原有信息2.释放节点内存空间,3.size-1)
//now_node =倒数第二个节点(删除尾节点, 正常删除外, tail ,size 需要改变),now_node =tail(删除失败 ,return -1))

int rem_next_list(List *list,ListNode *now_node,void (*destroy_node) (ListNode *list_node)){
	ListNode * old_node;//将要删除的节点
	ListNode *pnode ;//保存的中间节点

	if(list == NULL)//链表不存在
		return -1;

	if(list->size == 0)//链表为空
		return -1;

	if(list->size == 1)//链表只有一个节点
	{
		if(now_node == NULL)//删除头节点
		{
			old_node = list->head;
			destroy_node(old_node);//销毁节点,释放节点所占的内存空间

			list->head = list->tail = NULL;//链表为空
			--list->size ;

			return 0;

		}

		else //删除失败
			return -1;
	}

	else if (list->size == 2)//链表由两个节点
	{

		if(now_node == NULL)//删除头节点
		{
			pnode = list->head->next;
			old_node = list->head;
			destroy_node( old_node );//销毁节点
			list->head = list->tail = pnode;//删除后剩下的唯一节点,即是头节点,又是尾节点
			--list->size;
			return 0;
		}

		/*else if( now_node->next = list->tail )*/
		else if( now_node->next == list->tail )//删除尾节点
		{
		    now_node -> next = list->tail->next;//删除尾节点,必须使尾节点的前一节点 的next 指向 NULL;
			old_node = now_node->next;//要删除的节点,也就是尾节点
			//pnode = old_node->next;
			destroy_node( old_node );
			list->tail = list->head;//删除后只剩下一个节点了,尾节点和头节点是同一个节点,都是原来的头节点
			--list->size;
			return 0;
		}

		else//删除不存在的节点,失败
		{

			return -1;
		}
	}
	else //链表有n(n>2)个节点
	{
		if(now_node == NULL)//删除头节点
		{
			old_node = list->head;
			pnode = old_node->next;
			destroy_node( old_node );//销毁节点
			list->head = pnode;
			--list->size;
			return (0);
		}
		else//删除其他节点
		{
			old_node = now_node->next;

			if( old_node == NULL )//删除不存在的节点
			{
				return -1;
			}

			else if( old_node == list->tail )//删除尾节点
			{
				//now_node -> next = NULL;//如果删除的是尾节点 注意一定要将尾节点的前一节点的指针域 next 指向NULL
				now_node -> next = old_node -> next;
				//pnode = old_node->next;
				pnode = now_node;
				destroy_node( old_node );
				list->tail = pnode;
				--list->size;

				return 0;
			}

			else//删除中间节点
			{
				pnode = old_node->next;
				now_node->next = pnode;//将要删除的节点从链表中移除
				destroy_node(old_node);//释放节点所占的内存
				--list->size;//链表的节点数 -1
				return 0;

			}

		}
	}

}

//destroy_list:销毁单链表,从头到尾 将链表的节点销毁
//返回值:销毁成功 返回 0,销毁失败 返回-1;
//参数:已经初始化了的单链表
//时间复杂度:O(n)相当于遍历了整个单链表
void destroy_list(List *list){
	while (list->size > 0 )//如果链表不为空,调用已经定义好的函数从头到尾删除链表的节点
		rem_next_list(list,NULL,destroy_node);

}

int main(void){
	List list;//声明一个单链表
	List *plist = &list;

	//测试初始化链表:init_list(&list)
	init_list(plist);//初始化单链表 此时
	printf("list_size:%d\n",plist->size);
	printf("list_head:%p\n",plist->head);
	printf("list_tail:%p\n",plist->tail);

	//测试在链表中插入一个节点:int ins_next_list(List *list,ListNode *now_node, void *data)
	//测试数据集data{1,2,3,4,5}
	//测试用例[list,now_node,data]=[NULL,NULL,1];[list,NULL,1];[list,head,2];[list,中间节点,3]
	//[list,NULL,0];[list,tail,5]
	int data[5] ={1,2,3,4,5};

	int True = ins_next_list(NULL,NULL,&data[1]);//返回值为 -1插入失败
	printf("ins_next_list:%d\n",True);

	True = ins_next_list(plist,NULL,&data[1]);//添加头节点 数据项 data =2 ,list_size = 1链表中的数据元素:{2}
	printf("ins_next_list:%d\n",True);
	printf("List_size:%d\n",plist->size);
	printf("*data:%d\n",*(int*)(plist->head->data));

	True = ins_next_list(plist,plist->head,&data[2]);//在头节点后添加一个节点 数据项 data=3 ,list_size =2 链表中数据元素{2,3}
	printf("ins_next_list:%d\n",True);
	printf("List_size:%d\n",plist->size);
	printf("*data:%d\n",*(int*)(plist->head->next->data));

	True = ins_next_list(plist,plist->head->next,&data[3]);//在第二个节点后添加一个节点 数据项 data =4 ,list_size = 3 ,链表中的数据元素{2,3,4}
	printf("ins_next_list:%d\n",True);
	printf("List_size:%d\n",plist->size);
	printf("*data:%d\n",*(int*)(plist->head->next->next->data));

	True = ins_next_list(plist,NULL,&data[0]);//在链表头节点前插入一个节点 作为新的头节点,数据项 data = 1,链表中的数据元素 {1,2,3,4}
	printf("ins_next_list:%d\n",True);
	printf("List_size:%d\n",plist->size);
	printf("*data:%d\n",*(int*)(plist->head->data));

	True = ins_next_list(plist,plist->tail,&data[4]);//在尾节点后添加一个节点 作为新的尾节点,数据项 data = 5, 链表中的数据元素{1,2,3,4,5}
	printf("ins_next_list:%d\n",True);
	printf("List_size:%d\n",plist->size);
	printf("*data:%d\n",*(int*)(plist->tail->data));

	//输出链表中的所有数据元素(遍历){1,2,3,4,5}
	ListNode* pNode = plist->head;
	while( pNode != NULL ){

		printf("%d ",*(int*)pNode->data );
		pNode = pNode->next;
	}
	printf("\n");

	////测试销毁链表 现在链表的数据元素为{1,2,3,4,5},

 //   destroy_list(plist);
	//printf("plist->size:%d\n",plist->size);//如果为 0 链表成功销毁
	//

	//测试在一个链表中删除节点
//现在链表中总共有5个数据 从头至尾遍历 结果为{1,2,3,4,5}
//测试用例为{链表未初始化,链表为空, 链表有n(n=5)个节点(删除头节点,删除尾节点,删除中间节点)链表只有两个节点,链表只有一个节点}

	ListNode* qnode;//测试用的节点
	List qlist;
	List *pqlist = &qlist;//测试用的链表
	int result; //用来判断是否删除节点失败的结果,result = 0,删除成功,result = -1,删除失败

	//1.链表未初始化,通不过编译器的语法检查
	//qnode = NULL;
	//	result = rem_next_list(pqlist,qnode,destroy_node);//
	//printf("rem_next_list: %d",result);

	//1.链表为空,删除失败返回-1
	init_list(pqlist);
	qnode = NULL;
		result = rem_next_list(pqlist,qnode,destroy_node);//
	printf("rem_next_list: %d\n",result);

	//2.链表有5个节点,{1,2,3,4,5}删除头节点{2,3,4,5}
	pqlist = plist;
	qnode = NULL;
	result = rem_next_list(pqlist,qnode,destroy_node );
	printf("rem_next_list: %d\n",result);

    pNode = pqlist->head;
	while( pNode != NULL ){

		printf("%d ",*(int*)pNode->data );
		pNode = pNode->next;
	}
	printf("\n");

	//删除链表中的尾节点,删除后链表中元素为{2,3,4};

	pqlist = plist;

	//找到尾节点前一节点
	qnode = pqlist->head;
	while(qnode->next != pqlist->tail )
		qnode = qnode->next;
	//此时删除的是尾节点5
	result = rem_next_list(pqlist,qnode,destroy_node );
	printf("rem_next_list: %d\n",result);

    pNode = pqlist->head;
	while( pNode != NULL ){
		printf("%d ",*(int*)pNode->data );
		pNode = pNode->next;
	}
	printf("\n");

	//此时链表数据{2,3,4},删除中间节点3后链表数据域{2,4}
	pqlist = plist;

   //找到尾节点前一节点
	qnode = pqlist->head;

	//此时删除的是尾节点3
	result = rem_next_list(pqlist,qnode,destroy_node );
	printf("rem_next_list: %d\n",result);

    pNode = pqlist->head;
	while( pNode != NULL ){
		printf("%d ",*(int*)pNode->data );
		pNode = pNode->next;
	}
	printf("\n");

	//只有两个节点{2,4}删除头节点2 后,链表数据为{4}
	pqlist = plist;

	//要删除的节点
	qnode = NULL;
	//此时删除的是2各数据的头节点;
	result = rem_next_list(pqlist,qnode,destroy_node );
	printf("rem_next_list: %d\n",result);

    pNode = pqlist->head;
	while( pNode != NULL ){
		printf("%d ",*(int*)pNode->data );
		pNode = pNode->next;
	}
	printf("\n");

	////只有两个节点{2,4}删除尾节点4 后,链表数据为{2}
	//pqlist = plist;

	////要删除的节点前一节点
	//qnode = pqlist->head;
	////此时删除的是2各数据的头节点;
	//result = rem_next_list(pqlist,qnode,destroy_node );
	//printf("rem_next_list: %d\n",result);

 //   pNode = pqlist->head;
	//while( pNode != NULL ){
	//	printf("%d ",*(int*)pNode->data );
	//	pNode = pNode->next;
	//}
	//printf("\n");

	//只有两个节点{2,4},当前节点 为 尾节点时
	//pqlist = plist;
	//printf("size: %d\n",pqlist->size);

	////当前的节点 为尾节点 删除失败 返回-1;删除后数据不变还是{2,4}
	//qnode = pqlist->tail;
	//

	//result = rem_next_list(pqlist,qnode,destroy_node );
	//printf("rem_next_list: %d\n",result);

 //   pNode = pqlist->head;
	//while( pNode != NULL ){
	//	printf("%d ",*(int*)pNode->data );
	//	pNode = pNode->next;
	//}
	//printf("\n");

	//此时 链表中只剩一个节点数据{4},
	pqlist = plist;

	//要删除的前一节点 为head,删除失败 返回-1; 操作结束后链表中还是{4}
	qnode = pqlist->head;
	result = rem_next_list(pqlist,qnode,destroy_node );

	printf("rem_next_list: %d\n",result);

    pNode = pqlist->head;
	while( pNode != NULL ){
		printf("%d ",*(int*)pNode->data );
		pNode = pNode->next;
	}

	printf("\n");

	//此时 链表中只剩一个节点数据{4},
	pqlist = plist;

	//传入参数 为 NULL,删除成功返回 0; 操作结束后链表为 空;
	qnode = NULL;
	result = rem_next_list(pqlist,qnode,destroy_node );
	printf("rem_next_list: %d\n",result);
	printf("链表已经为 空啦,啦啦啦");

    pNode = pqlist->head;
	while( pNode != NULL ){
		printf("%d ",*(int*)pNode->data );
		pNode = pNode->next;
	}
	printf("\n");

	return 0;
}

3.测试结果:

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-25 13:59:28

单链表 之c代码的相关文章

单链表反转java代码

据说单链表反转问题面试中经常问,而链表这个东西相对于数组的确稍微难想象,因此今天纪录一下单链表反转的代码. 1,先定义一个节点类. 1 public class Node { 2 int index; 3 Node next; 4 5 public Node(int index, Node next) { 6 this.index = index; 7 this.next = next; 8 } 9 } 2,我一共写了三种方法 (1)迭代法.先将下一节点纪录下来,然后让当前节点指向上一节点,再将

php单链表实现的代码

<?php/*** 单链表*/ class Demo{private $id;public $name;public $next;public function __construct ($id = '', $name = ''){$this->id = $id;$this->name = $name;}static public function show ($head){$cur = $head;while ($cur->next) {echo $cur->next-&g

数据结构之线性表——链式存储结构之单链表(php代码实现)

<?php /**  *  * 1. 类LNode用作创建单链表时,生成新的节点.  * 2. 类SingleLinkList用于创建单链表以及对单链表的一些操作方法(实例化此类就相当于创建了一个空链表)  * 3. CreateListHead: 具有$num个数据元素的单链表的创建--头插法  * 4. CreateListTail: 具有$num个数据元素的单链表的创建--尾插法  * 5. DestroyList: 销毁单链表  * 6. ClearList:清空单链表  * 7. Li

java数据结构:单链表常见操作代码实现

一.概述: 本文主要总结单链表常见操作的实现,包括链表结点添加.删除:链表正向遍历和反向遍历.链表排序.判断链表是否有环.是否相交.获取某一结点等. 二.概念: 链表: 一种重要的数据结构,HashMap等集合的底层结构都是链表结构.链表以结点作为存储单元,这些存储单元可以是不连续的.每个结点由两部分组成:存储的数值+前序结点和后序结点的指针.即有前序结点的指针又有后序结点的指针的链表称为双向链表,只包含后续指针的链表为单链表,本文总结的均为单链表的操作. 单链表结构: Java中单链表采用No

【数据结构】单链表&amp;&amp;静态链表详解和代码实例

喜欢的话可以扫码关注我们的公众号哦,更多精彩尽在微信公众号[程序猿声] 01 单链表(Singly Linked List ) 1.1 什么是单链表? 单链表是一种链式存储的结构.它动态的为节点分配存储单元.当有节点插入时,系统动态的为结点分配空间.在结点删除时,应该及时释放相应的存储单元,以防止内存泄露.由于是链式存储,所以操作单链表时,必须知道头结点或者头指针的位置.并且,在查找第i个节点时,必须找到第i-1个节点. 1.2 单链表的存储结构代码描述 对于链式存储,通过上一节的讲解相信大家已

python单链表实例分享

有关python单链表的实现代码. 链表的定义:链表(linked list)是由一组被称为结点的数据元素组成的数据结构,每个结点都包含结点本身的信息和指向下一个结点的地址.由于每个结点都包含了可以链接起来的地址信息,所以用一个变量就能够访问整个结点序列.也就是说,结点包含两部分信息:一部分用于存储数据元素的值,称为信息域:另一部分用于存储下一个数据元素地址的指针,称为指针域.链表中的第一个结点的地址存储在一个单独的结点中,称为头结点或首结点.链表中的最后一个结点没有后继元素,其指针域为空. p

[大话数据结构]线性表之单链表结构和顺序存储结构

线性表定义: 零个或者多个数据元素的有限序列.元素之间是有顺序的,如果元素存在多个,则第一个元素无前驱,最后一个元素无后继.其他每个元素都有且只有一个前驱和后继.并且数据元素的类型要相同. 线性表的抽象数据类型: ADT 线性表(List) Data 线性表的数据对象集合为{a1,a2,...,an},每个元素的类型均为DataType. 其中,除第一个元素a1外,每一个元素有且只有一个直接前驱元素,除了最后一个元素an外,每一个元素有且只有一个直接后继元素. 数据元素之间的关系是一对一的关系.

逆置单链表(0957)swust-oj

Description 建立长度为n的单链表,然后将其数据元素逆置,即第1个元素变为最后一个元素,第2个元素变为倒数第2个元素,……,最后一个元素变为第1个元素.(处理的数据类型为字符型.必须使用链表完成.) Input 第一行为链表长度n: 第二行为链表中的n个数据元素的值. Output 逆置后的原始的值. Sample Input 10 A B C D E F G H I Sample Output I H G F E D C B A 分析:逆制,只需要用头插法建立单链表即可: 代码: #

单链表查找最大值、两个递增的链表合并并且去重

单链表查找最大值 代码: 1 #include<iostream> 2 #include<bits/stdc++.h> 3 using namespace std; 4 struct Node{ 5 int value; 6 Node * next; 7 }; 8 Node *a=new Node; //创建头指针 9 void build_link(Node * a,int n){ //建表 10 Node *p; 11 p=a; 12 for(int i=0;i<n;i+