数字之间的关系可以从两个完全不同的角度进行描述

  逻辑关系(逻辑结构)是数字之间的关系,与计算机无关
  物理关系(物理结构)是存放数字的存储区之间的关系

  逻辑结构有如下几种可能
    1.集合结构:所有数字可以被看作一个整体
    2.线性结构:可以用一条有顺序的线把所有数字连起来
    3.树状结构:所有数字是从一个数字开始向一个方向扩展出来的,任何数字可以扩展出多个其他数字
    4.网状结构:任何两个数字之间可以有直接的联系,所有数字之间的联系没有统一的方向

  物理结构有如下两种
    1.顺序结构:内存里的所有存储区连续排列,数组和动态分配内存都是顺序结构的例子,顺序结构里可以给每个存储区
    指定一个编号,通过编号可以找到对应的存储区,通过编号直接找到存储区的方法叫随机访问能力
    顺序结构里存储区个数很难改变,所以容易造成内存浪费,顺序结构不适合进行插入或删除操作

    2.链式结构:由多个相互独立的存储区构成,任何两个存储区之间可以使用指针连接,链式物理结构里每个存储区都必须是一个结构体类型的存储区,它们叫做节点。
    单向线性链式物理结构中任何两个节点之间都有前后顺序(任何节点里只有一个指针)
    单向线性链式物理结构中最后一个节点里的指针必须是空指针,可以在线性链式物理结构最前面增加一个无效节点,这个节点叫做头节点,
    在线性链式物理结构的最后面增加一个无效节点,这个节点叫尾节点。
    链式物理结构本身并不支持随机访问能力,链式物理结构适合进行插入删除操作

  

/*
    动态分配节点演示
*/
#include <stdio.h>
#include <stdlib.h>
typedef struct node 
{
    int num;
    struct node *p_next;
} node;
int main() 
{
    int num = 0;
    node *p_node = NULL, *p_tmp = NULL;
    node head = {0}, tail = {0};
    //准备一个空的链式物理结构
    head.p_next = &tail;
    while (1) 
  {
        printf("请输入一个数字:");
        scanf("%d", &num);
        if (num < 0) 
     {
            break;
        }
        //动态分配一个节点
        p_node = (node *)malloc(sizeof(node));
        if (!p_node) 
     {
            continue;
        }
        p_node->num = num;
        p_node->p_next = NULL;
        for (p_tmp = &head;p_tmp != &tail;p_tmp = p_tmp->p_next) 
     {
            node *p_first = p_tmp;
            node *p_mid = p_first->p_next;
            node *p_last = p_mid->p_next;
            if (p_mid == &tail || p_mid->num > num) 
        {
                //如果p_mid指针和尾节点捆绑
                //或p_mid指针捆绑的节点里的
                //数字比新数字大就应该把新
                //节点插入到p_first和p_mid
                //中间
                p_first->p_next = p_node;
                p_node->p_next = p_mid;
                break;
            }
        }
    }
    //删除某个数字所在的节点
    printf("请输入要删除的数字:");
    scanf("%d", &num);
    for (p_tmp = &head;p_tmp != &tail;p_tmp = p_tmp->p_next) 
  {
        node *p_first = p_tmp;
        node *p_mid = p_first->p_next;
        node *p_last = p_mid->p_next;
        if (p_mid != &tail && p_mid->num == num) 
     {
            //p_mid指针没有和尾节点捆绑
            //并且p_mid指针捆绑的节点里的
            //数字就是要删除的数字
            p_first->p_next = p_last;
            free(p_mid);
            p_mid = NULL;
            break;
        }
    }
    //把有效节点里的数字按顺序显示在屏幕上
    for (p_tmp = &head;p_tmp != &tail;p_tmp = p_tmp->p_next) 
  {
        node *p_first = p_tmp;
        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");
    //释放所有有效节点
    while (head.p_next != &tail) 
    {
        node *p_first = &head;
        node *p_mid = p_first->p_next;
        node *p_last = p_mid->p_next;
        //p_mid和第一个有效节点捆绑
        p_first->p_next = p_last;
        free(p_mid);
        p_mid = NULL;
    }
    return 0;
}

 


数据结构包含一组存储区和操作该存储区的函数,函数提供了对存储区的操作方法

栈的特点:先进后出,后进先出

  入栈: 负责把一个数字放到栈里

  出栈: 负责从栈里获得一个数字

 

顺序存储的栈操作

typedef struct 
{
    int buf[SIZE];
    int num;   //有效数字个数
} stack;

//栈初始化函数
void stack_init(stack *p_stack) 
{
    p_stack->num = 0;
}
//栈的清理函数
void stack_deinit(stack *p_stack) 
{
    p_stack->num = 0;
}
//判断满的函数
int stack_full(const stack *p_stack) 
{
    return p_stack->num >= SIZE;
}
//判断空的函数
int stack_empty(const stack *p_stack) {
    return !(p_stack->num);
}
//获得有效数字个数
int stack_size(const stack *p_stack) 
{
    return p_stack->num;
}
//向栈里增加数字的函数
void stack_push(stack *p_stack, int num) 
{
    p_stack->buf[p_stack->num] = num;
    p_stack->num++;
}
//从栈里获得数字的函数(把数字从栈里删除)
int stack_pop(stack *p_stack) 
{
    int ret = p_stack->buf[p_stack->num - 1];
    p_stack->num--;
    return ret;
}
//从栈里获得数字的函数(不删除数字)
int stack_top(const stack *p_stack) 
{
    return p_stack->buf[p_stack->num - 1];
}


队列:先进先出,后进后出

 

顺序存储队列操作

 

typedef struct 
{
    int buf[SIZE];
    int head/*最前面有效数字的下标*/;
    int tail/*最后一个数字所在存储区后一个存储区的下标*/;
    /*如果队列里没有数字则head等于tail*/
} queue;

void queue_init(queue *p_queue) 
{
    p_queue->head = 0;
    p_queue->tail = 0;
}
//队列清理函数
void queue_deinit(queue *p_queue) 
{
    p_queue->head = 0;
    p_queue->tail = 0;
}
//判断满的函数
int queue_full(const queue *p_queue) 
{
    return p_queue->tail - p_queue->head >= SIZE;
}
//判断空的函数
int queue_empty(const queue *p_queue) 
{
    return p_queue->head == p_queue->tail;
}
//获得数字个数的函数
int queue_size(const queue *p_queue) 
{
    return p_queue->tail - p_queue->head;
}
//向队列里加入数字的函数
int queue_push(queue *p_queue, int num) 
{
    if(queue_full(const queue *p_queue))
    {
        return 0;    
    }
    if(p_queue->tail==SIZE)
    {
        p_queue->tail=0;
    }
    p_queue->buf[p_queue->tail] = num;
    p_queue->tail++;

    return 1;
}
//从队列里获得数字的函数(要删除数字)
int queue_pop(queue *p_queue,int *value) 
{    
    if(queue_empty(const queue *p_queue))
    {
        return 0;
    }    
    *value = p_queue->buf[p_queue->head];
    p_queue->head++;
    return 1;
}
//从队列里获得数字的函数(不删除数字)
int queue_front(const queue *p_queue,int *value) 
{
    if(queue_empty(const queue *p_queue))
    {
        return 0;
    }
    *value = p_queue->buf[p_queue->head];
    return 1;
}