package com.dengzm.jianzhioffer;

/**
 * @Description 049 丑数
 * 我们把只包含因子2,3和5的数称作丑数。求按从大到小的第1500个丑数
 *
 * Created by deng on 2019/9/22.
 */
public class Jianzhi049 {

    public static void main(String[] args) {
        System.out.println(getUglyNumber(1));
        System.out.println(getUglyNumber(10));
        System.out.println(getUglyNumber(100));
        System.out.println(getUglyNumber(1500));
    }

    private static int getUglyNumber(int index) {
        if (index <= 0) {
            return 0;
        }

        int[] uglyNumbers = new int[index];
        uglyNumbers[0] = 1;
        int nextUglyNumberIndex = 1;

        int mutiply2 = 0;
        int mutiply3 = 0;
        int mutiply5 = 0;

        while (nextUglyNumberIndex < index) {
            int num = Math.min(Math.min(uglyNumbers[mutiply2] * 2, uglyNumbers[mutiply3] * 3), uglyNumbers[mutiply5] * 5);
            uglyNumbers[nextUglyNumberIndex] = num;

            while (uglyNumbers[mutiply2] * 2 <= uglyNumbers[nextUglyNumberIndex]) {
                mutiply2 ++;
            }

            while (uglyNumbers[mutiply3] * 3 <= uglyNumbers[nextUglyNumberIndex]) {
                mutiply3 ++;
            }

            while (uglyNumbers[mutiply5] * 5 <= uglyNumbers[nextUglyNumberIndex]) {
                mutiply5 ++;
            }

            nextUglyNumberIndex ++;
        }

        return uglyNumbers[index - 1];

    }
}
package com.dengzm.jianzhioffer;

import java.util.HashMap;

/**
 * @Description 050 第一个只出现一次的字符
 *
 * Created by deng on 2019/9/22.
 */
public class Jianzhi050 {

    public static void main(String[] args) {
        String data1 = "asdjbj!)kvbklasdk@%^*&^#&*lvka";
        String data2 = "qwertyuiopasdfghjkqwertyuiopasdfghjkl!";
        String data3 = "qwertyuiopasdfghjkqwertyuiopasdfghjk";

        System.out.println(findFirstNoRepeatChar(data1));
        System.out.println(findFirstNoRepeatChar(data2));
        System.out.println(findFirstNoRepeatChar(data3));
    }

    private static String findFirstNoRepeatChar(String target) {
        if (target == null || target.length() == 0) {
            throw new RuntimeException("sth is wrong with target string");
        }

        char[] targetChars = target.toCharArray();
        HashMap<Character, Integer> repeatNumbers = new HashMap<>();

        for (int i = 0; i < targetChars.length; i ++) {
            if (repeatNumbers.containsKey(targetChars[i])) {
                repeatNumbers.put(targetChars[i], repeatNumbers.get(targetChars[i]) + 1);
            } else {
                repeatNumbers.put(targetChars[i], 1);
            }
        }

        for (int i = 0; i < targetChars.length; i ++) {
            int number = repeatNumbers.get(targetChars[i]);
            if (number == 1) {
                return String.valueOf(targetChars[i]);
            }
        }

        return "there is no NonRepeating char in target string : " + target;
    }
}
package com.dengzm.lib;

/**
 * @Description 051 数组中的逆序对
 * 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求逆序对的总数
 * 例如:数组{7,5,6,4}中有5个逆序对,分别为{7,6}, {7,5}, {7,4}, {6,4}, {5,4}
 *
 * Created by deng on 2019/9/22.
 */
public class Jianzhi051 {

    public static void main(String[] args) {
        int[] data1 = new int[] {7, 5, 6, 4};
        int[] data2 = new int[] {7, 5, 6, 4, 8, 1};
        int[] data3 = new int[] {7, 5, 6, 4, 1};
        int[] data4 = new int[] {7, 5, 6, 4, 10, 5, 4, 19, 2};

        System.out.println("inverse pairs num is " + countInversePairs(data1));
        System.out.println("inverse pairs num is " + countInversePairs(data2));
        System.out.println("inverse pairs num is " + countInversePairs(data3));
        System.out.println("inverse pairs num is " + countInversePairs(data4));
    }

    /**
     * 通过归并排序的方式进行统计
     * 拆分data,合并时对逆序对进行统计
     *
     * @param data data
     * @return num of inverse pairs
     */
    private static int countInversePairs(int[] data) {
        if (data == null || data.length == 0) {
            return 0;
        }

        // 复制数组,不直接使用原始数据
        int[] copy = new int[data.length];

        System.arraycopy(data, 0, copy, 0, data.length);

        return countInversePairsCore(copy, 0, data.length - 1);
    }

    private static int countInversePairsCore(int[] copy, int start, int end) {
        if (start >= end) {
            return 0;
        }

        int length = (end - start) / 2;

        int left = countInversePairsCore(copy, start, start + length);
        int right = countInversePairsCore(copy, start + length + 1, end);

        // 初始化前后半段的最后一个数字的下标
        int i = start + length;
        int j = end;

        // 临时数据
        int[] temp = new int[end - start + 1];

        // 临时数据的下标
        int indexCopy = end - start;

        // return result
        int count = 0;

        // 归并-合并时,如果前段大于后段,则增加count,因为前后半段各自都是递增的,所以增加的数量为end - mid
        while (i >= start && j >= start + length + 1) {
            if (copy[i] > copy[j]) {
                temp[indexCopy --] = copy[i --];
                count += j - start - length;
            } else {
                temp[indexCopy --] = copy[j --];
            }
        }

        // 将剩余数据复制到临时数据
        while (i >= start) {
            temp[indexCopy --] = copy[i --];
        }

        while (j >= start + length + 1) {
            temp[indexCopy --] = copy[j --];
        }

        // 将临时数据同步回copy,保证合并后的数据为递增
        System.arraycopy(temp, 0, copy, start, temp.length);

        return left + right + count;
    }
}
package com.dengzm.jianzhioffer;

/**
 * @Description 052 两个链表的第一个公共节点
 *
 * Created by deng on 2019/9/22.
 */
public class Jianzhi052 {

    public static void main(String[] args) {
        Node node1 = new Node(1);
        Node node2 = new Node(2);
        Node node3 = new Node(3);
        Node node4 = new Node(4);
        Node node5 = new Node(5);
        Node node6 = new Node(6);
        Node node7 = new Node(7);

        node1.next = node2;
        node2.next = node3;
        node3.next = node6;
        node6.next = node7;

        node4.next = node5;
        node5.next = node6;

        Node result = findFirstSameNode(node1, node4);
        System.out.println(result == null ? "null" : result.value);
    }

    private static Node findFirstSameNode(Node head1, Node head2) {
        if (head1 == null || head2 == null) {
            return null;
        }

        int difLength;
        Node longHead;
        Node shortHead;

        int length1 = getLength(head1);
        int length2 = getLength(head2);

        if (length1 > length2) {
            longHead = head1;
            shortHead = head2;
            difLength = length1 - length2;
        } else {
            longHead = head1;
            shortHead = head2;
            difLength = length1 - length2;
        }

        if (difLength != 0) {
            for (int i = 0; i < difLength; i ++) {
                longHead = longHead.next;
            }
        }

        while (longHead != null && shortHead != null) {
            if (longHead == shortHead) {
                return longHead;
            }

            longHead = longHead.next;
            shortHead = shortHead.next;
        }

        return null;
    }

    private static int getLength(Node head) {
        int len = 0;
        Node temp = head;

        while(temp != null) {
            len ++;
            temp = temp.next;
        }

        return len;
    }

    private static class Node {
        int value;
        Node next;

        Node(int value) {
            this.value = value;
        }
    }
}
package com.dengzm.lib;

/**
 * @Description 053 在排序数组中查找数字
 * 题目一:数字在排序数组中出现的次数
 * 题目二:0~n-1中缺失的数字
 * 题目三:数组中数值和下标相等的元素:假设一个单调递增的数组里的每个元素都是整数并且唯一,请实现一个函数可以找出数组中任意一个数值等于其下标的元素
 *
 * Created by deng on 2019/10/29.
 */
public class Jianzhi053 {

    public static void main(String[] args) {
        int[] data1 = new int[] {1, 2, 3, 3, 3, 3, 4, 6};
        int[] data2 = new int[] {1, 2, 3, 3, 3, 3, 4, 4, 6, 7, 8};
        int[] data3 = new int[] {1, 2, 3, 3, 3, 3, 4, 6};

        int[] data4 = new int[] {0, 1, 2, 3, 4, 6, 7, 8, 9};
        int[] data5 = new int[] {0, 2};
        int[] data6 = new int[] {1};

        int[] data7 = new int[] {-3, -1, 0, 2, 4, 6, 8};
        int[] data8 = new int[] {-3, 1};
        int[] data9 = new int[] {0};
        int[] data10 = new int[] {-3};


        System.out.println("Q1:number of 3 in data1 is " + getNumberOfK(data1, 3));
        System.out.println("Q1:number of 4 in data2 is " + getNumberOfK(data2, 4));
        System.out.println("Q1:number of 4 in data3 is " + getNumberOfK(data3, 4));

        System.out.println("Q2:lost number in data4 is " + getLostNum(data4));
        System.out.println("Q2:lost number in data5 is " + getLostNum(data5));
        System.out.println("Q2:lost number in data6 is " + getLostNum(data6));

        System.out.println("Q3:the num of which is equal to its index in data7 is " + getNumEqualToIndex(data7));
        System.out.println("Q3:the num of which is equal to its index in data8 is " + getNumEqualToIndex(data8));
        System.out.println("Q3:the num of which is equal to its index in data9 is " + getNumEqualToIndex(data9));
        System.out.println("Q3:the num of which is equal to its index in data10 is " + getNumEqualToIndex(data10));
    }

    /**
     * 题目一
     * 通过二分法,找到数字k的第一个位置和最后一个的位置
     * 时间复杂度 O(logn)
     *
     * @param data data
     * @param k the num we are looking for
     * @return how many k in data
     */
    private static int getNumberOfK(int[] data, int k) {
        if (data == null || data.length == 0) {
            return 0;
        }

        int first = getFirstK(data, k, 0, data.length - 1);
        int last = getLastK(data, k, 0, data.length - 1);

        return first == -1 ? 0 : last - first + 1;
    }

    private static int getFirstK(int[] data, int k, int start, int end) {
        if (start > end) {
            return -1;
        }

        int middleIndex = (end + start) / 2;
        int middleData = data[middleIndex];

        if (middleData == k) {
            // 当前数字为k,开始判断是否为第一个k
            if (middleIndex == 0 || (middleIndex > 0 && data[middleIndex - 1] != k)) {
                // 当前位置下标为0 或 前一个数字不是k
                // 表示此时得到的下标即为第一个k
                return middleIndex;
            } else {
                // 下标不为0,且前一个数字为k,在前半段中再次进行二分查找
                end = middleIndex - 1;
            }
        } else {
            // 当前数字不为k,进行判断,在前后半段中的哪一个
            if (middleData > k) {
                end = middleIndex - 1;
            } else {
                start = middleIndex + 1;
            }
        }

        return getFirstK(data, k, start, end);
    }

    private static int getLastK(int[] data, int k, int start, int end) {
        if (start > end) {
            return -1;
        }

        int middleIndex = (end + start) / 2;
        int middleData = data[middleIndex];

        if (middleData == k) {
            // 当前数字为k,开始判断是否为最后一个k
            if (middleIndex == data.length - 1 || (middleIndex < data.length - 1 && data[middleIndex + 1] != k)) {
                // 当前位置下标为length-1 或 后一个数字不是k
                // 表示此时得到的下标即为最后一个k
                return middleIndex;
            } else {
                // 下标不为length-1,且后一个数字为k,在后半段中再次进行二分查找
                start = middleIndex + 1;
            }
        } else {
            // 当前数字不为k,进行判断,在前后半段中的哪一个
            if (middleData > k) {
                end = middleIndex - 1;
            } else {
                start = middleIndex + 1;
            }
        }

        return getLastK(data, k, start, end);
    }

    /**
     * 题目二
     * 0~n-1,数字大小与下标相同
     * 使用二分法进行查找,找到第一个下标与数字不等的位置,即为缺失的数字
     *
     * @param data data
     * @return the lost num
     */
    private static int getLostNum(int[] data) {
        if (data == null || data.length == 0) {
            return -1;
        }

        int left = 0;
        int right = data.length - 1;

        while (left <= right) {
            int middle = (left + right) / 2;

            // 当mid的下标与数字不等时,如果前一个相等,或是mid为0,则当前mid即为缺失的数字;否则继续在前半段查找
            if (data[middle] != middle) {
                if (middle == 0 || (data[middle - 1] == middle - 1)) {
                    return middle;
                } else {
                    right = middle - 1;
                }
            } else {
                // 下标相等时,在后半段查找
                left = middle + 1;
            }
        }

        return -1;
    }

    /**
     * 题目三
     * 二分法查找:如果num > index,则在前半段查找;如果num < index, 则在后半段查找
     *
     * @param data data
     * @return num of which is equal to its index
     */
    private static int getNumEqualToIndex(int[] data) {
        if (data == null || data.length == 0) {
            return -1;
        }

        int left = 0;
        int right = data.length - 1;

        while (left <= right) {
            int middle = (left + right) / 2;
            if (data[middle] == middle) {
                return middle;
            } else if (data[middle] < middle) {
                // num < index, 则在后半段查找
                left = middle + 1;
            } else {
                // num > index,则在前半段查找
                right = middle - 1;
            }
        }

        return -1;
    }
}