数据结构复习笔记 2

线性表

顺序表

顺序表是在计算机中以数组形式保存的线性表,是用一组地址连续的单元依次存储数据元素的线性数据结构。

在程序设计语言中,一维数组在内存中占用的存储空间就是一组连续的存储区域,因此,用一维数组来表示顺序表的数据存储区域.

考虑到线性表的运算有插入、删除等运算,即表长是可变的,因此,数组的容量需设计的足够大,设用:data[MAXSIZE]来表示,其中MAXSIZE是一个根据实际问题定义的足够大的整数,线性表中的数据从data[0] 开始依次顺序存放,但当前线性表中的实际元素个数可能未达到MAXSIZE多个,因此需用一个变量last 记录当前线性表中最后一个元素在数组中的位置,即last 起一个指针的作用,始终指向线性表中最后一个元素,因此,表空时last=-1。这种存储思想的具体描述可以是多样的。

从结构性上考虑,通常将data 和last 封装成一个结构作为顺序表的类型:

typedef struct {
   datatype data[MAXSIZE];
   int last;
}SeqList;

定义一个顺序表:Seqlist *L;

顺序表简单操作举例:

#include <stdio.h>
#include <stdlib.h>

#define MAXSIZE 10
typedef struct {
    int data[MAXSIZE];
    int last;
}Seqlist;
Seqlist *init_Seqlist() {
    Seqlist *L;
    L = (Seqlist *)malloc(sizeof(Seqlist));
    L->last = - 1;
    return L;
}

Seqlist *creat_Seqlist(Seqlist *L, int n) {
    for (int i=0; i<n; i++) {
        L->data[i] = i;
    }
    L->last = n - 1;
    return L;
}

int Insert_Seqlist(Seqlist *L, int i, int x) {
    if (L->last == MAXSIZE-1) {
        printf("表满\n");
        return -1;
    }
    if (i<0 || i>L->last+1) {
        printf("位置错\n");
        return 0;
    }
    for (int j=L->last; j>=i; j--) {
        L->data[j+1] = L->data[j];
    }
    L->data[i] = x;
    L->last++;
    return 1;
}

int main(void) {
    int n = 8;
    int len = n ;
    Seqlist *L;
    L = init_Seqlist();
    creat_Seqlist(L, n);

    if (Insert_Seqlist(L, 5, 88) ) {
        len++;
    }

    for (int i=0; i<n; i++) {
        printf("%d ", L->data[i]);
    }
    printf("\n%d\n", L->last);
    return 0;
}

链表
链表是由一个个结点构成的,结点定义如下:

typedef struct node
{ datatype data;
struct node *next;
} LNode,*LinkList;

定义头指针变量:
LinkList H;

栈与队列

栈和队列是在软件设计中常用的两种数据结构,它们的逻辑结构和线性表相同。其特点在于运算受到了限制:栈按“后进先出”的规则进行操作,队按“先进先出”的规则进行操作,故称运算受限制的线性表。

栈是限制在表的一端进行插入和删除的线性表。允许插入、删除的这一端称为栈顶,另一个固定端称为栈底。当表中没有元素时称为空栈。如图3.1.1 所示栈中有三个元素,进栈的顺序是a1、a2、a3,当需要出栈时其顺序为a3、a2、a1,所以栈又称为后进先出的线性表(Last In First Out),简称LIFO 表。

这里写图片描述

⑴ 栈初始化:Init_Stack(s)
初始条件:栈s 不存在
操作结果:构造了一个空栈。

⑵ 判栈空:Empty_Stack(s)
初始条件:栈s 已存在
操作结果:若s 为空栈返回为1,否则返回为0。

⑶ 入栈: Push_Stack(s,x)
初始条件:栈s 已存在
操作结果:在栈s 的顶部插入一个新元素x, x 成为新的栈顶元素。栈发生变化。

⑷ 出栈:Pop_Stack(s)
初始条件:栈s 存在且非空
操作结果:栈s 的顶部元素从栈中删除,栈中少了一个元素。栈发生变化。
⑸ 读栈顶元素:Top_Stack(s)
初始条件:栈s 存在且非空
操作结果:栈顶元素作为结果返回,栈不变化。

栈—栈的存储实现和运算实现

由于栈是运算受限的线性表,因此线性表的存储结构对栈也是适用的,只是操作不同而已。

顺序栈

利用顺序存储方式实现的栈称为顺序栈。类似于顺序表的定义,栈中的数据元素用一个预设的足够长度的一维数组来实现:datatype data[MAXSIZE],栈底位置可以设置在数组的任一个端点,而栈顶是随着插入和删除而变化的,用一个int top 来作为栈顶的指针,指明当前栈顶的位置,同样将data 和top 封装在一个结构中,顺序栈的类型描述如下:

#define MAXSIZE 1024
typedef struct
{datatype data[MAXSIZE];
int top;
}SeqStack

定义一个指向顺序栈的指针:
SeqStack *s;
通常0 下标端设为栈底,这样空栈时栈顶指针top=-1; 入栈时,栈顶指针加1,即s->top++; 出栈时,栈顶指针减1,即s->top–。栈操作的示意图如图3.2 所示。

这里写图片描述

图(a)是空栈,图(c)是A、B、C、D、E 5 个元素依次入栈之后,图(d)是在图(c)之后E、D 相继出栈,此时栈中还有3 个元素,或许最近出栈的元素D、E 仍然在原先的单元存储着,但top 指针已经指向了新的栈顶,则元素D、E 已不在栈中了,通过这个示意图要深刻理解栈顶指针的作用。

在上述存储结构上基本操作的实现如下:

⑴ 置空栈:首先建立栈空间,然后初始化栈顶指针。

SeqStack *Init_SeqStack()
{ SeqStack *s;
s=malloc(sizeof(SeqStack));
s->top= -1; return s;
}

⑵ 判空栈

int Empty_SeqStack(SeqStack *s)
{ if (s->top= = -1) return 1;
else return 0;
}

⑶ 入栈

int Push_SeqStack (SeqStack *s, datatype x)
{
    if (s->top= =MAXSIZE-1) return 0; /*栈满不能入栈*/
    else { 
        s->top++;
        s->data[s->top]=x;
        return 1;
    }
}

⑷ 出栈

int Pop_SeqStack(SeqStack *s, datatype *x)
{ if (Empty_SeqStack ( s ) ) return 0; /*栈空不能出栈*/
else { *x=s->data[s->top];
s->top--; return 1; } /*栈顶元素存入*x,返回*/
}

⑸ 取栈顶元素

datatype Top_SeqStack(SeqStack *s)
{ if ( Empty_SeqStack ( s ) ) return 0; /*栈空*/
else return (s->data[s->top] );
}

以下几点说明:
1. 对于顺序栈,入栈时,首先判栈是否满了,栈满的条件为:s->top= =MAXSIZE-1,栈满时,不能入栈; 否则出现空间溢出,引起错误,这种现象称为上溢。
2. 出栈和读栈顶元素操作,先判栈是否为空,为空时不能操作,否则产生错误。通常栈空时常作为一种控制转移的条件。

链栈
用链式存储结构实现的栈称为链栈。通常链栈用单链表表示,因此其结点结构与单链表的结构相同,在此用LinkStack 表示,即有:

typedef struct node
{ datatype data;
struct node *next;
}StackNode,* LinkStack;

说明top 为栈顶指针: LinkStack top ;因为栈中的主要运算是在栈顶插入、删除,显然在链表的头部做栈顶是最方便的,而且没有必要象单链表那样为了运算方便附加一个头结点。通常将链栈表示成图3.3 的形式。

这里写图片描述

链栈基本操作的实现如下:

⑴ 置空栈

LinkStack Init_LinkStack()
{ return NULL;
}

⑵ 判栈空

int Empty_LinkStack(LinkStack top )

{ if(top==-1return 1;
else return 0;
}

⑶ 入栈

LinkStack Push_LinkStackLinkStack top, datatype x)
{ StackNode *s;
s=malloc(sizeof(StackNode));
s->data=x;
s->next=top;
top=s;
return top;
}

⑷ 出栈

LinkStack Pop_LinkStack (LinkStack top, datatype *x)
{ StackNode *p;
if (top= =NULLreturn NULL;
else { *x = top->data;
p = top;
top = top->next;
free (p);
return top;
}
}

队列

前面所讲的栈是一种后进先出的数据结构,而在实际问题中还经常使用一种“先进先出” (FIFO—First In First Out)的数据结构:即插入在表一端进行,而删除在表的另一端进行,我们将这种数据结构称为队或队列,把允许插入的一端叫队尾(rear) ,把允许删除的一端叫队头(front)。如图3.11 所示是一个有5 个元素的队列。入队的顺序依次为a1、a2 、a3 、a4 、a5 ,出队时的顺序将依然是a1、a2 、a3 、a4 、a5 。

这里写图片描述

显然,队列也是一种运算受限制的线性表,所以又叫先进先出表。

在日常生活中队列的例子很多,如排队买东西,排头的买完后走掉,新来的排在队尾。在队列上进行的基本操作有:

⑴ 队列初始化:Init_Queue(q)
初始条件: 队q 不存在。
操作结果: 构造了一个空队。

⑵ 入队操作: In_Queue(q,x),
初始条件: 队q 存在。
操作结果: 对已存在的队列q,插入一个元素x 到队尾,队发生变化。

⑶ 出队操作: Out_Queue(q,x)
初始条件: 队q 存在且非空
操作结果: 删除队首元素,并返回其值,队发生变化。

⑷ 读队头元素:Front_Queue(q,x)
初始条件: 队q 存在且非空
操作结果: 读队头元素,并返回其值,队不变;

⑸ 判队空操作:Empty_Queue(q)
初始条件: 队q 存在
操作结果: 若q 为空队则返回为1,否则返回为0。

队列的存储实现及运算实现

与线性表、栈类似,队列也有顺序存储和链式存储两种存储方法。

1、顺序队

顺序存储的队称为顺序队。因为队的队头和队尾都是活动的,因此,除了队列的数据区外还有队头、队尾两个指针。顺序队的类型定义如下:

define MAXSIZE 1024 /*队列的最大容量*/
typedef struct
{datatype data[MAXSIZE]; /*队员的存储空间*/
int rear,front; /*队头队尾指针*/
}SeQueue;

定义一个指向队的指针变量:
SeQueue *sq;
申请一个顺序队的存储空间:
sq=malloc(sizeof(SeQueue));
队列的数据区为:

sq->data[0]---sq->data[MAXSIZE -1]
队头指针:sq->front
队尾指针:sq->rear

设队头指针指向队头元素前面一个位置,队尾指针指向队尾元素(这样的设置是为了某些运算的方便,并不是唯一的方法)。

置空队则为:sq->front=sq->rear=-1;

在不考虑溢出的情况下,入队操作队尾指针加1,指向新位置后,元素入队。操作如下:

sq->rear++;
sq->data[sq->rear]=x; /*原队头元素送x 中*/

在不考虑队空的情况下,出队操作队头指针加1,表明队头元素出队。操作如下:

sq->front++;
x=sq->data[sq->front];

队中元素的个数:m=(sq->rear)-(q->front);
队满时:m= MAXSIZE; 队空时:m=0。

按照上述思想建立的空队及入队出队示意图如图3.12 所示,设MAXSIZE=10。从图中可以看到,随着入队出队的进行,会使整个队列整体向后移动,这样就出现了图3.12(d)中的现象:队尾指针已经移到了最后,再有元素入队就会出现溢出,而事实上此时队中并未真的“满员”,这种现象为“假溢出”,这是由于“队尾入队头出”这种受限制

这里写图片描述

的操作所造成。解决假溢出的方法之一是将队列的数据区data[0..MAXSIZE-1]看成头尾相接的循环结构,头尾指针的关系不变,将其称为“循环队”,“循环队”的示意图如图3.13所示。

这里写图片描述

因为是头尾相接的循环结构,入队时的队尾指针加1 操作修改为:
sq->rear=(sq->rear+1) % MAXSIZE;
出队时的队头指针加1 操作修改为:
sq->front=(sq->front+1) % MAXSIZE;
设MAXSIZE=10,图3.14 是循环队列操作示意图。

这里写图片描述

链队

链式存储的队称为链队。和链栈类似,用单链表来实现链队,根据队的FIFO 原则,为了操作上的方便,我们分别需要一个头指针和尾指针,如图3.15 所示。

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值