劍指Offer(Java實現):醜數、第一個只出現一次的字符、數組中的逆序對、兩個鏈表的第一個公共節點、在排序數組中查找數字

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;
    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章