数据结构(2)——线性表

Ch2 线性表

知识结构:顺序存储结构(顺序表)、链式存储结构(单链表、双链表、循环链表)特殊的线性表——有序表
线性表的9个基本运算
1)初始化线性表、
2)销毁线性表、
3)判断线性表是否为空、
4)求线性表的长度、
5)输出线性表(线性表不为空时,顺序显示L中各节点的值域)、
6)求线性表L中指定位置的某个元素、
7)定位查找、
8)插入一个数据元素
9)删除数据元素

1.1 顺序表

顺序表指针:类型(顺序表类型);存储顺序表空间起始地址
使用顺序表的时候,传递的是顺序表指针,而非顺序表本身。
Eg: SqList *L;L=(SqList *)malloc(sizeof(SqList));
顺序表指针引用:使用引用参数的好处是可以将执行结果会传给参数。一般输出型参数均使用引用,不能参数值是否改变。
Eg: void CreatLIst(SqList *&L,Elem Type a[],int n) //注意:引用符号“&”是放在形参L前的。
1.1.2 顺序表的基本运算算法

//创建线性表
void InitList(SqList *&L)
{
 L=(SqList *)malloc(sizeof(SqList));
 L->length=0; 
} 
//销毁线性表
void DestroyList(SqList *&L)
{
 free(L);
} 
//判定是否为空表
bool ListEmpty(SqList *&L) 
{
 return(L->length==0);
}
//求线性表长度
int ListLength(SqList *&L)
{
 return(L->length);
} 
//输出线性表
void DispList(SqList *&L)
{
 int i;
 if(ListEmpty(L)) return;
 for(i=0;i<L->length;i++)
  printf("%c",L->data[i]);
 printf("\n");
 } 
 //求某个数据元素值(返回L中第i(物理地址)个元素的值,存放在e中)
 //以下算法时间复杂度为O(1);体现了顺序表的随机存取特性 
 bool GetElem(SqList *L,int i,ElemType &e)
 {
  if(i<1||i>L->length) return false;
  e=L->data[i-1];
  return true;
 } 
 //按元素值查找 
 int LocateElem(SqList *L,ElemType e)
 {
  int i=0;
  while(i<L->lenght&&L->data[i]!=e)
  i++;
  if(i>=L->lenght) 
   return 0;//i>L->length证明从头到尾都没找到 
  else
   return i+1;
  } 
//插入数据元素(在顺序表的第i(物理地址)个位置上插入新的元素e)
//以下算法的平均时间复杂度为O(n) 
bool ListInsert(SqList *&L,int i,ElemType e)
{
 int j;
 if(i<1||i>L->length+1)//注意也可以在末尾插如新元素,所以是L->length+1 
 return false;
 i--;
 for(j=L->length;j>i;j--)//注意循环要从顺序表末尾位置开始,不然移动数据时会覆盖原有数据
 //又因为长度是数据个数,本来就比下标大1,所以j从length开始就是从末尾元素下一个位置的下标开始的
        
  L->data[j]=L->data[j-1];
 L->data[i]=e;
 L->length++;
 return true;
}
//删除数据元素 (删除线性表位置i的元素)
//以下算法的平均时间复杂度为O(n) 
bool ListDelete(SqList *&L,int i,ElemType &e)
{
 int j;
 if(i<1||i>L->length)
 return false;
 i--;
 e=L->data[i];
 for(j=i;j<L->length-1;j++)
  L->data[j]=L->data[j+1];
 L->length--;
 return true;
 } 

1.1.3 线性表的算法设计
——数据采用顺序表存储,利用顺序表的基本运算解决问题。
例2:将一个线性表中小于表首元素(基准元素)值的元素放到基准元素前面,大于的放到后面。
解法一:先用temp存放基准元素,从下标1和下标length-1开始找,两个分别小于、大于基准元素值的元素交换位置,直至i=j,然后将基准元素放到i=j的下标位置上。(算法复杂度为O(n));
解法二:先用temp存放基准元素,i=0,j从length-1开始找,找到小于基准元素值的元素,放到下标i处,j–; i从下标1开始找,找到大于基准元素值的元素,后移到下标j处,i++.直至j<=i,将基准元素放到下标i处。解法二优于解法一,因为解法一中是交换两个元素,交换过程需要三次移动(temp=a;a=b;b=temp)。

1.2 单链表

单链表增加一个头节点的优点:
1)第一个节点的操作和表中其他节点的操作相一致,无需进行特殊处理;
2)无论链表知否为空,都有一个头节点,空表和非空表处理统一。
基本操作:插入节点:
//在p节点后插入s节点
s->next=p->next;p->next=s;
删除节点:
//删除p节点后的一个节点p->next=p->next->next;
1.2.1 单链表的建表方法
1)头插法
注意:建表链表的节点顺序和逻辑次序相反l

  • 从一个空表开始,创建一个头节点;
  • 依次读取数值数组a中的元素,生成新节点;
  • 将新节点插入到当前链表的表头上,直到结束为止。

示例代码:

//头插法建立单链表
void CreatListF(LinkList *&L,ElemType a[],int n)
{
 	LinkList *s;
 	int i;
 	L=(LinkList *)malloc(sizeof(LinkList));
	L->next=NULL;//创建头节点,其next域置为NULL
  	for(i=0;i<n;i++) 
  	{
   		s=(LinkList *)malloc(sizeof(LinkList));//循环建立数据节点 
   		s->data=a[i];
  		s->next=L->next;//将是节点插在原节点之前、头节点之后 
  		L->next=s;
  	}
} 

2)尾插法
注意:建表链表的节点顺序和逻辑次序相同。

  • 从一个空表开始,创建一个头节点;
  • 依次读取数值数组a中的元素,生成新节点;
  • 将新节点插入到当前链表的表尾上,直到结束为止。

注意:单链表里面,尾插法插入一个节点总是要找到它的前驱节点,但在单链表里除了头节点地址已知,其他节点并未标识,所以需要增加一个尾指针r,使其始终指向当前链表的尾节点。
示例代码:

//尾插法建表
void CreatListR(LinkList *&L,ElemType a[],int n)
{	
	LinkList *s,*r;	int i;
	L=(LinkList*)malloc(sizeof(LinkList));	
	r=L;//r始终指向尾节点,刚开始的尾节点就是头节点。
	for(i=0;i<n;i++) 	 
	{
		s=(LinkList *)malloc(sizeof(LinkList));//循环建立数据节点 
		s->data=a[i];
		r->next=s;//将*s插入*r之后 
		r=s;
	}
	r->next=NULL; 
}  

1.2.2 线性表基本运算在单链表上的实现

//单链表 
//在p节点后插入s节点 
s->next=p->next;
p->next=s;
//删除p节点后的一个节点
p->next=p->next->next;
//头 插法建立单链表
void CreatListF(LinkList *&L,ElemType a[],int n)
{
 LinkList *s;
 int i;
 L=(LinkList *)malloc(sizeof(LinkList));
 L->next=NULL;//创建头节点,其next域置为NULL
  for(i=0;i<n;i++) 
  {
   s=(LinkList *)malloc(sizeof(LinkList));//循环建立数据节点 
   s->data=a[i];
  s->next=L->next;//将是节点插在原开始结点之前、头节点之后 
  L->next=s;
  }
} 
//尾插法建表
void CreatListR(LinkList *&L,ElemType a[],int n)
{
 LinkList *s,*r;
 int i;
 L=(LinkList *)malloc(sizeof(LinkList));
 r=L;//r始终指向尾节点,刚开始的尾节点就是头节点。 
  for(i=0;i<n;i++) 
  {
   s=(LinkList *)malloc(sizeof(LinkList));//循环建立数据节点 
   s->data=a[i];
  r->next=s;//将*s插入*r之后 
  r=s;
  }
  r->next=NULL; 
}  
//初始化线性表()就是创建一个空表 
void InitList(LinkList *&L)
{
 L=(LinkList *)malloc(sizeof(LinkList));//注意空表也是有头节点的 
 L->next=NULL;
} 
// 销毁线性表
//释放单链表L占用的空间,要逐一释放全部节点的空间
void DestroyList(LinkList *&L)
{
 LinkList *pre=L,*p=L->next;//pre指向*p的前驱节点(开始时即为pre指向头节点) 
 while(p!=NULL)//循环扫描单链表 
 {
  free(pre);//释放ore节点 
  pre=p;//pre、p同步后移一个节点 
  p=pre->next;
  } 
 free(pre);//循环结束时p为NULL,pre指向尾节点,需要释放 
}
//判断线性表是否为空
bool ListEmpty(LinkList *L) 
{
 return(L->next==NULL);//即判断头节点指针域是否为NULL 
}
//求线性表的长度
//需要遍历单链表的所有节点,返回数据节点个数
int ListLength(LinkList *L)
{
 int n=0;
 LinkList *p=L;//开始时p指向头节点,头节点不计数,所以此时n为0
 while(p->next!=NULL) //注意:循环结束的条件应该是p指向尾节点而不是p为空 
 {
  n++;
  p=p->next; 
 }
 return(n);//循环结束p只想尾节点,n为节点个数 
 } 
 //输出线性表
 //注意扫描单链表L的每个数据节点,并输出其data域的值
 void DispList(LinkList *L)
 {
  LinkList *p=L->next;//注意要从数据节点开始输出
  while(P->next!=NULL)
  {
   printf("%d",p->data)
   p=p->next;//节点依次后移 
  } 
 } 
 //输出节点i的值
 //思路:遍历找到i节点,并返回其值给e 
 bool GetList(LinkList *L,int i,ElenType &e) 
 {
  int j=0;//记录当前节点的序号
  LinkList *p=L;
  while(j<i&&p!=NULL)
  {
   j++;
   p=p->next;
  } 
  if(p=NULL)//循环结束p为空,表示找到结束也没找到序号为i的节点 
   return false;
  else
    {
     e=p->data;
     return true; 
  }
 }
 //按元素值查找
 //从头开始扫描,寻找值域等于e的节点
 //算法时间复杂度为O(n)——单链表不具有随机存取特性 
 int LocateElem(LinkList *L,ElemType e)
 {
  int i=1;
  LinkList *p=L->next;//从第一个数据节点开始找
  while(p->data!=e&&p!=NULL)
  {
   p=p->next;
   i++;
  } 
  if(p==NULL)
   return(0);
  else 
   return(i);
  } 
  //在位置i插入一个元素
  //先找到第i-1个节点*p,将值为e的节点*s节点插入到其后 
  bool ListInsert(LinkList *&L,int i,ElemType e)
  {
   int j=0;
   LinkList *p=L;
   LinkList *s;
   while(j<i-1&&p!=NULL)
   {
    j++;
    p=p->next;
 }
 if(p==NULL)
  return false;
 else
  {
   s=(LinkList *)malloc(sizeof(LinkList));
   s->data=e;
   s->next=p->next;
   p->next=s;
   
   return true;
  }
   } 
//删除第i个元素
//同样要先扫描寻找第i-1个元素
   bool ListDelete(LinkList *&L,int i,ElemType &e)
  {
   int j=0;
   LinkList *p=L;
   LinkList *q;//待删除节点(第i个节点) 
   while(j<i-1&&p!=NULL)
   {
    j++;
    p=p->next;
 }
 if(p==NULL)
  return false;
 else
  {
   q=p->next;
   if(q==NULL)//即第i-1个位置刚好就是尾节点了
    return false;  
   e=q->data;
   p->next=q->next;
   free(q);
   
   return true;
  }
   }

1.2.3 单链表的算法设计方法
1.2.3.1 一查找为基础的算法设计l 按照条件进行节点查找l 进行插入或者删除操作
例1:删除节点值最大的节点(假设唯一)
设计思路:定义节点pre和p分别指向头节点和开始数据节点,依次扫描单链表,并比较其值,分别放入maxp和maxpre节点进行记录,扫描结束就删除maxpre节点时间复杂度为O(n)
例2:有一个带头节点的单链表L(至少有一个数据节点),设计算法使其元素递增有序。
设计思路:将单链表L看作两部分:一部分是头节点带领的有序链表;一部分是开始节点为P的无序链表。将p从第一个数据节点开始往后扫描,把p节点按有序方式一个个插入到头节点代表的链表中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值