线性表
线性表的定义
线性表(List):零个或多个相同类型的数据元素的有限序列
顺序线性表结构
线性表的抽象数据类型:
#include <stdio.h>
#define MaxSize 100 //线性表存储空间大小
#define ERROR 0
#define TRUE 1
typedef int ElemType;
typedef int Status;
// 线性表的声明
typedef struct list {
ElemType data[MaxSize]; //数据元素
int length; //线性表的长度
}List;
//线性表的相关操作
void InitList(List* L); //初始化一个空的线性表L
Status IsEmpty(List L); //判断线性表是否为空,空返回true
void ClearList(List* L); //清空线性表
int ListLength(List L); //返回线性表L的长度即元素个数
void GetElem(List L,int i,ElemType* e); //将线性表L中第i个位置元素值返回给e
int LocateElem(List L,ElemType* e); //将线性表L中元素值为e的,查找成功返回第i,否则返回error
Status ListInsert(List* L,int i,ElemType e); //在线性表L中第i个位置插入元素e,成功返回true
Status ListDelete(List* L,int i,ElemType* e); //在线性表L删除第i个位置的元素,并将其值返回给e,成功返回true
*内存地址的计算公式:
LOC(ai) = LOC(a1) + (i-1)c ; c单个元素在内存中所占空间
注意:虽然线性表是由数组实现的,但是“数组的长度”表示该数组存储空间可储存元素的个数,一般是固定值;而“线性表的长度”表示的是目前该线性表中储存的元素个数
线性表的插入和删除
//线性表的插入
Status ListInsert(List *L, int i, ElemType e){
int k;
//代码的健壮性
if(L->length == MaxSize){ //线性表已满
return ERROR;
}
if(i < 1 || i > L->length+1){ //查询的位置不在线性表内
return ERROR;
}
if(i <= L->length){
for(k = L->length-1; k >= i-1; k--){ //插入的位置不在表为,插入位置后的每个元素均要往后移一位
L->data[k+1] = L->data[k];
}
}
L->data[i-1] = e;
L->length++;
return TRUE;
}
顺序存储结构和点链表结构的优缺点对比:
栈
栈的定义
栈(stack):是限定仅在表尾进行插入删除操作的线性表
特性:栈尾先进后出
应用:
1.网页的返回键
2.递归函数
3.后缀表达式转换
4.数制转换(作业中布置)
栈的顺序存储结构
#include <stdio.h>
#define MaxSize 100
#define ERROR 0
#define TRUE 1
typedef int ElemType; //视情况而定数据元素类型
typedef int Status;
//栈的定义
typedef struck stack{
ElemType data[MaxSize]; //储存数据元素
int top; //栈顶指针
}Stack;
注意:top的初始值为-1时表示为空栈,为0时表示栈内有一个元素,为MaxSize-1时栈满,此情况和数组下标对应。
栈的抽象数据类型:
//进栈操作Push
Status Push(Stack *s, ElemType e){
//代码的健壮性
if(s->top == MaxSize-1){ //检测栈满,防止溢出
return ERROR;
}
s->data[++s->top] = e; //指针+1在栈顶插入目标元素
return TRUE;
}
//出栈操作Pop
Status Pop(Stack *s, ElemType *e){
if(s->top == -1){ //检测栈是否为空
return ERROR;
}
*e = s->data[s->top]; //将删除的值保存在e中,并返回其值
s->top--; //栈顶指针-1
return TRUE;
}
两栈共享空间
因为栈的线性存储事先必须规定数组的储存空间大小,会存在一个栈空间不够用的情况,而此时可能又有一个栈储存空间剩余很多造成内存空间浪费,所以提出共享栈的概念:用一个数组来存储两个栈。
思路:top1、top2分别是栈1和栈2的栈顶指针,从数组两端向中间靠拢;
栈空:top1 = -1, top2 = MaxSize
栈满:即top1和top2相遇,top1 + 1 = top2
具体的代码见书上P95-96
栈的链式存储结构
链栈的结构如下:
typedef struct stackNode{
ElemType data;
struct stackNode *next;
}StackNode, *LinkStackPtr;
typedef struct linkStack{
LinkStackPtr top; //栈顶指针
int count; //元素个数
}LinkSatck;
栈顶指针相当于单链表的头指针,栈顶元素相当于单链表的首节点,所以链栈中不需要头节点,采用头插法插入元素;
队列
队列(queue):是只允许在一端进行插入操作,在另一端进行删除操作的线性表;
特性:先进先出
队列的线性结构
typedef struct queue {
//数据元素
ElemType data[MaxSize];
int front; //头指针
int rear; //尾指针
}Queue;
具体的操作和栈差不多就不过多讲解了。
循环队列
一个普通队列可以想到,首部元素不断出队front一直后移,尾部一直插入元素rear一直后移最后会变成假溢出现象,如下图:
为了解决这个问题提出了循环队列:头尾相接的顺序存储结构
//循环队列结构和上述非循环队列一样
注意:
队空:rear == front
队满:(rear+1) % MaxSize == front //空出一个元素内存不用
队列长度公式:(rear-front+MaxSize) % MaxSixe
//初始化一个空队列
Status InitQueue(Queue *Q){
Q->front = 0;
Q->rear = 0; //初始化均为0
return TRUE;
}
//出队列操作
//1.判断队列是否已空
//2.front后移以为
Status deQueue(SqQueue* Q, QElemType* e) {
if (Q->front == Q->rear) { //如果队列空了
return ERROR;
}
*e = Q->data[Q->front];
Q->front = (Q->front + 1) % MAXSIZE; //如果假溢出,就循环到数组前面去
return OK;
}
队列的链式存储结构
typedef struct QNode { //队列中的单个数据结点
ElemType data;
struct QNode* next;
}QNode,*QueuePtr;
typedef struct queuePtr { //队列的链表结构
QueuePtr front, rear; //对头、队尾指针
}LinkQueue;