Объединить два LinkedList без создания нового LinkedList

Я пытаюсь взять два связанных списка, которые у меня есть в моем исполняемом файле, и объединить их в чередующиеся позиции. Ex. 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. Я просто очень запутался, как сделать функцию слияния.

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;
}

Возможно, вы не хотите изменять исходный список, тогда вы можете просто скопировать значение и создать новый узел для нового списка.

0

Другие решения

Это объединение вставляет узлы, которые были скопированы или будут украдены из списка источников, в целевой список в определенных позициях.

Я предполагаю, что вы хотите рассматривать исходный список как неизменяемый в этой операции, поэтому исходные узлы будут скопированы.

Что полезно для операции копирования и вставки, так это итератор — то, что указывает на узел и, после ++-op указывает на следующий узел в списке или за концом списка (эквиваленту nullptr):

(Я пишу такие фрагменты в одном исходном файле; переместить реализации в файлы .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;
}
0

По вопросам рекламы [email protected]