एक वेक्टर से तत्वों को मिटाते हुए


101

मैं मिटाना विधि का उपयोग कर एक वेक्टर से एक तत्व को साफ करना चाहता हूं। लेकिन यहां समस्या यह है कि तत्व केवल एक बार वेक्टर में होने की गारंटी नहीं है। यह कई बार मौजूद हो सकता है और मुझे उन सभी को साफ करने की आवश्यकता है। मेरा कोड कुछ इस तरह है:

void erase(std::vector<int>& myNumbers_in, int number_in)
{
    std::vector<int>::iterator iter = myNumbers_in.begin();
    std::vector<int>::iterator endIter = myNumbers_in.end();
    for(; iter != endIter; ++iter)
    {
        if(*iter == number_in)
        {
            myNumbers_in.erase(iter);
        }
    }
}

int main(int argc, char* argv[])
{
    std::vector<int> myNmbers;
    for(int i = 0; i < 2; ++i)
    {
        myNmbers.push_back(i);
        myNmbers.push_back(i);
    }

    erase(myNmbers, 1);

    return 0;
}

यह कोड स्पष्ट रूप से क्रैश हो जाता है क्योंकि मैं इसके माध्यम से पुनरावृत्ति करते हुए वेक्टर के अंत को बदल रहा हूं। इस लक्ष्य को हासिल करने का सबसे अच्छा तरीका क्या है? यानी, वेक्टर के माध्यम से कई बार पुनरावृत्ति किए बिना या वेक्टर की एक और प्रतिलिपि बनाने के बिना ऐसा करने का कोई तरीका है?

जवाबों:


167

हटाने / मिटाने का प्रयोग करें :

std::vector<int>& vec = myNumbers; // use shorter name
vec.erase(std::remove(vec.begin(), vec.end(), number_in), vec.end());

क्या होता है कि removeउन तत्वों को हटा देता है जो मूल्य के अलग होने ( number_in) की शुरुआत में होते हैं vectorऔर उस सीमा के बाद पहले तत्व को पुनरावृत्ति लौटाते हैं। फिर eraseइन तत्वों (जिसका मूल्य अनिर्दिष्ट है) को हटा देता है।


2
std::remove()ऐसे तत्वों को स्थानांतरित करता है जिन्हें हटाने के लिए तत्वों को ओवरराइट किया जाता है। एल्गोरिथ्म कंटेनर के आकार को नहीं बदलता है, और यदि nतत्व हटा दिए जाते हैं, तो यह अपरिभाषित है कि अंतिम nतत्व क्या हैं ।
विल्हेमटेल

20
erase-remove idiom का वर्णन स्कॉट मेयर्स द्वारा "प्रभावी एसटीएल: मानक टेम्पलेट पुस्तकालय के आपके उपयोग में सुधार के 50 विशिष्ट तरीके" पुस्तक में आइटम 32 में किया गया है।
एलिसैंड्रो जैकप्सन

1
@Benjin किसी भी अद्यतन की जरूरत नहीं है, यह वस्तुओं के विनाशकों को बुलाएगा यदि वे मौजूद हैं।
17

54
एसटीएल 'मुहावरे' इस तरह से मुझे छोटी परियोजनाओं के लिए पायथन का उपयोग करते हैं।
जोहान्स ओवरमैन

1
@LouisDionne जो एक पुनरावृत्ति अधिभार को संदर्भित करता है, मैं दो
पुनरावृत्ति

53

कॉल मिटाना पुनरावृत्तियों को अमान्य करेगा, आप इसका उपयोग कर सकते हैं:

void erase(std::vector<int>& myNumbers_in, int number_in)
{
    std::vector<int>::iterator iter = myNumbers_in.begin();
    while (iter != myNumbers_in.end())
    {
        if (*iter == number_in)
        {
            iter = myNumbers_in.erase(iter);
        }
        else
        {
           ++iter;
        }
    }

}

या आप एक functor और std :: वेक्टर :: erase के साथ मिलकर std :: remove_if का उपयोग कर सकते हैं :

struct Eraser
{
    Eraser(int number_in) : number_in(number_in) {}
    int number_in;
    bool operator()(int i) const
    {
        return i == number_in;
    }
};

std::vector<int> myNumbers;
myNumbers.erase(std::remove_if(myNumbers.begin(), myNumbers.end(), Eraser(number_in)), myNumbers.end());

इस मामले में अपने स्वयं के फ़नकार को लिखने के बजाय आप std :: remove का उपयोग कर सकते हैं :

std::vector<int> myNumbers;
myNumbers.erase(std::remove(myNumbers.begin(), myNumbers.end(), number_in), myNumbers.end());

C ++ 11 में आप एक फ़नकार के बजाय एक लैम्ब्डा का उपयोग कर सकते हैं:

std::vector<int> myNumbers;
myNumbers.erase(std::remove_if(myNumbers.begin(), myNumbers.end(), [number_in](int number){ return number == number_in; }), myNumbers.end());

C ++ में 17 std :: प्रयोगात्मक :: मिटा और std :: प्रयोगात्मक :: erase_if भी उपलब्ध हैं, सी ++ 20 में इन कर रहे हैं (अंत में) का नाम करने के लिए std :: मिटा और std :: erase_if :

std::vector<int> myNumbers;
std::erase_if(myNumbers, Eraser(number_in)); // or use lambda

या:

std::vector<int> myNumbers;
std::erase(myNumbers, number_in);

2
जब आप समान_टो का उपयोग कर सकते हैं, तो अपने स्वयं के फ़न्टर का उपयोग क्यों करें? :-P sgi.com/tech/stl/equal_to.html
क्रिस

3
वैसे, बुला eraseके साथ removeयह करने के लिए विहित तरीका है।
कोनराड रुडोल्फ

1
मुझे लगता है कि वास्तव में यही करता है। लेकिन अगर वह खुद फंसी हुई आई-सर्क का उपयोग करता है तो उसे remove_if का उपयोग करना चाहिए। या फ़न्क्टर के बिना केवल हटाने का उपयोग करें
जोहान्स शहाब -

3
+1 वर्तनी-आउट कोड ने मुझे एक प्रोग्रामिंग प्रतियोगिता में मदद की, जबकि "बस हटाए-मिटाए मुहावरे का उपयोग करें" नहीं किया।

13
  1. आप इंडेक्स एक्सेस का उपयोग करके इसे पुन: व्यवस्थित कर सकते हैं,

  2. O (n ^ 2) से बचने के लिए आप दो सूचकांकों का उपयोग कर सकते हैं, i - वर्तमान परीक्षण सूचकांक, j - सूचकांक अगली वस्तु को संग्रहीत करने के लिए और सदिश के नए आकार के चक्र के अंत में।

कोड:

void erase(std::vector<int>& v, int num)
{
  size_t j = 0;
  for (size_t i = 0; i < v.size(); ++i) {
    if (v[i] != num) v[j++] = v[i];
  }
  // trim vector to new size
  v.resize(j);
}

ऐसे मामले में आपके पास पुनरावृत्तियों का कोई अमान्य नहीं है, जटिलता O (n) है, और कोड बहुत संक्षिप्त है और आपको कुछ सहायक वर्गों को लिखने की आवश्यकता नहीं है, हालांकि कुछ मामलों में सहायक वर्गों का उपयोग करके अधिक लचीले कोड में लाभ हो सकता है।

यह कोड eraseविधि का उपयोग नहीं करता है , लेकिन आपके कार्य को हल करता है।

शुद्ध एसटीएल का उपयोग करके आप इसे निम्न तरीके से कर सकते हैं (यह मोटी के उत्तर के समान है):

#include <algorithm>

void erase(std::vector<int>& v, int num) {
    vector<int>::iterator it = remove(v.begin(), v.end(), num);
    v.erase(it, v.end());
}

4

इस पर निर्भर करते हुए कि आप ऐसा क्यों कर रहे हैं, std :: set का उपयोग std :: वेक्टर से बेहतर विचार हो सकता है।

यह प्रत्येक तत्व को केवल एक बार होने की अनुमति देता है। यदि आप इसे कई बार जोड़ते हैं, तो वैसे भी मिटाने के लिए केवल एक उदाहरण होगा। यह मिटाए गए ऑपरेशन को तुच्छ बना देगा। मिटाए गए ऑपरेशन में वेक्टर की तुलना में कम समय की जटिलता भी होगी, हालांकि, तत्वों को जोड़ना सेट पर धीमा है, इसलिए यह बहुत अधिक लाभकारी नहीं हो सकता है।

यह निश्चित रूप से काम नहीं करेगा यदि आप रुचि रखते हैं कि आपके वेक्टर में कितनी बार एक तत्व जोड़ा गया है या तत्वों को किस क्रम में जोड़ा गया है।


हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.