【问题标题】:Deleting Consecutive Element in a Linked List删除链表中的连续元素
【发布时间】:2016-11-06 12:26:38
【问题描述】:

给定以下链表的定义

typedef struct elemento {
    int inf;
    struct elemento *next;
} lista;

我正在尝试创建一个函数

lista *SeekAndDestroy(lista *p, int k);

给定一个列表 *p 和一个正整数 k,它在列表中搜索第一个和正好为 k 的连续元素序列,并从列表中消除这些元素。

我的尝试:

lista *SeekAndDestroy(lista *p, int k) {
    lista *a, *nuovo;
    int x = 0;

    a = (lista *)malloc(sizeof(lista));
    a->inf = p->inf;
    nuovo = a;
    p = p->next;

    while (p != NULL) {
        if (p->next != NULL) {
            if ((p->inf + p->next->inf) == k) {
                if (x != 1) {
                    p = p->next->next;
                    x = 1;
                    continue;
                }
            }
        }
        nuovo->next = (lista *)malloc(sizeof(lista));
        nuovo = nuovo->next;
        nuovo->inf = p->inf;
        p = p->next;
    }
    nuovo->next = NULL;
    return a;
}

我的解决方案有两个主要问题:
1) 最多删除两个连续的元素,而不是更多
2)如果要删除的项目是前两个,该功能不起作用 我怎么解决这个问题?谢谢

【问题讨论】:

  • 一个通用提示是不要修改输入参数,例如lista *p,而是使用其他指针在列表中移动。这可能会提示解决您的第一个问题。

标签: c algorithm linked-list


【解决方案1】:

现在,让我们忘记链表、指针和其他东西。比如说,我们必须解决给定数组的问题。我们可以这样做吗?当然!

for (int i = 0; i < array.length; ++i) {
    for (int j = i; j < array.length; ++j) {
        int sum = getRangeSum(array, i, j);
        if (sum != k) continue;

        // construct new array and return
    }
}

此代码可以进一步优化,但现在让我们保持简单。因此,在链表中,可以使用类似的方法。而且删除部分也很简单。您可以保留一个变量来跟踪 i 的前一个节点。我们称之为iParent。现在,我们可以将 [i, j] 段删除为iParent-&gt;next = j-&gt;next

显然,您需要考虑一些极端情况,例如是否找不到此类段或该段是否从链表的开头开始等。

【讨论】:

  • 你的回答对我很有用
【解决方案2】:

这是我为解决您面临的两个问题和任何其他边界条件而编写的函数:

list* Seek_Destroy(list* head, int target){
    if(head == NULL)
        return NULL;
    list* temp = head;
    bool find_complete = false;

    list *prev = temp;
    //Loop for iterating until list is complete or target sum is found.
    while( !find_complete){
        //Create a pointer for loop calculations
        list* sum_loop = temp;
        //Initialize sum to 0
        int sum =0;
        //Loop for checking whether the sum is equal to the target
        while(sum <= target && sum_loop->next!= NULL){
            //Keep adding the sum
            sum += sum_loop->inf;

            if(sum == target){
                //Set the flag for exiting outer loop
                find_complete = true;
                //Test whether head of the list is included in the sum
                if (temp == head){
                    //Make head point to the struct after the last element included in the sum loop
                    head = sum_loop->next;
                    //Delete and free memory
                    while(temp!= sum_loop){
                        list* del = temp;
                        temp = temp->next;
                        free(del);
                    }
                }else {
                    //Set the next pointer of previous element  of the list to point after the last element included in the sum loop
                    prev->next= sum_loop->next;
                    //Delete and free memory
                    while(temp!= sum_loop){
                        list* del = temp;
                        temp = temp->next;
                        free(del);
                    }
                }           
            break;
            }
            //Increment the pointer for the sum calculation         
            sum_loop = sum_loop->next;
        } 
        prev = temp;
        //Make temp point to next element in the list
        temp = temp->next;
        //IF entire list is traversed set the flag to true
        if (temp ==NULL){
            find_complete = true;
        }       
    }

    return head;
}

【讨论】:

    【解决方案3】:

    假设您的数字都是非负数,您可以使用更有效的算法。您只需在列表中运行两个指针 ptrAptrB,保持包含元素的总和。

    如果总和不是你需要的,你做两件事之一。首先,如果您当前的总和小于所需的总和,则通过推进ptrB 将下一个元素带入数组。

    如果您当前的总和比您需要的多更多,您可以通过推进ptrA 取出范围中的第一个元素。当然,这两个操作都应该调整当前范围和。这里有一个极端情况,如果当前范围内只有一个项目,您不想这样做。

    不用说,如果当前范围总和等于您需要的,您只需删除该范围并退出。

    就伪代码而言,它是这样的:

    def delExact(list, desiredSum):
        # Check non-empty and start range.
    
        if list is empty:
            return
        ptrA = list.first
        ptrB = ptrA
        rangeSum = ptrA.value
    
        # Continue until match found
    
        while rangeSum is not equal to desiredSum:
            # Select add-another or remove-first.
    
            if ptrA == ptrB, or rangeSum < desiredSum:
                # Need to bring another in, returning if list exhausted.
    
                ptrB = ptrB.next
                if ptrB == null:
                    return
                rangeSum = rangeSum + ptrB.value
            else:
                # Need to remove one.
    
                rangeSum = rangeSum - ptrA.value
                ptrA = ptrA.next
    
        # If we exit the loop, we've found a sum match.
        # Hence we need to delete ptrA through ptrB inclusive.
    

    但是,如果允许负数,这种两指针方法就会失效,因为您实际上并不知道后面的元素可能会产生什么影响。

    在这种情况下,您基本上必须对所有可能性进行详尽的搜索,这基本上可以归结为:

    for each element in list:
        for each possible segment from that element on:
            check and act on summed data
    

    这实际上更像是一种英文表示,这种野兽的伪代码如下:

    def delExact(list, desiredSum):
        # For each list element.
    
        ptrA = list.first
        while ptrA is not null:
            # For each possible segment starting at that element.
    
            segmentSum = 0
            ptrB = ptrA
            while ptrB is not null:
                add ptrB.value to segmentSum
    
                # Delete segment if sum matches, then return.
    
                if segmentSum is equal to desiredSum:
                    # Here we delete from ptrA through ptrB inclusive.
                    return
    
                # Otherwise, keep adding elements to segment.
    
                ptrB = ptrB.next
    
            # No matching segment, move on to next element.
    
            ptrA = ptrA.next
    
        # No matching segment at any element, just return.
    

    使用这些算法中的任一种将解决您关于仅删除两个元素的问题。

    在列表开头删除的问题是简单地认识到这一事实(ptrA == list.first)并确保在这种情况下调整first 指针。这是链表处理中的标准边缘情况,可以实现为:

    def deleteRangeInclusive(list, ptrA, ptrB):
        # Adjust list to remove ptrA/ptrB segment,
        #   allowing for possibility ptrA may be the head.
    
        if ptrA == list.first:
            list.first = ptrB.next
        else:
            beforeA = list.first
            while beforeA.next != ptrA:
                beforeA = beforeA.next
            beforeA.next = ptrB.next
    
        # Now we can safely remove the ptrA-ptrB segment.
    
        while ptrA != ptrB:
            tempA = ptrA
            ptrA = ptrA.next
            delete element tempA
        delete element ptrB
    

    【讨论】:

    • 列表可能包含负数,这两个指针的方法是否有效?这是一个负数的情况,list = [5, 1, -7, 3] and k = 2
    【解决方案4】:

    我是这样解决的:

    Lista *Distruggi(Lista *p, int k) {
        Lista *n = NULL, *nuova = NULL;
        int z = 0;
        for (Lista *i = p; i != NULL && z != 1; i = i->next) {
            for (Lista *j = i; j != NULL; j = j->next) {
                int sum = somma(i, j);
                if (sum != k) continue;
    
                n = (Lista *)malloc(sizeof(Lista));
                n->inf = p->inf;
                p = p->next;
                nuova = n;
    
                while (p != i) {
                    nuova->next = (Lista *)malloc(sizeof(Lista));
                    nuova = nuova->next;
                    nuova->inf = p->inf;
                    p = p->next;
                }
    
                while (j != NULL) {
                    nuova->next = (Lista *)malloc(sizeof(Lista));
                    nuova = nuova->next;
                    nuova->inf = j->inf;
                    j = j->next;
                }
                z = 1;
                break;
           }
        }
        if (z == 0) return p;//NO CHANGE
        else {//CHANGE
            nuova->next = NULL;
            return n;
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2019-12-28
      • 2021-12-28
      • 1970-01-01
      • 1970-01-01
      • 2020-10-29
      • 1970-01-01
      • 2011-08-09
      相关资源
      最近更新 更多