算法笔记——树,二叉树的遍历,完全二叉树,搜索二叉树,满二叉树,平衡二叉树,最低公共祖先,后继节点,序列化和反序列化

一、二叉树的遍历

非递归遍历

1.前序遍历(头左右)

非递归利用栈(先进后出)去实现。
先把头节点加入栈中,接下来的过程周而复始
1)从栈中弹出一个节点cur
2)处理cur节点(遍历的时候这个地方就打印,如果有别的需求,在这里更换处理方式即可)
3)先把cur的右孩子压入栈中(在有右孩子的前提下),再把cur的左孩子压入栈中(在有左孩子的情况下)。然后再到1)。

public static void preOrder(Node head){
        if (head!=null){
            Stack<Node> stack = new Stack<>();
            stack.add(head);
            while (!stack.isEmpty()){
                Node cur = stack.pop();
                //每次弹出一个输出一下)
                System.out.println(cur.value+" ");
                //先右后左
                if (cur.right != null){
                    stack.push(cur.right);
                }
                if (cur.left != null){
                    stack.push(cur.left);
                }
            }
        }
        System.out.println();
    }

注意一下stack的add和push方法:
stack本身无add方法,但它的继承类vector有这个方法,vector的父类list也有这个方法。
区别:
add的返回值是布尔类型
push的返回值是插入元素的类型
add是往栈底插入元素
push是从栈顶插入元素

2.后序遍历(左右头)

  • 这里发现后序和前序是反着的,先序遍历是头左右,先压的右栈,再压的左栈,所以他出栈就是头左右。
  • 后序是左右头,所以举一反三,如果先压左栈再压右栈,出来顺序就是头右左,这个顺序利用栈再倒一下,就是左右头,也就是后序了。

后序遍历的非递归实现:
先把头节点放入栈里,接下来的过程周而复始
1)弹出一个节点cur
2)把弹出的节点放入一个收集栈(这个时候不要输出或者做处理,先放另一个收集栈里去)
3)先把cur的左孩子压入栈中(在有左孩子的前提下),再把cur的右孩子压入栈中(在有右孩子的前提下)。然后再到1)。

public static void afterOrder(Node head){
        if (head != null){
            Stack<Node> stack = new Stack<>();
            Stack<Node> stackNew = new Stack<>();
            stack.add(head);
            while (!stack.isEmpty()){
                Node cur = stack.pop();
                stackNew.push(cur);
                if (cur.left != null){
                    stack.push(cur.left);
                }
                if (cur.right != null){
                    stack.push(cur.right);
                }
            }
            while (!stackNew.isEmpty()){
                System.out.println(stack.pop() + " ");
            }
        }
        System.out.println();
    }

3.中序遍历(左头右)

中序遍历的顺序是左头右,所以利用栈的实现方法如下:
1)从头节点开始,对每一棵子树,这棵子树的整个左边界依次进栈
2)然后依次弹出的过程中,打印
3)对弹出的节点的右树(如果有的话)周而复始,来到1)。就是把右树的所有左边界进栈。

public static void midOrder(Node head){
        if (head!= null){
            Stack<Node> stack = new Stack<>();
            while (!stack.isEmpty() || head != null){ //这里的head != null判断的是右树是不是为空
                if (head != null){//依次整个左边界进栈
                    stack.push(head);
                    head = head.left;
                }else {//当左边界没有了的时候,弹出,右树的整个左边界开始进栈
                    head = stack.pop();
                    System.out.println(head.value);
                    head = head.right; //head来到了右树,接上面的if语句
                }
            }
        }
        System.out.println();
    }

递归遍历

在这里插入图片描述

根据二叉树的递归序,我们发现先序在第一次出现的位置,中序在第二次出现的位置,后序在第三次出现的位置。

1.前序遍历

前序在两次递归之前,也就是第一次就打印

public static void preOrder2(Node head){
        if (head == null){
            return;//递归终止条件
        }
        System.out.println(head.value);
        preOrder2(head.left);
        preOrder2(head.right);
    }

2.中序遍历

中序在两次递归之间,也就是第二次就打印

public static void midOrder2(Node head){
        if (head == null){
            return;
        }
        midOrder2(head.left);
        System.out.println(head.value);
        midOrder2(head.right);
    }

3.后序遍历

后序在两次递归之后,也就是第三次就打印

public static void afterOrder2(Node head){
        if (head == null){
            return;
        }
        afterOrder2(head.left);
        afterOrder2(head.right);
        System.out.println(head.value);
    }

二叉树的深度优先遍历DFS

二叉树的深度优先遍历就是二叉树的前序遍历

二叉树的宽度优先遍历BFS

宽度优先遍历我们采用队列(先进先出)的方式。
1)先把头节点放入队列
2)每一次弹出就处理(打印)
3)对弹出的节点,先放入左孩子再放入右孩子(在有的前提下)
周而复始

public static void BFS(Node head){
        if (head == null){
            return;
        }
        Queue<Node> queue = new LinkedList<>();
        queue.add(head);
        while ( ! queue.isEmpty()){
            head = queue.poll();
            System.out.println(head.value);
            if (head.left != null){
                queue.add(head.left);
            }
            if (head.right != null){
                queue.add(head.right);
            }
        }

    }

扩展——求一个二叉树的宽度

这里再宽度遍历的基础上顺便求出这个树的宽度。
借助一个hashmap表。
第一次先将头节点加入这个map表,并加入队列
1)弹出一个节点,并通过map得到它的层数
a)然后开始判断,如果得到的这个层数和上一个结点的层数相同,说明还在同一层,那就节点数++,层数不变
b)如果得到的这个节点的层数和上一个结点的层数不相同,(先把上一层解决掉,就是更新出上面几层的最大节点数)说明已经跨层了,那就节点数赋值为1,层数++
2)对弹出的节点的左右子节点进队列,并在hashmap记录层数
周而复始

public static int width(Node head){
        if (head == null){
            return 0;
        }
        Queue<Node> queue = new LinkedList<>();
        HashMap<Node, Integer> hashMap = new HashMap<>();
        hashMap.put(head,1); //先把头节点放到hashmap
        queue.add(head); //头节点入队
        int curLevel = 1; //最初的层数,既然head不为空,则最少也有一层,从第一层开始
        int curLevelNodes = 0; //最初的节点数,这里为0,因为后面如果层数相同要++,所以别设置为1
        int max = Integer.MIN_VALUE; //某一层的最大节点数,也就是最大宽度,后面要更新
        while ( ! queue.isEmpty()){ //队列不为空一直进行,按宽度执行完
            head = queue.poll();  //弹出一个 弹出的这个节点接下来要做处理了,如果这道题是宽度遍历,下面就该输出了
            //开始处理,这里针对弹出的节点,以及节点在map中的值进行判断
            int curNodeLevel = hashMap.get(head); //当前节点的层数  通过hashmap获得,第一次获得的是头节点1
            if (curNodeLevel == curLevel){ //如果在同一层
                curLevelNodes++; //层数不变 节点数++
            }else { //如果发现这个节点不在同一层,这个时候就已经跨层了,那么先把上面所有层的最大值更新出来,一边给这一层用来比较,更新max
                max = Math.max(max,curLevelNodes); //最大值就是上一次的最大值和这一层的节点数
                curLevel++; //跨层了,层数要++
                curLevelNodes = 1; //这是为1,因为来到了新的一层的第一个节点
            }
            //下面是针对队列,层序遍历,要把所有的节点放入队列,先进先出一个一个用来在上面进行处理
            if (head.left != null){
                hashMap.put(head.left,curNodeLevel+1);
                queue.add(head.left);
            }
            if (head.right != null){
                hashMap.put(head.right,curNodeLevel+1);
                queue.add(head.right);
            }
        }
        return max;
    }

二、二叉树的相关概念及其实现判断

递归套路(解决很多树型DP的题)
1.假设以X节点为头,假设可以向X左树和X右树要到任何信息;
2.在上一步的假设下,讨论以X为头节点的树,得到答案的可能性(最重要)
3.在列出所有的可能性后,确定到底需要向左树和右树要什么样的信息
4.把左树信息和右树信息求全集,就是任何一颗子树都需要返回的信息S
5.递归函数都返回S,每一棵子树都这么要求
6.写代码,在代码中考虑如何把左树的信息和右树的信息整合出整棵树的信息。
信息传递,将信息变成一个结构体。

1.判断一棵树是否为搜索二叉树BST

搜索二叉树BST ====左孩子比父小,右孩子比父大

普通做法

1、利用中序遍历(递归),出来的顺序是左头右,如果遍历的结果是升序的,则是一棵搜索二叉树。

    public static boolean isBST(Node head){
        if (head == null){
            return true;
        }
        int preValue = Integer.MIN_VALUE;
        boolean isLeft = isBST(head.left);//左树上检查是不是搜索二叉树
        if (! isLeft){
            //如果左树不是搜索二叉树,那这棵树肯定不是搜索二叉树,返回false
            return false;
        }
        //看上一次处理到的节点,当前节点是否比我上次处理到的节点大
        if (head.value <= preValue){
            return false;
        }else {
            preValue = head.value;
        }
        //最后判断右树,右树是BST,整棵树都是BST
        return isBST(head.right);
    }

2、利用中序遍历的非递归来判断

public static boolean isBST2(Node head){
        if (head == null){
            return true;
        }
        int preValue = Integer.MIN_VALUE;
        Stack<Node> stack = new Stack<>();
        while (!stack.isEmpty()){
            if (head != null){
                stack.push(head);
                head = head.left;
            }else {
                head = stack.pop();
                if (head.value <= preValue){
                    return false;
                }else {
                    preValue = head.value;
                }
                head = head.right;
            }
        }
        return true;
    }

还有一种做法,就是准备一个集合,把每次遍历的值放到里面,最后再看是否是正序的,再不说了。、

	public static boolean isBST(Node head) {
		if (head == null) {
			return true;
		}
		LinkedList<Node> inOrderList = new LinkedList<>();
		process(head, inOrderList);
		int pre = Integer.MIN_VALUE;
		for (Node cur : inOrderList) {
			if (pre >= cur.value) {
				return false;
			}
			pre = cur.value;
		}
		return true;
	}

	public static void process(Node node, LinkedList<Node> inOrderList) {
		if (node == null) {
			return;
		}
		process(node.left, inOrderList);
		inOrderList.add(node);
		process(node.right, inOrderList);
	}

套路做法

  • 条件:

左是搜索二叉树
右是搜索二叉树
左的最大值小于头(我),右的最小值大于头(我)

  • 需要 === 一个返回这三个条件的类

左是否是搜索二叉树,以及你的最大值和最小值
右是否是搜索二叉树,以及你的最小值和最大值
(我们左树只需要最大值,右树只需要最小值,因为递归的条件是相等的,所以不能只拿一个最大值,一个最小值,要都拿上)

//用来返回递归信息的类
public static class ReturnData{
        public boolean isBST;
        public int max;
        public int min;

        public ReturnData(boolean isBST, int max, int min) {
            this.isBST = isBST;
            this.max = max;
            this.min = min;
        }
    }

	//递归 要返回三个东西 是否是搜索树,最大值,最小值
    public static ReturnData process(Node x){
        if (x == null){
            return null;
        }
		//左树上的信息,右树上的信息, 最后要返回整颗子树的信息
         ReturnData leftData = process(x.left);
         ReturnData rightData = process(x.right);
         
		 //左右树上要返回的最大值和最小值
         int max = x.value;
         int min = x.value;
         if (leftData != null){
             max = Math.max(leftData.max, max);
             min = Math.min(leftData.min, min);
         }
         if (rightData != null){
             max = Math.max(rightData.max, max);
             min = Math.min(rightData.min, min);
         }
         //通过左右树来返回这颗子树是不是搜索二叉树
         boolean isBST = true; //假设为true 找条件反驳它,反驳不过,则为true
         //先判断左边,如果左不为空并且(左不是二叉搜索树或者左边的最大值比这棵子树的头节点还大)返回false
         if (leftData != null && (!leftData.isBST || leftData.max >= x.value)) {
            isBST =  false;
         }
         //再判断右边,如果右不为空并且(右不是二叉搜索树或者右边的最小值比这棵子树的头节点还小)返回false
         if (rightData != null && (!rightData.isBST || x.value >= rightData.min)){
            isBST = false;
         }
         //返回这棵子树的信息-->是否为二叉搜索树,以及左树的最大值,和右树的最小值
         return new ReturnData(isBST,min,max);
    }
    //调用递归判断是不是二叉搜索树,只需要返回是不是二叉搜索树
    public static boolean isBST3(Node head){
        return process(head).isBST;
    }

2.判断一颗二叉树是完全二叉树CBT

完全二叉树CBT ====要么每层满 要么每层从左往右变满。

思路:
按宽度遍历(利用到队列)
1)依次遇到的节点如果有右孩子,没左孩子,直接返回false
2)在1条件不违规的情况下,如果遇到了第一个左右两孩子不双全的情况,那么接下来所有的结点必须为叶节点

    public boolean isCBT(Node head){
        if (head == null){
            return true;
        }
        //按宽度遍历,所以准备一个队列
        LinkedList<Node> queue = new LinkedList<>();
        boolean left = false;//是否有遇到过左右两个孩子不双全的情况,第一次先置为false
        Node l = null;
        Node r = null;
        queue.add(head);
        while (!queue.isEmpty()){
            head = queue.poll();
            l = head.left;//左孩子
            r = head.right;//右孩子
            //下面的if语句(left && !(l == null && r == null))表示上一次遇到了左右不双全的情况,但却发现当前节点不是叶节点,那就直接false
            //(l == null && r != null)表示遇到了一个左孩子为空,而右孩子不为空的节点,直接返回false
            if ((left && !(l == null && r == null)) || (l == null && r != null)){
                return false;
            }
            if (l != null){
                queue.add(l);
            }
            if (r != null){
                queue.add(r);
            }
            //判断当前节点是否是双全节点
            if (l == null || r == null){
                return true;
            }
        }
        return true;
    }

3.判断一颗二叉树是否是满二叉树FBT

满二叉树===节点数 = 2^最大深度 - 1

这道题用递归套路去做
我们需要的东西:左树上的高度和节点数,右树上的高度和节点数。
达到满二叉树的条件是节点数 = 2^最大深度 - 1

public static class Info{
        public int Nodes;
        public  int heights;

        public Info(int nodes, int heights) {
            this.Nodes = nodes;
            this.heights = heights;
        }
    }

    public static Info process(Node x){
        if (x == null){
            return new Info(0,0);
        }

        Info leftData = process(x.left);
        Info rightData = process(x.right);

        //接下来你要自己加工自己的信息,然后返回你的两个东西
        int height = Math.max(leftData.heights, rightData.heights)+1;
        int nodes = leftData.Nodes + rightData.Nodes +1;

        return new Info(nodes,height);

    }

    public static boolean isFullTree_(Node head){
        if (head == null){
            return true;
        }
        //收两棵树的信息
        Info data = process(head);
        return data.Nodes == (1 << data.heights) - 1;
    }

4.判断一棵二叉树是否是平衡二叉树

递归套路:
左树是平衡
右树是平衡
左右高度差不超过一
都成立就是平衡二叉树

需要:
左树是否是平的,高度是多少
右树是否是平的,高度是多少

 public static boolean isBalanced_(Node head){
 		if(head == nul){
 			return true;
 		}
        return process(head).isBalanced;
    }
    public static class ReturnType{
        public boolean isBalanced;  //是否平衡
        public int height;  //高度
        public ReturnType(boolean isBalanced, int height) {
            this.isBalanced = isBalanced;
            this.height = height;
        }
    }
    //递归函数要返回两个值
    public static ReturnType process(Node x){
        if (x == null){ //base case
            return new ReturnType(true,0);
        }
        //左树和右树返回信息
        ReturnType leftData = process(x.left);
        ReturnType rightData = process(x.right);
        //自己也得返回信息
        //x为头的树的高度
        int height = Math.max(leftData.height, rightData.height)+1;
        boolean isBalanced = leftData.isBalanced && rightData.isBalanced && Math.abs(leftData.height - rightData.height) < 2;
        return new ReturnType(isBalanced,height);
    }

三、给定两个二叉树的节点node1和node2,找到他们的最低公共祖先节点

  • 法一,利用哈希表和哈希set

先将所有父节点存到哈希表里,然后遍历其中一个节点的父节点,完成后再遍历另一个节点的父节点,直到第一次遇到相同的父节点,返回

    public static Node low(Node head, Node o1,Node o2){
        if (head == null){
            return null;
        }
        HashMap<Node, Node> parentMap = new HashMap<>();
        parentMap.put(head,head);
        fillParentMap(head,parentMap);
        HashSet<Node> set1 = new HashSet<>();
        set1.add(head);
        Node cur = o1;
        while (cur != parentMap.get(cur)){ //这个截至条件就是它跑到了头节点的时候,它和父节点重合了
            set1.add(cur);
            cur = parentMap.get(cur);
            //上面依次把从o1开始所有的父节点加入set中
        }
        cur = o2;
        while ( !set1.contains(cur)){
            cur = parentMap.get(cur);
        }
        return cur;
    }
    //把二叉树的每个节点对应的父节点的信息放到哈希表中
    public static void fillParentMap(Node head, HashMap<Node ,Node> parentMap){
        if (head.left != null){
            parentMap.put(head.left,head);
            fillParentMap(head.left,parentMap);
        }
       if (head.right != null){
           parentMap.put(head.right,head);
           fillParentMap(head.right,parentMap);
       }
//       if (head == null){
//           return;
//       }
//       if (head.left != null){
//           parentMap.put(head.left,head);
//       }
//       if (head.right != null){
//           parentMap.put(head.right,head);
//       }
//       fillParentMap(head.left,parentMap);
//       fillParentMap(head.right,parentMap);
    }

在这里插入图片描述

- 法二 超级优化代码

情况1: o1是o2的最低公共祖先,或o2是01 的
情况2: o1和o2彼此不互为最低公共祖先
如果一个子树上既没有o1又没有o2,则它一定返回空。

public static Node lowestAncestor(Node head, Node o1, Node o2) {
        //如果碰到了null则直接返回自己, 如果碰到了o1,返回o1,如果碰到了o2,返回o2
        if (head == null || head == o1 || head == o2) {
            return head;
        }
        //上面都跳过的话 ,去左边拿答案
        Node left = lowestAncestor(head.left, o1, o2);
        //再去拿右边的答案
        Node right = lowestAncestor(head.right, o1, o2);
        //如果左边不为空且右边不为空,返回它自己本身
        if (left != null && right != null) {
            return head;
        }
        //如果左边不等于空返回左边,如果左边==null,返回右边的。
        //特殊情况,如果左右都等于空,上面的都不符合,那返回的就是空的
        return left != null ? left : right;
    }

对比下面的图来理解一下
图1是情况1,图2是情况2
先看图1:
首先来到A,不满足 (head == null || head == o1 || head == o2) 这个语句,则继续往下,开始递归。先左边,a先需要得到b的返回值,b先需要得到d的返回值,d本身不为空,跳过第一个if语句,b的左右孩子都是null,这个时候再看代码,第二个if不符合,再来到return语句,return也不符合,所以它就直接返回空了,所以d的返回值是null,因为它的左孩子返回null,右孩子也是,所以它是null。此时b得到了它的左孩子d的返回值为null,再看b需要的右孩子的返回值,来到o1,中了条件,直接返回o1。这时b需要给a给返回值,b的左孩子为空,右孩子不为null,所以根据return left != null ? left : right,返回它的右孩子,也就是o1。这时,a拿到了左孩子b的返回值o1,再去要右孩子的返回值,同理c的返回值是null,这是直接返回给a,这是最终的返回当然就是o1,而o1恰恰就是情况1的答案。
再看图2:
此时o1 和 o2在不同的子树上,b开始递归的时候,自己观察,b的左边返回的是o1,b的右边返回的是o2,所以b中了第二个if语句
if (left != null && right != null) {
return head;
}
所以直接返回b,而b恰恰也就是答案。

在这里插入图片描述

四、在二叉树法找到一个节点的后继节点

现在有一种新的二叉树节点类型如下

public class Node { 
	public int value;
	public Node left; 
	public Node right; 
	public Node parent; 
	public Node(int val) { 
		value = val; 
		} 
  }

该结构比普通二叉树节点结构多了一个指向父节点的parent指针。 假设有一棵Node类型的节点组成的二叉树,树中每个节点的parent指针都正确地指向自己的父节点,头节 点的parent指向null。 只给一个在二叉树中的某个节点node,请实现返回node的后继节点的函数。 在二叉树的中序遍历的序列中, node的下一个节点叫作node的后继节点。

在这里插入图片描述

  • 题目做法及分析

如果node节点和node节点的后继节点之间的实际距离为L,只用走过L个节点,时间复杂度为O(L),额外空间复杂度O(1),

情况1:如果node有右子树,那么后继节点就是右子树上最左边的节点(想一想中序遍历)。
情况2:如果node上没有右子树,那么先看node是不是node父节点的左孩子节点,如果是左孩子节点,那么此时node的父节点就是node的后继节点;如果这个node是node父节点的右孩子节点,就向上寻找node的后继节点,假设向上移动的节点记为s,s的父节点记为p,如果发现s是p的左孩子节点,那么节点p就是node节点的后继节点,否则就一直向上移动。
情况3:那就是在情况2的基础上,一直向上寻找,没有找到node的后继节点,说明node不存在后继节点,就像下图情况2没有节点1和3就是成情况3了。

  • 看着些字感觉已经懵逼了,来个例子
    在这里插入图片描述在这里插入图片描述
  • 上代码
public class Node {
        public int value;
        public Node left;
        public Node right;
        public Node parent;
        public Node(int val) {
            value = val;
        }
    }
    //获取最左节点的方法
    public static Node getLeftMost(Node node){
        if (node == null){
            return node;
        }
        while (node.left != null){
            node = node.left;
        }
        return node;
    }
    public static Node getSuccessorNode(Node node){
        if (node == null){
            return node;
        }
        //情况1,如果node有右子树  后继就是右边的最左节点
        if (node.right != null){
            return getLeftMost(node.right);
        }else {
            Node parent = node.parent;
            //如果父节点不为空(这里暗指这个node无后继节点,也就是最后一个节点),
            // 或者这个节点是父节点的左孩子,就退出循环
            //否则一直网上找
            while (parent != null && parent.left != node){
                node = parent;
                parent = node.parent;
            }
            return parent;
        }
    }

五、二叉树的序列化和反序列化

二叉树被记录成文件的过程就叫做二叉树的序列化,通过文件内容重建原来二叉树的过程叫二叉树的反序列化。

  • 法一:通过先序遍历实现序列化和反序列化

序列化
“#”表示这个节点为空
“!”表示一个节点的结束

#序列化
public static String serial(Node head){
        String str = "";
        if (head == null){
            return "#!";
        }
        str = head.value + "!";
        str += serial(head.left);
        str += serial(head.right);
        return str;
    }

反序列化,重做先序遍历
把结果字符str变成字符串数组,准备一个队列,把数组里的值加进去,然后依次弹出的过程中建树

public static Node recon(String preStr){
        String [] values = preStr.split("!");
        Queue<String> queue = new LinkedList<>();
        for (int i = 0; i != values.length; i++){
            queue.offer(values[i]);
        }
        return reconPreOrder(queue);
    }
    public static Node reconPreOrder(Queue<String> queue){
        String value = queue.poll();
        if (value.equals('#')){
            return null;
        }
        Node head = new Node(Integer.valueOf(value));
        head.left = reconPreOrder(queue);
        head.right = reconPreOrder(queue);
        return head;
    }
  • 法二:层序遍历做

先序列化,按层序的套路,准备一个队列。
反序列化,重做层序遍历,遇到“#”就生成null节点,同时不把null节点放入队列。

//序列化
    public static String serial1(Node head){
        if (head == null){
            return "#!";
        }
        String res = head.value + "!";
        Queue<Node> queue = new LinkedList<>();
        queue.offer(head);
        while (!queue.isEmpty()){
            head = queue.poll();
            if (head.left != null){
                res += head.left.value+"!";
                queue.add(head.left);
            }else{
                res += "#!";
            }
            if (head.right != null){
                res += head.right.value+"!";
                queue.add(head.right);
            }else {
                res += "#!";
            }
        }
        return res;
    }
//反序列化
public Node recon1(String levelStr){
        String[] values = levelStr.split("!");
        int index = 0;
        Node head = generateNodeByString(values[index++]);
        Queue<Node> queue = new LinkedList<>();
        if (head != null){
            queue.add(head);
        }
        Node node = null;
        while (!queue.isEmpty()){
            node = queue.poll();
            node.left = generateNodeByString(values[index++]);
            node.right = generateNodeByString(values[index++]);
            if (node.left != null){
                queue.add(node.left);
            }
            if (node.right != null){
                queue.add(node.right);
            }
        }
        return head;
    }
    public static Node generateNodeByString(String val){
        if (val.equals("#")){
            return null;
        }
        return new Node(Integer.valueOf(val));
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值