圖說演算法使用JavaScript(二十三)

9-2鏈結串列實作二元樹

所謂鏈結串列實作二元樹,就是利用鏈結串列來儲存二元樹。基本上,使用串列來表示二元樹的好處是對於節點的增加與刪除相當容易,缺點是很難找到父節點,除非在每一節點多增加一個欄位。以上述宣告而言,此節點所存放的資料型態為整數。寫成如下宣告:

class tree {
       constructor() {
              this.data=0;
              this.left=null;
              this.right=null;
        }
}

以串列方式建立二元樹的演算法如下:

var creat_tree=(root,val)=> {
       newnode=new tree();
       newnode.data=val;
       newnode.left=null;
       newnode.right=null;
       if (root==null) {
            root=newnode;
            return root;
      }
      else {
            current=root;
            while (current!=null) {
                  back=current;
                  if (current.data>val)
                      current=current.left;
                  else
                  current=current.right;
      }
      if (back.data>val)
             backup.left=newnode;
      else
             backup.right=newnode;
     }
       return root;
}

JS               list_tree.js

class tree {
	constructor() {
		this.data=0;
		this.left=null;
		this.right=null;
	}
}
var create_tree=(root,val)=> {
	newnode=new tree();
	newnode.data=val;
	newnode.left=null;
	newnode.right=null;
	if (root==null) {
		root=newnode;
		return root;
	}
	else {
		current=root;
		while (current!=null) {
			backup=current;
			if (current.data>val)
				current=current.left;
			else
				current=current.right;
		}
		if (backup.data>val)
			backup.left=newnode;
		else
			backup.right=newnode;
	}
	return root;
}
data=[5,6,24,8,12,3,17,1,9];
ptr=null;
root=null;
for (i=0; i<9; i++) 
	ptr=create_tree(ptr,data[i]);
process.stdout.write('左子樹:\n');
root=ptr.left;
while (root!=null) {
	process.stdout.write(root.data+'\n');
	root=root.left;
}
process.stdout.write('------------------------------\n');
process.stdout.write('右子樹:\n');
root=ptr.right;
while (root!=null) {
	process.stdout.write(root.data+'\n');
	root=root.right;
}
process.stdout.write('\n');

PHP               list_tree.php

class Node {
    public $data;
    public $left;
    public $right;

    public function __construct($data) {
        $this->data = $data;
        $this->left = null;
        $this->right = null;
    }
}

// 定義二元搜尋樹類別
class BinaryTree {
    public $root;

    public function __construct() {
        $this->root = null;
    }

    // 插入節點(遞迴方式)
    public function insert($data) {
        $this->root = $this->insertRecursively($this->root, $data);
    }

    private function insertRecursively($node, $data) {
        if ($node === null) {
            return new Node($data);
        }

        if ($data < $node->data) {
            $node->left = $this->insertRecursively($node->left, $data);
        } else {
            $node->right = $this->insertRecursively($node->right, $data);
        }

        return $node;
    }

    // 中序遍歷(in-order traversal)
    public function inorderTraversal($node) {
        if ($node !== null) {
            $this->inorderTraversal($node->left);
            echo $node->data . " ";
            $this->inorderTraversal($node->right);
        }
    }
}

// 使用範例
$tree = new BinaryTree();
$tree->insert(50);
$tree->insert(30);
$tree->insert(70);
$tree->insert(20);
$tree->insert(40);
$tree->insert(60);
$tree->insert(80);

echo "中序遍歷結果: ";
$tree->inorderTraversal($tree->root);

PHP                  create_tree.php

用class實作二元數:目前中序走訪函數有問題

改為另一個PHP解法   create_tree-1.php

class TreeNode {
    public $value;
    public $left;
    public $right;

    public function __construct($value) {
        $this->value = $value;
        $this->left = null;
        $this->right = null;
    }
}

function insertBST($root, $value) {
    if ($root === null) {
        return new TreeNode($value);
    }
    if ($value < $root->value) {
        $root->left = insertBST($root->left, $value);
    } else {
        $root->right = insertBST($root->right, $value);
    }
    return $root;
}

// 測試:建 BST
$values = [10, 5, 15, 3, 7, 12, 18];
$root = null;

foreach ($values as $val) {
    $root = insertBST($root, $val);
}

// 中序遍歷 BST
function inorderTraversal($node) {
    if ($node === null) return;
    inorderTraversal($node->left);
    echo $node->value . ' ';
    inorderTraversal($node->right);
}

inorderTraversal($root); // 輸出:3 5 7 10 12 15 18

圖說演算法使用JavaScript(二十二)

9-1陣列實作二元樹

如果使用循序的一維陣列來表示二元樹,首先可將此二元樹假想成一個完滿二元樹(Full Binary Tree),而且第k個階度具有2^(k-1)個節點,並且依序存放在此一維陣列中。首先來看看使用一維陣列建立二元樹的表示方法及索引值的配置:

從上圖中,我們可以看到此一維陣列中的索引值有以下關係:

1.左子樹索引值是父節點索引值*2
2.右子樹索引值是父節點索引值*2+1

接著就來看如何以一維陣列建立二元樹的實例,事實上就是建立一個二元搜尋樹,這是一種很好的排序應用模式,因為在建立二元樹的同時,資料已經經過初步的比較判斷,並依照二元樹的建立規則來存放資料。所謂二元搜尋樹具有以下特點:

1.可以是空集合,但若不是空集合則節點上一定要有健值。
2.每一個樹根的值須大於左子樹的值。
3.每一個樹根的值需小於右子樹的值。
4.左右子樹也是二元搜尋樹。
5.樹的每個節點值都不同。

JS                  array_tree.js

length=8;
data=[6,3,5,9,7,8,4,2];
btree=[];
for (i=0; i<16; i++)  btree[i]=0;
	process.stdout.write("原始陣列內容: \n");

for (i=0; i<length; i++) 
	process.stdout.write("[" +data[i] + "] ");
	process.stdout.write('\n');

	for (i=0; i<length; i++) {
		for (level=1; btree[level] !=0;) {
			if (data[i] > btree[level])
				level=level*2+1;
			else
				level=level*2;
		}
		btree[level]=data[i];
	}
process.stdout.write("二元樹內容:\n");
for (i=1; i<16; i++)
	process.stdout.write("[" + btree[i] + "] ");
process.stdout.write('\n');

PHP                array_tree.php

$length=8;
$brtree=array();
$data=array(6,3,5,9,7,8,4,2);
echo "<br>原始陣列:<br>";
print_arr($data);
echo "<br>";

for ($i=0; $i<$length; $i++) {
	for ($level=1; $brtree[$level]!=0;) {
		if ($data[$i]>$brtree[$level])
			$level=$level*2+1;
		else
			$level=$level*2;
	}
	$brtree[$level]=$data[$i];
}

//$brtree = asort($brtree);
//print_arr($brtree);
//echo "<-here<br>";
$count=$length*2;
$new_brtree=array();
for ($j=1; $j<=$count; $j++) {
	if (!$brtree[$j]) 
		$value=0;
	else 
		$value=$brtree[$j];
	//echo "{$j}-[{$value}]   ";
	$new_brtree[]=$value;
}

print_arr($new_brtree);


function print_arr ($arr) {
	$i=0;
	foreach ($arr as $key =>$value){
		$key=$key+1;
		echo $key."[".$value."]   ";
		//echo ($i%8==0 && $i!=0) ? "<br>":"   ";
		$i++;
	}
}

圖說演算法使用JavaScript(二十一)

樹狀演算法

樹狀結構是一種日常生活中應用相當廣泛的非線性結構,樹狀演算法在程式中建立與應用大多使用鏈結串列來處理,因為鏈結串列的指標用來處理樹是相當方便,只需改變指標即可。此外,當然也可以使用陣列這樣的連續記憶體來表示二元樹,至於使用陣列或鏈結串列都各有利弊,本章將分別為各位介紹常見的相關演算法。

由於二元樹的應用相當廣泛,所以衍生了許多特殊的二元樹結構。我們首先為您介紹如下:

完滿二元樹(Full Binary Tree)
H=4                 高度
2^4-1=2*2*2*2-1=15 節點數
完整二元樹(Complete BinaryTree)

如果二元樹的深度為h,所含的節點數小於2^h-1,但其節點的編號方式如同深度為h的完滿二元樹一般,從左到右,由上到下的順序一一對應結合。如下圖:

對於完整二元樹而言,假設有N個節點,那麼此二元樹的階層(Level)為[log^2(N+1)]。

歪斜樹(Skewes Binary Tree)

當一個二元樹完全沒有右節點或左節點時,我們就把它稱為左歪斜樹或右歪斜樹。

嚴格二元樹(strictly binary tree)