数据结构-线性表

数据之间具有3中基本结构:

    线性结构:数据元素之间为1对1 关系

    树形结构:数据元素之间为1对多的关系

    网状结构:数据元素之间为多对多结构

最简单的线性结构:线性表

    下面是对顺序表的一些操作

#include <stdio.h>
#include <string.h>
#define MAXSIZE 100  //定义线性表的最大长度

typedef struct    //定义顺序表结构
{
    DATA ListData[MAXSIZE+1]; //保存顺序表的数组 
    int ListLen;              //顺序表已存结点 的数量 
}SeqListType;
void SeqListInit(SeqListType *SL) //初始化顺序表
{
    SL->ListLen=0;     //初始化时,设置顺序表长度为0 
} 
int SeqListLength(SeqListType *SL)  //返回顺序表的元素数量 
{
    return (SL->ListLen);    
}
int SeqListAdd(SeqListType *SL,DATA data)  //增加元素到顺序表尾部
{
    if(SL->ListLen>=MAXSIZE)  //顺序表已满 
    {
        printf("顺序表已满,不能再添加结点了!\n");
        return 0;    
    }
    SL->ListData[++SL->ListLen]=data;
    return 1;
} 
int SeqListInsert(SeqListType *SL,int n,DATA data)
{
    int i;
    if(SL->ListLen>=MAXSIZE)   //顺序表结点数量已超过最大数量 
    {
        printf("顺序表已满,不能插入结点!\n");
        return 0;             //返回0表示插入不成功 
    }
    if(n<1 || n>SL->ListLen-1)  //插入结点序号不正确
    {
        printf("插入元素序号错误,不能插入元素!\n");
        return 0;              //返回0,表示插入不成功 
    } 
    for(i=SL->ListLen;i>=n;i--)  //将顺序表中的数据向后移动 
        SL->ListData[i+1]=SL->ListData[i]; 
    SL->ListData[n]=data;        //插入结点 
    SL->ListLen++;               //顺序表结点数量增加1 
    return 1;                   //返回成功插入  
}
int SeqListDelete(SeqListType *SL,int n)  //删除顺序表中的数据元素 
{
    int i;
    if(n<1 || n>SL->ListLen+1)  //删除元素序号不正确
    {
        printf("删除结点序号错误,不能删除结点!\n");
        return 0;              //返回0,表示删除不成功 
    } 
    for(i=n;i<SL->ListLen;i++)  //将顺序表中的数据向前移动 
        SL->ListData[i]=SL->ListData[i+1]; 
    SL->ListLen--;               //顺序表元素数量减1 
    return 1;                   //返回成功删除  
}
DATA *SeqListFindByNum(SeqListType *SL,int n)  //根据序号返回数据元素
{
    if(n<1 || n>SL->ListLen+1)  //元素序号不正确
    {
        printf("结点序号错误,不能返回结点!\n");
        return NULL;              //返回0,表示不成功 
    } 
    return &(SL->ListData[n]);	//返回的是结构体中元素的地址 
} 
int SeqListFindByCont(SeqListType *SL,char *key)  //按关键字查询结点 
{
    int i;
    for(i=1;i<=SL->ListLen;i++)
        if(strcmp(SL->ListData[i].key,key)==0)  //如果找到所需结点 
            return i;        //返回结点序号 
    return 0;  //遍历后仍没有找到,则返回0 
}

    可以看到在查找顺序表的时候特别方便,给出一个序号就可以立马查找到,但是当插入一个数据或者删除一个数据的时候,就涉及到对一大段数据进行操作了,这样既麻烦,有可能造成在对大量数据操作时候可能产生的失误,安全性能降低。


    这个时候引入链表:

    NOTE:链表是采用动态存储分配的一种结构,就是说你可以在你需要的时候申请一块内存来存储数据,而且c语言程序不可以自动回收动态分配的空间,所以在删除某个节点的时候,应该用free函数来释放其占用的内存。

#include <stdlib.h>
typedef struct Node
{
    DATA data;				//数据域
    struct Node *next;
}ChainListType;
#include <string.h>


//返回的是一个ChainListType类型的数据,首先还是要确定ChainListType是

//head这个指针指向的是原来链表末尾节点
ChainListType *ChainListAddEnd(ChainListType *head,DATA data)  //添加结点到链表结尾 
{
    ChainListType *node,*h;
    if(!(node=(ChainListType *)malloc(sizeof(ChainListType))))
    {
        printf("为保存结点数据申请内存失败!\n"); 
        return NULL;  //分配内存失败 
    }
    node->data=data; //保存数据
    node->next=NULL;  //设置结点指针为空,即为表尾 
    if(head==NULL)  //是头指针 
    {
        head=node;
        return head;
    }
    h=head;
    while(h->next!=NULL) //查找链表的末尾节点
        h=h->next ;
    h->next=node;
    return head;
}
ChainListType *ChainListAddFirst(ChainListType *head,DATA data) 
{
    ChainListType *node,*h;
    if(!(node=(ChainListType *)malloc(sizeof(ChainListType))))
    {
        printf("为保存结点数据申请内存失败!\n"); 
        return NULL;  //分配内存失败 
    }
    node->data=data; //保存数据 
    node->next=head;  //指向头指针所指结点 
    head=node;        //头指针指向新增结点
    return head; 
}
ChainListType *ChainListInsert(ChainListType *head,char *findkey,DATA data)  //插入结点到链表指定位置 
{
    ChainListType *node,*node1;    
    if(!(node=(ChainListType *)malloc(sizeof(ChainListType)))) //分配保存结点的内容 
    {
        printf("为保存结点数据申请内存失败!\n"); 
        return 0;  //分配内存失败 
    }
    node->data=data;  //保存结点中的数据 
    node1=ChainListFind(head,findkey);
    if(node1)  //若找到要插入的结点 
    {
        node->next=node1->next;  //新插入结点指向关键结点的下一结点 
        node1->next=node;    //设置关键结点指向新插入结点 
    }else{
        free(node);//释放内存
        printf("未找到插入位置!\n"); 
    }
    return head;//返回头指针
}
ChainListType *ChainListFind(ChainListType *head,char *key) //按关键字在链表中查找内容 
{
    ChainListType *h;
    h=head;       //保存链表头指针 
    while(h)      //若结点有效,则进行查找 
    {
        if(strcmp(h->data.key,key)==0) //若结点关键字与传入关键字相同 
            return h;  //返回该结点指针 
        h=h->next; //处理下一结点 
    }
    return NULL; //返回空指针 
}
int ChainListDelete(ChainListType *head,char *key)
{
    ChainListType *node,*h; //node保存删除结点的前一结点 
    node=h=head;    
    while(h)
    {
        if(strcmp(h->data.key,key)==0) //找到关键字,执行删除操作 
        {
            node->next=h->next;  //使前一结点指向当前结点的下一结点
            free(h);  //释放内存 
            return 1;
        }else{
            node=h;  //指向当前结点 
            h=h->next; //指向下一结点 
        }
     }
     return 0;//未删除 
}
int ChainListLength(ChainListType *head)//获取链表结点数量 
{
    ChainListType *h;
    int i=0;
    h=head;
    while(h)      //遍历整个链表 
    {
        i++; //累加结点数量 
        h=h->next;//处理下一结点 
    }
    return i;//返回结点数量 
}

    可以看到,链表在进行数据的插入或者删除的时候,所需操作要比顺序表少很多,但是进行查询的时候没有顺序表方便。

 队列

    队列是一种特殊的线性表,值允许在表的前端进行删除操作,就像一队人,在出队的时候从队首出,入队的时候自动的去找队尾排到队尾。是一种“先来先服务”的结构。 

#define QUEUEMAX 15
typedef struct
{
    DATA data[QUEUEMAX]; //队列数组 
    int head; //队头 
    int tail; //队尾 
}SeqQueue;

    队头队尾就是队头或者队尾元素的位置的标号。入队的时候需要修改队尾的标号,队尾标号加1。出队的时候修改队首的标号,队首标号加1.

SeqQueue *SeqQueueInit()
{
    SeqQueue *q;
    if(q=(SeqQueue *)malloc(sizeof(SeqQueue))) //申请保存队列的内存 
    {
        q->head = 0;//设置队头 
        q->tail = 0;//设置队尾 
        return q;
    }else
        return NULL; //返回空 
}
void SeqQueueFree(SeqQueue *q) //释放队列
{
    if (q!=NULL)
        free(q);
} 
int SeqQueueIsEmpty(SeqQueue *q)  //队列是否为空 
{
    return (q->head==q->tail);
}
int SeqQueueIsFull(SeqQueue *q)//队列是否已满 
{
    return (q->tail==QUEUEMAX);
}
int SeqQueueLen(SeqQueue *q) //获取队列长度 
{
    return(q->tail-q->head);
}
int SeqQueueIn(SeqQueue *q,DATA data)//顺序队列的入队函数
{
    if(q->tail==QUEUEMAX)
    { 
        printf("队列已满!\n");
        return(0);
    }else{
        q->data[q->tail++]=data;
        return(1);
    }
}
DATA *SeqQueueOut(SeqQueue *q)//顺序队列的出队
{
    if(q->head==q->tail)
    {
        printf("\n队列已空!\n");
        return NULL;
    }else{
        return &(q->data[q->head++]);
    }
}
DATA *SeqQueuePeek(SeqQueue *q) //获取队头元素
{
    if(SeqQueueIsEmpty(q))
    {
        printf("\n队列为空!\n");
        return NULL; 
    }else{
        return &(q->data[q->head]);
    }
} 

    对于队列,同样存在一个问题,当不断的出队之后,明明前面空着许多的位置,但是入队的时候却只能从队尾入,而队尾此时已经达到最大值,这种现象叫做“假溢出”。

    为了解决这个问题,引入了

循环队列

    循环队列在初始的时候队首队尾标号相同,在入队的时候,如果队尾还未达到最大值,则和普通队列一致,如果超过最大值了,则队尾标号=(tail+1)% maxsize;如果这个时候tail=head,则真的说明队列已经满了,则提示溢出。在出队的时候,head=head+1,同样,这是在head还处于maxsize范围之内的,如果超过了,也应当进行head=(head+1)%maxsize操作。

CycQueue *CycQueueInit()
{
    CycQueue *q;
    if(q=(CycQueue *)malloc(sizeof(CycQueue))) //申请保存队列的内存 
    {
        q->head = 0;//设置队头 
        q->tail = 0;//设置队尾 
        return q;
    }else
        return NULL; //返回空 
}
void CycQueueFree(CycQueue *q) //释放队列
{
    if (q!=NULL)
        free(q);
} 
int CycQueueIsEmpty(CycQueue *q)  //队列是否为空 
{
    return (q->head==q->tail);
}
int CycQueueIsFull(CycQueue *q)//队列是否已满 
{
    return ((q->tail+1)%QUEUEMAX==q->head);
}
int CycQueueIn(CycQueue *q,DATA data)//入队函数
{
    if((q->tail+1)%QUEUEMAX == q->head )
    { 
        printf("队列已满!\n");
        return 0;
    }else{
        q->tail=(q->tail+1)%QUEUEMAX;//求列尾序号 
        q->data[q->tail]=data;
        return 1;
    }	
}
DATA *CycQueueOut(CycQueue *q)//循环队列的出队函数
{
    if(q->head==q->tail) //队列为空 
    {
        printf("队列已空!\n");
        return NULL;
    }else{
        q->head=(q->head+1)%QUEUEMAX;
        return &(q->data[q->head]);
    }
}
int CycQueueLen(CycQueue *q) //获取队列长度 
{
    int n;
    n=q->tail-q->head;
    if(n<0)
        n=QUEUEMAX+n;
    return n;
}
DATA *CycQueuePeek(CycQueue *q) //获取队定中第1个位置的数据
{
    if(q->head==q->tail)
    {
        printf("队列已经为空!\n");
        return NULL; 
    }else{
        return &(q->data[(q->head+1)%QUEUEMAX]);
    }
} 

后进后出结构

    首先数据结构的定义

typedef struct stack
{
    DATA data[SIZE+1]; //数据元素 
    int top; //栈顶 
}SeqStack;

    入栈操作:判断top+1是不是已经超过了最大SIZE,如果没有贼top上移,top+1,将入栈的数据存放到栈的数据段的top+1位置。

    出栈操作:判断top-1是不是小于0;如果是的话说明栈是空的。不是的话,top-1;弹出原来top位置对应的栈顶元素。

SeqStack *SeqStackInit()
{
    SeqStack *p;
    if(p=(SeqStack *)malloc(sizeof(SeqStack))) //申请栈内存 
    {
        p->top=0; //设置栈顶为0 
        return p;//返回指向栈的指针 
    }
    return NULL;
}
int SeqStackIsEmpty(SeqStack *s) //判断栈是否为空 
{
    return(s->top==0);
}
void SeqStackFree(SeqStack *s) //释放栈所占用空间 
{
    if(s)
        free(s);
}
void SeqStackClear(SeqStack *s)  //清空栈 
{
    s->top=0;
} 
int SeqStackIsFull(SeqStack *s) //判断栈是否已满
{
    return(s->top==SIZE);
} 
int SeqStackPush(SeqStack *s,DATA data) //入栈操作 
{
     if((s->top+1)>SIZE)
     {
         printf("栈溢出!\n"); 
         return 0;
     }
     s->data[++s->top]=data;//将元素入栈
     return 1; 
}
DATA SeqStackPop(SeqStack *s) //出栈操作 
{
     if(s->top==0)
     {
         printf("栈为空!");
         exit(0);
     }
     return (s->data[s->top--]);
}
DATA SeqStackPeek(SeqStack *s) //读栈顶数据
{
     if(s->top==0)
     {
         printf("栈为空!");
         exit(0);
     }
     return (s->data[s->top]);
} 


    


    



          

猜你喜欢

转载自blog.csdn.net/vfi7018/article/details/80518860