线性表的链式存储

单链表上的基本运算

单链表的存储定义:

typedef struct Node
{
	ElemType data;
	struct Node* next;
}Node,*Linklist;   //Linklist为结构指针类型

1、初始化单链表

InitList(Linklist *L)
{
	*L=(Linklist)malloc(sizeof(Node));   //建立头节点 
	(*L)->next==NULL;   //建立空的单链表L 
}

注:L是指向头结点的指针(指向指针的指针)。

2、建立单链表
假设线性表中结点数据类型是字符,并以“$”作为输入结束符。

(1)头插法建表:
重复读数据,生成新结点,存数据到新结点数据域;
将新结点插入表头结点之后,直至读入结束标志为止。

在这里插入图片描述

void CreateFromHead(LinkList L){ //L是带头结点的空链表指针 
{
	Node *s,*r;
	char c;
	int flag=1;
	r=L; //r指针动态指向链表的当前表尾
	while(flag);
	{
		c=getchar();
		if(c!='$')
		{
			s=(Node*)malloc(sizeof(Node)); //建立新结点s 
			s->data=c;
			r->next=s;  
			r=s;
		}
		else 
		{
		    flag=0;
		    r->next=NULL; //将最后一个结点的next域置为空
		}
	}
}

(2)尾插法建表
指针r跟踪链尾结点;
新结点追加。

在这里插入图片描述

void CreateFromHead(LinkList L){ //L是带头结点的空链表指针 
{
	Node *s;
	char c;
	int flag=1;
	while(flag);
	{
		c=getchar();
		if(c!='$')
		{
			s=(Node*)malloc(sizeof(Node)); //建立新结点s 
			s->data=c;
	
		}
		else flag=0;
	}
}

3、单链表查找

(1)按序号查找:查找带头结点单链表中第i个结点。
算法思想:
用一指针(p)从头结点(L)开始顺着链扫描结点,同时计数;
直到扫描了i个结点或链表扫描完。

Node* Get(LinkList L,int i) /*找到(1≤i≤n),返回该结点; 否则返回NULL*/
{	
    int j;  
    Node *p;
    if(i<=0) return NULL;
   	p=L; j=0/*从头结点(编号0)开始扫描 */
   	while ((p->next!=NULL) && (j<i) /*p不为尾结点且没到i*/ 
   	{ 	p=p->next;
	    j++}            /* 扫描下一结点,并计数 */
   	if(i==j)  return p;   /* 找到了第i个结点 */
   	else return NULL/* 找不到,i≤0或i>n */
}

(2)按值查找:查找是否有值等于e的结点,若有则返回首次找到的结点地址,否则返回NULL。
算法思想:
从第一个结点开始扫描,顺着链逐个将结点的值和给定值e作比较。

Node *Locate(LinkList L,ElemType key) /*查找值为key的结点,找到返回结点地址,否则返回NULL*/
{  	Node *p;
   	p=L->next;   /*从第一个结点开始比*/
   	while (p!=NULL) /*链没完就比对*/
     	if (p->data!=key)
     		p=p->next;     
     	else break;     /*找到结点key,退出循环*/
   	return p;
}

这两个算法的时间复杂度都是O(n).

4、求单链表长度

int ListLength(Linklist L)
{
	Node *p;
	p=L->next;
	int j=0;
	while(p!=NULL)
	{   p=p->next;
		j++;
	}
	return j;
}

5、单链表插入操作
在带头结点的单链表L中第i个元素之前插入元素e。
定位:用指针pre查找第i-1个结点;
构造:构造值为e的新结点并由指针s指示。
插入:新结点抓后链(指针域指向第i个结点),第i-1个结点抓新结点。
在这里插入图片描述

void InsList(LinkList L,int i,ElemType e)
{  
	Node *pre, *s;  
	pre=L; int k=0;
  	while(pre!=NULL && k<i-1)   /*pre指针定位第i-1个结点*/ 
	{ 	pre=pre->next;	
	    k=k+1;     
	}
  	if(k!=i-1)          //链长不足i-1     
  	{ 	printf("插入位置不合理!")return; 
	}
  	s=(Node*)malloc(sizeof(Node)); 
  	s->data=e;            //构造待插入结点
  	s->next=pre->next; 
	pre->next=s;
	return OK;
} 

6、单链表删除操作
在单链表L中删除第i个结点。
定位:用指针pre计数扫描找到第i-1个结点,指针r指向第i个结点
绕过:让第i-1个结点连接第i+1个结点,绕过第i个结点
删除:释放第i个结点空间。
在这里插入图片描述

int DelList(LinkList L,int i,ElemType *e) /*删除第i个元素,值存入*e中*/
{ 	
    Node *pre,*r; 
	int k=0;
	pre=L;
	while(pre->next!=NULL && k<i-1)	/*寻找结点i前驱,使pre指向它*/
	{ 	
	    pre = pre->next;  
	    k = k+1; 	
	}	
	if(pre->next==NULL)     /* p->next=NULL或i<1跳出循环,删除位置i不合法。*/
	{	
	    printf("删除结点的位置i不合理!"); 	
		return ERROR;
	}
	r=pre->next;   /*r指向第i个结点*/
	pre->next=r->next;    /*绕过删除结点r*/
	*e=r->data; 	
	free(r);    /*释放结点r内存空间*/
	return OK;
}

PS:删除算法中的循环条件(pre->next!=NULL && k<i-1)与前插(pre!=NULL && k<i-1)的条件不同,因为前插时的插入位置有m+1个(m为数据元素的总数)。i=m+1是指在第m+1个位置插入,即链尾。而删除操作合法位置只有m个,若采用与前插相同的循环条件,则会出现指针指空的情况,使删除操作失败。

7、合并两个有序单链表
将元素非递减有序排列的单链表LA和LB,合并成非递减有序排列的单链表LC。
要求:新表LC利用表LA和LB中的元素结点空间,而不需要额外申请结点空间。
思路:
通过更改结点next域重建结点间的线性关系;
利用尾插法保证新表非递减有序。
例如LA=(2, 2, 3), LB=(1, 3, 3, 4), 则LC=(1, 2, 2, 3, 3, 3, 4)

LinkList MergeLinkList(LinkList LA,LinkList LB)
{
   	Node *pa=LA->next, *pb=LB->next;  	//pa、pb跟踪LA、LB表
	LinkList LC=LA; 
	LC->next=NULL;r=LC; //初始化空置LC,借用LA头结点(r指向)
	while(pa!=NULL && pb!=NULL)    //两表均未处理完,选择较小值结点插入到新表LC中
	{	if(pa->data <= pb->data)
		{	r->next=pa; r=pa; pa=pa->next;}
		else
		{	r->next=pb; r=pb; pb=pb->next;} 
	}
	if(pa) 	
	    r->next = pa; //表LA未完,后续元素链到新表LC表尾
	else
		r->next = pb; //否则将表LB中后续元素链到新表LC表尾
	free(LB); 
	return(LC);
 }

在这里插入图片描述
在这里插入图片描述

循环链表

循环链表 :首尾相接的单链表,尾结点指针域指向头结点或第一个结点,形成一个环。
在这里插入图片描述
判别p是否为尾指针:p!=Lp->next!=L.
用尾指针表示链表,头/尾结点存储位置分别是rear->next->nextrear,访问它们的时间复杂度O(1)。

1、初始化循环单链表

InitCLinkList(LinkList *CL)
{
	*cl=(*LinkList)malloc(sizeof(Node)); //建立头结点 
	(*CL) ->next=*CL; //建立空的循环单链表CL 
}

2、建立循环单链表

假设线性表中数据类型是字符,以“$”作为输入结束标志符。

void CreateCLinkList(LinkList CL) //采用尾插法
{
	Node *s,*rear;
	char c;
	rear=CL;
	c=getchar();
	while(c!='$')
	{
		s=(Node*)malloc((sizeof(Node));
		s->data=c;
		rear->next=s;
		rear=s;
		c=getchar();
	}
	rear->next=CL; //让最后一个结点的next域指向头结点
}

3、循环单链表的合并

合并带头结点的循环单链表LA、LB,合并后头指针为LA,不排序。
思想:
先找到两个链表的尾,由指针p、q指向它们;
将LA的尾(p指向)与LB的第一个结点链接起来;
修改LB的尾q,使它的链域指向LA的头结点。

算法1(采用头指针的循环单链表):

LinkList merge_1(LinkList LA,LinkList LB)
{
  	Node *p,*q;
    p=LA; q=LB;
 	while(p->next!=LA) p=p->next;	/*找到表LA的表尾*/
 	while(q->next!=LB) q=q->next;	/*找到表LB的表尾*/
 	q->next=LA;	 /*改表LB尾指针,指向表LA头结点*/
 	p->next=LB->next; /*改表LA尾指针,指向表LB第一个结点*/
	free(LB); 
	return(LA);
}

执行时间是O(n).

算法2(采用尾指针的循环单链表):

LinkList merge_2(LinkList RA,LinkList RB)
{
  	Node *p;
    p=LA->next;  //保存链表RA的头结点地址 
 	RA->next=RB->next->next;  //RB的开始结点连在RA的终端结点之后 
	free(RB->next);  //释放RB的头结点 
	RB->next=p;  //RA的头结点连在RB的终端结点之后 
	return RB;  //返回新循环链表的尾指针 
}

执行时间是O(1).

双向链表

单链表每个结点增加指向其前趋的指针域prior。
这样形成的链表有两条方向不同的链——双 ( 向) 链表 。

从前往后的操作与单链表相同:求表长、定位、取元素;
方便找前驱结点。
双链表的结点结构 :
在这里插入图片描述
结构定义:

typedef struct DNode
{   ElemType data;
	struct DNode *prior,*next;
} DNode,* DoubleList;

在这里插入图片描述
设p为指向双链表的某一结点,则有下式成立:
p->prior->next==p;
p==p->next->prior.

1、双向链表的前插操作

在第i个结点之前插入新结点。
在这里插入图片描述

void DlinkIns(DoubleList L,int i,ElemType e)
{ 	DNode *s, *p;/*检查待插入位置i是否合法*//*若位置i合法,则让指针p指向它*/
  	s=(DNode*)malloc(sizeof(DNode));
  	if(s)
   	{  	s->data=e;
      	s->prior=p->prior;   (1) 
		p->prior->next=s;    (2) //链接前链
      	s->next=p;           (3)  
		p->prior=s;          (4) //链接后链
		return TRUE;  	
	}
	else return FALSE;
}  

2、双向链表的删除操作

删除双向链表的第i个结点。
在这里插入图片描述

int	DlinkDel(DoubleList L,int i,ElemType *e)
{	DNode *p;/*首先检查待插入的位置i是否合法*//*若位置i合法,则让指针p指向它*/
  	*e = p->data;//取值
  	p->prior->next=p->next;
  	p->next->prior=p->prior;
  	free(p);
   	return TRUE;
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值