zoukankan      html  css  js  c++  java
  • 一道单链表题引发的思考

    最近在刷leetCode,遇到两道原理相近的题,觉得十分有趣和典型,通过思考和借鉴其他coder写法发现了新的smell

    先从简单的那道题聊起,原题是这样描述了:

    给定两个字符串形式的非负整数 num1 num2 ,计算它们的和。

     注意:

    num1 num2 的长度都小于 5100.

    num1 num2 都只包含数字 0-9.

    num1 num2 都不包含任何前导零。

    你不能使用任何內建 BigInteger 库, 也不能直接将输入的字符串转换为整数形式。

    举个具体例子:

    输入  : "10",

              "10"

    输出  : ”20"

    拿着这个题,第一反应就是想利用php的灵活性搞点黑魔法的实现。因为php的弱类型,字符串和数字之间的转换不要太酸爽,于是秒写出了下面的解法1:

    function addStrings($num1, $num2) {
        $num3= $num1 + $num2;
        return (string)$num3;
    }

    这也太简单了吧?

    当然不可能这么容易,因为会遇到$num1$num2太大,出现诸如下面的输出报错:

    这就涉及到大int相加的进位问题,关于这个问题参考别人的实现如下:

    function bigDataAdd($a,$b) {
        $m = strlen($a);
        $n = strlen($b);
        $num = $m>$n?$m:$n;//取最长数进行循环相加和进位
        $result = '';//结果
        $flag = 0; //进位标志
        while($num--){
            $t1 = 0;//用来存储当前位加数
            $t2 = 0;//用来存储当前位被加数
            if($m>0){
                $t1 = $a[--$m];
            }
            if($n>0){
                $t2 = $b[--$n];
            }
            $t = $t1+$t2+$flag;//当前位加法运算考虑上一轮的进位标志
            $flag = intval($t/10);//本轮是否进位
            $result = ($t%10).$result;//向高位添加结果
        }
        //最高位加完发现还有进位标志,需要再向最高位+1
        if ($flag) {
            $result = $flag.$result;
        }
        return $result;
    }

    循环进位相加,最后完成大int相加。

    所以第一版的代码改为:

    function addStrings($num1, $num2) {
        $num3= bigDataAdd($num1, $num2);
        return (string)$num3;
    }

    这种思路就是:将两个非负数字符串当成整型相加,然后直接转字符串返回结果。唯一要特别处理的就是大int相加进位的问题。

     

    仔细想想,这道题从出题人的角度可能并不是只想利用php的特性,毕竟最开始能使用的实现语言并没有php,要考虑其他编程语法的实现(java,c++,c),可见这道题正统解法应是他法。

    另外一种解法就是要把字符串的每一个下标进行遍历相加,处理好进位问题。

    所以我们需要用一个进位符$carry去记录每次是否进位,然后也利用它来存储每次遍历中的临时结果。

    大概的思路:

    分别去倒序遍历$num1$num2,处理好进位,然后再返回最后结果(reverse).

    具体的实现是:

     

    unction addStrings($num1, $num2) {
        $carry = 0;
        $i = strlen($num1) - 1;
        $j = strlen($num2) - 1;
        $return_str = '';
        
        while($i >=0 || $j >= 0 || $carry != 0) {
            if ($i >= 0) {
                $carry += $num1[$i] + 0;
                $i--;
            }
            if ($j >= 0) {
                $carry += $num2[$j] + 0;
                $j--;
            }
            $return_str .= (string)($carry % 10);
            $carry = floor($carry / 10);
        }
        return strrev($return_str);
    }

     

    这就是解法二,性能和耗时都蛮不错的:打败了94%的php用户。

    如果再抽象一下我们的入参,其实字符串也是一种单链表的具体实现。所以也可以将字符串封装成ListNode(简单的单链表)。其他思路和上面是一样的,只是将遍历两个字符串变成遍历两个链表。

    具体如下:

    class NewSolution {
        public function addTowStrings($num1, $num2) {
            $list1 = null;
            $list2 = null;
            // 构建list1
            $length1 = strlen($num1);
            $length2 = strlen($num2);
    
            $list1 = $this->makeStringToListNode(strrev($num1));
            $list2 = $this->makeStringToListNode(strrev($num2));
            $q = $list1;
            $p = $list2;
            $carry = 0;
    
            $dummyNode = new ListNode(0);
            $currentNode = $dummyNode;
            $newNode = $currentNode;
            while($q != null || $p != null || $carry != 0) {
                //print_r("come here");
                // 遍历两个链表
                if ($q != null) {
                    $carry += $q->val;
                }
                if ($p != null) {
                    $carry += $p->val;
                }
                $sum = $carry % 10;
    
                $currentNode->next = new ListNode($sum);
    
                $currentNode = $currentNode->next;
    
                $carry = floor($carry / 10);
                if ($q != null) {
                    $q = $q->next;
    
                }
                if ($p != null) {
                    $p = $p->next;
                }
            }
    
            $realNewNode = $newNode->next;
    
            $result = "";
            while($realNewNode != null) {
                $result .= (string) $realNewNode->val;
                $realNewNode = $realNewNode->next;
            }
    
            return strrev($result);
    
        }
        public function makeStringToListNode($string) {
            $length = strlen($string);
            // 哑节点
            $dummyNode = new ListNode(0);
            $currentNode=  $dummyNode;
            $return_node = $currentNode;
            for ($i = 0; $i < $length; $i++) {
                $currentNode->next = new ListNode($string[$i]);
                $currentNode = $currentNode->next;
            }
    
            return $return_node->next;
        }
    }
    
    $s = new NewSolution();
    $str = $s->addTowStrings("13002", "11");

    这种写法比较学院派了,性能和内存使用都是非常差的。

    所以刷题既要保证能get到核心考点,又要兼顾性能才行。而php的确因为过于自由化导致我们可能会深陷到dark magic的marsh。请提高警惕,也不忘初心!

  • 相关阅读:
    [py]戏说python面向对象细节
    [py]彻底细究web框架的wsgi+逻辑处理模块
    [py]access日志入mysql-通过flask前端展示
    [sql]mysql管理手头手册,多对多sql逻辑
    [py]requests+json模块处理api数据,flask前台展示
    [py]flask从0到1-模板/增删改查
    [wx]雪落香杉树人物关系图
    [py]资源搜集
    [py]python之信用卡ATM
    【Unity技巧】开发技巧(技巧篇)
  • 原文地址:https://www.cnblogs.com/freephp/p/11171175.html
Copyright © 2011-2022 走看看