列出两个列表中出现的元素?

时间:2017-06-23 00:51:08

标签: arrays algorithm sorting binary-search-tree

给定两个排序的数组(当然没有重复),有没有办法找出并打印出两个数组中出现的所有元素?

我知道是否可以通过迭代一个数组并构建一个哈希表,然后迭代另一个数组并查找构建的表。但这需要一个O(n)的空间。

我试图查看是否存在需要不断增加空间的方式,并且只需要对每个数组进行迭代不超过一次。有可能吗?

现在如果上述问题是可能的,如果两个排序列表存储在两个二叉搜索树中,那么同样的方法是否适用于同谋限制?

4 个答案:

答案 0 :(得分:2)

对于数组,执行等效的合并操作,但没有输出。在合并过程中,将检测到任何重复项。这将只涉及每个列表一次传递,并且一旦到达任一列表的末尾就会终止。

可以使用堆栈迭代遍历二叉搜索树,但最坏情况的堆栈空间是O(n)。 Morris遍历(执行Web搜索)可以在不使用堆栈的情况下遍历二叉树,并通过更改和恢复树中的链接来实现O(n)时间复杂度(大多数节点将被访问多次,但是时间开销为多次n仍然是时间复杂度O(n))。典型的Morris遍历功能在整棵树上运行。这将需要更改,以便它按顺序返回每个节点,以便可以使用类似逻辑的合并来检查重复项。我为此写了一些测试代码,所以如果你遇到困难我可以提供帮助。

当按顺序遍历二叉树时,每个当前节点都有一个前驱节点,即在当前节点之前按顺序发生的节点。前任节点将具有空“右”指针。在Morris遍历期间,每个前任节点的“右”指针从null更改为指向其后继节点。最终,当到达前任节点时,遵循右指针到达其后继节点,然后后继节点的前任节点的右指针恢复为空。

由于它已经过了2天,这里是一个Morris遍历函数的示例代码,它返回一个指向每个节点的指针。部分逻辑在main()的for循环中,在再次调用遍历函数之前将返回的指针前进到右边(另一种方法是让一个辅助函数向右前进,然后调用主遍历函数) :

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

/* binary tree node */
typedef struct BTNODE_
{
    struct BTNODE_* left;
    struct BTNODE_* right;
    int data;
}BTNODE;

/* traverse binary tree without stack */
/* initial input parameter is pointer to root */
/* predecessor of cur is largest value < cur */
/* predecessor of cur = cur->left->right->right->right ... */
BTNODE * MorrisTraversal(BTNODE *cur)
{
BTNODE *pre;
    if(cur == NULL)
        return(NULL);
    while(cur != NULL){
        /* if left end of branch, return */
        if(cur->left == NULL)
            return(cur);
        /* advance pre to predecessor of cur */
        pre = cur->left;
        while(pre->right != NULL && pre->right != cur)
            pre = pre->right;
        /* if right end of branch, change pre->right = cur */
        /*  and advance cur left */
        if(pre->right == NULL){
            pre->right = cur;
            cur = cur->left;
        /* else back at cur, so restore pre->right = NULL */
        /*  and return */
        } else {
            pre->right = NULL;
            return(cur);
        }
    }
    return(NULL);
}

BTNODE* newbtNode(int data)
{
BTNODE* btNode = (BTNODE*) malloc(sizeof(BTNODE));
    btNode->left = NULL;
    btNode->right = NULL;
    btNode->data = data;
    return(btNode);
}

int main()
{
BTNODE *cur;
/* create a 15 element binary tree */
BTNODE *root = newbtNode(8);
    root->left                = newbtNode( 4);
    root->left->left          = newbtNode( 2);
    root->left->left->left    = newbtNode( 1);
    root->left->left->right   = newbtNode( 3);
    root->left->right         = newbtNode( 6);
    root->left->right->left   = newbtNode( 5);
    root->left->right->right  = newbtNode( 7);
    root->right               = newbtNode(12);
    root->right->left         = newbtNode(10);
    root->right->left->left   = newbtNode( 9);
    root->right->left->right  = newbtNode(11);
    root->right->right        = newbtNode(14);
    root->right->right->left  = newbtNode(13);
    root->right->right->right = newbtNode(15);
    for(cur = root; cur; cur = cur->right){
        cur = MorrisTraversal(cur);
        printf("%2d ", cur->data);
    }
    return 0;
}

答案 1 :(得分:0)

即使有3个循环,整体操作计数也会随着输入项的数量而线性增长。这是O(n)。虽然第一个while不会像其他两个那样以可预测的方式递增,但它不会改变O(n)。这基本上是一个合并操作。在合并过程中,将检测到任何重复项。这将只涉及每个列表一次传递,并且一旦到达任一列表的末尾就会终止。

&#13;
&#13;
function mergetwosorted(a, b) {

  var c = new Array(a.length + b.length),
    ai = 0,
    bi = 0,
    ci = 0;


  while (ai < a.length && bi < b.length) {

    if (a[ai] < b[bi]) {
      c[ci] = a[ai];
      ai++;
    } else {
      c[ci] = b[bi]
      bi++;
    }
    ci++;
  }

  while (ai < a.length) {
    c[ci] = a[ai];
    ai++
    ci++
  }
  while (bi < b.length) {
    c[ci] = b[bi];
    ci++;
    bi++;
  }
  return c;

}


console.log(mergetwosorted([1, 2, 3, 4], [2, 3, 4, 5, 6]))
&#13;
&#13;
&#13;

答案 2 :(得分:0)

从两个数组的开头开始。

arr [0],arr1 [0]

如果是arr [0]&gt; arr1 [0],二进制搜索arr1中等于或大于arr1的位置。然后你有一个类似类型的子问题arr [1..n],arr [k..m]其中k是在二进制搜索阶段找到的索引。

答案 3 :(得分:0)

我想你可以看看std :: set_difference http://en.cppreference.com/w/cpp/algorithm/set_intersection

我尝试使用通常使用二叉搜索树实现的set,它可以工作。

set<int> s1 = {1, 2, 4, 5};
set<int> s2 = {0, 2, 4, 7};
vector<int> v;
set_intersection(s1.begin(), s1.end(),
                 s2.begin(), s2.end(),
                 back_inserter(v));
for (auto i : v)
    cout << i << '\n';

输出

2
4

这是问题的递归思路 对于长度为n的任何数组A,我将使用A [2 .. n]来表示数组[A [2],...,A [n]]

设A,B为长度为n,m的两个输入数组。 如果其中任何一个是空的,答案是微不足道的。

否则,让我们比较第一个条目。

如果A [0] == B [0],那么我们知道A [0]出现在两个数组中,我们将A [0]存储到输出列表并继续搜索数组A [2 .. n]和B [2 .. m]

如果A [0]&lt; B [0],然后我们知道A {0]不会出现在两个数组中,因为B中的每个元素至少是B [0]。所以我们继续搜索数组A [2 .. n]和B。

类似地,如果A [0]> B [0],我们继续搜索数组A和B [2 .. n]

然后通过递归你可以找到答案。为什么它适用于二叉搜索树?这是因为我们只需要为每个输入增加序列。我们可以通过在树上遍历顺序来获得这样的序列。