CS106B Assignment #6: Priority Queue

制作自己的PQueue类

pqueue:队列的拓展,有优先级的队列,即出队时 是先出优先级最大的。

动手设计类的接口,并通过4种方式实现

一. The interface

基本是以下这些,还加个几个debug用的函数

    class PQueue
          {
            public:
              PQueue();
              ~PQueue();
              int size();
              bool isEmpty();
              void enqueue(int newElem);
              int dequeueMax();
           };

pqueue.h文件:

/*
 * File: pqueue.h
 * --------------
 * Defines the interface for the priority queue class.
 *
 * Julie Zelenski, CS106, Fall 2007
 */


#ifndef _pqueue_h
#define _pqueue_h

#include "genlib.h"
#include "vector.h"
#include "disallowcopy.h"

/*
 * Class: PQueue
 * -------------
 * This is the class for a priority queue.  This is not
 * simple FIFO queue, it is a priority queue, where elements are
 * retrieved in order of priority, not just by longevity in queue.
 * The elements are integers and the integer is assumed to represent
 * the priority (larger integer is higher priority).
 */
class PQueue
{
public:

    /*
     * Constructor: PQueue
     * Usage: PQueue pq;
     *        PQueue *ppq = new PQueue;
     * ---------------------------------
     * Initializes a new pqueue to have no elements.
     */
    PQueue();


    /*
     * Destructor: ~PQueue
     * Usage: delete ppq;
     * ------------------
     * Deallocates all the storage associated with this pqueue.
     */
    ~PQueue();


    /*
     * Member function: isEmpty
     * Usage: if (pq.isEmpty()) . . .
     * -------------------------------
     * Returns true if this pqueue contains no elements.
     */
    bool isEmpty();


    /*
     * Member function: size
     * Usage: nElemes = pq.size();
     * ---------------------------
     * Returns number of elements contained in this pqueue.
     */
    int size();


    /*
     * Member function: enqueue
     * Usage: pq.enqueue(val);
     * -----------------------
     * Adds the specified element to this pqueue. No effort is made to
     * avoid duplicates.
     */
    void enqueue(int newElem);


    /*
     * Member function: eequeueMax
     * Usage: maxElem = pq.dequeueMax();
     * ---------------------------------
     * Removes the largest priority element from this pqueue and returns it.
     * If this pqueue is empty, this function raises an error.
     */
    int dequeueMax();


    /*
     * Member function: bytesUsed
     * Usage: numBytes = pq.bytesUsed();
     * ----------------------------------
     * This function would not usually be included as part of the class,
     * but this is here as part of evaluating the tradeoffs betweem
     * implementations. Given a pqueue, this function counts up
     * and return the total amount of space used given its
     * current contents.
     */
    int bytesUsed();


    /*
     * Member function: implementationName
     * Usage: cout << pq.implementationName();
     * ---------------------------------------
     * This operation would not usually be included as part of the class
     * class, but is included to help with analyzing and reporting results.
     * This member function returns a string that describes the PQueue
     * implementation strategy ("sorted linked list", "vector", etc.).
     */
    string implementationName();


    /*
     * Member function: printDebuggingInfo
     * Usage: pq.printDebuggingInfo();
     * -------------------------------
     * This operation would not usually be included as part of the class,
     * but is included to give you a hook to put any helpful debugging
     * print code (for example, something that prints the goopy details of
     * the internal structure). You don't need to implement this routine and can
     * ignore it entirely, but you may find it helpful to use as you are
     * doing development.
     */
    void printDebuggingInfo();

private:

    // If implemented using Vector data mamber, default memberwise copy
    // works fine, but if implemented as linked list, (ie pointer data member)
    // copying would create unintended sharing.
    // It's fine is to disallow copying for all implementations as
    // a precaution
    DISALLOW_COPYING(PQueue)

   
    //unsortedvector的数据/
    Vector<int> entries;
    //
    
    //sortedlist的数据/
    struct Cell {
        int value;
        Cell *next;
    };
    Cell *head;
    //
    
    //chunklist的数据//
    struct Cell{
        int * values;
        Cell* next;
        int BlockInUse;
    };
    Cell* head;
    PQueue::Cell *initCell(int newValue, Cell *nextCell);
    void shiftAdd(Cell *cell, int newValue);
    void splitAdd(Cell *cur, int newValue);
    //

    ///heap的数据//
    int* array;
    int capacity;
    int count;
    void swap (int indexA, int indexB);
    void expandCapacity();
    //

};


#endif

二. The implementation

1. Unsorted vector implementation

用未排序的Vector实现,之所叫未排序,因为入队时是无序入队,出队是采用一个个比较出队

关键出队函数:

int PQueue::dequeueMax()
{	
	if (isEmpty())
		Error("Tried to dequeue max from an empty pqueue!");
		
	int maxIndex = 0;	// assume first element is largest until proven otherwise
	int maxValue = entries[0];
	for (int i = 1; i < entries.size(); i++) {
		if (entries[i] > maxValue) {
			maxValue = entries[i];
			maxIndex = i;
		}
	}
	entries.removeAt(maxIndex);	// remove entry from vector
	return maxValue;
}

2. Sorted linked list implementation

排序好的链表实现。排序好的意思是入队是按次序接入链表

关键入队函数:

void PQueue::enqueue(int newValue)
{
 	Cell *cur, *prev, *newOne = new Cell;
 	
 	newOne->value = newValue;
 	
 	for (prev = NULL, cur = head; cur != NULL; prev=cur, cur = cur->next) {
 		if (newValue > cur->value) break;
 	}
 	newOne->next = cur;
 	if (prev) 
 		prev->next = newOne;
 	else 
 		head = newOne;
}

3. Sorted chunklist implementation

采用chunklist的思想设计

像这样每个链表存储一个vector,vector的大小自己设定

pqchunk.cpp:

#include "pqueue.h"
#include "genlib.h"
#include <iostream>

int const MaxElemsPerBlock = 4;  //设定每个chunk的大小

PQueue::PQueue()
{
    head = NULL; //创建头指针
}

PQueue::~PQueue()
{
    while (head != NULL)
    {
        Cell* next = head->next;
        delete[]head->values;   //删除数组
        delete head;    //删除指针
        head = next;
    }
}

bool PQueue::isEmpty()
{
    return (head == NULL);
}

int PQueue::size()
{
    int count = 0;
    for(Cell *cur = head; cur != NULL; cur = cur->next)//把所有的BlockInUse加起来就可以
        count += cur->BlockInUse;
    return count;
}

void PQueue::enqueue(int newValue)
{
    //加入第一个数,并得到head
    if(head == NULL)
    {
        Cell *newChunk = initCell(newValue, NULL);
        head = newChunk;
        return;;
    }

    Cell *cur, *prev, *newChunk = new Cell;
    for(prev = NULL,cur = head; cur != NULL; prev = cur,cur = cur->next)//遍历链表的套路模版
    {
        for (int i = 0; i < cur->BlockInUse; i++)
        {
            if (newValue > cur->values[i])
            {
                // 数组未满直接插入
                if (cur->BlockInUse < MaxElemsPerBlock)
                {
                    shiftAdd(cur,newValue);
                    return;
                }
                    // 如果数是最大的,需要创建新的头
                else if (i == 0 && prev == NULL)
                {
                    Cell *newChunk1 = initCell(newValue, cur);
                    head = newChunk1;
                    return;
                }
                    // 把值插入前一块中,且前一块 块数未满
                else if (i == 0 && prev != NULL && prev->BlockInUse < MaxElemsPerBlock)
                {
                    shiftAdd(prev,newValue);
                    return;
                }
                    // 这种情况当前块和之前块都满链链,于是采用splidAdd
                else
                {
                    splitAdd(cur,newValue);
                    return;

                }
            }
        }
    }


}

/*
 * 把值插入Cell的数组中
 * 此函数只适用于数组未满的情况
 */
void PQueue::shiftAdd(Cell *cell, int newValue)
{
    int index;
    for(index = 0; index < MaxElemsPerBlock; index++)
    {
        if(newValue > cell->values[index])  //找到插入位置
            break;
    }
    for(int i=MaxElemsPerBlock-1; i > index; i--)
        cell->values[i] = cell->values[i-1];  //shift 数组里面的值
    cell->values[index] = newValue;   //插入
    cell->BlockInUse++;
}

/*
 *把值插入已经满的Cell的数组
 *步骤:新Cell-->复制满的一半到新Cell中-->连接新的Cell-->把值插入
 */
void PQueue::splitAdd(Cell *cur, int newValue)
{
    Cell* newChunk = new Cell;
    newChunk->values = new int[MaxElemsPerBlock];
    for(int i = MaxElemsPerBlock/2; i < MaxElemsPerBlock; i++)
    {
        newChunk->values[i-(MaxElemsPerBlock/2)] = cur->values[i];  //复制一半到新Cell中
        //将cur的后一半归0
    }
    cur->BlockInUse = MaxElemsPerBlock/2;
    newChunk->BlockInUse = MaxElemsPerBlock/2;

    newChunk->next = cur->next;//连接新的Cell
    cur->next = newChunk;

    //把值插入cur中
    for (int j = 0; j < cur->BlockInUse; j++)
    {
        if (newValue > cur->values[j])
        {
            shiftAdd(cur,newValue);
            return;
        }
    }

    // 把值插入newchunk中
    for (int k = 0; k < newChunk->BlockInUse; k++)
    {
        if (newValue > newChunk->values[k])
        {
            shiftAdd(newChunk,newValue);
            return;
        }
    }
}

/*
 * 创建一个新的Cell,并返回Cell的指针
 * 该Cell指针指向传入的指针,若传NULL,表明Cell位于链表末端
 */
PQueue::Cell* PQueue::initCell(int newValue, Cell *nextCell)
{
    Cell* newChunk = new Cell;
    newChunk->values = new int[MaxElemsPerBlock];
    newChunk->values[0] = newValue;
    newChunk->BlockInUse = 1;
    newChunk->next = nextCell;
    return newChunk;
}


int PQueue::dequeueMax()
{
    if (isEmpty())
        Error("Tried to dequeue max from an empty pqueue!");
    int num = head->values[0]; // 需要返回的值,因为入队已经排序好,所以在第一个

    // 如果本来含有>1个数,则直接移动
    if (head->BlockInUse > 1)
    {
        for (int i = 0; i < head->BlockInUse-1; i++)
        {
            head->values[i] = head->values[i+1];
        }
    }

    head->BlockInUse--;

    // 没有剩余数
    if (head->BlockInUse == 0)
    {
        Cell *toBeDeleted = head;
        head = head->next;     // 删除Cell
        delete[] toBeDeleted->values;
        delete toBeDeleted;
    }
    return num;
}

int PQueue::bytesUsed()
{
    int total = sizeof(*this);
    for (Cell *cur = head; cur != NULL; cur = cur->next)
        total += sizeof(*cur);
    return total;
}

string PQueue::implementationName()
{
    return "chunk list";
}

void PQueue::printDebuggingInfo() {
    int count = 0;

    cout << "------------------ START DEBUG INFO ------------------" << endl;
    for (Cell *cur = head; cur != NULL; cur = cur->next) {
        cout << "Cell #" << count << " (at address " << cur << ") values = ";
        for (int i = 0; i < cur->BlockInUse; i++) {
            cout << cur->values[i] << " ";
        }
        cout << " next = " << cur->next << endl;
        count++;
    }
    cout << "------------------ END DEBUG INFO ------------------" << endl;
}

测试结果:

 -----------   Testing Basic PQueue functions -----------
 The pqueue was just created.  Is it empty? true
 
 Now enqueuing integers from 1 to 10 (increasing order)
 Pqueue should not be empty.  Is it empty? false
 Pqueue should have size = 10.  What is size? 10
 ------------------ START DEBUG INFO ------------------
 Cell #0 (at address 0x7f96a0500170) values = 10 9  next = 0x7f96a05000c0
 Cell #1 (at address 0x7f96a05000c0) values = 8 7 6 5  next = 0x7f96a0500010
 Cell #2 (at address 0x7f96a0500010) values = 4 3 2 1  next = 0x0
 ------------------ END DEBUG INFO ------------------
 Dequeuing the top 5 elements: 10 9 8 7 6
 Pqueue should have size = 5.  What is size? 5
 ------------------ START DEBUG INFO ------------------
 Cell #0 (at address 0x7f96a05000c0) values = 5  next = 0x7f96a0500010
 Cell #1 (at address 0x7f96a0500010) values = 4 3 2 1  next = 0x0
 ------------------ END DEBUG INFO ------------------
 
 Dequeuing all the rest: 5 4 3 2 1
 Pqueue should be empty.  Is it empty? true
 ------------------ START DEBUG INFO ------------------
 ------------------ END DEBUG INFO ------------------

4. Heap implementation

采用堆排序的思想,用array存放数据,且当存的数的数量超过范围时,array会自动变大

pqheap.cpp:

#include "pqueue.h"
#include "genlib.h"
#include <iostream>


PQueue::PQueue()
{
    capacity = 10;
    array = new int [capacity];
    count = 1;
    array[0] = 0;
}

PQueue::~PQueue()
{
    delete[] array;
}

bool PQueue::isEmpty()
{
    if(count <= 1)
        return true;
    else return false;
}

int PQueue::size()
{
    if(count ==0)
        return 0;
    else
        return  count-1;
}

void PQueue::enqueue(int newElem)
{
    if(count == capacity) expandCapacity();
    array[count++]= newElem;
    int index = count -1;
    //堆插入过程
    while(index >1 && array[index] > array[index/2])
    {
        swap(index, index/2);
        index /= 2;
    }
}

int PQueue::dequeueMax()
{
    if(isEmpty())
        Error("Tried to dequeue max from an empty pqueue!");
    int value = array[1]; //取出最大值
    array[1] = array[--count];
    int index =1;
    //堆重新生成过程
    while(index*2 < count)
    {
        if((index*2 +1) < count &&
                array[(index*2) +1] > array[index*2] &&
                array[index] < array[(index*2 +1)])
        {
            swap(index, (index*2)+1);
            index = (index*2)+1;
        }
        else if(array[index] < array[index*2])
        {
            swap(index, index*2);
            index = index*2;
        }
        else break;
    }
    return value;
}

void PQueue::swap(int indexA, int indexB)
{
    int temp = array[indexA];
    array[indexA] = array[indexB];
    array[indexB] = temp;
}

int PQueue::bytesUsed()
{
    return sizeof(*this) + count;
}

string PQueue::implementationName()
{
    return "heap";
}

void PQueue::printDebuggingInfo()
{
    cout << "------------------ START DEBUG INFO ------------------" << endl;
    cout << "Pqueue contains " << size() << " entries" << endl;
    for (int i = 1; i < count ; i++)
        cout << array[i] << " ";
    cout << endl;
    cout << "------------------ END DEBUG INFO ------------------" << endl;
}

void PQueue::expandCapacity()
{
    int* oldarray = array;
    capacity *=2;
    array = new int[capacity];
    for(int i=0; i< count; i++)
    {
        array[i] = oldarray[i];
    }
    delete[] oldarray;
}

测试结果:

-----------   Testing Basic PQueue functions -----------
The pqueue was just created.  Is it empty? true

Now enqueuing integers from 1 to 10 (increasing order)
Pqueue should not be empty.  Is it empty? false
Pqueue should have size = 10.  What is size? 10
------------------ START DEBUG INFO ------------------
Pqueue contains 10 entries
10 9 6 7 8 2 5 1 4 3 
------------------ END DEBUG INFO ------------------
Dequeuing the top 5 elements: 10 9 8 7 6 
Pqueue should have size = 5.  What is size? 5
------------------ START DEBUG INFO ------------------
Pqueue contains 5 entries
5 4 2 1 3 
------------------ END DEBUG INFO ------------------

Dequeuing all the rest: 5 4 3 2 1 
Pqueue should be empty.  Is it empty? true
------------------ START DEBUG INFO ------------------
Pqueue contains 0 entries

------------------ END DEBUG INFO ------------------
Enqueuing 500 numbers into pqueue in increasing order.
Using dequeue to pull out numbers in sorted order.  Are they sorted? 1
Enqueuing 500 random values into the pqueue.
Using dequeue to pull out numbers in sorted order.  Are they sorted? 1
Now, let's run an empirical time trial.

How large a pqueue to time? (10000 to 1000000, or 0 to quit): 10000

---- Performance for 10000-element pqueue (heap) -----

Time to enqueue into 10000-element pqueue: 0.127 usecs
Time to dequeue from 10000-element pqueue: 0.185 usecs
Time to pqsort random sequence of 10000 elements: 2.295 msecs
Time to pqsort sorted sequence of 10000 elements: 3.262 msecs
Time to pqsort reverse-sorted sequence of 10000 elements: 1.593 msecs

Running memory trial on 10000-element pqueue
After consecutive enqueues, 10000-element pqueue is using 41 KB of memory
After more enqueue/dequeue, 9936-element pqueue is using 41 KB of memory

------------------- End of trial ---------------------

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值