目录
剑指 Offer 21. 调整数组顺序使奇数位于偶数前面【数组】
剑指 Offer 39. 数组中出现次数超过一半的数字【数组】
剑指 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)中间元素值不等于下标元素,则目标对象在前半部分
接着不断二分下去,直到找到元素。
刷题感想:
数组类题大多数都是用指针来解题的,比如双指针、二分法、还有多个指针的。数组类题解题思想就是借助它是一块连续的内存空间来解题,按照下标元素的规律,再加上指针来解题。
其中查找类题大都是二分查找的思想,遇到数组类题先想着用双指针、二分查找思路,不行再考虑别的思路。