数组中的问题
关于数组
1、数组应用场景
排序:选择排序、插入排序、归并排序、快速排序
查找:二分查找
数据结构:栈、队列、堆
2、如何写出正确的程序
明确变量和含义;循环不变量;小数据量调试
举例:二分查找法:找中间,再往左或右序列中寻找。
对于有序数列,才能使用二分查找法
public static int binarySearch(Comparable[] arr, int n, Comparable target){
int l = 0, r = n - 1; // 在[l...r]的范围里寻找target
while(l <= r){ // 当 l == r时,区间[l...r]依然是有效的
int mid = l + (r - l) / 2;
if(arr[mid].compareTo(target) == 0) return mid;
if(target.compareTo(arr[mid]) > 0)
l = mid + 1; // target在[mid+1...r]中; [l...mid]一定没有target
else // target < arr[mid]
r = mid - 1; // target在[l...mid-1]中; [mid...r]一定没有target
}
return -1;
}
循环中的循环不变性
改变变量定义,依然可以写出正确的代码:
//上述代码中,若对边界的定义改变:
int l=0,r=n;//意味着在[l……r)的范围里寻找target
while(l<r){//当l==r时,区间[l……r)是无效的
int mid=l+(r-l)/2;//避免整型溢出问题
if(arr[mid].compareTo(target)==0)return mid;
if(target.compareTo(arr[mid]) > 0)
l=mid+1;//target在[mid+1...r)中寻找
else
r=mid;//target在[l...mid)中
}
Move Zeros
leetcode283:将数组中所有的0挪到数组的末尾,而维持其他所有非0元素的相对位置。
思路:扫描数组,将所有非0元素选出,填补到数组前面,其他元素变为0.
方法一:开辟一个新的数组存放非0元素,空间复杂度为O(n)
public void moveZeroes(int[] nums) {
ArrayList<Integer> nonZeroElements=new ArrayList<>();
for(int i=0;i<nums.length;i++) {
if(nums[i]!=0)
nonZeroElements.add(nums[i]);
}
for(int i=0;i<nonZeroElements.size();i++)
nums[i]=nonZeroElements.get(i);
for(int i=nonZeroElements.size();i<nums.length;i++)
nums[i]=0;
}
方法二:设置新的index:k,指示非0元素按顺序排列在[0…k)中,空间复杂度为O(1)
方法三:将k和i位置的元素进行交换,i指示非0元素
public void moveZeroes(int[] nums) {
int k=0;
for(int i=0;i<nums.length;i++)
if(nums[i]!=0)
swap(nums,k++,i);
}
练习:27,26,80
基础算法思路的应用
75、Sort Colors:数组中元素取值只有012三种可能,进行原地排序。
没有思路,尝试暴力解法。再想是否能做的更好。普通排序O(nlogn)
(1)计数排序:分别统计0,1,2的个数,然后放回数组。O(n)
错误处理:assert…
(2)三路快速排序
public void sortColors(int[] nums) {
int zero=-1;// [0...zero] == 0
int two=nums.length; // [two...n-1] == 2
for(int i=0;i<two;) {
if(nums[i]==1)
i++;
else if(nums[i]==2) {//else if!!
swap(nums,i,--two);
}
else {
assert nums[i] == 0;
swap(nums, ++zero, i++);//注意i++
}
}
}
private void swap(int[] nums, int i, int j){
int t = nums[i];
nums[i]= nums[j];
nums[j] = t;
}
练习:88,215
88:归并排序中的归并。
215:寻找K大元素。利用快排partition,将pivot放置在了其正确的位置上。
对撞指针
167:在有序数组中寻找两个元素,使其和为target,返回索引。
是否可以使用相同元素?返回索引从0还是1开始?多个解怎么办?没有解怎么办?
题目保证一定有解且有唯一解。
(1)暴力解法O(n2),没有充分利用原数组的性质–有序
(2)有序?二分搜索?O(nlogn)
public int[] twoSum(int[] numbers,int target){
if(numbers.length<2||!isSorted(numbers))...
for(int i=0;i<numbers.length-1;i++){
int j=binarySearch(numbers,i+1,numbers.length-1,target-numbers[i]);//why i+1;
if(j!=-1){
int[] res={i+1;j+1};
return res
}
}
}
private int binarySearch(int[] nums,int l,int r,int target)
(3)O(n)对撞指针
nums[i]+nums[j]==target找到了!
nums[i]+nums[j]<target i++
nums[i]+nums[j]<target j–
public int[] twoSum(int[] numbers, int target) {
int l=0;
int r=numbers.length-1;
while(l<r) {
if(numbers[l]+numbers[r]==target) {
int[] res= {l+1,r+1};//下标从1开始
return res;
}
else if(numbers[l]+numbers[r]<target)
l++;
else
r--;
}
throw new IllegalStateException("The input has no solution");
}
练习
125、回文串
空字符串是否看做回文串?字符的定义?大小写问题
344、倒序字符串
345、元音字母翻转
11、Container with most water
双索引技术:滑动窗口
209、Minimum Size Subarray Sum
给定整型数组和数字s,找到数组中最短的一个连续子数组,使得连续子数组的数字和sum>=s,返回长度值。
-
什么叫子数组
-
没有解怎么办?返回0
(1)暴力解O(n3):遍历所有的连续数组
(2)双索引
确定三点:
1)窗口内是什么?满足和>=s的长度最小的连续子数组
2)如何移动窗口的起始位置?窗口内值>s,则窗口向前移动
3)如何移动窗口的结束位置?窗口的结束位置就是遍历数组的指针
思路:
使用 left、right 两指针,分别表示窗口的左右边界。
移动右指针,扩大窗口,直到子数组达到目标值 target。
移动左指针,缩小窗口,直到子数组不满足目标值。
// 时间复杂度: O(n)
// 空间复杂度: O(1)
public int minSubArrayLen(int s,int[] nums) {
if(s <= 0 || nums == null)
throw new IllegalArgumentException("Illigal Arguments");
int l=0,r=-1;//nums[l...r]为我们的滑动窗口
int sum=0;
int res=nums.length+1;
while(l<nums.length) {
if(r+1<nums.length&&sum<s) {//r到达最右侧不能再拓展了
sum+=nums[++r];//右边界向前拓展
}
else {
sum-=nums[l++];
}
if(sum>=s)
res=Math.min(res,r-l+1);
}
if(res==nums.length+1)
return 0;
return res;
}
T3、Longest Substring without repeating characters
在一个字符串中寻找没有重复字母的最长子串。
滑动窗口:
a) 两个指针,代表左右边界
b) 每一步迭代,左指针+1;不断移动右指针
c) 左右指针都是只往前进的
问题:
字符集?只有字母?数字+字母?ASCII?
大小写是否敏感?
如何记录重复字符?freq[256]或者使用Set
public int lengthOfLongestSubstring(String s) {
int[] freq=new int[256];
int l=0,r=-1;
int res=0;
while(l<s.length()) {
if(r+1<s.length()&&freq[s.charAt(r+1)]==0) {
freq[s.charAt(++r)]++;
}
else {
freq[s.charAt(l)]--;
l++;
}
res=Math.max(res, r-l+1);
}
return res;
}
练习(滑动窗口):
438:find all anagrams in a string
字符集?英文小写字母
返回解的顺序?任意
76(hard):
字符集?
没有解?
多个解?