圖說演算法使用JavaScript(八)

5-2陣列與多項式

多項式是數學中相當重要的表現方式,通常如果使用電腦來處理多項式的各種相關運算,可以將多項式以陣列Arrray或鏈結串列Linked List來儲存。本節中,我們還是集中討論多項式以陣列結構表示的相關應用。
5-2-1多項式陣列表示法
這個稱為P(x)為一n次多項式,而一個多項式使用陣列結構儲存在電腦中,可以使用兩種模式。
1.使用一個n+2長度的一維陣列存放,陣列的第一個位置儲存最大的指數n,其他位置依照指數n遞減,依序儲存相對應的係數:
使用這種方法的優點就是在電腦中運用時,對於多項式的各種運算(如加法與乘法)較為方便設計。不過如果多項式的係數為多半為零,就顯得太浪費空間了。
2.只儲存多項式中非零項目。如果有m項非零項目,則使用2m+1長的陣列來儲存每一個非零項目的係數及指數,但陣列的第一個元素則為此多項式非零項的個數。
這種方法的優點是可以節省不必要的記憶空間浪費,但缺點則是在多項式各種多項式演算法設計時,會較為複雜許多。

JS            poly_add.js

ITEMS=6;
var PrintPoly=(Poly,items)=>{
  MaxExp=Poly[0];
  for (i=1; i<Poly[0]+2;i++){
    MaxExp=MaxExp-1;
    if (Poly[i]!=0){
      if(MaxExp+1!=0)
        process.stdout.write(Poly[i]+'X^'+(MaxExp+1)+' ');
      else
        process.stdout.write(' '+Poly[i]);
      if (MaxExp>=0)
        process.stdout.write('+');
    }
  }
}
var PolySum=(Poly1,Poly2)=>{
  result=[];
  result[0]=Poly1[0];
  for(i=1;i<Poly1[0]+2;i++){
    result[i]=Poly1[i]+Poly2[i]; //等冪的係數相加
  }
  PrintPoly(result,ITEMS);
}

PolyA=[4,3,7,0,6,2]; //用方法一,降冪方式敘述:3X^4+7X^3+6X+2
PolyB=[4,1,5,2,0,9]; //用降冪方式敘述:X^4+5X^3+2X^2+9
process.stdout.write('多項式A=> ');
PrintPoly(PolyA,ITEMS);
console.log();
process.stdout.write('多項式B=> ');
PrintPoly(PolyB,ITEMS);
console.log();
process.stdout.write('A+B => ');
PolySum(PolyA,PolyB);

PHP           poly_add.php

$ploy_a=array(4,3,7,0,6,2);
$ploy_b=array(4,1,5,2,0,9);

function get_poly ($arr){
  $len=count($arr);
  $maxexp=$arr[0];
  $re_poly="";

  for ($i=1; $i<$arr[0]+2; $i++){
    
    if ($len !=($arr[0]+2)){
      return "多項式格式錯誤";
      break; //檢驗長度
    }
    
    $maxexp=$maxexp-1;
    if ($arr[$i]!=0){
      if(($maxexp+1)!=0)
        $re_poly=$re_poly.$arr[$i]."X^".($maxexp+1)." "; //字串加法
      else 
        $re_poly=$re_poly." ".$arr[$i];
      
      if($maxexp>=0)
        $re_poly=$re_poly."+";
    }
  }

  return $re_poly; 
  //echo $len."-".$maxexp."<br>";
}

function poly_add($arr_a,$arr_b){
   $result=array();
   $result[0]=$arr_a[0];

   for ($i=1; $i<$arr_a[0]+2; $i++){
   
    if ($arr_a[0] !=$arr_b[0]){
      return "多項式長度錯誤";
      break; //兩個是否不同
    }
    
    $result[$i]=$arr_a[$i]+$arr_b[$i];
   }
   return $result;
}

$get_poly_a = get_poly($ploy_a);
$get_poly_b = get_poly($ploy_b);
$get_poly_add = get_poly(poly_add($ploy_a,$ploy_b));

echo $get_poly_a."<br>";
echo $get_poly_b."<br>";
echo $get_poly_add."<br>";

沒有思考到,級數不一樣時的加法。

如:X^5 + 4X^3 +1   跟 X^4 + 5X^3 + 12X + 5  

圖說演算法使用JavaScript(七)

全方位應用的陣列與串列演算法

5-1-1矩陣相加

請設計一程式來宣告3個二維陣列,來實作2個矩陣相加的過程,並顯示兩矩陣相加後的結果。

JS          matrix_add.js

var A = new Array();
var B = new Array();
var C = new Array();

for (let i=0; i<3; i++){
	A[i]=new Array();
	B[i]=new Array();
	C[i]=new Array();
}

A= [[1,3,5],[7,9,11],[13,15,17]];
B= [[9,8,7],[6,5,4],[3,2,1]];

N=3;
for (let i=0;i<3; i++){
	for (let j=0; j<3; j++){
		C[i][j]=A[i][j]+B[i][j];
	}
}
console.log("[矩陣A和矩陣B相加的結果]");
let str='';
for (let i=0; i<3; i++){
	for (let j=0; j<3; j++){
		str=str+C[i][j]+'\t';
	}
	str=str+'\n';
}
console.log(str);

PHP         matrix_add.php


$A_arr=array(array(1,3,5),array(7,9,11),array(13,15,17));
$B_arr=array(array(9,8,7),array(6,5,4),array(3,2,1));
$C_arr=array();
for ($i=0; $i<count($A_arr); $i++){
	 for($j=0; $j<count($A_arr);$j++){
	 	$C_arr[$i][$j] = $A_arr[$i][$j] + $B_arr[$i][$j];
	 }
}
print_r($C_arr);
echo "<br><hr><br>";
foreach ($C_arr as $item ){
     echo "[";
     $t=1;
     foreach ($item as $value){
      if($t<3)  echo $value.",";
      else echo $value; 
        $t++;
			}
     echo "]<br>";
}

函式說明


foreach ($arr as $yourstr){
echo $yourstr //$yourstr => $arr or $value
}

5-1-2矩陣相乘

如果談到兩個矩陣A與B的相乘,是有某些條件限制。首先必須符合A為一個m*n的矩陣,B為一個n*p的矩陣,對A*B之後的結果為一個m*p的矩陣C。

範例:
請設計一程式來實作下列兩個矩陣的相乘結果。

JS       matrix_multiply.js

const M=2;
const N=3;
const P=2;
A=[6,3,5,8,9,7];
B=[5,10,14,7,6,8];
C=[0,0,0,0];
if (M<=0 || N<=0 || P<=0) console.log('[錯誤:維數M,N,P必須大於0]');
for (let i=0; i<M; i++){
	for (let j=0; j<P; j++){
		let Temp=0;
	for (let k=0; k<N; k++) Temp = Temp +parseInt(A[i*N+k])*parseInt(B[k*P+j]);
			C[i*P+j] = Temp;
  }
}
console.log('[AxB的結果是]');
let str='';
for (i=0; i<M; i++){
	for (j=0; j<P; j++){
		str = str+C[i*P+j]+ '\t';
	}
	str = str+'\n';
}
console.log(str);

PHP            matrix_multiply.php

$a_arr = array(array(6,3,5),array(8,9,7));
$b_arr = array(array(5,10),array(14,7),array(6,8));

$a_m = count($a_arr);    //陣列 M列
$a_n = count($a_arr[0]); //陣列 N行

$b_n = count($b_arr);    //陣列 N列
$b_p = count($b_arr[0]); //陣列 P行

$c_arr = array();
if ($a_m <=0 || $a_n <=0 || $b_n<=0 || $b_p<=0 || $a_n!=$b_n){
  echo "條件錯誤";
}
    
//echo "a_m=".$a_m."; a_n=".$a_n."<br>";
//echo "b_n=".$b_n."; b_p=".$b_p."<br>";

for ($i=0 ; $i<$a_m; $i++){
    for ($j=0; $j<$b_p; $j++){
      $temp=0;
     for ($k=0 ; $k<$a_n; $k++){
         $temp = $temp + intval($a_arr[$i][$k])*intval($b_arr[$k][$j]);
         //echo $temp."<br>";
         $c_arr[$i][$j]=$temp;
      }
         //echo $temp."<br>";
   }
    //echo "--<br>";
}
print_r($c_arr);

5-1-3轉置矩陣
「轉置矩陣」At就是把原矩陣的行座標元素相互調換,假設At為A的轉置矩陣,則有At[j,i]=[i,j],如下圖所示:

範例

    請設計一程式來實作一4*4二微陣列的轉置矩陣。

JS          transpose.js

arrA=[[1,2,3,4],[5,6,7,8],[9,10,11,12],[13,14,15,16]];
N=4;
arrB=[[],[],[],[]];
console.log('原設定的矩陣內容');
for (i=0; i<4; i++){
	str = '';
	for (j=0; j<4; j++){
		str= str+arrA[i][j]+'\t';
	}
	console.log(str);
}

for(i=0; i<4; i++){
	for(j=0; j<4; j++){
		arrB[i][j]=arrA[j][i];
	}
}
console.log('[轉置矩陣的內容為]');
for (i=0; i<4; i++){
	str = '';
	for (j=0; j<4; j++){
		str= str+arrB[i][j]+'\t';
	}
	console.log(str);
}

PHP          transpose.php

$a_arr = array(array(1,2,3,4),array(5,6,7,8),array(9,10,11,12),array(13,14,15,16));
$b_arr = array();

$arr_m = count($a_arr);
$arr_n = count($a_arr[0]);

echo "M-".$arr_m."N-".$arr_n."<br>";

for ($i=0; $i<$arr_m; $i++){
  for($j=0; $j<$arr_n; $j++){
      $b_arr[$i][$j]=$a_arr[$j][$i];
  }
}

foreach($a_arr as $item){
   foreach ($item as $value){
    echo $value."   ";
   }
   echo "<br>";
}

foreach($b_arr as $item){
   foreach ($item as $value){
    echo $value."   ";
   }
   echo "<br>";
}
5-1-4 稀疏矩陣
稀疏矩陣最簡單的定義就是一個矩陣中大部分的元素為0,即可稱為「稀疏矩陣」Sparse Matrix。例如下列的矩陣就是典型的稀疏矩陣。
當然如果直接使用傳統的二維陣列來儲存上圖的稀疏矩陣也是可以,但事實上許多元素都是0。這樣的做法在矩陣很大時的稀疏矩陣,就會十分浪費記憶體空間。而改進空間的方法就是利用三項式(3-tuple)的資料結構。我們把每一個非零項目以(i,j,item-value)來表示。更詳細的形容,就是假如一個稀疏矩陣有n個非零項目,那麼可以利用一個A(0:n,1:3)的二維陣列來表示。
其中A(0,1)代表此稀疏矩陣的列數,A(0,2)代表此稀疏矩陣的行數,而A(0,3)則是此稀疏矩陣非零項目的總數。另外每一個非零項目以(i,j,item-value)來表示。其中i為此非零項目所在的列數,j為此非零項目所在行數,item-value則此分零項的值。以上圖6*6稀疏矩陣為例,則可以以下表示:
A(0,1)=>表示此舉矩陣的列數。
A(0,2)=>表示此舉矩陣的行數。
A(0,3)=>表示此舉矩陣非零項目的總數。
這種利用3項式(3-tuple)資料結構來壓縮稀疏矩陣,可以減少記憶體不必要的浪費。

JS          sparse.js

var NONZERO=0;
temp=1;
Sparse=[[15,0,0,22,0,-15],[0,11,3,0,0,0],
        [0,0,0,-6,0,0],[0,0,0,0,0,0],
        [91,0,0,0,0,0],[0,0,28,0,0,0]];//宣告稀疏矩陣,稀疏矩陣的所有元素設為0
str='';
console.log('[稀疏矩陣的各個元素]');
for (i=0;i<6;i++) {
   for(j=0;j<6;j++){
      process.stdout.write(Sparse[i][j]+'\t');
      if (Sparse[i][j]!=0) NONZERO=NONZERO+1;
   }
   console.log();
}

Compress=new Array();   //宣告壓縮陣列
for (let i=0; i<NONZERO+1; i++) Compress[i]=[];

//開始壓縮稀疏矩陣
Compress[0][0]=6;
Compress[0][1]=6;
Compress[0][2]= NONZERO;

for (i=0; i<6; i++){
   for (j=0; j<6; j++){
      if (Sparse[i][j]!=0){
         Compress[temp][0]=i;
         Compress[temp][1]=j;
         Compress[temp][2]=Sparse[i][j];
         temp=temp+1;
      }
   }
}
console.log('[稀疏矩陣壓縮後的內容]');//印出壓縮矩陣
for (i=0; i<NONZERO+1;i++){
   for (j=0; j<3; j++){
      process.stdout.write(Compress[i][j]+'\t');
   }
   console.log();
}

PHP            sparse.php

$sparse=array(array(15,0,0,22,0,-15),array(0,11,3,0,0,0),
              array(0,0,0,-6,0,0),array(0,0,0,0,0,0,0),
              array(91,0,0,0,0,0),array(0,0,28,0,0,0));
$M = count($sparse);
$N = count($sparse[0]);
$nonzero=0;//計算沒有為零的數量
$temp=1;
$compress=array();

for ($i=0; $i<$N; $i++){
  for($j=0; $j<$N; $j++){
    if ($sparse[$i][$j]!=0)
      $nonzero=$nonzero+1;
  }
}
echo "原始陣列"."<br>";
foreach ($sparse as $key => $item) {
    foreach($item as $list => $value){
      echo $value."   ";
    }
      echo "<br>";
}

$compress[0][0]=$M;
$compress[0][1]=$N;
$compress[0][2]=$nonzero;

for($i=0; $i<$N; $i++){
  for($j=0; $j<$M; $j++){
    if ($sparse[$i][$j]!=0){
      $compress[$temp][0]=$i;
      $compress[$temp][1]=$j;
      $compress[$temp][2]=$sparse[$i][$j];
      $temp=$temp+1;
    }
  }
}
echo "壓縮後的陣列<br>";

foreach ($compress as $key => $item) {
    foreach($item as $list => $value){
      echo $value."   ";
    }
      echo "<br>";
}

圖說演算法使用JavaScript(六)

3-14階梯狀圖形外觀 Staircase

對於給定數量的級別,請用$和符號空格打輸出類似「階梯狀」的圖形外觀。

JS           staircase.js

const draw = no => {
	let pictures = "";
	for (let row=0; row<no; row++){
		str="";
		for (let column = 0; column<no; column++)
			str += column <= row? "$":" ";
		    pictures +=str + "\n";
	    }
		return pictures;
};
console.log(draw(15));

PHP          staircase.php

$my_num = 15; 

function pictures($n){

	for($i=0; $i<$n; $i++){
		  
		  if($i!=0) echo "<br>";
		  
		  for ($j=0; $j<=$i; $j++){
		  echo "$";	
		  }
	}

}

$pictures = pictures($my_num);

3-15金字塔圖形外觀  Pyramid

對於給定數量的級別,請使用$和符號空格打輸出類似「金字塔」的圖形外觀。

課本的JS範例是錯的

我解的是對的↓

PHP           pyramid.php

$my_num = 15; 

//function pictures($n){

	$layer = $my_num; //幾層
	$num = $my_num*2-1; //個數
	$mid = $layer ; //中間數

	for($h=1; $h<=$layer; $h++){
	   for($j=$mid-$h; $j>=1; $j--){
	   	echo "  ";
	   }
	   for($i=1; $i<=$h*2-1; $i++){
	   	echo "$";
	   }
	   echo "<br>";
	 }
echo "<br>".$layer."-".$num."-".$mid;

JavaScript 概念三明治(二)

物件型別

   「物件」指的就是物件。恩,但其實有很多東西本身也算是物件,例如陣列和函式,不相信嗎?讓我們繼續看下去。你可以在JavaScript裡面宣告一個陣列,然後用typeof去得到這個陣列的型別,就可以看出一些端倪:
JavaScript提供了Array這個所有地方都可以使用全域物件,而在這個Array.isArray的方法能夠讓我們使用,來知道某數值是不是一個陣列。
好,那麼剛剛說函式也是物件型別,是這樣嗎?你會發現,當你一樣用typeof去觀察一個function的型別時,所得到的結果不是object,而是function。
先別急著反駁,讓我先用一個最簡單的方式證明你看。前面應該提到過,一個物件型別的數值,可以用.或是[]來做存取。那麼首先我宣告一個函式,並把這個函式當作物件使用,在上面新增一個屬性之後,再取出這個屬性來看看,是不是得到同樣的值:
    函式真的是像物件一樣的東西,事實上,它真的就是。在JavaScript裡面,函式算是一個比較物件,稱為「函式物件」(Function Object),它除了可以被當作函式來呼叫,也能夠當作物件使用。

一級函式  First-Class Function

    當我們說一個語言具有一級函式的特性時,代表這個語言把函式當作變數值一樣看待,也因此可以把函式當作參數一樣傳入另外一個函式裡面,也能夠以函式作為另一個函式的回傳值。在Function Programing 裡面,也是因為這個特性,才有辦法做到複合函式(Function Composition)。
在JavaScript內,函式本身也是一個特殊的物件(就是函式物件),所以才有辦法把函式當作參數傳給另外一個函式來做使用。

高階函式 High Order Function

    高階函式有人稱為HOF。只要是可以接收函式作為參數,或是回傳函式作為輸出的函式,我們就能夠把它稱為高階函式,例如,JavaScript裡面的陣列這個類別,上面有許多讓你很方便能夠對陣列做操作的方法,例如:map方法讓你能夠依序對每個陣列做一些修改、而filter則讓你能夠過濾掉不符合條件的元素。
我們可以試著思考看看他們是怎麼被實做出來的,下面就以Array.map為例,一起來模擬這個方法、邊思考它的運作方式。

試著實作陣列上的map方法

map是在任何陣列上都可以取用的一個方法,它接收一個能夠修改陣列元素的函式。並在運算之後將每個元素被修改後的數值,以另外一個全新陣列最為回傳值。
   由於map其實就是對陣列元素個別巡訪,然後做某些操作之後回傳,所以可以推敲出可能的步驟如下:
1.將包含開發者修改的邏輯函式傳入map方法內。
2.先創造一個全新的陣列,用以存放運算之後的結果。
3.執行一個以陣列長度為執行次數的迴圈。
4.利用迴圈的計數值作為陣列的索引,來找到每個陣列元素。
5.取得陣列元素、逐個取得修改函式運算完的結果。
6.逐個放入運算結果值的陣列並待迴圈節結束後回傳。
有了這些運作的邏輯,我們也能夠嘗試自己實作類似map方法的邏輯,看起來可能會像是這樣:
function arrayMap(fn, array){
 let newArray = [];
  for (let i=0; i < array.length; i++){
   let result = fn(array[i]);
    newArray.push(result);
   }
   return newArray;
}
let array =[1,2,3];
let newArray = arrayMap(function(item){
   return item*2;
},array)
console.log(newArray);

試著實作陣列上的filter方法

    既然都講到這裡了,我們就來看看另一類似的filter方法。filter一樣接受一個函式來進行運算,只不過相對於map是變更陣列的個別元素,filter的目的是過濾不需要的元素,你應該也能從字面上看出來。
傳進filter的函式,會以回傳值的條件判斷,來作為陣列元素會不會被過濾掉的依據,若回傳值與布林值的true等價、可被轉型為true,那麼就保留這個元素,反之這個元素就不會出現在運算結果的陣列裡面。
而我們同樣的能夠推測filter的運作流程,預期上會與map相似:
1.將包含開發者判斷邏輯的函式傳入filter方法內。
2.先創造一個全新的陣列,用以存放運算的結果。
3.執行一個以陣列長度為執行次數的迴圈。
4.利用迴圈的計數值作為陣列的索引,來找到每個陣列元素。
5.取得陣列元素、並逐個取得判斷函式運算文成的結果。
6.依照判斷函式的結果決定是否要放入結果值的陣列,並待迴圈結束後回傳。
function arrayFilter(fn, array){
   let newArray=[];

   for (let i=0; i < array.length; i++){
      let arrayElement = array[i];
      let shouldkeep = fn(arrayElement);

      if (Boolean(shouldkeep)){
         newArray.push(arrayElement);
      }
   }

   return newArray;
}

let array = [1,2,3];
let resultArray = arrayFilter(function (element){
   return element > 1;
},array);

console.log(resultArray);

圖說演算法使用JavaScript(五)

3-11最大利潤 Max Profit

    給定一系列股票價格,找出產生最大利潤的最小買入價和最大賣出價。舉例以陣列的方式提供一系列的股票價格,例如[24,27,32,36,45],這個函數會找出這一系列的股票價格的最低買入價及最高賣出價,再以陣列的方式回傳,如此一來可以計算出這支股票的最大利潤。

JS          max_prifit.js

var maxProfit=(prices)=> {
	let low = prices[0] < prices[1]? prices[0]:prices[1];
	    high = prices[0] < prices[1] ? prices[1]:prices[2];
	let maxProfit = high - low;
	let temp = low;

	for (let index = 2; index < prices.length; index++){
		const sellPrice = prices[index];

		if (low > sellPrice) temp = prices[index];
		else{
			const  profit = sellPrice - low;
			if (profit > maxProfit)
				(maxProfit = profit),
				(high = sellPrice),
				(low = temp);
		}
	}
    return [low, high];
}
console.log("產生最大歷潤的最小買入價和最大賣出價分別為:");
console.log(maxProfit([24,27,32,36,45]));

PHP          max_profit.php

$arr= array (24,27,32,36,45);
function get_max_profit($arr){

$low = ($arr[0] < $arr[1]) ? $arr[0] : $arr [1];
$high = ($arr[0] > $arr[1]) ? $arr[0] : $arr[1];
$maxProfit = $high - $low;
$temp = $low;
$len = count($arr);
$ans = array();
$i=1;
 while (  $i <= $len-1){
		$sellPrice = $arr[$i];

		if($low > $sellPrice) $temp = $arr[$i];

		else {
			$profit = $sellPrice -$low;
			if($profit > $maxProfit){
				$maxProfit = $profit;
				$high = $sellPrice;
				$low = $temp;
			}

		}
		$i++;
	}

	$ans[] =array($low,$high);
	return $ans;
}
$my_max_profix = get_max_profit($arr);
print_r($my_max_profix);

3-12費伯納序列  Fibonacci

JS          fibonacci.js

var fib=(n)=>{
	if (n==0){
		return 0;
	} else if (n==1 || n==2){
		return 1;
	} else{
		return (fib(n-1)+fib(n-2));
	}
}

const num=10;

for(i=0; i<=num; i++){
	console.log("fib("+i+")=" + fib(i));
}

PHP          fibonacci.php

$my_num = 30;  //超過38時,執行會超過30秒
function my_fibnacci ($n){

	switch ($n) {
		case ($n==0):
			return 0;
			break;
		case ($n==1 ||$n==2):
			return 1;
			break;
		default:
			return(my_fibnacci($n-1)+my_fibnacci($n-2));
			break;
	}
}
for ($i=0 ; $i<=$my_num; $i++){
	echo "fib(".$i.")=".my_fibnacci($i)."<br>";
}

3-13記憶式費伯納序列 Memoized Fibonacci

    這是一種具有記憶性質的費伯納序列的做法,它可以使得在求取費伯納序列能以較高的效率取得。也就是動態規劃法,它是一種具備記憶體功能的演算法。

JS          momoized_fibonacci.js

output =[];   //宣告陣列
var fib=(n)=>{
	if (n==0)  return 0;
	
	if (n==1 ) return 1;
	else{
		output[0]=0;
		output[1]=1;
		for (t=2; t<=n; t++){
			output[t]=output[t-1]+output[t-2];
		}
		return output[n];
	}
}
const num=9;
for(i=0; i<=num; i++){
	console.log("fib("+i+")=" + fib(i));
}

PHP           memoize_fibonacci.php

$my_num = 50; 

function my_fibnacci ($n){
   $ans=array();
	 
	 if($n==0) return 0;
	 if($n==1) return 1;
	 else{
	 	$ans[0]=0;
	 	$ans[1]=1;
	 	for ($j=2; $j<=$n; $j++){
	 		 $ans[$j]=$ans[$j-1]+$ans[$j-2];
	 	}
	 	return $ans[$n];
	 }
	}

for ($i=0 ; $i<=$my_num; $i++){
	echo "fib(".$i.")=".my_fibnacci($i)."<br>";
}

JavaScript 概念三明治(一)

物件 Object

物件是JavaScript 裡面很重要的型別,在其他語言裡的物件可能有不一樣的意思,但在JavaScript裡面,物件就是「一連串鍵與值配對(key-value pairs)的組合」。換句話說,這意味著物件是由一個個的名稱與一個個的對應內容聚集在一起後形成的結果,這個物件的鍵名又被稱為物件的屬性(property),屬性的用途跟變數有一點相像,都是讓開發者在存取時有一個獨特的名稱,用以區別不同內容。

創造一個物件

一個物件最簡單的宣告方式,就是在賦予變數時,用兩個大括號,將即將宣告的物件包裹起來,並用冒號來區分物件內的屬性名稱與想要存放的數值內容。範例如下:

物件屬性的存取

一般的變數在宣告並被賦值後,我們就用該變數名稱來表示要存取對應內容。而這個變數如果存取的內容是物件,則可以搭配中括號「[]」,或是小數點符號「.」來拿到物件裡面某個屬性的內容:

anObject["key1"] --> 用中括號來存取物件屬性的內容
abObject.key1 --> 用小數點符號來存取物件屬性的內容

陣列 Array

陣列在許多程式語言都會出現。它跟物件有一點點像,但是使用的方式有些差異。如同前面提到的,如果物件是一連串的名稱與對應內容組合而成的型別,那麼陣列就是「把一系列的數值放在一起形成的組合」。意即可以先把陣列看成沒有屬性的物件,只是陣列並非像物件那樣是用大括號來宣告。
創造一個陣列
使用中括號就能簡單宣告出一個陣列,並在中括號內用逗點區隔想要存放的各個值,與物件一樣,陣列可以存放多於一個以上的數值。

var array = ["內容一", "內容二"];

與物件屬性可以存放的內容相同,對於陣列裡面可以存放的內容,沒有型別上的限制,想要在陣列裡面放數字、字串、布林、物件或甚至是另外一個陣列,都是可以的。通常存放陣列裡面的數值,我們會用「元素」(element)來稱呼。
存取陣列的元素
存取陣列元素的方法跟物件一樣,可以使用中括號來表示。但是陣列不像物件那樣有明確的屬性可以知道要對哪一個內容作存取,該可麼辦呢?在陣列裡面的處理方式是,既然沒有明確的名稱,那麼就從第一個元素開始,給每一個元素一個足以區別內容的號碼,我們稱之為「索引」(index),陣列的這個號碼,是從0開始算的。
存取陣列的長度
陣列元素的長度可以用.length來取得,它的用法就跟存取物件屬性一模一樣。事實上,陣列背後的實作也是以物件來達成,只是使用的方式不一樣而已。

var array = ["1", "2", "3"];
console.log(array.length);
//3

函式宣告

函式最簡單的使用方式是透過function這個關鍵字來告訴JavaScript你想要宣告的函式。與變數一樣,函式在一般情況下也必須具有名稱才能讓JavaScript知道你想呼叫(執行)哪一個函式。
至於函式執行後回傳的結果則透過return來決定要回傳內容是什麼,而就像前面提到的一樣,return不一定要存在於函式的內容中,只不過如果在函式內沒有任何return被使用,或是有return但後面沒有任何要回傳的值,JavaScript都會自動幫你將回傳的值設置為undefined,所以可以把一個函式回傳的預設值當作是undefined

變數宣告

ES6出現了兩個新的變數宣告的關鍵字,也是目前實際產品開發中最常使用到的兩個語法,它們分別是let以及const。在前面則是用var來處理。
let
let其實與var差不多,與var不同是宣告變數的範疇、以及不能被重複宣告。  var是函式範疇,而let是區塊範疇 

let 是區塊範疇
區塊範疇相比於var的函式範疇,究竟有什麼優缺點呢?一個很明顯的好處就是,能夠更有效避免變數意外地被修改或覆蓋。
var是函式範疇
當我在全域宣告一個變數i,並在後面使用了for迴圈,for迴圈裡面的條件判斷也用到另外一個新的宣告的i,但是你會發現在for條件判斷用i其實在迴圈進行時也無意間對外面的全域變數進行了修改,最後,外面的變數i的內容就跟著變成10。而若使用const和let,這個問題就不會發生。
const和let的範疇則是一樣的,那麼const和let相較起來又有什麼差異呢?
const
const這個字本身意味著常數。它的範疇與let相同,都屬於區塊範疇,但是與let布一樣的是,使用const宣告變數在第一次宣告,並被指派後,就不能修改。也就是說,使用const所宣告的變數,只能夠被讀取。
使用const宣告變數時,一定要跟著一起指派內容。它與var或let不一樣,可以指宣告變數名稱而不給值。若你使用const時這麼做的話,就會出現錯誤。
而上面講到const所宣告的變數無法再重新指派的規則,其實有一個看起來像是例外的情況,那就是當const與物件、陣列一起使用時,會有這樣的情形發生:
看到了嗎?使用const所宣告的變數內容若是物件,則該物件裡的屬性還是能夠被存取跟修改,這看來很像是在對該變數內容作修改,其實並沒有。當變數被宣告並且存放的數值是一個物件時,所帶給變數的並不是物件本身的內容,而是物件存放的記憶體位置。
所以只要該變數所參考的記憶體位置保持一致,就不會有問題。而換另外一個角度來看,若我重新對const所宣告的變數指派另一個物件,那是會報錯的。

圖說演算法使用JavaScript(四)

3-7題目:將句中或片語單字反轉 Reverse Words
給定一個短句,將句子中的每一個單字的字母順序反轉,請注意,是句子中的每個單字,以字做為單位進行字母順序反轉。

JS     reverse_word.js

const change = str =>{
	const answer = [];

	str.split(" ").forEach(word =>{ //注意""有空格
		let temp = "";  //注意""沒空格
		for(let i = word.length-1; i>=0; i--){
			temp +=word[i];
		}
	answer.push(temp);
	});

	return answer.join(" "); //注意""有空格
};

console.log("原來句子:");
console.log("The greatest test of courage on earth is to bear defeat without losing heart.");
console.log("句子中每個單字都反轉:");
console.log(change("The greatest test of courage on earth is to bear defeat without losing heart."));

PHP        reverse_word.php

$my_str="The greatest test of courage on earth is to bear defeat without losing heart.";

function my_reverse ($str){

  $my_arr = explode(" ", $str);
  $len = count($my_arr);
  $i = 0;
  while ($i<=$len-1){
    $t_str[]=strrev($my_arr[$i]);
    $i++;
  }

  $my_re=join(" ",$t_str);
  return $my_re;
}

$my_re = my_reverse($my_str);

echo $my_str."<br>";
echo $my_re;
使用函數:
explode()
count()
join() 或 implode() 把陣列元素组合成的字符串。
strre() 反轉字串
3-8題目:首字大寫 Capitalization
給定一個短句,將句子中的每一個單字的第一個字母轉為大寫,請注意,是每句子中的每一個單字,以字做為單位進行首字大寫轉換。

JS       capitalization.htm

const bigletter = sentence => {
	const words = []; //宣告words陣列

	for (let word of sentence.split(""))
		words.push(word[0].toUpperCase() + word.slice(1));

	return words.join("");
};

console.log("原來的句子");
console.log("Genius is an infinite capacity for taking pains.");
console.log("首字大寫的句子:");
console.log(bigletter("Genius is an infinite capacity for taking pains."));

PHP          capitalization.php  

$my_str="Genius is an infinite capacity for taking pains.";
function my_bigletter ($str){
	$my_arr = explode(" ",$str);
	$my_len = count($my_arr);
	$t_big = array();
	$i = 0;
	
	while ($i<=$my_len-1){
		array_push($t_big, ucfirst($my_arr[$i]));
		$i++;
	}
	
	$f_big = implode(" ", $t_big);
	return $f_big; 
}

$f_bigletter=my_bigletter($my_str);
echo $f_bigletter;
使用函數
explode()
count()
array_push() 将一个或多个元素插入陣列的尾端
implode()
3-9平均值 Mean
給定一個數字組,計算平均值,例如給定的陣列[1,2,3],則回傳平均值為2.0。這支程式可以利用Javascript的reduce()方法,它能將陣列中每項元素(由左至右)傳入回呼函式,將陣列化為單一值。

JS        mean.js

const stat1 = [2,8,7,6,5,6,4];
const stat2 = [9,2,8,7,6,5,6,4];
const reducer = (accu, currentValue) => accu + currentValue;
//2,8,7,6,5,6,4
console.log("第1組原始資料");
console.log("[2,8,7,6,5,6,4]");
console.log(stat1.reduce(reducer)/stat1.length);
//9,2,8,7,6,5,6,4
console.log("第2組原始資料");
console.log("[9,2,8,7,6,5,6,4]");
console.log(stat2.reduce(reducer)/stat2.length);

PHP        mean.php

$my_arr_1=array(2,8,7,6,5,6,4);
$my_arr_2=array(9,2,8,7,6,5,6,4);
echo "數字陣列1:";
print_r($my_arr_1);
echo "<br>";
echo "數字陣列2:";
print_r($my_arr_2);
echo "<br>";
function get_mean ($my_arr){
$len = count($my_arr);$total = array_sum($my_arr);
$mean = $total/$len;return $mean;
}
$my_mean_1 = get_mean($my_arr_1);
$my_mean_2 = get_mean($my_arr_2);
echo "my_mean_1=".$my_mean_1."<br>";
echo "my_mean_2=".$my_mean_2."<br>";
使用函數
count()
array_sum() 計算陣列元素值的總和
3-10回傳給定總和的數值序對 Two Sum
這個函數會傳入兩個引數,第一個引數是一個包含多個數字的整數值,第二個引數為給定的總和值(sum),該函數會回傳所有加總起來會等於給定總和的所有數值序對,這個數值序對內的數字可以多次使用。例如給定的第一個引數為[1,2,2,3,4],給定的第二個引數為數值4,則陣列中的3及1加總結果為4。另一種狀況,陣列中的2及2加總結果為4,則回傳的結果值為[[2,2],[3,1]]。

JS          two_sum.js

const twototal = (num, total) =>{
	const subpair = [];
	const ans = [];
      for (let a of num) {
		const b = total -a;
		if (ans.indexOf(b) !== -1){
			subpair.push([a,b]);
		}
		ans.push(a);
	}
	return subpair;
}
console.log ("第1組原始資料");
console.log ("twototal([1,2,2,3,4], 4)=");
console.log (twototal([1,2,2,3,4], 4));
console.log ("第2組原始資料");
console.log ("twototal([2,3,5,1,5,1,3,4], 6)=");
console.log (twototal([2,3,5,1,5,3,4], 6));
Array.prototype.indexOf() 找出元素索引值的陣列indexOf()
Array.prototype.push() 會將一或多個的值,加入至一個陣列中

PHP          two_sum.php

$arr_1 =array (2,3,5,1,5,1,3,4);
function get_two_sum($arr, $num){
$t_arr = array();
$ans = array();
$len = count($arr);
$i=0;
	while ($i <=$len-1){
		$b = $num - $arr[$i];
		$arr_slice = array_slice($arr, $i+1);
		if (in_array($b, $arr_slice) == true){
			$ans[]=array($arr[$i], $b);
		}
		$i++;
	}
	return $ans;
}
$my_two_sum = get_two_sum($arr_1, 6);
print_r($my_two_sum);
使用函數
count()
arr_slice($arr,$num) 函數切割 $arr陣列、$num從多少開始切
in_array($val,$arr) 在函數中尋找是否有值

圖說演算法使用JavaScript(三)

3-4題目:最常出現的字母
撰寫一個函數,傳入引數為一個字串,其輸出的結果會將該字串中出現頻率最高的字母輸出。

JS     max_character.htm

const my_max = str =>{
	const words = {};  //You can create a const object

	for (let ch of str)
		words[ch] = words[ch]+1 || 1;

	let counter = 0;
	let most = null;

	for (let ch in words) {
		if (words[ch] > counter){
			counter = words[ch];
			most = ch;
		}
	}
	return most;
};
console.log(my_max("tomorrow"));

PHP    max_character.php

//$my_str="不要在你的智慧中夾雜著傲慢。不要使你的謙虛新缺乏智慧。";
$my_str="Hello World! 你好世界!";
//$my_str="我為人人,人人為我";
//$my_str="gooddoog";
echo "原來的字串:".$my_str."<br>";

function mb_str_split($str){
	//不分中英字串都可以切成陣列
	return preg_split('/(?<!^)(?!$)/u',$str);
}

function get_most_str($str){

	$arr = mb_str_split($str);    //切字串成陣列
	$len = count ($arr);
	$my_count = 0;
	
	for ($i=0 ; $i<=$len-1; $i++){
		
		$str_count=substr_count($str,$arr[$i]);
		//計算出現次數
		if ($str_count>$my_count){

			$my_count=$str_count;
			$most = $arr[$i];
		}
	}

	return [$most,$my_count];
}

$most_str= get_most_str($my_str);
echo "最大數為".$most_str[0]."=".$most_str[1]."<br>";
使用函數:
substr_count() 計算子字串在字串中出現的次數
3-5題目:判斷兩字是否相同 Anagrams
這個函數的功能是給定兩個包含相同數量的字母的單字或片語,並進行判斷所傳入的單字或片語是否相同的檢查函數。

JS   anagrams.js

const  compare = str =>{

	const table ={}; //宣告一個table物件

	for (let char of str.replace(/\w/g, "").toLowerCase())
		table[char] = table[char]+1 || 1;

	return table;
};

const anagrams = (strA, strB) =>{
	const charA = compare(strA);
	const charB = compare(strB);

	if (Object.keys(charA).length !==Object.keys(charB).length)
		return false;

	for (let char in charA)
		if (charA[char] !== charB[char]) return false;

	return true;
};

console.log(anagrams("happy birthday", "happy birthday"));
console.log(anagrams("happyy birthday", "hhappy birthday"));
說明:
str.replace(/\w/g,"").toLowerCase();
\w 包含數字字母與底線,等同於[A-Za-z0-9_]      出處

PHP     anagrams.php

$my_str_1="happy birthday";
//$my_str_1="大家好";
$my_str_2="hhhappybirthday";
//$my_str_2="happy birthday";

echo "字串_1:".$my_str_1."<br>";
echo "字串_2:".$my_str_2."<br>";

function mb_str_split($str){
	//不分中英字串都可以切成陣列
	return preg_split('/(?<!^)(?!$)/u',$str);
}

/*
$str_1_ar = mb_str_split($my_str_1);
$str_2_ar = mb_str_split($my_str_2);

$str_len_1 = count ($str_1_ar);
$str_len_2 = count ($str_2_ar);
$flag =($str_len_1 != $str_len_2) ;	
$i=0;
if($flag==1){
	echo "這兩個長度不同<br>";

}else{
	
  while ($i<= $str_len_1-1){

  		$flag2=strcmp($str_1_ar[$i],$str_2_ar[$i]);
	    
	    if($flag2==1){
	    	echo "這兩個字母不同<br>";
	    	break;
	    }
	      $i++;
	}
}
*/
function my_anagrams ($str1, $str2){

	$str_1_ar = mb_str_split($str1);
  $str_2_ar = mb_str_split($str2);
  $str_len_1 = count ($str_1_ar);
  $str_len_2 = count ($str_2_ar);
  $flag1 =($str_len_1 != $str_len_2) ;
  $i=0;	
  $msg1="這兩個長度相同<br>";
  $msg2="這兩個字母相同<br>";

  if($flag1==1){
	  $msg1="這兩個長度不同<br>";
	  $msg2="這兩個字母不同<br>";
  }
  else{
  	 while ($i<= $str_len_1-1){

  		$flag2=strcmp($str_1_ar[$i],$str_2_ar[$i]);
	    
	    if($flag2==1){
	    	$msg2="這兩個字母不同<br>";
	    }
	      $i++;
		}//while
   
  }//elseif
	
  return [$msg1,$msg2];

}//function

$my_msg = my_anagrams($my_str_1,$my_str_2);

echo $my_msg[0];
echo $my_msg[1];
函數使用:
strcmp(str1,str2) 比較兩個字串(對大小寫敏感)
3-6題目:反向陣列  Reverse Array
將給定的陣列中的元素,由前往後排列方式,變更成由後往前的排列順序。例如給定原陣列的內容值為[1,2,3,4],則所撰寫的這個函數會回傳[4,3,2,1]‵.

JS      reverse.js

const rev1 = dim =>{
	for (let i=0; i <dim.length /2; i++){
		const temp = dim[i];
		dim[i] = dim[dim.length-1-i];
		dim[dim.length-1-i]=temp;
	}
	return dim;
};

const rev2 = dim =>{
	for (let i=0; i<dim.length/2; i++) {
		[dim[i], dim[dim.length-1-i]] = [
			dim[dim.length-1-i], dim[i]
		];
	}
	return dim;
};

console.log("['a','e','i','o','u']反向陣列:",rev1(['a','e','i','o','u']));
console.log("[2,4,6,8,10]反向陣列:",rev2([2,4,6,8,10]));

PHPh      reverse.php

$my_arr_1=array("a","e","i","o","u");
$my_arr_2=array(2,4,6,8,10);
//$my_str_2="happy birthday";

echo "陣列_1:";
print_r($my_arr_1);
echo "<br>";
echo "陣列_2:";
print_r($my_arr_2);

$re_arr_1 = array_reverse($my_arr_1);
$re_arr_2 = array_reverse($my_arr_2);

echo "<br>";
echo "反陣列_1:";
print_r($re_arr_1);
echo "<br>";
echo "反陣列_2:";
print_r($re_arr_2);
函數使用:
array_reverse($arr) 以相反的順序傳回數組

圖說演算法使用JavaScript(二)

3-1題目:字串反轉(String Reversal)
撰寫一個函數,傳入的引數為一個字串,其輸出結果會將該字串以反轉的方式輸出。

JS   String_Reversal.js

const reverse1 = str =>
	str
		.split("")
		.reverse()
		.join("")

const reverse2 = str =>{
	let result = "";
	for (let ch of str) result = ch +result;
		return result;
	};

console.log(reverse1("不要在你的智慧中夾雜著傲慢。不要使你的謙虛新缺乏智慧。"));
console.log(reverse2("一個人年輕的時候,不會思索,他將一事無成。"))

PHP    String_Reversal.php

$my_str="不要在你的智慧中夾雜著傲慢。不要使你的謙虛新缺乏智慧。";
echo "原來的字串:".$my_str."<br>";

function my_reverse ($str){

	$len = strlen($str);
	$str_ar = str_split($str);
	$str_ar = array_reverse($str_ar);
	$str_ar = join($str_ar);
	//echo $len;
	return $str_ar;

}

$my_len=my_reverse($my_str); 
$my_len = strrev($my_str);
//這兩個方法,中文反轉後都會出現亂碼。
使用函數
strlen($str) 傳回字串的長度
array_reverse($arr) 以相反的順序傳回陣列
join() 把陣列組合為一個字串
strrev 反轉字串

PHP 中文字串反轉會出現亂碼,需要做以下的判斷。


3-2題目:迴文Palindrome
所謂迴文是指由左唸到右或由又唸到左,字母排列順序都一樣的,例如以下的字串就是一個迴文:
"人人愛我!我愛人人"

JS   palindrome.js

const isPalindrome = str =>
	str
        .split("")
	.every((ch,index) => ch === str[str.length - 1 - index]);

console.log("[人人愛我!我愛人人] 這句話是否為迴文(palindrome)?",
             isPalindrome("人人愛我!我愛人人"));

PHP   palindrome.php

$my_str="不要在你的智慧中夾雜著傲慢。不要使你的謙虛新缺乏智慧。";
//$my_str="Hello World! 你好世界!";
//$my_str="我為人人,人人為我";
//$my_str="gooddoog";
echo "原來的字串:".$my_str."<br>";

function mb_str_split($str){
	//不分中英字串都可以切成陣列
	return preg_split('/(?<!^)(?!$)/u',$str);
}

$arr = mb_str_split($my_str);
print_r ($arr);
$len = count($arr);     //計算字串數
$mid_len=floor($len/2); //取中間整數
echo "<br>-----<br>";

for ($i=0; $i<=$mid_len-1; $i++){

	 if($i==0) 
	 	$last=$len-1;   //第一筆 頭-尾
	 else 
	   $last=$len-1-$i;
	//比對兩個字是否一樣,一樣就為0
	$check_f = strcmp($arr[$i],$arr[$last]); 
	echo $arr[$i]."-".$arr[$last]."<br>";
	if ($check_f ==0){
		$flag="true";
		continue;
	}else{
		$flag="false";
		//break;
	}
}

echo "<br>".$len."-".$mid_len."<br>";
echo "FLAG=".$flag;
?>
使用函數:
count() 傳回陣列中元素的數目
floor() 取中間整數
strcmp 比較兩個字串(對大小寫敏感)

3-3題目:整數反轉(Integer Reversal)
整數反轉的函數是只給定一個整數的引數,該函數會反轉該數字的順序。例如,原傳入字串為「12345」,則其輸入結果為「54321」。

JS      inter_reversal.js

const rev = integer =>
	Math.sign(integer)*parseInt(
		integer
		.toString()
		.split("")
		.reverse()
		.join("")
		);
console.log(rev(98754321));
console.log(rev(-987001200));

PHP    integer_reversal.php

$my_int=-987001200;
//$my_int=987654321;
//print_r(str_split($my_int));
echo "原始的數字=".$my_int;
function integer_reversal ($int){
    $minus=0;
	$arr=str_split(strval($int));
		
	$len = count($arr)-1;
    //echo "<br>".$len;
	for($i=$len; $i>=0; $i--){
		if($arr[$i]=="-")
		 $minus=1;  //偵測是否為負數
		else
      	 $re_arr[]=$arr[$i];
    }
	return [$re_arr,$minus]; //返回兩個數值
}

$my_re=integer_reversal($my_int);
echo "<br>";
$num=intval(join($my_re[0]));
if ($my_re[1]==1)
$num=$num*(-1);	
echo "<br>反轉後數字=".$num;
使用函數:
strval() 函數用於取得變數的字串值