セキュリティ系の勉強・その他開発メモとか雑談. Twitter, ブログカテゴリ一覧
本ブログはあくまでセキュリティに関する情報共有の一環として作成したものであり,公開されているシステム等に許可なく実行するなど、違法な行為を助長するものではありません.

二分木の関係性の覚え方

超適当自分流覚え方。(必ずしも正しくはない)


f:id:thinline196:20180202222456j:plain




高さ n -> 節の数 2^(n+1)-1

高さに対して倍々に増えて行くので指数関数のグラフを意識すると良い。最後のマイナス1は根の数が1つだけだからとなんとなく覚えておけばなんとかなる。
指数関数 - Wikipedia



節の数 n -> 高さ log(2) n

高さの逆。高さが増えて行くためには節の数は倍々に増えている必要があります。なので、増えていくnを抑えて高さを数えて行く必要があります。対数関数のグラフを思い浮かべましょう。
対数 - Wikipedia


節の数 n -> 枝の数 n-1

節ごとがそれぞれ親に繋がる枝を持っているので、節の数と基本的には同じになるが、根のみ親を持たないためマイナス1をしてます。

逆ポーランド表記法

概要

"3 + 5" を "3 5 +" と表記する方法。数式を入力した際、二分木として受け取り、後行順に辿って行くことでこのような形になる。

問題

スタックに下図のように値がスタックに格納されており、矢印のように次に演算子を検知した。その場合の数式はどのようなものとなるか?

f:id:thinline196:20180202221749j:plain:w300




・スタックは上から順番に取り出される。

A. B 演算子 C

クイックソート

クイックソートは対象となる配列を分割していきながら整列を行います。

ポイントとしては、

  • 基準をもとに分割をしてくこと
  • それが再帰的であること

手順


1. 基準値を決定
2. 基準値より小さな要素と大きな要素をそれぞれ分割する
3. 分割してできた2つの配列に対し1.の操作を配列の長さが1になるまで繰り返す



基本はこの順番な気がしますが、基準値の選び方は様々で、配列の一番右側を基準としたり真ん中を基準としたりするなどがあった気がします。

また、小さな要素と大きな要素の選別も両サイドから探し始めて要素を入れ替えていくパターンと、新たな配列を用意してそこに移していくパターンもあります。前者の方法をする場合、再帰処理の前に基準値を配列の真ん中に持ってくること、再帰の際に配列を参照渡ししその小さい要素の開始点と終了点を引数に渡してあげることで実現します。

f:id:thinline196:20180125230102j:plain:w300




正直なところ、ソートをコーディングする際の条件式の書き方によって多少やり方も変わりますが、基本的にやっていることは変わらないと思います。以下、疑似コード。両サイドから入れ替えていくパターンです。

function qsort(Data, left, right)                 //Dataは参照渡しでleftとrightは整列する配列の両端
    int pivot, lp, rp
    pivot = Data[right]                                //基準を右端にとる
    lp = left
    rp = right
    while(lpがrpに等しくない)                   //このwhileで基準より小を左側に基準以上を右に写す
        while(Data[lp]がpivotより小さい)
            lp++
        endwhile
        while(lpがrpより小さい & Data[rp-1]がpivot以上)
            rp--
        endwhile
        if(lpがrpより小さい)
            Dataのlpとrp-1要素を入れ替え
        endif
    endwhile                                     //ここまでで大小が左右に別れる

    Dataのlpとright要素を入れ替え     //基準を真ん中に持ってくる
    if(lp-1がleftより小さい)                //次に整列させる配列の要素が1個なら終了
        qsort(Data, left, lp-1)
    endif
    if(rp+1がrightより小さい)            //次に整列させる配列の要素が1個なら終了
        qsort(Data, rp+1, right)
    endif
endfunction

【応用情報】Unicode -> UTF-8 -> 16進数表現の問題

問題

Unicode文字列をUTF-8エンコードすると、各文字の先頭バイトは2進数表示が0又は11で始まり、それ以降のバイトは10で始まる。16進数表示された次のデータは何文字のUnicode文字列をエンコードしたものか。

E3 83 9F E3 82 B9 E3 83 81 E3 83 AB 32 35 74 68


解き方

変換ツールを使えば一発ですが、それはダメです。まず、文字コードの仕様から見ます。

  • Unicode 世界各国の文字を統一して扱うための16bitコード。後に21bitに拡張された。
  • UTF-8 Unicode8bit単位で符号化するための文字符号化形式。ASCII文字と互換性をもち、ASCIIは0から始まる1byte文字で、それ以外の多バイト文字は、先頭バイト→11から始まる1byte、先頭以外のバイト→10から始まる1byteで符号化する。


ここで重要なのは、UTF-88bit文字ではないこと!!!僕は知りませんでした。笑 つまり1byteごとに区切ってちょうどいい大きさにするやつって感じですか。
ASCIIは7bit文字なので、必然的に1byte内に収まるんですね。一方Unicodeは2byte以上必要になるので、先頭バイトとそれ以外のバイトの始まりが分けられているんですかね?(多分)




文字だとわかりにくいので、図を用意。

f:id:thinline196:20180118172606j:plain:w300
Unicode->UTF8->16進数




改めて問題を確認。

問題文の最初の部分はエンコード後の先頭バイトについて。わざわざ書いてくれていました。優しい!
次に問題の文字列を確認。16進数が並んでいますね。これは、UTF-8を2進数→16進数に直した結果でしょうか。直し方の手順としては、

  • UTF-8から1byte分持ってくる(これで一文字とは限らない) 11100011
  • 4bitごとに分ける  1110 0011
  • 16進数に変換する  E3

こんな感じです。



手順がわかったところで、次は文字の切れ目の目安である、先頭byteが0又は11の探し方です。
2桁で表されている16進数の左側に先頭byteの情報が反映されるため、ここを確認すれば見つけ出すことができます。

  • 先頭が0から始まる4bitは 0000~0111 これを16進数に直すと0~7
  • 先頭が11から始まる4bitは 1100~1111  これを16進数に直すとC~F


つまり、2桁の左側が上の数字から始まっていれば、そこは先頭byteであり、文字の始まりであると言えそうです。
(余談ですが、先頭バイト以外は10で始まると言いましたが、これは16進数でいう8~Bにあたり、見事被らないですね)



あとは、問題から上の条件で始まる16進数を見つけ、数えればそれが答えです。



(E3 83 9F) (E3 82 B9) (E3 83 81) (E3 83 AB) (32) (35) (74) (68)

A.8文字







ところでなんて書いてあるのでしょうね。
以上。