链表和数组实现线性表

目录

(一)线性表

1.1抽象数据类型

1.2 线性表的顺序存储

1.3 线性表的链式存储

(二)广义表

  (三)多重链表

(四)实例分析

矩阵的表示

多项式的表示



今天走入数据结构的线性表一节。希望各位大神多多指点哦。

(一)线性表

线性表第一个元素没有前驱元素,称为头结点。最后一个元素没有后驱元素,称尾结点。中间的每一个元素都有前驱元素和后驱元素。

线性表按照物理存储结构,又可以分为顺序表和链表。线性表没有元素的时候称为空表。表起的位置叫做表头,表结束的位置叫做表尾

常见的线性表:顺序表,链表,栈,队列,字符串。一个数据元素可以由若干项组成,把数据元素称为记录,含有大量记录线性表叫做文件。

线性表是一种逻辑上连续的物理结构

数组(物理上和逻辑上都是线性)——浪费内存

链表 (逻辑上线性,物理上非线性)——按需申请内存

1.1抽象数据类型

类型名称: 线性表(List)

数据对象集:线性表是n个元素构成的有序序列(a1,a2,a3…an)

操作集: 线性表L∈list , i表示位置,元素X∈ElementTyp

初始化空表:List make()           
查找值为x的下标 int find(int x,List ptrl)
在线性表的第i个位置插入一个值为x的元素(i从1开始)void insert(int X,int i,List Ptrl);
删除指定位序为i的元素 void Delete(int i,Last Ptrl)
List make()
{
 List Ptrl;
 Ptrl=(List)malloc(sizeof(struct LNode));

 Ptrl->last=-1;
 return Ptrl;
}
int find(int x,List Ptrl)
{
  int i=0;
  while(Ptrl->Date[i]!=X&&i<=Ptrl->last)
   {
     i++;
   }

    if(i>Ptrl->last) return -1;//没找到
    else return i;
}

插入:

void insert(int X,int i,List Ptrl)
{
  int j;
  if(Ptrl->Last==MAXSIZE-1)//Maxsize是数组大小
       printf("表满");//表已经满了,不能插入

   if(i<1||i > Ptrl->Last+2)
   {
        printf("位置不合法");
        return;
   }//检查插入的合法性

for(j=Ptrl->Last;j>=i-1;j--)
   {
   //先移动a[n]
   Ptrl->Date[j+1]=Ptrl->Date[j];//每次把j位置的元素移动到i+1
   Ptrl->Date[i-1]=x;//新元素插入(数组从1开始下标)
   Ptrl->Last++;//last仍然指向最后的元素,多了一个元素
   }
   return;
}

此时的插入的数组是从1开始计数的,实际上从数组下标i-1的时候开始插入。

也就是要把i-1下标之后的元素全部往后面移动一位,留出(i-1)的位置来插入。

删除:

删除表中第i个位置上的元素 (要删除下标为i-1的元素,要把下标为i的移动到i-1位置上)

void Delete(int i,Last Ptrl)
{
  int j;
  if( i<1|| i>Ptrl->last+1 )//检查空表以及删除位置的合法性
   {
    printf("不存在第%d个元素”,i);
    return 0;
   }
 
  for(j=i;j<=Ptrl->Last;j++)
    {
     Ptrl->Date[j-1]=Ptrl->Date[j];//i到i-1 i+1到i
    }

     ptrl->last--;//Last元素仍然指向最后一个元素
     return;
 }

1.2 线性表的顺序存储

typedef struct LNode* List;//定义结构体指针名字为List

struct LNode{
 int a[100];
 int last;//last记录线性表最后一个元素存放的位置
};

struct LNode L;
List Ptrl;

访问下标为i的元素  :L.a[i]或者Ptrl->a[i] 

线性表长度:L.last+1或者ptrl->last+1

1.3 线性表的链式存储 

typedef struct LNode*List;

struct LNode{
 int data;//数据域
 List next;//地址域
};

struct LNode L;
List Ptrl;
求表长       int Length(List Ptrl)
按序号查找元素    List FindKth(int k,List Ptrl)
按值查找   List Find(int X,list Ptrl)

插入新结点 List insert(int X,int i,List Ptrl)

删除链表的第i个结点  List Delete(int i,List Ptrl)

求表长(链表遍历)

int Length(List Ptrl)
{
   List p=Ptrl;//p指向表的第一个结点
   int count=0;

   while(p){
     p=p->Next;
     count++;//当前p指向的是第j个结点
  }
   return count;
}

 按序号查找

List FindKth(int k,List Ptrl)
{
  list p=ptrl;
  int i;

  while(p!=NULL&&i<k)
  {
   p=p->next;
   i++;
  }

  if(i==k) return p;//找到第k个,返回指针
  else return NULL;//否则返回空
}
  

按值查找 

List Find(int X,list Ptrl)
{
  List p=Ptrl;
  while(p!=NULL&&p->Date!=x)
    {
        p=p->Next;
    }
    return p;
}

 插入(在第i-1个结点后插入一个值为x的新结点)
(1)先构造一个新结点,用s指向
(2)  再找到链表的第i-1个结点,用p指向
(3)修改指针,插入结点(p之后的新结点s)

List insert(int X,int i,List Ptrl)//在第i个位置插入x
{
   List p,s;

   if(i==1){
        //新节点插入在表头
        s=(List)malloc(sizeof(struct LNode));//申请,填充结点
        s—>Date=X;
        s->Next=Ptrl;
        return s;//返回新表头指针
     }


   p=Findkth(i-1,Ptrl);//查找第i-1个结点
   if(p==Null){
       printf("参数错”);
       return NUll;
    }

   else{
     s=(List)malloc(sizeof(struct LNode));//申请填充结点
     s->Date=X;
     s->Next=p->Next;//新节点插入在第i-1个节点后面
     p->Next=s;
     return NUll;
}

 删除(删除链表的第i个结点)
  1.先找到链表的第i-1个结点,用p指向;     
  2.再用指针s指向要被删除的结点(p的下一个结点)
  3.然后修改指针,删除s所指的结点
  4.最后释放s所指结点的空间

List Delete(int i,List Ptrl)
{
  List p,s;
  if(i==1)//若要删除的是表的第一个结点
  {
     s=Ptrl;//s指向第一个结点
     if(Ptrl!=Null) Ptrl=Ptrl->Next;//从列表中删除
     else return Null;
     free(s);//释放被删除的结点
     return Ptrl;
 }

 p=FindKth(i-1,Ptrl);

 if(p==NULL)
  {
     printf("这个结点不存在"); return NULL;
  }
 else if(p->Next==Null)
 {
  printf("第%d个结点不存在",i); return Null;}
 else {

  s=p->Next;//s指向第i个结点
  p->Next=s->Next;//从链表中删除

  free(s);
  return NULL;
}
 

(二)广义表 

(1)广义表是线性表的推广

  (2)   对于线性表而言,n个元素的都是最基本的单元素。广义表中这些元素不仅可以是单元素,也可以是另一个广义表。

(3)广义表中可能会遇到指针域或者数据域union可以把不同类型的数据组合在一起

typedef struct GNode *GList

struct GNode{
  int Tag;//标志域:0表示结点是单元素,1表示结点是广义表
  union{
     int Data;//子表指针域sublist与单元素数据域Date复用,即共用存储空间
     GList SubLidst;
     }URegion;

    GList Next;//指向后继节点
};
TagData/SubListNext

(三)多重链表

多重链表:链表中的结点可能同时隶属于多个链

>多重链表的指针域会有多个

>但是包含俩个指针域的链表不一定是多重链表,比如在双向链表不是多重链表 

>如果你的链表带有换头的操作可以带有返回值,否则就不用。

(四)实例分析

矩阵的表示:

分析:

1.用二维数组表示明显有俩个缺陷:

>一是数组的大小需要事先确定

>对于“稀疏矩阵”(0很多),但造成大量的空间浪费

2.采用一种典型的多重链表———十字链表来存储稀疏矩阵

(每个结点既属于每一行也属于每一列)

 >只存储矩阵非0元素:结点的数据域:行坐标Row,列坐标Col,数值Vaule

 >每个结点通过俩个指针域,把同行,同列串起来

 行指针(向右指针)Right 

 列指针(向下指针)Down

>头结点的标识值为-1(head),矩阵非零元素结点的标识值为Term

>用一个Tag标识区分头结点和非0元素的结点.

多项式的表示

 a(n)=a0+a1*x+a2*x^2+a3*x^3+…+an*x^n;

 方法1: 顺序存储结构直接表示

利用数组的连续存储空间顺序

数组各分量对应多项式各项

则俩个多项式相加即俩个数组的对应分量相加

下标i

0

1

2

3

4

5

a[i]

1

0

-3

0

0

4

typedef struct Lnode*List

Struct LNode{

    int Date[20];
    int last;
} 

Struct Lnode l;

Last ptrl;  

方法2:顺序存储结构表示非零项

每个非零项ai x^i涉及俩个信息 ai和x^i。可以把一个多项式看成一个(ai,i)二元组的集合

 用结构数组表示:数组分量分别由ai,指数i组成的结构,对应一个非零项。

 G(x)=9x^12+15x^8+3x^2

下标i

0

1

2

系数ai

3

15

9

指数i

2

8

12

相加过程:从头开始,比较俩个多项式当前对应项的指数

P1

(9,12)

(15,8)

(3,2)

P2

(26,19)

(-4,8)

(-13,6)

(82,0)

P3

(26,19)

(9,12)

(11,8)

(-13,6)

(3,2)

(82,0)

方法3:链表结构存储非零项

链表中的每个结点存储多项式中的一个非零项,包括系数和指数俩个数据域以及一个指针域

Coef

Expon

link

采用不带头结点的单向链表,按照指数递减的顺序排列各项

struct PolyNode{
 int coef;//系数
 int expon;//指数
 struct PolyNode*link;//指向下一个结点的指针
};

typedef stuct PolyNode* Poly;
Poly p1,p2;

思路:

俩个指针p1和p2分别指向俩个多项式的第一个结点,不断循环。

>1:p1->expon==p2->expon系数相加,若结果不为0就作为多项式对应项的系数。同时,p1和p2指向下一项。

>2:p1->expon>p2->expon:将p1当前的项存入多项式中,并且使p1指向下一项

>3.p1->expon<p2->expon:将p2当前的项存入多项式中,并且使p2指向下一项

当某一个多项式处理完之后,再将另一个多项式的所有结点复制到结果多项式中。

void Attach(int c,int e,Poly* rear)//指针的指针
{
 poly p;
 p=(polyno)malloc(sizeof(struct polyno));

 //对新结点复制
 p->coef=c;
 p->expon=e;
 p->link=NULL;

 (*rear)->link=p;
 *rear=p;//更改rear的值
}
 

 

 

poly add(poly p1,poly p2)
{
  poly front,rear,temp;
  //front和rear分别指向结果多项式的头和尾
  int sum;
  
  rear=(poly)malloc(sizeof(struct polynode));
  front=rear;
  //构造一个临时空结点,作为多项式的表头

  while(p1&&p2)
 {
   swithch(Compare(p1->expan,p2->expan))
   case 1:
     Attach(p1->coef,p1->expon,&rear);
     //拷贝函数,把p1的相关信息接到rear的后面
     p1=p1->link;//p1后移
     break;
 
  case -1:
     Attach(p2->coef,p2->expon,&rear);
     //拷贝函数,把p2的相关信息接到rear的后面
     p2=p2->link;//p2后移
     break;

 case 0:
   sum=p1->coef+p2->coef;
   if(sum) Attach(sum,p1->expan,&rear);
   p1=p1->next;
   p2=p2->next;
   break;
 }

//将未处理完的另外一个多项式的所有结点依次复制到多项式中
for(;p1;p1=p1->link) Attach(p1->rear,p1->expan,&rear);
for(;p2;p2=p2->link) Attach(p2->rear,p2->expan,&rear);

rear->link=NULL;
temp=front;//释放临时结点
front=front->next;//让front指向多项式的第一个非零项
free(temp);
return front;
}

多项式的加法和乘法

(1)多项式的表示:只表示非零项(动态数组)

数组链表
编程简单,容易调试动态性强
需要事先确定数组大小编程复杂,调试困难
typedef struct PolyNode*Polynomial;

struct PolyNode {
   int ceof;
   int expon;
   Polynomial link;
}
int main()
{
  Polynomial P1,P2,PP,PS;

 P1=read();
 P2=read();
 PP=Mult(P1,P2);
 print(PP);
 
 Ps=Add(P1,P2);
 print(PS);
}

 

//读入多项式
Polynomial ReadPoly()
{
  int n,c,e;
  Polynomial P,Rear,t;
  scanf("%d",&n);
 //n表示有几位多项式

 P=(Polynomial)malloc(sizeof(struct PolyNode));//链表空结点
 P->link=NULL;
 Rear=P;

  while(n--)
  {
     scanf("%d%d",&c,&e);
     Attach(c,e,&rear);
     //rear是结果表达式的尾指针
     //并且要使得rear的值向后移动,在插入的结点后面
  }
  t=P;P=P->link;free(t);
  return P;
}

rear的初值: 让rear指向一个空结点,以后的值全插在rear后面.

多项式相乘:逐项插入(把P1的当前项乘P2的当前项,加入到结果多项式中)

Polynomial mult(Polynomial p1,Polynomial p2)
{
	Polynomial p,rear,t1,t2,temp;//t1,t2用来暂时替代p1,p2; 
	int c,e;
	if(!p1||!p2) return NULL;//p1或者p2为空时,返回空 

	t1 = p1; 
	t2 = p2;
	p = malloc(sizeof(struct PolyNode));
	p->link = NULL;
	rear = p;

	while(t2)        //先用p1的第一项乘以p2,得到p; 
	{
		attach(t1->coef*t2->coef,t1->expon+t2->expon,&rear);
		t2 = t2->link;	
	}

	t1 = t1->link;

	while(t1)
	{
		t2 = p2;    //初始化,保证每次从t2头部开始 
		rear = p;

		while(t2)
		{
			e = t1->expon+t2->expon;
			c = t1->coef*t2->coef;
			while(rear->link&&rear->link->expon > e)
           //查找第一个小于或者等于当前指数的非空节点 
			          rear = rear->link;
			if(rear->link&&rear->link->expon == e) 

           //如果相等,非0时,系数相加,为0时,删除掉该节点 
			{
				if(rear->link->coef+c) 
				       rear->link->coef += c;
				else
				{
					t = rear->link;
					rear->link = t->link;
					free(t);
				}
			}

			else//如果小于,建立新节点,并且存储数据 。  
			{
				t = (Polynomial)malloc(sizeof(struct PolyNode));
				t->coef = c;
				t->expon = e;
				t->link = rear->link;
				rear->link = t;
				rear = rear->link;
			}
			t2 = t2->link;
		}
		t1=t1->link;
	}
	t2 = p;
	p=p->link;
	free(t2);
	return p;
}

void Printpoly(Polynomial p)
{
	int flag = 0;
	if(!p) {
		printf("0 0\n");
		return ;
	}//p为空
 
	while(p)
	{
		if(!flag) flag = 1;
		else printf(" ");//flag用来控制第一个数的空格,控制格式; 
		printf("%d %d",p->coef,p->expon);
		p=p->link;
	}
	printf("\n");

}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

太一TT

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

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

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

打赏作者

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

抵扣说明:

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

余额充值