线性表的链式表示与实现

1.单链表

①特点
用一组任意的存储单元存储线性表的数据元素
利用指针实现了用不相邻的存储单元存放逻辑上相邻的元素
每个数据元素ai,除存储本身信息外,还需存储其直接后继的信息
结点:数据元素ai 的存储映像。
数据域:元素本身信息
指针域:指示直接后继的存储位置
定义:结点中只含一个指针域的链表。
特征:
每个元素(表项)由结点(Node)构成。
线性结构
结点可以不连续存储
表可扩充
最后一个节点的指针域为空
头指针、头结点、第一个元素结点
以线性表中第一个数据元素a1 的存储地址作为线性表的地址,称作线性表的头指针
有时为了操作方便,在第一个结点之前虚加一个“头结点”,以指向头结点的指针为链表的头指针
单链表存储结构的实现
ypedef struct LNode {
ElemType data;
struct LNode *next;
} LNode ,*LinkList;
或者
struct LNode {
ElemType data;
struct LNode *next;
} ;
typedef struct LNode LNode;
typedef LNode * LinkList;
(*p) 表示p所指向的结点
(*p).data p->data 表示p指向结点的数据域
(*p).next p->next 表示p指向结点的指针域
生成一个LNode型新结点:
p=(LinkList)malloc(sizeof(LNode));
系统回收p结点:
free§
单链表特点:
它是一种动态结构,整个存储空间为多个链表共用
不需预先分配空间
指针占用额外存储空间
不能随机存取,查找速度慢
单链表基本操作的实现
GetElem(L, i, &e) // 取第i个数据元素
ListInsert(&L, i, e) // 插入数据元素
ListDelete(&L, i, e) // 删除数据元素
CreateList_L(&L, n) // 创建线性表

定义:
#define NULL 0
单链表查找操作—GetElem(L, i, &e)
算法的基本思想
令p为指针变量,首先指向第一个结点,变量j为计数器;
依次向后查找,循环结束的条件,p为空或者j=i。
如果p为空并且j>i,出错,
否则找到了,用e返回第i个元素。
算法实现:

Status GetElem_L(LinkList L, int i, ElemType &e)
  {  // L是带头结点的链表的头指针,以 e 返回第 i 个元素
	  p = L->next; // p指向第一个结点
      j = 1;  // j为计数器
	  while (p && j<i)  //while(p!=NULL&&j<i)
	  {     p = p->next;  ++j;  }
         if ( !p || j>i )  //if(p==NULL||j>i)
             return ERROR;      
	  e = p->data;//  取得第 i 个元素
	  return OK;}

单链表的插入操作 ---- ListInsert(&L, i, e)
第i个元素之前插入元素e
s = (LinkList)malloc(sizeof(LNode));// 生成新结点
s->data = e;
s->next = p->next;
p->next = s;
两条语句顺序不能错
需要知道前驱结点的指针
单链表插入操作 —ListInsert_L ( LinkList L, int i, int e )

int  ListInsert_L ( LinkList L, int i, int e ) 
{// 带头结点的单链表L中第 i 个位置之前插入元素 e
LNode *p,*s;   int j;
p = L; j = 0;
while ( p && j<i-1 ) { p = p->next; ++j; } 
if ( ! p || j >i-1 ) return ERROR; 
s = ( LinkList ) malloc ( sizeof ( LNode ) ); // 生成新结点
s->data = e; // 使新结点数据域的值为 e
s->next = p->next; // 将新结点插入到单链表 L 中
p->next = s; // 修改第 i-1 个结点指针
return OK;}

时间复杂度: T(n)=O(n)
单链表的删除操作 -----ListDelete(&L, i, &e)
删除第i个元素,并保存数据到元素e中
q = p->next; // 用指针 q 指向被删除结点
p->next = q->next; // 删除第 i 个结点
*e=ai;
free(q);
需要知道前驱结点指针,并且保存第i个结点指针
单链表的删除操作—— ListDelete(&L, i, &e)

int ListDelete_L ( LinkList L, int i, int *e )

{// 删除第 i 个元素,并由 e 返回其值
LNode *p,*q;int j;
p = L; j = 0;
while ( p->next && j<i-1 ) { p = p->next; ++j; } 
if ( ! p || j >i-1 ) return ERROR; // 删除位置不合理
 q = p->next;   // 用指针 q 指向被删除结点
p->next = q->next;   // 删除第 i 个结点
*e = q->data;    // 取出第 i 个结点数据域值
free (q);     // 释放第 i 个结点
return OK;
} // LinkDelete_L

时间复杂度: T(n)=O(n)
创建单链表—CreateList_L(&L, n)
头插法:就是按结点的逆序方法逐渐将结点插入到链表的头部。新加入的结点在链表的头部
尾插法:就是按结点的顺序逐渐将结点插入到链表的尾部。
头插法创建带有头结点的单链表

void CreateList_L ( LinkList  &L, int n )

{    // 输入 n 个数据元素的值,建立带头结点的单链表 L
LNode *p;    int i;
L = ( LinkList ) malloc ( sizeof ( LNode ) );
L->next = NULL; // 先建立一个带头结点的空链表
 for ( i = n; i > 0; --i )
 {	p = ( LinkList ) malloc ( sizeof ( LNode ) ); 
	scanf ("%d" ,&p->data ); 
	p->next = L->next; 	
    L->next = p;       
         } 
  } 

尾插法创建带有头结点的单链表
CreateListTail_L(LinkList &L, int n)
{
r= L = (LinkList) malloc( sizeof (LNode) );
L->next = NULL;
for( i=1; i<=n; ++i){
s = (LinkList) malloc( sizeof (LNode) );
scanf( &s->data);
s->next = NULL;
r->next = s; ①
r = s; ②
}
}

线性表的合并问题
void MergeList_L ( LinkList La, LinkList Lb, LinkList *Lc, )
{ // 归并 La 和 Lb 得到新的单链表 Lc ,Lc 的元素也按非递减排列
Node *pa,*pb;
pa = La->next; pb = Lb->next;
Lc = pc = La; // 用 La 的头结点作为 Lc 的头结点
while ( pa && pb ) {
if ( pa->data <= pb->data ) { // 如果 pa->data≤pb->data
pc->next = pa; pc = pa; pa = pa->next;
}
else { pc->next = pb; pc = pb; pb = pb->next; }
}
pc->next = pa ? pa : pb; // 插入剩余段
free (Lb); // 释放 Lb 的头结点
} // MergeList_L
2.循环链表
考虑如何实现将带有尾指针的两个循环链表合并为一个?
P=rearA->next;
rearA->next=rearB->next->next;
q=rearB->next;
rearB->next=p;
Free(q)

2.双向链表

在这里插入图片描述

如果插入与删除仅在链表的两端发生,可采用带表尾指针的循环链表结构。
在表尾插入,时间复杂性 O(1)
在表尾删除,时间复杂性 O(n)
在表头插入,相当于在表尾插入
在表头删除,时间复杂性 O(1)
双向链表
双向链表的C语言定义

typedef struct DuLNode {
     ElemType data;
     struct DuLNode *prior,*next;
} DuLNode ,*DuLinkList;

在这里插入图片描述

结点指向 p->prior->next == p == p->next->prior
在这里插入图片描述
双向链表中插入操作
在这里插入图片描述
s=(DuLNode *)malloc(sizeof(DuLNode ));
s->data=x;
s->prior=p->prior;
p->prior->next=s;
s->next=p;
p->prior=s;
双向链表的插入操作算法:

Status ListInsert_DuL ( DuLinkList &L, int i, ElemType e ) {

if ( ! ( s = ( DuLinkList ) malloc ( sizeof ( DuLNode ) ) ) ) return ERROR
s->data = e; // 将数据放入新结点的数据域
s->prior = p->prior; // 将 p 的前驱结点指针放入新结点的前向指针域
s->next = p; // 将 p 的放入新结点的反向指针域
p->prior->next = s; // 修改 p 的前驱结点的反向指针
p->prior = s; // 修改 p 的前向指针
return OK;
} // ListInsert_DuL
T(n)=O(n)
双向链表删除操作
在这里插入图片描述
双向链表的删除操作算法:
Status ListDelete_DuL ( DuLinkList &L, int i, ElemType &e ) {
// 删除带头结点的双向循环链表 L 中第 i 个元素并将元素值返回,1≤i≤表长
if ( ! ( p = GetElemP_DuL ( L, i ) ) ) return ERROR;
// 在 L 中确定第 i 个元素,p 为指向该结点的指针;
// 若 i < 1 或 i > 表长,则 p 为 NULL,第 i 个元素不存在
e = p->data; // 将 p 指向的结点数据域中的值取出
p->prior->next = p->next; // 修改 p 的前驱结点的反向指针
p->next->prior = p->prior; // 修改 p 的后继结点的前向指针
free §; // 释放 p 结点
return OK;
} // ListDelete_DuL
T(n)=O(n)
顺序表与链表的比较
存取方式
顺序表可以随机存取,也可以顺序存取
链表是顺序存取的
插入/删除时移动元素个数
顺序表平均需要移动近一半元素
链表不需要移动元素,只需要修改指针
若插入/删除仅发生在表的两端,宜采用带尾指针的循环链表
考虑如何选择合适的存储结构?
用上述定义的单链表实现线性表的操作时,存在的问题:
单链表的表长是一个隐含的值;
在单链表的最后一个元素之后插入元素时,需遍历整个链表;
在链表中,元素的“位序”概念淡化,结点的“位置”概念加强。

3.一元多项式的表示与相加

一元多项式的表示:
在这里插入图片描述
n 阶多项式 Pn(x) 有 n+1 项。
系数 P0, P1, P2, …, Pn
指数 0, 1, 2, …, n。按升幂排列
若:
对S(x)这样的多项式采用全部存储的方式则浪费空间。怎么办?
一般n次多项式可以写成:在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
其存储结构可以用顺序存储结构,也可以用单链表:
多项式的链表表示:typedef struct Polynode
{ int coef,exp;
struct Polynode *next;
} Polynode , *Polylist ;
优点是:
多项式的项数可以动态地增长,不存在存储溢出问题。
插入、删除方便,不移动元素。
一元多项式的建立
2) 通过键盘输入一组多项式的系数和指数,以输入系数0为结束标志,并约定建立多项式链表时,总是按指数从小到大的顺序排列。
算法描述:从键盘接受输入的系数和指数; 用尾插法建立一元多项式的链表。

 polylist  polycreate (   ) 
{  
    polynode *head,  *rear,  *s;            int c, e; 
     head=(Polynode *)malloc(sizeof(Polynode)); 
    rear=head; 
    scanf(″%d, %d″, &c, &e); 
     while(c! =0) {
   s=(Polynode*)malloc(sizeof(Polynode)); 
   s->coef=c ;   s->exp=e ; 
   rear->next=s ;    rear=s; 
   scanf(″%d, %d″, &c, &e);     } 
   rear->next=NULL;    return(head); 
} 

一元多项式相加
扫描两个多项式,若都未检测完:
若当前被检测项指数相等,系数相加。若未变成 0,则将结果加到结果多项式。
若当前被检测项指数不等,将指数小者加到结果多项式。
若一个多项式已检测完,将另一个多项式剩余部分复制到结果多项式。
在这里插入图片描述

while (pa!=NULL&&pb!=NULL)
{    if  (pa->exp< pb->exp)
         {  pc->next=pa;  pc=pa;  pa=pa->next;}
   else if (pa->exp==pb->exp)
         {      sum=pa->coef + pb->coef;
                 if (sum!=0)
                 {     pa->coef=sum; pc->next=pa; 
                        pc=pa;pa=pa->next;   
                         r=pb;pb=pb->next;free(r);
                   }
                else 
                {     r=pa;pa=pa->next;free(r);
                      r=pb;pb=pb->next;free(r);
                 }                
}   else
        {      pc->next=pb;   pc=pb;pb =pb->next;    }
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cai-4

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值