线性表之链式表示

1. 单链表

1.1 单链表的定义

通过一组任意的存储单元来存储线性表中的数据元素,是线性表的链式存储。	

1.2 单链表的特点

  • 解决了顺序表插删需要移动大量元素的缺点
  • 引入了额外的指针域,浪费了空间
  • 单链表是非随机存取的存储结构,查找需要从头遍历
  • 最后一个结点的next指向“空”(通常用NULL或“^”符号表示)

1.3 单链表结点类型描述

  • 因为逻辑相邻不一定物理相邻,所以需要额外的指针来存储后继信息

  • 单链表结点的组成

    • data:数据域,存放数据元素
    • next:指针域,存放后继结点的地址
  • 代码定义

typedef struct LNode
{
    
    
	ElemType data;				//数据域 
	struct LNode *next;			//指针域,指向后继结点 
}LNode,*LinkList;

1.4 单链表上基本操作的实现

1.4.1 头插法创建单链表

实现思路

 1. 从空表开始
 2. 新结点读取数据
 3. 将新结点插入到链表表头,即头结点之后

实现代码

/*	头插法创建单链表
 * 	L:单链表
 * n:单链表结点的个数 
*/ 	 
LinkList CreateList(LinkList L,int n)
{
    
    
	LinkList p;	                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             
	int i;
	
//	L = InitList(L);						
	
	//循环创建新结点 
	for(i=0;i<n;++i)							
	{
    
    
		//创建新结点 
		p = (LNode *)malloc(sizeof(LNode));

		//读取输入值,设置数据域 
		scanf("%d",&p->data);
		
		//p的后继结点设置为L的后继结点 
		p->next = L->next;
		
		//L的后继设置为p 
		L->next = p;
	}
	
	//返回头结点 
	return L;	
}

1.4.2 尾插法创建单链表

实现思路

 1. 将新结点插入到表尾
 2. 需增设表尾指针r,使其始终指向表尾结点

实现代码

//尾插法创建单链表
LinkList CreateList_R(LinkList L,int n)
{
    
    
	LinkList p,r;
	int i;
	
	//创建头结点 
	L = (LNode *)malloc(sizeof(LNode));
	
	//初始为空表
	L->next = NULL;    
	
	r = L;
	
	//循环创建新结点 
	for(i=0;i<n;++i)
	{
    
    
		//生成新结点 
		p = (LNode *)malloc(sizeof(LNode));

		//读取输入值,设置数据域
		scanf("%d",&p->data);
		
		//设置 r 的后继结点为 p 
		r->next = p; 
		
		//设置尾结点为p 
		r = p;
	}
	
	//尾节点置空 
	r->next = NULL;
	
	return L;
}

1.4.3 按值查找结点

实现思路

 1. 从头开始遍历,返回其中数据域为给定e的结点的指针

实现代码

/*	从头开始遍历,返回其中数据域值为给定e的结点的指针,不存在,返回NULL
*/
LNode *locateElem(LinkList L,ElemType e)
{
    
    
	// 
	LNode *p = L->next;
	
	//判断表是否为空   &&    还没有找到数据域的值是e的结点 
	while(p!=NULL && p->data != e)
	{
    
    
		p = p->next;
	}
	
	//没找到,p为NULL,找到了,返回结点 
	return p;
}

1.4.4 插入结点

实现思路

 1. 将值为x的结点插入到单链表的第i个位置上
 2. 先检查插入位置i(1<=i<=L.length+1)的合法性
 3. 找到待插入结点的前驱结点,在其后执行插入

单链表插入结点

实现代码

/*	
 * 在L的第i个位置上插入元素值为x的结点 
*/
void InsertNode(LinkList L,int i,ElemType x){
    
    
	int *e;
	LNode *p;		
	
	//创建一个新结点 
	LNode *s = (LNode *)malloc(sizeof(LNode));		
	
	//数据域赋值 
	s->data  = x;								
	
	//找到第 i 个结点的前驱结点 
	p = GetElem(L,i-1,e);
	
	//将s的后继结点设置为p的后继结点 
	s->next = p->next;
	
	//将p的后继结点设置为s 
	p->next = s;
	
} 

1.4.5 删除结点

实现思路

 1. 将值单链表的第i个位置上的结点删除
 2. 先检查删除位置i(1<=i<=L.length)的合法性
 3. 找到单链表上的第i-1个位置上的结点,在执行删除操作

实现代码

/*
*	删除第 i 个结点,并将数据域通过e范返回
*/
void deleteNode(LinkList L,int i,ElemType *e){
    
    
	
	//被删除指针的前驱结点和辅助指针 
	LNode *p,
		  *q;
	
	//被删除结点的前驱结点 
	p =  GetElem(L,i-1,e);
	
	//q指向需要删除的结点 
	q = p->next;
	
	//将需要删除的结点从链表上取下 
	p->next = q->next;
	
	//将被删除结点的数据域赋值给e 
	*e = q->data;
	
	//释放需要删除的结点 
	free(q);
} 

2. 双链表

2.1 双链表与单链表的区别

  • 单链表只能从前向后遍历,这对于插入是不方便的
  • 双链表使得可以通过某结点访问它的直接前驱、直接后继

2.2 双链表的构成

  • 数据域
  • prior 前驱指针域
  • next 后继指针域
    双链表的构成

2.3 双链表结点类型描述

typedef struct DNode{
    
    
	ElemType data;				//数据域 
	struct DNode	*prior,		//前驱指针域 
					*next;		//后继指针域 
}DNode,*DLinkList;

2.4 双链表的基本操作实现

2.4.1 双链表的创建

实现代码

/* 
*	双链表的创建,返回头指针
*/ 
DNode *CreatList(DLinkList L,int n)
{
    
    
    DNode *head,*pNode,*sNode;
    int i;
    head = L;
    pNode = head;
    
    //循环创建新结点  
    for(i=1;i<=n;i++)
    {
    
    
    	//初始化新结点 
        sNode = InitList(sNode);
		 
		scanf("%d",&sNode->data);

		//pNode的后继结点指向sNode
		pNode->next=sNode;
		
		// sNode 的前驱结点指向  pNode
		sNode->prior=pNode;
		
		//将 sNode 赋值给  pNode
        pNode = sNode;
    }
	
	//返回头指针 
    return head; 
}

2.4.2 双链表的按位序删除

实现代码

/*
	根据位序删除双链表中的数据
*/ 
DLinkList deleteList1(DLinkList L,int i){
    
    
	DNode *pNode,*qNode;
	
	
	pNode = GetElem(L,i); 
	
	
	qNode = pNode->prior;
	
	qNode->next = pNode->next;
	
	pNode->next->prior = qNode;
	
	free(pNode);
	
	return L;
}

2.4.3 双链表的按值删除

实现代码

/*
	根据数据域删除双链表中的数据 
*/ 
DLinkList deleteList(DLinkList L,int i){
    
    
	DNode *pNode,*qNode;
	
	pNode = L->next;
	
	while(pNode->data != i){
    
    
		pNode = pNode->next;
	}
	
	
	qNode = pNode->prior;
	
	qNode->next = pNode->next;
	
	pNode->next->prior = qNode;
	
	free(pNode);
	
	return L;
}

3. 循环链表

3.1 循环单链表

3.1.1 循环单链表的定义

与单链表不同的是,循环单链表表尾结点指向了头结点,从而形成环

3.1.2 循环单链表的示意图

在这里插入图片描述

3.2 循环双链表

3.2.1 循环双链表的定义

与双链表不同的是,循环双链表的头结点的prior指针指向了表尾结点,表尾结点的next指向了头结点

3.2.2 循环双链表的示意图

在这里插入图片描述

4. 静态链表

4.1 静态链表的定义

借用数组实现的描述线性表的链式存储结构

4.2 静态链表的示意图

在这里插入图片描述

 1. 游标0的next值为2,线性表的表头元素存放在下标为2处(a)
 2. 游标2的next值为5:	线性表的直接后继元素存放在下标为5处(b)
 3. 游标5的next值为6:	线性表的直接后继元素存放在下标为6处(c)
 4. 游标为6的next值为3:	线性表的直接后继元素存放在下标为3处(d)
 5. 游标为3的next值为-1:	表示d为表尾

4.3 静态链表的特点

  • 插删不需要移动元素,只需要修改指针
  • 需要一次性分配大量空间,存储结构可以反映数据之间的逻辑性
  • 静态链表不允许扩容

单链表整体代码下载地址:https://download.csdn.net/download/SoloVersion/13711949
双链表整体代码下载地址:https://download.csdn.net/download/SoloVersion/13711997

关注我的技术公众号,时常会有优质的技术文章推送。
微信扫一扫下方二维码即可关注:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/SoloVersion/article/details/111313168