数组是非常基础的数据结构,考察数组的题目难度低,主要考对代码的编写能力。
数组是存放在连续内存空间上的相同类型数据的集合。
向数组中进行数据的添加或删除时,需要对其它元素进行移动。
关于二维数组:
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1。题目链接
思路:当数组满足条件:有序数组、无重复元素时,可以考虑使用二分查找的方法解题。在二分查找中有两种二分方法:左闭右闭和左闭右开,它们的区别是有边界right的值不同,根据right值的不同对逻辑代码中判断条件进行调整(while寻找每一次边界的处理都要根据区间的定义来操作)。我们掌握法一即可。
代码如下(Java):
class Solution {
public int search(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while (left <= right){ //while循环的条件是左边界小于等于右边界
int mid = (right - left) / 2 + left; //防溢出方法
if (nums[mid] < target){
left = mid + 1;
}else if (nums[mid] > target){
right = mid - 1;
}else {
return mid; //返回目标值的位置
}
}
return -1; //未找到目标值
}
}
给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。请必须使用时间复杂度为 O(log n) 的算法。题目链接
思路:关键词有排序数组,可以考虑二分法。通过推导可以得知此时有四种情况:
对于情况2,只需要返回对应的位置即可,对于其它三种情况,返回有边界值right+1即可。
代码如下:
class Solution {
public int searchInsert(int[] nums, int target) {
int left = 0, right = nums.length - 1;
while (left <= right){
int mid = (right - left) / 2 + left;
if (nums[mid] < target){
left = mid + 1;
}else if (nums[mid] > target){
right = mid - 1;
}else {
return mid;
}
}
return right + 1;
}
}
给定一个按照升序排列的整数数组 nums,和一个目标值 target。找出给定目标值在数组中的开始位置和结束位置。如果数组中不存在目标值 target,返回 [-1, -1]。题目链接
进阶:你可以设计并实现时间复杂度为 O(log n) 的算法解决此问题吗?
思路:暴力解法,情况分为数组为空和数组不为空的情况,如果数组为空,则返回[-1,-1]即可;如果数组不为空,则利用二分法寻找target位置,找到则使用while循环找到target的范围,如果没有找到,则直接返回[-1,-1].
代码如下:
class Solution {
public int[] searchRange(int[] nums, int target) {
if (nums.length == 0) return new int[]{-1,-1};
int low = 0, high = nums.length - 1;
int mid = 0;
while (low <= high) {
mid = (high - low)/2 + low;
if (nums[mid] > target) {
high = mid - 1;
}else if (nums[mid] < target){
low = mid + 1;
}else {
break;
}
}
if (low <= high) {
int cntR = mid, cntL = mid;
while (cntR < nums.length && nums[cntR] == target) {
cntR++;
}
while (cntL >= 0 && nums[cntL] == target) {
cntL--;
}
return new int[]{cntL+1, cntR-1};
}
return new int[]{-1,-1};
}
}
以上方法为原创暴力解法,以下为力扣题解方法
思路:分别找左右边界,以右边界为例,如果nums[mid]的值大于target,则有边界为mid-1;如果nums[mid]的值小于等于target,则左边界和右边界都更新为mid+1.
代码如下:
class Solution {
int[] searchRange(int[] nums, int target) {
int leftBorder = getLeftBorder(nums, target);
int rightBorder = getRightBorder(nums, target);
// 情况一
if (leftBorder == -2 || rightBorder == -2) return new int[]{-1, -1};
// 情况三
if (rightBorder - leftBorder > 1) return new int[]{leftBorder + 1, rightBorder - 1};
// 情况二
return new int[]{-1, -1};
}
int getRightBorder(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
int rightBorder = -2; // 记录一下rightBorder没有被赋值的情况
while (left <= right) {
int middle = left + ((right - left) / 2);
if (nums[middle] > target) {
right = middle - 1;
} else { // 寻找右边界,nums[middle] == target的时候更新left
left = middle + 1;
rightBorder = left;
}
}
return rightBorder;
}
int getLeftBorder(int[] nums, int target) {
int left = 0;
int right = nums.length - 1;
int leftBorder = -2; // 记录一下leftBorder没有被赋值的情况
while (left <= right) {
int middle = left + ((right - left) / 2);
if (nums[middle] >= target) { // 寻找左边界,nums[middle] == target的时候更新right
right = middle - 1;
leftBorder = right;
} else {
left = middle + 1;
}
}
return leftBorder;
}
}
给你一个非负整数 x ,计算并返回 x 的 算术平方根 。
由于返回类型是整数,结果只保留 整数部分 ,小数部分将被舍去 。
注意:不允许使用任何内置指数函数和算符,例如 pow(x, 0.5) 或者 x ** 0.5 。题目链接
思路:使用二分查找的思想
tips: 为了防止溢出,将 m*m > x 改为 x/m < m
代码如下:
class Solution {
public int mySqrt(int x) {
if (x == 1){
return 1;
}
int min = 0, max = x;
while (max - min > 1){
int m = (max + min) / 2;
if (x / m < m){
max = m;
}
else {
min = m;
}
}
return min;
}
}
给定一个 正整数 num ,编写一个函数,如果 num 是一个完全平方数,则返回 true ,否则返回 false 。
进阶:不要 使用任何内置的库函数,如 sqrt 。题目链接
思路:二分法
代码如下:
class Solution {
public boolean isPerfectSquare(int num) {
int min = 1, max = num;
int m = 0, t = 0;
while (max >= min) {
m = (max - min) / 2 + min;
t = num / m;
if (t == m) {
if(num % m == 0) {
return true;
}
min = m + 1;
}else if (t < m) {
max = m - 1;
}else {
min = m + 1;
}
}
return false;
}
}