📌  相关文章
📜  在没有额外空间的排序单链接中查找给定总和的对

📅  最后修改于: 2022-05-13 01:57:45.204000             🧑  作者: Mango

在没有额外空间的排序单链接中查找给定总和的对

给定一个排序的单向链表和一个值 x,任务是找到总和等于 x 的对。我们不允许使用任何额外的空间,预期的时间复杂度为 O(n)。

例子:

Input : head = 3-->6-->7-->8-->9-->10-->11 , x=17
Output: (6, 11), (7, 10), (8, 9)

提示:我们可以修改原始链表

这个问题的一个简单解决方案是一个一个地取每个元素并向前遍历剩余的列表,以找到其总和等于给定值 x 的第二个元素。这种方法的时间复杂度为 O(n 2 )。

此问题的有效解决方案基于以下文章中讨论的想法。
在双向链表中查找对:我们使用相同的算法从两端遍历链表。
XOR 链表:在单向链表中,我们只能向前遍历列表。我们使用 XOR 概念将单向链表转换为双向链表。



以下是步骤:

  • 首先我们需要将我们的单向链表转换成双向链表。在这里,我们给出具有仅次于指针不上一个指针单链表结构的节点,所以我们的单链表转换成双向链表我们使用的内存使用效率双向链表(XOR链表)。
  • 在异或链表中,单向链表的每个 next指针都包含 nextprev指针的异或。
  • 将单向链表转换为双向链表后,我们初始化两个指针变量,以在已排序的双向链表中查找候选元素。首先用双向链表开始初始化,即; first = head并用双向链表的最后一个节点初始化 second ,即;第二个 = last_node
  • 这里我们没有随机访问,所以为了初始化指针,我们遍历列表直到最后一个节点并将最后一个节点分配给第二个。
  • 如果 firstsecond 的当前总和小于 x,那么我们首先向前移动。如果第一个第二个元素的当前总和大于 x,那么我们向后移动第二个。
  • 循环终止条件也不同于数组。当两个指针中的任何一个变为 NULL,或者它们彼此交叉(first=next_node),或者它们变得相同(first == second)时,循环终止。
C++
// C++ program to find pair with given sum in a singly
// linked list in O(n) time and no extra space.
#include
using namespace std;
 
/* Link list node */
struct Node
{
    int data;
 
    /* also contains XOR of next and
    previous node after conversion*/
    struct Node* next;
};
 
/* Given a reference (pointer to pointer) to the head
    of a list and an int, push a new node on the front
    of the list. */
void insert(struct Node** head_ref, int new_data)
{
    /* allocate node */
    struct Node* new_node =
        (struct Node*) malloc(sizeof(struct Node));
 
    /* put in the data */
    new_node->data = new_data;
 
    /* link the old list off the new node */
    new_node->next = (*head_ref);
 
    /* move the head to point to the new node */
    (*head_ref) = new_node;
}
 
/* returns XORed value of the node addresses */
struct Node* XOR (struct Node *a, struct Node *b)
{
    return (struct Node*) ((uintptr_t) (a) ^ (uintptr_t) (b));
}
 
// Utility function to convert singly linked list
// into XOR doubly linked list
void convert(struct Node *head)
{
    // first we store address of next node in it
    // then take XOR of next node and previous node
    // and store it in next pointer
    struct Node *next_node;
 
    // prev node stores the address of previously
    // visited node
    struct Node *prev = NULL;
 
    // traverse list and store xor of address of
    // next_node and prev node in next pointer of node
    while (head != NULL)
    {
        // address of next node
        next_node = head->next;
 
        // xor of next_node and prev node
        head->next = XOR(next_node, prev);
 
        // update previous node
        prev = head;
 
        // move head forward
        head = next_node;
    }
}
 
// function to Find pair whose sum is equal to
// given value x
void pairSum(struct Node *head, int x)
{
    // initialize first
    struct Node *first = head;
 
    // next_node and prev node to calculate xor again
    // and find next and prev node while moving forward
    // and backward direction from both the corners
    struct Node *next_node = NULL, *prev = NULL;
 
    // traverse list to initialize second pointer
    // here we need to move in forward direction so to
    // calculate next address we have to take xor
    // with prev pointer because (a^b)^b = a
    struct Node *second = head;
    while (second->next != prev)
    {
        struct Node *temp = second;
        second = XOR(second->next, prev);
        prev = temp;
    }
 
    // now traverse from both the corners
    next_node = NULL;
    prev = NULL;
 
    // here if we want to move forward then we must
    // know the prev address to calculate next node
    // and if we want to move backward then we must
    // know the next_node address to calculate prev node
    bool flag = false;
    while (first != NULL && second != NULL &&
            first != second && first != next_node)
    {
        if ((first->data + second->data)==x)
        {
            cout << "(" << first->data << ","
                 << second->data << ")" << endl;
 
            flag = true;
 
            // move first in forward
            struct Node *temp = first;
            first = XOR(first->next,prev);
            prev = temp;
 
            // move second in backward
            temp = second;
            second = XOR(second->next, next_node);
            next_node = temp;
        }
        else
        {
            if ((first->data + second->data) < x)
            {
                // move first in forward
                struct Node *temp = first;
                first = XOR(first->next,prev);
                prev = temp;
            }
            else
            {
                // move second in backward
                struct Node *temp = second;
                second = XOR(second->next, next_node);
                next_node = temp;
            }
        }
    }
    if (flag == false)
        cout << "No pair found" << endl;
}
 
// Driver program to run the case
int main()
{
    /* Start with the empty list */
    struct Node* head = NULL;
    int x = 17;
 
    /* Use insert() to construct below list
    3-->6-->7-->8-->9-->10-->11 */
    insert(&head, 11);
    insert(&head, 10);
    insert(&head, 9);
    insert(&head, 8);
    insert(&head, 7);
    insert(&head, 6);
    insert(&head, 3);
 
    // convert singly linked list into XOR doubly
    // linked list
    convert(head);
    pairSum(head,x);
    return 0;
}


输出:

(6,11) , (7,10) , (8,9)

时间复杂度:O(n)

如果您希望与专家一起参加现场课程,请参阅DSA 现场工作专业课程学生竞争性编程现场课程。