个人笔记:算法讲座4.2——快速求差

本文仅供参考学习使用,谢谢

问题描述:

Bob也为Alice准备了一道智力游戏题。给出一个整数序列,每次Bob给出位置号I和j,要Alice快速报出这两个位置之间,最大数与最小数的差值。聪明如你,要怎么记录这些数,才能最快给出答案呢?

  • 输入:

第一行有两个数,分别表示整数序列的长度M和查询次数N.
第二行是长度为M的整数序列。
从第三行开始N行,每行两个数,表示查询的I、j位置。

  • 输出:

共N行,分别为每次查询的结果。

思路分析:

  • 如何创建一颗线段树:

对于一颗线段树,在递归创建这颗二叉树的时候每次遇到叶子结点,该一个叶子结点储存一维数组的第L(=R)个节点,同时叶子结点的左后孩子为空

如果不是叶子结点,则递归创建他的
左子树CreateTree(L,(L+R)/2)
右子树CreateTree((L+R)/2+1,R)

  • 如何将叶子结点的极值和叶子结点产生联系:

对于每一个节点,只有两种情况:

  1. 无孩子结点,即叶子结点

此时该节点的MAX或MIN 等于 它本身的MAX或MIN

  1. 有孩子结点,且必定有两个孩子

此时该节点的MAX或MIN 等于 他的左孩子或右孩子 的 较大的MAX或 较小的MIN

  • 如何找到区间内的最大值或最小值:

对于区间[L,R]中的最大值和最小值有四种情况:

返还每段的最大值即可

  1. T->left== L && T->right== R
    在这里插入图片描述
  2. T->left<=L && R<=(T->left+T->right)/2在这里插入图片描述
  3. (T->left+T->right)/2+1<=L && R<=T->right
    在这里插入图片描述
  4. L<=(T->left+T->right)/2 && (T->left+T->right)/2+1<=R
    在这里插入图片描述

算法描述:

  • struct Node结构体:

包含 线段的左右端下标,
指向结点的左右孩子的两个指针,
该段区间的最大值和最小值。

  • CreateTree函数:

首先申请一个Node型的节点的储存空间,将最开始输入的L、R值赋值给跟结点的左右端下标,
如果L==R的话说明当前结点是叶子结点,需要输入一个值赋给该节点的最值,且该节点的最大最小值相等
如果L<R说明当前结点是非叶子结点,需要递归生成其左子树和右子树

  • bulid_max函数:

递归到该线段树的叶子结点的双亲结点处,然后将其最大最小值设置为叶子结点中较大的最大值或较小的最小值。
如果递归到的结点的右孩子没有孩子结点,则说明这个右孩子是叶子结点,其最值相等不用改变

补充说明:每一个跟结点的三层以内只有两种情况
在这里插入图片描述

  • find_max函数:

有三种情况,每一种情况只需要return当前线段的最值即可

  1. T->left== L && T->right== R
  2. T->left<=L && R<=(T->left+T->right)/2
  3. (T->left+T->right)/2+1<=L && R<=T->right

测试数据:

  • 数据1:

5 3
3 2 7 9 10
1 5
2 3
3 5

  • 结果1:

8
5
3

  • 数据2:

12 3
3 5 1 8 4 2 10 9 2 6 9 15
1 3
5 10
8 11

  • 结果2:

4
8
7

//
//  main.c
//  线段树
//
//  Created by xxc on 2020/3/22.
//  Copyright © 2020 xxc. All rights reserved.
//

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

typedef struct Node {
    int left,right;//left->线段最左侧的数值,right->线段最右侧的数值
    struct Node * lchild;//指向该节点的左孩子(线段[L,(L+R)/2])
    struct Node * rchild;//指向该节点的右孩子(线段[(L+R)/2+1,R])
    int max;
    int min;
}Node;

Node * CreateTree(int L, int R){
    Node * T;
    T = (Node *)malloc(sizeof(Node));
    if(!T) exit(-1);
    
    T->left = L;
    T->right = R;

    if (L==R) {
        T->lchild=NULL;
        T->rchild=NULL;
        
        int value=0;
        scanf("%d",&value);
        T->max=value;
        T->min=value;
        
    } else {
        T->lchild=CreateTree(L,(L+R)/2);
        T->rchild=CreateTree((L+R)/2+1,R);
    }
    
    return T;
}

void bulid_max(Node * T){//每一个节点,要么有两个孩子节点,要么一个都没有
    if (T->lchild!=NULL) {
        bulid_max(T->lchild);
        bulid_max(T->rchild);
        T->max=T->lchild->max>T->rchild->max?T->lchild->max:T->rchild->max;
    }else if (T->lchild==NULL){
        T->max=T->max;
    }
}

void bulid_min(Node * T){//每一个节点,要么有两个孩子节点,要么一个都没有
    if (T->lchild!=NULL) {
        bulid_min(T->lchild);
        bulid_min(T->rchild);
        T->min=T->lchild->min<T->rchild->min?T->lchild->min:T->rchild->min;
    }else if (T->lchild==NULL){
        T->min=T->min;
    }
}

int find_max(int L, int R, Node * T){
    if (T->left==L && T->right==R) {
        return T->max;
    }else if (T->left<=L && R<=(T->left+T->right)/2){
        return find_max(L, R, T->lchild);
    }else if ((T->left+T->right)/2+1<=L && R<=T->right){
        return find_max(L, R, T->rchild);
    }else if (L<=(T->left+T->right)/2 && (T->left+T->right)/2+1<=R){
//        find_max(L, (T->left+T->right)/2, T->lchild);
//        find_max((T->left+T->right)/2+1, R, T->rchild);
        return find_max(L, (T->left+T->right)/2, T->lchild)>find_max((T->left+T->right)/2+1, R, T->rchild)?find_max(L, (T->left+T->right)/2, T->lchild):find_max((T->left+T->right)/2+1, R, T->rchild);
    }
    return 0;
}

int find_min(int L, int R, Node * T){
    if (T->left==L && T->right==R) {
        return T->min;
    }else if (T->left<=L && R<=(T->left+T->right)/2){
        return find_min(L, R, T->lchild);
    }else if ((T->left+T->right)/2+1<=L && R<=T->right){
        return find_min(L, R, T->rchild);
    }else if (L<=(T->left+T->right)/2 && (T->left+T->right)/2+1<=R){
//        find_min(L, (T->left+T->right)/2, T->lchild);
//        find_min((T->left+T->right)/2+1, R, T->rchild);
        return find_min(L, (T->left+T->right)/2, T->lchild)<find_min((T->left+T->right)/2+1, R, T->rchild)?find_min(L, (T->left+T->right)/2, T->lchild):find_min((T->left+T->right)/2+1, R, T->rchild);
    }
    return 0;
}


void check_tree(Node * T){
    if (T != NULL) {
        printf("%d  %d  %d  %d\n",T->left,T->right,T->max,T->min);
        check_tree(T->lchild);
        check_tree(T->rchild);
    }
}

int main(int argc, const char * argv[]) {
    int L=1,R=0,N=0;
    Node * T=NULL;
    scanf("%d%d",&R,&N);
    T=CreateTree(L, R);
    
    bulid_max(T);
    bulid_min(T);
//    check_tree(T);
    
    int end[N];
    
    for (int i=0; i<N; i++) {
        int L1=0,R1=0;
        scanf("%d%d",&L1,&R1);
        int value=find_max(L1, R1, T)-find_min(L1, R1, T);
        end[i]=value;
    }
    
    for (int i=0; i<N; i++) {
        printf("%d\n",end[i]);
    }
    
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值