数据结构 - 单链表(C语言实现)

一. 链表的定义
  链表是动态分配存储空间的链式存储结构
  其包括一个“头指针”变量,其中第0个结点称为整个链表的头结点,头结点中存放一个地址,该地址指向一个元素,头结点一般不存放具体数据,只是存放第一个结点的地址。
  链表中每一个元素称为“结点”,每个结点都由两部分组成:存放数据元素的数据域和存储直接后继存储位置的指针域。指针域中存储的即是链表的下一个结点存储位置,是一个指针。多个结点链接成一个链表。
  最后一个结点的指针域设置为空(NULL),作为链表的结束标志,表示它没有后继结点。
  使用结构体变量作为链表中的结点,因为结构体变量成员可以是数值类型,字符类型,数组类型,也可以是指针类型,这样就可以使用指针类型成员来存放下一个结点的地址,使其它类型成员存放数据信息。

  当一个序列中只含有指向它的后继结点的链接时,就称该链表为单链表。
  单链表的示意图如下:
            单链表示意图
二. 单链表的表示及实现
  链表和顺序表的最大不同之处就是链表的物理地址不是相邻的。顺序表中采用数组下标的形式(即物理地址的相邻)表示元素的逻辑顺序,而链表是采用指针的方式使不相邻的地址存储逻辑相邻的元素。

  • 链表的优点:不需要连续的空间进行存储;对于插入、删除节点操作较为方便,时间复杂度O(1)。
  • 链表的缺点:对于指定位置元素读取较顺序表麻烦;空间利用率比较低。
  1. 定义

    #define ERROR	-1
    #define OK		0
    
    typedef struct LNode{
    	int Data;
    	struct LNode *Next;
    }List; 
    
  2. 创建带头节点的单链表

    List *CreatList(){
    	//创建带头节点的链表 
    	List *L=NULL;
    	L = (List *)malloc(sizeof(List));
    	if(!L) exit(-1);//申请存储失败 
    	L->Data = -1;
    	L->Next = NULL;
    	return L; 
    }
    
  3. 尾插法插入数据元素

    int ListInsertR(List *L, int e){
    	//将数据e插入链表L尾部
    	List *p=L, *q=NULL;//p追踪链表尾部 q为新节点 
    	q = (List *)malloc(sizeof(List));
    	if(!q) exit(-1);//存储申请失败
    	q->Data = e;
    	q->Next = NULL;
    	//查找链表尾部地址 
    	while(p && p->Next){
    		p = p->Next;
    	} 
    	q->Next = p->Next;
    	p->Next = q;
    	return OK;
    }
    
  4. 读取链表指定位置元素

    int ListRead(List *L, int x, int *e){
    	//将链表中第 x个元素数据保存到e中
    	List *p=L;
    	int i=0;
    	while(i < x){
    		if(p->Next == NULL){
    			return ERROR;//读取元素越界 
    		}
    		p = p->Next;
    		++i;
    	} 
    	(*e) = p->Data;
    	return OK;
    } 
    
  5. 获取链表长度

    int GetListLength(List *L, int *e){
    	//将链表L的长度存储在e中(不含头结点)
    	List *p=L->Next;
    	int Length=0;
    	//计算链表长度 
    	while(p){
    		p = p->Next;
    		++Length;
    	}
    	//返回链表长度 
    	(*e) = Length;
    	return OK;
    }
    
  6. 从链表中删除指定位置节点

    int ListDel(List *L, int x, int *e){
    	//在链表L中删除第x个节点并将数据存储在e中
    	List *q=L, *p=NULL;//q追踪x个节点 p保存节点释放
    	int i=0;//循环变量
    	if(x < 1) return ERROR;//删除节点非法 
    	//找到前一个节点地址 
    	while( i < x-1){
    		if(q->Next == NULL){
    			return ERROR;//删除元素越界 
    		} 
    		q = q->Next;
    		++i;
    	} 
    	//将节点删除 
    	p = q->Next;
    	q->Next = p->Next;
    	(*e) = p->Data;
    	free(p);
    	return OK; 
    }
    
  7. 函数验证

    int GetListLength(List *L, int *e){
    	//将链表L的长度存储在e中(不含头结点)
    	List *p=L->Next;
    	int Length=0;
    	//计算链表长度 
    	while(p){
    		p = p->Next;
    		++Length;
    	}
    	//返回链表长度 
    	(*e) = Length;
    	return OK;
    }
     
    int main(int argc, char *argv[]) {
    	List *L=NULL,*p=NULL;
    	int i=1, e=0;
    	//创建节点 
    	L = CreatList();
    	printf("创建链表成功!\n");
    	GetListLength(L, &e); 
    	printf("当前链表长度为 %d 。\n",e);
    	//循环插入元素 
    	for(i;i<10;i++){
    		ListInsertR(L, i);
    	}
    	printf("向链表中插入了 %d 个数据元素。\n",i-1);
    	//读取出链表内全部数据 
    	p = L->Next;
    	i=1;
    	GetListLength(L, &e); 
    	printf("当前链表长度为 %d 。\n",e);
    	printf("当前链表数据为:");
    	while(p){
    		ListRead(L, i, &e);
    		printf("%d",e);
    		++i;
    		p = p->Next;
    	}
    	i = 5;
    	printf("删除第 %d 个节点。\n",i);
    	ListDel(L, i, &e); 
    	
    	p = L->Next;
    	i=1;
    	GetListLength(L, &e); 
    	printf("当前链表长度为 %d 。\n",e);
    	printf("当前链表数据为:");
    	while(p){
    		ListRead(L, i, &e);
    		printf("%d",e);
    		++i;
    		p = p->Next;
    	}
    	
    	return 0;
    }
    

    在这里插入图片描述
    经验证函数有效。

猜你喜欢

转载自blog.csdn.net/qq_40344307/article/details/89073217