线性表Linear List

引子:多项式表示

一元多项式及其运算:

主要运算:多项式相加、相减、相乘等

分析:如何表示多项式?

(1)顺序存储结构直接表示

数组各分量对应多项式各项,a[i]:项的系数

两个多项式相加:两个数组对应分量相加

问题:如何表示多项式

空间利用率较低

(2)顺序存储结构表示非零项

每个非零项涉及两个信息:系数和指数i

可以将一个多项式看成是一个二元组的集合。

用结构数组表示:数组分量是由系数、指数i组成的结构,对应一个非零项

例如:

为了方便相加可以按指数大小存储

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

(3)链表结构存储非零项

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

coefexponlink
typedef struct PolyNode *Polynomial;

struct PolyNode
{
    int coef;
    int expon;
    Polynomial link;
};

线性表定义

多项式表示问题的启示:

1.同一个问题可以有不同的表示(存储)方法

2.有一类共性问题:有序线性序列的组织和管理

线性表:0个或多个同类型数据元素的有限序列

班级同学的花名册是线性表,因为这是有限序列。在较复杂的线性表中,一个数据元素可以由若干个数据项组成。

线性表的抽象数据类型定义

线性表的抽象数据类型定义如下:

ADT 线性表(List)
Data
	线性表的数据对象集合为{a1,a2,...,an},每个元素的类型均为DataType。其中,除第一个元素a1外,每一个元素有且只有一个直接前驱元素,除了最后一个元素an,每一个元素有且只有一个直接后继元素。数据元素之间的关系是一对一的关系。
Operation
	InitList(*L):初始化操作,建立一个空的线性表L。
	ListEmpty(L):若线性表为空,返回true,否则返回false。
	ClearList(*L):将线性表清空。
	GetElem(L,i,*e):将线性表L中的第i个未知元素值返回给e。
	LocateElem(L,e):在线性表L中查找与给定值e相等的元素,如果查找成功,返回该元素在表中序号表示成功;否则,返回0表示失败。
	ListInsert(*L,i,e):在线性表L中的第i个位置插入新元素e。
	ListDelete(*L,i,*e):删除线性表L中第i个位置元素,并用e返回其值。
	ListLength(L):返回线性表L的元素个数。
endADT

线性表的顺序存储实现

利用数组的连续存储空间顺序存放线性表的各元素

typedef struct LNode* List;

struct LNode
{
    ElementType Data[MAXSIZE];
    int Last;//记录最后一位有效位
};
struct LNode L;
List PtrL;

访问下标为i的元素:L.Data[i]PtrL->Data[i]

线性表的长度:L.Last+1PtrL->Last+1

陈越姥姥《数据结构》课程实现版本:

#define MAXSIZE 20 /*存储空间初始分配量*/

typedef struct LNode* List;

struct LNode
{
    ElementType Data[MAXSIZE];
    int Last;//记录最后一位有效位
};
struct LNode L;
List PtrL;

List MakeEmpty()
{
    List PtrL;
    PtrL=(List)malloc(sizeof(struct LNode));
    PtrL->Last=-1;
    return PtrL;
}
int Find(ElementType X,List PtrL)
{
    int i=0;
    while(i<=PtrL->Last&&PtrL->Data[i]!=X)
        i++;
    if(i>PtrL->Last) return -1;//如果没找到,返回-1
    else return i;//找到后返回的是存储位置(下标)
}

/*插入(第i(1<=i<=n+1)个位置上插入一个值为X的新元素*/

void Insert(ElementType X,int i,List PtrL)       /*i是序数,为下标+1,不是下标*/
{
    int j;
    if(PtrL->Last==MAXSIZE-1) /*表空间已满,不能插入*/
    {
        printf("表满");
        return;
    }
    if(i<1||i>PtrL->Last+2)    /*检查插入位置的合法性*/
    {
        printf("位置不合法");
        return;
    }
    for(j=PtrL->Last;j>=i-1;j--)
        PtrL->Data[j+1]=PtrL->Data[j];    /*将ai~an倒序向后移动*/
    PtrL->Data[i-1]=X;       /*新元素插入*/
    PtrL->Last++;        /*Last仍指向最后一个元素*/
    return;
}

/*删除:删除表的第i(1<=i<=n)个位置上的元素*/

void Delete(int i,List PtrL)
{
    int j;
    if(i<1||i>PtrL->Last+1)    /*检查是否为空表及删除位置的合法性*/
    {
        printf("不存在第%d个元素",i);
        return;
    }
    for(j=i;j<=PtrL->Last;j++)
        PtrL->Data[j-1]=PtrL->Data[j];     /*将a(i+1)~an顺序向前移动*/
    PtrL->Last--;         /*Last仍指向最后一个元素*/
    return;
}

《大话数据结构》给出的顺序存储的链表实例:

#include <bits/stdc++.h>
using namespace std;

#define MAXSIZE 100
#define OK 1
#define ERROR 0
#define OVERFLOW -2             //这样就为 `OVERFLOW` 定义了一个值为 `-2` 的常量。

typedef int ElemType;
typedef int Status;

typedef struct
{
    ElemType *elem;
    int length;
}SqList;

//线性表的初始化
Status InitList(SqList &L)
{
    L.elem=new ElemType[MAXSIZE];
    if(!L.elem)
        exit(OVERFLOW);
    L.length=0;
    return OK;
}
//线性表的取值
Status GetElem(SqList L,int i,ElemType &e)
{
    if(i<1||i>L.length)
        return ERROR;
    e=L.elem[i-1];
    return OK;
}
//查找元素
int LocateElem(SqList L,ElemType e)
{
    for(int i=0;i<L.length;i++)
    {
        if(L.elem[i]==e)
            return i+1;
    }
    return 0;
}
//线性表插入元素
Status ListInsert(SqList &L,int i,ElemType e)
{
    if(i<1||i>L.length+1)
        return ERROR;
    if(L.length==MAXSIZE)
        return ERROR;
    for(int j=L.length-1;j>=i-1;j--)
    {
        L.elem[j+1]=L.elem[j];
    }
    L.elem[i-1]=e;
    L.length++;
    return OK;
}
//线性表删除元素
Status ListDelete(SqList &L,int i)
{
    if(i<1||i>L.length)
        return ERROR;
    for(int j=i;j<=L.length-1;j++)
        L.elem[j-1]=L.elem[j];
    L.length--;
    return OK;
}
//打印线性表
Status Display(SqList &L)
{
    for(int i=0;i<L.length;i++)
        printf("%d ",L.elem[i]);
    printf("\n");
    return 0;
}

int main()
{
    SqList L;
    int v,k,opt;
    InitList(L);
    printf("1:在线性表中存入5个值\n");
    printf("2:查找线性表中的元素\n");
    printf("3:向线性表中插入一个元素\n");
    printf("4:从线性表中删除一个元素\n");
    printf("5:退出\n");
    while(1)
    {
        printf("输入你的选择:");
        cin>>opt;
        if(opt==1)
        {
            printf("请输入要插入的5个值:");
            for(int i=1;i<=5;i++)
            {
                cin>>v;
                ListInsert(L,i,v);
            }
            printf("当前线性表为:");
            Display(L);
        }
        else if(opt==2)
        {
            printf("请输入要查找的元素:");
            cin>>v;
            k=LocateElem(L,v);
            printf("要查找的元素的所在的位置为:%d\n",k);
        }
        else if(opt==3)
        {
            printf("请输入要插入的元素及插入的位置:");
            cin>>v>>k;
            ListInsert(L,k,v);
            printf("插入元素后的线性表为:");
            Display(L);
        }
        else if(opt==4)
        {
            printf("输入要删除的元素的序数:");
            cin>>v;
            ListDelete(L,v);
            printf("删除后的线性表为:");
            Display(L);
        }
        else if(opt==5)
        {
            printf("退出成功!");
            break;
        }
    }
    return 0;

}
1:在线性表中存入5个值
2:查找线性表中的元素
3:向线性表中插入一个元素
4:从线性表中删除一个元素
5:退出
输入你的选择:1
请输入要插入的5个值:9 4 2 1 0
当前线性表为:9 4 2 1 0
输入你的选择:2
请输入要查找的元素:4
要查找的元素的所在的位置为:2
输入你的选择:3
请输入要插入的元素及插入的位置:5 3
插入元素后的线性表为:9 4 5 2 1 0
输入你的选择:4
输入要删除的元素的序数:6
删除后的线性表为:9 4 5 2 1
输入你的选择:5
退出成功!

线性表的链式存储实现

不要求逻辑上相邻的两个元素物理上也相邻;通过”链“建立起数据元素之间的逻辑关系。

插入、删除元素不需要移动数据元素,只需要修改”链“。

如果只知道链表头,该怎么访问序号为i的元素?以及怎么求线性表的长度?

建立结构体和结构体指针:

typedef struct LNode *List;

struct LNode
{
    ElementType Data;
    List Next;
};
struct LNode L;
List PtrL;

展示链表结构:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct  list_node
{
	int data ;
	struct list_node *next ;
};

typedef struct list_node list_single ;
list_single *create_list_node(int data)
{
	list_single *node = NULL ;
	node = (list_single *)malloc(sizeof(list_single));
	if(node == NULL){
		printf("malloc fair!\n");
	}
	memset(node,0,sizeof(list_single));
	node->data = data;
	node->next = NULL ;
	return node ;
}
int main(void)
{
	int data = 100 ;
	list_single *node_ptr = create_list_node(data); //创建一个节点
	printf("node_ptr->data=%d\n",node_ptr->data);   //打印节点里的数据
	printf("node_ptr->next=%d\n",node_ptr->next);
	free(node_ptr);
	return 0 ;
}
node_ptr->data=100
node_ptr->next=0

1.求链表的长度

int Length(List Ptrl)
{
    List p=Ptrl;     //p指向表的第一个结点
    int j=0;
    while(p)
    {
        p=p->Next;     
        j++;        //当前p指向的是第j个结点
    }
    return j;
}

2.查找(1)按序号(不是下标)查找:FindKth

List FindKth(int K,List PtrL)
{
    List p=PtrL;
    int i=1;
    while(p!=NULL&&i<K)
    {
        p=p->Next;
        i++;
    }
    if(i==K)
        return p;     //找到第K个,返回指针
    else
        return NULL;  //否则返回空
}

(2)按值查找

List Find(ElementType X,List PtrL)
{
    List p=PtrL;
    while(p!=NULL && p->Data!=X)
    {
        p=p->Next;
    }
    return p;
}

3.插入(在个结点后插入一个值为X的新结点

这个结点是第i个结点。

步骤(1)先构造一个新结点,用s指向;(2)再找到链表的第i-1个结点,用p指向;(3)然后修改指针,插入结点(p之后插入的新结点是s)。

链表要先断再接,顺序不能更改!!

链表要先断再接,顺序不能更改!!

链表要先断再接,顺序不能更改!!

List Insert(ElementType X,int i,List PtrL) 
{
    List p,s;
    if(i==1)           //新节点插在表头
    {
        s=(List)malloc(sizeof(struct LNode));     //申请、填装结点
        s->Data=X;
        s->Next=Ptrl;
        return s;           //返回新表头指针
    }
    p=FindKth(i-1;PtrL);       //查找第i-1个结点
    if(p==NULL)        //第i-1个不存在,不能插入
    {
        printf("参数i错");
        return NULL;
    }
    else
    {
        s=(List)malloc(sizeof(struct LNode));      //申请、填装结点
        s.Data=X;
        s->Next=p->Next;         //新节点插在第i-1个结点的后面
        p->Next=s;
        return PtrL;
    }
}

4.删除(删除链表的个位置上的的结点

步骤:(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);       //查找第i-1个结点
    if(p==NULL)        
    {
        printf("第%d个结点不存在",i-1);
        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 PtrL;
    }
}

陈越姥姥《数据结构》课程给出的完整代码如下:

typedef struct LNode *List;

struct LNode
{
    ElementType Data;
    List Next;
};
struct LNode L;
List PtrL;

int Length(List Ptrl)
{
    List p=Ptrl;     //p指向表的第一个结点
    int j=0;
    while(p)
    {
        p=p->Next;     
        j++;        //当前p指向的是第j个结点
    }
    return j;
}

List FindKth(int K,List PtrL)
{
    List p=PtrL;
    int i=1;
    while(p!=NULL&&i<K)
    {
        p=p->Next;
        i++;
    }
    if(i==K)
        return p;     //找到第K个,返回指针
    else
        return NULL;  //否则返回空
}

List Find(ElementType X,List PtrL)
{
    List p=PtrL;
    while(p!=NULL && p->Data!=X)
    {
        p=p->Next;
    }
    return p;
}

List Insert(ElementType X,int i,List PtrL) 
{
    List p,s;
    if(i==1)           //新节点插在表头
    {
        s=(List)malloc(sizeof(struct LNode));     //申请、填装结点
        s->Data=X;
        s->Next=Ptrl;
        return s;           //返回新表头指针
    }
    p=FindKth(i-1;PtrL);       //查找第i-1个结点
    if(p==NULL)        //第i-1个不存在,不能插入
    {
        printf("参数i错");
        return NULL;
    }
    else
    {
        s=(List)malloc(sizeof(struct LNode));      //申请、填装结点
        s.Data=X;
        s->Next=p->Next;         //新节点插在第i-1个结点的后面
        p->Next=s;
        return PtrL;
    }
}

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);       //查找第i-1个结点
    if(p==NULL)        
    {
        printf("第%d个结点不存在",i-1);
        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 PtrL;
    }
}

《大话数据结构》中链式存储的链表实例:

#include <stdio.h>
#include <stdlib.h>

#define ERROR 0
#define OK 1
typedef int Status;
typedef int ElementType;

typedef struct LNode *List;

struct LNode
{
    ElementType Data;
    List Next;
};
struct LNode L;
List PtrL;

//表的创建(头插法)

void CreateListHead(List *L,int m[],int n)
{
    List p;
    int i;
    *L=(List)malloc(sizeof(struct LNode));
    (*L)->Next=NULL;
    for(i=0;i<n;i++)
    {
        p=(List)malloc(sizeof(struct LNode));
        p->Data=m[i];
        p->Next=(*L)->Next;
        (*L)->Next=p;
    }
}

//表的创建(尾插法)
void CreateListTail(List *L,int m[],int n)
{
    List p,r;
    int i;
    *L=(List)malloc(sizeof(struct LNode));
    r=*L;
    for(i=0;i<n;i++)
    {
        p=(List)malloc(sizeof(struct LNode));
        p->Data=m[i];
        r->Next=p;
        r=p;
    }
    r->Next=NULL;
}

Status GetElem(List L,int i,ElementType *e)
{
    int j;
    List p;
    p=L->Next;
    j=1;
    while(p&&j<i)
    {
        p=p->Next;
        j++;
    }
    if(!p||j>i)
        return ERROR;
    *e=p->Data;
    return OK;
}
Status ListInsert(List *L,int i,ElementType e)
{
    int j;
    List p,s;
    p=*L;
    j=1;
    while(p&&j<i)
    {
        p=p->Next;
        j++;
    }
    if(!p||j>i)
        return ERROR;
    s=(List)malloc(sizeof(struct LNode));
    s->Data=e;
    s->Next=p->Next;
    p->Next=s;
    return OK;
}

Status ListDelete(List *L,int i)
{
    int j;
    List p,q;
    p=*L;
    j=1;
    while(p->Next&&j<i)
    {
        p=p->Next;
        j++;
    }
    if(!(p->Next)||j>i)
        return ERROR;

    q=p->Next;
    p->Next=q->Next;
    free(q);
    return OK;
}
Status Output(List L)
{
    List p;
    p=L->Next;
    while(p)
    {
        printf("%d",p->Data);
        p=p->Next;
    }
    printf("\n");
}

int main()
{
    List L;
    int i,j,k,n,e,m[100];
    printf("请输入要存储元素的总个数:");
    scanf("%d",&n);
    printf("请输入各个元素的值:");
    for(i=0;i<n;i++)
        scanf("%d",&m[i]);

    CreateListHead(&L,m,n);
    printf("此时链表的元素如下所示:\n");
    Output(L);
    printf("请输入要获取的第j个元素并返回到e值中(输入j的值):");
    scanf("%d",&j);
    GetElem(L,j,&e);
    printf("此时e的值为第j个元素值:%d\n",e);
    printf("请输入在第k个元素前插入一个元素e1:");
    int e1;
    scanf("%d%d",&k,&e1);
    ListInsert(&L,k,e1);
    printf("此时链表的个元素如下:\n");
    Output(L);
    printf("请输入要删除元素的序号:");
    int l;
    scanf("%d",&l);
    ListDelete(&L,l);
    printf("此时链表的各元素如下:\n");
    Output(L);
    return 0;

}
请输入要存储元素的总个数:5
请输入各个元素的值:1 2 3 4 5
此时链表的元素如下所示:
54321
请输入要获取的第j个元素并返回到e值中(输入j的值):2
此时e的值为第j个元素值:4
请输入在第k个元素前插入一个元素e1:1 2
此时链表的个元素如下:
254321
请输入要删除元素的序号:2
此时链表的各元素如下:
24321

广义表和多重链表

我们知道了一元多项式的表示,那么二元多项式又该如何表示呢?比如给定二元多项式:

广义表(Generalized List)

  • 广义表是线性表的推广;
  • 对于线性表而言,n个元素都是基本的单元素;
  • 广义表中,这些元素不仅可以是单元素也可以是另一个广义表。
typedef struct GNode *Glist;
struct GNode
{
    int Tag;    //标志域:0表示结点是单元素,1表示结点是广义表
    union
    {
        ElementType Data;    //子表指针域SubList与单元素数据域Data复用,即共用存储空间
        GList SubList;
    }URegion;
    GList Next;      //指向后继结点
};

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

  • 多重链表中结点的指针域会有多个,如前面例子包含了Next和SubList两个指针域
  • 但包含两个指针域的链表并不一定是多重链表,比如双向链表不是多重链表

多重链表有广泛的用途:基本上如树、图这样相对复杂的数据结构都可以采用多重链表的方式存储。