Skip to content

Commit acd2996

Browse files
committed
8/15/23 - back from AL, meds refilled
- reverse_sublist.cc - do_terminated_lists_overlap.cc (again, did this once in the past) - do_lists_overlap.cc - queue_from_stacks.cc - is_valid_parenthesization.cc - stack_with_max.cc
1 parent 483cb8d commit acd2996

9 files changed

+515
-72
lines changed

elements-of-programming-interviews/cpp/do_lists_overlap.cc

+108-2
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,117 @@
66
#include "test_framework/test_failure.h"
77
#include "test_framework/timed_executor.h"
88

9+
/*
10+
- We can do (floyd test) tortise-and-hare method to determine if a list contains
11+
a cycle.
12+
- Overlapping cyclic lists are not ensured to join cycle at the same node, but
13+
eventually both cross over the same node.
14+
15+
Cases:
16+
- Either is null
17+
- One has cycle and other doesn't (no overlap)
18+
- No overlap, no cycles
19+
- No overlap, both have cycles
20+
- Overlap, no cycle
21+
- Overlap and cycle
22+
- Both lists are same list (special overlap case)
23+
24+
Null test
25+
First test for cycles/length; both must agree.
26+
- Neither contains cycles: reuse previous overlap test (shorten and compare)
27+
or just return last node
28+
- Both contain cycles: can get first node of cycle for each, and then rotate
29+
around cycle until either is found
30+
*/
31+
32+
int ListLength(shared_ptr<ListNode<int>> head) {
33+
int length = 0;
34+
shared_ptr<ListNode<int>> slow = head, fast = head;
35+
while (slow) {
36+
slow = slow->next;
37+
length++;
38+
fast = (fast) ? fast->next : fast;
39+
fast = (fast) ? fast->next : fast;
40+
if (slow && slow == fast) {
41+
// printf("%p == %p\n", slow.get(), fast.get());
42+
return -1;
43+
}
44+
}
45+
return length;
46+
}
47+
48+
shared_ptr<ListNode<int>> GetFirstCycleNode(shared_ptr<ListNode<int>> head) {
49+
// NOTE: breaks if used on an acyclic list.
50+
if (!head) {
51+
return nullptr;
52+
}
53+
shared_ptr<ListNode<int>> slow = head, fast = head;
54+
do {
55+
slow = slow->next;
56+
fast = fast->next->next;
57+
} while (slow != fast);
58+
slow = head;
59+
while (slow != fast) {
60+
slow = slow->next;
61+
fast = fast->next;
62+
}
63+
return fast;
64+
}
65+
66+
shared_ptr<ListNode<int>> CyclicOverlapTest(shared_ptr<ListNode<int>> l0,
67+
shared_ptr<ListNode<int>> l1) {
68+
if (!l0 || !l1) {
69+
return nullptr;
70+
}
71+
// NOTE: breaks if used on an acyclic list.
72+
l0 = GetFirstCycleNode(l0);
73+
shared_ptr<ListNode<int>> l0Origin = l0;
74+
l1 = GetFirstCycleNode(l1);
75+
76+
while (l0 != l1) {
77+
l0 = l0->next;
78+
if (l0 == l0Origin) {
79+
return nullptr;
80+
}
81+
}
82+
return l0;
83+
}
84+
85+
shared_ptr<ListNode<int>> AcyclicOverlapTest(shared_ptr<ListNode<int>> l0,
86+
shared_ptr<ListNode<int>> l1) {
87+
// NOTE: breaks if used on a cyclic list.
88+
89+
if (!l0 || !l1) {
90+
return nullptr;
91+
}
92+
while (l0->next) {
93+
l0 = l0->next;
94+
}
95+
while (l1->next) {
96+
l1 = l1->next;
97+
}
98+
return (l0 == l1) ? l0 : nullptr;
99+
}
100+
9101
shared_ptr<ListNode<int>> OverlappingLists(shared_ptr<ListNode<int>> l0,
10102
shared_ptr<ListNode<int>> l1) {
11-
// TODO - you fill in here.
12-
return nullptr;
103+
if (!l0 || !l1) {
104+
return nullptr;
105+
}
106+
int l0Size = ListLength(l0), l1Size = ListLength(l1);
107+
if ((l0Size == -1 || l1Size == -1) && (l0Size != l1Size)) {
108+
return nullptr;
109+
}
110+
if (l0Size == -1) {
111+
return CyclicOverlapTest(l0, l1);
112+
113+
} else {
114+
return AcyclicOverlapTest(l0, l1);
115+
}
13116
}
117+
118+
/*-----------*/
119+
14120
void OverlappingListsWrapper(TimedExecutor& executor,
15121
shared_ptr<ListNode<int>> l0,
16122
shared_ptr<ListNode<int>> l1,

elements-of-programming-interviews/cpp/do_terminated_lists_overlap.cc

+80-31
Original file line numberDiff line numberDiff line change
@@ -4,44 +4,84 @@
44
#include "test_framework/generic_test.h"
55
#include "test_framework/test_failure.h"
66
#include "test_framework/timed_executor.h"
7+
using JoshuaListTools::PrintList;
78
using std::shared_ptr;
89

9-
/*
10-
If two singly linked lists share any node, they will have the same
11-
final node, so we can traverse to the end and compare. The worst case
12-
is that two lists share only their final node, which we cannot determine
13-
without walking them completely so our best case is O(N) and O(c) space.
14-
-----
15-
The book doesn't specify this, but the tests want the _first_ shared node, not
16-
any shared node. We could find this in O(N^2) time and O(C) space by testing
17-
every node in l0 against every node in l1 (i.e. advance l0 one at a time and
18-
walk l1 each time). We could do it in O(N) time and space by adding every
19-
address of l1 to a set, and then checking each address of l0 against it,
20-
returning the first or nullptr.
10+
/*
11+
12+
Approach 1 (not implemented). O(n) time, O(c) space, but modifies list.
13+
- Measure length of both
14+
- Reverse both twice
15+
- Remeasure lengths:
16+
- If lengths change, they overlap (one of them will be shorter because
17+
reversal will break joint)
18+
- Walk both from heads; when shorter's next is null, set it to other's next.
19+
Then return that next.
20+
21+
Insight: If two lists are the same length and overlap, then they both meet at
22+
their ith node (the shared portion will be the same length for both, so they can
23+
only have the same overall length if their unshared portions are equally as
24+
long).
25+
26+
Approach 2 (implemented). O(n) time, O(c) space, no side effects.
27+
We only need to tell if two lists overlap, and if so we can return any node
28+
in them. They're both sure to terminate, so why not just iterate to the end
29+
of both and compare the address of the final nodes?
30+
31+
Unfortunately the book doesn't say this, but the test cases expect you to
32+
return the first node they have in common, not any of them.
33+
34+
35+
Approach 3 (implemented). O(n) time, O(c) space, no side effects.
36+
- Measure length of both
37+
- If lengths are unequal, advance the head of the longer one until they are.
38+
- Advance heads one at a time until a joint node is found (or end is reached)
2139
*/
2240

23-
#include <set>
24-
using std::set;
41+
shared_ptr<ListNode<int>> OverlappingNoCycleListsAnyNode(
42+
shared_ptr<ListNode<int>> l0, shared_ptr<ListNode<int>> l1) {
43+
if (!l0 || !l1) {
44+
return nullptr;
45+
}
46+
PrintList(l0);
47+
PrintList(l1);
48+
49+
while (l0->next != nullptr) {
50+
l0 = l0->next;
51+
};
52+
while (l1->next != nullptr) {
53+
l1 = l1->next;
54+
};
55+
56+
return (l0.get() == l1.get()) ? l0 : nullptr;
57+
}
2558

2659
shared_ptr<ListNode<int>> OverlappingNoCycleLists(
2760
shared_ptr<ListNode<int>> l0, shared_ptr<ListNode<int>> l1) {
28-
set<shared_ptr<ListNode<int>>>* l0_nodes = new set<shared_ptr<ListNode<int>>>();
29-
while(l0 != nullptr) {
30-
l0_nodes->insert(l0);
31-
l0 = l0->next;
32-
}
33-
34-
shared_ptr<ListNode<int>> retval = nullptr;
35-
while(l1 != nullptr) {
36-
if (l0_nodes->find(l1) != l0_nodes->end()){
37-
retval = l1;
38-
break;
39-
}
40-
l1 = l1->next;
41-
}
42-
delete l0_nodes;
43-
return retval;
61+
if (!l0 || !l1) {
62+
return nullptr;
63+
}
64+
int l0Length = JoshuaListTools::MeasureListLength(l0),
65+
l1Length = JoshuaListTools::MeasureListLength(l1);
66+
while (l0Length > l1Length) {
67+
l0 = l0->next;
68+
l0Length--;
69+
}
70+
while (l1Length > l0Length) {
71+
l1 = l1->next;
72+
l1Length--;
73+
}
74+
75+
while (l0 && l1) {
76+
if (l0 == l1) {
77+
return l0;
78+
}
79+
l0 = l0->next;
80+
l1 = l1->next;
81+
}
82+
return nullptr;
4483
}
84+
4585
void OverlappingNoCycleListsWrapper(TimedExecutor& executor,
4686
shared_ptr<ListNode<int>> l0,
4787
shared_ptr<ListNode<int>> l1,
@@ -71,11 +111,20 @@ void OverlappingNoCycleListsWrapper(TimedExecutor& executor,
71111
auto result = executor.Run([&] { return OverlappingNoCycleLists(l0, l1); });
72112

73113
if (result != common) {
74-
throw TestFailure("Invalid result");
114+
printf("Invalid result; expected: %p (%d), got %p (%d)\n", common.get(),
115+
common->data, result.get(), result->data);
116+
throw TestFailure("Test failed.\n");
75117
}
76118
}
77119

78120
int main(int argc, char* argv[]) {
121+
/*
122+
shared_ptr<ListNode<int>> head = ConstructList(5);
123+
PrintList(head);
124+
auto newHead = ReverseList(head);
125+
PrintList(newHead);
126+
*/
127+
79128
std::vector<std::string> args{argv + 1, argv + argc};
80129
std::vector<std::string> param_names{"executor", "l0", "l1", "common"};
81130
return GenericTestMain(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#include <memory>
2+
3+
#include "list_node.h"
4+
#include "test_framework/generic_test.h"
5+
#include "test_framework/test_failure.h"
6+
#include "test_framework/timed_executor.h"
7+
using std::shared_ptr;
8+
9+
/*
10+
If two singly linked lists share any node, they will have the same
11+
final node, so we can traverse to the end and compare. The worst case
12+
is that two lists share only their final node, which we cannot determine
13+
without walking them completely so our best case is O(N) and O(c) space.
14+
-----
15+
The book doesn't specify this, but the tests want the _first_ shared node, not
16+
any shared node. We could find this in O(N^2) time and O(C) space by testing
17+
every node in l0 against every node in l1 (i.e. advance l0 one at a time and
18+
walk l1 each time). We could do it in O(N) time and space by adding every
19+
address of l1 to a set, and then checking each address of l0 against it,
20+
returning the first or nullptr.
21+
*/
22+
23+
#include <set>
24+
using std::set;
25+
26+
shared_ptr<ListNode<int>> OverlappingNoCycleLists(
27+
shared_ptr<ListNode<int>> l0, shared_ptr<ListNode<int>> l1) {
28+
set<shared_ptr<ListNode<int>>>* l0_nodes = new set<shared_ptr<ListNode<int>>>();
29+
while(l0 != nullptr) {
30+
l0_nodes->insert(l0);
31+
l0 = l0->next;
32+
}
33+
34+
shared_ptr<ListNode<int>> retval = nullptr;
35+
while(l1 != nullptr) {
36+
if (l0_nodes->find(l1) != l0_nodes->end()){
37+
retval = l1;
38+
break;
39+
}
40+
l1 = l1->next;
41+
}
42+
delete l0_nodes;
43+
return retval;
44+
}
45+
void OverlappingNoCycleListsWrapper(TimedExecutor& executor,
46+
shared_ptr<ListNode<int>> l0,
47+
shared_ptr<ListNode<int>> l1,
48+
shared_ptr<ListNode<int>> common) {
49+
if (common) {
50+
if (l0) {
51+
auto i = l0;
52+
while (i->next) {
53+
i = i->next;
54+
}
55+
i->next = common;
56+
} else {
57+
l0 = common;
58+
}
59+
60+
if (l1) {
61+
auto i = l1;
62+
while (i->next) {
63+
i = i->next;
64+
}
65+
i->next = common;
66+
} else {
67+
l1 = common;
68+
}
69+
}
70+
71+
auto result = executor.Run([&] { return OverlappingNoCycleLists(l0, l1); });
72+
73+
if (result != common) {
74+
throw TestFailure("Invalid result");
75+
}
76+
}
77+
78+
int main(int argc, char* argv[]) {
79+
std::vector<std::string> args{argv + 1, argv + argc};
80+
std::vector<std::string> param_names{"executor", "l0", "l1", "common"};
81+
return GenericTestMain(
82+
args, "do_terminated_lists_overlap.cc", "do_terminated_lists_overlap.tsv",
83+
&OverlappingNoCycleListsWrapper, DefaultComparator{}, param_names);
84+
}

elements-of-programming-interviews/cpp/is_valid_parenthesization.cc

+41-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,49 @@
1+
#include <stack>
12
#include <string>
23

34
#include "test_framework/generic_test.h"
5+
using std::stack;
46
using std::string;
7+
8+
/*
9+
- String can contain [], (), {}
10+
- Invalid if:
11+
- pop from empty: "}"
12+
- unclosed: "()"
13+
- interleaved: "{(})"
14+
Can we just use one stack and push all openers into it? Error if nonempty at
15+
end, pop from empty, or pop doesn't match.
16+
*/
17+
18+
bool isPushable(char c) { return c == '[' || c == '(' || c == '{'; }
19+
bool isPoppable(char c) { return c == ']' || c == ')' || c == '}'; }
20+
char getOpener(char c) {
21+
switch (c) {
22+
case ']':
23+
return '[';
24+
case ')':
25+
return '(';
26+
case '}':
27+
return '{';
28+
default:
29+
return '!';
30+
}
31+
}
32+
533
bool IsWellFormed(const string& s) {
6-
// TODO - you fill in here.
7-
return true;
34+
stack<char> parens;
35+
for (char c : s) {
36+
if (isPushable(c)) {
37+
parens.push(c);
38+
} else if (isPoppable(c)) {
39+
if (parens.empty() || parens.top() != getOpener(c)) {
40+
return false;
41+
} else {
42+
parens.pop();
43+
}
44+
}
45+
}
46+
return parens.empty();
847
}
948

1049
int main(int argc, char* argv[]) {

0 commit comments

Comments
 (0)