给定两个排序的数组(当然没有重复),有没有办法找出并打印出两个数组中出现的所有元素?
我知道是否可以通过迭代一个数组并构建一个哈希表,然后迭代另一个数组并查找构建的表。但这需要一个O(n)的空间。
我试图查看是否存在需要不断增加空间的方式,并且只需要对每个数组进行迭代不超过一次。有可能吗?
现在如果上述问题是可能的,如果两个排序列表存储在两个二叉搜索树中,那么同样的方法是否适用于同谋限制?
答案 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)。这基本上是一个合并操作。在合并过程中,将检测到任何重复项。这将只涉及每个列表一次传递,并且一旦到达任一列表的末尾就会终止。
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;
答案 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]
然后通过递归你可以找到答案。为什么它适用于二叉搜索树?这是因为我们只需要为每个输入增加序列。我们可以通过在树上遍历顺序来获得这样的序列。