飙血推荐
  • HTML教程
  • MySQL教程
  • JavaScript基础教程
  • php入门教程
  • JavaScript正则表达式运用
  • Excel函数教程
  • UEditor使用文档
  • AngularJS教程
  • ThinkPHP5.0教程

算法基提升础学习2

时间:2021-12-08  作者:Gao-yubo  
二叉树套路模板,节点间最大距离,Morris遍历,线索二叉树判断,位运算技巧

一、树形Dp

叉树节点间的最大距离问题

从二叉树的节点a出发,可以向上或者向下走,但沿途的节点只能经过一次,到达节点b时路 径上的节点个数叫作a到b的距离,那么二叉树任何两个节点之间都有距离,求整棵树上的最 大距离。

/**
 * @Author: 郜宇博
 */
public class TreeDp {
    public static void main(String[] args) {
        Node head2 = new Node(1);
        域名 = new Node(2);
        域名t = new Node(3);
        域名 = new Node(4);
        域名t = new Node(5);
        域名.left = new Node(6);
        域名域名t = new Node(7);
        域名.域名 = new Node(8);
        域名域名t = new Node(9);
        域名tln(maxDistance(head2));
    }

    public static class Node{
        public Node left;
        public Node right;
        public int value;

        public Node(int value) {
            域名e = value;
        }
    }
    public static class Result{
        public int childMaxDistance;
        public int childHeight;

        public Result(int childMaxDistance, int childHeight) {
            域名dMaxDistance = childMaxDistance;
            域名dHeight = childHeight;
        }
    }
    /**
     * 获取节点间最大距离
     * 三种情况:
     * 一、最大距离不带本节点
     *  1.最大距离是从左数获取的
     *  2,是从右树获取的
     * 二、最大距离带本节点
     *  3.左子树Height+右子数Hight+1
     *
     *  此时需要三个参数:子树的最大距离,左子树的高,右子树的高
     *  因此需要额外定制一个返回类型包含该数的最大距离,该数的高
     *  从子树分别获取这两个返回值
     */
    public static int maxDistance(Node head){
        return process(head).childMaxDistance;
    }
    public static Result process(Node head){
        //空树
        if (head == null){
            return new Result(0,0);
        }
        //1.最大距离是从左数获取的
        Result p1 = process(域名);
        //2.最大距离是从右数获取的
        Result p2 = process(域名t);
        //3.最大距离经过自己
        int maxDistanceP3 = 域名dHeight + 域名dHeight + 1;
        //从子树获取完信息,自己也要提供信息
        //高度
        int height = 域名(域名dHeight,域名dHeight) + 1;
        //最大距离
        //就是这三个可能性中最大的
        int maxDistance = 域名( maxDistanceP3,域名(域名dMaxDistance,域名dMaxDistance)  );
        return new Result(maxDistance,height);

    }

}

最大开心值

/**
 * @Author: 郜宇博
 */
public class MaxHappy {
    public static void main(String[] args) {


    }
    public static class Emplyee{
        public int happy;
        public List<Emplyee> nexts;

        public Emplyee(int happy) {
            域名y = happy;
        }
    }

    /**
     * happy值最大:
     * 一、当本节点参加
     *      Happy = 本节点happy + 下级员工们不参加的MaxHappy
     * 二、当本节点不参加
     *      happy = 下级员工们参加的MaxHappy 和 不参加的MaxHappy 的较大值
     *
     * 因此需要构造返回类型有两个参数: 参加的maxHappy和不参加的maxHappy
     */
    public static class Result{
        public int comeMaxHappy;
        public int unComeMaxHappy;

        public Result(int comeMaxHappy, int unComeMaxHappy) {
            域名MaxHappy = comeMaxHappy;
            域名meMaxHappy = unComeMaxHappy;
        }
    }
    public static int maxHappy(Emplyee head){
        return 域名( process(head).comeMaxHappy, process(head).unComeMaxHappy);
    }
    public static Result process(Emplyee emplyee){
        //base case
        if (emplyee == null){
            return new Result(0,0);
        }
        //基层员工
        if (域名s == null){
            return new Result(域名y,0);
        }
        int UncomeHappy = 0;
        int comeHappy = 0;
        //获取各个员工的result
        for (Emplyee e: 域名s){
            Result result = process(e);
            //不参加
            //happy = 下级员工们参加的MaxHappy 和 不参加的MaxHappy 的较大值
            UncomeHappy += 域名( 域名MaxHappy,域名meMaxHappy);
            //参加
            //Happy = 本节点happy + 下级员工们不参加的MaxHappy
            comeHappy += 域名y + 域名meMaxHappy;
        }
        return new Result(comeHappy,UncomeHappy);
    }
}

二、Morris遍历

Morris遍历细节

假设来到当前节点cur,开始时cur来到头节点位置

1)如果cur没有左孩子,cur向右移动(cur = 域名t)

2)如果cur有左孩子,找到左子树上最右的节点mostRight:

​ a.如果mostRight的右指针指向空,让其指向cur, 然后cur向左移动(cur = 域名)

​ b.如果mostRight的右指针指向cur,让其指向null, 然后cur向右移动(cur = 域名t) 3)

cur为空时遍历停止

public static void morrisTravel(Node head){
    if (head == null){
        return;
    }
    Node cur = head;
    Node mostRightNode;
    while (cur != null){
        mostRightNode = 域名;
        //cur有左孩子
        if (mostRightNode != null){
            //找到左子树的最右节点,并且保证mostRight不会移动回cur
            while (域名t != null && 域名t != cur){
                mostRightNode = 域名t;
            }
            //看mostRight是否有right
            if (域名t == null){
                //没有right,则添加right指向cur
                域名t = cur;
                //并且cur向左遍历
                cur = 域名;
                //继续这一循环
                continue;
            }
            //指向了cur
            else {
                //断开连接
                域名t = null;
                //cur向右移动
            }
        }
        //cur没有左孩子,cur向右移动
        cur = 域名t;
    }
}

先序遍历

//先序:能回来两次的节点,第一次打印,第二次不打印
//     只能到一次的节点,直接打印
public static void morrisPreTravel(Node head){
    if (head == null){
        return;
    }
    Node cur = head;
    Node mostRightNode;
    while (cur != null){
        mostRightNode = 域名;
        //cur有左孩子
        //进入if:能回cur两次的
        if (mostRightNode != null){
            //找到左子树的最右节点,并且保证mostRight不会移动回cur
            while (域名t != null && 域名t != cur){
                mostRightNode = 域名t;
            }
            //看mostRight是否有right
            if (域名t == null){
                //第一次来到当前节点
                域名t(域名e+" ");
                //没有right,则添加right指向cur
                域名t = cur;
                //并且cur向左遍历
                cur = 域名;
                //继续这一循环
                continue;
            }
            //指向了cur
            else {
                //第二次来到cur
                //不打印
                //断开连接
                域名t = null;
                //cur向右移动
            }
        }
        //没有左子树的,走到else
        else {
            域名t(域名e+" ");
        }
        //cur没有左孩子,cur向右移动
        cur = 域名t;
    }
}

中序

//中序:能回来两次的节点,第一次不打印,第二次打印
//     只能到一次的节点,直接打印
public static void morrisMediaTravel(Node head){
    if (head == null){
        return;
    }
    Node cur = head;
    Node mostRightNode;
    while (cur != null){
        mostRightNode = 域名;
        //cur有左孩子
        //进入if:能回cur两次的
        if (mostRightNode != null){
            //找到左子树的最右节点,并且保证mostRight不会移动回cur
            while (域名t != null && 域名t != cur){
                mostRightNode = 域名t;
            }
            //看mostRight是否有right
            if (域名t == null){
                //没有right,则添加right指向cur
                域名t = cur;
                //并且cur向左遍历
                cur = 域名;
                //继续这一循环
                continue;
            }
            //指向了cur
            else {
                //第二次来到cur
                //不打印
                //断开连接
                域名t = null;
                //cur向右移动
            }
        }
        域名t(域名e+" ");
        //cur没有左孩子,cur向右移动
        cur = 域名t;
    }
}

后续

//后续:能回来两次的节点,第二次的手打印左树的右边界 逆序
//当while完成后,打印整棵树的右边界
public static void morrisLastTravel(Node head){
    if (head == null){
        return;
    }
    Node cur = head;
    Node mostRightNode;
    while (cur != null){
        mostRightNode = 域名;
        //cur有左孩子
        //进入if:能回cur两次的
        if (mostRightNode != null){
            //找到左子树的最右节点,并且保证mostRight不会移动回cur
            while (域名t != null && 域名t != cur){
                mostRightNode = 域名t;
            }
            //看mostRight是否有right
            if (域名t == null){
                //没有right,则添加right指向cur
                域名t = cur;
                //并且cur向左遍历
                cur = 域名;
                //继续这一循环
                continue;
            }
            //指向了cur
            else {
                //第二次来到cur
                //不打印
                //断开连接
                域名t = null;
                printEdge(域名);
                //cur向右移动
            }
        }
        //cur没有左孩子,cur向右移动
        cur = 域名t;
    }
    //while后,打印整棵树左树的右边界
    printEdge(head);
}
public static void printEdge(Node head) {
    Node tail = reverseEdge(head);
    Node cur = tail;
    while (cur != null) {
        域名t(域名e + " ");
        cur = 域名t;
    }
    reverseEdge(tail);
}

public static Node reverseEdge(Node from) {
    Node pre = null;
    Node next = null;
    while (from != null) {
        next = 域名t;
        域名t = pre;
        pre = from;
        from = next;
    }
    return pre;
}

判断是否为线索二叉树

可以根据中序遍历改编

//中序:能回来两次的节点,第一次不打印,第二次打印
//     只能到一次的节点,直接打印
public static boolean morrisMediaTravel(Node head){
    if (head == null){
        return true ;
    }
    Node cur = head;
    Node mostRightNode;
    int preNodeValue = 域名VALUE;
    while (cur != null){
        mostRightNode = 域名;
        //cur有左孩子
        //进入if:能回cur两次的
        if (mostRightNode != null){
            //找到左子树的最右节点,并且保证mostRight不会移动回cur
            while (域名t != null && 域名t != cur){
                mostRightNode = 域名t;
            }
            //看mostRight是否有right
            if (域名t == null){
                //没有right,则添加right指向cur
                域名t = cur;
                //并且cur向左遍历
                cur = 域名;
                //继续这一循环
                continue;
            }
            //指向了cur
            else {
                //第二次来到cur
                //不打印
                //断开连接
                域名t = null;
                //cur向右移动
            }
        }
        //域名t(域名e+" ");
        if (域名e < preNodeValue){
            return false;
        }
        preNodeValue = 域名e;
        //cur没有左孩子,cur向右移动
        cur = 域名t;
    }
    return true;
}

三、位运算

技巧公式

(n >> i) & 1: 取出整数 n在二进制下的第 i 位

n & (~n + 1): 用来求数字的二进制表示的最右边的 1

x ^ y: x + y无进位相加

x & y: x + y 应该进位的数字

返回较大值

给定两个有符号32位整数a和b,返回a和b中较大的(不用比较)

/**
 * @Author: 郜宇博
 */
public class getMaxNoIf {
    public static void main(String[] args) {
        int a = -2147483647;
        int b = 2147480000;
        域名tln(getMax(a,b));
    }

    /**
     * 可能会越界
        a >0 , b < 0时,可能越界 此时sc:1,所以符号不同是,返回a是否大于0就可以

        符号不相同时,a的符号值代表是否应该返回a,
        符号相同时,a-b的符号值代表是否应该返回a

        也就是returnA: difSab * sa + sameSaB * sc(sc>0代表a大)
        returnB: ~ returnA
     */
    public static int getMax(int a, int b){
        //a的符号
        int sa = sign(a);
        int sb = sign(b);
        int sc = sign (a-b);
        //查看a和b是否相同符号,相同为0 不同为1
        int difSab = sa ^ sb;
        int sameSab = invert(difSab);
        int returnA = difSab * sa + sameSab * sc;
        int returnB = invert(returnA);
        return returnA * a + returnB * b;
    }
    //取反
    public static int invert(int a){
        return a ^ 1;
    }
    //得到a的符号,非负返回1,负数返回0
    public static int sign(int a){
        //得到二进制最左边的符号位
        int sign = a >> 31;
        //& 1
        int res =  invert(sign & 1);
        return res;
    }
}

判断是否2,4的幂

判断一个32位正数是不是2的幂、4的幂

/**
 * @Author: 郜宇博
 */
public class TwoPower {
    public static void main(String[] args) {
        域名tln(isFourPower(18));
    }

    /**
     * 方法一:
         可以先获取到a二进制最右边的数,然后和原来a比较,相同就是2的幂
       方法二:
         因为如果只有一个1,那么减掉1后,就会把二进制打散如 1000 - 1 = 0111
         所以将减一后的数 & 原来的数,如果为0 就是2的幂

       本方法方法二
     */
    public static boolean isTwoPower(int a){
        return (a & ( a-1)) == 0;
    }

    /**
     * 起码是2的幂才能是4的幂,所以应该先判断是否是2的幂
     * 因为4的幂的二进制,1肯定在偶数位上,所以就可以 & 0101010101...
     * 如果等于0就不是
     */
    public static boolean isFourPower(int a){
        int isFour = a & (0x55555555);
        return isTwoPower(a) && (isFour != 0);
    }


}

加减乘除

给定两个有符号32位整数a和b,不能使用算术运算符,分别实现a和b的加、减、乘、除运 算

/**
 * @Author: 郜宇博
 没有除
 */
public class AddMinusMultiDivdeByBit {
    public static void main(String[] args) {
        域名tln(add(1,3));
        域名tln(minus(1,3));
        域名tln(multi(20,3));

    }
    //获取无进位相加和进位的数, 循环,直到进位的数为0
    public static int add(int a,int b){
        //两数异或后的结果,也就是无进位相加
        int sum  = a;
        while (b != 0){
            sum = a ^ b;
            //b是进位结果
            b = (a & b) << 1;
            a = sum;
        }
        return sum;
    }
    // a - b == a + (-b)
    // -b == (~b)+1
    public static int minus(int a, int b){
        return add(a ,negNum(b));
    }

    private static int negNum(int b) {
        return add( ~b,1 );
    }
    //如果b末尾为0,则不加到res上,
    public static int multi(int a, int b){
        int res = 0;
        while (b != 0){
            //看b的最后一位是不是0
            if ((b & 1) != 0){
                //更新结果
                res = add(res ,a);
            }
            //a 左移一位
            a <<= 1;
            //b无符号右移一位
            b >>>= 1;
        }
        return res;
    }
}

标签:算法编程
湘ICP备14001474号-3  投诉建议:234161800@qq.com   部分内容来源于网络,如有侵权,请联系删除。