数据结构的概念、堆栈
数据结构与算法
数据结构研究程序里如何使用存储区存放数字,算法研究解决一些常见问题的通用方法。数字之间的关系可以从两个完全不同的角度描述。
逻辑关系(逻辑结构)描述数字之间与计算机无关的关系;物理关系(物理结构)描述存放数字的存储区之间的关系。
逻辑结构
1.集合结构:所有的数字可以被看做一个整体
2.线性结构:如果可以用一条有顺序的线把所有数字串起来则数字之间是线性结构的
3.树状结构:所有数据都是从一个数据向同一个方向扩展出来的,任何数字可以拓展出多个其他数字
4.网状结构:任何两个数字之间都可以有直接的联系,所有数字之间的联系没有统一的方向
物理结构
1.顺序结构:所有存储区在内存里连续排列,数组和动态分配内存都是顺序结构的例子,顺序结构里每个存储区都有一个编号,可以直接通过编号找到对应的存储区。顺序结构可以不通过其他存储区直接找到需要的存储区,这种能力叫随机访问能力。顺序结构里的存储区个数很难调整,容易造成内存浪费。
2.链式结构:由多个相互独立的存储区构成,任何两个存储区之间可以用指针连接,链式物理结构里每个存储区都是一个结构体类型的存储区,他们叫做节点,单向线性链式物理结构中任何两个节点之间都有前后顺序(每个节点里只需要包含一个指针)。单向线性链式物理结构里最后一个节点里的指针必须是空指针。可以在单向线性链式物理机构中最前面的节点前再增加一个节点,这个节点叫头节点。可以在单向线性链式物理结构中最后面的节点后再增加一个节点,这个节点叫尾节点。
链式物理结构不支持随机访问能力,链式物理结构适合进行插入或删除操作,链式物理结构里的节点个数可以随时调整,
/*
*
*链式物理结构固定代码
*
* */
#include<stdio.h>
typedef struct node {
int num;
struct node *p_next;
} node;
int main (){
node node1 = {10}, node2 = {20}, node3 = {30},head = {0}, tail = {0};
node *p_node = NULL;
head.p_next = &node1;
node1.p_next = &node2;
node2.p_next = &node3;
node3.p_next = &tail;
/*
*p_node指针循环变量会从头节点开始
*向后依次捆绑每个节点,直到最后一个有效节点为止
* */
for (p_node = &head;p_node != &tail;p_node = p_node->p_next){
/*
*以下三个指针和三个相邻节点捆绑
* 其中p_first指针捆绑的节点在最前面
*p_last指针捆绑的节点在最后
*p_mid指针捆绑的节点在中间
*p_mid指针在循环过程中从第一个有效节点一直捆绑到尾节点
* */
node *p_first = p_node;
node *p_mid = p_first->p_next;
node *p_last = p_mid->p_next;
if (p_mid != &tail){
printf("%d ",p_mid->num);
}
}
printf("\n");
return 0;
}
插入删除操作
/*
*
*链式结构
*
* */
#include<stdio.h>
typedef struct node {
int num;
struct node *p_next;
} node;
int main (){
node node1 = {10}, node2 = {20}, node3 = {30},head = {0}, tail = {0};
node node4 = {15};
node *p_node = NULL;
head.p_next = &node1;
node1.p_next = &node2;
node2.p_next = &node3;
node3.p_next = &tail;
/*
*p_node指针循环变量会从头节点开始
*向后依次捆绑每个节点,直到最后一个有效节点为止
*
* */
for (p_node = &head;p_node != &tail;p_node = p_node->p_next){
/*
*以下三个指针和三个相邻节点捆绑
* 其中p_first指针捆绑的节点在最前面
*p_last指针捆绑的节点在最后
*p_mid指针捆绑的节点在中间
*p_mid指针在循环过程中从第一个有效节点一直捆绑到尾节点
*
* */
node *p_first = p_node;
node *p_mid = p_first->p_next;
node *p_last = p_mid->p_next;
if (p_mid != &tail){
printf("%d ",p_mid->num);
}
}
printf("\n");
/*
*在链式结构里
*删除数据
* */
for (p_node = &head;p_node != &tail;p_node = p_node->p_next){
node *p_first = p_node;
node *p_mid = p_first->p_next;
node *p_last = p_mid->p_next;
if (p_mid != &tail && p_mid->num == 20){
p_first->p_next = p_last;
break;
}
}
/*
*链式结构插入数据
*并保证排序从小到大
* */
for (p_node = &head;p_node != &tail;p_node = p_node->p_next){
node *p_first = p_node;
node *p_mid = p_first->p_next;
node *p_last = p_mid->p_next;
if (p_mid == &tail || p_mid->num > node4.num){
p_first->p_next = &node4;
node4.p_next = p_mid;
break;
}
}
for (p_node = &head;p_node != &tail;p_node = p_node->p_next){
node *p_first = p_node;
node *p_mid = p_first->p_next;
node *p_last = p_mid->p_next;
if (p_mid != &tail){
printf("%d ",p_mid->num);
}
}
printf("\n");
return 0;
}
数据结构
数据结构由一组存储区和相关的管理函数构成,这些函数提供了对存储区的使用方法,程序里的其他语句都只能通过这些函数使用这些存储区。
栈
栈是一种数据结构,栈可以用来存放数字,栈里的数字有前后顺序,先进入栈的数字在前,后进入栈的数字在后,每次只能从栈里获得一个数字,这个数字必须是最后一个放进去的数字,这种使用数字的方法叫后进先出。
push和pop
编写栈的时候需要提供一个函数用来向栈里加入数字,这个函数名称通常叫push,编写栈的时候需要提供一个函数用来从栈里获得数字,这个函数名称通常叫pop。
栈练习:编写程序从键盘得到多个非负整数,把他们倒序显示在屏幕上,要求用栈实现。
/*
*栈演示主函数
* */
#include<stdio.h>
#include"1stack.h"
int main (){
int num = 0;
stack stk = {0};
stack_init(&stk);
while (1){
printf("请输入一个数字:");
scanf("%d",&num);
if (num < 0){
break;
}
if (!stack_push(&stk,num)){
break;
}
}
while (1){
if (!stack_pop(&stk,&num)){
break;
}
printf("%d ",num);
}
printf("\n");
stack_deinit(&stk);
return 0;
}
/*
*栈演示头文件
* */
#ifndef _1STACK_H_
#define _1STACK_H_
typedef struct {
int buf[SIZE];
int qty;
} stack;
//栈初始化函数
void stack_init (stack *);
//栈清理函数
void stack_deinit (stack *);
//统计栈里有效数字个数
int stack_size (const stack *);
//判断栈是否已经满了
int stack_full (const stack *);
//判断栈是否为空
int stack_empty (const stack *);
//向栈里加入数字的函数
int stack_push (stack *,int);
//从栈里获得数字的函数(从栈里删除数字)
int stack_pop (stack *,int *);
//从栈里获得数字的函数(不会删除数字)
int stack_top (const stack *,int *);
#endif //_1STACK_H_
/*
*栈演示源文件
* */
#include"1stack.h"
//栈初始化函数
void stack_init (stack *p_stack){
p_stack->qty = 0;
}
//栈清理函数
void stack_deinit (stack *p_stack){
p_stack->qty = 0;
}
//统计栈里有效数字个数
int stack_size (const stack *p_stack){
return p_stack->qty;
}
//判断栈是否已经满了
int stack_full (const stack *p_stack){
return p_stack->qty >= SIZE;
}
//判断栈是否为空
int stack_empty (const stack *p_stack){
return !(p_stack->qty);
}
//向栈里加入数字的函数
int stack_push (stack *p_stack,int num){
if (p_stack->qty >= SIZE){
return 0;
}
p_stack->buf[p_stack->qty] = num;
p_stack->qty++;
return 1;
}
//从栈里获得数字的函数(从栈里删除数字)
int stack_pop (stack *p_stack,int *p_num){
if (!(p_stack->qty)){
return 0;
}
*p_num = p_stack->buf[p_stack->qty - 1];
p_stack->qty--;
return 1;
}
//从栈里获得数字的函数(不会删除数字)
int stack_top (const stack *p_stack,int *p_num){
if (!(p_stack->qty)){
return 0;
}
*p_num = p_stack->buf[p_stack->qty - 1];
return 1;
}
说明:程序执行后,根据数据输入非负数,数据输入完成时,以负数结束输入。