day 8-9 算法:二叉樹遍歷、遞歸與分治

1. 題目

  1. 實現pow(x,n),即計算x的n次冪函數
  2. 求衆數,給定一個大小爲n的數組,找到其中的衆數。衆數是指出現次數大於 n/2 的元素。

2. 基本知識

2.1 二叉樹遍歷

  1. 前序遍歷:根-左-右
  2. 中序遍歷:左-根-右
  3. 後序遍歷:左-右-根

二叉樹

前序順序:A-B-D-E-C-F-G
中序遍歷:D-B-E-A-F-C-G
後序遍歷:D-E-B-F-G-C-A

2.2 遞歸

遞歸,就是在運行的過程中調用自己

構成遞歸的條件:

  1. 子問題和原始問題是同樣的問題,並且會更簡單
  2. 不能無限循環,必須有一個終止條件

示例:

// n的階乘:1*2*3*...*(n-1)*n
public int Factorial(int n){
	if (n <=1) {
		return 1;
	}
	return n*Factorial(n-1);
}

2.3 分治

將複雜問題分爲兩個或者多個子問題,直至問題能夠簡單求解,最後複雜問題是這些簡單問題解的合併。一般使用遞歸來實現。

3. 算法題解題

3.1 實現pow(x,n),即計算x的n次冪函數

解法1:暴力解法
循環n次,每次乘以x。
時間複雜度爲O(n),空間複雜度也爲O(1)

double myPow(double x, int n){
    int N = n;
    // n小於0的情況爲(1/x)的 -n次冪,例子:pow(2,-2)結果爲:pow(1/2,2)
    if (n < 0){
        N = -n;
        x = 1/x;
    }
    double ans = 1;
    for (int i = 0; i < N; i++) {
        ans = ans * x;
    }
    return ans;
}

解法2: 遞歸
xn可以拆解爲兩個x(n/2),同理一直拆解下去,知道n爲1時返回1.需要注意的是奇數情況下,返回值爲int,則n/2等同於(n-1)/2,所以需要多乘以x。
此解法時間複雜度爲O(logn),空間複雜度爲:O(1)

double myPow(double x, int n){
    int N = n;
    // n小於0的情況爲(1/x)的 -n次冪,例子:pow(2,-2)結果爲:pow(1/2,2)
    if (n < 0){
        N = -n;
        x = 1/x;
    }
    return fastPow(x,N);
}

private double fastPow(double x, int n) {
    if (n == 0) return  1;

    double half = fastPow(x,n/2);

    if (n%2 == 0){
        //偶數
        return half*half;
    }else{
        //奇數情況下,返回值爲int,則n/2等同於(n-1)/2
        return half * half * x;
    }
}

解法3:循環

此解法位置暫時預留

3.2 求衆數,給定一個大小爲n的數組,找到其中的衆數

解法1:暴力解法
兩層循環,分別對同一個元素的個數進行累加,得到衆數
時間複雜度爲O(n2),空間複雜度也爲O(1)

public int majorityElement(int[] nums){

    int majorityCount = nums.length/2;

    for (int number :nums) {
        int count = 0;
        for (int ele :nums) {
            if (number == ele) {
                    count += 1;
                }
        }
        if (count > majorityCount) return number;
    }
    return -1;
}

解法2:哈希表
使用hash表存儲元素及其對應的出現次數,然後,遍歷hash表,將出現次數大於n/2的元素返回。
此解法時間複雜度:O(n),空間複雜度O(n)

public int majorityElement(int[] nums){

    int majorityCount = nums.length/2;

    HashMap<Integer, Integer> countMap = getCountNums(nums);

    Map.Entry<Integer, Integer> majorityEntry = null;
    for (Map.Entry<Integer, Integer> entry :countMap.entrySet()) {
        if(entry.getValue() > majorityCount){
            majorityEntry = entry;
        }
    }
    if (majorityEntry == null) return -1;

    return majorityEntry.getKey();
}

/**
 * 用hashMap存儲key和出現的次數
 * @param nums
 * @return
 */
private HashMap<Integer, Integer> getCountNums(int[] nums) {
    HashMap<Integer, Integer> majorMap = new HashMap<>();

    for (int number :nums) {
        if (majorMap.containsKey(number)){
            majorMap.put(number, majorMap.get(number) + 1);
        }else{
            majorMap.put(number, 1);
        }
    }
    return majorMap;
}

解法3 排序
此解法時間複雜度O(1),空間複雜度O(1)

/**
 * 因爲衆數要個數大於n/2,所以排序後的n/2處必定是衆數元素
 * @param nums
 * @return
 */
public int majorityElemet(int[] nums){
    Arrays.sort(nums);
    return nums[nums.length/2];
}

算法4 分治
如果把數組一分爲二,求出左邊的衆數和右邊的衆數,如果兩邊得到的衆數一樣,則直接返回,如果不一樣,則在當前的子數組中算出該數的個數,然後左右衆數的個數比較,誰個數多就返回誰。

public int majorityElement(int[] nums){
    return majorityElementEach(nums, 0, nums.length -1);
}

private int majorityElementEach(int[] nums, int le, int ri) {
    // 只有一個元素了,直接返回
    if (le == ri)
        return nums[le];
    int mid = (ri  - le)/2 + le;
    // 從中分開,分別計算左右的衆數
    int left = majorityElementEach(nums, le, mid);
    int right = majorityElementEach(nums,mid+1, ri);

    // 如果左右都有相同的衆數,則返回
    if (left == right) return left;

    // 分別計算left和right的個數
    int leftCount = countInRange(nums,left,le,ri);
    int rightCount = countInRange(nums,right,le,ri);

    return leftCount > rightCount ? leftCount : rightCount;
}

private int countInRange(int[] nums, int num, int le, int ri) {
    int count = 0;
    for (int i = le; i < ri - le; i++) {
        if (nums[i] == num){
            count ++;
        }
    }
    return count;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章