KMP 演算法

懒羊羊爱吃灰太狼發表於2024-09-04

#kmp演算法
在一個長的字串中匹配指定字串我們想到的是一個一個比較 如果發現一個不相等,就把長字串向前移動一位,然後再重頭開始繼續比較;

function kmp($str1,$str2){
    $l1 = strlen($str1);
    $l2 = strlen($str2);
    $i=0;
    $j=0;
    while($j<$l2 && $i<$l1){
        if($str1[$i] == $str2[$j]){
                $i++;
                $j++;
            }else{
                $i=$i-$j+1;//先回到i位置 然後起始位置向後移動一位
                $j=0; j回到初始位置;
            }

    }
    if($j == $l2){//如果查到此時$j的長度肯定是等於$l2 的
        return $i-$j; 
    }else{
        return -1;    
    }
}

這種演算法效率低下 最壞情況下比較 ($l2 -$l1+1)*$l2次
kpm 演算法

function getNext($str) {


     $next[0] = -1;

     $j = 0;

     $k = -1;
     $len = strlen($str);

     while ($j < $len - 1) {

        if ($k == -1 || $str[$j] == $str[$k]) {

            if ($str[++$j] == $str[++$k]) {

                $next[$j] = $next[$k];

            } else {

                $next[$j] = $k;

            }


        } else {

            $k = $next[$k];

        }

     }

     return $next;

 }
function kmp($str1,$str2) {

    $l1 = strlen($str1);
    $l2 = strlen($str2);
    $i=0;
    $j=0;

     $next = getNext($str2);

     while ($j < $l2 && $i < $l1) {

        if ($j == -1 || $str1[$i] == $str2[$j]) { // 當j為-1時,要移動的是i,當然j也要歸0

            $i++;

            $j++;

        } else {

            $j = $next[$j]; // j回到指定位置

        }

     }
    if ($j == $l2) {
        return $i-$j; 
    }else{
        return -1;       
    }
 }
  1. 二叉樹的遍歷
    因為好多演算法都是c語言實現的實現方式跟php可能有些區別 ,但是原理本質上都差不多,所以希望不要糾結,有思考不到位的地方希望大家指出來
    class node{
     public $left = null;
     public $right = null;
     public $data ;
     public $flag ;
     public function __construct($data, $left=null, $right=null,$flag=0){
         $this->data = $data;
         $this->left = $left;
         $this->right = $right;
         $this->flag = $flag; //在後序遍歷時候有用標誌位,是否訪問過的標誌 0 沒訪問過 1 訪問過
     }
    }
    $root_0 = new node(10);
    $node1_1 = new node(11);
    $node1_2 = new node(12);
    $node2_1 = new node(21);
    $node2_2 = new node(22);
    $node2_3 = new node(23);
    $node2_4 = new node(24);
    $node3_1 = new node(31);
    $node3_2 = new node(32);
    $node3_3 = new node(33);
    $node3_4 = new node(34);
    $root_0->left = $node1_1;
    $root_0->right = $node1_2;
    $node1_1->left = $node2_1;
    $node1_1->right = $node2_2;
    $node1_2->left = $node2_3;
    $node1_2->right = $node2_4;
    $node2_1->left = $node3_1;
    $node2_1->right = $node3_2;
    $node2_2->left = $node3_3;
    $node2_2->right = $node3_4;
    生成後的樹 如圖

kmp 演算法

1.1 二叉樹廣度優先遞迴遍歷

 function breadthTraversal($root){
    $queue = [];
    array_push($queue,$root);
    while($queue){
        $node = array_pop($queue);
        if($node->left){
            array_unshift($queue,$node->left);
        }
        if($node->right){
            array_unshift($queue,$node->right);
        }
        echo $node->data.'----';
    }
}
breadthTraversal($root_0);
結果 :10----11----12----21----22----23----24----31----32----33----34-  一層一層的遍歷相當

1.2 二叉樹深度優先遞迴遍歷(遞迴版本)

 function deepTraversal($root){
     //  echo $root->data.'  ';  前續遍歷
        if($root->left){
            dbs($root->left);
        }
    //    echo $root->data.'  ';  中序遍歷
        if($root->right){
            dbs($root->right);
        }
            //echo $root->data.'  ';  後序遍歷
}
deepTraversal($root_0);
前序遍歷:10 11 21 31 32 22 33 34 12 23 24  
中序遍歷:31 21 32 11 33 22 34 10 23 12 24 
後序遍歷:31 32 21 33 34 22 11 23 24 12 10 

1.3 二叉樹深度優先遞迴遍歷(非遞迴遞迴版本)
1.3.1 前序遍歷非遞迴

function preOrderTraversal($node){
       $queue = [];
       array_push($queue,$node);
       while($queue){
        $node = array_pop($queue);
        while($node){
            echo $node->data.'   ';
            array_push($queue,$node->right);
            $node = $node->left;
        }
    }
}
preOrderTraversal($root_0);
前續遍歷非遞迴結果:10 11 21 31 32 22 33 34 12 23 24 

1.3.2 中序遍歷非遞迴

function inOrderTraversal($root){
       $queue = [];
       array_push($queue,$root);
       while($queue){

        while($node = array_pop($queue)){
                array_push($queue,$node);
           // if($node->left){//$node->left 為null 時候  //沒有表示無子節點了 千萬不能加 if 判斷  除非加個完的標誌  我剛開始在這裡加判斷 結果死迴圈
                array_push($queue,$node->left); 
           // }
        }
        if($queue){
           $node = array_pop($queue);
            echo $node->data.'   ';
            //if($node->right){$node->right 為null 時候 沒有表示右節點遍歷完 當為root節點時候表示  全部遍歷完  千萬不能加 if 判斷  我剛開始在這裡加判斷 結果死迴圈  因為  這裡,$node->right 要是不存在表示這node節點遞迴完畢
              array_push($queue,$node->right);    
            //}      
        }    
    }        
}
inOrderTraversal($root_0);
中序遍歷非遞迴結果:31  21  32  11  33  22  34  10  23  12  24  

1.3.3 後序遍歷非遞迴

function postOrderTraversal($root){
       $queue = [];
       array_push($queue,$root);
       while($queue){

           while($node = array_pop($queue)){
            array_push($queue,$node);
            array_push($queue,$node->left);
        }
        if($queue){
            $node = array_pop($queue);  
            // 這步之前其實跟前面差不多,我剛開始也寫這裡然後怎麼想也搞不定後面的步奏,後來看了一篇部落格別人寫的
            if($node->flag==0){
               $node->flag=1;
               array_push($queue,$node);
               // $node->right=null 時候,push($queue,null)後就當$node= null==pop($queue)不在while中迴圈,總的來說就是表示node節點右節點遍歷完
               array_push($queue,$node->right);
            }else{
                echo $node->data.'  ';// 表示node節點後的所有子節點節點都遍歷完了
                array_push($queue,null); 
            }
        }
   }
}
postOrderTraversal($root_0);
後序遍歷非遞迴結果:31  32  21  33  34  22  11  23  24  12  10 

2 堆排


function heapSort(&$a){
    $count = count($a)-1;
    for($i=intval($count/2); $i >=1; $i--) {//重最後一個元素開始構建一個大頂堆
        moveHeap($a,$i,$count);
    }
    for ($j=$count; $j >=1 ; $j--) {
        swap($a,1,$j);//交換第一個元素和最後一個元素
        moveHeap($a,1,$j-1);//重新構建大頂堆
    }
    function moveHeap(&$a,$j,$n){\

  $left = 2*$j;
    $right = 2*$j+1;
    $max = $j;
    if($left<=$n && $a[$left]>$a[$max] ){
        $max = $left;
    }
    if($right<=$n && $a[$right]>$a[$max]){
        $max = $right;
    }
    if($max!=$j){
        swap($a,$max,$j);
        moveHeap($a,$max,$n);//遞迴版本
//        while($max!=$j){//非遞迴版本
//             $j = $max;
//            $left = 2*$j;
//            $right = 2*$j+1;
//            if($left<=$n && $a[$left]>$a[$j]){
//                $max = $left;
//            }
//            if($right<=$n && $a[$right]>$a[$right]){
//                $max = $right;
//            }
//            if($max!=$j){
//                swap($a,$max,$j);
//            }
//
//        }
  }
    function swap(&$a,$l,$r){//交換方法
    $tem = $a[$l];
    $a[$l] = $a[$r];
    $a[$r] = $tem;
}
$a = [0,12,2,31,22,6,23,11,9,32,4];
print_r($a);
heapSort($a);
print_r($a);
Array
(
    [0] => 0
    [1] => 2
    [2] => 4
    [3] => 6
    [4] => 9
    [5] => 11
    [6] => 12
    [7] => 22
    [8] => 23
    [9] => 31
    [10] => 32
)0個是佔位符

歸併排序

function mergeSort($a){
    $count = count($a);
    if ($count <= 1) {
           return $a;
    } else {
          $mid = intval(($count)/2);
          $left  = array_slice($a, 0,$mid);
          $right = array_slice($a,$mid);

          $s = mergeSort($left);
          $e = mergeSort($right);

        return array_m($s,$e);
    }
 }
 function array_m($s,$e)
 {
     $r = array();
     $i = 0;
     $j = 0;
     $count_s = count($s);
     $count_e = count($e);
     while ($i < $count_s && $j < $count_e) {
         if ($s[$i] > $e[$j]) {
             $r[] = $e[$j];
             $j++;

         } else {
             $r[] = $s[$i];
             $i++;
         }
     }

     while($j<$count_e){
         $r[] = $e[$j];
         $j++;
     }
     while($i<$count_s){
         $r[] = $s[$i];
         $i++;
     }
     return $r;
 }

快排

function quickSort($a){
        $count=count($a);
        if($count<=1)return $a;
        $l = [];
        $r = [];
        $p = [];
        $m = $a[0];
        for ($i=0; $i <$count ; $i++) {
            if($a[$i]<$m){
                $l[]=$a[$i];
            }elseif($a[$i]==$m){
                $p[]=$a[$i];
            }else{
                $r[]=$a[$i];
            }
        }

    $l = quickSort($l);
    $p = quickSort($p);
    $r = quickSort($r);

    return array_merge($l,$p,$r);
}

總結

  1. 非遞迴版本其實就是通過棧去模擬遞迴函式呼叫的棧

  2. 根節點最好看成是兩個沒有孩子的父節點便於去模擬函式的執行流程

  3. 這三個遍歷 我也還沒完全想明白 還需要時刻回想,模擬遞迴的思維去思考,我們可以先簡單用三個節點去走通流程。

    折半查詢演算法

單連結串列反轉

function reverse($x) {
    $head=$current=$x;
    if($x && !$x->next){
        return $head;
    }elseif(!$x){
        return false;
    }
    elseif($x && $x->next){
        while($next = $current->next){//
            $current->next = $next->next//把下下個節點指向當前節點的next指標
            $next->next = $head;//把節點插入到頭節點前面
            $head = $next;//把頭節點重新指向第一個節點
        }
        return $head;
    }

獲取二叉樹的深度

深度就是樹的層數,當我們層序遍歷樹的時候,遍歷完一層我們就加一,但是什麼時候才是遍歷完一層呢,我們需要一個佇列用來儲存遍歷當前節點的子節點,比如當我們遍歷完第n層的時候,佇列裡面的節點肯定全部是第n+1層節點的,此時子需要獲取佇列長度就知道第n+1層節點個數,這樣往前推到第0層(就是根節點)此時,下一層節點個數是1,以此往後推遍歷完當前層就能知道下一層節點個數。

function findLevel($node){
    if(!$node->left && !$node->right){
        return false;
    }
    $arr = [];
    array_push($arr,$node);//把根節點push進佇列

    $count = 0;當前層已經遍歷的數量
    $depth=0;//樹的深度初始值
    $nextcount=1;//第一層節點的數量初始化為1,根節點
    while(count($arr)){
        $node = array_pop($arr);
        $count++;
        if($node->left){
            array_push($arr,$node->left);
        }
        if($node->right){
            array_push($arr,$node->right);
        }
        if($count==$nextcount){//當前層遍歷完
            $nextcount=count($arr);//獲取下層節點數量
            $count=0;
            $depth++;//當前層遍歷完和深度加1
        }
    }
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章