数据结构--线性表

1.逻辑结构

线性表是n个类型相同的数据元素的有限序列,除第一个元素外,每个数据元素只有一个直接前驱和直接后继,数据元素之间具有一对一的关系,一个线性表中的数据类型必须属于同一数据对象

2.顺序存储

用一组地址连续的存储单元依次存储线性表中的各个元素

2.1地址计算

Loc(a​​​i)=Loc(a1)+(i-1)*k(每个元素占k个单元,Loc(a1)为首元素地址)

2.2顺序存储的表示

2.2.1代码
#define MAXSIZE  100  //宏定义表示线性表的最大长度
typedef struct
{
ElemType elem[MAXSIZE];   //线性表占用的数组空间
int last;          //记录线性表中最后一个元素在数组中的位置,空表置为-1
}SeqList;

ElemType是为了描述统一而自定的,可以根据需要为int,char,float,或者struct类型

通过变量定义语句SeqList L;将L定义为SeqList类型的变量,可通过L.elem[i-1]访问顺序表中序号为i的元素

通过指针变量定义语句SeqList L1,*L; L=&L1;将L定义为指向SepList类型的指针变量,使用时可以通过L->elem[i-1]访问顺序表中序号为i的元素

2.2.2基本运算
1.查找操作

查找顺序表中与给定值e相等的数据元素,并返回其序号

int Locate(SeqList L,ElemType e)
{int i=0;
while(i<=L.last&&L.elem[i]!=e)  //顺序扫描表,直到找到或者到了表尾依旧没找到
      i++;
if(i<=last)
    return i+1;
else
    return -1    //表明查找失败
}
2.插入操作

在表的第i个元素前插入一个新的元素

算法思想:先判断插入位置是否合理,在判断表是否满了,允不允许插入,最后找到位置插入并且将表尾向后移一位

#define OK 1
#define ERROR 0
int InsList(Seqlist *L,int i,ElemType e)
{
int k;
if(i<1||i>L->last+2){
      printf("插入位置不合法")
      return ERROR;
}
if(L->last>=MAXSIZE-1){
    printf("表已满,无法插入");
    return ERROR;
}
for(k=L->last;k>=i-1;k--)
    L->elem[k+1]=L->elem[k];
L->elem[i-1]=e;
L->last++;
return OK;
}
3.删除操作

将表中的第i个元素删除,并将删除的元素储存起来

int DelList(SeqList *L,ElemType *e)
{
int k;
if(i<1||i>L->last+1){
      printf("删除位置不合理");
      return ERROR;
}
*e=L->elem[i-1];
for(k=i;k<=l->last;k++)
     L->elem[k-1]=L->elem[k];
L->last--;
return OK;
}
4.合并操作

有两个顺序表LA和LB,其元素均为非递减有序排列。编写算法,将他们合并成一个顺序表LC,要求LC也是非递减有序排列。

void mergeList(SeqList *LA,SeqList *LB,SeqList *LC)
{
int i,j,k,l;
i=0;j=0;k=0;
while(i<=LA->last&&j<=LB->last){
      if(LA->elem[i]<=LB->elem[j]){
          LC->elem[k]=LA->elem[i];
          i++;k++;
         }
      else{
          LC->elem[k]=LB->elem[j];
          j++;k++;
          }
while(i<=LA->last)                //看是否有表空,直接接到C表后面去
{
  LC->elem[k]=LA->elem[i];
  i++;k++
}
while(j<=LB->last)
{
  LC->elem[k]=LB->elem[j];
  j++;k++
}
LC->last=LA->last+LB->last+1;
}

3.链式存储

3.1单链表

用一组任意的存储单元来存放线性表的结点,可以是连续的也可以是不连续的。链表的结点包括两个域:数据域用来存储节点的值,指针域用来储存数据元素的直接后继的地址,每个结点只有一个next指针域的链表称为单链表

为了操作的统一方便,可以在单链表的第一个结点之前附设一个头节点。头节点的数据域可以储存一些关于线性表的长度等附加信息,也可以不储存任何信息。

typedef struct Node
{
ElemType data;
struct Node* next;
}Node,*LinkList;

LinkList与Node*同为结构指针类型,这两种类型是等价的。通常习惯上用LinkList定义指向某个单链表的头指针变量,例如定义LinkList L,则L为指向单链表的头指针变量;用Node*定义指向单链表中结点的指针,例如Node* p,则p为指向单链表中结点的指针变量。

3.1.1单链表的基本运算
1.初始化单链表
InitList(LinkList *L)
{
*L=(LinkList)malloc(sizeof(Node));
(*L)->next=NULL;
}
2.头插法建立单链表
void CreatFromHead(LinkList L)
{Node *s;
char c;
int flag=1;    //flag初值为1,当输入'$'时flag为0,建表结束
while(flag){
    c=getchar();
    if(c!='$')
       {
           s=(Node *)malloc(sizeof(Node));   //建立新结点
           s->data=c;
           s->next=L->next;    //将s结点插入表头
           L->next=s;
       }
     else flag=0;
   }
}

采用头插法得到的单链表输入顺序与逻辑顺序相反

3.尾插法建立单链表

添加一个尾指针,使其指向表尾,此时输入顺序与逻辑顺序相同

void CreatFromTail(LinkList L)
{
    Node *r,*s;
    int flag=1;
    r=L;
    char c;
    while(flag){
        c=getchar();
        if(c!='$'){
            s=(Node*)malloc(sizeof(Node));
            s->data=c;
            r->next=s;
            r=s;
        }
        else{
            flag=0;r->next=NULL;   //将最后一个结点的next域置为空,表示结束建表
        }
    }
}

4.单链表插入操作

在线性表的第i个结点位置之前插入一个新元素e

5.单链表的删除操作

步骤与插入操作大同小异

6.合并两个有序的单链表

有两个单链表LA,LB,其元素均为非递减有序排列,编写一个算法,将他们合并成一个单链表LC,且LC也是非递减有序排列

LinkList MergeLinkList(LinkList LA,LinkList LB)
{
    Node *pa,*pb,*r;
    LinkList LC;
    pa=LA->next;pb=LB->next;
    LC=LA;
    LC->next=NULL;r=LC;
    while(pa!=NULL&&pb!=NULL){
        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;    //表A没完就接到c后面
    if(pb)  r->next=pb;
    return LC;
}

3.2循环链表

循环链表是一个首尾相接的链表,与单链表在判别p结点是否为尾结点时条件不同,单链表中判别条件为p!=NULL或p->next!=NULL,而单循环链表的判别条件则是p!=L或p->next!=L

3.2.1循环链表的基本运算
1.初始化循环链表
InitCLinkList (LinkList *CL){
    *CL=(LinkList)malloc(sizeof(Node));
    (*CL)->next=*CL;
}
2.建立循环链表

本质上与建立单链表无异,只是在最后一步要让最后一个结点的next域指向头结点

3.合并两个循环链表

分为两种情况,如果给的是表头指针,则要遍历链表到表尾,再将其中一个链表的表尾指针连接到表头,并将另一个链表释放掉,如果给的是表尾指针则不需要遍历操作

3.3双向链表

结构定义如下

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

优点是可以从表中快速找到一个结点的直接前驱,与单链表类似,它也可以存在双向循环链表

3.3.1双向链表的插入操作

int DLinkList(DoubleList L,int i,ElemType e){
    DNode *s,*p;
    s=(DNode *)malloc(sizeof(DNode));
    /*先检查插入位置是否合理,此处代码省略,如果合理,则找到第i个结点,并让p指向它*/
    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;  //TRUE和FALSE是宏定义了的
    }
    else return FALSE;
}
3.2.2双向链表的删除操作

1.p->prior->next=p->next    2.p->next->prior=p->prior   free(p)

  • 28
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值