Skip to content
This repository was archived by the owner on May 22, 2026. It is now read-only.

Commit b64a9d5

Browse files
Merge pull request #16 from RyosukeDTomita/feature/bst
feature/bst
2 parents 7156980 + d87a8dd commit b64a9d5

6 files changed

Lines changed: 210 additions & 15 deletions

File tree

data_structure/tree/tree.hs

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
data Tree a = Node a [Tree a] deriving (Show)
1+
data Tree a = Node a [Tree a] deriving (Show) -- 型コンストラクタとしてTreeをデータコンストラクタとしてNodeを作成
22

33
-- 葉ノード(子を持たないノード)を作成
44
leaf :: a -> Tree a
@@ -11,14 +11,14 @@ rootValue (Node x _) = x
1111
-- 特定の値を持つノードの子として新しい値を挿入
1212
-- 見つからない場合は元の木を返す
1313
insertAt :: (Eq a) => a -> a -> Tree a -> Tree a
14-
insertAt target newVal (Node x cs)
15-
| x == target = Node x (leaf newVal : cs)
16-
| otherwise = Node x (map (insertAt target newVal) cs)
14+
insertAt target newVal (Node x children)
15+
| x == target = Node x (leaf newVal : children)
16+
| otherwise = Node x (map (insertAt target newVal) children) -- mapするのはすべての子要素について調査するため
1717

1818
-- 特定の値を持つノードを削除(子ノードは親に昇格)
1919
-- ルートノードは削除できない
2020
delete :: (Eq a) => a -> Tree a -> Tree a
21-
delete target (Node x cs) = Node x (concatMap (deleteHelper target) cs)
21+
delete target (Node x children) = Node x (concatMap (deleteHelper target) children)
2222
where
2323
deleteHelper :: (Eq a) => a -> Tree a -> [Tree a]
2424
deleteHelper t (Node v children')
@@ -27,33 +27,33 @@ delete target (Node x cs) = Node x (concatMap (deleteHelper target) cs)
2727

2828
-- 木に特定の値が含まれているか検索
2929
contains :: (Eq a) => a -> Tree a -> Bool
30-
contains target (Node x cs) = x == target || any (contains target) cs
30+
contains target (Node x children) = x == target || any (contains target) children
3131

3232
-- 木のサイズ(ノード数)を取得
3333
size :: Tree a -> Int
34-
size (Node _ cs) = 1 + sum (map size cs)
34+
size (Node _ children) = 1 + sum (map size children)
3535

3636
-- 木の深さを取得
3737
depth :: Tree a -> Int
3838
depth (Node _ []) = 1
39-
depth (Node _ cs) = 1 + maximum (map depth cs)
39+
depth (Node _ children) = 1 + maximum (map depth children)
4040

4141
-- 木を整形して表示(枝を使って階層構造を明確化)
4242
prettyPrint :: (Show a) => Tree a -> String
4343
prettyPrint tree = go "" "" tree
4444
where
45-
go prefix childPrefix (Node x cs) =
45+
go prefix childPrefix (Node x children) =
4646
prefix
4747
++ show x
4848
++ "\n"
49-
++ drawChildren childPrefix cs
49+
++ drawChildren childPrefix children
5050

5151
drawChildren _ [] = ""
5252
drawChildren prefix [c] =
5353
go (prefix ++ "└── ") (prefix ++ " ") c
54-
drawChildren prefix (c : cs') =
54+
drawChildren prefix (c : children') =
5555
go (prefix ++ "├── ") (prefix ++ "") c
56-
++ drawChildren prefix cs'
56+
++ drawChildren prefix children'
5757

5858
main :: IO ()
5959
main = do

search/README.md

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,25 @@
55
- 先頭から対象となる値を探す。
66
- 計算量は O(n)。
77

8+
### 実装例
9+
10+
- [x] C
11+
- [x] Python
12+
- [x] Haskell
13+
814
### 線形探索 with 番兵法(Sentinel loop)あり
915

1016
- 前提として線形探索においてループの終了条件は,以下の 2 つがある。
1117
- 探索する値が見つかったか
1218
- 探索する値が見つからなかった場合に配列の末尾に到達したか
1319
- 探索する配列に番兵として探索する値を末尾に追加することで,必ず探索する値が見つかるようになるため,ループの終了条件を探索する値が見つかったかの 1 つにすることができる。
1420

21+
#### 実装例
22+
23+
- [x] C
24+
- [x] Python
25+
- [x] Haskell(特にメリットはない)
26+
1527
---
1628

1729
## 二分探索(Binary search)
@@ -22,10 +34,38 @@
2234
- 配列を半分にわけて,ピボットを基準にターゲットとなる値が左右のどちらにあるか探索する。
2335
- 計算効率は、1 ステップごとに探索対象が半分になっていく。これは 2 進数においては 1 桁減ることを表しているので計算量は O(log2 n)と表せる。
2436

25-
### 関数を満たす最大/最小の値の探索
37+
#### 実装例
38+
39+
- [x] C
40+
- [x] Python
41+
- [x] Haskell
42+
43+
### 関数を満たす最大/最小の値を二分探索で探す
2644

2745
- 二分探索を使い、関数f(x)がピボットに対してTrueかFalseかを判定することで、関数f(x)を満たす最大/最小のxの範囲を探索できる。
2846

47+
#### 実装例
48+
49+
- [ ] C
50+
- [ ] Python
51+
- [x] Haskell
52+
53+
---
54+
55+
## BST(Binary Search Tree)
56+
57+
- 二分木探索とも。
58+
- 各ノードに値が格納されており、左の子ノードは親ノードより小さい値、右の子ノードは親ノードより大きい値を持つという構造を維持する二分木。
59+
- ノードの削除時には、そのノードの右部分木の最小値を持つノードを削除するノードの位置に移動させることで、BSTの構造を維持する。
60+
- 右部分技の最小値は、左部分木のどの値よりも大きいが、右部分木のどの値よりも小さいため、BSTの構造が維持される。
61+
- (右部分木の最小値は、子ノードがないので移動させやすい。)
62+
63+
### 実装例
64+
65+
- [ ] C
66+
- [ ] Python
67+
- [x] Haskell
68+
2969
---
3070

3171
## bit 全探索(bit search)
@@ -43,5 +83,3 @@
4383
## h幅優先探索(Breadth First Search)
4484

4585
---
46-
47-
## 2 分木探索(Binary search tree)
File renamed without changes.

search/bst/bst.hs

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
{-# LANGUAGE CPP #-}
2+
{-# LANGUAGE OverloadedStrings #-}
3+
4+
-- {-# OPTIONS_GHC -DATCODER #-}
5+
import Debug.Trace (traceShowId)
6+
7+
#ifdef ATCODER
8+
debug :: Bool ; debug = False
9+
#else
10+
debug :: Bool ; debug = True
11+
#endif
12+
13+
dbgId :: (Show a) => a -> a
14+
dbgId x
15+
| debug = traceShowId x
16+
| otherwise = x
17+
18+
data BST a
19+
= Empty
20+
| Node a (BST a) (BST a)
21+
deriving (Show)
22+
23+
search :: (Ord a) => a -> BST a -> Bool
24+
search _ Empty = False
25+
search target (Node v left right)
26+
| target == v = True
27+
| target < v = search target left -- 自分より小さい値は左側の枝に
28+
| otherwise = search target right
29+
30+
insert :: (Ord a) => a -> BST a -> BST a
31+
insert x Empty = Node x Empty Empty
32+
insert x (Node v left right)
33+
| x == v = Node v left right
34+
| x < v = Node v (insert x left) right
35+
| otherwise = Node v left (insert x right)
36+
37+
-- 削除対象の位置にその位置からみた右部分木の最小値に置き換える。
38+
-- これにより、左部分技よりは小さく、右部分技のどの値よりも小さな値がトップにくるため二分木が破綻しない。
39+
delete :: (Ord a) => a -> BST a -> BST a
40+
delete _ Empty = Empty
41+
delete x (Node v left right)
42+
| x < v = Node v (delete x left) right
43+
| x > v = Node v left (delete x right)
44+
| otherwise = removeNode (Node v left right)
45+
46+
-- ノード削除ロジック
47+
removeNode :: (Ord a) => BST a -> BST a
48+
removeNode (Node _ Empty right) = right
49+
removeNode (Node _ left Empty) = left
50+
removeNode (Node _ left right) =
51+
-- 右部分木の最小値を取り出し、そのノードを除いた木も返す
52+
let (m, right') = detachMin right
53+
in Node m left right'
54+
removeNode Empty = Empty
55+
56+
detachMin :: BST a -> (a, BST a)
57+
detachMin Empty = error "detachMin: empty tree"
58+
detachMin (Node x Empty right) = (x, right)
59+
detachMin (Node x left right) =
60+
let (m, left') = detachMin left
61+
in (m, Node x left' right)
62+
63+
-- 木を整形して表示(枝を使って階層構造を明確化)
64+
prettyPrint :: (Show a) => BST a -> String
65+
prettyPrint tree = go "" "" tree
66+
where
67+
go _ _ Empty = ""
68+
go prefix childPrefix (Node x left right) =
69+
prefix
70+
++ show x
71+
++ "\n"
72+
++ drawChildren childPrefix left right
73+
74+
drawChildren prefix Empty Empty = ""
75+
drawChildren prefix left Empty =
76+
go (prefix ++ "└── ") (prefix ++ " ") left
77+
drawChildren prefix Empty right =
78+
go (prefix ++ "└── ") (prefix ++ " ") right
79+
drawChildren prefix left right =
80+
go (prefix ++ "├── ") (prefix ++ "") left
81+
++ go (prefix ++ "└── ") (prefix ++ " ") right
82+
83+
main :: IO ()
84+
main = do
85+
let bTree =
86+
Node
87+
5
88+
( Node
89+
3
90+
( Node
91+
1
92+
Empty
93+
Empty
94+
)
95+
( Node
96+
4
97+
Empty
98+
Empty
99+
)
100+
)
101+
( Node
102+
7
103+
( Node
104+
6
105+
Empty
106+
Empty
107+
)
108+
( Node
109+
8
110+
Empty
111+
Empty
112+
)
113+
)
114+
putStrLn "=====INITIAl====="
115+
putStrLn $ prettyPrint bTree
116+
print $ search 3 bTree -- 存在する
117+
print $ search 10 bTree -- 存在しない
118+
119+
-- 追加
120+
putStrLn "=====ADD 10====="
121+
let bTree' = insert 10 bTree
122+
putStrLn $ prettyPrint bTree'
123+
print $ search 10 bTree'
124+
125+
-- 存在する要素を追加
126+
putStrLn "=====ADD 3(already exists)====="
127+
let bTree'' = insert 3 bTree
128+
putStrLn $ prettyPrint bTree''
129+
print $ search 3 bTree''
130+
131+
-- 削除
132+
putStrLn "=====DELETE 5====="
133+
let bTree''' = delete 5 bTree
134+
putStrLn $ prettyPrint bTree'''
135+
print $ search 5 bTree'''
136+
137+
-- 存在しない要素を削除する
138+
putStrLn "=====DELETE 8(not exist)====="
139+
let bTree'''' = delete 8 bTree
140+
putStrLn $ prettyPrint bTree''''
141+
print $ search 8 bTree''''
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
-- 高級言語の場合はやっても分岐の数は減らないので意味がない。
2+
linearSearch :: [Int] -> Int -> Int
3+
linearSearch xs target =
4+
let xs' = xs ++ [target] -- 番兵を追加
5+
in head
6+
[ i
7+
| i <- [0 .. length xs' - 1],
8+
xs' !! i == target
9+
]
10+
11+
main :: IO ()
12+
main = do
13+
let randomList = [30, 75, 69, 16, 47, 77, 60, 80, 74, 8, 77, 1, 60, 33, 70, 29, 24, 91, 60, 69]
14+
print randomList
15+
let target = 77
16+
print $ linearSearch randomList 77

0 commit comments

Comments
 (0)