数据结构(学习笔记)(线性表)

说明:

学习内容根据b站王卓老师视频学习记录
参考地址第02周08--2.4线性表的顺序表示和实现3_哔哩哔哩_bilibili


目录

1. 线性表

顺序表

 链表

线性表的应用


1. 线性表


顺序表


在写代码之前可以做一些简单的定义(方便解读)

#define LIST_MAX 100            //多项式可能达到的最大长度
#define OVERFLOW -2
#define OK 1
#define ERROR 0

typedef int Status;
typedef char ElemType;

 存储结构:

1.静态定义:

//确定存储空间(静态分布)
typedef struct{
    int elem[LIST_MAX];               //存储存储地址
    int length;                //线性表的当前长度
}SqList;                //顺序表类型

2.动态定义

//指针的形式(动态分布)
typedef struct{
    int *elem;              //指向存储地址
    int length;                //顺序表的当前长度
}SqList;                //顺序表类型

动态定义因为没有直接定义线性表的存储空间,所以在使用时(初始化时要动态分配存储空间)引用如下形式的代码:

SqList L;
L.data = (ElemType *)malloc(sizeof(ElemType)*LIST_MAX); 

 补充:
        多项式非零项的定义(动态分布):

//多项式非零项的定义
typedef struct{
    float p;            //系数
    int e;              //指数
}Polynomial;

typedef struct{
    Polynomial *elem;           //存储空间的基地址
    int length;                 //当前项的个数
}SqList;                //多项式的顺序存储结构类型为SqList

 初始化:(以动态分布为例子)

//线性表的初始化(参数引用引用)
Status InitList(SqList &L){               //构建一个空的顺序表
    L.elem = new int[LIST_MAX];
    if (!L.elem)
        return OVERFLOW;
    L.length = 0;
    return OK;
}

顺序表的基本简单操作:
        (销毁、清空、求长度、判断是否为空)的相关操作

//销毁线性表L
void DestoryList(SqList &L){
    if (L.elem)
        delete L.elem;              //释放存储空间
}

//清空线性表
void ClearList(SqList &L){
    L.length = 0;               //将线性表的长度置为0
}

//求线性表的长度
int GetLength(SqList &L){
    return (L.length);
}

//判断线性表L是否为空
int IsEmpty(SqList &L){
    if(L.length == 0){
        return 1;
    } else{
        return 0;
    }
}

   顺序表的基本操作:

    顺序表的取值:
        在顺序表L中获取i位置的数据,并用e来返回获取到的元素;

//线性表(顺序表)的取值(根据位置i获取相应位置数据元素的内容)
int GetElem(SqList L,int i,ElemType &e){
    if (i<1 || i>L.length)
        return ERROR;               //判断i位置是否合理,不合理返回ERROR
    e = L.elem[i-1];                //第i-1个位置存储着第i个数据
    return OK;
}

  顺序表的查找:
         查找顺序表L中数据和e相等的元素,如果查找成功则返回其序号,若没有查找 到则返回0;

//顺序表的查找
int LocateElem(SqList L,ElemType e){
    //在线性表L中查找值为e的数据元素,返回其序号(是第几个元素)
    for (int i = 0; i < L.length; ++i) {
        if (L.elem[i] == e)
            return i+1;                 //查找成功,返回序号
        return 0;               //查找失败,返回0
    }
}

顺序表的插入:
        在顺序表L中第i位置处插入数据e;

//顺序表的插入
Status ListInsert(SqList &L,int i,ElemType e){
    if(i<1 || i>L.length+1)
        return ERROR;               //i值不合法
    if (L.length == LIST_MAX)
        return ERROR;               //当前存储空间已经满了

    for (int j=L.length-1; j>=i-1; ++j) {
        L.elem[j+1] = L.elem[j];                //插入位置及之后的元素后移
    }

    L.length++;                //表长增加1
    return OK;
}

顺序表的删除:
        在顺序表L中删除第i位置处的元素;

//线性表的删除
Status ListDelete(SqList &L,int i){
    if (i<1 || (i>L.length)){
        return ERROR;               //i值不合法
    }

    for (int j=i; j<=L.length; ++j) {
        L.elem[j-1] = L.elem[j];                //被删除的元素之后的元素前移
    }

    L.length--;             //表长减1
    return OK;
}

 链表


单链表
       
理解概念:节点只有一个指针域的链表。

        无头节点时空表:头指针指向空;
        有头节点的空表:头节点的指针域为空。

        头节点的好处:在插入、删除等操作时不需要特殊处理。

        顺序表采用的是随机存取,链表采用的是顺序存取。

存储结构

//单链表的存储结构
typedef struct Lnode{
    ElemType data;              //结点的数据域
    struct Lnode *next;             //结点的指针域
}Lnode,*LinkList;               //LinkList为指向结构体Lnode的指针类型

        使用*LinkList的好处:在定义链表时可以直接定义LinkList L,等价与Lnode *L;
                                            同时在定义结点指针p时:可以直接写LinkList p,等价于Lnode *p;

初始化:
       
建立一个空表

//单链表的初始化
Status InitList(LinkList &L){
    L = new Lnode;              //c++语言
    //或L = (LinkList)malloc(sizeof(Lnode));         //c语言
    L->next = NULL;
    return OK;
}

 单链表的基本操作的实现:

         判断链表是否为空:
               
如果为空就返回1,不为空返回0。

//判断链表是否为空
int ListEmpty(LinkList L){
    if (L->next){               //非空
        return 0;
    } else{
        return 1;
    }
}

         链表的销毁:
             
  从头结点开始依次释放所有结点。

//单链表的销毁
Status DestroyList(LinkList &L){
    Lnode *p;               //或LinkList p;
    while (L){
        p = L;
        L = L->next;
        delete p;
    }
    return OK;
}

         清空单链表:
               
链表中除头指针和头节点其他清空。

//清空单链表
Status ClearList(LinkList &L){
    Lnode *p,*q;                //或LinkList p,q;
    p = L->next;

    while (p){              //没到表尾
        q = p->next;
        delete p;
        p = q;
    }
    L->next = NULL;             //头节点指针域为空
}

         求单链表的表长:
               
从首元节点开始,依次计数所有的结点。

//求单链表的表长
Status ListLength(LinkList L){
    Lnode *p;               //或LinkList p;
    int count = 0;              //记录长度
    p = L->next;

    while(p){               //遍历求单链表的长度
        count++;
        p = p->next;
    }
    return count;
}

         取值:
               
取单链表中第i位置的元素并用e返回。

//取值
Status GetElem(LinkList L,int i,ElemType &e){
    Lnode *p;
    p = L->next;                //初始化
    int j=1;

    while (p && j<i){               //向后扫描,直到p指向第i个元素或p为空
        p = p->next;
        j++;
    }

    if (!p || j>i){             //第i个元素不存在
        return ERROR;
    }

    e = p->data;                //取第i个元素
    return OK;
}

         查找:
               
1.根据指定的数据获取该数据所在的位置(地址)。

//查找
Lnode *LocateList(LinkList L,ElemType e){
    Lnode *p;
    p = L->next;

    while(p && p->data != e){
        p = p->next;
    }
    return p;
}

                2.根据指定数据获取该数据位置序号。

//查找(序号)
int LocateList(LinkList L,ElemType e){
    Lnode *p;
    p = L->next;
    int i = 1;

    while(p && p->data!=e){
        p = p->next;
        i++;
    }
    
    if (p){                 //在单链表中找到e
        return i;
    } else{                 //在单链表中没有找到(失败)
        return 0;
    }
}

         插入:
               
在第i个结点前插入值为e的结点。

Status ListInsert(LinkList &L,int i,ElemType e){
    Lnode *p;
    p = L;
    int j = 0;              //用来记录指针的位置

    while (p && j<j-1){             //循环寻找第i-1个结点,并使p指向i-1结点
        p = p->next;
        j++;
    }

    if (!p || j>i-1)                //i大于表长+1或者小于1,插入位置非法
        return ERROR;
    else{
        Lnode  *s = new Lnode;               //建立新节点
        s->data = e;
        s->next = p->next;
        p->next = s;
        return OK;
    }
}

         删除:
               
删除第i个结点。

//删除
Status DeleteList(LinkList &L,int i,ElemType &e){
    Lnode *p;
    p = L;                //p指向链表L的头结点
    int j = 0;

    while(p->next && j<i-1){                //寻找第i个结点,并使p指向其前驱
        p = p->next;
        j++;
    }

    if (!(p->next) || j>i-1)                //位置不合理
        return ERROR;
    else{
        Lnode *q = p->next;             //创建q指针指向p->next;方便表示p->next->next
        p->next = q->next;
        e = q->data;
        delete q;               //释放删除结点的空间
        return OK;
    }
}

         单链表的建立:
                        头插法:
元素插入到链表的头部

//链表的建立(头插法)
void CreateList(LinkList &L,int n){
    L = new Lnode;              //或L = (LinkList)malloc(sizeof(Lnode))
    L->next = NULL;             //先建立一个带头结点的单链表

    for (int i = n; i > 0; --i) {
        Lnode *p = new Lnode ;             //生成新结点p=(Lnode *)malloc(sizeof(Lnode((
        cin>>p->data;               //输入元素值 scanf(&p->data);(c语言)
        p->next = L->next;              //插入到表头
        L->next = p;
    }
}

                        尾插法: 元素插入在链表尾部。

//链表的建立(头插法)
void CreateList(LinkList &L,int n){
    L = new Lnode;              //或L = (LinkList)malloc(sizeof(Lnode))
    L->next = NULL;             //先建立一个带头结点的单链表

    for (int i = n; i > 0; --i) {
        Lnode *p = new Lnode ;             //生成新结点p=(Lnode *)malloc(sizeof(Lnode((
        cin>>p->data;               //输入元素值 scanf(&p->data);(c语言)
        p->next = L->next;              //插入到表头
        L->next = p;
    }
}

 循环链表:一种头尾相连的链表(最后一个结点的指针指域向头结点)

        判断链表为空:判断最后一个结点的指针域是否指向头结点。(p->next!=L)

        带尾指针循环链表的合并:

//循环链表的合并
//假设Ta、Tb都是非空的单循环链表
LinkList Connect(LinkList Ta,LinkList Tb){
    Lnode *p = Ta->next;                //p存表头结点
    Ta->next = Tb->next->next;              //Tb表头连接Ta表尾
    delete Tb->next;                 //释放Tb表头结点
    Tb->next = p;                   //修改指针
    return Tb;
}

双链表
        理解概念:节点有两个指针域的链表。

        存储结构:

//双向链表结构定义
typedef struct DuLNode{
    ElemType data;
    struct DuLNode *prior,*next;
}DuLNode,*DuLinkList;

         在双链表中:p->next->prior = p = p->prior->next;

        双向链表的插入:
                
在带头结点的双向循环链表L中第i位置之前插入元素e。

//双向链表的插入
//在带头结点的双向循环链表L中第i位置之前插入元素e
Status ListInsert(DuLinkList &L,int i,ElemType e){
    DuLNode *p;
    if (!(p=GetElem(L,i)))              //GetElem(L,i)得到双向链表的第i个位置元素的地址(伪代码)
        return ERROR;
    DuLNode *s = new DuLNode;
    s->data = e;
    s->prior = p->prior;
    p->prior->next = s;
    s->next = p;
    p->prior = s;
    return OK;
}

        双向链表的删除:
                
删除带头结点的双向链表L的第i个元素,并用e返回。

//双向链表的删除
//删除带头结点的双向链表L的第i个元素,并用e返回。
Status ListDelete(DuLinkList &L,int i,ElemType &e){
    DuLNode *p;
    if(!(p = GetElem(L,i)))             //GetElem(L,i)得到双向链表的第i个位置元素的地址(伪代码)
        return ERROR;

    e = p->data;
    p->prior->next = p->next;
    p->next->prior = p->prior;
    delete(p);
    return OK;
}

线性表的应用


 线性表的合并:(无序)

步骤:

  1. 分别获取LA表以及LB表的长度n,m。

  2. 从LB中第一个数据开始,循环n次执行以下操作:

    • 从LB中查找第i个数据元素赋值给e;

    • 在LA中查找元素e,如果不存在,将e插在表LA后。

//线性表的合并
void Union(SqList &La,SqList &Lb){
    int La_len = GetLength(La);
    int Lb_len = GetLength(Lb);
    ElemType e;

    for (int i = 1; i <= Lb_len; ++i) {
        GetElem(Lb,i,e);
        if (!LocateElem(La, e)) {
            ListInsert(La, ++La_len, e);
        }
    }
}

有序表的合并:

        顺序表实现:(代码在定义指针时会有报错,当然也可以不用指针去做)

算法步骤:

  • 创建一个表长为m+n的空表LC

  • 指针pc初始化,指向LC的第一个元素。

  • 指针pah和pb初始化分别指向LA和LB的第一个元素。

  • 当指针papb均未到达相应表尾时,则依次比较papb所指向的元素值,从LA或LB摘取元素值较小的节点插入LC的最后。

  • 如果pb已将到达LB的表尾,依次将LA的剩余元素插入LC的最后。

  • 如果pa已将到达LA的表尾,依次将LB的剩余元素插入LC的最后。

//有序表的合并——顺序表实现
void MergeList(SqList LA,SqList LB,SqList &LC){
    SqList *pa,*pb,*pc,*pa_last,*pb_last;               //创建几个指针(此处为自己创建,下文会有报错,原视频无定义)
    pa = LA.elem;
    pb = LB.elem;               //指针pa,pb的初始值分别指向两个表的第一个元素

    LC.length = LA.length + LB.length;
    LC.elem = new ElemType[LC.length];              //为合并后的新表分配一个数组空间

    pc = LC.elem;               //指针pc指向新表的第一个元素

    pa_last = LA.elem + LA.length-1;                //指针pa_last指向LA表的最后一个元素
    pb_last = LB.elem + LB.length-1;                //指针pb_last指向LB表的最后一个元素

    while (pa<=pa_last && pb<=pb_last){             //两个表都为非空
        if (*pa<=*pb)               //依次“摘取”两表中值较小的结点
            *pa++=*pa++;
        else
            *pc++ =*pb++;
    }

    while (pa<= pa_last)
        *pa++ = *pa++;              //LB表已经到达表尾,将LA中剩余元素加入LC
    while (pb<=pb_last)             //LA表已经到达表尾,将LB中剩余元素加入LC
        *pc++ = *pb++;
}

        有序表合并:用链表实现
                (相比顺序表的指针创建相对简单的多)

//有序表的合并——用链表实现
void MergeList(LinkList &La,LinkList &Lb,LinkList &Lc){
    Lnode *pa,*pb,*pc;
    pa = La->next;
    pb = Lb->next;
    pc = Lc = La;               //用La的头结点作为Lc的头结点

    while(pa && pb){                //谁小谁在前
        if (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;                //插入剩余段
        delete Lb;              //释放Lb的头结点
    }
}

第一章完结

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值