零、查找算法
https://blog.51cto.com/u_15047490/2561082
二分查找参考:https://www.cnblogs.com/gzshan/p/12570332.html
一、旋转数组
对应牛客43题
1.题目
汇编语言中有一种移位指令叫做循环左移(ROL),现在有个简单的任务,就是用字符串模拟这个指令的运算结果。对于一个给定的字符序列 S ,请你把其循环左移 K 位后的序列输出(保证 K 小于等于 S 的长度)。例如,字符序列 S = ”abcXYZdef” , 要求输出循环左移 3 位后的结果,即 “XYZdefabc” 。是不是很简单?OK,搞定它!
数据范围:输入的字符串长度满足 ,
进阶:空间复杂度 ,时间复杂度
2.思路
注意点,要注意n大于str长度的情况,可通过取余实现
如:
输入:“aab”,10
返回值:“aba”
3.代码
//内置方法
public class Solution{
private chars reverse(char str, int n){
if (str == null || str.length()<= 1 || n <= 0 ) return str;
if ( n > str.length()) n= n%str.length();
return str.subString(n) + str.subString(0,n);
}
}
//翻转三次
public class Solution {
public String LeftRotateString(String str,int n) {
// base case
if (str == null || str.length()<= 1 || n<=0) return str;
if (n> str.length()) n= n%str.length();
//新建数组
char [] chars = str.toCharArray();
//左右翻转,然后整体翻转
reverse (chars, 0, n-1);
reverse (chars, n , str.length()-1);
reverse (chars, 0, str.length()-1);
return new String(chars);
}
private void reverse (char[] chars, int start, int end ){
while(start < end){
char temp = chars [start];
chars[start] = chars[end];
chars[end] = temp;
start ++;
end--;
}
}
}
二、二分查找
1.题目
在有序数组中查找一个特定的值target
2.思路
二分查找
- 若 target == nums[mid],直接返回
- 若 target < nums[mid],则 target 位于左侧区间 [left,mid) 中。令 right = mid-1,在左侧区间查找
- 若 target > nums[mid],则 target 位于右侧区间 (mid,right] 中。令 left = mid+1,在右侧区间查找
3.代码
public class Solution{
public boolean search(int[] nums, int target) {
if(nums==null || nums.length==0) return false;
int low=0,high=nums.length-1;
while(low<=high){
int mid=low+(high-low)/2;
if(nums[mid]==target) return true;
else if(target<nums[mid]){
high=mid-1;
}else{
low=mid+1;
}
}
return false;
}
}
三、搜索旋转排序数组
1.题目
整数数组 nums 按升序排列,数组中的值 互不相同 。
在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转,使数组变为 [nums[k], nums[k+1], …, nums[n-1], nums[0], nums[1], …, nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,5,6,7] 在下标 3 处经旋转后可能变为 [4,5,6,7,0,1,2] 。
给你 旋转后 的数组 nums 和一个整数 target ,如果 nums 中存在这个目标值 target ,则返回它的下标,否则返回 -1 。
示例
输入: nums = [4,5,6,7,0,1,2], target = 0
输出: 4
输入: nums = [4,5,6,7,0,1,2], target = 3
输出: -1
2.思路
二分查找,从任一位置进行分隔,则必有其中一半是有序的。因此,我们可以仍然从中点位置进行分隔,然后通过将最左边元素nums[low]和nums[mid]进行比较,从而可以判断出来,是左侧还是右侧是连续有序的,进而可以选择接下来继续选择哪一边进行继续查找。
1、若 target == nums[mid],直接返回
2、若 nums[left] <= nums[mid],说明左侧区间 [left,mid]「连续递增」。此时:
若 nums[left] <= target <= nums[mid],说明 target 位于左侧。令 right = mid-1,在左侧区间查找
否则,令 left = mid+1,在右侧区间查找
3、否则,说明右侧区间 [mid,right]「连续递增」。
此时:
若 nums[mid] <= target <= nums[right],说明 target 位于右侧区间。令 left = mid+1,在右侧区间查找
否则,令 right = mid-1,在左侧区间查找
3.代码
public class Solution {
public int search(int[] nums, int target) {
if(nums==null || nums.length==0)
return -1;
int low=0,high=nums.length-1;
while(low<=high){
int mid=low+(high-low)/2;
if(nums[mid]==target)
return mid;
else if(nums[low]<=nums[mid]){ //左侧连续
if(target<nums[mid] && target>=nums[low])
high=mid-1;
else
low=mid+1;
}else{ //右侧连续
if(target>nums[mid] && target<=nums[high])
low=mid+1;
else
high=mid-1;
}
}
return -1;
}
四、搜索旋转排序数组II
1.题目
已知存在一个按非降序排列的整数数组 nums ,数组中的值不必互不相同。
在传递给函数之前,nums 在预先未知的某个下标 k(0 <= k < nums.length)上进行了 旋转 ,使数组变为 [nums[k], nums[k+1], …, nums[n-1], nums[0], nums[1], …, nums[k-1]](下标 从 0 开始 计数)。例如, [0,1,2,4,4,4,5,6,6,7] 在下标 5 处经旋转后可能变为 [4,5,6,6,7,0,1,2,4,4] 。
给你 旋转后 的数组 nums 和一个整数 target ,请你编写一个函数来判断给定的目标值是否存在于数组中。如果 nums 中存在这个目标值 target ,则返回 true ,否则返回 false 。
2.思路
本题是上一题的延续,其区别在于数组中是否包含重复元素,当数组中允许元素重复时,如果nums[low]和nums[mid]是相等的,那我们就无法判断究竟是哪一边连续有序,这时我们可以让指针只移动一位,相当于排除一个元素。其余和上题基本相同。
3.代码
class Solution {
public boolean search(int[] nums, int target) {
//base case
if (nums == null || nums.length == 0 ) return false;
//二分查找
int left = 0;
int right = nums.length-1;
while ( left <= right){
int mid = left +(right - left)/2;
if (nums[mid] == target) return true;
if (nums[left] == nums[mid]) left ++;
else if (nums[left] < nums[mid]){
if (nums[left]<= target && target <nums[mid]) right = mid-1;
else{
left = mid+1;
}
}else{
if(target > nums[mid] && target <= nums[right]) left = mid+1;
else {
right = mid -1;
}
}
}
return false;
}
}