数组理论基础
数组的定义
数组是存放在连续内存空间上的相同类型数据的集合。
数组的特点
- 数组下标都是从0开始的。
- 数组内存空间的地址是连续的
- 正是因为数组的在内存空间的地址是连续的,所以我们在删除或者增添元素的时候,就难免要移动其他元素的地址。
数组的元素是不能删的,只能覆盖。
不同编程语言的内存管理是不一样的
C++
在C++中二维数组是连续分布的。
JAVA
在Java中二维数组是非连续分布的。
leetcode 704.二分查找
题目链接 : 704.二分查找
解题思路
思路一(暴力解法)
我第一感觉是通过for()对数组进行遍历,对数组中每个数与target进行比较,然后输出下标
class Solution {
public int search(int[] nums, int target) {
for(int i = 0; i < nums.length; i++){
if(nums[i] == target){
return i;
}
}
return -1;
}
}
时间复杂度: O(n);
空间复杂度: O(1);
思路二(二分法—左闭右闭)
由于思路一的解法时间复杂度为O(n),为了减少时间复杂度
分析题目:
1. 升序数组
2. 无重复值
我们可以从中间开始判断,会有一下三个结果:
1.sum[mid] < target 数组为升序数组,所以target只会存在于(mid + 1, l)
2.sum[mid] > target 数组为升序数组,所以target只会存在于(r, mid - 1)
3.sum[mid] = target 所以target = mid;
class Solution {
public int search(int[] nums, int target) {
int l = 0;
int r = nums.length - 1;
while(l <= r){
int mid = (r + l) / 2 ;
if(nums[mid] < target){
l = mid + 1;
}else if(nums[mid] > target){
r = mid - 1;
}else{
return mid;
}
}
return -1;
}
}
时间复杂度: O(log(n));
空间复杂度: O(1);
思路三(二分法—左闭右开)
与第二种思路不同的地方在于区间的不同,解释如图:
左闭右闭:
左闭右开:
class Solution {
public int search(int[] nums, int target) {
int l = 0;
int r = nums.length;
while(l < r){
int mid = (l + r) / 2;
if(nums[mid] < target){
l = mid + 1;
}else if(nums[mid] > target){
r = mid;
}else{
return mid;
}
}
return -1;
}
}
注意:
1.在左闭右开算法中,我们没有将 l==r 考虑进去,所以我们需要考虑在最后存在两个值时两个值都已经进行了判断,但是mid值在(l+r)为奇数时,mid会取左边小的值,最小的值就会进行判断,所以在最后只存在两个值时,右边的值一定进行了判断,而右边的值不一定进行了判断,我们就将已经判断的值(mid)赋予r,所以对右边的值的选取我采取 r = mid,而左边值的选取采取 l = mid + 1。
2.但是上述方法存在一种情况无法解决,当右边值一直没有发生改变,右边值就没有进行判断,解决措施就是将r = num,length。
左闭右闭与左闭右开的区别
两者的根本的不同在于对区间的定义
左闭右闭:while( l <= r ) l == r 是有意义的,我们循环时可以将r的值mid+1,mid+1未知,我们循环最后会将 l == r 进行比较。
左闭右开:while(l < r) l == r 是没有意义的,我们循环时可以将mid 赋值给 r,因为mid已知,不需要在考虑 l == r的情况。
leetcode 27. 移除元素
题目链接:27.移除元素
解题思路
思路一(暴力解法):
首先想到的将val从数组中剔除,但是数组无法直接将值直接删除,只能通过覆盖,将要删除的数,直接将后面的值全部往前移一位,然后将数字的长度减小。
class Solution {
public int removeElement(int[] nums, int val) {
int result = nums.length;
for(int i = 0; i < result; i++){
if(nums[i] == val){
for(int j = i; j < result - 1; j++){
nums[j] = nums[j + 1];
}
result--;
i--;
}
}
return result;
}
}
思路二(双指针—快慢双指针):
我们可以利用两个指针对数组进行改变,快指针对数组进行遍历如果num[fast]==val,我们将slow指针就不动,直到不相等时,将fast的值赋予slow。
class Solution {
public int removeElement(int[] nums, int val) {
int slow = 0;
for(int fast = 0; fast < nums.length; fast++){
if(nums[fast] != val){
nums[slow] = nums[fast];
slow++;
}
}
return slow;
}
}
思路三(双指针)
使用两个指针(left,right)分别在数组左右两端,从left开始遍历如果num[left] == target,就将right与left的值进行替换,在对进行交换的值进行判断,如果等于target就将right-1与他交换,以此类推直到left > right结束。
class Solution {
public int removeElement(int[] nums, int val) {
int l = 0;
int r = nums.length - 1;
int res = nums.length;
while(l <= r){
if(nums[l] == val){
int temp = nums[l];
nums[l] = nums[r];
nums[r] = temp;
r--;
l--;
res--;
}
l++;
}
return res;
}
}
今天的收获
1.对数组的一些定义进行巩固。
2.通过704.二分查找 学会如何使用二分法,还有二分法的两种方式和其区别。
3.通过27.移除元素 学会如何使用双指针。
感想
今天是我第一天写博客,花的时间比较长,但是今天的收获也很多,完成了博客也让我成就感满满,我希望我能一直坚持到最后一天,加油!!!