1. 程式人生 > >【劍指offer】31-40題

【劍指offer】31-40題

31.求連續子陣列(包含負數)的最大和

思路:若和小於0,則將最大和置為當前值,否則計算最大和。

程式碼實現

public int FindGreatestSumOfSubArray(int[] array) {
    if (array == null || array.length == 0)
        return 0;
    int cur = array[0];
    int greast = array[0];

    for (int i = 1; i < array.length; i++) {
        if (cur < 0) {
            cur = array[i];
        }else {
            cur += array[i];
        }

        if (cur > greast) {
            greast = cur;
        }
    }
    return greast;
}

32.從1到整數n中1出現的次數

思路:若百位上數字為0,百位上可能出現1的次數由更高位決定;若百位上數字為1,百位上可能出現1的次數不僅受更高位影響還受低位影響;若百位上數字大於1,則百位上出現1的情況僅由更高位決定

程式碼實現

public long CountOne2(long n) {
    long count = 0; // 1的個數
    long i = 1;  // 當前位
    long current = 0,after = 0,before = 0;

    while((n / i) != 0) {
        before = n / (i * 10); // 高位
        current = (n / i) % 10; // 當前位
        after = n - (n / i) * i;  // 低位

        if (current == 0)
            //如果為0,出現1的次數由高位決定,等於高位數字 * 當前位數
            count = count + before * i;
        else if(current == 1)
            //如果為1,出現1的次數由高位和低位決定,高位*當前位+低位+1
            count = count + before * i + after + 1;
        else if (current > 1)
            // 如果大於1,出現1的次數由高位決定,(高位數字+1)* 當前位數
            count = count + (before + 1) * i;
        //前移一位
        i = i * 10;
    }
    return count;
}

解法二:公式法

public int NumberOf1Between1AndN_Solution(int n){
    int count=0;

    for(int i = 1; i <= n; i *= 10){
        int a = n / i; // 高位
        int b = n % i; // 低位
        count += (a+8) / 10 * i;
        if(a % 10 == 1){
            count += b + 1;
        }
    }
    return count;
}

33.輸入一個正整數陣列,把數組裡所有數字拼接起來排成一個數,列印能拼接出的所有數字中最小的一個

思路:先將整型陣列轉換成String陣列,然後將String陣列排序,最後將排好序的字串陣列拼接出來。關鍵就是制定排序規則。或使用比較和快排的思想,將前面的數和最後的數比較,若小則放到最前面,最後再遞迴呼叫。

程式碼實現

public String PrintMinNumber(int [] numbers) {
    if(numbers == null || numbers.length == 0)
        return "";
    int len = numbers.length;
    String[] str = new String[len];
    StringBuilder sb = new StringBuilder();
    for(int i = 0; i < len; i++){
        str[i] = String.valueOf(numbers[i]);
    }
    Arrays.sort(str,new Comparator<String>(){
        @Override
        public int compare(String s1, String s2) {
            String c1 = s1 + s2;
            String c2 = s2 + s1;
            return c1.compareTo(c2);
        }
    });

    for(int i = 0; i < len; i++){
        sb.append(str[i]);
    }
    return sb.toString();
}

34.醜數是隻包含因子2、3和5的數,求從小到大的第N個醜數。

思路:乘2或3或5,之後比較取最小值。

程式碼實現

public int GetUglyNumber_Solution(int index) {
    if (index <= 0)
        return 0;
    int[] arr = new int[index];
    arr[0] = 1;
    int multiply2 = 0;
    int multiply3 = 0;
    int multiply5 = 0;

    for (int i = 1; i < index; i++) {
        int min = Math.min(arr[multiply2] * 2,Math.min(arr[multiply3] * 3,arr[multiply5] * 5));
        arr[i] = min;
        if (arr[multiply2] * 2 == min)
            multiply2++;
        if (arr[multiply3] * 3 == min)
            multiply3++;
        if (arr[multiply5] * 5 == min)
            multiply5++;
    }
    return arr[index - 1];
}

35.在一個字串(1<=字串長度<=10000,全部由字母組成)中找到第一個只出現一次的字元,並返回它的位置

思路:利用LinkedHashMap儲存字元和出現次數。

程式碼實現

public int FirstNotRepeatingChar(String str) {
    if (str == null || str.length() == 0)
        return -1;
    char[] c = str.toCharArray();
    LinkedHashMap<Character,Integer> hash=new LinkedHashMap<Character,Integer>();

    for(char item : c) {
        if(hash.containsKey(item))
            hash.put(item, hash.get(item)+1);
        else
            hash.put(item, 1);
    }

    for(int i = 0;i < str.length(); i++){
        if (hash.get(str.charAt(i)) == 1) {
            return i;
        }
    }
    return -1;
}

36.在陣列中的兩個數字,如果前面一個數字大於後面的數字,則這兩個數字組成一個逆序對。輸入一個數組,求出這個陣列中的逆序對的總數P

思路:本質是歸併排序,在比較時加入全域性變數count進行記錄逆序對的個數,若data[start] >= data[index] ,則count值為mid+1-start

程式碼實現

int count = 0;
public int InversePairs(int [] array) {
    if(array==null)
        return 0;
    mergeSort(array,0,array.length-1);
    return count;
}

private void mergeSort(int[] data,int start,int end) {
    int mid = (start + end) / 2;
    if (start < end) {
        mergeSort(data, start, mid);
        mergeSort(data, mid + 1, end);
        merge(data, start, mid, end);
    }
}

public void merge(int[] data,int start,int mid,int end) {
    int arr[] = new int[end - start + 1];
    int c = 0;
    int s = start;
    int index = mid + 1;
    while (start <= mid && index <= end) {
        if (data[start] < data[index]) {
            arr[c++] = data[start++];
        } else {
            arr[c++] = data[index++];
            count += mid +1 - start;
            count %= 1000000007;
        }
    }

    while (start <= mid) {
        arr[c++] = data[start++];
    }

    while (index <= end) {
        arr[c++] = data[index++];
    }

    for (int d : arr) {
        data[s++] = d;
    }
}

37.輸入兩個連結串列,找出它們的第一個公共結點。

思路:先求出連結串列長度,然後長的連結串列先走多出的幾步,然後兩個連結串列同時向下走去尋找相同的節點,程式碼量少的方法需要將兩個連結串列遍歷兩次,然後從頭開始相同的節點。

程式碼實現

// 不需要遍歷連結串列的解法
public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) {
    ListNode p1 = pHead1;
    ListNode p2 = pHead2;
    while (p1 != p2){
        p1 = (p1 != null ? p1.nextNode : pHead2);
        p2 = (p2 != null ? p2.nextNode : pHead1);
    }
    return p1;
}

38.統計一個數字在排序陣列中出現的次數。

思路:利用二分查詢+遞迴思想,進行尋找。當目標值與中間值相等時進行判斷

程式碼實現

public int GetNumberOfK(int[] array,int k) {
    int result=0;
    int mid = array.length/2;

    if(array==null || array.length == 0)
        return 0;
    if(array.length == 1) {
        if(array[0] == k)
            return 1;
        else
            return 0;
    }

    if(k < array[mid])
        result += GetNumberOfK(Arrays.copyOfRange(array, 0, mid),k);
    else if(k > array[mid])
        result += GetNumberOfK(Arrays.copyOfRange(array, mid, array.length),k);
    else {
        for(int i = mid;i < array.length;i++) {
            if(array[i] == k)
                result++;
            else
                break;
        }

        for(int i = mid - 1;i >= 0;i--) {
            if(array[i] == k)
                result++;
            else
                break;
        }
    }

    return result;
}

39.輸入一棵二叉樹,求該樹的深度。從根結點到葉結點依次經過的結點(含根、葉結點)形成樹的一條路徑,最長路徑的長度為樹的深度。

思路:利用遞迴遍歷分別返回左右子樹深度

程式碼實現

public int TreeDepth(TreeNode root) {
    if (root == null)
        return 0;

    int left = TreeDepth(root.left);
    int right = TreeDepth(root.right);
    return left > right ? left + 1 : right + 1;
}

39.1輸入一棵二叉樹,判斷該二叉樹是否是平衡二叉樹。

思路:平衡因子的絕對值<= 1.

程式碼實現

public boolean IsBalanced_Solution(TreeNode root) {
    if (root == null)
        return true;
    int left = getDepth(root.left);
    int right = getDepth(root.right);
    int diff = left - right;
    if (diff >= -1 && diff <= 1) {
        return true;
    }
    return false;
}

public int getDepth(TreeNode root) {
    if (root == null)
        return 0;
    int depth = 0;
    int leftNode = getDepth(root.left);
    int rightNode = getDepth(root.right);
    depth = leftNode > rightNode ? leftNode : rightNode;
    return depth + 1;
}

40.一個整型數組裡除了兩個數字之外,其他的數字都出現了兩次。請寫程式找出這兩個只出現一次的數字。

思路:兩個相同的數異或後為0,將所有數異或後得到一個數,然後求得1在該數最右邊出現的index,然後判斷每個數右移index後是不是1。

程式碼實現

public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) {
    if (array == null)
        return;
    num1[0] = 0;
    num2[0] = 0;
    int number = array[0];
    for (int i = 1; i < array.length; i++)
        number ^= array[i];
    // 異或後的數1出現在第幾位
    int index = 0;
    while ((number & 1) == 0) {
        number = number >> 1;
        index++;
    }

    for (int i = 0; i < array.length; i++) {
        // 判斷第index位是不是0
        boolean isBit = ((array[i] >> index) & 1) == 0;
        if (isBit) {
            num1[0] ^= array[i];
        } else {
            num2[0] ^= array[i];
        }
    }
}

未完待續。。。。。。