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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值