【问题标题】:Priority Queues using linked lists and classes使用链表和类的优先队列
【发布时间】:2022-01-22 07:14:16
【问题描述】:

我是数据结构的新手,一直在尝试实现以下代码,但无法使其工作。我不完全理解我在做什么所以需要一些帮助,我正在使用链表和类创建一个优先级队列。将年龄作为输入并按降序显示所有年龄值。这是代码。

#include <iostream>
using namespace std;
class Node
{
private:
    int age;
    Node* next;

public:

    int getAge() {

        return this->age;
    }

    void setAge(int age) {

        this->age = age;
    }

    Node* getNext() {

        return this->next;
    }

    Node* setNext(Node* next) {

        this->next = next;
    }
};

class Priority_Queue
{
private:
    Node* front;
    Node* rear;
public:
    Priority_Queue()
    {
        front = NULL;
        rear = NULL;
    }

    void enqueue(int x)
    {
        if (front == NULL && rear == NULL) {
            Node* newNode = new Node();
            newNode->setAge(x);
            newNode->setNext(NULL);
        }

        else if (front != NULL && front->getAge() < x) {
            Node* newNode = new Node();
            newNode->setAge(x);
            newNode->setNext(rear);
            front = newNode;
        }

        else if (front != NULL && front->getAge() > x) {

            Node* newNode = new Node();
            newNode->setAge(x);
            newNode->setNext(NULL);
            rear = newNode;
        }
    
    }
    void dequeue()
    {
        if (front == NULL)
            cout << "Can't remove from an empty queue!" << endl;

        else{
            int x = front->getAge();
            Node* p = front;
            front = front->getNext();
            delete p;
            cout << "Deleted Age is " << x << endl;
        }
    }

    void display()
    {
        Node* ptr;
        ptr = rear;
        if (front == NULL)
            cout << "Queue is empty\n";
        else
        {
            cout << "Queue is :\n";

            while (ptr != NULL)
            {
                cout << ptr->getAge() << endl;
                ptr = ptr->getNext();
            }
        }
    }
};
int main()
{
    int c, p;
    Priority_Queue pq;
    cout << "\nWelcome to Patient information system" << endl;
    cout << "\Enter Your choice of the activity" << endl;
    do
    {

        cout << "1.Insert\n";
        cout << "2.Delete\n";
        cout << "3.Display\n";
        cout << "4.Exit\n";
        cout << "Enter your choice : ";
        cin >> c;
        switch (c)
        {
        case 1:
            cout << "Input the age value to be added in the queue : ";
            cin >> p;
            pq.enqueue(p);
            break;
        case 2:
            pq.dequeue();
            break;
        case 3:
            pq.display();
            break;
        case 4:
            break;
        default:
            cout << "Wrong choice\n";
        }
    } while (c != 4);
    cout << endl;
    return 0;
}

【问题讨论】:

  • 这段代码有什么问题?你有什么问题?

标签: c++ linked-list priority-queue


【解决方案1】:

这是一个快速的代码审查:

#include <iostream>
using namespace std;  // Bad practice; especially in a multi-file project which
                      // this should be.

// The Node should be an "implementation detail." I care about the queue, not
// the plumbing that makes it work.
class Node {
 private:
  int age;
  Node* next;  // A sorted queue using only a singly linked list seems like a
               // bad idea.

 public:
  // Use of this is not needed; it can hurt readability
  int getAge() { return this->age; }

  void setAge(int age) { this->age = age; }

  Node* getNext() { return this->next; }

  // Return should be void; you don't return anything
  Node* setNext(Node* next) { this->next = next; }
};

class Priority_Queue {
 private:
  Node* front;
  Node* rear;

 public:
  Priority_Queue() {
    front =
        NULL;  // Prefer default member initialization, then default this ctor
    rear = NULL;  // Prefer nullptr over NULL, it's been around for a decade now
  }

  /*
   * SUPER BAD: No destructor, copy constructor, or assignment operator
   * overload. This class manages heap-allocated resources, and no care is taken
   * to ensure that proper copies are made, or that the object is properly
   * destroyed.
   *
   * This is known as the Rule of 3, but has recently been called the Rule of
   * 0/3/5. Ideally, you would be adhering to the Rule of 5.
   */

  void enqueue(int x) {
    if (front == NULL && rear == NULL) {  // Unnecessarily redundant
      Node* newNode = new Node();  // Why not provide a constructor for this?
      newNode->setAge(x);
      newNode->setNext(NULL);
    }

    else if (front != NULL &&
             front->getAge() < x) {  // Pointers can be directly checked
      Node* newNode = new Node();
      newNode->setAge(x);
      newNode->setNext(rear);
      front = newNode;
    }

    else if (front != NULL && front->getAge() > x) {
      Node* newNode = new Node();
      newNode->setAge(x);
      newNode->setNext(NULL);
      rear = newNode;
    }
  }
  void dequeue() {
    if (front == NULL)
      cout << "Can't remove from an empty queue!"
           << endl;  // Do nothing, no need for a message

    else {
      int x = front->getAge();
      Node* p = front;
      front = front->getNext();
      delete p;
      cout << "Deleted Age is " << x
           << endl;  // Printing in general is a bad idea from within a data
                     // structure
    }
  }

  void display() {
    Node* ptr;
    ptr = rear;  // Starting at end of list for printing
    if (front == NULL)
      cout << "Queue is empty\n";
    else {
      cout << "Queue is :\n";

      while (ptr != NULL) {
        cout << ptr->getAge() << endl;
        ptr = ptr->getNext();
      }
    }
  }
};

就学生代码而言,这还不错。当涉及到排序时,我们不会费心实际移动节点;鉴于您的 Node 类的布局,这是不必要的。

我要做的第一件事是将其拆分为多个文件。它实际上使写作更容易一些。第一个文件是我们的优先队列头。

PriorityQueue.hpp

#ifndef PQUEUE_HPP
#define PQUEUE_HPP

class Priority_Queue {
 private:
  struct Node;  // Prevents Nodes from being declared outside of this class
  Node* front = nullptr;
  Node* rear = nullptr;

  // Helper functions
  void sort();
  void push_front(int x);  // Very simple with singly linked list
  friend void swap(Priority_Queue& lhs, Priority_Queue& rhs);

 public:
  Priority_Queue() = default;

  // Only going for Rule of 3 for brevity
  Priority_Queue(const Priority_Queue& other);
  ~Priority_Queue();

  Priority_Queue& operator=(Priority_Queue other);

  void enqueue(int x);
  void dequeue();
  void display();
};

// Now that we're nested, a struct can be used instead
// Will make things a lot easier
struct Priority_Queue::Node {
  int age = 0;
  Node* next = nullptr;

  Node() = default;
  Node(int a);
  Node(int a, Node* n);
};

#endif

这声明了类和它们包含的函数。最大的单一变化是Node 现在是struct,并在Priority_Queue 中私下声明。我应该能够看到,更不用说在课堂外声明Nodes。它们就是所谓的实现细节。他们帮助我们编写课程,但看不到任何业务。

Node 设为结构还允许我们删除所有成员函数。我们将让类直接操作数据。

PriorityQueue.cpp

#include "PriorityQueue.hpp"

#include <algorithm>
#include <iostream>

// This is sloppy, but time contraints won't allow me to make this nicer.
Priority_Queue::Priority_Queue(const Priority_Queue& other) {
  Node* otherNode = other.front;
  while (otherNode) {
    push_front(otherNode->age);
  }
  sort();
}

Priority_Queue::~Priority_Queue() {
  while (front) {
    Node* d = front;
    front = front->next;
    delete d;
  }
  rear = nullptr;
}

// Untested
Priority_Queue& Priority_Queue::operator=(Priority_Queue other) {
  swap(*this, other);
  return *this;
}

void Priority_Queue::enqueue(int x) {
  if (!front) {           // Simpler pointer check
    front = new Node(x);  // Provided ctor makes this simpler
    rear = front;
    // Single node, no need to sort
    return;
  }

  push_front(x);
  sort();
  // Separate your concerns. Enqueue gets a node into the list, we worry about
  // sorting elsewhere.
}

// Should be called pop(); not changing so that your main() can be left alone
void Priority_Queue::dequeue() {
  if (!front) return;

  Node* p = front;
  front = front->next;
  delete p;
}

void Priority_Queue::display() {
  Node* ptr = front;
  if (!front)
    std::cout << "Queue is empty\n";
  else {
    std::cout << "Queue is :\n";

    while (ptr) {
      std::cout << ptr->age << '\n';
      ptr = ptr->next;
    }
  }
}

void Priority_Queue::sort() {
  // We'll go with a bubble sort
  bool madeSwap;
  do {
    madeSwap = false;
    Node* node = front;

    while (node->next) {
      if (node->age > node->next->age) {
        std::swap(node->age, node->next->age);
        madeSwap = true;
      }
      node = node->next;
    }
  } while (madeSwap);
}

void Priority_Queue::push_front(int x) { front = new Node(x, front); }

// Untested
void swap(Priority_Queue& lhs, Priority_Queue& rhs) {
  using std::swap;
  swap(lhs.front, rhs.front);
  swap(lhs.rear, rhs.rear);
}

// Nested class
Priority_Queue::Node::Node(int a) : age(a) {}  // next is automatically nullptr
Priority_Queue::Node::Node(int a, Node* n) : age(a), next(n) {}

您的 main.cpp 基本上没有受到影响。它现在包括PriorityQueue.hpp,但没有什么不同。

您必须编译这两个 cpp 文件才能获得工作程序。像g++ -Wall -Wextra -std=c++17 main.cpp PriorityQueue.cpp 这样的东西。您会注意到我也按升序顺序排序,以避免公然复制/粘贴。这不是一个巨大的威慑,但总比没有好。

要查看它的运行情况,您可以访问this 链接。

【讨论】:

    【解决方案2】:

    您的代码存在太多问题,您离工作代码太远了,我无法为您提供帮助。

    请阅读:https://www.programiz.com/dsa/priority-queue

    还有一个 C++ 示例,您可以对其进行测试和调试以准确了解其工作原理。

    一些问题:

    • setNext 应该返回 void。
    • 你永远不会在 enque 中设置前面(如果在 enque 中,请先查看)。
    • 您应该使用 std::vector 或 std::list 作为基础类型。
    • 您不需要使用“this”指针(例如“this->age = age”)
    • 算法没有按照你的想法做(不是很接近) ...我可以用这个来很长时间。

    【讨论】:

    • 恐怕不是一个特别有用的答案。它更像是一系列的cmets。也就是说,我同意这里提出的大多数观点。分歧点:链表是我通常不使用智能指针的地方。使用智能指针,列表销毁变得递归,限制了列表的大小。如果您知道列表总是很短,很时髦,但是如果您不知道列表大小的上限,您可能会陷入堆栈溢出。相反,我会制定一个由五个兼容的 Linked List 类组成的规则来管理列表,但前提是 std::list 被排除在外。
    • SO 在智能指针方面存在问题,即在不应该的时候推荐它们。如果您从头开始编写数据结构,智能指针实际上并没有让事情变得更容易。最简单的例子是跟踪头部和尾部的链表。单个元素列表不能使用智能指针,除非你想走std::shared_ptr 的臃肿路线,这是没有意义的。同样,在更长的列表中,所有中间节点都没有指向它们的专用指针,它们也不应该。如果您尝试使用std::unique_ptr,当您离开创建它们的函数时,它们都会消失。
    • @sweenish:我正在考虑使用 std::vector(或 std::list)作为优先级队列的基础类型,而不是智能指针。
    • @user4581301:我不同意你进入堆栈溢出的观点。您只需要不递归地执行此操作。用“while (head) head = head->next”来做,你有 0 个问题,不管列表有多大。但正如我之前所说,我并没有考虑智能指针。
    • 使用std::vectorstd::list 确实会更简单。这是我向我的学生推荐的,当我们使用这样的数据结构时,我们只是在限制某些东西。我想我操之过急,因为很多发帖人认为智能指针在数据结构中有意义,而实际上并非如此。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多