数组是存放在连续空间上的相同数据类型的集合。
注意:下标从0开始;内存空间连续。
正因为数组的内存地址空间连续,所以在删除、添加元素的时候需要移动其他元素。
数组的元素不能删除,只能覆盖!
二维数组特殊
在C++中,二位数组在内存中也是连续的,相当于多个一维数组。
void test_arr() {
int array[2][3] = {
{0, 1, 2},
{3, 4, 5}
};
cout << &array[0][0] << " " << &array[0][1] << " " << &array[0][2] << endl;
cout << &array[1][0] << " " << &array[1][1] << " " << &array[1][2] << endl;
}
int main() {
test_arr();
}
Result:
0x7ffee4065820 0x7ffee4065824 0x7ffee4065828
0x7ffee406582c 0x7ffee4065830 0x7ffee4065834
由于是int类型,所有每个之间差4字节
而在Java中,由于是由JVM处理,所以毫无规则可言。
public static void test_arr() {
int[][] arr = {{1, 2, 3}, {3, 4, 5}, {6, 7, 8}, {9,9,9}};
System.out.println(arr[0]);
System.out.println(arr[1]);
System.out.println(arr[2]);
System.out.println(arr[3]);
}
Result:
[I@7852e922
[I@4e25154f
[I@70dea4e
[I@5c647e05
练习题
1. 二分查找-力扣704(简单)
1.1 题目704. 二分查找
1.2 全闭区间写法
我们定义 target 是在一个在左闭右闭的区间里,也就是[left, right]
因为定义target在[left, right]区间,所以有如下两点:
- while (left <= right) 要使用 <= ,因为left == right是有意义的,所以使用 <=
- if (nums[middle] > target) right 要赋值为 middle - 1,因为当前这个nums[middle]一定不是target,那么接下来要查找的左区间结束下标位置就是 middle - 1
- 时间复杂度:O(log n)
- 空间复杂度:O(1)
//Java代码:
class Solution {
public int search(int[] nums, int target) {
// 避免当 target 小于nums[0] nums[nums.length - 1]时多次循环运算
if(target<nums[0]||target>nums[nums.length-1]){
return -1;
}
int left=0;
int right=nums.length-1;
while(left<=right){
int mid = left+(right-left)/2;
if(nums[mid]>target){
right=mid-1;
}else if(nums[mid]<target){
left=mid+1;
}else{
return mid;
}
}
return -1;
}
}
1.3 左闭右开
如果说定义 target 是在一个在左闭右开的区间里,也就是[left, right)
有如下两点:
- while (left < right),这里使用 < ,因为left == right在区间[left, right)是没有意义的
- if (nums[middle] > target) right 更新为 middle,因为当前nums[middle]不等于target,去左区间继续寻找,而寻找区间是左闭右开区间,所以right更新为middle,即:下一个查询区间不会去比较nums[middle]
//左闭右开
class Solution {
public int search(int[] nums, int target) {
int left=0;
int right=nums.length;
while(left<right){
int mid = left+(right-left)/2;
if(nums[mid]>target){
right=mid;
}else if(nums[mid]<target){
left=mid+1;
}else{
return mid;
}
}
return -1;
}
}
2 搜索位置-力扣35(简单)
2.1 题目35:搜索插入位置
2.2 二分解法(Java)
class Solution {
public int searchInsert(int[] nums, int target) {
//二分法
int left = 0;
int right = nums.length-1;
while(left<=right){
int mid = left+(right-left)/2;
if(target==nums[mid]){
return mid;
}else if(target<nums[mid]){
right=mid-1;
}else if(target>nums[mid]){
left=mid+1;
}
}
return right+1;
}
}
2.3 C解法
class Solution {
// lower_bound 返回最小的满足 nums[i] >= target 的 i
// 如果数组为空,或者所有数都 < target,则返回 nums.size()
// 要求 nums 是非递减的,即 nums[i] <= nums[i + 1]
// 闭区间写法
int lower_bound(vector<int>& nums, int target) {
int left = 0, right = (int) nums.size() - 1; // 闭区间 [left, right]
while (left <= right) { // 区间不为空
// 循环不变量:
// nums[left-1] < target
// nums[right+1] >= target
int mid = left + (right - left) / 2;
if (nums[mid] < target) {
left = mid + 1; // 范围缩小到 [mid+1, right]
} else {
right = mid - 1; // 范围缩小到 [left, mid-1]
}
}
return left;
}
// 左闭右开区间写法
int lower_bound2(vector<int>& nums, int target) {
int left = 0, right = nums.size(); // 左闭右开区间 [left, right)
while (left < right) { // 区间不为空
// 循环不变量:
// nums[left-1] < target
// nums[right] >= target
int mid = left + (right - left) / 2;
if (nums[mid] < target) {
left = mid + 1; // 范围缩小到 [mid+1, right)
} else {
right = mid; // 范围缩小到 [left, mid)
}
}
return left;
}
// 开区间写法
int lower_bound3(vector<int>& nums, int target) {
int left = -1, right = nums.size(); // 开区间 (left, right)
while (left + 1 < right) { // 区间不为空
// 循环不变量:
// nums[left] < target
// nums[right] >= target
int mid = left + (right - left) / 2;
if (nums[mid] < target) {
left = mid; // 范围缩小到 (mid, right)
} else {
right = mid; // 范围缩小到 (left, mid)
}
}
return right;
}
public:
int searchInsert(vector<int>& nums, int target) {
return lower_bound(nums, target); // 选择其中一种写法即可
}
};
3 查找元素第一位和最后一位的下标-力扣34(中等)
3.1 题目:34在排序数组中查找元素的第一个和最后一个位置
3.2 解法JAVA
寻找target在数组里的左右边界,有如下三种情况:
- 情况一:target 在数组范围的右边或者左边,例如数组{3, 4, 5},target为2或者数组{3, 4, 5},target为6,此时应该返回{-1, -1}
- 情况二:target 在数组范围中,且数组中不存在target,例如数组{3,6,7},target为5,此时应该返回{-1, -1}
- 情况三:target 在数组范围中,且数组中存在target,例如数组{3,6,7},target为6,此时应该返回{1, 1}
//JAVA版本
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;
}
}
3.2 解法C
//C嘎嘎版本
class Solution {
public:
vector<int> searchRange(vector<int>& nums, int target) {
int leftBorder = getLeftBorder(nums, target);
int rightBorder = getRightBorder(nums, target);
// 情况一
if (leftBorder == -2 || rightBorder == -2) return {-1, -1};
// 情况三
if (rightBorder - leftBorder > 1) return {leftBorder + 1, rightBorder - 1};
// 情况二
return {-1, -1};
}
private:
int getRightBorder(vector<int>& nums, int target) {
int left = 0;
int right = nums.size() - 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(vector<int>& nums, int target) {
int left = 0;
int right = nums.size() - 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;
}
};
4 平方根-力扣69(简单)
4.1 题目:x 的平方根
4.2 二分法求解
class Solution {
public int mySqrt(int x) {
int left=0;
int ret=-1;
int right=x;
while(left<=right){
int mid = left+(right-left)/2;
if((long)mid*mid<=x){
ret = mid;
left=mid+1;
}else{
right=mid-1;
}
}
return ret;
}
}
5 有效完全平方数-力扣367(简单)
5.1 题目:367. 有效的完全平方数
5.2 暴力与二分
//暴力解法
class Solution {
public boolean isPerfectSquare(int num) {
long x=1;
long sq=1;
while(sq<=num){
if(sq==num){
return true;
}
x++;
sq=x*x;
}
return false;
}
}
class Solution {
public boolean isPerfectSquare(int num) {
//二分
int left=0;
int right=num;
while(left<=right){
int mid = left+(right-left)/2;
long sq = (long) mid*mid;
if(sq<num){
left=mid+1;
}else if(sq>num){
right=mid-1;
}else {
return true;
}
}
return false;
}
}