数据结构中线性表、栈、队列代码复现(参考)

数据结构中线性表、栈、队列代码复现(参考)

image.png

〇、写在前面

若干定义

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#define ERROR 0
#define OK 1
#define OVERFLOW -1
#define STACK_INIT_SIZE 100
//顺序栈的预分配的空间大小
#define STACKINCREMENT 20
typedef int SElemType;
typedef int Status;

一、线性表--顺序存储结构 SqList

1.1 结构体定义

typedef struct
{
    ElemType elem[MAXLEN]; /*顺序表中存放元素的数组,其中 ElemType 为抽象数据类型,在程
    序具体实现时可以用任意类型代替*/
    int length;            /*顺序表的长度,即元素个数*/
} SqList;                  /*顺序表的类型*/

image.png

1.2 一些基本操作实现

插入 SqList_Insert(重要)

Status SqList_Insert(SqList &L, int i, ElemType x)
/*在顺序表 L 中第 i 个元素前插入新元素 x*/
{
    if (i < 1 || i > L.length + 1)
        return ERROR; /*插入位置不正确则出错*/
    if (L.length >= MAXLEN)
        return OVERFLOW; /*顺序表 L 中已放满元素,再做插入操作则溢出*/
    for (int j = L.length - 1; j >= i - 1; j--)
        L.elem[j + 1] = L.elem[j]; /*将第 i 个元素及后续元素位置向后移一位*/
    L.elem[i - 1] = x;             /*在第 i 个元素位置处插入新元素 x*/
    L.length++;                    /*顺序表 L 的长度加 1*/
    return OK;
}

删除 SqList_Delete(重要)

Status SqList_Delete(SqList &L, int i, ElemType &e)
/*在顺序表 L 中删除第 i 个元素*/
{
    if (i < 1 || i > L.length)
        return ERROR; /*删除位置不正确则出错*/
    e = L.elem[i - 1];
    for (int j = i; j <= L.length - 1; j++)
        L.elem[j - 1] = L.elem[j]; /*将第 i+1 个元素及后继元素位置向前移一位*/
    L.length--;                    /*顺序表 L 的长度减 1*/
    return OK;
}
int SqList_Search(SqList L, ElemType e)
/* 在顺序表中查找值为 e 的元素,如果找到,则函数返回该元素在顺序表中的位置,否则返回 0*/
{
    int i;
    for (i = 1; i <= L.length && L.elem[i - 1] != e; i++)
        ; /*从第一个元素开始依次将每个元素值与给定值 x 比较*/
    if (i <= L.length)
        return i;
    else
        return 0;
}

创建 SqList_Create

Status SqList_Create(SqList &L) //建立一个顺序表 L
{
    printf("Please input the length:"); /*请求输入顺序表中元素个数*/
    scanf("%d", &L.length);
    printf("Please input the Value:\n"); /*请求输入顺序表中各个元素*/
    for (int i = 0; i < L.length; i++)
        scanf("%d", &L.elem[i]);
    return OK;
}

输出 output

void Output(SqList L) //输出顺序表中各个元素值
{
    for (int i = 0; i < L.length; i++)
        printf(" %d", L.elem[i]);
    printf("\n");
}

1.3 应用

去重 SqList_DeleteDuplicate

Status SqList_DeleteDuplicate(SqList &L)
{
    int i, j;
    for (int i = 1; i < L.length; i++)
        if (L.elem[i] == L.elem[i - 1])
        {
            for (int j = i; j < L.length; j++)
                L.elem[j - 1] = L.elem[j];
            L.length--;
            i--;
        }
return OK;
}

2.1 结构体定义

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

2.2 一些基本操作实现

Status LinkList_Insert(LinkList &L, int i, ElemType e)
// 在单链表 L 的第 i 个数据元素之前插入新的元素 e,i 的合法值是 1≤i≤表长+1
{
    LinkList p = L, s;     // p 指针指示链表中的头结点
    int j = 0;             // j 指示 p 指针所指向的结点在表中的位序号
    while (p && j < i - 1) //找到第 i 个元素的前驱结点,并用 p 指针指示它
    {
        p = p->next;
        ++j;
    }
    if (!p || j > i - 1) // i 不合法(找不到前驱结点)
        return ERROR;
    s = (LinkList)malloc(sizeof(LNode)); //产生新的结点
    s->data = e;
    s->next = p->next; //修改链指针,让新结点插入到第 i-1 个元素结点 p 的后面
    p->next = s;
    return OK;
}
补充说明
1.j<i-1(是为了找到待删节点前一个位置)

2.考虑输入不合法值的情况
i>len+1时,while循环后p指针为空,i<1时,j>i+1

3.找到第i个元素的前驱节点p后,插入三步骤:
(1)创建一个新的结点s。
(2)将此结点的数据域赋值为e,并将p->next赋值为s->next。
(3)将s赋值给p->next

4.因为是动态分配空间,所以不需要判断存储空间是否满了,要是静态分配空间需要判断空间是否满了,满了要扩容

image.png

Status LinkList_Delete(LinkList &L, int i, ElemType &e)
// 删除单链表 L 中的第 i 个数据元素,并用 e 返回其值,i 的合法值是 1<=i<=表长
{
    LinkList p = L, q;           // p 指针指示链表中的头结点
    int j = 0;                   // j 指示 p 指针所指向的结点在表中的位序号
    while (p->next && j < i - 1) //找到被册结点的前驱结点
    {
        p = p->next;
        ++j;
    }
    if (!p->next || j > i - 1) // i 不合法(找不到前驱结点)
        return ERROR;
    q = p->next;       // q 指向待删结点
    p->next = q->next; // 修改链指针让待删结点从链中脱离出来
    e = q->data;       //用 e 保存待删结点的数据元素值
    free(q);           //释放待删结点空间
    return OK;
}
一些思考
思考1:为什么这里while循环是判断p->next是否为空指针,而插入函数的while里面的条件却是p呢?
提示:请考虑只有头结点的情况。

思考2:参数e的作用,为何函数内会有&取地址符?
答:返回被删除的值,加&是为了引用对象的地址,对引用的操作就等于对实际对象的操作

思考3:空表的情况不需要考虑吗??
提示:请考虑这是带头节点的链表。带头节点链表的空表条件是:
L->next=NULL,while循环已经包括在里面了,当然,没有带头节点的,自然需要另写。

p指向待删节点前驱,q指向待删节点,删除操作即 p->next=q->next

image.png

注意,此例子带头节点

Status LinkList_Locate(LinkList L, int i, ElemType &e)
//查找线性表中第 i 个数据元素,如果查找成功,则用 e 返回其数据元素值
{
    LinkList p = L->next; // p 指向链表中的首结点
    int j = 1;            // j 记录 p 结点在表中的位序号
    while (p && j < i)    //沿着后继指针一个一个“点数”
    {
        p = p->next;
        j++;
    }
    if (!p || j > i) // i 值不合法
        return ERROR;
    e = p->data; //用 e 返回第 i 个元素的值
    return OK;
}

Status LinkList_Creat(LinkList &L, int n)
//用头插法创建一个带头结点的单链表
{
    L = (LinkList)malloc(sizeof(LNode)); //先创建一个空链表
    L->next = NULL;
    printf("请按逆位序输入各个数据元素值:\n "); //请求输入 n 个线性表中各个元素
    for (int i = 0; i < n; i++)                 //将各元素结点依次插入链表的头部
    {
        LinkList p = (LinkList)malloc(sizeof(LNode));
        if (!p)
            return ERROR;
        scanf("%d", &p->data);
        p->next = L->next;
        L->next = p;
    }
    return OK;
}
Status LinkListCreat_1(LinkList &L, int n)
//用尾插法创建一个带头结点的单链表
{
    L = (LinkList)malloc(sizeof(LNode)); //先创建一个空链表
    L->next = NULL;
    LinkList r = L;
    printf("请按顺序输入 n 个有重复值的数据元素:\n "); //请求输入 n 个线性表中各个元素
    for (int i = 0; i < n; i++)                        //将各元素结点依次插入链表的头部
    {
        LinkList p = (LinkList)malloc(sizeof(LNode));
        if (!p)
            return ERROR;
        scanf("%d", &p->data);
        r->next = p;
        r = p;
    }
    r->next = NULL; //最后一个结点的后继指针置为空
    return OK;
}

头插法需要仔细理解看一下,请看下图

初始化

image.png
插入

image.png

image.png

输出output

void Output(LinkList L)
{
    LinkList p = L->next;
    while (p)
    {
        printf("%d ", p->data);
        p = p->next;
    }
}

2.3 应用

Status LinkList_DeleteDuplicate(LinkList &L)
{
    LinkList p = L->next;
    while (p->next)
    {
        LinkList q = p->next;
        if (p->data == q->data)
        {
            p->next = q->next;
            free(q);
        }
        else
        {
            p = p->next;
        }
    }
    printf("LinkList_DeleteDuplicate finish!\n");
    return OK;
}

image.png

三、顺序栈

3.1 栈的结构体定义

typedef struct
{
    SElemType *base; // 栈的存储空间基地址(栈底指针)
    SElemType *top;  //指示栈顶元素的下一存储单元的位置(栈顶指针)
    int stacksize;
    //栈当前的存储空间容量
} SqStack;

3.2 栈的基本操作

初始化 InitStack

Status InitStack(SqStack &S)
// 创建一个空的顺序栈 S
{
    S.base = (SElemType *)malloc(STACK_INIT_SIZE * sizeof(SElemType)); // 分配预定义大小的存储空间
    if (!S.base)
        //如果空间分配失败
        exit(OVERFLOW);
    S.top = S.base;
    S.stacksize = STACK_INIT_SIZE;
    //置当前栈顶指针指向栈底的位置
    //置当前分配的存储空间容量为 STACK_INIT_SIZE 的值
    return OK;
}

Push(重要)

Status Push(SqStack &S, SElemType e)
// 在顺序栈 S 中插入新的元素 e, 使其成为新的栈顶元素
{
    if (S.top - S.base >= S.stacksize)
    //当前存储空间已满,则扩充空间
    {
        S.base = (SElemType *)realloc(S.base, (S.stacksize + STACKINCREMENT) * sizeof(SElemType));
        if (!S.base)
            //如果空间分配失败
            exit(OVERFLOW);
        S.top = S.base + S.stacksize;
        //修改增加空间后的基址
        S.stacksize += STACKINCREMENT;
        //修改增加空间后的存储空间容量
    }
    *S.top++ = e;
    // e 压栈后,top 指针再后移一位
    return OK;
}

pop(重要)

Status Pop(SqStack &S, SElemType &e)
// 删除顺序栈 S 中的栈顶数据元素,并用 e 返回其值
{
    if (S.base == S.top) //如果栈空
    {
        printf("The Stack is NULL!\n");
        return ERROR;
    }
    e = *--S.top;
    //删除栈顶元素并用 e 返回其值
    return OK;
}

应用--判断回文数

Status IsHuiWen(LinkList L)
//判断单链表 L 中的字符序列是否构成回文,若是,则返回 OK,否则返回 ERROR
{
    SElemType e;          //用于存放栈中弹出的字符
    SqStack S;            // S 也可以是顺序栈,即说明为 SqStack S;
    InitStack(S);         //创建一个空的栈
    LinkList p = L->next; //引进一个指针 p,使其指向单链表的第一个数据元素结点
    while (p)
    //将链中字符数据依次入栈
    {
        Push(S, p->data);
        p = p->next;
    }
    p = L->next;
    while (p)
    {
        // p 重新指向单链表中的第一个数据元素结点
        //将链中字符依次与栈中弹出的字符进行比较
        Pop(S, e);
        if (p->data == e)
            //如果相等,则继续比较
            p = p->next;
        else
            //如果不相等,则退出比较
            break;
    }
    if (!p)
        //如果所有字符都对应相等,则为回文,函数返回 OK
        return OK;
    else
        return ERROR;
    // 否则不为回文,函数返回 ERROR
}

四、链栈

image.png

链栈结构体定义

typedef struct SNode
{
    SElemType data;
    struct SNode *next;
}SNode,*LinkStack;

链栈的基本操作

初始化

Status InitLinkStack(LinkStack &S)    //输入数组b的牌
{
    S=(LinkStack)malloc(sizeof(SNode));
    S->data=-1;
    S->next=NULL;
    return OK;
}

push

Status Push(LinkStack &S,SElemType e)
{
    LinkStack p = (LinkStack)malloc(sizeof(SNode));
    if(!p)  return ERROR;
    p->data=e;
    p->next=S;
    S=p;
    return OK;
}

pop

Status Pop(LinkStack &S,SElemType &e)
{
    if(S==NULL) return ERROR;
    LinkStack p=S;
    e=p->data;
    S=p->next;
    free(p);
    return OK;
}

输出display

void displayS(LinkStack &S)
{
    LinkStack p=S;
    while(p)
    {
        if(p->data==-1) break;
        printf("%d ",p->data);
        p=p->next;
    }
    printf("\n");
}

五、队列

队列结构体定义

typedef struct
{
    QElemType *base;
    // 队列存储空间基地址
    int front; //指示队首元素存储单元的位置(队首指针)
    int rear;  //指示队尾元素的下一存储单元的位置(队尾指针)
} SqQueue;

队列的基本操作

image.png

初始化 InitQueue

Status InitQueue(SqQueue &Q)
// 创建一个空的循环顺序队列 Q
{
    Q.base = (QElemType *)malloc(MAXQSIZE * sizeof(QElemType));
    if (!Q.base)
        //如果空间分配失败
        return ERROR;
    Q.front = Q.rear = 0;
    return OK;
}

入队 EnQueue(重要)

Status EnQueue(SqQueue &Q, QElemType e)
//在循环顺序队列 Q 中,插入新元素 e 使其成为队尾元素
{
    if ((Q.rear + 1) % MAXQSIZE == Q.front) //若队列满,函数返回 ERROR
    {
        printf("The Queue is OVERFLOW!\n");
        return ERROR;
    }
    Q.base[Q.rear] = e;               //新元素成为队尾元素
    Q.rear = (Q.rear + 1) % MAXQSIZE; //利用模运算,"尾指针"加 1,使尾指针后移一个位置
    return OK;
}

出队 DeQueue(重要)

Status DeQueue(SqQueue &Q, QElemType &e)
//在循环顺序队列 Q 中,删除 Q 的队首元素
{
    if (Q.front == Q.rear) //若队列空,函数返回 ERROR
    {
        printf("The Queue is NULL!\n");
        return ERROR;
    }
    e = Q.base[Q.front];
    /*将队首元素用 e 保存其值*/
    Q.front = (Q.front + 1) % MAXQSIZE; //利用模运算,"头指针"加 1,使头指针后移一个位置
    return OK;
}

输出 Out

void Out(SqQueue Q)
//队列元素的输出函数
{
    int k, m;
    k = Q.front;
    m = Q.rear;
    while (k != m)
    {
        printf("%4d", Q.base[k]);
        k = (k + 1) % MAXQSIZE;
    }
    printf("\n");
}

为什么要空出一个存储空间,主要解决假溢出的情况:
image.png


posted @ 2021-12-26 20:44  yuezi2048  阅读(109)  评论(0编辑  收藏  举报