सी में शब्दकोश लागू करने का त्वरित तरीका


132

सी में प्रोग्राम लिखते समय जो चीजें मुझे याद आती हैं उनमें से एक शब्दकोश डेटा संरचना है। सी में एक को लागू करने का सबसे सुविधाजनक तरीका क्या है? मैं प्रदर्शन की तलाश में नहीं हूं, लेकिन इसे खरोंच से कोड करने में आसानी होती है। मैं नहीं चाहता कि यह या तो सामान्य हो - जैसे कि स्ट्रिंग-> int करेगा। लेकिन मैं चाहता हूं कि यह वस्तुओं की एक मनमानी संख्या को संग्रहीत करने में सक्षम हो।

यह एक अभ्यास के रूप में अधिक इरादा है। मुझे पता है कि 3 पार्टी लाइब्रेरी उपलब्ध हैं जिनका उपयोग कोई भी कर सकता है। लेकिन एक क्षण के लिए विचार करें, कि वे मौजूद नहीं हैं। ऐसी स्थिति में आप जिस तरह से तेज कर सकते हैं, वह उपरोक्त आवश्यकताओं को पूरा करते हुए शब्दकोश को लागू कर सकता है।


4
यदि आप इसे आपके लिए प्रदान करने से चूक गए हैं, तो आप इसे तीसरे पक्ष के कार्यान्वयन का उपयोग करने के बजाय इसे खरोंच से क्यों बनाना चाहते हैं?
कार्ल Knechtel

हां, वह विकल्प हमेशा मौजूद रहता है। मैंने इस प्रश्न को एक अभ्यास के रूप में अधिक प्रस्तुत किया।
रोहित

10
सी में हैशटेबल लिखना एक मजेदार व्यायाम है - हर गंभीर सी प्रोग्रामर को इसे कम से कम एक बार करना चाहिए।
ली

मुझे लगता है कि एक शब्दकोष के बजाय एक डेटाटाइप के रूप में एक शब्दकोश हो रहा है, क्योंकि इसे बहुत सारे तरीकों से लागू किया जा सकता है - एक सूची, एक हैशटेबल, एक पेड़, एक आत्म-संतुलन वाला पेड़, आदि। क्या आप एक शब्दकोश, या एक हैशटेबल के लिए पूछ रहे हैं ?
पॉल हैंकिन

1
संबंधित: कैसे एक अजगर की तरह सी [] (में शब्दकोश का प्रतिनिधित्व करने के? Stackoverflow.com/questions/3269881/... )
गौरांग टंडन

जवाबों:


114

सी प्रोग्रामिंग लैंग्वेज की धारा 6.6 एक सरल शब्दकोश (हैशटेबल) डेटा संरचना प्रस्तुत करती है। मुझे नहीं लगता कि एक उपयोगी डिक्शनरी कार्यान्वयन इससे सरल हो सकता है। आपकी सुविधा के लिए, मैं यहां कोड पुन: प्रस्तुत करता हूं।

struct nlist { /* table entry: */
    struct nlist *next; /* next entry in chain */
    char *name; /* defined name */
    char *defn; /* replacement text */
};

#define HASHSIZE 101
static struct nlist *hashtab[HASHSIZE]; /* pointer table */

/* hash: form hash value for string s */
unsigned hash(char *s)
{
    unsigned hashval;
    for (hashval = 0; *s != '\0'; s++)
      hashval = *s + 31 * hashval;
    return hashval % HASHSIZE;
}

/* lookup: look for s in hashtab */
struct nlist *lookup(char *s)
{
    struct nlist *np;
    for (np = hashtab[hash(s)]; np != NULL; np = np->next)
        if (strcmp(s, np->name) == 0)
          return np; /* found */
    return NULL; /* not found */
}

char *strdup(char *);
/* install: put (name, defn) in hashtab */
struct nlist *install(char *name, char *defn)
{
    struct nlist *np;
    unsigned hashval;
    if ((np = lookup(name)) == NULL) { /* not found */
        np = (struct nlist *) malloc(sizeof(*np));
        if (np == NULL || (np->name = strdup(name)) == NULL)
          return NULL;
        hashval = hash(name);
        np->next = hashtab[hashval];
        hashtab[hashval] = np;
    } else /* already there */
        free((void *) np->defn); /*free previous defn */
    if ((np->defn = strdup(defn)) == NULL)
       return NULL;
    return np;
}

char *strdup(char *s) /* make a duplicate of s */
{
    char *p;
    p = (char *) malloc(strlen(s)+1); /* +1 for ’\0’ */
    if (p != NULL)
       strcpy(p, s);
    return p;
}

ध्यान दें कि यदि दो तारों का हैश टकराता है, तो यह एक O(n)लुकअप समय हो सकता है । का मान बढ़ाकर आप टकराव की संभावना को कम कर सकते हैं HASHSIZE। डेटा संरचना की पूरी चर्चा के लिए, कृपया पुस्तक से परामर्श करें।


1
यदि यह सी पुस्तक से है, तो मुझे आश्चर्य है कि क्या अधिक कॉम्पैक्ट कार्यान्वयन हो सकता है।
रोहित

30
@ रोहित, उपयोगी सी कोड के एक टुकड़े के लिए, यह उससे ज्यादा कॉम्पैक्ट नहीं है। मुझे लगता है कि आप हमेशा कुछ व्हाट्सएप को हटा सकते थे ...
रयान काल्होन

7
यहाँ क्यों hashval = *s + 31 * hashval;बिल्कुल 31 और कुछ और नहीं है?
ッ ア ア

12
31 प्रधान है। टक्करों की संभावना को कम करने के लिए प्रायः हैश फ़ंक्शन में प्राइम का उपयोग किया जाता है। इसका पूर्णांक कारक के साथ कुछ करना है (अर्थात आप अभाज्य कारक नहीं बन सकते हैं)।
11:30 बजे jnovacho

2
@Overdrivr: इस उदाहरण में आवश्यक नहीं है। हैशटैब स्थिर अवधि का है। स्थिर अवधि के साथ Uninitialized चर (जो कि, कार्यों के बाहर घोषित किए गए हैं, और जो भंडारण वर्ग के साथ घोषित किए गए हैं), सही प्रकार के शून्य के रूप में बाहर शुरू करने की गारंटी है (यानी: 0 या NULL या 0.0)
carvone

19

सबसे तेज़ तरीका होगा कि आप पहले से मौजूद कार्यान्वयन का उपयोग करें, जैसे कि uthash

और, यदि आप वास्तव में इसे स्वयं कोड करना चाहते हैं, तो इससे प्राप्त एल्गोरिदम uthashकी जांच और फिर से उपयोग किया जा सकता है। यह बीएसडी-लाइसेंस प्राप्त है, इसलिए कॉपीराइट नोटिस से अवगत कराने की आवश्यकता के अलावा, आप इसके साथ क्या कर सकते हैं में बहुत अच्छी तरह से असीमित हैं।


8

कार्यान्वयन में आसानी के लिए, एक सरणी के माध्यम से भोलेपन से हरा करना कठिन है। कुछ त्रुटि की जाँच के अलावा, यह एक पूर्ण कार्यान्वयन (अप्रयुक्त) है।

typedef struct dict_entry_s {
    const char *key;
    int value;
} dict_entry_s;

typedef struct dict_s {
    int len;
    int cap;
    dict_entry_s *entry;
} dict_s, *dict_t;

int dict_find_index(dict_t dict, const char *key) {
    for (int i = 0; i < dict->len; i++) {
        if (!strcmp(dict->entry[i], key)) {
            return i;
        }
    }
    return -1;
}

int dict_find(dict_t dict, const char *key, int def) {
    int idx = dict_find_index(dict, key);
    return idx == -1 ? def : dict->entry[idx].value;
}

void dict_add(dict_t dict, const char *key, int value) {
   int idx = dict_find_index(dict, key);
   if (idx != -1) {
       dict->entry[idx].value = value;
       return;
   }
   if (dict->len == dict->cap) {
       dict->cap *= 2;
       dict->entry = realloc(dict->entry, dict->cap * sizeof(dict_entry_s));
   }
   dict->entry[dict->len].key = strdup(key);
   dict->entry[dict->len].value = value;
   dict->len++;
}

dict_t dict_new(void) {
    dict_s proto = {0, 10, malloc(10 * sizeof(dict_entry_s))};
    dict_t d = malloc(sizeof(dict_s));
    *d = proto;
    return d;
}

void dict_free(dict_t dict) {
    for (int i = 0; i < dict->len; i++) {
        free(dict->entry[i].key);
    }
    free(dict->entry);
    free(dict);
}

2
"कार्यान्वयन में आसानी के लिए": आप बिल्कुल सही हैं: यह सबसे आसान है। इसके अलावा, यह ओपी के अनुरोध को लागू करता है "मैं चाहता हूं कि यह वस्तुओं की एक मनमानी संख्या को स्टोर करने में सक्षम हो" - उच्चतम मतदान जवाब ऐसा नहीं करता है (जब तक आप मानते हैं कि एक संकलन समय लगातार लेने से "मनमाना" संतुष्ट होता है ...)
द्विविदक

1
यह उपयोग-मामले के आधार पर एक मान्य दृष्टिकोण हो सकता है, लेकिन ओपी ने स्पष्ट रूप से एक शब्दकोश का अनुरोध किया है, और यह निश्चित रूप से एक शब्दकोश नहीं है।
डैन बेहार्ड

3

एक साधारण हैश फ़ंक्शन और संरचनाओं की कुछ लिंक की गई सूची बनाएं, हैश के आधार पर, मान जोड़ने के लिए कौन सी लिंक की गई सूची असाइन करें। इसे पुनः प्राप्त करने के लिए हैश का उपयोग करें।

मैंने कुछ समय पहले एक साधारण कार्यान्वयन किया था:

...
#define K 16 // गुणांक गुणांक

तानाशाही
{
    चार नाम; / * कुंजी का नाम * /
    इंट वाल; / * मान * /
    संरचना तानाशाही * अगला; / * लिंक फ़ील्ड * /
};

typedef structure तानाशाही;
तानाशाही * टेबल [के];
int initialized = 0;


शून्य पुट (चार *, इंट);

शून्य init_dict ()
{   
    initialized = 1;
    int i;  
    for (i = 0; iname = (char *) malloc (strlen (key_name) +1);
    पीटीआर-> वैल = स्वाल;
    strcpy (ptr-> नाम, key_name);


    पीटीआर-> अगला = (स्ट्रक्चर तानाशाह *) टेबल [एचएस];
    तालिका [hsh] = ptr;

}


int getval (char * key_name)
{   
    int hsh = hash (key_name);   
    तानाशाह * पीटीआर;
    के लिए (पीटीआर = तालिका [एचएस]; पीटीआर! = (तानाशाही *) 0;
        ptr = (तानाशाही *) ptr-> अगला)
    अगर (strcmp (ptr-> नाम, key_name) == 0)
        वापसी ptr-> वैल;
    वापसी -1;
}

1
क्या आपको आधा कोड याद नहीं है? "हैश ()" और "पुटवल (") कहाँ है?
स्वदेव

3

GLib और gnulib

यदि आपके पास अधिक विशिष्ट आवश्यकताएं नहीं हैं, तो ये आपकी सबसे अच्छी संभावनाएं हैं, क्योंकि वे व्यापक रूप से उपलब्ध हैं, पोर्टेबल और संभावित रूप से कुशल हैं।

यह भी देखें: क्या सामान्य डेटा संरचनाओं के साथ कोई खुला स्रोत सी लाइब्रेरी हैं?


2

यहाँ एक त्वरित कार्यान्वयन है, मैंने इसे एक स्ट्रिंग से 'मैट्रिक्स' (सॉक्ट) प्राप्त करने के लिए उपयोग किया है। आपके पास एक बड़ा सरणी हो सकता है और रन पर इसके मान भी बदल सकते हैं:

typedef struct  { int** lines; int isDefined; }mat;
mat matA, matB, matC, matD, matE, matF;

/* an auxilary struct to be used in a dictionary */
typedef struct  { char* str; mat *matrix; }stringToMat;

/* creating a 'dictionary' for a mat name to its mat. lower case only! */
stringToMat matCases [] =
{
    { "mat_a", &matA },
    { "mat_b", &matB },
    { "mat_c", &matC },
    { "mat_d", &matD },
    { "mat_e", &matE },
    { "mat_f", &matF },
};

mat* getMat(char * str)
{
    stringToMat* pCase;
    mat * selected = NULL;
    if (str != NULL)
    {
        /* runing on the dictionary to get the mat selected */
        for(pCase = matCases; pCase != matCases + sizeof(matCases) / sizeof(matCases[0]); pCase++ )
        {
            if(!strcmp( pCase->str, str))
                selected = (pCase->matrix);
        }
        if (selected == NULL)
            printf("%s is not a valid matrix name\n", str);
    }
    else
        printf("expected matrix name, got NULL\n");
    return selected;
}

2

मुझे आश्चर्य है कि पुस्तकालयों के किसी भी उल्लेखित hsearch / hcreate सेट को शामिल नहीं किया गया है, जो कि विंडोज़ पर उपलब्ध नहीं है, लेकिन POSIX द्वारा अनिवार्य है, और इसलिए लिनक्स / GNU सिस्टम में उपलब्ध है।

लिंक का एक सरल और पूर्ण मूल उदाहरण है जो बहुत अच्छी तरह से इसके उपयोग की व्याख्या करता है।

यहां तक ​​कि इसमें थ्रेड सेफ वैरिएंट भी है, यह प्रयोग करने में आसान है और बहुत अच्छा है।


2
ध्यान देने योग्य बात यह है कि यहां के लोग कहते हैं कि यह एक तरह से अनुपयोगी है, हालांकि मैंने खुद इसे आजमाया नहीं है: stackoverflow.com/a/6118591/895245
Ciro Santilli 郝海东 people people people 法轮功

1
पर्याप्त रूप से, हालांकि, मैंने कम से कम एक ऐप में hcreate_r (कई हैश टेबल के लिए) संस्करण की कोशिश की है जो वास्तविक दुनिया पर विचार करने के लिए काफी लंबे समय तक चला। सहमत हैं कि इसका GNU विस्तार है लेकिन फिर कई अन्य कामों के लिए भी यही स्थिति है। हालाँकि मैं अभी भी यह तर्क दूंगा कि आप अभी भी इसे किसी वास्तविक दुनिया ऐप में संचालित होने वाली एक बड़ी कुंजी मूल्य जोड़ी के लिए उपयोग कर सकते हैं
fkl

0

एक हैशटेबल एक सरल "डिक्शनरी" का पारंपरिक कार्यान्वयन है। यदि आप गति या आकार के बारे में परवाह नहीं करते हैं, तो इसके लिए बस Google । कई स्वतंत्र रूप से उपलब्ध कार्यान्वयन हैं।

यहाँ पहले एक मैंने देखा - एक नज़र में, यह मुझे ठीक लग रहा है। (यह बहुत ही बुनियादी है। यदि आप वास्तव में असीमित मात्रा में डेटा रखना चाहते हैं, तो आपको टेबल मेमोरी को बढ़ने के लिए "लॉजिक" में कुछ तर्क जोड़ना होगा।)

सौभाग्य!


-1

हाशिंग कुंजी है। मुझे लगता है कि इसके लिए लुकअप टेबल और हैशिंग की का उपयोग करें। आप कई हैशिंग फंक्शन ऑनलाइन पा सकते हैं।


-1

सबसे तेज विधि बाइनरी ट्री का उपयोग होगी। इसका सबसे खराब मामला भी केवल O (logn) है।


15
यह गलत है। एक द्विआधारी पेड़ के लिए सबसे खराब मामला लुक ओ (एन) है (खराब सम्मिलन आदेश के कारण पतित मामला, जिसके परिणामस्वरूप लिंक सूची, मूल रूप से) जब यह असंतुलित होता है।
रैंडी हावर्ड
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.