剑指Offer数组篇-----刷题总结

目录

剑指 Offer 03. 数组中重复的数字【数组类】

剑指 Offer04. 二维数组中的查找【数组】

剑指 Offer 11. 旋转数组的最小数字

剑指 Offer 21. 调整数组顺序使奇数位于偶数前面【数组】

剑指 Offer 29. 顺时针打印矩阵【数组】

剑指 Offer 39. 数组中出现次数超过一半的数字【数组】

剑指 Offer 45. 把数组排成最小的数【数组】

剑指 Offer 53 - I. 在排序数组中查找数字 I【数组】

剑指 Offer 53 - II. 0~n-1中缺失的数字【数组】

刷题感想:


剑指 Offer 03. 数组中重复的数字【数组类】

在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。

示例 1:

输入:[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3 

2.解题思路:Set集合
题目叫我们找出任意一个重复元素即可,这里我们可以利用Set集合“不允许有重复元素”的特性来解题。先逐一遍历数组,将数组元素一个一个放入容器中,当容器中包含了此元素时,就找到了重复元素,此时return此元素即可。如果一直没有重复元素,最后return -1,因为数组元素取值是0~n-1,所以不会有-1。
 

剑指 Offer04. 二维数组中的查找【数组】

在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

示例:

现有矩阵 matrix 如下:

[
  [1,   4,  7, 11, 15],
  [2,   5,  8, 12, 19],
  [3,   6,  9, 16, 22],
  [10, 13, 14, 17, 24],
  [18, 21, 23, 26, 30]
]
给定 target = 5,返回 true。

给定 target = 20,返回 false。

2.解题思路:
很明显,由于该二维数组从上到下递增,从左到右递增的特殊性,遍历整个矩阵进行查找不是该题目的意图所在。总结规律我们可以发现:应该从矩阵的右上角或者左下角开始查找。左上角或右下角就会比较复杂,因为都是递增或递减的。

  (1)以右上角为例,首先选取右上角的数字,如果该数字等于要查找的数字,则查找过程结束;

        (2)如果该数字大于要查找的数字,则说明该列其他元素都大于要查找的数字,便可以删掉该列;

        (3)如果该数字小于要查找的数字,则说明该行其他元素也都小于要查找的数字,便可以删掉该行。

  这样,每一次比较都可以剔除一行或者一列,进而缩小查找范围,时间复杂度为O(n)。
 

剑指 Offer 11. 旋转数组的最小数字

把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。

给你一个可能存在 重复 元素值的数组 numbers ,它原来是一个升序排列的数组,并按上述情形进行了一次旋转。请返回旋转数组的最小元素。例如,数组 [3,4,5,1,2] 为 [1,2,3,4,5] 的一次旋转,该数组的最小值为 1。

示例 1:

输入:numbers = [3,4,5,1,2]
输出:1

2.解题思路:二分法查找
 本题的直观解法很简单,直接对数组进行一次遍历就可以找到最小值,复杂度为O(n),但是显然这不是本题的意图所在,因为没有利用到任何旋转数组的特性。

首先,我们明确知道这个数组是被旋转了,也就意味着,这个数组实际上可以被划分为两个部分。

1、左边是一个递增的数组
2、右边是一个递增的数组
3、左右两部分相交的位置出现了一个异常点,小的数字在大的数字后面,比如下图中 1 在 7 的后面,正常来说递增数组应该是 1 在 7 的前面,而在这个位置产生了异常。


那么,只要我们可以找到异常点,也就找到了旋转数组的最小元素。 

 具体操作如下:

1、设置两个指针 left 和 right ,其中 left 指向当前区间的开始位置,right 指向当前区间的结束位置,计算出两者的中间索引位置 mid 。

 2、接下来,我们研究思考一下这三个指针指向的元素之间的关系。

1、如果 mid 指向的元素大于 right 指向的元素,也就意味着异常点肯定是发生在 [ mid + 1 , right ] 这个区间的,不需要再在 [ left ,mid ] 这个区间里面查找,那么可以更新 left 的位置为 mid + 1。
2、如果 mid 指向的元素小于 right 指向的元素,也就意味着 [ mid , right ] 这个区间中所有的元素都是正常递增,不需要再在 [ mid , right ] 这个区间里面查找,异常点发生在 [ left , mid ] 这个区间,那么可以更新 right的位置为 mid 。
3、由于数组中可能存在重复的元素,比如 [1, 1, 1, 0, 1],那么mid 指向的元素会等于 right 指向的元素,此时无法判断异常点会在哪个区间,因此我们可以从头到尾遍历一下剩下的区间,找到那个最小的元素。

剑指 Offer 21. 调整数组顺序使奇数位于偶数前面【数组】

输入一个整数数组,实现一个函数来调整该数组中数字的顺序,使得所有奇数位于数组的前半部分,所有偶数位于数组的后半部分。

示例:

输入:nums = [1,2,3,4]
输出:[1,3,2,4] 
注:[3,1,2,4] 也是正确的答案之一。

2.解题思路:双指针
这道题目最终可以得到一个这样的数组,左边的部分都是奇数,右边的部分都是偶数。

 如上图所示,绿色部分都是奇数,黄色部分都是偶数,并且此时 left 指向了奇数的最后一个元素,right 指向了偶数的第一个元素。

为了得到这样一个结果,需要执行如下的操作:

设置两个指针 left 和 right,left 指向当前区间的最左侧元素,right 指向当前区间的最右侧元素。
left向右移动,right向左移动;

left指向奇数,则继续向右移动,直到指向偶数停下;
right指向偶数,则继续向左移动,直到指向奇数停下;

然后left与right指向的元素交换位置,当left>=right时,退出循环。

剑指 Offer 29. 顺时针打印矩阵【数组】

输入一个矩阵,按照从外向里以顺时针的顺序依次打印出每一个数字。

示例 1:

输入:matrix = [[1,2,3],[4,5,6],[7,8,9]]
输出:[1,2,3,6,9,8,7,4,5]

2.解题思路:

设置四个指针top、right、bottom、left,判断临界条件

由于是按照从外到内的顺序依次打印,所以可以把矩阵想象成若干个圈,用一个循环来打印矩阵,每次打印矩阵中的一圈。

而对于每一圈的打印,很自然便可以想到遵循从左到右,从上到下,从右到左,从下到上的顺序。但是这里需要注意的是最后一圈的打印,由于矩阵并不一定是方阵,最后一圈有可能退化为只有一行,只有一列,甚至只有一个数,因此要注意进行判断,避免重复打印。

如下集中特例情况:

 

剑指 Offer 39. 数组中出现次数超过一半的数字【数组】

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。

示例 1:

输入: [1, 2, 3, 2, 2, 2, 5, 4, 2]
输出: 2

2.解题思路:一对一单挑,人多的胜

方法一:首先对数组进行排序,在一个有序数组中,次数超过一半的必定是中位数,那么可以直接取出中位数,然后遍历数组,看中位数是否出现次数超过一半,这取决于排序的时间复杂度,最快为O(nlogn)。

      

方法二:遍历数组,用 HashMap 保存每个数出现的次数,这样可以从map中直接判断是否有超过一半的数字,这种算法的时间复杂度为O(n),但是这个性能提升是用O(n)的空间复杂度换来的。

        

方法三(最优解法):根据数组特点得到时间复杂度为O(n)的算法。根据数组特点,数组中有一个数字出现的次数超过数组长度的一半,也就是说它出现的次数比其他所有数字出现的次数之和还要多。因此,我们可以在遍历数组的时候设置两个值:一个是数组中的数result,另一个是出现次数times。当遍历到下一个数字的时候,如果与result相同,则次数加1,不同则次数减一,当次数变为0的时候说明该数字不可能为多数元素,将result设置为下一个数字,次数设为1。这样,当遍历结束后,最后一次设置的result的值就是符合要求的值(如果有数字出现次数超过一半,则必为该元素,否则不存在)

剑指 Offer 45. 把数组排成最小的数【数组】

输入一个非负整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。

示例 :

输入: [3,30,34,5,9]

输出: "3033459"

2.解题思路:利用快速排序+递归思想

1.先将整型数组转为字符串数组(防止数字过大溢出)

2.再用快速排序、递归的思想,定一个pivot基准值,分出left、right两个区域,递归不断分下去,将左右两块区域的元素移动,最终另左边任意元素与pivot组合都小于pivot在前,右边任意元素与pivot组合都大于pivot在前。

 

剑指 Offer 53 - I. 在排序数组中查找数字 I【数组】

统计一个数字在排序数组中出现的次数。例如,输入排序数组{1,2,3,3,3,3,4,5}和数字3,由于数字3在该数组中出现了4次,所以函数返回4。

示例 :

输入: nums = [5,7,7,8,8,10], target = 8
输出: 2

2.解题思路:二分查找+递归思想  target:目标值

1.如果中间数字比target大,那target只能出现在数组的前半段

   

2.如果中间数字比target小,那target只能出现在数组的后半段

        

3.如果中间数字等于target,那就寻找左右两边target的值,左右两边同时遍历数组,直到找到目标元素,指针停止。

剑指 Offer 53 - II. 0~n-1中缺失的数字【数组】

一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。

示例:

输入: [0,1,2,3,4,5,6,7,9]
输出: 8

2.解题思路:二分查找+递归思想

首先数组0-n-1中数值是有序的,只有其中一个数不在数组中,设它为m,则m之前的数值与下标都是相等的,m之后的数值都比下标大1,即m+1、m+2......

二分法求解

(1)中间元素值等于下标元素,则目标对象在后半部分

(2)中间元素值不等于下标元素,则目标对象在前半部分

接着不断二分下去,直到找到元素。

刷题感想:

数组类题大多数都是用指针来解题的,比如双指针、二分法、还有多个指针的。数组类题解题思想就是借助它是一块连续的内存空间来解题,按照下标元素的规律,再加上指针来解题。

其中查找类题大都是二分查找的思想,遇到数组类题先想着用双指针、二分查找思路,不行再考虑别的思路。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值