前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >两道看似简单的面试高频算法题

两道看似简单的面试高频算法题

作者头像
乔戈里
发布2019-07-29 19:40:41
3580
发布2019-07-29 19:40:41
举报
文章被收录于专栏:Java那些事Java那些事

来源公众号:苦逼的码农

作者:帅地

1、求 x 的 n 次方

当然,这道题你也可以采用 n 次循环让 n 个 x 相乘,不过,这样的做法毫无意义,因为估计小学生也会做。

不过这道题如果知道了思路,还是挺简单,我举个例子吧,例如我们要求 2^8。

1、首先,我们可以通过 2 * 2 = 4 得到 2^2

2、接着,我们利用刚才的结果,让 4 * 4 = 16 得出 2^4

3、接着,同样的道理,让 16 * 16 = 256 得出 2^8

通过这种方法,只需要三次相乘即可得出,也就是说,我们可以在 O(logn) 的时间复杂度求出 x 的 n 次方。这种方法的思想,我们也称之为快速幂思想,和二分查找的思想有点类型,每次都进行翻倍或者缩小一半。

这个时候有人可以能会问,如果 n = 8 或者 n = 16 ,由于 n 是 2 的幂次方,所以可以按照你上面的方法做,那如果 n = 12 呢?

其实道理是一样的,我们可以对 12 进行拆分啊,把 12 拆分成 12 = 4 + 8 就可以了。然后就有 2^12 = 2^4 * 2^8。

那如果 n = 13 呢,也是一样的,拆分成 13 = 1 + 4 + 8,即 2^13 = 2^1 * 2^4 * 2^8。

也就是说,任何整数,都可以把它拆分成若干个 2 的幂次方进行相加。

代码如下

代码语言:javascript
复制
// 为了代码短一段,这里假设 n 是非负整数,并且不会产生溢出
int pow(int x, int n){
    int res = 1;
    while(n > 0){
        if(n % 2 == 1){
            res *= x;
        }
        n >> 1;
        x = x * x;
    }
}

好吧,上篇文章中,我说不可以使用位运算,上面的代码还是用到了位运算,如果不使用的话,代码会比较复杂,这里跟各位说声不好意思,如果你对里面的代码看的不是很懂,说明你的牛逼的位运算不熟悉,可以看我之前的文章:【算法技巧】位运算装逼指南

2、搜索旋转排序数组

假设按照升序排序的数组在预先未知的某个点上进行了旋转。( 例如,数组 [0,1,2,4,5,6,7] 可能变为 [4,5,6,7,0,1,2] )。

搜索一个给定的目标值 target,如果数组中存在这个目标值,则返回它的索引,否则返回 -1 。你可以假设数组中不存在重复的元素。你的算法时间复杂度必须是 O(log n) 级别。

代码语言:javascript
复制
示例 1:

输入: nums = [4,5,6,7,0,1,2], target = 0
输出: 4
示例 2:

输入: nums = [4,5,6,7,0,1,2], target = 3
输出: -1

解答

为了方便讲解,这里我们把数组中的最小值称之为旋转点吧。接下来我们就以上面中的例子来进行讲解。

没有旋转之前的数组

旋转之后的数组

显然,这个旋转点是最特殊的点,因为旋转点既比左边的数小,同时也比右边的数小。

我们知道,在我们平时的二分查找中(也就是数组没有旋转之前),我们会把目标值 target 与 中间元素 mid 进行比较,每次比较都会有如下三种结果

(1)如果 target 比 mid 小,那么 mid 以及 mid 右边的所有元素一定比 target 大,所以 target 只能存在于 mid 的左边元素中。

(2)如果 target 比 mid 大,那么 mid 以及 mid 左边的所有元素一定比 target 小,所以 target 只能存在于 mid 的右边元素中。

(3)如果 target 与 mid 相等,则直接把 mid 对应的下标返回即可。

而在这道题中,情况要比这个复杂,因为它还有受旋转点的位置所影响,具体可以分为以下两种情况。

这里我在强调一下,二分查找的结果就是要把范围缩小一般,所以我们的目的就是,寻找出 target 究竟是位于 mid 的左边还是右边。

(一)情况1:中间元素在旋转点的左侧

(1)target 在 mid 的左边。

如果 target 在 mid 的左边,显然需要满足条件:最左边元素 <= target < mid

(2)taeget 在 mid 的右边

如果不满足(1)的条件,则意味着在右边

(二)情况2:中间元素在旋转点的右侧或者和旋转点重合

右侧

重合

(1)target 在 mid 的 右边

显然,需要满足的条件是 mid < target <= 最右侧元素

(1)target 在 mid 的左边

如果不满足 (1) 中的条件,则在左边。

当然,在上面的情况中,我们没有 mid 与 target 相等的情况,因为如果他们相等的话,就直接返回下边即可,无需讨论。

上面的各种情况我们都讨论好了,下面我们直接按照上面的逻辑来写代码即可,代码如下:

代码语言:javascript
复制
int rotatedBinarySearch(int[] arr, int target){
    // 最左侧元素下标
    int left = 0;
    // 最右侧元素下标
    int right = arr.length - 1;
    while(left <= right){
        // 中间元素下标
        int mid = left + (right - left) / 2;
        if(arr[mid] == target){
            return mid;
        }

        // 情况1:如果中间元素在旋转点左侧
        if(arr[mid] >= arr[left]){
            //target 如果位于中间元素的左侧
            if(arr[mid] > target && target >= arr[left]){
                right = mid - 1;
            }else{
                left = mid + 1;
            }
        }
        // 情况2:中间元素在旋转点的右侧
        else{
            // target 如果位于中间元素的右侧
            if(arr[mid] < target && target <= arr[right]){
                left = mid + 1;
            }else{
                right = mid - 1;
            }
        }
    }
    return -1;
}

最后

对于有些需要分很多种情况讨论的题,说时候,不是很好讲,就算我详细着给你们讲,还是容易把你们给绕晕,所以在以后的选题中,我会尽量选择一些典型的,一道题能够引申出某个重点知识点的题来讲,当然,也欢迎大家给我提供一些有意思的题,后面打算写写红黑树、trie 树、递归树等高级数据结构,敬请期待。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-07-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 程序员乔戈里 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、求 x 的 n 次方
  • 2、搜索旋转排序数组
  • 最后
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档