数据结构Day4
操作受限的线性表 栈 基本概念 顺序栈 顺序栈结构 创建顺序栈 判空和判满 栈扩容 入栈 出栈 遍历 销毁栈
链式栈 队列 基本概念 顺序队列 循环顺序队列定义 循环队列的创建 循环顺序队列的判空和判满 循环顺序队列的入队 循环顺序队列的遍历 循环顺序队列的出队 循环顺序队列的销毁
链式队列 链式队列的定义 链式队列的创建 链式队列的判空 链式队列的入队 链式队列的遍历 链式队列的出队 链式队列的销毁
操作受限的线性表
在显示生活中,我们可能使用的大多是操作受限的线性容器,都属于线性结构,但是只能进行部分操作
做核酸:每次只能在队尾入队,在队头出队 汽车过隧道:先进入隧道的车,先出隧道 水杯:只能杯口加水,杯口出水 手枪弹夹:先压入的子弹最后打出 只能在指定部位进行操作的线性表我们称为操作受限的线性表 分类
栈:其插入和删除只允许出现在同一端的线性表称为栈 特点:先进后出(FILO)或者后出先进(LIFO) 队列:其插入和删除操作只允许在不同端进行的线性表称为队列 特点:先进先出(FIFO) 总结上述两个数据结构都是只允许在端点处进行操作的受限线性表
栈
基本概念
栈:操作受限的线性表,其插入和删除只能在同一端进行 特点:后进先出(LIFO)
栈顶:能够进行插入和删除的一端称为栈顶 栈底:不能被操作的一端称为栈底 分类
顺序栈
顺序栈结构
typedef int datatype;
typedef struct
{
datatype * data;
int top;
int size;
} SeqStack, * SeqStack_ptr;
创建顺序栈
SeqStack_ptr stack_create ( int size)
{
SeqStack_ptr S = ( SeqStack_ptr) malloc ( sizeof ( SeqStack) ) ;
if ( NULL == S)
{
printf ( "创建失败\n" ) ;
return NULL ;
}
S-> data = ( datatype * ) malloc ( sizeof ( datatype) * size) ;
if ( NULL == S-> data)
{
printf ( "创建失败\n" ) ;
free ( S) ;
return NULL ;
}
S-> top = - 1 ;
S-> size = size;
return S;
}
判空和判满
int stack_empty ( SeqStack_ptr S)
{
if ( NULL == S || S-> data == NULL )
{
printf ( "非法栈\n" ) ;
return - 1 ;
}
return S-> top == - 1 ;
}
int stack_full ( SeqStack_ptr S)
{
if ( NULL == S || S-> data == NULL )
{
printf ( "非法栈\n" ) ;
return - 1 ;
}
return S-> top == S-> size- 1 ;
}
栈扩容
static int stack_expend ( SeqStack_ptr S)
{
if ( NULL == S)
{
printf ( "非法栈\n" ) ;
return - 1 ;
}
datatype * temp = ( datatype * ) malloc ( sizeof ( datatype) * S-> size* 2 ) ;
if ( NULL == temp)
{
printf ( "扩容失败\n" ) ;
return - 1 ;
}
memcpy ( temp, S-> data, sizeof ( datatype) * S-> size) ;
free ( S-> data) ;
S-> data = temp;
S-> size *= 2 ;
}
入栈
int stcak_push ( SeqStack_ptr S, datatype e)
{
if ( NULL == S)
{
printf ( "非法栈\n" ) ;
return - 1 ;
}
if ( stack_full ( S) )
{
stack_expend ( S) ;
}
S-> top++ ;
S-> data[ S-> top] = e;
return 0 ;
}
出栈
int stack_pop ( SeqStack_ptr S)
{
if ( NULL == S || stack_empty ( S) )
{
printf ( "出栈失败\n" ) ;
return - 1 ;
}
datatype e = S-> data[ S-> top] ;
S-> top-- ;
return 0 ;
}
遍历
int stack_show ( SeqStack_ptr S)
{
if ( NULL == S || stack_empty ( S) )
{
return - 1 ;
}
printf ( "从栈顶到栈底的元素依次为:" ) ;
for ( int i = S-> top; i >= 0 ; i-- )
{
printf ( "%d\t" , S-> data[ i] ) ;
}
putchar ( 10 ) ;
}
销毁栈
void stack_destroy ( SeqStack_ptr S)
{
if ( NULL == S)
{
return ;
}
if ( NULL == S-> data)
{
free ( S) ;
S = NULL ;
return ;
}
free ( S-> data) ;
free ( S) ;
S = NULL ;
return ;
}
链式栈
概念:链式存储的栈 实现方式:
只进行头插和头删的单向链表就是一个链式栈 单向链表的头部就是栈顶,尾部就是栈底 只进行尾插和尾删的单向链表就是一个链式栈 单向链表的尾部就是栈顶,单向链表的头部就栈底 更加倾向于使用第一种形式,因为尾插和尾删的实现方式每一次操作都需要遍历一次整个栈
队列
基本概念
操作受限:插入和删除只能在不同端进行 队头:允许删除操作的一端称为队头 队尾:允许插入操作的一端称为队尾 分类:
顺序队列
普通顺序队列
有一个连续的存储空间存储队列 有一个变量标记队头元素的下标 有一个变量标记队尾元素的下标 普通顺序队列的弊端: 假溢满:队列中仍有空间存储变量,但是队尾到达上线后却判断队列已满无法存储新的数据 为了解决此问题,一般使用循环顺序队列
循环顺序队列定义
# define MAX 8
typedef int datatype;
typedef struct
{
datatype data[ MAX] ;
int head;
int tail;
} SeqQueue, * SeqQueue_ptr;
循环队列的创建
SeqQueue_ptr queue_create ( )
{
SeqQueue_ptr Q = ( SeqQueue_ptr) malloc ( sizeof ( SeqQueue) ) ;
if ( NULL == Q)
{
printf ( "创建失败\n" ) ;
return NULL ;
}
Q-> head = 0 ;
Q-> tail = 0 ;
return Q;
}
循环顺序队列的判空和判满
int queue_empty ( SeqQueue_ptr Q)
{
if ( NULL == Q)
{
printf ( "非法队列\n" ) ;
return - 1 ;
}
return Q-> head == Q-> tail;
}
int queue_full ( SeqQueue_ptr Q)
{
if ( NULL == Q)
{
printf ( "非法队列\n" ) ;
return - 1 ;
}
return ( Q-> tail+ 1 ) % MAX == Q-> head;
}
循环顺序队列的入队
int queue_push ( SeqQueue_ptr Q, datatype e)
{
if ( NULL == Q || queue_full ( Q) )
{
printf ( "入队失败\n" ) ;
return - 1 ;
}
Q-> data[ Q-> tail] = e;
Q-> tail = ( Q-> tail+ 1 ) % MAX;
return 0 ;
}
循环顺序队列的遍历
void queue_show ( SeqQueue_ptr Q)
{
if ( NULL == Q || queue_empty ( Q) )
{
printf ( "非法队列\n" ) ;
return ;
}
printf ( "从队头到队尾的元素分别为:" ) ;
for ( int i = Q-> head ; i != Q-> tail; i = ( i+ 1 ) % MAX)
{
printf ( "%d\t" , Q-> data[ i] ) ;
}
putchar ( 10 ) ;
}
循环顺序队列的出队
int queue_pop ( SeqQueue_ptr Q)
{
if ( NULL == Q || queue_empty ( Q) )
{
printf ( "出队失败\n" ) ;
return - 1 ;
}
Q-> head = ( Q-> head+ 1 ) % MAX;
return 0 ;
}
循环顺序队列的销毁
void queue_destroy ( SeqQueue_ptr Q)
{
if ( NULL == Q)
{
return ;
}
free ( Q) ;
Q = NULL ;
}
链式队列
实现方式:
使用单向链表进行头插尾删来实现:此时链表的头部称为队尾,链表的尾部称为队头 使用单向链表进行头删尾插来实现:此时链表的头部称为对头,链表的尾部称为队尾 以上两种方式,都涉及到对链表尾部的操作,每次进行对尾部的操作是,都需要遍历整个链表,比较麻烦 此时,我们可以设置两个指针,分别指向单向链表的头部和尾部,删除时,直接头删,插入时,使用尾指针进行尾插
链式队列的定义
typedef char datatype;
typedef struct Node
{
union
{
int len;
datatype data;
} ;
struct Node * next;
} Node, * Node_ptr;
typedef struct LinkQueue
{
Node_ptr head;
Node_ptr tail;
} LinkQueue, * LinkQueue_ptr;
链式队列的创建
LinkQueue_ptr queue_create ( )
{
LinkQueue_ptr L = ( LinkQueue_ptr) malloc ( sizeof ( LinkQueue) ) ;
if ( NULL == L)
{
printf ( "链式队列创建失败\n" ) ;
return NULL ;
}
Node_ptr H = ( Node_ptr) malloc ( sizeof ( Node) ) ;
if ( NULL == H)
{
printf ( "链表创建失败\n" ) ;
free ( L) ;
L = NULL ;
return NULL ;
}
H-> len = 0 ;
H-> next = NULL ;
L-> head = H;
L-> tail = H;
return L;
}
链式队列的判空
int queue_empty ( LinkQueue_ptr L)
{
if ( NULL == L)
{
printf ( "非法队列\n" ) ;
return - 1 ;
}
return L-> tail == L-> head;
}
链式队列的入队
int queue_push ( LinkQueue_ptr L, datatype e)
{
if ( NULL == L)
{
printf ( "链表非法\n" ) ;
return - 1 ;
}
Node_ptr p = ( Node_ptr) malloc ( sizeof ( Node) ) ;
if ( NULL == p)
{
printf ( "节点申请失败\n" ) ;
return - 1 ;
}
p-> data = e;
p-> next = NULL ;
L-> tail-> next = p;
L-> tail = p;
L-> head-> len++ ;
return 0 ;
}
链式队列的遍历
void queue_show ( LinkQueue_ptr L)
{
if ( NULL == L || NULL == L-> head || queue_empty ( L) )
{
printf ( "遍历失败\n" ) ;
return ;
}
printf ( "从队头到队尾的元素分别为:" ) ;
Node_ptr q = L-> head-> next;
while ( q)
{
printf ( "%c\t" , q-> data) ;
q = q-> next;
}
putchar ( 10 ) ;
}
链式队列的出队
int queue_pop ( LinkQueue_ptr L)
{
if ( NULL == L || NULL == L-> head || queue_empty ( L) )
{
printf ( "出队失败\n" ) ;
return - 1 ;
}
Node_ptr p = L-> head-> next;
L-> head-> next = p-> next;
free ( p) ;
p = NULL ;
L-> head-> len-- ;
if ( NULL == L-> head-> next)
{
L-> tail = L-> head;
}
return 0 ;
}
链式队列的销毁
void queue_destroy ( LinkQueue_ptr L)
{
if ( NULL == L)
{
return ;
}
if ( NULL == L-> head)
{
free ( L) ;
}
while ( ! queue_empty ( L) )
{
queue_pop ( L) ;
}
free ( L-> head) ;
free ( L) ;
L = NULL ;
}