算导实验四 区间树上重叠区间查找

实验要求

对红黑树算法进行修改,使其成为一颗区间数,并实现区间树上的重叠区间查找算法。

程序输入:

              1、生成区间树

                     文件名: insert.txt

                     文件格式:第一行为待插入数据的个数,第二行之后每一行表示一个区间

              注:1)初始时树应为空。

                     2)按顺序插入,例如,对于下图的数据,插入顺序应为 [50, 60],[20, 25],                         [70, 90]

 

                2、待查询区间应由控制台输入

                       程序输出: 控制台直接打印查找结果(如果存在多个区间与query重叠, 需输出                                            所有与query重叠的区间)。

目录

1.实验内容

2.实验目的

3.区间树的数据结构

4.源码 + 注释

4.1区间树的构造

4.2 Max的维护

4.3 查找算法

5.算法测试结果

6.实验过程中遇到的困难及收获

 7.实验源代码


 

1.实验内容

掌握区间树上重叠区间的查找算法。

2.实验目的

对实验三的红黑树算法进行修改,使其成为一棵区间树,并实现区间树上的重叠区间查找算法。

3.区间树的数据结构

public class IntervalTreeNode
{
    private IntervalTreeNode left; // 左子

    private IntervalTreeNode right; // 右子

    private IntervalTreeNode parent; // 父结点

    private int color; // 颜色,黑色为1,红色为0

    private int max; // max=max(high, left.max, right.max])

    private int low; // 区间左端点

    private int high; // 区间右端点
}

上述代码所示为区间树的结点结构定义。每个结点具有指向父结点的指针parent ;指向左右孩子的指针left,right ;结点的颜色color ;区间的左右端点low,high ;以及以该结点为根的树中的右端点的最大值max

4.源码 + 注释

4.1区间树的构造

    //插入 外部接口
    public void insert(int[] interval)

    //插入 内部接口
    private void insert(IntervalTreeNode node)

    //维护
    private void insertFixup(IntervalTreeNode node)

上述代码为区间树构造的方法,其与红黑树的插入结点算法相同。

4.2 Max的维护

当我们对结点进行左旋或右旋操作时,由于改变了结点的相对位置,因此会导致max 发生改变,此时需要对max 做维护,使其仍然满足区间树的性质。

    //左旋
    private void leftRotate(IntervalTreeNode x)
    {
        //Step1:y指向x的右孩子
        //Step2:y.left连接到x.right
        //Step3:x父结点的left或right指向y
        //Step4:x连接到y.left

        //Step5:更新max信息
        updateMax(x);
        updateMax(y);
    }

    //右旋
    private void rightRotate(IntervalTreeNode y)
    {
        //Step1:x指向y的左孩子
        //Step2:x.right连接到y.left
        //Step3:y父结点的left或right指向x
        //Step4:y.left连接到x

        //Step5:更新max信息
        updateMax(x);
        updateMax(y);
    }

上述代码为左旋与右旋算法做的修改,在完成连接操作后,对旋转的结点及其左或右孩子的max 信息进行维护,具体更新max 的方法如下所示。若结点没有左右孩子,则max 值为结点的high 值;若只有一个孩子,则max 值为该结点的high 和孩子的max 中的较大值;若有两个孩子,则max 值为该结点的high ,左右孩子的max 中的最大值。

    //更新max
    private void updateMax(IntervalTreeNode node)
    {
        if (node.getLeft() == null && node.getRight() == null) node.setMax(node.getHigh());
        else if (node.getLeft() == null) node.setMax(Math.max(node.getHigh(), node.getRight().getMax()));
        else if (node.getRight() == null) node.setMax(Math.max(node.getHigh(), node.getLeft().getMax()));
        else node.setMax(Math.max(node.getHigh(), Math.max(node.getLeft().getMax(), node.getRight().getMax())));
    }

4.3 查找算法

    //寻找
    public List<IntervalTreeNode> intervalSearch(int[] interval)
    {
        //寻找重叠的第一个节点
        IntervalTreeNode x = root;
        while (x != null && !isOverlap(interval, new int[]{x.getLow(), x.getHigh()}))
        {
            if ((x.getLeft() != null) && x.getLeft().getMax() >= interval[0]) x = x.getLeft();
            else x = x.getRight();
        }
        if (x == null)
        {
            System.out.println("No overlap intervals");
            return null;
        }

        //一旦找到便遍历以该节点为根的子树,寻找所有结点
        return levelOrder(interval, x);
    }

上述代码为区间树上的重叠区间查找算法。xroot 开始寻找重叠的第一个节点。如果x 不为空并且要查找的区间与x 区间不重合的话,如果xmax 值比要查找区间的左端点大,则向左走;反之向右走。循环退出时若xnull ,则说明在这个区间树中没有与待查找区间重叠的区间,若x 不为空,则遍历以x 为根的子树来寻找所有与待查找区间重叠的区间。

5.算法测试结果

输入查找区间[10,30] 返回结果如下

输入查找区间[100,110] 返回结果如下

6.实验过程中遇到的困难及收获

1、区间树是红黑树的扩张,大部分操作跟红黑树相同或相似,但在对附加信息max 进行维护时,左旋和右旋的过程中要对max 单独进行更新维护。

2、通过测试证明查找算法是正确有效的。

3、对于任意一个具有n 个结点的区间树,我们最多需要循环logn+2 次,(即从根节点查找到叶子结点),每次循环都是常数时间,故在最坏情况下时间复杂度为O(log n) 。在一个比较差的搜索中,假设每次我们寻找的区间都是叶子结点,在这个情况下的时间复杂度为Ω(log n) 。所以区间树上的重叠区间查找算法的时间复杂度为Ɵ(log n)

 7.实验源代码

IntervalTreeNode.java

/*
 * @Title:
 * @Package
 * @Description:区间树结点
 * @author yangf257
 * @date 2021/11/29  16:21
 */

public class IntervalTreeNode
{
    private IntervalTreeNode left; // 左子

    private IntervalTreeNode right; // 右子

    private IntervalTreeNode parent; // 父结点

    private int color; // 颜色,黑色为1,红色为0

    private int max; // max=max(high, left.max, right.max])

    private int low; // 区间左端点

    private int high; // 区间右端点

    public IntervalTreeNode(int color, int low, int high, int max, IntervalTreeNode left, IntervalTreeNode right, IntervalTreeNode parent)
    {
        this.color = color;
        this.low = low;
        this.high = high;
        this.max = max;
        this.left = left;
        this.right = right;
        this.parent = parent;

        // TODO 自动生成构造函数存根
    }

    public IntervalTreeNode(int color, int low, int high)
    {
        this.color = color;
        this.low = low;
        this.high = high;
        this.max = high;
        this.left = null;
        this.right = null;
        this.parent = null;
    }

    public IntervalTreeNode getLeft()
    {
        return left;
    }

    public void setLeft(IntervalTreeNode left)
    {
        this.left = left;
    }

    public IntervalTreeNode getRight()
    {
        return right;
    }

    public void setRight(IntervalTreeNode right)
    {
        this.right = right;
    }

    public IntervalTreeNode getParent()
    {
        return parent;
    }

    public void setParent(IntervalTreeNode parent)
    {
        this.parent = parent;
    }

    public int getColor()
    {
        return color;
    }

    public void setColor(int color)
    {
        this.color = color;
    }

    public int getMax()
    {
        return max;
    }

    public void setMax(int max)
    {
        this.max = max;
    }

    public int getLow()
    {
        return low;
    }

    public int getHigh()
    {
        return high;
    }

    @Override
    public String toString()
    {
        return "[" + this.low + ", " + this.high + "]";
    }
}

IntervalTree.java

/*
 * @Title:
 * @Package
 * @Description:区间树类
 * @author yangf257
 * @date 2021/11/29  16:37
 */

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

public class IntervalTree
{
    private static final int BLACK = 1;
    private static final int RED = 0;
    private IntervalTreeNode root;

    //寻找
    public List<IntervalTreeNode> intervalSearch(int[] interval)
    {
        //寻找重叠的第一个节点
        IntervalTreeNode x = root;
        while (x != null && !isOverlap(interval, new int[]{x.getLow(), x.getHigh()}))
        {
            if ((x.getLeft() != null) && x.getLeft().getMax() >= interval[0]) x = x.getLeft();
            else x = x.getRight();
        }
        if (x == null)
        {
            System.out.println("No overlap intervals");
            return null;
        }

        //一旦找到便遍历以该节点为根的子树,寻找所有结点
        return levelOrder(interval, x);
    }

    private boolean isOverlap(int[] interval1, int[] interval2)
    {
        return (interval1[1] >= interval2[0]) && (interval2[1] >= interval1[0]);
    }

    //插入 外部接口
    public void insert(int[] interval)
    {
        IntervalTreeNode node = new IntervalTreeNode(RED, interval[0], interval[1]);
        insert(node);
    }

    //插入 内部接口
    private void insert(IntervalTreeNode node)
    {
        IntervalTreeNode parent = null;
        IntervalTreeNode x = root;
        while (x != null)
        {
            parent = x;
            if (node.getLow() < x.getLow()) x = x.getLeft();
            else x = x.getRight();
        }

        node.setParent(parent);
        if (parent == null) root = node;
        else
        {
            if (node.getLow() < parent.getLow()) parent.setLeft(node);
            else parent.setRight(node);
        }
        insertFixup(node);
    }

    //维护
    private void insertFixup(IntervalTreeNode node)
    {
        IntervalTreeNode parent, grandparent;
        while ((parent = node.getParent()) != null && parent.getColor() == RED)
        {
            grandparent = parent.getParent();
            //case 1,2,3
            if (parent == grandparent.getLeft())    //case 1,2,3:父结点是祖父结点的left
            {
                IntervalTreeNode uncle = grandparent.getRight();  //叔叔结点
                if (uncle != null && uncle.getColor() == RED)    //case 1:叔叔为RED
                {
                    uncle.setColor(BLACK);
                    parent.setColor(BLACK);
                    grandparent.setColor(RED);
                    node = grandparent;
                }
                else    //case 2,3:叔叔为BLACK
                {
                    if (node == parent.getRight())    //case 2:node是父结点的right
                    {
                        IntervalTreeNode tmp;
                        leftRotate(parent);
                        tmp = parent;
                        parent = node;
                        node = tmp;
                    }
                    //case 3:node是父结点的left
                    parent.setColor(BLACK);
                    grandparent.setColor(RED);
                    rightRotate(grandparent);
                }
            }
            //case 4,5,6:父结点是祖父结点的right
            else
            {
                IntervalTreeNode uncle = grandparent.getLeft();
                if (uncle != null && uncle.getColor() == RED)   //case 4:叔叔为RED
                {
                    uncle.setColor(BLACK);
                    parent.setColor(BLACK);
                    grandparent.setColor(RED);
                    node = grandparent;
                }
                else    //case 5,6:叔叔为BLACK
                {
                    if (node == parent.getLeft())    //case 5:node是父结点的left
                    {
                        IntervalTreeNode temp;
                        rightRotate(parent);
                        temp = node;
                        node = parent;
                        parent = temp;
                    }
                    //case 6:node是父结点的right
                    parent.setColor(BLACK);
                    grandparent.setColor(RED);
                    leftRotate(grandparent);
                }
            }
        }
        root.setColor(BLACK);
    }

    //左旋
    private void leftRotate(IntervalTreeNode x)
    {
        //Step1:y指向x的右孩子
        IntervalTreeNode y = x.getRight();

        //Step2:y.left连接到x.right
        x.setRight(y.getLeft());
        if (y.getLeft() != null) y.getLeft().setParent(x);

        //Step3:x父结点的left或right指向y
        y.setParent(x.getParent());
        if (x.getParent() == null) root = y;    // 如果 “x的父亲” 是空节点,则将y设为根节点
        else if (x == x.getParent().getLeft()) x.getParent().setLeft(y);    // 如果 x是它父节点的左孩子,则将y设为“x的父节点的左孩子”
        else x.getParent().setRight(y); // 如果 x是它父节点的右孩子,则将y设为“x的父节点的右孩子”

        //Step4:x连接到y.left
        y.setLeft(x);
        x.setParent(y);

        //Step5:更新max信息
        updateMax(x);
        updateMax(y);
    }

    //右旋
    private void rightRotate(IntervalTreeNode y)
    {
        //Step1:x指向y的左孩子
        IntervalTreeNode x = y.getLeft();

        //Step2:x.right连接到y.left
        y.setLeft(x.getRight());
        if (x.getRight() != null) x.getRight().setParent(y);

        //Step3:y父结点的left或right指向x
        x.setParent(y.getParent());
        if (y.getParent() == null) root = x;    // 如果 “y的父亲” 是空节点,则将x设为根节点
        else if (y == y.getParent().getRight()) y.getParent().setRight(x);  // 如果 y是它父节点的右孩子,则将x设为“y的父节点的右孩子”
        else y.getParent().setLeft(x);   // (y是它父节点的左孩子) 将x设为“x的父节点的左孩子”

        //Step4:y.left连接到x
        x.setRight(y);
        y.setParent(x);

        //Step5:更新max信息
        updateMax(x);
        updateMax(y);
    }

    //层序遍历
    private List<IntervalTreeNode> levelOrder(int[] interval, IntervalTreeNode root)
    {
        List<IntervalTreeNode> overlapList = new ArrayList<>();
        Queue<IntervalTreeNode> queue = new LinkedList<>();
        queue.add(root);
        IntervalTreeNode curr;
        while (!queue.isEmpty())
        {
            curr = queue.poll();
            if (isOverlap(interval, new int[]{curr.getLow(), curr.getHigh()})) overlapList.add(curr);
            if (curr.getLeft() != null) queue.add(curr.getLeft());
            if (curr.getRight() != null) queue.add(curr.getRight());
        }

        return overlapList;
    }

    //更新max
    private void updateMax(IntervalTreeNode node)
    {
        if (node.getLeft() == null && node.getRight() == null) node.setMax(node.getHigh());
        else if (node.getLeft() == null) node.setMax(Math.max(node.getHigh(), node.getRight().getMax()));
        else if (node.getRight() == null) node.setMax(Math.max(node.getHigh(), node.getLeft().getMax()));
        else node.setMax(Math.max(node.getHigh(), Math.max(node.getLeft().getMax(), node.getRight().getMax())));
    }
}

IntervalSearchTest.java

/*
 * @Title:
 * @Package
 * @Description:
 * @author yangf257
 * @date 2021/11/29  19:06
 */

import org.junit.Test;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class IntervalSearchTest
{
    @Test
    public void test1()
    {
        //课本案例
        int[][] interval = {{0, 3}, {5, 8}, {6, 10}, {8, 9}, {15, 23}, {16, 21}, {17, 19}, {19, 20}, {25, 30}, {26, 26}};
        IntervalTree intervalTree = new IntervalTree();
        for (int i = 0; i < interval.length; i++)
        {
            intervalTree.insert(interval[i]);
        }

        List<IntervalTreeNode> list = intervalTree.intervalSearch(new int[]{3, 7});
        for (int i = 0; i < list.size(); i++)
        {
            System.out.println(list.get(i).toString());
        }
    }

    public static void main(String[] args)
    {
        int[][] interval = input();
        IntervalTree intervalTree = new IntervalTree();
        for (int i = 0; i < interval.length; i++)
        {
            intervalTree.insert(interval[i]);
        }

        Scanner scanner = new Scanner(System.in);
        int[] target = new int[2];
        System.out.println("Inter the interval: ");
        for (int i = 0; i < 2; i++)
        {
            target[i] = scanner.nextInt();
        }

        List<IntervalTreeNode> list = intervalTree.intervalSearch(target);
        if (list == null) return;
        for (int i = 0; i < list.size(); i++)
        {
            System.out.println(list.get(i).toString());
        }
    }

    private static int[][] input()
    {
        int length = 0;
        String intervalstr = "";
        String filePath = "C:\\Users\\HP\\Desktop\\算法\\Experiment\\ExperimentCode\\4.IntervalSearch\\src\\insert.txt";
        try
        {
            File file = new File(filePath);
            if (file.isFile() && file.exists())
            {
                InputStreamReader isr = new InputStreamReader(new FileInputStream(file), "utf-8");
                BufferedReader br = new BufferedReader(isr);
                String lineTxt = null;
                int count = 0;
                while ((lineTxt = br.readLine()) != null)
                {
                    count++;
                    if (count == 1) length = Integer.parseInt(lineTxt);
                    else
                    {
                        intervalstr += lineTxt;
                        intervalstr += " ";
                    }
                }
                br.close();
            }
            else
            {
                System.out.println("文件不存在!");
            }
        } catch (Exception e)
        {
            System.out.println("文件读取错误!");
        }

//        System.out.println(intervalstr);

        List<Integer> array = new ArrayList<>();
        int num = 0;
        for (int i = 0; i < intervalstr.length(); i++)
        {
            if (intervalstr.charAt(i) != ' ')
            {
                if (num != 0) num *= 10;
                num += intervalstr.charAt(i) - '0';
            }
            else
            {
                array.add(num);
//                System.out.println(num);
                num = 0;
            }
        }
        array.add(num);

        int[][] interval = new int[length][2];
        for (int i = 0; i < interval.length; i++)
        {
            for (int j = 0; j < 2; j++)
            {
                interval[i][j] = array.get(i * 2 + j);
            }
        }

//        for (int i = 0; i < interval.length; i++)
//        {
//            for (int j = 0; j < 2; j++)
//            {
//                System.out.println(interval[i][j]);
//            }
//        }
        return interval;
    }
}

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值