(注:java语言不存在指针,所以程序员也无法获取元素的地址,寻址操作由虚拟机执行完成)
算法描述:二分查找又称为折半查找,目的是在有序数组之中查找特定的元素,通过逐步缩小查找范围,可以将时间复杂度从O(n)降低到O(logn)。
力扣题目连接
题目描述:
给定一个 n 个元素有序的(升序)整型数组 nums 和一个目标值 target ,写一个函数搜索 nums 中的 target,如果目标值存在返回下标,否则返回 -1
解析:通过题目之中的:数组+有序+一个目标值,我们可以快速得出这道题目暴力法的时间复杂度为O(n)。使用二分查找则会降低本题目的时间复杂度至O(logn)
使用暴力法解决问题的代码为:
class Solution{
public int search(int[] nums, int target) {
// 使用一个循环用于遍历数组nums
for(int i = 0 ; i < nums.length ; i++){
if(nums[i] == target)
return i;
}
return -1;
}
}
使用暴力法,我们需要逐个的去访问数组之中每个元素的下标,并判断下标所对应的数组元素的值是否与目标值相同,若相同,则返回该元素的下标。遍历完整个数组也未发现目标值,则返回-1。上述暴力法的时间复杂度为:O(n)。
根据二分法的区间定义(左闭右开,左闭右闭),二分法的写法分为两种。
左闭右开意味着我们对rigth的赋值比数据下标范围大1,对于数组nums,它的第一个元素的下标应该为0,最后一个元素的下标应该为nums.length-1。如果我们定义区间为[0,nums.length),当left==right的时候,[left,right)是没有意义的,所以我们设定循环的跳出条件为while(left
左闭右开对应的代码的写法为:
//左闭右开代码写法
class Solution{
public int search(int[] nums , int target){
int left = 0;
int right = nums.length;
while(left < right){
int mid = ( left + right ) / 2;
if(nums[mid] < target){
left = mid + 1 ;
}
if(nums[mid] > target ){
right = mid;
}
if(nums[mid] == target)
return mid;
}
return -1;
}
}
左开右开则代表给左指针和右指针的赋值在数组之中均存在与之对应的元素。此时的取值范围为[0,nums.length-1],此时left==right是存在实际意义的,所以我们设定循环的跳出条件为while(left<=right),此时对应的代码为:
//左闭右闭代码写法
class Solution{
public int search(int[] nums , int target){
int left = 0;
int right = nums.length - 1;
while(left <= right){
int mid = ( left + right ) / 2;
if(target > nums[mid])
left = mid + 1;
if(target < nums[mid])
right = mid - 1;
if(nums[mid] == target)
return mid;
}
return -1;
}
}
力扣题目链接
题目描述:给你一个数组 nums 和一个值 val,你需要 原地 移除所有数值等于 val 的元素。元素的顺序可能发生改变。然后返回 nums 中与 val 不同的元素的数量。
由于数组的元素在内存之中地址是连续的,所以我们不能直接删除某个元素,而是将其覆盖。
对于本题,我分别使用暴力法和双指针法解决:
思路:为了实现对目标数的查询,我们首先使用单个for循环对数组进行遍历。若发现数组之中某个元素(x)的值和val相同,则进入另一层for循环,将数组的全体元素从x向前挪一位(将x去除),同时让外层循环变量减一(因为原本的x被x后面的元素替换了),且数组长度减一。
//暴力法
class Solution{
public int removeElement(int[] nums , int val ){
int size = nums.length;
for(int i = 0 ; i < size ; i++){
if(nums[i] == val){
for(int j = i + 1 ; j < size ; j++){
nums[j - 1] = nums[j];
}
i --;
size --;
}
}
// System.out.println(size);
return size;
}
}
思路:双指针法需要定义块指针fastIndex和慢指针slowIndex,fastIndex用于寻找所有不是目标元素的元素,slowIndex则是对所有非目标元素的新的下标生成。
//双指针法
class Solution{
public int removeElement(int[] nums , int val){
int slowIndex = 0;
int size = nums.length;
for(int fastIndex = 0; fastIndex < size ; fastIndex++){
if(nums[fastIndex] != val){
nums[slowIndex] = nums[fastIndex];
slowIndex ++ ;
}
}
return slowIndex;
}
}
思路:定义一个左指针leftIndex和一个右指针rightIndex,将leftIndex自增,用于寻找和目标值val相同的值,每当nums[lestIndex]=val,令nums[leftIndex]=nums[right](将该目标值用rightIndex指向的值代替),随后rightIndex–,并判断nums[rightIndex]是否等于val,若是,right - -。
代码:
// 相向双指针
class Solution{
public int removeElement(int[] nums , int val){
int leftIndex = 0;
int rightIndex = nums.length - 1;
while(rightIndex>=0 && nums[rightIndex] == val) rightIndex--;
while(leftIndex <= rightIndex){
if(nums[leftIndex] == val){
nums[leftIndex] = nums[rightIndex];
rightIndex -- ;
}
leftIndex ++;
while(rightIndex>=0 && nums[rightIndex] == val) rightIndex--;
}
return leftIndex;
}
}
力扣题目链接
题目描述:给你一个按 非递减顺序 排序的整数数组 nums,返回 每个数字的平方 组成的新数组,要求也按 非递减顺序 排序。
思路:本题的数组之中的元素是从小到大排列的,可能会出现负数,但是负数在求平方和之后可能比原本的正数的平方和更大,因此我们使用双指针法来,左指针指向数组的第一个元素(数组最小元素),右指针指向最后一个元素(数组最大元素),定义一个index,index为数组的长度,从最高位开始判断数据的左指针和右指针对应的平方和大小,每次将最大的值更新到index对应的数组位置之中。
//双指针法
class Solution{
public int[] sortedSquares(int[] nums) {
//双指针左闭右闭
int left = 0;
int right = nums.length - 1;
int index = nums.length - 1;
int[] result = new int[nums.length];
while(left <= right){
int valueOfLeft = numSqrt(nums[left]);
int valueOfRight = numSqrt(nums[right]);
if(valueOfLeft >= valueOfRight){
result[index] = valueOfLeft;
left ++;
index -- ;
}else{
result[index] = valueOfRight;
index -- ;
right --;
}
}
return result;
}
public int numSqrt(int num){
return num*num;
}
}
//暴力法
class Solution{
public int[] sortedSquares(int[] nums){
int[] result = new int[nums.length];
for(int i = 0 ; i < nums.length ; i++){
result[i] = numSqrt(nums[i]);
}
Arrays.sort(result);
return result;
}
public int numSqrt(int num){
return num*num;
}
}