【问题标题】:Rename a list with value of another one用另一个值重命名一个列表
【发布时间】:2015-07-28 20:41:04
【问题描述】:

我尝试实现一个用 C 语言编写的程序,其中有两个链表,我需要创建第三个链表,其中第一个链表的所有值最终用第二个链表的值重命名按他们的顺序。更新的第三个列表中的任何值都不应该重复,它应该返回错误。

查看下面给出的示例以了解该程序的工作原理:

例 1

A = [a, b, c, d]
B = [e, f]

第三个将是:

C = [e, f, c, d]

例 2

A = [a, b, c, d]
B = [a, e]

第三个将是:

C = [a, e, c, d]

例 3

A = [a, b, c, d]
B = [c, d]

这应该返回一个错误,因为 C 将是

C = [c, d c, d] 

但它不能有重复的值。

例 4

A = [a, b, c, d]
B = [b, a]

这不应该返回任何错误,因为 C 将是

C = [b, a, c, d] 

(没有重复值,列表 A 的前两个元素将被重命名为列表 B 的前两个元素)。

您可以在下面找到我的想法,但我对这个问题的不同解决方案感兴趣

T //Temp
C //Result

for (int i = 0; i < |A|; i++) 
{
    if(i > length of B && T is not empty)
    {
        //One or more elements were not been renamed
        return ERROR
    }

    if(A[i] == B[i])
    {
        C[i] = B[i];
    }
    else
    {
        C[i] = B[i];

        if(T contains A[i])
        {
            pop A[i] from T
        }
        else
        {
            push A[i] in T
        }
    }

}

编辑

背景:该算法支持从给定字段名称列表 (B) 的具体表 (A) 创建别名表 (C)。

  • 每个列表/表格不能包含重复值。
  • B 的长度小于或等于 A 的长度(我无法重命名已有的更多值)

【问题讨论】:

  • A = [a, b, a, d] B = [b, a] 的情况下你想要的输出是什么?
  • 你还在坚持链表吗?看起来很适合数组。
  • 我已经指定 A 和 B 都不包含重复值(这个假设是先验的)
  • 所以你的问题是减少到先用B 元素构建一个新列表,然后再用A 元素构建一个没有第一个len(B) 元素的新列表。或C=concat(B, A[len(B)..])。问题是什么?一个简单的循环就可以了。
  • 你认为length(B) &lt;= length(A)?如果不是,C 会是什么样子?示例:A=[a], B=[b,c]C 成为 [b][b,c]

标签: c linked-list rename


【解决方案1】:

这可以在 Python 中轻松完成。像这样的:

def merge_lists(list1, list2):
    if len(list2) > len(list1):
        raise ValueError("list2 cannot be of greater length than list1")
    list3 = list(list1)  # make copy of list1
    list_after_overlap = list1[len(list2):]  # get slice of list1 starting from end of list2
    index = 0
    for item in list2:
        if item not in list_after_overlap:
            list3[index] = item
        else:
            raise ValueError("merged list would have repeated values")
        index += 1
    return list3

这不仅仅是 Python 的炫耀,尽管它可以说是完成这项工作的更好工具。这也像伪代码一样读起来很好,并且对我们的算法进行了原型化和测试,我们可以在 C 中实现相同的逻辑,我们只需要一些辅助函数。

正如已经指出的那样,一个简单的数组就可以了,由于您没有在您的问题中指定数据类型,我们将使用一个普通的旧 int:

merged_list_t merge_lists(const list_t list1, const list_t list2)
{
    merged_list_t result;
    result.merged_list.list_length = 0;
    result.merged_list.values = NULL;
    if (list2.list_length > list1.list_length)
    {
        result.error = ERROR_LENGTH_EXCEEDED;
    }
    else
    {
        result.merged_list.values = (int*)malloc(sizeof(int)*(list1.list_length));
        result.error = (result.merged_list.values == NULL) ? ERROR_MEMORY : ERROR_NONE;
    }

    if (result.error == ERROR_NONE)
    {
        replicate_list(&result.merged_list, list1, list2.list_length);
        list_t list_after_overlap = get_list_slice(list1, list2.list_length);
        for (size_t index = 0; index < list2.list_length && result.error == ERROR_NONE; index++)
        {
            if (!item_in_list(list2.values[index], list_after_overlap))
            {
                result.merged_list.values[index] = list2.values[index];
            }
            else
            {
                result.error = ERROR_REPEATED_VALUES;
                free(result.merged_list.values);  /* we won't be needing this anymore */
                result.merged_list.values = NULL;
                result.merged_list.list_length = 0;
            }
        }
    }

    return result;
}

如您所见,算法基本相同,但有所改进。在 python 代码中,我们复制了整个 list1,然后覆盖了重叠的值,这对于大型列表可能是有意义的浪费。现在我们只得到重叠之后的部分,它从list2之后开始,并用它来测试重复。这是完整的代码,在 main 上有一些基本测试的 hack:

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

typedef enum {
    ERROR_NONE,             /* success */
    ERROR_REPEATED_VALUES,  /* merged list would have repeated values */
    ERROR_LENGTH_EXCEEDED,   /* second list length exceeds that of first list */
    ERROR_MEMORY            /* could not allocate memory for the merged list */
} error_t;

typedef struct {
    int *values;         /* pointer to int array that contains the list values */
    size_t list_length;  /* the number of elements in the list (array) */
}list_t;

typedef struct {
    list_t merged_list;       /* has pointer (values) to merged list (array), which MUST be freed */
    error_t error;            /* indicates success or the error code of the merge operation */
}merged_list_t;

typedef enum {FALSE=0, TRUE=1} bool_t; /* use stdbool.h preferably, if available */


/* === Test Utility functions */
static void print_list(const list_t list)
{
    putchar('[');
    for (size_t index = 0; index < list.list_length; index++)
    {
        printf("%d", list.values[index]);
        if (index < list.list_length - 1)
        {
            printf(", ");
        }
    }
    printf("]\n");
}


static void print_merged_list(const merged_list_t list)
{
    if (list.merged_list.values != NULL && list.error == ERROR_NONE)
    {
        print_list(list.merged_list);
    }
    else
    {
        switch (list.error)
        {
        case ERROR_NONE: printf("Merged list is null (empty)\n"); break;
        case ERROR_LENGTH_EXCEEDED: printf("Error: Length of list2 is greater than length of list1\n"); break;
        case ERROR_MEMORY: printf("Error: Unable to allocate memory for result\n"); break;
        case ERROR_REPEATED_VALUES: printf("Error: Merged list would have duplicate entries\n"); break;
        default: printf("Unexpected or unhandled error\n"); break;
        }
    }
}


/* utility functions */
static void replicate_list(list_t *new_list, const list_t list, size_t start)
{
    for (size_t index = start; index < list.list_length; index++)
    {
        new_list->values[index] = list.values[index];
    }
    new_list->list_length = list.list_length;
}


static list_t get_list_slice(const list_t list, size_t start_index)
{
    list_t sliced_list;
    if (list.values != NULL && start_index < list.list_length)
    {
        sliced_list.values = list.values + start_index;
        sliced_list.list_length = list.list_length - start_index;
    }
    return sliced_list;
}


static bool_t item_in_list(int item, const list_t list)
{
    bool_t in_list = FALSE;
    for (size_t i=0; i < list.list_length && !in_list; i++)
    {
        in_list = (item == list.values[i]) ? TRUE : FALSE;
    }
    return in_list;
}


/* 
    Produces a merged list which consists of list1 replaced by the overlapping elements of list2,
    as long as the resulting list does not cause elements of the merged list to be repeated.
    Input:
        list1[]: int array of arbitrary length consisting of unique elements
        list2[]: int array of length smaller than of list1 consisting of unique elements
    Returns:
        A merged_list_t structure containing the merged list structure (which MUST be freed) and its length
        or an error code if the lists are of invalid length or the merge operation produces duplicate values
 */
merged_list_t merge_lists(const list_t list1, const list_t list2)
{
    merged_list_t result;
    result.merged_list.list_length = 0;
    result.merged_list.values = NULL;
    if (list2.list_length > list1.list_length)
    {
        result.error = ERROR_LENGTH_EXCEEDED;
    }
    else
    {
        result.merged_list.values = (int*)malloc(sizeof(int)*(list1.list_length));
        result.error = (result.merged_list.values == NULL) ? ERROR_MEMORY : ERROR_NONE;
    }

    if (result.error == ERROR_NONE)
    {
        replicate_list(&result.merged_list, list1, list2.list_length);
        list_t list_after_overlap = get_list_slice(list1, list2.list_length);
        for (size_t index = 0; index < list2.list_length && result.error == ERROR_NONE; index++)
        {
            if (!item_in_list(list2.values[index], list_after_overlap))
            {
                result.merged_list.values[index] = list2.values[index];
            }
            else
            {
                result.error = ERROR_REPEATED_VALUES;
                free(result.merged_list.values);  /* we won't be needing this anymore */
                result.merged_list.values = NULL;
                result.merged_list.list_length = 0;
            }
        }
    }

    return result;
}


void main(void)
{
    printf("Testing basic scenario\n");
    int a1[] = { 1, 2, 3, 4, 5 };
    int a2[] = { 6, 7 };
    list_t l1;
    l1.list_length = sizeof(a1) / sizeof(a1[0]);  /* get number of elements */
    l1.values = a1;

    list_t l2;
    l2.list_length = sizeof(a2) / sizeof(a2[0]);
    l2.values = a2;

    merged_list_t ml = merge_lists(l1, l2);
    print_list(l1);
    print_list(l2);
    print_merged_list(ml);
    free(ml.merged_list.values);

    printf("Testing merge with duplicate values\n");
    int a3[] = { 1, 2, 3, 4, 5 };
    int a4[] = { 4, 6, 8 };
    list_t l3;
    l3.list_length = sizeof(a3) / sizeof(a3[0]);  /* get number of elements */
    l3.values = a3;

    list_t l4;
    l4.list_length = sizeof(a4) / sizeof(a4[0]);
    l4.values = a4;

    merged_list_t ml2 = merge_lists(l3, l4);
    print_list(l3);
    print_list(l4);
    print_merged_list(ml2);
    free(ml2.merged_list.values);

    printf("Testing list2 with value from list1\n");
    int a5[] = { 1, 2, 3, 4, 5 };
    int a6[] = { 3, 6, 9 };
    list_t l5;
    l5.list_length = sizeof(a5) / sizeof(a5[0]);  /* get number of elements */
    l5.values = a5;

    list_t l6;
    l6.list_length = sizeof(a6) / sizeof(a6[0]);
    l6.values = a6;

    merged_list_t ml3 = merge_lists(l5, l6);
    print_list(l5);
    print_list(l6);
    print_merged_list(ml3);
    free(ml3.merged_list.values);

    _getch();
}

但是您特别要求链接列表,所以要最终回答您的问题,这是一种方法:

merged_list_t merge_lists(const list_t list1, const list_t list2)
{
    merged_list_t result;
    list_t list_after_overlap;
    initialize_list(&result.merged_list);
    if (list2.list_length > list1.list_length)
    {
        result.error = ERROR_LENGTH_EXCEEDED;
    }
    else
    {
        bool_t success = replicate_list(&list_after_overlap, list1, list2.list_length);
        result.error = (success) ? ERROR_NONE: ERROR_MEMORY;
    }

    if (result.error == ERROR_NONE)
    {
        for (list_item_t *item = list2.head; item != NULL && result.error == ERROR_NONE; item = item->next)
        {
            if (!item_in_list(*item->value, list_after_overlap))
            {
                /* duplicate each item and append to merged_list */
                bool_t success = append_list_item(&result.merged_list, new_list_item(*item->value));
                result.error = success ? ERROR_NONE : ERROR_MEMORY;
            }
            else
            {
                result.error = ERROR_REPEATED_VALUES;
                destroy_list(&list_after_overlap);
                destroy_list(&result.merged_list);
            }
        }
    }

    if (result.error == ERROR_NONE)
    {
        /* join overlap with difference */
        result.merged_list.tail->next = list_after_overlap.head;
        list_after_overlap.head = result.merged_list.head;
        result.merged_list.tail = list_after_overlap.tail;
    }

    return result;
}

同样的逻辑,合并函数和友元只是被重构以处理链表。完整代码如下:

/* Enable debug heap functions (Visual Studio) */
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>

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


typedef enum {
    ERROR_NONE,             /* success */
    ERROR_REPEATED_VALUES,  /* merged list would have repeated values */
    ERROR_LENGTH_EXCEEDED,   /* second list length exceeds that of first list */
    ERROR_MEMORY            /* could not allocate memory for the merged list */
} error_t;

typedef struct list_item_ {
    int *value;  /* pointer to single value */
    list_item_ *next;
    list_item_ *previous;
}list_item_t;

typedef struct {
    list_item_t *head;
    list_item_t *tail;
    size_t list_length;
}list_t;

typedef struct {
    list_t merged_list;   /* linked list with result, MUST be freed */
    error_t error;        /* indicates success or the error code of the merge operation */
}merged_list_t;

typedef enum {FALSE=0, TRUE=1} bool_t;


/* === Test Utility functions === */
static void print_list(const list_t list)
{
    putchar('[');
    for (list_item_t *item = list.head; item != NULL; item = item->next)
    {
        printf("%d", *item->value);
        if (item->next != NULL)
        {
            printf(", ");  /* add comma if it's not the last item (tail) */
        }
    }
    printf("]\n");
}


static void print_merged_list(const merged_list_t list)
{
    if (list.merged_list.head != NULL && list.error == ERROR_NONE)
    {
        print_list(list.merged_list);
    }
    else
    {
        switch (list.error)
        {
        case ERROR_NONE: printf("Merged list head is null (empty list)\n"); break;
        case ERROR_LENGTH_EXCEEDED: printf("Error: Length of list2 is greater than length of list1\n"); break;
        case ERROR_MEMORY: printf("Error: Unable to allocate memory for result\n"); break;
        case ERROR_REPEATED_VALUES: printf("Error: Merged list would have duplicate entries\n"); break;
        default: printf("Unexpected or unhandled error\n"); break;
        }
    }
}


/* helper functions */
static void initialize_list(list_t *list)
{
    list->head = NULL;
    list->tail = NULL;
    list->list_length = 0;
}


static list_item_t* new_list_item(int value)
{
    list_item_t *item = (list_item_t*)malloc(sizeof(list_item_t));
    if (item != NULL)
    {
        item->value = (int*)malloc(sizeof(int));
        if (item->value != NULL)
        {
            *item->value = value;
            item->next = NULL;
            item->previous = NULL;
        }
        else
        {
            free(item);
            item = NULL;
        }
    }
    return item;
}


static bool_t append_list_item(list_t *list, list_item_t *item)
{
    bool_t success = TRUE;

    if (item == NULL)
    {
        success = FALSE;
    }
    else
    {
        if (list->head == NULL)
        {
            /* first item, set as head and tail */
            list->head = item;
            list->head->next = NULL;
            list->head->previous = NULL;
            list->tail = item;
        }
        else
        {
            /* item (new tail) will be preceded by the current tail */
            item->previous = list->tail;
            /* link current tail to new item */
            list->tail->next = item;
            /* make item the new tail */
            list->tail = item;
            list->tail->next = NULL;
        }
        list->list_length++;
    }
    return success;
}


static bool_t set_list_values(list_t *list, const int *values, size_t values_length)
{
    bool_t success = TRUE;
    initialize_list(list);
    for (size_t index = 0; index < values_length && success; index++)
    {
        list_item_t *item = new_list_item(values[index]);
        success = append_list_item(list, item);
    }
    if (success)
    {
        list->list_length = values_length;
    }

    return success;
}


static void destroy_list(list_t *list)
{
    list_item_t *next_item = NULL;
    for (list_item_t *item = list->head; item != NULL; item = next_item)
    {
        next_item = item->next;
        free(item->value);
        item->value = NULL;
        free(item);
        item = NULL;
    }
    list->list_length = 0;
    list->head = NULL;
    list->tail = NULL;
}


static bool_t replicate_list(list_t *new_list, const list_t list, const size_t start)
{
    size_t count = 0;
    list_item_t *item;
    bool_t success = TRUE;

    initialize_list(new_list);
    for (item = list.head; item != NULL && success; item = item->next, count++)
    {
        /* skip items before start */
        if (count >= start)
        {
            /* create new list with remaining items */
            success = append_list_item(new_list, new_list_item(*item->value));
        }
    }
    if (!success)
    {
        destroy_list(new_list);
    }

    return success;
}


static bool_t item_in_list(int item, const list_t list)
{
    bool_t in_list = FALSE;
    for (list_item_t *l_item = list.head; (l_item != NULL) && !in_list; l_item = l_item->next)
    {
        in_list = (item == *l_item->value) ? TRUE : FALSE;
    }

    return in_list;
}


/* 
    Produces a merged list which consists of list1 replaced by the overlapping elements of list2,
    as long as the resulting list does not cause elements of the merged list to be repeated.
    Input:
        list1[]: a linked list of arbitrary length consisting of unique elements
        list2[]: a linked list of length less than or equal to the length of list 1, also with unique elements
    Returns:
        A merged_list_t structure containing the merged linked list (which MUST be freed) and its length
        or an error code if the lists are of invalid length or the merge operation produces duplicate values
 */
merged_list_t merge_lists(const list_t list1, const list_t list2)
{
    merged_list_t result;
    list_t list_after_overlap;
    initialize_list(&result.merged_list);
    if (list2.list_length > list1.list_length)
    {
        result.error = ERROR_LENGTH_EXCEEDED;
    }
    else
    {
        bool_t success = replicate_list(&list_after_overlap, list1, list2.list_length);
        result.error = (success) ? ERROR_NONE: ERROR_MEMORY;
    }

    if (result.error == ERROR_NONE)
    {
        for (list_item_t *item = list2.head; item != NULL && result.error == ERROR_NONE; item = item->next)
        {
            if (!item_in_list(*item->value, list_after_overlap))
            {
                /* duplicate each item and append to merged_list */
                bool_t success = append_list_item(&result.merged_list, new_list_item(*item->value));
                result.error = success ? ERROR_NONE : ERROR_MEMORY;
            }
            else
            {
                result.error = ERROR_REPEATED_VALUES;
                destroy_list(&list_after_overlap);
                destroy_list(&result.merged_list);
            }
        }
    }

    if (result.error == ERROR_NONE)
    {
        /* join overlap with difference */
        result.merged_list.tail->next = list_after_overlap.head;
        list_after_overlap.head = result.merged_list.head;
        result.merged_list.tail = list_after_overlap.tail;
    }

    return result;
}


void main(void)
{
    printf("Testing basic scenario\n");
    int a1[] = { 1, 2, 3, 4, 5 };
    int a2[] = { 6, 7 };
    list_t l1;
    set_list_values(&l1, a1, sizeof(a1) / sizeof(a1[0]));
    list_t l2;
    set_list_values(&l2, a2, sizeof(a2) / sizeof(a2[0]));

    print_list(l1);
    print_list(l2);
    merged_list_t ml = merge_lists(l1, l2);
    print_merged_list(ml);
    destroy_list(&l1);
    destroy_list(&l2);
    destroy_list(&ml.merged_list);


    printf("Testing merge with duplicate values\n");
    int a3[] = { 1, 2, 3, 4, 5 };
    int a4[] = { 4, 6, 8 };
    list_t l3;
    set_list_values(&l3, a3, sizeof(a3) / sizeof(a3[0]));
    list_t l4;
    set_list_values(&l4, a4, sizeof(a4) / sizeof(a4[0]));

    print_list(l3);
    print_list(l4);
    ml = merge_lists(l3, l4);
    print_merged_list(ml);
    destroy_list(&l3);
    destroy_list(&l4);
    destroy_list(&ml.merged_list);


    printf("Testing list2 with value from list1\n");
    int a5[] = { 1, 2, 3, 4, 5 };
    int a6[] = { 3, 6, 9 };
    list_t l5;
    set_list_values(&l5, a5, sizeof(a5) / sizeof(a5[0]));
    list_t l6;
    set_list_values(&l6, a6, sizeof(a6) / sizeof(a6[0]));

    print_list(l5);
    print_list(l6);
    ml = merge_lists(l5, l6);
    print_merged_list(ml);
    destroy_list(&l5);
    destroy_list(&l6);
    destroy_list(&ml.merged_list);
    printf("Try printing empty list...\n");
    print_merged_list(ml);

    /* print memory leak report (Visual Studio)*/
    _CrtDumpMemoryLeaks();
    _getch();
}

如果您使用的是 Visual Studio,请注意 _CrtDumpMemoryLeaks() 宏,这对于检测内存泄漏非常方便。当然,如果你很幸运能够运行 Linux,只需摆脱它并改用 Valgrind。在我的系统上,它是干净的。

这是 main 生成的输出示例:

测试基本场景

[1, 2, 3, 4, 5]

[6, 7]

[6, 7, 3, 4, 5]

使用重复值测试合并

[1, 2, 3, 4, 5]

[4,6,8]

错误:合并的列表会有重复的条目

使用 list1 中的值测试 list2

[1, 2, 3, 4, 5]

[3,6,9]

[3, 6, 9, 4, 5]

尝试打印空列表...

合并列表头为空(空列表)

【讨论】:

    猜你喜欢
    • 2011-10-02
    • 1970-01-01
    • 2021-03-08
    • 1970-01-01
    • 2015-12-06
    • 2012-05-11
    • 1970-01-01
    • 1970-01-01
    • 2020-01-30
    相关资源
    最近更新 更多