update and rebuild at 9.21
This commit is contained in:
@@ -1,9 +1,4 @@
|
||||
# Review 1
|
||||
<style>
|
||||
html {
|
||||
-webkit-print-color-adjust: exact;
|
||||
}
|
||||
</style>
|
||||
|
||||
* Hajin Ju, 2024062806
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Review 1
|
||||
# Review 2
|
||||
|
||||
* Hajin Ju, 2024062806
|
||||
|
||||
|
||||
85
reviews/R3.md
Normal file
85
reviews/R3.md
Normal file
@@ -0,0 +1,85 @@
|
||||
# Review 3
|
||||
|
||||
* Hajin Ju, 2024062806
|
||||
|
||||
## Problem 1
|
||||
|
||||
Write `O` if an entry is true or `X` otherwise.
|
||||
|
||||
### Solution 1
|
||||
|
||||
| | $$O(n \lg n)$$ | $$\Omega(n \lg n)$$ | $$\Theta(n\lg n)$$ |
|
||||
| :-----------: | :------------: | :-----------------: | :----------------: |
|
||||
| $$\lg n$$ | O | X | X |
|
||||
| $$n$$ | O | X | X |
|
||||
| $$n \lg n$$ | O | O | O |
|
||||
| $$n \lg^2 n$$ | X | O | X |
|
||||
| $$n^2$$ | X | O | X |
|
||||
|
||||
|
||||
## Problem 2
|
||||
|
||||
Show $3n + 1 = O(n^2)$ by the definition of $O$.
|
||||
|
||||
|
||||
### Solution 2
|
||||
|
||||
A function $f(n) = O(g(n))$ if there exist constants $c\geq 0$ and $n_0\geq 0$, s.t.
|
||||
|
||||
$$n \geq n_0 \Rightarrow \leq |f(n)| \leq c |g(n)|$$
|
||||
|
||||
---
|
||||
|
||||
let $g(n) = n^2$
|
||||
let $f(n) = 3n+1$
|
||||
|
||||
suppose $c=4,\, n_0 = 1$
|
||||
|
||||
and then, for all $n \geq 1 \to |3n+1| \leq 4n^2$
|
||||
|
||||
therefore, $3n+1 = O(n^2)$ by the above definition.
|
||||
|
||||
|
||||
## Problem 3
|
||||
|
||||
Write asymptotic notations that satisfy each relation and explain why.
|
||||
|
||||
1. Transitivity
|
||||
2. Reflexivity
|
||||
3. Symmetry
|
||||
|
||||
### Solution 3
|
||||
|
||||
1. Transitivity
|
||||
|
||||
* $O$ is transitive
|
||||
because $f(n) = O(g(n))$ and $g(n) = O(h(n))$ implies $f(n) = O(h(n))$
|
||||
there must exists $n_0\geq 0$, s.t. $n \geq n_0 \Rightarrow f(n) \leq c_0 g(n) \leq c_1 c_0 h(n)$
|
||||
|
||||
* $\Omega$ is transitive
|
||||
because $f(n) = \Omega(g(n))$ and $g(n) = \Omega(h(n))$ implies $f(n) = \Omega(h(n))$
|
||||
there must exists $n_0\geq 0$, s.t. $n \geq n_0 \Rightarrow f(n) \geq c_0g(n) \geq c_1 c_0 h(n)$
|
||||
|
||||
* $\Theta$ is transitive
|
||||
because $f(n) = \Theta(g(n))$ and $g(n) = \Theta(h(n))$ implies $f(n) = \Theta(h(n))$
|
||||
$$f(n) = O(h(n)) \land f(n) = \Omega(h(n))$$
|
||||
|
||||
2. Reflexivity
|
||||
|
||||
* $O$ is reflexive
|
||||
because $f(n) = O(f(n))$ where $c=1$
|
||||
* $\Omega$ is reflexive
|
||||
because $f(n) = \Omega(f(n))$ where $c=1$
|
||||
* $\Theta$ is reflexive
|
||||
* because $f(n) = \Theta(f(n))$
|
||||
|
||||
3. Symmetry
|
||||
|
||||
* $O$ is **not** symmetric
|
||||
because $f(n) = O(g(n))$ does not imply $g(n) = O(g(n))$
|
||||
for example, $n = O(n^2)$ cannot imply $n^2 = O(n)$
|
||||
* $\Omega$ is **not** symmetric
|
||||
because $f(n) = \Omega(g(n))$ does not imply $g(n) = \Omega(g(n))$
|
||||
for example, $n^2 = \Omega(n)$ cannot imply $n = \Omega(n^2)$
|
||||
* $\Theta$ is symmetric
|
||||
because $f(n) = \Theta(g(n))$ implies $g(n) = \Theta(g(n))$
|
||||
68
reviews/R4-1.md
Normal file
68
reviews/R4-1.md
Normal file
@@ -0,0 +1,68 @@
|
||||
# Review 4-1
|
||||
|
||||
* Hajin Ju, 2024062806
|
||||
|
||||
## Problem 1
|
||||
|
||||
Show that the solution of $T(n) = 2T(\lfloor n / 2 \rfloor) + n $ is $O(n \lg n)$ by the substitution method. (Show the inductive step only.)
|
||||
|
||||
### Solution 1
|
||||
|
||||
* inductive step
|
||||
|
||||
$$T(n) \leq c n \lg n, \quad (\text{for some}\; c > 0,\, n > n_0)$$
|
||||
|
||||
We can assume the hypothesis($T(n) = O(n\lg n)$) holds for all positive int smaller than $n$.
|
||||
then,
|
||||
|
||||
$$T(\lfloor n / 2 \rfloor) \leq c \lfloor n / 2 \rfloor \lg {\lfloor n / 2 \rfloor} \leq c (n / 2) \lg ( n / 2)\\= c(n/2)(\lg n - \lg 2)$$
|
||||
|
||||
$$\begin{align*}
|
||||
T(n) &= 2T(\lfloor n / 2 \rfloor) + n \leq cn(\lg n - \lg 2) + n\\
|
||||
&=cn\lg n - cn \lg 2 + n\\
|
||||
&=cn \lg - cn + n\\
|
||||
&\leq cn\lg n
|
||||
\end{align*}$$
|
||||
|
||||
therefore, $T(n) = O(n \lg n)$
|
||||
|
||||
## Problem 2
|
||||
|
||||
Use a recursion tree to determine a good asymptotic upper bound on the recurrence $T(n) = 3T(\lfloor n / 4 \rfloor) + \theta(n^2)$.
|
||||
|
||||
### Solution 2
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A["$$T(n):\;cn^2$$"] --> B1["$$T(n/4):\;cn^2/16$$"];
|
||||
A --> B2["$$T(n/4):\;cn^2/16$$"];
|
||||
A --> B3["$$T(n/4):\;cn^2/16$$"];
|
||||
|
||||
subgraph L0[ ]
|
||||
A
|
||||
end
|
||||
|
||||
subgraph L1[ ]
|
||||
B1
|
||||
B2
|
||||
B3
|
||||
end
|
||||
```
|
||||
|
||||
* level 0
|
||||
* $cn^2$
|
||||
* level 1
|
||||
* $\frac{3}{16}cn^2$
|
||||
* level $k$
|
||||
* $(\frac{3}{16})^k cn^2$
|
||||
* level $\log_4 n$
|
||||
* $n^{\log_4 3}$
|
||||
|
||||
|
||||
therefore, total cost is
|
||||
|
||||
$$\begin{align*}
|
||||
T(n) &= \sum^{\log_4 n - 1}_{i = 0}(\frac{3}{16})^i cn^2 + \Theta(n^{\log_4 3})\\
|
||||
&< \sum^{\infty}_{i = 0}(\frac{3}{16})^i cn^2 + \Theta(n^{\log_4 3})\\
|
||||
&=\frac{16}{13}cn^2 +\Theta(n^{\log_4 3}) = O(n^2)
|
||||
\end{align*}
|
||||
$$
|
||||
32
reviews/R4-2.md
Normal file
32
reviews/R4-2.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# Review 4-2
|
||||
|
||||
* Hajin Ju, 2024062806
|
||||
|
||||
## Problem 1
|
||||
|
||||
Guess the solution to the recurrence $T(n) = T(n/3) + T(2n/3) + cn$, where $c$ is constant, is $\Theta(n\lg n) by applealing to a recursion tree.$
|
||||
|
||||
### Solution 1
|
||||
|
||||
* level 0
|
||||
$\Sigma = cn$
|
||||
* level 1
|
||||
$\Sigma = cn/3 + 2cn/3 = cn$
|
||||
* level 2
|
||||
$\Sigma = cn/9 + 2cn/9 + 2cn/9 + 4cn/9 = cn$
|
||||
* level k
|
||||
$\Sigma = cn$
|
||||
* level h
|
||||
$\Sigma = cn$
|
||||
|
||||
The shortest depth $n\to 1$ is $h = \lg_{3/2}{n}$
|
||||
The longest depth $n\to 1$ is $h = \lg_{3}{n}$
|
||||
|
||||
so $T(n) = \text{depth} * cn$ and
|
||||
|
||||
$cn \lg_{3/2}{n} \leq T(n) \leq cn \lg_{3}{n} $
|
||||
|
||||
therefore $T(n) = \Theta(n\lg n)$
|
||||
|
||||
|
||||
|
||||
478
reviews/R5.md
Normal file
478
reviews/R5.md
Normal file
@@ -0,0 +1,478 @@
|
||||
# Review 5
|
||||
|
||||
* Hajin Ju, 2024062806
|
||||
|
||||
## Problem 1
|
||||
|
||||
Illustrate the operation of `HEAPSORT` on the array $A = \set{5, 13, 2, 25, 7, 17, 20, 8, 4}$
|
||||
|
||||
### Solution 1
|
||||
|
||||
* BUILD-MAX-HEAP
|
||||
|
||||
```python { cmd, output='html' hide }
|
||||
from bs4 import BeautifulSoup
|
||||
import math
|
||||
|
||||
# --- SVG 생성을 위한 설정값 ---
|
||||
CONFIG = {
|
||||
'canvas_padding': 10,
|
||||
'node_radius': 12,
|
||||
'font_size': 10,
|
||||
'x_spacing': 30, # 형제 노드 간 최소 수평 간격
|
||||
'y_spacing': 40, # 세대 간 수직 간격
|
||||
'stroke_width': 1.5,
|
||||
'colors': {
|
||||
'node_border': '#aaa',
|
||||
'node_bg': '#fff',
|
||||
'node_text': '#333',
|
||||
'line': '#ccc',
|
||||
'active_border': '#c0392b',
|
||||
'active_bg': '#f2d7d5',
|
||||
}
|
||||
}
|
||||
|
||||
def calculate_positions(arr):
|
||||
"""2-Pass 알고리즘을 사용하여 노드 좌표를 계산합니다."""
|
||||
positions = {}
|
||||
if not arr:
|
||||
return {}, 0, 0
|
||||
|
||||
initial_y = CONFIG['canvas_padding'] + CONFIG['node_radius']
|
||||
|
||||
# 1단계: In-order 순회로 겹치지 않는 초기 x 좌표 할당
|
||||
x_next = 0
|
||||
def assign_initial_x(i):
|
||||
nonlocal x_next
|
||||
if i >= len(arr) or arr[i] is None:
|
||||
return
|
||||
|
||||
assign_initial_x(2 * i + 1) # 왼쪽 서브트리
|
||||
|
||||
y = int(math.log2(i + 1)) * CONFIG['y_spacing'] + initial_y
|
||||
positions[i] = (x_next, y)
|
||||
x_next += CONFIG['x_spacing']
|
||||
|
||||
assign_initial_x(2 * i + 2) # 오른쪽 서브트리
|
||||
|
||||
assign_initial_x(0)
|
||||
|
||||
# 2단계: Post-order 순회로 부모 노드를 자식들의 중앙으로 재조정
|
||||
def center_parents(i):
|
||||
if i >= len(arr) or arr[i] is None:
|
||||
return
|
||||
|
||||
left_child, right_child = 2 * i + 1, 2 * i + 2
|
||||
|
||||
center_parents(left_child)
|
||||
center_parents(right_child)
|
||||
|
||||
has_left = left_child < len(arr) and left_child in positions
|
||||
has_right = right_child < len(arr) and right_child in positions
|
||||
|
||||
# 자식이 있는 경우에만 부모 위치 조정
|
||||
if has_left and has_right:
|
||||
# 자식이 둘 다 있으면 그 중앙으로 이동
|
||||
left_x = positions[left_child][0]
|
||||
right_x = positions[right_child][0]
|
||||
positions[i] = ((left_x + right_x) / 2, positions[i][1])
|
||||
elif has_left and not has_right:
|
||||
# 왼쪽 자식만 있으면 그 위로 이동
|
||||
positions[i] = (positions[left_child][0], positions[i][1])
|
||||
elif not has_left and has_right:
|
||||
# 오른쪽 자식만 있으면 그 위로 이동
|
||||
positions[i] = (positions[right_child][0], positions[i][1])
|
||||
|
||||
center_parents(0)
|
||||
|
||||
# 맨 왼쪽 노드가 잘리지 않도록 전체 트리 이동
|
||||
if not positions: return {}, 0, 0
|
||||
min_x = min(p[0] for p in positions.values())
|
||||
x_shift = 0
|
||||
if min_x < CONFIG['canvas_padding'] + CONFIG['node_radius']:
|
||||
x_shift = -min_x + CONFIG['canvas_padding'] + CONFIG['node_radius']
|
||||
|
||||
shifted_positions = {i: (x + x_shift, y) for i, (x, y) in positions.items()}
|
||||
|
||||
max_x = max(p[0] for p in shifted_positions.values())
|
||||
max_y = max(p[1] for p in shifted_positions.values())
|
||||
canvas_width = max_x + CONFIG['canvas_padding'] + CONFIG['node_radius']
|
||||
canvas_height = max_y + CONFIG['canvas_padding'] + CONFIG['node_radius']
|
||||
|
||||
return shifted_positions, canvas_width, canvas_height
|
||||
|
||||
def create_single_tree_svg(soup, step_data):
|
||||
# (이전과 동일, 변경 없음)
|
||||
arr = step_data.get('heap', [])
|
||||
highlighted = step_data.get('highlighted', set())
|
||||
positions, width, height = calculate_positions(arr)
|
||||
svg = soup.new_tag('svg', attrs={'width': width, 'height': height, 'xmlns': "http://www.w3.org/2000/svg"})
|
||||
for i in range(len(arr)):
|
||||
if i not in positions: continue
|
||||
px, py = positions[i]
|
||||
left_child, right_child = 2 * i + 1, 2 * i + 2
|
||||
if left_child < len(arr) and left_child in positions:
|
||||
cx, cy = positions[left_child]
|
||||
svg.append(soup.new_tag('line', attrs={'x1': px, 'y1': py, 'x2': cx, 'y2': cy, 'stroke': CONFIG['colors']['line'], 'stroke-width': CONFIG['stroke_width']}))
|
||||
if right_child < len(arr) and right_child in positions:
|
||||
cx, cy = positions[right_child]
|
||||
svg.append(soup.new_tag('line', attrs={'x1': px, 'y1': py, 'x2': cx, 'y2': cy, 'stroke': CONFIG['colors']['line'], 'stroke-width': CONFIG['stroke_width']}))
|
||||
for i, val in enumerate(arr):
|
||||
if i not in positions: continue
|
||||
x, y = positions[i]
|
||||
is_active = i in highlighted
|
||||
svg.append(soup.new_tag('circle', attrs={'cx': x, 'cy': y, 'r': CONFIG['node_radius'], 'stroke': CONFIG['colors']['active_border'] if is_active else CONFIG['colors']['node_border'], 'stroke-width': CONFIG['stroke_width'], 'fill': CONFIG['colors']['active_bg'] if is_active else CONFIG['colors']['node_bg']}))
|
||||
svg.append(soup.new_tag('text', attrs={'x': x, 'y': y, 'font-family': 'monospace', 'font-size': CONFIG['font_size'], 'text-anchor': 'middle', 'dominant-baseline': 'central', 'fill': CONFIG['colors']['node_text'], 'font-weight': 'bold' if is_active else 'normal'}))
|
||||
svg.find_all('text')[-1].string = str(val)
|
||||
return svg
|
||||
|
||||
def create_heap_visualization_div(steps_data):
|
||||
# (이전과 동일, 변경 없음)
|
||||
soup = BeautifulSoup("", "html.parser")
|
||||
wrapper_div = soup.new_tag("div")
|
||||
style = soup.new_tag("style")
|
||||
style.string = """.heap-svg-flow { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; justify-content: flex-start; } .heap-svg-flow .arrow { font-size: 24px; color: #555; }"""
|
||||
wrapper_div.append(style)
|
||||
flow_container = soup.new_tag("div", attrs={"class": "heap-svg-flow"})
|
||||
wrapper_div.append(flow_container)
|
||||
for i, step in enumerate(steps_data):
|
||||
svg_tree = create_single_tree_svg(soup, step)
|
||||
flow_container.append(svg_tree)
|
||||
if i < len(steps_data) - 1:
|
||||
arrow = soup.new_tag("div", attrs={"class": "arrow"})
|
||||
arrow.string = "→"
|
||||
flow_container.append(arrow)
|
||||
return wrapper_div.prettify()
|
||||
|
||||
# --- 🚀 메인 실행 부분 ---
|
||||
if __name__ == "__main__":
|
||||
MANUAL_STEPS = [
|
||||
{"heap": [5, 13, 2, 25, 7, 17, 20, 8, 4], "highlighted": set()},
|
||||
{"heap": [5, 13, 2, 25, 7, 17, 20, 8, 4], "highlighted": {8, 7, 3}},
|
||||
{"heap": [5, 13, 20, 25, 7, 17, 2, 8, 4], "highlighted": {2, 5, 6}},
|
||||
{"heap": [5, 25, 20, 13, 7, 17, 2, 8, 4], "highlighted": {1, 3, 4, 7, 8}},
|
||||
{"heap": [25, 13, 20, 8, 7, 17, 2, 5, 4], "highlighted": {0, 1, 2, 3, 4, 5, 6, 7, 8}},
|
||||
]
|
||||
|
||||
html_output = create_heap_visualization_div(MANUAL_STEPS)
|
||||
print(html_output)
|
||||
```
|
||||
|
||||
* EXTRACT-MAX
|
||||
|
||||
```python { cmd, output='html' hide }
|
||||
from bs4 import BeautifulSoup
|
||||
import math
|
||||
|
||||
# --- SVG 생성을 위한 설정값 ---
|
||||
CONFIG = {
|
||||
'canvas_padding': 10,
|
||||
'node_radius': 12,
|
||||
'font_size': 10,
|
||||
'x_spacing': 30, # 형제 노드 간 최소 수평 간격
|
||||
'y_spacing': 40, # 세대 간 수직 간격
|
||||
'stroke_width': 1.5,
|
||||
'colors': {
|
||||
'node_border': '#aaa',
|
||||
'node_bg': '#fff',
|
||||
'node_text': '#333',
|
||||
'line': '#ccc',
|
||||
'active_border': '#c0392b',
|
||||
'active_bg': '#f2d7d5',
|
||||
}
|
||||
}
|
||||
|
||||
def calculate_positions(arr):
|
||||
"""2-Pass 알고리즘을 사용하여 노드 좌표를 계산합니다."""
|
||||
positions = {}
|
||||
if not arr:
|
||||
return {}, 0, 0
|
||||
|
||||
initial_y = CONFIG['canvas_padding'] + CONFIG['node_radius']
|
||||
|
||||
# 1단계: In-order 순회로 겹치지 않는 초기 x 좌표 할당
|
||||
x_next = 0
|
||||
def assign_initial_x(i):
|
||||
nonlocal x_next
|
||||
if i >= len(arr) or arr[i] is None:
|
||||
return
|
||||
|
||||
assign_initial_x(2 * i + 1) # 왼쪽 서브트리
|
||||
|
||||
y = int(math.log2(i + 1)) * CONFIG['y_spacing'] + initial_y
|
||||
positions[i] = (x_next, y)
|
||||
x_next += CONFIG['x_spacing']
|
||||
|
||||
assign_initial_x(2 * i + 2) # 오른쪽 서브트리
|
||||
|
||||
assign_initial_x(0)
|
||||
|
||||
# 2단계: Post-order 순회로 부모 노드를 자식들의 중앙으로 재조정
|
||||
def center_parents(i):
|
||||
if i >= len(arr) or arr[i] is None:
|
||||
return
|
||||
|
||||
left_child, right_child = 2 * i + 1, 2 * i + 2
|
||||
|
||||
center_parents(left_child)
|
||||
center_parents(right_child)
|
||||
|
||||
has_left = left_child < len(arr) and left_child in positions
|
||||
has_right = right_child < len(arr) and right_child in positions
|
||||
|
||||
# 자식이 있는 경우에만 부모 위치 조정
|
||||
if has_left and has_right:
|
||||
# 자식이 둘 다 있으면 그 중앙으로 이동
|
||||
left_x = positions[left_child][0]
|
||||
right_x = positions[right_child][0]
|
||||
positions[i] = ((left_x + right_x) / 2, positions[i][1])
|
||||
elif has_left and not has_right:
|
||||
# 왼쪽 자식만 있으면 그 위로 이동
|
||||
positions[i] = (positions[left_child][0], positions[i][1])
|
||||
elif not has_left and has_right:
|
||||
# 오른쪽 자식만 있으면 그 위로 이동
|
||||
positions[i] = (positions[right_child][0], positions[i][1])
|
||||
|
||||
center_parents(0)
|
||||
|
||||
# 맨 왼쪽 노드가 잘리지 않도록 전체 트리 이동
|
||||
if not positions: return {}, 0, 0
|
||||
min_x = min(p[0] for p in positions.values())
|
||||
x_shift = 0
|
||||
if min_x < CONFIG['canvas_padding'] + CONFIG['node_radius']:
|
||||
x_shift = -min_x + CONFIG['canvas_padding'] + CONFIG['node_radius']
|
||||
|
||||
shifted_positions = {i: (x + x_shift, y) for i, (x, y) in positions.items()}
|
||||
|
||||
max_x = max(p[0] for p in shifted_positions.values())
|
||||
max_y = max(p[1] for p in shifted_positions.values())
|
||||
canvas_width = max_x + CONFIG['canvas_padding'] + CONFIG['node_radius']
|
||||
canvas_height = max_y + CONFIG['canvas_padding'] + CONFIG['node_radius']
|
||||
|
||||
return shifted_positions, canvas_width, canvas_height
|
||||
|
||||
def create_single_tree_svg(soup, step_data):
|
||||
# (이전과 동일, 변경 없음)
|
||||
arr = step_data.get('heap', [])
|
||||
highlighted = step_data.get('highlighted', set())
|
||||
positions, width, height = calculate_positions(arr)
|
||||
svg = soup.new_tag('svg', attrs={'width': width, 'height': height, 'xmlns': "http://www.w3.org/2000/svg"})
|
||||
for i in range(len(arr)):
|
||||
if i not in positions: continue
|
||||
px, py = positions[i]
|
||||
left_child, right_child = 2 * i + 1, 2 * i + 2
|
||||
if left_child < len(arr) and left_child in positions:
|
||||
cx, cy = positions[left_child]
|
||||
svg.append(soup.new_tag('line', attrs={'x1': px, 'y1': py, 'x2': cx, 'y2': cy, 'stroke': CONFIG['colors']['line'], 'stroke-width': CONFIG['stroke_width']}))
|
||||
if right_child < len(arr) and right_child in positions:
|
||||
cx, cy = positions[right_child]
|
||||
svg.append(soup.new_tag('line', attrs={'x1': px, 'y1': py, 'x2': cx, 'y2': cy, 'stroke': CONFIG['colors']['line'], 'stroke-width': CONFIG['stroke_width']}))
|
||||
for i, val in enumerate(arr):
|
||||
if i not in positions: continue
|
||||
x, y = positions[i]
|
||||
is_active = i in highlighted
|
||||
svg.append(soup.new_tag('circle', attrs={'cx': x, 'cy': y, 'r': CONFIG['node_radius'], 'stroke': CONFIG['colors']['active_border'] if is_active else CONFIG['colors']['node_border'], 'stroke-width': CONFIG['stroke_width'], 'fill': CONFIG['colors']['active_bg'] if is_active else CONFIG['colors']['node_bg']}))
|
||||
svg.append(soup.new_tag('text', attrs={'x': x, 'y': y, 'font-family': 'monospace', 'font-size': CONFIG['font_size'], 'text-anchor': 'middle', 'dominant-baseline': 'central', 'fill': CONFIG['colors']['node_text'], 'font-weight': 'bold' if is_active else 'normal'}))
|
||||
svg.find_all('text')[-1].string = str(val)
|
||||
return svg
|
||||
|
||||
def create_heap_visualization_div(steps_data):
|
||||
# (이전과 동일, 변경 없음)
|
||||
soup = BeautifulSoup("", "html.parser")
|
||||
wrapper_div = soup.new_tag("div")
|
||||
style = soup.new_tag("style")
|
||||
style.string = """.heap-svg-flow { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; justify-content: flex-start; } .heap-svg-flow .arrow { font-size: 24px; color: #555; }"""
|
||||
wrapper_div.append(style)
|
||||
flow_container = soup.new_tag("div", attrs={"class": "heap-svg-flow"})
|
||||
wrapper_div.append(flow_container)
|
||||
for i, step in enumerate(steps_data):
|
||||
svg_tree = create_single_tree_svg(soup, step)
|
||||
flow_container.append(svg_tree)
|
||||
if i < len(steps_data) - 1:
|
||||
arrow = soup.new_tag("div", attrs={"class": "arrow"})
|
||||
arrow.string = "→"
|
||||
flow_container.append(arrow)
|
||||
return wrapper_div.prettify()
|
||||
|
||||
# --- 🚀 메인 실행 부분 ---
|
||||
if __name__ == "__main__":
|
||||
MANUAL_STEPS = [
|
||||
{"heap": [25, 13, 20, 8, 7, 17, 2, 5, 4], "highlighted": set()},
|
||||
{"heap": [20, 13, 17, 8, 7, 4, 2, 5], "highlighted": set()},
|
||||
{"heap": [17, 13, 5, 8, 7, 4, 2], "highlighted": set()},
|
||||
{"heap": [13, 8, 5, 2, 7, 4], "highlighted": set()},
|
||||
{"heap": [8, 7, 5, 2, 4], "highlighted": set()},
|
||||
{"heap": [7, 4, 5, 2], "highlighted": set()},
|
||||
]
|
||||
|
||||
html_output = create_heap_visualization_div(MANUAL_STEPS)
|
||||
print(html_output)
|
||||
```
|
||||
|
||||
```
|
||||
out 25 -> out 20 -> out 17 -> out 13 -> out 8
|
||||
```
|
||||
|
||||
* MAX-HEAP-INSERT(A, 15)
|
||||
|
||||
```python { cmd, output='html' hide }
|
||||
from bs4 import BeautifulSoup
|
||||
import math
|
||||
|
||||
# --- SVG 생성을 위한 설정값 ---
|
||||
CONFIG = {
|
||||
'canvas_padding': 10,
|
||||
'node_radius': 12,
|
||||
'font_size': 10,
|
||||
'x_spacing': 30, # 형제 노드 간 최소 수평 간격
|
||||
'y_spacing': 40, # 세대 간 수직 간격
|
||||
'stroke_width': 1.5,
|
||||
'colors': {
|
||||
'node_border': '#aaa',
|
||||
'node_bg': '#fff',
|
||||
'node_text': '#333',
|
||||
'line': '#ccc',
|
||||
'active_border': '#c0392b',
|
||||
'active_bg': '#f2d7d5',
|
||||
}
|
||||
}
|
||||
|
||||
def calculate_positions(arr):
|
||||
"""2-Pass 알고리즘을 사용하여 노드 좌표를 계산합니다."""
|
||||
positions = {}
|
||||
if not arr:
|
||||
return {}, 0, 0
|
||||
|
||||
initial_y = CONFIG['canvas_padding'] + CONFIG['node_radius']
|
||||
|
||||
# 1단계: In-order 순회로 겹치지 않는 초기 x 좌표 할당
|
||||
x_next = 0
|
||||
def assign_initial_x(i):
|
||||
nonlocal x_next
|
||||
if i >= len(arr) or arr[i] is None:
|
||||
return
|
||||
|
||||
assign_initial_x(2 * i + 1) # 왼쪽 서브트리
|
||||
|
||||
y = int(math.log2(i + 1)) * CONFIG['y_spacing'] + initial_y
|
||||
positions[i] = (x_next, y)
|
||||
x_next += CONFIG['x_spacing']
|
||||
|
||||
assign_initial_x(2 * i + 2) # 오른쪽 서브트리
|
||||
|
||||
assign_initial_x(0)
|
||||
|
||||
# 2단계: Post-order 순회로 부모 노드를 자식들의 중앙으로 재조정
|
||||
def center_parents(i):
|
||||
if i >= len(arr) or arr[i] is None:
|
||||
return
|
||||
|
||||
left_child, right_child = 2 * i + 1, 2 * i + 2
|
||||
|
||||
center_parents(left_child)
|
||||
center_parents(right_child)
|
||||
|
||||
has_left = left_child < len(arr) and left_child in positions
|
||||
has_right = right_child < len(arr) and right_child in positions
|
||||
|
||||
# 자식이 있는 경우에만 부모 위치 조정
|
||||
if has_left and has_right:
|
||||
# 자식이 둘 다 있으면 그 중앙으로 이동
|
||||
left_x = positions[left_child][0]
|
||||
right_x = positions[right_child][0]
|
||||
positions[i] = ((left_x + right_x) / 2, positions[i][1])
|
||||
elif has_left and not has_right:
|
||||
# 왼쪽 자식만 있으면 그 위로 이동
|
||||
positions[i] = (positions[left_child][0], positions[i][1])
|
||||
elif not has_left and has_right:
|
||||
# 오른쪽 자식만 있으면 그 위로 이동
|
||||
positions[i] = (positions[right_child][0], positions[i][1])
|
||||
|
||||
center_parents(0)
|
||||
|
||||
# 맨 왼쪽 노드가 잘리지 않도록 전체 트리 이동
|
||||
if not positions: return {}, 0, 0
|
||||
min_x = min(p[0] for p in positions.values())
|
||||
x_shift = 0
|
||||
if min_x < CONFIG['canvas_padding'] + CONFIG['node_radius']:
|
||||
x_shift = -min_x + CONFIG['canvas_padding'] + CONFIG['node_radius']
|
||||
|
||||
shifted_positions = {i: (x + x_shift, y) for i, (x, y) in positions.items()}
|
||||
|
||||
max_x = max(p[0] for p in shifted_positions.values())
|
||||
max_y = max(p[1] for p in shifted_positions.values())
|
||||
canvas_width = max_x + CONFIG['canvas_padding'] + CONFIG['node_radius']
|
||||
canvas_height = max_y + CONFIG['canvas_padding'] + CONFIG['node_radius']
|
||||
|
||||
return shifted_positions, canvas_width, canvas_height
|
||||
|
||||
def create_single_tree_svg(soup, step_data):
|
||||
# (이전과 동일, 변경 없음)
|
||||
arr = step_data.get('heap', [])
|
||||
highlighted = step_data.get('highlighted', set())
|
||||
positions, width, height = calculate_positions(arr)
|
||||
svg = soup.new_tag('svg', attrs={'width': width, 'height': height, 'xmlns': "http://www.w3.org/2000/svg"})
|
||||
for i in range(len(arr)):
|
||||
if i not in positions: continue
|
||||
px, py = positions[i]
|
||||
left_child, right_child = 2 * i + 1, 2 * i + 2
|
||||
if left_child < len(arr) and left_child in positions:
|
||||
cx, cy = positions[left_child]
|
||||
svg.append(soup.new_tag('line', attrs={'x1': px, 'y1': py, 'x2': cx, 'y2': cy, 'stroke': CONFIG['colors']['line'], 'stroke-width': CONFIG['stroke_width']}))
|
||||
if right_child < len(arr) and right_child in positions:
|
||||
cx, cy = positions[right_child]
|
||||
svg.append(soup.new_tag('line', attrs={'x1': px, 'y1': py, 'x2': cx, 'y2': cy, 'stroke': CONFIG['colors']['line'], 'stroke-width': CONFIG['stroke_width']}))
|
||||
for i, val in enumerate(arr):
|
||||
if i not in positions: continue
|
||||
x, y = positions[i]
|
||||
is_active = i in highlighted
|
||||
svg.append(soup.new_tag('circle', attrs={'cx': x, 'cy': y, 'r': CONFIG['node_radius'], 'stroke': CONFIG['colors']['active_border'] if is_active else CONFIG['colors']['node_border'], 'stroke-width': CONFIG['stroke_width'], 'fill': CONFIG['colors']['active_bg'] if is_active else CONFIG['colors']['node_bg']}))
|
||||
svg.append(soup.new_tag('text', attrs={'x': x, 'y': y, 'font-family': 'monospace', 'font-size': CONFIG['font_size'], 'text-anchor': 'middle', 'dominant-baseline': 'central', 'fill': CONFIG['colors']['node_text'], 'font-weight': 'bold' if is_active else 'normal'}))
|
||||
svg.find_all('text')[-1].string = str(val)
|
||||
return svg
|
||||
|
||||
def create_heap_visualization_div(steps_data):
|
||||
# (이전과 동일, 변경 없음)
|
||||
soup = BeautifulSoup("", "html.parser")
|
||||
wrapper_div = soup.new_tag("div")
|
||||
style = soup.new_tag("style")
|
||||
style.string = """.heap-svg-flow { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; justify-content: flex-start; } .heap-svg-flow .arrow { font-size: 24px; color: #555; }"""
|
||||
wrapper_div.append(style)
|
||||
flow_container = soup.new_tag("div", attrs={"class": "heap-svg-flow"})
|
||||
wrapper_div.append(flow_container)
|
||||
for i, step in enumerate(steps_data):
|
||||
svg_tree = create_single_tree_svg(soup, step)
|
||||
flow_container.append(svg_tree)
|
||||
if i < len(steps_data) - 1:
|
||||
arrow = soup.new_tag("div", attrs={"class": "arrow"})
|
||||
arrow.string = "→"
|
||||
flow_container.append(arrow)
|
||||
return wrapper_div.prettify()
|
||||
|
||||
# --- 🚀 메인 실행 부분 ---
|
||||
if __name__ == "__main__":
|
||||
MANUAL_STEPS = [
|
||||
{"heap": [16, 14, 10, 8, 7, 9, 3, 2, 4, 1], "highlighted": set()},
|
||||
{"heap": [16, 14, 10, 8, 7, 9, 3, 2, 4, 1, 15], "highlighted": {10}},
|
||||
{"heap": [16, 15, 10, 8, 14, 9, 3, 2, 4, 1, 7], "highlighted": set()},
|
||||
]
|
||||
|
||||
html_output = create_heap_visualization_div(MANUAL_STEPS)
|
||||
print(html_output)
|
||||
```
|
||||
|
||||
## Problem 2
|
||||
|
||||
Fill in the blanks in the following pseudocode for Heapsort.
|
||||
|
||||
### Solution 2
|
||||
|
||||
```{ .line-numbers }
|
||||
HEAPSORT(A)
|
||||
BUILD-MAX-HEAP
|
||||
for i = A.length downto 2
|
||||
exchange A[1] with A[i]
|
||||
A.heap-size = A.heap-size - 1
|
||||
MAX-HEAPIFY(A, 1)
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user