面向对象的数据结构 – 以 Heap 为例

面向对象的数据结构 – 以 Heap 为例

之前学数据结构时都是使用 C/C++ 实现,习惯了面向过程的方式再使用面向对象还有一点点不知所措。

借着 操作系统短进程优先算法 的机会第一次尝试使用 Java 实现了一个简单的数据结构。

这里实现的 Heap 只包含了最基础的排序,没有添加任意删除等操作。

Motivation

在此之前,有个还没有公开发表的 flag,想在这学期结束前后写一个运行在 C51/STM32 上的简易的操作系统(尽管这学期才过了四分之一,但我已经可以预见这个 flag 要鸽了),因此最初的选择就是用 C 写。

可经过复习后发现想要实现操作系统级的调度算法有些许困难,如果只是实现算法思想,语言只是工具平台而已。用 C 当然可以,想着写习惯了面向过程还是应该试一下面向对象的方式。

Realization

Heap 的实现还是很简单的,这里整理了一下以前的笔记:

定义

堆是一颗完全二叉树,树中的每一个结点都大于(小于)等于其父亲节点的值,由此引出两种堆:

  • 大根堆:父亲结点的值大于或等于左右节点
  • 小根堆:父亲结点的值小于或等于左右结点

存储方式

由于堆是一个完全二叉树,可以使用数组进行存储,减少空间的浪费。(但也可以使用链式存储)
因为 Java 的链式存储没有写过,这里直接使用了 ArrayList 替代数组进行存储

维护

up 和 down 是两个基础的维护操作,指的是元素在树中的深度变化。两个操作的最坏时间复杂度均为$O(log h)$

在 up 和 down 操作里的 swapcompare 是两个自定义的方法,这里的 PCB 是一个自定义的对象,直接沿用的操作系统实验中的代码片段,具体定义可以参考这里:Operating Systems: Lab 1 SPF(Shortest-Process-First)

public void swap(int u, int v)
{
    PCB Temp = heap.get(u);
    heap.set(u, heap.get(v));
    heap.set(v, Temp);
}
public int compare(int u, int v) 
{
    PCB pcb1 = this.heap.get(u);
    PCB pcb2 = this.heap.get(v);

    if (pcb1.getRemaining() == pcb2.getRemaining()) 
    {
        if(pcb1.getQueueID() > pcb2.getQueueID())
        {
            return 1;
        }else{
            return -1;
        }
    }

    if (pcb1.getRemaining() > pcb2.getRemaining()) 
    {
        return 1;
    }

    if (pcb1.getRemaining() < pcb2.getRemaining()) 
    {
        return -1;
    }

    System.out.println("Error, please check the method!");
    return 0;
}

up操作

up操作实际上就是对比自己与父节点的大小,从而进行交换,其中使用while或递归进行书写,可以保证把当前结点调整到合适的位置。

public void up(int point) 
{
    while (point != 0 && (this.compare(point, point / 2) < 0)) {
        this.swap(point, point / 2);
        point /= 2;
    }
}

down操作

down操作实际上就是用自己和两个子节点进行比较,选择较大的子节点然后和父亲结点交换,其中使用while或者递归的方式书写,可以保证结点被放到合适的位置。

public void down(int point) 
{
    int size=heap.size()-1;
    while(point * 2 <= size)
    {
        int temp = point;
        if(point * 2 <= size && (this.compare(point, point * 2) > 0)) 
        {
            temp = 2 * point;
        }
        if(point * 2 + 1 <= size && (this.compare(temp,point * 2 + 1) > 0)) 
        { 
            temp = 2 * point + 1;
        }
        if(point != temp)
        {
            this.swap(point,temp);
            point = temp;
        }else break;
    }
}

基本操作

Heap 的操作其实也不算少,这里只根据实际实现了三个:

建堆与插入

想要使用数组维护 Heap,需要让数组的下标从 1 开始,对于 ArrayList 来说需要预先插入一个空节点,这里在 Constructor 中进行了处理。

Heap()
{
    heap = new ArrayList<PCB>();
    PCB emptyPCB = new PCB(-1);
    heap.add(emptyPCB);
    order = 0;
}

之后的建堆过程是通过不断的插入实现的。

public void push(PCB newNode)
{
    newNode.setQueueID(order);
    heap.add(newNode);
    order++;
    this.up(heap.size() - 1);
}

查询最值

由于堆的性质,查找最小(最大)值只需要$O(1)$的操作

public PCB top()
{
    if(this.empty()) return null;
    else return heap.get(1);
}

删除最值

删除时需要三个操作维护堆的合法性:

  1. 将最后一个元素赋值给堆顶元素
  2. 将最后一个元素删除
  3. 对堆顶元素进行 down() 操作
public void pop() 
{
    int size = heap.size();
    heap.set(1,heap.get(size - 1));
    heap.remove(size-1);
    down(1);
}

Thinking

从抽象到更抽象,面向对象的思想确实是一个巨大的进步,受限于个人水平,在实现中还带有一些面向过程的风格,技术上还未涉及泛型、接口等内容,再次回忆 C++ 的 STL,也是从 具体 到 抽象 的过程,从学习的角度在一定阶段是要避免使用的,但是在实际的解决问题中 抽象/面向对象 的思的的确确为我们提供了便利。

Code

import java.util.*;

public class Heap 
{
    ArrayList<PCB> heap;
    private static int order;

    /*
     * Initialize the heap.
     * In order make the index of first process is one, 
     * we need use the constructor to push 'empyt process' to the heap.
     */
    Heap()
    {
        heap = new ArrayList<PCB>();
        PCB emptyPCB = new PCB(-1);
        heap.add(emptyPCB);
        order = 0;
    }

    /*
     * Swap the element in ArrayList.
     */
    public void swap(int u, int v)
    {
        PCB Temp = heap.get(u);
        heap.set(u, heap.get(v));
        heap.set(v, Temp);
    }

    /*
     * Compare the element in ArrayList.
     * The first keyword is remaining time,
     * The second keyword is queue id.
     * 
     * Queue id is the order of the element push the heap.
     * 
     * As we know that remaining time may be the same, but queueID is unique.
     * So according to the queueID to compare can always be true.
     */
    public int compare(int u, int v) 
    {
        PCB pcb1 = this.heap.get(u);
        PCB pcb2 = this.heap.get(v);

        if (pcb1.getRemaining() == pcb2.getRemaining()) 
        {
            if(pcb1.getQueueID() > pcb2.getQueueID())
            {
                return 1;
            }else{
                return -1;
            }
        }

        if (pcb1.getRemaining() > pcb2.getRemaining()) 
        {
            return 1;
        }

        if (pcb1.getRemaining() < pcb2.getRemaining()) 
        {
            return -1;
        }

        System.out.println("Error, please check the method!");
        return 0;
    }

    /*
     * We need two operations to make the heap is legal.
     * The purpose of up() is make the lesser element to upper layer.
     * The purpose of down() is make the bigger element to lower layer.
     */
    public void up(int point) 
    {
        while (point != 0 && (this.compare(point, point / 2) < 0)) {
            this.swap(point, point / 2);
            point /= 2;
        }
    }

    public void down(int point) 
    {
        int size=heap.size()-1;
        while(point * 2 <= size)
        {
            int temp = point;
            if(point * 2 <= size && (this.compare(point, point * 2) > 0)) 
            {
                temp = 2 * point;
            }
            if(point * 2 + 1 <= size && (this.compare(temp,point * 2 + 1) > 0)) 
            { 
                temp = 2 * point + 1;
            }
            if(point != temp)
            {
                this.swap(point,temp);
                point = temp;
            }else break;
        }
    }

    /*
     * There is some common method of data structure. 
     */
    public void push(PCB newNode)
    {
        newNode.setQueueID(order);
        heap.add(newNode);
        order++;
        this.up(heap.size() - 1);
    }

    public PCB top()
    {
        if(this.empty()) return null;
        else return heap.get(1);
    }

    public void pop() 
    {
        int size = heap.size();
        heap.set(1,heap.get(size - 1));
        heap.remove(size-1);
        down(1);
    }

    public boolean empty()
    {
        if(heap.size() > 1) 
        {
            return false;
        }else{
            return true;
        }
    }

    public int size()
    {
        return heap.size();
    }

}

评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇