【问题标题】:Singly linked list - deleting/adding item at position x单链表 - 在位置 x 删除/添加项目
【发布时间】:2015-05-31 07:09:10
【问题描述】:

这是关于单链表的。我有以下代码。我现在想扩展这些代码,以便可以在某个特定位置添加/删除元素。我不知道如何去实施它。

这是我目前所拥有的:

#include <stdio.h>
#include <conio.h>
#include <stdlib.h>

struct list
{
    int amount;
    struct element *first;
};

struct element
{
    int number;
    struct element *next;
    int *temp;
};

int main()
{
    struct list lk;
    struct element *ptr, *temp;
    int amount;
    int i;

    printf("How much elements u want enter?");              
    scanf("%d", &amount);

    ptr = (struct element *) calloc(1, sizeof(struct element));   
    lk.first = ptr;                    
    lk.amount = 0;                      
    printf("Please enter 1. number :");
    scanf("%d", &(ptr->number));                
    temp = ptr;                 

    for (i = 2; i <= amount; i++)
    {
        printf("Please enter %d. number", i);
        ptr = (struct element *) calloc(1, sizeof(struct element));
        lk.amount++;                
        scanf("%d", &(ptr->number));
        ptr->next = NULL;           
        temp->next = ptr;           

        temp = ptr;
    }

    ptr = lk.first;

    while (ptr != NULL)
    {
        printf("%d \n", ptr->number);
        ptr = ptr->next;

    }

    getch();
    return 0;
}

我找到了以下方法,但我不知道如何为我的程序调整它:

void insertInList (list* L, element* position, element* new)
{   
    if (position == 0)
    {
        new->next = L->first;
        L->first= new;
        L->amount++;
    }
    else
    { 
       new->next = position->next;
       position->next = new;
       L->amount++;
    }
}

我在用户输入后对此进行了测试:

struct list lk;
struct element *ptr, *temp, number1, number2;
int amount;
int i;

printf("Which element u want add:");
scanf("%d", number1.number);


printf("On which position u want add the element?:");
scanf("%d", number2.number);

initList(&lk);
insertInList(&lk, &zahl2, &zahl1);

我得到一个 AccessViolentException,在行之后 > scanf("%d", number1.number);

【问题讨论】:

  • 这是非常基础的数据结构人员,请尝试在网络上挖掘更多信息。
  • 编写一个函数element* findPointerForPosition(list* L, int numberOfPosition),它将找到指向您要插入的位置的指针。然后将insertInList 与您的列表一起使用,指针findPointerForPosition 返回并指向您要插入的元素。
  • 您需要编写scanf("%d, &amp;number1.number),因为您需要将一个指向整数的指针传递给scanf,而不是一个值。这会导致访问冲突,因为number1.number 的值被scanf 解释为地址。

标签: c list singly-linked-list


【解决方案1】:

让我们从单链接非循环列表的 2 个特殊情况开始。第一种是添加数据的通用方法,就是继续将节点添加到列表的末尾。那里的函数可能如下所示:

/* insert node at end of list */
void insert_end (list *l, int n)
{
    struct lnode *ptr = NULL;
    if (!(ptr = calloc (1, sizeof *ptr))) {
        fprintf (stderr, "%s() error: memory exhausted.\n", __func__);
        exit (EXIT_FAILURE);
    }

    ptr->number = n;
    ptr->next = NULL;

    if (l->cnt == 0) 
    {
        l->first = ptr;
        l->cnt++;
        return;
    }

    lnode *iter = l->first;  /* pointer to iterate list */

    while (iter->next) iter = iter->next;
    iter->next = ptr;
    l->cnt++;
}

您只需为下一个元素分配存储空间(我将它们保留为节点)。您只需检查金额(重命名为cnt)是否为0。如果是这样,添加为第一个节点。如果不是,则创建一个 pointer to list 以用作 iterator 并遍历列表指针,直到 node-&gt;nextNULL 并在末尾添加新节点.

(注意:如果插入效率是关键,双链循环列表不需要迭代,只需在末尾添加一个节点,在list-&gt;prev位置添加即可,即使在具有数亿个节点的列表中也可以盲目地快速添加)

下一个变体是想在列表的开头或开头添加一个新节点。在这里你只需创建ptr-&gt;next = l-&gt;first 然后l-&gt;first = ptr

/* insert node at beginning of list */
void insert_start (list *l, int n)
{
    struct lnode *ptr = NULL;
    if (!(ptr = calloc (1, sizeof *ptr))) {
        fprintf (stderr, "%s() error: memory exhausted.\n", __func__);
        exit (EXIT_FAILURE);
    }

    ptr->number = n;

    if (l->cnt == 0) 
        ptr->next = NULL;
    else
        ptr->next = l->first;

    l->first = ptr;
    l->cnt++;
}

如何在列表中的给定位置插入一个节点。您需要验证(0 &lt;= pos &lt;= lk-&gt;cnt) 的位置(或者您可以将任何大于lk-&gt;cnt 的值设置为等于lk-&gt;cnt)。您已经了解了如何遍历节点以使用列表指针迭代 iter = lter-&gt;next 直到到达最后一个节点。到达nth 节点也不例外。要在给定位置插入,您将获得该位置,因此只需迭代 pos 次数即可到达插入点:

/* insert node at position */
void insert_pos (list *l, int n, int pos)
{
    /* validate position */
    if (pos < 0 || pos > l->cnt) {
        fprintf (stderr, "%s() error: invalid position.\n", __func__);
        return;
    }

    /* if empty or pos 0, insert_start */
    if (l->cnt == 0 || pos == 0) {
        insert_start (l, n);
        return;
    }

    struct lnode *ptr = NULL;
    if (!(ptr = calloc (1, sizeof *ptr))) {
        fprintf (stderr, "%s() error: memory exhausted.\n", __func__);
        exit (EXIT_FAILURE);
    }

    ptr->number = n;
    ptr->next = NULL;

    lnode *iter = l->first;  /* pointer to iterate list */

    while (--pos)
        iter = iter->next;

    if (iter->next)
        ptr->next = iter->next;

    iter->next = ptr;
    l->cnt++;
}

下一个变体是以数字排序顺序在列表中的任意位置添加一个节点。如果新的ptr-&gt;number = 6; 并且您已经拥有57,则在持有57 的人之间插入新的ptr注意: 下面的这个函数还处理放置第一个节点,以及小于列表中第一个节点的节点,以及将节点放置在列表的末尾。它基本上都是在寻找给定的新节点将去哪里。如果您的目标是按排序顺序插入节点,则可以将其用作唯一的输入例程,或者您可以仅使用它来填充特殊情况。

/* insert node at end of list */
void insert_ordered (list *l, int n)
{
    /* if first node of n < first->number */
    if (l->cnt == 0 || n < l->first->number) {
        insert_start (l, n);
        return;
    }

    struct lnode *ptr = NULL;
    if (!(ptr = calloc (1, sizeof *ptr))) {
        fprintf (stderr, "%s() error: memory exhausted.\n", __func__);
        exit (EXIT_FAILURE);
    }

    ptr->number = n;
    ptr->next = NULL;

    lnode *iter = l->first;  /* pointer to iterate list */

    while (iter->next && n > iter->next->number) {
        iter = iter->next;
    }

    if (iter->next)
        ptr->next = iter->next;

    iter->next = ptr;
    l->cnt++;
}

只要我们正在扩展您的列表,您就应该保持main 函数的干净,并在您完成后使用print 列表和free 分配给列表的所有内存。可以实现此目的的几个辅助函数可能是:

void prn_list (list l)
{
    lnode *ptr = l.first;
    int i = 0;
    while (ptr)
    {
        printf("   node[%2d] : %d\n", i++, ptr->number);
        ptr = ptr->next;
    }
}

void free_list (list l)
{
    lnode *ptr = l.first;

    while (ptr)
    {
        lnode *del = ptr;
        ptr = ptr->next;
        free (del);
        del = NULL;
    }
}

删除的工作方式类似。将它们放在一起,您将获得一个具有输入功能的半稳健列表。请注意,struct 还创建了 typedefs,以减少打字并提高可读性。

#include <stdio.h>
#include <stdlib.h>
// #include <conio.h>

typedef struct lnode
{
    int number;
    struct lnode *next;
} lnode;

typedef struct
{
    int cnt;
    lnode *first;
} list;

void insert_end (list *l, int n);
void insert_start (list *l, int n);
void insert_ordered (list *l, int n);
void insert_pos (list *l, int n, int pos);
void prn_list (list l);
void free_list (list l);

int main (void)
{
    list lk = { 0, NULL };

    int num = 0;
    int i = 0;

    printf ("\n number of nodes to enter: ");              
    scanf ("%d", &num);

    for (i = 0; i < num; i++)
    {
        int n = 0;
        printf (" enter node[%d]->number: ", i);
        scanf("%d", &n);
        insert_end (&lk, n);
    }

    printf ("\n The list contains '%d' nodes.\n", lk.cnt);
    printf ("\n The list nodes are:\n\n");
    prn_list (lk);

    printf ("\n enter number to add at start: ");              
    scanf("%d", &num);
    insert_start (&lk, num);

    printf ("\n The list contains '%d' nodes.\n", lk.cnt);
    printf ("\n The list nodes are:\n\n");
    prn_list (lk);

    printf ("\n enter number to add in order: ");              
    scanf("%d", &num);
    insert_ordered (&lk, num);

    printf ("\n The list contains '%d' nodes.\n", lk.cnt);
    printf ("\n The list nodes are:\n\n");
    prn_list (lk);

    printf ("\n enter number to add at position: ");              
    scanf("%d", &num);
    printf ("\n position must be (0 <= pos <= %d)\n", lk.cnt);
    printf ("\n enter position in list for '%d': ", num);
    scanf("%d", &i);
    insert_pos (&lk, num, i);

    printf ("\n The list contains '%d' nodes.\n", lk.cnt);
    printf ("\n The list nodes are:\n\n");
    prn_list (lk);

    printf ("\n Freeing list memory:\n\n");
    free_list (lk);

    //getch();
    return 0;
}

/* insert node at end of list */
void insert_end (list *l, int n)
{
    struct lnode *ptr = NULL;
    if (!(ptr = calloc (1, sizeof *ptr))) {
        fprintf (stderr, "%s() error: memory exhausted.\n", __func__);
        exit (EXIT_FAILURE);
    }

    ptr->number = n;
    ptr->next = NULL;

    if (l->cnt == 0) 
    {
        l->first = ptr;
        l->cnt++;
        return;
    }

    lnode *iter = l->first;  /* pointer to iterate list */

    while (iter->next) iter = iter->next;
    iter->next = ptr;
    l->cnt++;
}

/* insert node at beginning of list */
void insert_start (list *l, int n)
{
    struct lnode *ptr = NULL;
    if (!(ptr = calloc (1, sizeof *ptr))) {
        fprintf (stderr, "%s() error: memory exhausted.\n", __func__);
        exit (EXIT_FAILURE);
    }

    ptr->number = n;

    if (l->cnt == 0) 
        ptr->next = NULL;
    else
        ptr->next = l->first;

    l->first = ptr;
    l->cnt++;
}

/* insert node at end of list */
void insert_ordered (list *l, int n)
{
    /* if first node of n < first->number */
    if (l->cnt == 0 || n < l->first->number) {
        insert_start (l, n);
        return;
    }

    struct lnode *ptr = NULL;
    if (!(ptr = calloc (1, sizeof *ptr))) {
        fprintf (stderr, "%s() error: memory exhausted.\n", __func__);
        exit (EXIT_FAILURE);
    }

    ptr->number = n;
    ptr->next = NULL;

    lnode *iter = l->first;  /* pointer to iterate list */

    while (iter->next && n > iter->next->number)
        iter = iter->next;

    if (iter->next)
        ptr->next = iter->next;

    iter->next = ptr;
    l->cnt++;
}

/* insert node at position */
void insert_pos (list *l, int n, int pos)
{
    /* validate position */
    if (pos < 0 || pos > l->cnt) {
        fprintf (stderr, "%s() error: invalid position.\n", __func__);
        return;
    }

    /* if pos 0, insert_start */
    if (l->cnt == 0 || pos == 0) {
        insert_start (l, n);
        return;
    }

    struct lnode *ptr = NULL;
    if (!(ptr = calloc (1, sizeof *ptr))) {
        fprintf (stderr, "%s() error: memory exhausted.\n", __func__);
        exit (EXIT_FAILURE);
    }

    ptr->number = n;
    ptr->next = NULL;

    lnode *iter = l->first;  /* pointer to iterate list */

    while (--pos)
        iter = iter->next;

    if (iter->next)
        ptr->next = iter->next;

    iter->next = ptr;
    l->cnt++;
}

/* print all nodes in list */
void prn_list (list l)
{
    lnode *ptr = l.first;
    int i = 0;
    while (ptr)
    {
        printf("   node[%2d] : %d\n", i++, ptr->number);
        ptr = ptr->next;
    }
}

/* free memory for all nodes */
void free_list (list l)
{
    lnode *ptr = l.first;

    while (ptr)
    {
        lnode *del = ptr;
        ptr = ptr->next;
        free (del);
        del = NULL;
    }
}

使用/输出

$ ./bin/ll_single_ins

 number of nodes to enter: 3
 enter node[0]->number: 5
 enter node[1]->number: 7
 enter node[2]->number: 9

 The list contains '3' nodes.

 The list nodes are:

   node[ 0] : 5
   node[ 1] : 7
   node[ 2] : 9

 enter number to add at start: 2

 The list contains '4' nodes.

 The list nodes are:

   node[ 0] : 2
   node[ 1] : 5
   node[ 2] : 7
   node[ 3] : 9

 enter number to add in order: 6

 The list contains '5' nodes.

 The list nodes are:

   node[ 0] : 2
   node[ 1] : 5
   node[ 2] : 6
   node[ 3] : 7
   node[ 4] : 9

 enter number to add at position: 4

 position must be (0 <= pos <= 5)

 enter position in list for '4': 4

 The list contains '6' nodes.

 The list nodes are:

   node[ 0] : 2
   node[ 1] : 5
   node[ 2] : 6
   node[ 3] : 7
   node[ 4] : 4
   node[ 5] : 9

 Freeing list memory:

valgrind 内存错误检查

$ valgrind ./bin/ll_single_ins
==22898== Memcheck, a memory error detector
==22898== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==22898== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==22898== Command: ./bin/ll_single_ins
==22898==

number of nodes to enter: 3
enter node[0]->number: 5
enter node[1]->number: 7
enter node[2]->number: 9

The list contains '3' nodes.
<snip>

==22519== HEAP SUMMARY:
==22519==     in use at exit: 0 bytes in 0 blocks
==22519==   total heap usage: 5 allocs, 5 frees, 80 bytes allocated
==22519==
==22519== All heap blocks were freed -- no leaks are possible
==22519==
==22519== For counts of detected and suppressed errors, rerun with: -v
==22519== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)

【讨论】:

  • 感谢您的澄清。
【解决方案2】:

要在“条目 N”之后添加条目,您只需搜索“条目 N”。然后你会做new_entry-&gt;next = current_entry-&gt;next; current_entry-&gt;next = new_entry;

对于在“entry N”之前添加一个entry,以及对于删除“entry N”,需要在查找的时候跟踪前一个entry的地址;这样当你找到“条目 N”时,你仍然知道上一个条目的地址。

在这种情况下,删除“条目 N”大部分变为previous_entry-&gt;next = current_entry-&gt;next;;并在“条目 N”之前插入一个新条目变为new_entry-&gt;next = previous_entry-&gt;next; previous_entry-&gt;next = new_entry;。但是,对于这两种情况,对于“N == 0”,前一个条目可能不存在。在这种情况下,您必须修改列表的头部而不是条目。

例如(删除,其中something 是您的结构类型的名称):

int deleteEntry(int n, something **headPtr) {
    something *previous = NULL;
    something *current = *headPtr;

    while( (current != NULL) && (n > 0) ) {
        previous = current;
        current = current->next;
        n--;
    }
    if(current == NULL) {
         return ERROR;    // End of list reached before N found
    }
    if(previous == NULL) {
        *headPtr = current->next;
    } else {
        previous->next = current->next;
    }
    free(current);
    return OK;
}

注意:完全可以更早地处理n == 0 的情况(在while 循环之前)。我这样写是为了方便修改搜索条件并将其转换为删除满足其他条件的第一个条目的内容。

【讨论】:

    【解决方案3】:

    将元素插入列表的功能还可以,但我有一些建议:

    • 不要将变量命名为与关键字相同的名称(例如 new)。这会让其他人很困惑,我不确定是否所有编译器都允许这样做......
    • 在纯 C 中,您需要编写例如struct list* L 而不是 list* L(或者您添加一个 typedef,例如 typedef struct list LIST;,它允许您编写 LIST *L 而不是 struct list* L)。
    • 在使用指针之前,请务必确保它们不为 NULL。
    • 为您的函数添加返回值,以便调用者能够区分成功和失败。

    我写了以下函数:

    void initList(struct list* L)
    {
        if (L)
        {
            L->amount = 0;
            L->first = NULL;
        }
    }
    
    int insertInList(struct list* L, struct element* position, struct element* newElem)
    {
        int result = 0;
        struct element *iterator;
    
    
        if (L)
        {
            /* Check if newElem is already within list and if so don't add it again! */
            for (iterator = L->first; iterator != NULL; iterator = iterator->next)
            {
                if (iterator == newElem)
                {
                    break;
                }
            }
    
            if (iterator != newElem)   /* newElem not within list, yet? */
            {
                if (position != 0)
                {
                    newElem->next = position->next;
                    position->next = newElem;
                }
                else
                {
                    newElem->next = L->first;
                    L->first= newElem;
                }
    
                L->amount++;
                result = 1;
            }
        }
    
        return (result);
    }
    
    
    int deleteFromList(struct list* L, struct element* elem)
    {
        int result = 0;
        struct element *iterator;
    
        if ((L) && (L->amount > 0))   /* list with elements in it? */
        {
            if (L->first != elem)   /* elem is not the first element? */
            {
                /* iterator all items to find entry preceeding elem */
                for (iterator = L->first; iterator != NULL; iterator = iterator->next)
                {
                    if (iterator->next == elem)
                    {
                        iterator->next = elem->next;  /* set next element to elemen after elem */
    
                        result = 1;
                        break;
                    }
                }
            }
            else
            {
                L->first = elem->next;   /* set new head of list */
    
                result = 1;
            }
    
    
            if (result == 1)   /* item deleted? */
            {
                L->amount--;
                elem->next = NULL;   /* ensure next pointer of elem does not point into list! */
            }
        }
    
        return (result);
    }
    
    
    int deleteIndexFromList(struct list* L, int iElement)   /* iElement is zero based: 0=first element, 1=second element, ... */
    {
        int result = 0;
        struct element *iterator;
    
    
        if ((L) && (L->amount >= iElement))
        {
            /* iterator all items to find entry preceeding elem */
            for (iterator = L->first; iterator != NULL; iterator = iterator->next)
            {
                if (iElement == 0)
                {
                    result = deleteFromList(L, iterator);
                    break;
                }
                iElement--;
            }
        }
    
        return (result);
    }
    

    您可以使用调试器和以下测试程序来测试代码:

    int main(void)
    {
        struct list L;
        struct element A, B, C, D;
    
    
        A.number = 5;
        B.number = 10;
        C.number = 15;
        D.number = 20;
    
    
        initList(&L);
        insertInList(&L, NULL, &A);
        insertInList(&L, &A, &B);
        insertInList(&L, &B, &C);
        insertInList(&L, &C, &D);
        /* now your list is 5 -> 10 -> 15 -> 20 */
    
        deleteFromList(&L, &A);
        /* now your list is 10 -> 15 -> 20 */
    
        deleteFromList(&L, &C);
        /* now your list is 10 -> 20 */
    
        return (0);
    }
    

    【讨论】:

    • 谢谢你,我要试试这个!...但是你能不能添加删除特定位置元素的方法?
    • 您的意思是删除例如第三个元素的功能?
    • 我做了一些修改并添加了一个新函数deleteIndexFromList
    • 谢谢,我测试了 insertInList 方法,但我得到了一个异常......我要编辑我的线程开启器,除了异常和我的代码...... :)
    • 你知道为什么这不起作用吗?当你需要更多信息时告诉我。
    猜你喜欢
    • 1970-01-01
    • 2014-07-09
    • 1970-01-01
    • 2011-11-17
    • 2015-10-02
    • 2020-11-17
    • 1970-01-01
    • 1970-01-01
    • 2011-07-09
    相关资源
    最近更新 更多