《大话数据结构》:线性表

C语言-大话数据结构:线性表

//
// Created by pengxiangzhou on 2021/1/24.
//

#include <form.h>

#ifndef C_DS_LINEAR_LIST_H
#define C_DS_LINEAR_LIST_H

#endif //C_DS_LINEAR_LIST_H

/*线性表-顺序存储结构
 * 顺序存储结构的三个属性:数组data,存储位置为存储空间的位置;数组存储数据元素的最大值;线性表当前长度
 * 数组的长度是存放线性表的存储空间的长度,存储分配后这个量一般不会变,线性表的长度等于线性表中数据元素的个数,线性表的长度<=数组的长度
 * */
#define MAXIMIZE 20
typedef int ElemType;
typedef struct {
    
    
    ElemType data[MAXIMIZE];   /*数组存储数据元素,最大值为MAXIMIZE*/
    int length;
}SqList;

/*顺序表存储结构的插入与删除*/
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
typedef int Status;
/*获得元素操作
 * Status是函数的类型,其值是函数结果状态代码,如OK等
 * 初始条件:顺序线性表L已存在,1<= i <= ListLength(L)
 * 操作结果:用e返回L中第i个数据元素的值
 * */
Status Sequence_List_GetElem(SqList L, int i ,ElemType *e)
{
    
    
    if(L.length ==0||i<1||i>L.length)
        return ERROR;
    *e = L.data[i-1];
    return OK;
}
/*插入操作
 *如果插入位置不合理,抛出异常;如果线性表长度大于等于数组长度,则抛出异常或动态增加容量;表长+1;
 * 从最后一个元素开始向前遍历到第i个位置,分别将他们都向后移动一个位置;将要插入的元素填入位置i处;
 * 这里的 "L->"代表"L."
 * */
Status Sequence_ListInsert(SqList *L, int i, ElemType *e)
{
    
    
    int k;
    if (L->length==0)  /*线性表为空*/
        return ERROR;
    if (i<1 || i >L->length) /*删除位置不正确*/
        return ERROR;
    *e=L->data[i-1];
    if (i<L->length)   /*如果删除不是最后位置*/
    {
    
    
        for (k=i;k<L->length;k++)  /*将删除位置后继元素前移*/
            L->data[k-1]=L->data[k];
    }
    L->length--;
    return OK;
}

/*线性表-链式存储结构
 * 每个数据元素除了存储本身的信息外,还要存储一个指示其直接后继的信息。
 * 存储数据元素信息的域称为数据域,把存储直接后继位置的域称为指针域。指针域中存储的信息称作指针或链
 * 数据域和指针域的两部分信息组成数据元素的存储映像,称为结点。
 * 链表中的第一个结点的存储位置叫做头指针,头结点的数据域可不存储任何信息,也可以存储如线性表的长度等附加信息;线性链表的最后一个结点指针为空,用NULL表示
 * */

/*p->data表示结点的数据域,p->next表示结点的i+1指针*/
typedef struct Node{
    
    
    ElemType data;
    struct Node *next; //结点由存放数据元素的数据域存放后继结点地址的指针域组成
}Node;
typedef struct Node *LinkList; /*定义LinkList*/

/* 获得元素操作
 * 初始条件:顺序线性表L已存在,1<=i<=ListLength(L)
 * 操作结果:用e返回L中第i个数据元素的值
 * 缺点:必须从头开始找,直到第i个元素为止*/
Status Linked_structure_GetElem(LinkList L, int i ,ElemType *e)  //?为什么不是LinkList *L
{
    
    
    int j;
    LinkList p;  /*声明一结点p*/
    p = L->next; /*让p指向链表L的第一个结点*/
    j = 1;  /*j为计数器*/
    while (p&&j<i)   /*p不为空或者计数器j还没有等于i时,循环继续*/
    {
    
    
        p = p->next; /*让p指向下一个结点*/
        ++j;
    }
    if (!p || j>i)
        return ERROR;
    *e = p->data;
    return OK;
}
/*插入操作:在第i个数据中插入结点的算法思路
 *声明一结点p指向链表第一个结点,初始化j从1开始
 * 当j<i时,就遍历链表,让p的指针向后移动,不断指向下一结点,j累加1
 * 若到链表末尾p为空,则说明第i个元素不存在
 *
 * */
/*初始条件:顺序线性表L已存在,1<=i<=ListLength(L)*/
/*操作结果:在L中第i个位置之前插入新的数据元素e,L的长度加1*/
#include <stdlib.h>
Status Linked_structure_ListInsert(LinkList *L, int i ,ElemType e)
{
    
    
    int j;
    LinkList p,s;
    p = *L;
    j = 1;
    while (p&&j<i) /*寻找第i个结点*/
    {
    
    
        p = p->next;
        ++j;
    }
    if (!p || j>i)
        return ERROR;  /*第i个元素不存在*/
    s = (LinkList) malloc(sizeof(Node));  /*生成新的结点,malloc在stdlib.h*/
    s->data = e;
    s->next = p->next;
    p->next = s;
}
/*删除操作:将该结点的前继结点的指针绕过,指向后继结点,并释放该结点
 * */
Status Linked_structure_ListDelete(LinkList *L, int i ,ElemType *e)
{
    
    
    int j;
    LinkList p,q;
    p = *L;
    j = 1;
    while (p&&j<i) /*寻找第i个结点*/
    {
    
    
        p = p->next;
        ++j;
    }
    if (!(p->next)|| j>i)
        return ERROR;  /*第i个元素不存在*/
    q = p->next;
    p->next = q->next;   /*将q的后继赋值给p的后继*/
    *e = q->data;  /*将q结点中的数据给e*/
    free(q);  /*让系统回收此结点,释放内存*/
    return OK;
}

/*单链表的整表创建是个动态生成链表的过程,即从"空表"的初始状态起,其次建立各元素结点,并逐个插入链表
 *随机产生n个元素的值,建立带表头结点的单链线性表L(头插法)
 * */
#include <time.h>
void Linked_structure_CreateListHead(LinkList *L, int n )
{
    
    
    LinkList p;
    int i;
    srand(time(0)); /*初始化随机数种子*/
    *L = (LinkList)malloc(sizeof(Node));
    (*L)->next = NULL;
    for (i = 0; i<n;i++)
    {
    
    
        p = (LinkList)malloc(sizeof(Node));
        p->data = rand()%100+1; /*随机生成100以内的数字*/
        p->next = (*L)->next;
        (*L)->next = p;
    }
}
void CreateListHead(LinkList *L, int n)
{
    
    
    LinkList p;
    int i;
    srand(time(0));
    *L = (LinkList)malloc(sizeof(Node));
    (*L)->next = NULL;
    for (i = 0; i<n;i++)
    {
    
    
        p = (LinkList)malloc(sizeof(Node));
        p->data=rand()%100+1;
        p->next=(*L)->next;
        (*L)->next=p;
    }
}

void CreatListTail(LinkList *L,int n)
{
    
    
    LinkList p,r;
    int i;
    srand(time(0));
    *L = (LinkList)malloc(sizeof(Node));  /*L是指整个单链表*/
    r = *L; /*r为指向尾部的结点,r会随着循环增长为一个多结点的链表*/
    for (i=0;i<n;i++)
    {
    
    
        p = (Node *)malloc(sizeof(Node));
        p->data = rand()%100+1;
        r->next = p;
        r=p; /*新结点是尾结点,记为r*/
    }
    r->next = NULL; /*将尾结点的指针域置空*/
}

Status ClearList(LinkList *L)
{
    
    
    LinkList p,q;
    p = (*L)->next;  /*p指向第一个结点*/
    while (p)  /*没到表尾*/
    {
    
    
        q = p->next;  /*当前结点被释放后,q用于记录下一个结点的信息*/
        free(p);
        p = q;
    }
    (*L)->next = NULL; /*头结点指针域为空*/
    return OK;
}

/*静态链表;使用数组代替指针,来描述单链表
 * 数组的元素由两个数据域组成,data和cur;数组的每个下表都对应一个data(数据域,存放数据元素)和一个cur(相当于单链表的next指针,存放该元素的后继在数组中的下标)
 * */
/*线性表的静态链表存储结构*/
#define MAXSIZE 1000
typedef struct
{
    
    
    ElemType data;
    int cur;
} Component,StaticLinkList[MAXSIZE];
/*将一维数组space中各分量链成一备用链表,space[0].cur为头指针,"0"表示空指针*/
Status InitList(StaticLinkList space)
{
    
    
    int i;
    for (i = 0 ; i < MAXIMIZE; i ++)
        space[i].cur = i +1;
    space[MAXIMIZE-1].cur = 0;  /*目前静态链表为空,最后一个元素的cur为0*/
    return OK;
}
/* 静态链表的插入操作
 * 需要解决的问题:静态模拟动态链表结构的存储空间的分配,需要时申请,无用时释放
 * 在动态链表中,结点的申请和释放分别借用malloc()和free()来实现,静态链表不存在该函数,需要自行实现
 * 解决方法:将所有未被使用过的及已被删除的分量用游标链成一个备用的链表,每当进行插入时,从备用链表上取得第一个结点作为待插入的新结点
 * */
/*若备用空间链表非空,则返回分配的结点下标,否则返回0
 *数组第一个元素的cur用来存放备用链表第一个结点的下标
 * */
int ListLength(StaticLinkList L)
{
    
    
    int j = 0;
    int i = L[MAXIMIZE-1].cur;
    while (i)
    {
    
    
        i = L[i].cur;
        j++;
    }
    return j;
}
int Malloc_SLL(StaticLinkList space)
{
    
    
    int i = space[0].cur;   /*返回第一个备用空闲的下标*/
    if (space[0].cur)
        space[0].cur = space[i].cur; /*拿出下一个分量用来做备用*/
    return i; /*返回数组头元素的cur存的第一个空闲的下标*/
}
/*?在L中第i个元素之前插入新的数据元素e*/
Status ListInsert(StaticLinkList L, int i, ElemType e)
{
    
    
    int j, k , l;
    k = MAXIMIZE - 1;          /*k是最后一个元素的下标*/
    if (i<1||i> ListLength(L)+1)
        return ERROR;
    j = Malloc_SLL(L);  /*获得空闲分量的下标*/
    if (j)
    {
    
    
        L[j].data=e;   /*将数据赋值给此分量的data*/
        for(l=1;l<=i-1;l++)     /*找到第i个元素之前的位置*/
            k=L[k].cur;
        L[j].cur = L[k].cur;  /*把第i个元素之前的cur赋值给新元素的cur*/
        L[k].cur = j; /*把新元素的下标赋值给第i个元素之前元素的cur*/
        return OK;
    }
    return ERROR;
}
/*?删除在L中第i个数据元素e*/
void Free_SSL(StaticLinkList space, int k)
{
    
    
    space[k].cur = space[0].cur; /*把第一个元素cur值赋给要删除的分量cur*/
    space[0].cur = k;  /*把要删除的分量下标赋值给第一个元素的cur*/
}
Status ListDelete(StaticLinkList L,int i )
{
    
    
    int j,k;
    if (i>ListLength(L)+1||i<1)
        return ERROR;
    k = MAXIMIZE - 1;
    for (j=1;j<=i-1;j++)
        k = L[k].cur;
    j = L[k].cur;
    L[k].cur = L[j].cur;
    Free_SSL(L,j);
    return OK;
}
/*循环链表:将单链表中终端结点的指针端由空指针改为指向头结点,就使整个单链表形成一个环
 * 循环链表和单链表的主要差异在于循环的判断条件:单链表判断p->next是否为空,循环链表判断p->next是否等于头结点*/
/*双向链表:在单链表的每个结点中,再设置一个指向其前驱结点的指针域,双向链表中的结点都有两个指针域,一个指向直接后继,一个指向直接前驱
 * 优点:可以反向遍历查找等数据结构,但是在插入和删除时,需要更改两个指针变量
 * */
/*线性表的双向链表存储结构*/
typedef struct DulNode
{
    
    
    ElemType data;
    struct DulNode *prior; /*直接前驱指针*/
    struct DulNode *next; /*直接后继指针*/
}DulNode,*DuLinkList;

猜你喜欢

转载自blog.csdn.net/weixin_43464554/article/details/113256148