【问题标题】:Merge two LinkedList without creating a new LinkedList合并两个 LinkedList 而不创建新的 LinkedList
【发布时间】:2016-01-28 12:10:44
【问题描述】:

我正在尝试获取可执行文件中的两个链表,并将它们以交替的位置相互合并。前任。 ListOne 1,2,3 和 ListTwo 4,5 新的 ListOne 应该是 1,4,2,5,3。

LinkedList .h 文件:

class LinkedList
{
private:
struct ListNode
{
    string firstName;
    string lastName;
    long int phoneNumber;
    struct ListNode *next;
};
ListNode *head;

public:
LinkedList()
{
    head = nullptr;
}
~LinkedList();
void appendNode(string f, string l, long int p);
void displayList();
};

LinkedList .cpp 文件:

LinkedList::~LinkedList()
{
cout << "LinkList destructor" << endl;
}

void LinkedList::appendNode(string f, string l, long int p)
{
    ListNode *newNode;
    ListNode *nodePtr;
    newNode = new ListNode;

    newNode -> firstName = f;
    newNode -> lastName = l;
    newNode -> phoneNumber = p;
    newNode -> next = nullptr;

    if (!head)
        head = newNode;

    else
    {
        nodePtr = head;

        while (nodePtr -> next)
            //while nodePtr is pointing to another node
            nodePtr = nodePtr -> next;
            //move to that node

        nodePtr -> next = newNode;
        //inset the newNode at the end of the linked list
    }
 }

 void LinkedList::displayList()
{
    ListNode *nodePtr;
    nodePtr = head;

    while(nodePtr)
    //while nodePtr is true, meaning there is a node in the list
    {
        cout << nodePtr -> firstName << endl;
        cout << nodePtr -> lastName << endl;
        cout << nodePtr -> phoneNumber << endl;
        nodePtr = nodePtr -> next;
     }
}

可执行文件:

LinkedList ListOne;
LinkedList ListTwo;

ListOne.appendNode("Cate", "Beckem", 7704563454);
ListOne.appendNode("Cabe","Tomas", 7703451523);

ListTwo.appendNode("Mary", "Smith", 4043456543);
ListTwo.appendNode("Mark", "Carter", 4045433454);

我的程序运行完美,包括 displayList 函数。我只是很困惑如何进行合并功能。

【问题讨论】:

  • 您只需向LinkedList 添加一个方法,该方法采用LinkedList 并遍历传入的参数并为传入LinkedList 中的每个节点调用appendNode跨度>
  • @Gread.And.Powerful.Oz 哦,我明白你在说什么。我会试试看
  • 请使用相关代码编辑您的帖子,以便我更简洁准确地回答您的问题。
  • @Gread.And.Powerful.Oz 你认为你可以为那个方法改正一些伪代码,因为我画了几个空白
  • 查看上述appendNode 方法中的while 循环。首先,方法签名:merge(LinkedList &amp;b) 然后ListNode* node = b.head; while (node) { appendNode(node); node = node-&gt;next; } --- 请注意复制node,否则您将链接两个列表。

标签: c++ class linked-list c++14 alternating


【解决方案1】:

该合并是将已从源列表复制或将从源列表中窃取的节点插入到目标列表中的某些位置。

我假设您希望在该操作上将源列表视为不可变的,因此将复制源节点。

对复制和插入操作都有用的是iterator——它指向一个节点,并且在++-op 指向列表中的下一个节点或列表末尾之后(指向相当于nullptr):

(我将这样的 sn-ps 写在单个源文件中;将实现移动到 .cpp 文件或内联它们)

//
#include <type_traits>
using std::conditional;
#include <stdexcept>
using std::runtime_error;

class LinkedList
{


private:
    // [...]

    template <bool const_tag>
    struct NodeIterator {
        using element_type = typename conditional<
            const_tag,
            const ListNode,
            ListNode
        >::type;
        using pointer_type = element_type*;
        using reference_type = element_type&;

        static const NodeIterator end;

        NodeIterator(pointer_type p_in = nullptr) : p(p_in) {}

        NodeIterator& operator++ () {
            if (nullptr == p)
                throw runtime_error("Attempt to dereference nullptr");
            this->p = p->next;
            return *this;
        }

        bool operator== (const NodeIterator& rhs) const {
            return this->p != rhs.p;
        }

        bool operator!= (const NodeIterator& rhs) const {
            return !(*this == rhs);
        }

        pointer_type operator->() const {
            return p;
        }

        reference_type operator*() const {
            if (nullptr == p)
                throw runtime_error("Attempt to dereference nullptr");
            return *p;
        }

    private:
        pointer_type p;    
    }; // template <bool const_tag> struct NodeIterator

    static constexpr bool mutable_tag = false; 
    static constexpr bool const_tag = true; 

    using iterator_type = NodeIterator<mutable_tag>;
    using const_iterator_type = NodeIterator<const_tag>;


public:     
    LinkedList() :  head(nullptr) {}

    iterator_type begin() const { return iterator_type(head); }
    const iterator_type& end() const { return iterator_type::end; }

    const_iterator_type cbegin() const { return const_iterator_type(head); }
    const const_iterator_type& cend() const { return const_iterator_type::end; }
    // [...]    


}; // class LinkedList


// [...]    
template<bool is_const>
const LinkedList::NodeIterator<is_const>
LinkedList::NodeIterator<is_const>::end(nullptr);

// [...]

现在在合并函数代码中,您将迭代器放在相应列表的开头

auto it_one = listOne.begin();
auto it_two = listTwo.cbegin();

并递增it_one直到它指向目标列表中要插入副本的元素,然后递增it_two直到它指向要复制的源列表的节点,然后

ListNode& one = *it_one;
ListNode* ptwo = new ListNode(*it_two); // copies

ptwo->next = one.next;
one.next = ptwo;

基本上就是这样。

因此,这里真正的问题不是技术上的合并方法,而是操作的复杂性。

只要两个列表都按照要创建的结果列表的预期顺序排序,您就可以重复使用迭代器,从而清楚地遍历两个列表,它是O(N)N = max(size(listOne), size(listTwo))

如果您必须对它们进行初步排序以降低合并本身的成本,那么这种排序会导致O(N log N) 复杂性。


附带说明,存储迭代器也简化了其他操作的实现;例如显示它们只是

for (const auto& node : ListOne) {
    std::cout << node.firstName << std::endl;
    std::cout << node.lastName << std::endl;
    std::cout << node.phoneNumber << std::endl;
}

【讨论】:

    【解决方案2】:

    制作合并功能并不难。可以只用一个新的头记录新的列表,然后遍历这两个列表,依次将节点从这两个列表移动到新的列表中,如下所示。

     LinkedList LinkedList::merge(LinkedList b)
    {
        // if one list is null, return the other
        if (this->head == nullptr)
            return b;
        if (b.head == nullptr)
            return *this;
    
        LinkedList newlist;
        ListNode *ap = this->head, *bp = b.head, *p = nullptr;
        // if two pointer is all not null, move node from these to new list in turn
        while (ap != nullptr && bp != nullptr)
        {
            if (newlist.head == nullptr)
            {
                p = newlist.head = ap;
                ap = ap->next;
                p = p->next = bp;
                bp = bp->next;
            }
            else
            {
                p = p->next = ap;
                ap = ap->next;
                p = p->next = bp;
                bp = bp->next;
            }
        }
        // maybe one list is longer, and there is some node left.
        if (ap != nullptr)
            p->next = ap;
        if (bp != nullptr)
            p->next = bp;
        //clear original list
        this->head = b.head = nullptr;
        //if you want to merge list b to the caller list, you can change to
        //this->head = newlist->head and beginning part also need some modification.
        return newlist;
    }
    

    也许您不想更改原始列表,那么您可以复制该值并为新列表创建新节点。

    【讨论】:

      猜你喜欢
      • 2018-01-05
      • 2021-12-31
      • 1970-01-01
      • 2013-04-10
      • 2014-06-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多