How to chain delete pairs from a vector in C++?

穿精又带淫゛_ 提交于 2020-01-04 14:03:55

问题


I have this text file where I am reading each line into a std::vector<std::pair>,

handgun bullets
bullets ore
bombs ore
turret bullets

The first item depends on the second item. And I am writing a delete function where, when the user inputs an item name, it deletes the pair containing the item as second item. Since there is a dependency relationship, the item depending on the deleted item should also be deleted since it is no longer usable. For example, if I delete ore, bullets and bombs can no longer be usable because ore is unavailable. Consequently, handgun and turret should also be removed since those pairs are dependent on bullets which is dependent on ore i.e. indirect dependency on ore. This chain should continue until all dependent pairs are deleted.

I tried to do this for the current example and came with the following pseudo code,

for vector_iterator_1 = vector.begin to vector.end
{
    if user_input == vector_iterator_1->second
    {
        for vector_iterator_2 = vector.begin to vector.end
        {
            if vector_iterator_1->first == vector_iterator_2->second
            {
                delete pair_of_vector_iterator_2
            }
        }

        delete pair_of_vector_iterator_1
    }
}

Not a very good algorithm, but it explains what I intend to do. In the example, if I delete ore, then bullets and bombs gets deleted too. Subsequently, pairs depending on ore and bullets will also be deleted (bombs have no dependency). Since, there is only one single length chain (ore-->bullets), there is only one nested for loop to check for it. However, there may be zero or large number of dependencies in a single chain resulting in many or no nested for loops. So, this is not a very practical solution. How would I do this with a chain of dependencies of variable length? Please tell me. Thank you for your patience.

P. S. : If you didn't understand my question, please let me know.


回答1:


One (naive) solution:

  • Create a queue of items-to-delete
  • Add in your first item (user-entered)
  • While(!empty(items-to-delete)) loop through your vector
  • Every time you find your current item as the second-item in your list, add the first-item to your queue and then delete that pair

Easy optimizations:

  • Ensure you never add an item to the queue twice (hash table/etc)



回答2:


personally, I would just use the standard library for removal:

vector.erase(remove_if(vector.begin(), vector.end(), [](pair<string,string> pair){ return pair.second == "ore"; }));

remove_if() give you an iterator to the elements matching the criteria, so you could have a function that takes in a .second value to erase, and erases matching pairs while saving the .first values in those being erased. From there, you could loop until nothing is removed.

For your solution, it might be simpler to use find_if inside a loop, but either way, the standard library has some useful things you could use here.




回答3:


I couldn't help myself to not write a solution using standard algorithms and data structures from the C++ standard library. I'm using a std::set to remember which objects we delete (I prefer it since it has log-access and does not contain duplicates). The algorithm is basically the same as the one proposed by @Beth Crane.

#include <iostream>
#include <vector>
#include <utility>
#include <algorithm>
#include <string>
#include <set>

int main()
{
    std::vector<std::pair<std::string, std::string>> v
    {   {"handgun", "bullets"},
        {"bullets", "ore"},
        {"bombs", "ore"},
        {"turret", "bullets"}};

    std::cout << "Initially: " << std::endl << std::endl;
    for (auto && elem : v)
        std::cout << elem.first << " " << elem.second << std::endl;

    // let's remove "ore", this is our "queue"
    std::set<std::string> to_remove{"bullets"}; // unique elements

    while (!to_remove.empty()) // loop as long we still have elements to remove
    {
        // "pop" an element, then remove it via erase-remove idiom
        //  and a bit of lambdas
        std::string obj = *to_remove.begin();
        v.erase(
            std::remove_if(v.begin(), v.end(),
                           [&to_remove](const std::pair<const std::string,
                                        const std::string>& elem)->bool
            {
                // is it on the first position?
                if (to_remove.find(elem.first) != to_remove.end())
                {
                    return true;
                }
                // is it in the queue?
                if (to_remove.find(elem.second) != to_remove.end())
                {
                    // add the first element in the queue
                    to_remove.insert(elem.first);
                    return true;
                }
                return false;
            }
                          ),
            v.end()
        );
        to_remove.erase(obj); // delete it from the queue once we're done with it
    }

    std::cout << std::endl << "Finally: " << std::endl << std::endl;
    for (auto && elem : v)
        std::cout << elem.first << " " << elem.second << std::endl;
}



回答4:


@vsoftco I looked at Beth's answer and went off to try the solution. I did not see your code until I came back. On closer examination of your code, I see that we have done pretty much the same thing. Here's what I did,

std::string Node;

std::cout << "Enter Node to delete: ";

std::cin >> Node;

std::queue<std::string> Deleted_Nodes;

Deleted_Nodes.push(Node);

while(!Deleted_Nodes.empty())
{
    std::vector<std::pair<std::string, std::string>>::iterator Current_Iterator = Pair_Vector.begin(), Temporary_Iterator;

    while(Current_Iterator != Pair_Vector.end())
    {
        Temporary_Iterator = Current_Iterator;

        Temporary_Iterator++;

        if(Deleted_Nodes.front() == Current_Iterator->second)
        {
            Deleted_Nodes.push(Current_Iterator->first);

            Pair_Vector.erase(Current_Iterator);
        }

        else if(Deleted_Nodes.front() == Current_Iterator->first)
        {
            Pair_Vector.erase(Current_Iterator);
        }

        Current_Iterator = Temporary_Iterator;
    }

    Deleted_Nodes.pop();
}

To answer your question in the comment of my question, that's what the else if statement is for. It's supposed to be a directed graph so it removes only next level elements in the chain. Higher level elements are not touched.

1 --> 2 --> 3 --> 4 --> 5

Remove 5: 1 --> 2 --> 3 --> 4

Remove 3: 1 --> 2 4 5

Remove 1: 2 3 4 5

Although my code is similar to yours, I am no expert in C++ (yet). Tell me if I made any mistakes or overlooked anything. Thanks. :-)



来源:https://stackoverflow.com/questions/29421874/how-to-chain-delete-pairs-from-a-vector-in-c

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!