数据结构回顾——单链表操作详解及C语言实现


1 前言

  上一篇文章主要描述线性表组成之一的顺序表的插入、删除、查找操作和C语言的实现。本文描述另一线性表【单链表】的操作和C语言实现。


2 链表头节点

  一个单链表分为带头节点和不带头节点,带头节点与不带头节点,对于单链表的初始化、插入、删除等操作都不同,因此有必要了解这两类单链表。


2.1 带头节点链表

  数据结构中,在单链表的第一个结点之前附设一个结点,它没有直接前驱(不存储有效数据),称之为头结点。带头节点的链表,头指针始终指向头节点。


在这里插入图片描述

带头结点的单链表


带头节点的链表特点:

  • 头指针存放的是头节点地址
  • 头指针始终指向头结点,不因为节点的插入而改变

2.2 不带头节点链表

  不带头节点的链表,头指针指向链表的第一个元素节点地址。与带头节点的链表不同,头指针的指向可能会因为节点的插入、删除操作,需重新改变头指针指向。


在这里插入图片描述

不带头结点的单链表

不带头节点的链表特点:

  • 头指针存放的是链表第一个元素节点地址
  • 头指针指向可能因为节点的插入、删除而改变

2.3 带头节点与不带头节点链表处理

  根据带头节点和不带头节点的链表特点,两者在具体操作过程需要作不同的处理。

  • 不带头节点的链表,对于第一个节点插入、删除需特别处理
  • 带头节点节点的链表,通过判断头节点指针域是否为空来检查空表
if (head->next == NULL)
{
    
    
	/* todo */
}
  • 不带头节点的链表,通过判断头指针是否为空来检查空表
if (head == NULL)
{
    
    
	/* todo */
}
  • 实际场合一般使用带头节点的链表,这样对于链表的操作上更为简单,但需占用一个节点内存空间
  • 根据使用习惯,对于带头节点的链表,链表的有效长度一般不包括头节点

  文章以下部分描述的链表都是带头节点的链表。


3 单链表创建

  单链表创建,一般是创建一个空的链表,这里我们创建一个带头节点的空链表。

link_list_t* create_link_list(void)
{
    
    
	link_list_t* head = NULL;
    
    head = (link_list_t*)malloc(sizeof(link_list_t));
    
    if (head != NULL)
    {
    
    
        head->data=0;			/* 头结数据域为空 */
		head->pnext = NULL;		
    }
    return head;
}

4 单链表清空与销毁

  链表清空指的是删除链表有效元素节点,释放节点内存空间。链表销毁指的是删除所有节点,包括头结点,并释放节点内存空间。单链表清空与销毁都是遍历整个链表。

单链表清空伪代码:

int clear_link_list(link_list_t *list)
{
    
    
	link_list_t *p = NULL;
	link_list_t *q = NULL;
	
	p = list->pnext;
	while(p != NULL)
	{
    
    
		q = p->pnext;
		free(p);
		p = q;
	}
	list->pnext=NULL;

	return 0;
}

单链表销毁伪代码:

int destory_link_list(link_list_t *list)
{
    
    
	link_list_t *p = NULL;
	while(list != NULL)
	{
    
    
		p = list->pnext;
		free(list);
		list = p;
	}
	list = NULL;

	return 0;
}

5 单链表查找

  单链表无论是插入还是删除操作,都需先查找到目标节点,所以首先描述单链表的查找操作。单链表查找有两种方式,分别是根据元素索引号和节点数据元素值查找,这两种方式都需要从链表头开始遍历链表,直至查找到指定节点。对于元素值查找,只适用于链表中存储的元素值都是唯一的情况,否则只能使用节点索引号查找。

  如下图,查找一个带头节点单链表的第二个节点,可以通过索节点引号[1]查找或者通过唯一的节点数据元素[a1]查找。


在这里插入图片描述

单链表查找操作

C语言实现伪代码:

/* 通过节点索引号查找 */
link_list_t *get_link_list_node_pos(link_list_t *list, int pos)  
{
    
    
	link_list_t *p = NULL;

	p = list;
	while(pos>=0)
	{
    
    
		p = p->pnext;
		if (p == NULL)
		{
    
    
			break;
		}
		pos--;
	}
	return p;
}
/* 通过节点数据元素查找 */
link_list_t *get_link_list_node_elem(link_list_t *list, int elem)  
{
    
    
	link_list_t *p = NULL;
	
	p = list->pnext;
	while(p!=NULL)
	{
    
    
		if (p->data == elem)
		{
    
    
			return p;
		}
		p = p->pnext;
	}
	return NULL;
}

6 单链表插入

  单链表插入时间复杂度为O(1)。与顺序表一样,根据插入位置不同,单链表插入类型分为三种,不同的插入类型主要遍历链表节点的次数不同,即是效率不同。

  • 表头插入,无需遍历链表
  • 表尾插入,无需遍历链表
  • 表中间插入,需遍历链表,即是查找操作

 单链表插入步骤:

【1】查找到插入位置的前一节点,可通过节点索引号或者唯一节点数据元素查找

【2】申请待插入节点内存并赋值,节点指针域指向插入位置节点

【3】插入位置前一节点指针域指向插入节点

在这里插入图片描述

单链表插入操作

C语言实现伪代码:

int insert_link_list_node_pos(link_list_t *list, int value, int pos)  
{
    
    
	link_list_t *p = NULL;
	link_list_t *node = NULL;
	
	if (pos == 0)
	{
    
    
		p = list;	/* 插入第一个节点位置 */
	}
    else
    {
    
    
    	p = get_link_list_node_pos(list, pos-1);	/* 获取插入位置的前一结点 */
    }

	if (p == NULL)
	{
    
    
		return -1;
	}
	node = malloc(sizeof(link_list_t));
	if (node == NULL)
	{
    
    
		return -1;
	}
    /* 插入过程 */
	node->data = value;
	node->pnext = p->pnext;
	p->pnext = node;
    return 0;
}

7 单链表删除

  单链表删除时间复杂度为O(1)。单链表删除与插入是一个相反的的过程,删除类型有三种,与插入类型对应。实质上,三种类型可以认为是一种类型,并且实现方式都是一样的。

  • 表头删除
  • 表尾删除
  • 表中间删除

  单链表删除步骤:

【1】查找到删除位置的前一节点,可通过节点索引号或者唯一节点数据元素查找

【2】删除位置前一节点指针域指向删除节点的下一节点

【3】释放删除节点内存


在这里插入图片描述

单链表删除操作

C语言实现伪代码:

int delete_link_list_node_pos(link_list_t *list, int pos) 
{
    
    
	link_list_t *p = NULL;
	link_list_t *node = NULL;

	if (pos == 0)
	{
    
    
		p = list;	/* 删除第一个节点 */
	}
	else	
	{
    
    
		p = get_link_list_node_pos(list, pos-1);	/* 获取删除位置的前一结点 */
	}
	if ((p!=NULL) && (p->pnext!=NULL))
	{
    
    /* 删除过程 */
		node = p->pnext;						
		p->pnext = node->pnext;	
		free(node);
	}
	else
	{
    
    
		return -1;
	}
}

8 实例

  • 实现一个带头节点的单链表,头节点不纳入链表长度计算
  • 提供单链表创建、长度查询、空表检查、插入、删除、查找、清空、销毁操作接口
  • 提供节点索引号和节点数据元素查找实现方法
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <stdbool.h>

typedef struct _link_list
{
    
    
  	int data;
	struct _link_list *pnext;/* 下一节点 */
}link_list_t;

link_list_t* create_link_list(void)
{
    
    
	link_list_t* head = NULL;
    
    head = (link_list_t*)malloc(sizeof(link_list_t));
    
    if (head != NULL)
    {
    
    
        head->data=0;		/* 头结数据域为空 */
		head->pnext = NULL;
    }

    return head;
}

bool check_link_list_empty(link_list_t *list)
{
    
    
	if (list == NULL)
	{
    
    
		return true;
	}
	if (list->pnext == NULL)	
	{
    
    
		return true;
	}
	else
	{
    
    
		return false;
	}
}

int destory_link_list(link_list_t *list)
{
    
    
	link_list_t *p = NULL;
	
	if ((list==NULL) || (list->pnext==NULL))
	{
    
    
		return 0;
	}
	while(list != NULL)
	{
    
    
		p = list->pnext;
		free(list);
		list = p;
	}
	list = NULL;

	return 0;
}

int clear_link_list(link_list_t *list)
{
    
    
	link_list_t *p = NULL;
	link_list_t *q = NULL;
	
	if ((list==NULL) || (list->pnext==NULL))
	{
    
    
		return 0;
	}
	p = list->pnext;
	while(p != NULL)
	{
    
    
		q = p->pnext;
		free(p);
		p = q;
	}
	list->pnext=NULL;

	return 0;
}


int get_link_list_capacity(link_list_t *list)
{
    
    
	link_list_t *p = NULL;
	int size = 0;
	
	if (list == NULL)
	{
    
    
		return 0;
	}
	p = list->pnext;
	while(p != NULL)
	{
    
    
		size++;
		p = p->pnext;
	}
	return size;
}

link_list_t *get_link_list_node_pos(link_list_t *list, int pos)  
{
    
    
	link_list_t *p = NULL;
	
	if ((list==NULL) || (pos<0))
	{
    
    
		return NULL;
	}

	p = list;
	while(pos>=0)
	{
    
    
		p = p->pnext;
		if (p == NULL)
		{
    
    
			break;
		}
		pos--;
	}
	return p;
}

link_list_t *get_link_list_node_elem_per(link_list_t *list, int elem)  
{
    
    
	link_list_t *p = NULL;
	
	if ((list==NULL) || (list->pnext==NULL))
	{
    
    
		return NULL;
	}

	p = list->pnext;
	while(p!=NULL)
	{
    
    
		if (p->pnext->data == elem)
		{
    
    
			return p;
		}
		p = p->pnext;
	}
	return NULL;
}

int insert_link_list_node_pos(link_list_t *list, int value, int pos)  
{
    
    
	link_list_t *p = NULL;
	link_list_t *node = NULL;

	if ((list==NULL) || (pos<0))
	{
    
    
		return -1;
	}

	if (pos == 0)
	{
    
    
		p = list;	/* 插入第一个节点位置 */
	}
    else
    {
    
    
    	p = get_link_list_node_pos(list, pos-1);	/* 获取插入位置的前一节点 */
    }

	if (p == NULL)
	{
    
    
		return -1;
	}
	node = (link_list_t*)malloc(sizeof(link_list_t));
	if (node == NULL)
	{
    
    
		return -1;
	}
	node->data = value;
	node->pnext = p->pnext;
	p->pnext = node;

    return 0;
}

int insert_link_list_node_elem(link_list_t *list, int value, int elem)  
{
    
    
	link_list_t *p = NULL;
	link_list_t *node = NULL;

	if (list==NULL)
	{
    
    
		return -1;
	}

    p = get_link_list_node_elem_per(list, elem);	/* 获取插入位置的前一节点 */

	if (p == NULL)
	{
    
    
		return -1;
	}
	node = malloc(sizeof(link_list_t));
	if (node == NULL)
	{
    
    
		return -1;
	}
	node->data = value;
	node->pnext = p->pnext;
	p->pnext = node;
    return 0;
}

int delete_link_list_node_pos(link_list_t *list, int pos) 
{
    
    
	link_list_t *p = NULL;
	link_list_t *node = NULL;

	if ((list==NULL) || (pos<0))
	{
    
    
		return -1;
	}
	if (pos == 0)
	{
    
    
		p = list;	/* 删除第一个节点 */
	}
	else	
	{
    
    
		p = get_link_list_node_pos(list, pos-1);	/* 获取删除位置的前一节点 */
	}
	if ((p!=NULL) && (p->pnext!=NULL))
	{
    
    
		node = p->pnext;
		p->pnext = node->pnext;
		free(node);
	}
	else
	{
    
    
		return -1;
	}
}

int delete_link_list_node_elem(link_list_t *list, int elem) 
{
    
    
	link_list_t *p = NULL;
	link_list_t *node = NULL;

	if (list==NULL)
	{
    
    
		return -1;
	}
	p = get_link_list_node_elem_per(list, elem);	/* 获取删除位置的前一节点 */
	if ((p!=NULL) && (p->pnext!=NULL))
	{
    
    
		node = p->pnext;
		p->pnext = node->pnext;
		free(node);
	}
	else
	{
    
    
		return -1;
	}
}


int main(int argc, char *argv[]) 
{
    
    
	link_list_t *linklist = NULL;
	link_list_t *ptemp;
	int elem = 0;
	int i;
	
	/* 创建单链表 */
	linklist = create_link_list();	

	/* 插入操作 */
	insert_link_list_node_pos(linklist, 0, 0);
	insert_link_list_node_pos(linklist, 1, 1);
	insert_link_list_node_pos(linklist, 3, 2);
	insert_link_list_node_pos(linklist, 5, 1);

	printf("link list capacity:[%d]\n", get_link_list_capacity(linklist));
	for(i=0; i<get_link_list_capacity(linklist); i++)
    {
    
    /* 查找操作 */
        ptemp = get_link_list_node_pos(linklist, i);
        printf("link list node[%d]=%d\n", i, ptemp->data);
    }

	/* 删除操作 */
	printf("delete link list node[2]\n");
	delete_link_list_node_pos(linklist, 2);
	printf("link list capacity:[%d]\n", get_link_list_capacity(linklist));
	for(i=0; i<get_link_list_capacity(linklist); i++)
    {
    
    /* 查找操作 */
        ptemp = get_link_list_node_pos(linklist, i);
        printf("link list node[%d]=%d\n", i, ptemp->data);
    }
	destory_link_list(linklist);	/* 销毁单链表 */
}

编译执行

  • 在Ubuntu16.04下执行结果
acuity@ubuntu:/mnt/hgfs/LSW/STHB/temp$ gcc link_list.c -o link_list
acuity@ubuntu:/mnt/hgfs/LSW/STHB/temp$ ./link_list
link list capacity:[4]
link list node[0]=0
link list node[1]=5
link list node[2]=1
link list node[3]=3
delete link list node[2]
link list capacity:[3]
link list node[0]=0
link list node[1]=5
link list node[2]=3

猜你喜欢

转载自blog.csdn.net/qq_20553613/article/details/108037008