9.4 二分探索木(写経編)

これはアカンかったです。次節点の探索のところ、ややこしすぎませんか?

あっそういえばポインタの参照を久しぶりに使いました。(rootの入れ替えのところ)

#include <iostream>
#include <cstdio>
#include <memory>

using namespace std;

struct Node {
  int key;
  Node* parent;
  Node* left;
  Node* right;
  
  Node(int key = -1) : key(key), parent(nullptr), left(nullptr), right(nullptr) {};
};

struct Tree {
  Node* root;  
  
  Tree() : root(nullptr) {};
};

static void insert(Tree& tree, int v) {
  Node* z = new Node(v);
  Node* x(tree.root);
  Node* y(nullptr);
  while (x != nullptr) {
    y = x;
    if (z->key < x->key) {
      x = x->left;
    } else {
      x = x->right;
    }
  }
  
  z->parent = y;
  if (y == nullptr) {
    tree.root = z;
  } else if (z->key < y->key) {
    y->left = z;
  } else {
    y->right = z;
  }
}

static Node* find(Node* node, int v) {
  while (node != nullptr && node->key != v) {
    if (node->key > v) {
      node = node->left;
    } else {
      node = node->right;
    }
  }
  return node;
}

static Node* getSuccessor(Node* x) {
  if (x->right) {
    x = x->right;
    while (x->left) {
      x = x->left;
    }
    return x;
  }
  
  Node* y = x->parent;
  while (y && x == y->right) {
    x = y;
    y = y->parent;
  }
  return y;
}

static void deleteNode(Node*& root, int v) {
  Node* z = find(root, v);
  Node* y;
  if (!z->left || !z->right) {
    y = z;
  } else {
    y = getSuccessor(z);  
  }

  Node* x;
  if (y->left) {
    x = y->left;
  } else {
    x = y->right;
  }
  
  if (x) {
    x->parent = y->parent;
  }
  
  if (!y->parent) {
    root = x;    
  } else if (y == y->parent->left) {
    y->parent->left = x;
  } else {
    y->parent->right = x;
  }
  
  if (y != z) {
    z->key = y->key;  
  }
  delete y;
}

void preorderWalk(const Node* node) {
  if (!node) {
    return;
  }
  cout << " " << node->key;
  if (node->left != nullptr) {
    preorderWalk(node->left);
  }
  if (node->right != nullptr) {
    preorderWalk(node->right); 
  }
}

void inorderWalk(const Node* node) {
  if (!node) {
    return;
  }
  if (node->left != nullptr) {
    inorderWalk(node->left);
  }
  cout << " " << node->key;

  if (node->right != nullptr) {
    inorderWalk(node->right); 
  }
}

static Tree TREE;

int main() {
  int n;
  cin >> n;

  string command;
  int v;
  for (int i = 0; i < n; i++) {
    cin >> command;
    if (command[0] == 'i') {
      scanf("%d", &v);
      insert(TREE, v);
    } else if (command[0] == 'f') {
      scanf("%d", &v);
      Node* found = find(TREE.root, v);
      cout << (found ? "yes" : "no") << endl;
    } else if (command[0] == 'd') {
      scanf("%d", &v);
      deleteNode(TREE.root, v);
    } else {
      inorderWalk(TREE.root);
      cout << endl;
      preorderWalk(TREE.root);
      cout << endl;
    }
  }

  return 0;
}

9.3 二分探索木:探索

お仕事でいろいろとやられているので間が空いてしまいました。

今回のお題は前回の内容を引き継いでいるので、あとは探索機能を入れるだけでした。

※20180417追記:巡回出力のところがバグっていたので直しました

#include <iostream>
#include <cstdio>
#include <memory>

using namespace std;

struct Node {
  int key;
  Node* parent;
  Node* left;
  Node* right;
  
  Node(int key = -1) : key(key), parent(nullptr), left(nullptr), right(nullptr) {};
};

struct Tree {
  Node* root;  
  
  Tree() : root(nullptr) {};
};

static void insert(Tree& tree, int v) {
  Node* z = new Node(v);
  Node* x(tree.root);
  Node* y(nullptr);
  while (x != nullptr) {
    y = x;
    if (z->key < x->key) {
      x = x->left;
    } else {
      x = x->right;
    }
  }
  
  z->parent = y;
  if (y == nullptr) {
    tree.root = z;
  } else if (z->key < y->key) {
    y->left = z;
  } else {
    y->right = z;
  }
}

static bool find(const Node* node, int v) {
  if (node == nullptr) {
    return false;
  }
  if (node->key > v) {
    return find(node->left, v);
  } else if (node->key < v) {
    return find(node->right, v);
  }
  return true;
}

void preorderWalk(const Node* node) {
  if (!node) {
    return;
  }
  cout << " " << node->key;
  if (node->left != nullptr) {
    preorderWalk(node->left);
  }
  if (node->right != nullptr) {
    preorderWalk(node->right); 
  }
}

void inorderWalk(const Node* node) {
  if (!node) {
    return;
  }
  if (node->left != nullptr) {
    inorderWalk(node->left);
  }
  cout << " " << node->key;

  if (node->right != nullptr) {
    inorderWalk(node->right); 
  }
}

static Tree TREE;

int main() {
  int n;
  cin >> n;

  string command;
  int v;
  for (int i = 0; i < n; i++) {
    cin >> command;
    if (command[0] == 'i') {
      scanf("%d", &v);
      insert(TREE, v);
    } else if (command[0] == 'f') {
      scanf("%d", &v);
      bool found = find(TREE.root, v);
      cout << (found ? "yes" : "no") << endl;
    } else {
      inorderWalk(TREE.root);
      cout << endl;
      preorderWalk(TREE.root);
      cout << endl;
    }
  }

  return 0;
}

9.2 二分探索木:挿入

問題に書いてあった解説どおりの実装です。delete を呼んでないのが気持ち悪いですが、まあ良いでしょう。良くないか。

#include <iostream>
#include <cstdio>
#include <memory>

using namespace std;

struct Node {
  int key;
  Node* parent;
  Node* left;
  Node* right;
  
  Node(int key = -1) : key(key), parent(nullptr), left(nullptr), right(nullptr) {};
};

struct Tree {
  Node* root;  
  
  Tree() : root(nullptr) {};
};

static void insert(Tree& tree, int v) {
  Node* z = new Node(v);
  Node* x(tree.root);
  Node* y(nullptr);
  while (x != nullptr) {
    y = x;
    if (z->key < x->key) {
      x = x->left;
    } else {
      x = x->right;
    }
  }
  
  z->parent = y;
  if (y == nullptr) {
    tree.root = z;
  } else if (z->key < y->key) {
    y->left = z;
  } else {
    y->right = z;
  }
}

void preorderWalk(const Node* node) {
  if (node->key == -1) {
    return;
  }
  cout << " " << node->key;
  if (node->left != nullptr) {
    preorderWalk(node->left);
  }
  if (node->right != nullptr) {
    preorderWalk(node->right); 
  }
}

void inorderWalk(const Node* node) {
  if (node->key == -1) {
    return;
  }
  if (node->left != nullptr) {
    inorderWalk(node->left);
  }
  cout << " " << node->key;

  if (node->right != nullptr) {
    inorderWalk(node->right); 
  }
}

static Tree TREE;

int main() {
  int n;
  cin >> n;

  string command;
  int v;
  for (int i = 0; i < n; i++) {
    cin >> command;
    if (command[0] == 'i') {
      scanf("%d", &v);
      insert(TREE, v);
    } else {
      inorderWalk(TREE.root);
      cout << endl;
      preorderWalk(TREE.root);
      cout << endl;
    }
  }

  return 0;
}

8.5 木巡回の応用:木の復元

ちょっと間が空いてしまいました。自力で考えたとっkは、すべての木を復元しようとしたのですが、NGでした。ということでこちらは写経となっております。シンプルな再帰アルゴリズムを思いつくにはどうしたら良いんでしょうねえ。。

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

static vector<int> preorder(100), inorder(100);

static void inputOrder(vector<int>& order, int num) {
  for (int i = 0; i < num; i++) {
    cin >> order[i];
  }
}

static int current = 0, outIndex = 0;

static int next() {
  int c = preorder[current];
  current++;
  return c;
}

static int find(int c) {
  auto it = find(inorder.begin(), inorder.end(), c); 
  return distance(inorder.begin(), it);
}

static void reconstruction(int l, int r) {
  if (l >= r) {
    return;
  } 
  int c = next();
  int m = find(c);
  reconstruction(l, m);
  reconstruction(m + 1, r);

  if (outIndex != 0) {
    cout << " " << c;
  } else {
    cout << c;
  }
  outIndex++;
}

int main() {
  int n;
  cin >> n;

  inputOrder(preorder, n);
  inputOrder(inorder, n);
  reconstruction(0, n);

  cout << endl;

  return 0;
}

8.4 木の巡回(手動編)

詳細は端折りますが、いろいろと難しく考えすぎて失敗しました。こんなシンプルな方法で巡回になるんですね。。

#include <iostream>
#include <vector>

using namespace std;

struct Node {
  int id;
  int parent;
  int left;
  int right;

  Node() : id(), parent(-1), left(-1), right(-1) {}
  };

static vector<Node> TREE(25);

static void inputNode(Node& node, int id) {
  node.id = id;
  cin >> node.left >> node.right;
}

static const Node& getRoot(const vector<Node>& tree, int num) {
  for (int i = 0; i < num; i++) {
    if (tree[i].parent == -1) {
      return tree[i];
    } 
  }
}

static void getNextPreorder(const vector<Node>& tree, const Node& node) {
  if (node.id == -1) {
    return;
  }

  cout << " " << node.id;
  
  if (node.left != -1) {
    getNextPreorder(tree, tree[node.left]);
  }

  if (node.right != -1) {
    getNextPreorder(tree, tree[node.right]);
  }
}

static void preorderTreeWalk(const vector<Node>& tree, const Node& root) {
  cout << "Preorder" << endl;
  getNextPreorder(tree, root);
  cout << endl;
}

static void getNextInorder(const vector<Node>& tree, const Node& node) {
  if (node.id == -1) {
    return;
  }
  
  if (node.left != -1) {
    getNextInorder(tree, tree[node.left]);
  }

  cout << " " << node.id;

  if (node.right != -1) {
    getNextInorder(tree, tree[node.right]);
  }
}

static void inorderTreeWalk(const vector<Node>& tree, const Node& root) {
  cout << "Inorder" << endl;
  getNextInorder(tree, root);
  cout << endl;
}

static void getNextPostorder(const vector<Node>& tree, const Node& node) {
  if (node.id == -1) {
    return;
  }
  
  if (node.left != -1) {
    getNextPostorder(tree, tree[node.left]);
  }

  if (node.right != -1) {
    getNextPostorder(tree, tree[node.right]);
  }

  cout << " " << node.id;
}

static void postorderTreeWalk(const vector<Node>& tree, const Node& root) {
  cout << "Postorder" << endl;
  getNextPostorder(tree, root);
  cout << endl;
}

static void setupTree(vector<Node>& tree, int num) {
  for (int i = 0; i < num; i++) {
    Node& node = tree[i];
    if (node.left != -1) {
      tree[node.left].parent = node.id;
    }
    if (node.right != -1) {
      tree[node.right].parent = node.id;
    }
  }
}

int main() {
  int n;
  cin >> n;

  int id;
  for (int i = 0; i < n; i++) {
    cin >> id;
    inputNode(TREE[id], id);
  }

  setupTree(TREE, n);
  const Node root = getRoot(TREE, n);

  preorderTreeWalk(TREE, root);
  inorderTreeWalk(TREE, root);
  postorderTreeWalk(TREE, root);

  return 0;
}

8.3 二分木の表現(手動編)

高さの算出が前回と違うところですね。構造体にメソッドがつけられることを忘れていたので若干処理を移動しました。あまりキレイではないですがこれくらいで。。

#include <iostream>
#include <vector>
#include <string>
#include <cstdio>
#include <algorithm>

using namespace std;

static const string ROOT = "root";
static const string INTERNAL_NODE = "internal node";
static const string LEAF = "leaf";

struct Node {
  int id;
  int parent;
  int depth;
  int height;
  int left;
  int right;

  Node() : id(), parent(-1), depth(0), height(0), left(-1), right(-1) {}

  bool hasChild() const {
    return left != -1 || right != -1; 
  }

  int getDegree() const {
    if (hasChild()) {
      if (left != -1 && right != -1) {
       return 2;
      } else {
        return 1;
      }
    } else {
      return 0;
    }
  }

  const string& getType() const {
    if (parent == -1) {
      return ROOT;
    } else if (hasChild()) {
    return INTERNAL_NODE;
    } else {
      return LEAF;
    }
  }
};

static vector<Node> TREE(25);

static void inputNode(Node& node, int id) {
  node.id = id;
  cin >> node.left >> node.right;
}

static void setDepth(vector<Node>& tree, Node& node) {
  if (node.left != -1) {
    tree[node.left].depth = node.depth + 1;
    setDepth(tree, tree[node.left]);
  }
  
  if (node.right != -1) {
    tree[node.right].depth = node.depth + 1;
    setDepth(tree, tree[node.right]);
  }
}

static int getHeight(const vector<Node>& tree, const Node& node) {
  int height = 0, leftHeight = 0, rightHeight = 0;
  if (node.hasChild()) {
    if (node.left != -1) {
      leftHeight = getHeight(tree, tree[node.left]) + 1;
    }
    if (node.right != -1) {
      rightHeight = getHeight(tree, tree[node.right]) + 1;
    }
    height = max(leftHeight, rightHeight);
  }
  return height;
}

static int getSibling(const vector<Node>& tree, const Node& node) {
  if (node.parent == -1) {
    return -1;
  } else {
    const Node& parentNode = tree[node.parent];
    if (parentNode.left == node.id) {
      return parentNode.right;
    } else {
      return parentNode.left;
    }
  }
}

static void setupTree(vector<Node>& tree, int num) {
  for (int i = 0; i < num; i++) {
    Node& node = tree[i];
    if (node.left != -1) {
      tree[node.left].parent = node.id;
      tree[node.left].depth = node.depth + 1;
      setDepth(tree, tree[node.left]);
    }
    if (node.right != -1) {
      tree[node.right].parent = node.id;
      tree[node.right].depth = node.depth + 1;
      setDepth(tree, tree[node.right]);
    }
    node.height = getHeight(tree, node);
  }
}

static void printTree(const vector<Node>& tree, int num) {
  int depth = 0, sibling = 0;
  for (int i = 0; i < num; i++) {
    sibling = getSibling(tree, tree[i]);
    const string& type = tree[i].getType();
    printf("node %d: parent = %d, sibling = %d, degree = %d", i, tree[i].parent, sibling, tree[i].getDegree());
    printf(", depth = %d, height = %d, %s\n", tree[i].depth, tree[i].height, type.c_str());
  }
}

int main() {
  int n;
  cin >> n;

  int id;
  for (int i = 0; i < n; i++) {
    cin >> id;
    inputNode(TREE[id], id);
  }
  
  setupTree(TREE, n);
  printTree(TREE, n);

  return 0;
}

8.2 根付き木の表現(手動編)

解説を見ないで書いた版です。構造体 Node の子の表現を vector にしました。しかし解説では「左右の子のみでも解ける方法」となっており、vector は仰々しかったかなと。。深さの算出は再帰です。子の数が0になるまで回します。

節の入力時の番号が、0からのインクリメンタル値と思い込んでいて、なかなか答えが合わずに苦労しました(実際はランダムで入力される)。

#include <iostream>
#include <vector>
#include <string>
#include <cstdio>

using namespace std;

struct Node {
  int id;
  int parent;
  int depth;
  vector<int> child;
  
  Node() : id(), parent(-1), depth(0), child(0, 0) {}
};

static vector<Node> TREE(100000);

static void inputNode(Node& node, int id, int k) {
  node.id = id;

  int c;
  for (int i = 0; i < k; i++) {
    cin >> c;
    node.child.push_back(c);
  }
}

static const string ROOT = "root";
static const string INTERNAL_NODE = "internal node";
static const string LEAF = "leaf";

static const string& getNodeType(const Node& node) {
  if (node.parent == -1) {
    return ROOT;
  } else if (node.child.size() != 0) {
    return INTERNAL_NODE;
  } else {
    return LEAF;
  }
}

static void setDepth(vector<Node>& tree, Node& node) {
  for (int i : node.child) {
    tree[i].depth = node.depth + 1;
    setDepth(tree, tree[i]);
  }
}

static void setupTree(vector<Node>& tree, int num) {
  for (int i = 0; i < num; i++) {
    const Node& node = tree[i];
    for (int j : node.child) {
      tree[j].parent = node.id;
      tree[j].depth = node.depth + 1;
      setDepth(tree, tree[j]);
    }
  }
}

static void printTree(const vector<Node>& tree, int num) {
  int node, parent = -1, depth = 0;

  for (int i = 0; i < num; i++) {
    const string type = getNodeType(tree[i]);
    printf("node %d: parent = %d, depth = %d, %s, [", i, tree[i].parent, tree[i].depth, type.c_str());
    for (int j  = 0; j < tree[i].child.size(); j++) {
      if (j != 0) {
        printf(", "); 
      }
      printf("%d", tree[i].child[j]);
    }
    printf("]\n");
  }
}

int main() {
  int n;
  cin >> n;

  int id, k;
  for (int i = 0; i < n; i++) {
    cin >> id >> k;
    inputNode(TREE[id], id, k);
  }
  
  setupTree(TREE, n);
  printTree(TREE, n);

  return 0;
}