数据结构中线性表、栈、队列代码复现(参考)
数据结构中线性表、栈、队列代码复现(参考)
〇、写在前面
若干定义
#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; /*顺序表的类型*/
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;
}
查找 SqList_Search
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;
}
二、线性表--链式存储结构 LinkList
2.1 结构体定义
typedef struct LNode
{
ElemType data;
struct LNode *next;
} LNode, *LinkList;
2.2 一些基本操作实现
插入 LinkList_Insert(重要)
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.因为是动态分配空间,所以不需要判断存储空间是否满了,要是静态分配空间需要判断空间是否满了,满了要扩容
删除 LinkList_Delete(重要)
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
查找 LinkList_Locate
注意,此例子带头节点
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;
}
创建 LinkList_Creat
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;
}
头插法需要仔细理解看一下,请看下图
初始化
插入
输出output
void Output(LinkList L)
{
LinkList p = L->next;
while (p)
{
printf("%d ", p->data);
p = p->next;
}
}
2.3 应用
LinkList_DeleteDuplicate
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;
}
三、顺序栈
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
}
四、链栈
链栈结构体定义
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;
队列的基本操作
初始化 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");
}
为什么要空出一个存储空间,主要解决假溢出的情况: