• 链表+数学:实现两个大数相加 ( Leetcode 02/67/371/445/989 )


     

     

     

     

     节点代码

     

     

     

     

     完整代码:

      <1> 链表实现代码

    class ListNode {
     int val; // 数据域
     ListNode next; // 指针域,指向下⼀个节点
     ListNode() {
     }
     ListNode(int x) {
     val = x;
     }
    }

      <2>存在 long 类型溢出的问题,需要⼿动测试

    /**
     * 暴⼒解法:
     * 遍历两个链表使⽤数学思维分别将他们转成整数
     * 对两个整数进⾏求和得到sum
     * 将sum按照数学思维再转成链表
     * ⼿动测试:
     * 若超过语⾔⽀持的数据类型范围,则报错
     * 解决办法:BigInteger
     *
     *
     * @param l1
     * @param l2
     * @return
     */
     public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
     //把链表转成数字,注意次序为逆序
     long l1Value = 0;
     int digit = 0;
     while (l1 != null) {
    //该位对应的 单位
     int pow = (int) Math.pow(10, digit);
     //在当前数值基础上增加新的⼀个⾼位
     l1Value += (long)l1.val * pow;
     digit++;
     //链表指向下⼀个节点
     l1 = l1.next;
     }
     long l2Value = 0;
     digit = 0;
     while (l2 != null) {
     //该位对应的 单位
     int pow = (int) Math.pow(10, digit);
     //在当前数值基础上增加新的⼀个⾼位
     l2Value += (long)l2.val * pow;
     digit++;
     //链表指向下⼀个节点
     l2 = l2.next;
     }
     //创建⼀个新链表,头部为空节点
     ListNode head = new ListNode();
     ListNode cur = head;
     //数字相加
     long sum = l1Value + l2Value;
     if (sum == 0) {
     head = new ListNode(0);
     return head;
     }
     //数字再转成链表
     while (sum > 0) {
     //每次取当前最低位
     int val = (int) (sum % 10);
     //移除最低位
     sum = sum / 10;
     //创建新节点
     ListNode node = new ListNode(val);
     //插⼊链表尾部
     cur.next = node;
     //链表尾部指针移动
     cur = cur.next;
     }
     return head.next;
     }

      <3>解决办法:⽤ java.math.BigInteger 替代 long 类型。注意全类名写法。

    /**
    * 暴⼒解法:存在的问题:使⽤long也会存在溢出
    * 解决办法:java.math.BigInteger
    * 该类在leetcode默认环境没有导⼊,所以使⽤全类名编写
    *
    * @param l1
    * @param l2
    * @return
    */
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
     //把链表转成数字,注意次序为逆序
     java.math.BigInteger l1Value = java.math.BigInteger.valueOf(0);
     int digit = 0;
     while (l1 != null) {
     java.math.BigInteger carry =
    java.math.BigInteger.valueOf(10).pow(digit);
     l1Value =
    l1Value.add(carry.multiply(java.math.BigInteger.valueOf(l1.val)));
     digit++;
     l1 = l1.next;
     }
     java.math.BigInteger l2Value = java.math.BigInteger.valueOf(0);
     digit = 0;
     while (l2 != null) {
     java.math.BigInteger carry =
    java.math.BigInteger.valueOf(10).pow(digit);
     l2Value =
    l2Value.add(carry.multiply(java.math.BigInteger.valueOf(l2.val)));
     digit++;
     l2 = l2.next;
     }
     ListNode head = new ListNode();
     ListNode cur = head;
     //数字相加,然后再转成链表
     java.math.BigInteger sum = l1Value.add(l2Value);
     if (sum.compareTo(java.math.BigInteger.valueOf(0)) == 0) {
     head = new ListNode(0);
     return head;
     }
     while (sum.compareTo(java.math.BigInteger.valueOf(0)) > 0) {
     int val = sum.mod(java.math.BigInteger.valueOf(10)).intValue();
     sum = sum.divide(java.math.BigInteger.valueOf(10));
     ListNode node = new ListNode((int) val);
     cur.next = node;
     cur = cur.next;
     }
     return head.next;
    }

     

     

     完整代码:

     /**
     * 最优解:数学思维解法
     * 1.遍历两个链表
     * 2.对应位置的节点数值相加
     * 3.将计算结果插⼊新链表尾部
     * ⼤于10,则进位,将进位加到下个节点
     *
     * 边界问题
     * 两个链表边界:next==null
     * 细节问题
     * 两个链表⻓度不⼀致,短链表⾼位视为0
     * 链表最⾼位发⽣进位,结果链表需要增加⼀个节点存放进位数字
     *
     * @param l1
     * @param l2
     * @return
     */
     public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
     ListNode p = l1, q = l2; // 原链表的两个遍历指针
     ListNode resultHead = new ListNode(-1); // 结果链表的头结点head
     ListNode curr = resultHead; // 结果链表的遍历指针,代表当前操作的节点
     int carry = 0; // 进位
     // 1.遍历两个链表
     while (p != null || q != null) { // 以⻓链表为准
     // 获取当前节点的值:链表较短,已⽆节点,取0
     int x = p != null ? p.val : 0;
     int y = q != null ? q.val : 0;
     // 2.对应位置的节点数值相加
     int sum = x + y + carry;
     carry = sum / 10; // 如何得到进位:和对10求整,得到此次计算的进位
     int num = sum % 10; // 存放到新链表节点中的数值
     // 3.将计算结果插⼊新链表尾部
     curr.next = new ListNode(num); // 创建新节点
     curr = curr.next;
     p = p == null ? p : p.next;
     q = q == null ? q : q.next;
     }
     if (carry > 0) { // 处理进位节点
     curr.next = new ListNode(carry);
     }
     return resultHead.next;
    }

    测试用例

    <1>辅助数据结构:链表。代码如下:

    class ListNode {
     int val; // 数据域
     ListNode next; // 指针域,指向下⼀个节点
     ListNode() {
     }
     ListNode(int x) {
     val = x;
     }
    }

    <2>测试⽤例:

    输⼊:(2 -> 4 -> 3) + (5 -> 6 -> 4)
    输出:7 -> 0 -> 8
    原因:342 + 465 = 807

    <3>测试代码:

    public static void main(String[] args) {
     Solution solution=new AddTwoNumbers2().new Solution();
     int[] arr1 = {2, 4, 3};
     int[] arr2 = {5, 6, 4};
     ListNode l1 = new AddTwoNumbers2().new ListNode();
     ListNode l2 = new AddTwoNumbers2().new ListNode();
     ListNode l1Cur = l1;
     ListNode l2Cur = l2;
     for (int i = 0; i < arr1.length; i++) {
     ListNode node1 = new AddTwoNumbers2().new ListNode(arr1[i]);
     ListNode node2 = new AddTwoNumbers2().new ListNode(arr2[i]);
     l1Cur.next = node1;
     l1Cur = node1;
     l2Cur.next = node2;
     l2Cur = node2;
    }
     ListNode result = solution.addTwoNumbers(l1.next, l2.next);
     while (result != null) {
     System.out.print(result.val + " "); // 输出:7 0 8
     result = result.next;
     }
     }

     

     

    练习题

    Leetcode 67 - 二进制求和

    给你两个二进制字符串,返回它们的和(用二进制表示)。
    输入为 非空 字符串且只包含数字 1 和 0。

    代码:

    class Solution {
        public String addBinary(String a, String b) {
            StringBuffer ans = new StringBuffer();
            // 二进制相加,末尾对齐,逐位相加,逢二进一
            int n = Math.max(a.length(), b.length()), carry = 0;//carry表示上一位置的进位,初始为0
            //从低位开始遍历相加,a、b中短的位置补0
            for (int i = 0; i < n; ++i) {
                carry += i < a.length() ? (a.charAt(a.length() - 1 - i) - '0') : 0;
                carry += i < b.length() ? (b.charAt(b.length() - 1 - i) - '0') : 0;
                ans.append((char) (carry % 2 + '0'));
                carry /= 2;
            }
            //进位有值,存入答案字符串中
            if (carry > 0) {
                ans.append('1');
            }
            //最终将答案串反转
            ans.reverse();
    
            return ans.toString();
        }
    }

    Leetcode 371 - 两整数之和

    不使用运算符 + 和 - ,计算两整数 a 、b 之和。

    示例 1:
      输入: a = 1, b = 2
      输出: 3

    示例 2:
      输入: a = -2, b = 3
      输出: 1

    说明:

      • 输入输出的取值范围为[-2^31,2^31-1]
      • 如果计算结果溢出,我们将你的计算结果以二进制补码表示,并只取低32位作为输出

    代码

    //无进位加法使用异或运算计算得出,进位结果使用与运算和移位运算计算得出;
    class Solution {
        public int getSum(int a, int b) {
            if (b!=0){
                int xor=a^b;
                int and = (a&b)<<1;
                return getSum(xor,and);
            }else{
                return a;
            }
        }
    }

    Leetcode 445 - 两数相加 II

    给你两个 非空 链表来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储一位数字。将这两数相加会返回一个新的链表。

    你可以假设除了数字 0 之外,这两个数字都不会以零开头。

    示例:
      输入:(7 -> 2 -> 4 -> 3) + (5 -> 6 -> 4)
      输出:7 -> 8 -> 0 -> 7

    代码:

    class Solution {
        public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
            Deque<Integer> stack1 = new LinkedList<Integer>();
            Deque<Integer> stack2 = new LinkedList<Integer>();
            // 把L1、L2中的数字依次压入栈中
            while (l1 != null) {
                stack1.push(l1.val);
                l1 = l1.next;
            }
            while (l2 != null) {
                stack2.push(l2.val);
                l2 = l2.next;
            }
            int carry = 0;
            ListNode ans = null;
            //再依次取出来相加,存到节点中
            while (!stack1.isEmpty() || !stack2.isEmpty() || carry != 0) {
                int a = stack1.isEmpty() ? 0 : stack1.pop();
                int b = stack2.isEmpty() ? 0 : stack2.pop();
                int cur = a + b + carry;
                carry = cur / 10;
                cur %= 10;
                ListNode curnode = new ListNode(cur);
                curnode.next = ans;
                ans = curnode;
            }
            return ans;
        }
    }

    Leetcode 989 -  数组形式的整数加法

    对于非负整数 X 而言,X 的数组形式是每位数字按从左到右的顺序形成的数组。例如,如果 X = 1231,那么其数组形式为 [1,2,3,1]。

    给定非负整数 X 的数组形式 A,返回整数 X+K 的数组形式。

    示例 1:
      输入:A = [1,2,0,0], K = 34
      输出:[1,2,3,4]
      解释:1200 + 34 = 1234

    示例 2:
      输入:A = [2,7,4], K = 181
      输出:[4,5,5]
      解释:274 + 181 = 455

    示例 3:
      输入:A = [2,1,5], K = 806
      输出:[1,0,2,1]
      解释:215 + 806 = 1021

    示例 4:
      输入:A = [9,9,9,9,9,9,9,9,9,9], K = 1
      输出:[1,0,0,0,0,0,0,0,0,0,0]
      解释:9999999999 + 1 = 10000000000

    提示:

      1 <= A.length <= 10000
      0 <= A[i] <= 9
      0 <= K <= 10000
      如果 A.length > 1,那么 A[0] != 0

    代码

    //将整个加数加入数组表示的数的最低位,通过求余数计算留下来的数值,求除数计算进位数值;
    class Solution {
        public List addToArrayForm(int[] A, int K) {
            int N = A.length;
            int cur = K;
            List ans = new ArrayList();
    
            int i = N;
            while (--i >= 0 || cur > 0) {
                if (i >= 0)
                    cur += A[i];
                ans.add(cur % 10);
                cur /= 10;
            }
            Collections.reverse(ans);
            return ans;
        }
    }
  • 相关阅读:
    高并发系统中的常见问题
    区块链需要解决诸多问题
    什么是“区块链”技术
    github源码开源区块链浏览器
    JavaScript 内存
    行为驱动开发(BDD)
    Vue.js
    Net程序员学习Linux
    Mybatis数据操作
    Metatable和Metamethod(转)
  • 原文地址:https://www.cnblogs.com/JasperZhao/p/15038437.html
Copyright © 2020-2023  润新知