双指针
(2)633. 平方数之和
双指针去寻找两数平方和未target
注意r起点为Math.sqrt(target),因为target可为int中最大值2^31-1故,l^2+r^2可出现大于int。
注意用long存储减少溢出
public boolean judgeSquareSum(int c) {
long l=0,r=(long)Math.sqrt(c);
//l,r也需要用long存储,不然会计算结果为int,再存给long那就已经被劫
while(l<=r){
long num = l*l+r*r;
if(num<c){
l++;
}
else if(num>c){
r--;
}
else{
return true;
}
}
return false;
}
/*
几点:String 可利用indexOf确定是否包含该字符。不包含返回-1
String 可toCharArray()转化成char[]
String(char[])可传入字符数组进行构造
*/
import java.util.*;
class Solution {
public String reverseVowels(String s) {
String re ="aAeEiIoOuU";
char[] str = s.toCharArray();
int i = 0,j=str.length-1;
while(i<j){
while(i<j&&re.indexOf(str[i])<0)i++;
while(i<j&&re.indexOf(str[j])<0)j--;
if(i<j){
char t = str[i];
str[i] = str[j];
str[j] = t;
i++;
j--;
}
}
return new String(str);
}
}
/*
sign标记是否为第一次出现不相等
即不相等(1)非第一次不相等 false
(2)第一次不相等,验证 l+1与r是否匹配,匹则过滤掉l
验证l与r-1是否匹配,匹则过滤掉r
无 false
*/
public boolean validPalindrome(String s) {
int l=0,r=s.length()-1;
int sign = 0;
while(l<r){
if(s.charAt(l)==s.charAt(r)){
l++;
r--;
}
else{
if(sign==1)
return false;
else{
sign = 1;
if(s.charAt(l+1)==s.charAt(r))
l++;
else if(s.charAt(l)==s.charAt(r-1))
r--;
else{
return false;
}
}
}
}
return true;
}
class Solution {
//因为要长度最长,且长度相等时字符序最小串
/*
长度大于当前则直接更换
长度与当前相等且新串序小于当前则替换(String 比较的方法compareTo())
*/
public String findLongestWord(String s, List<String> dictionary) {
String res = "";
for(String str :dictionary){
if(isSub(s,str)){
if(str.length()>res.length()||(str.length()==res.length()&&res.compareTo(str)>0)){
res = str;
}
}
}
return res;
}
//判断是否为字串
//t是否为s的字串:相等ti++,si++;不相等si++ 最终t遍历完则是字串
public boolean isSub(String s, String t){
int si = 0, ti = 0;
while(si<s.length()&&ti<t.length()){
if(s.charAt(si)==t.charAt(ti)){
si++;
ti++;
}
else
si++;
}
if(ti>=t.length())
return true;
return false;
}
}
排序
class Solution {
public int findKthLargest(int[] nums, int k) {
return findK(nums,nums.length-k,0,nums.length-1);
}
public int findK(int[] nums,int k, int l, int r){
//要记录左右边界
int el=l,er=r;
int mid = nums[r];
while(l<r){
while(l<r&&nums[l]<mid) l++;
//每次确定一个位置后,下表要改变。
if(l<r)
nums[r--] = nums[l];
while(l<r&&nums[r]>mid) r--;
if(l<r)
nums[l++] = nums[r];
}
nums[l] = mid;
if(l==k)
return mid;
else if(l<k)
return findK(nums,k,l+1,er);
else
return findK(nums,k,el,l-1);
}
}
class Solution {
/*
比较器的结构
Comparator<T> com = new Comparator<T>(){
public int compare(T o1,T o2){
return 0;
}
};
对于可单个控制的空用
Queue<Map.Entry<Integer,Integer>> queue =
new PriorityQueue<Map.Entry<Integer,Integer>>
((o1,o2)->(o1.getValue()-o2.getValue()));
*/
public int[] topKFrequent(int[] nums, int k) {
Map<Integer,Integer> map = new HashMap<Integer,Integer>();
for(int i = 0;i<nums.length;i++)
map.put(nums[i],map.getOrDefault(nums[i],0)+1);
Comparator<Map.Entry<Integer,Integer>> com = new Comparator<Map.Entry<Integer,Integer>>(){
public int compare(Map.Entry<Integer,Integer> o1,Map.Entry<Integer,Integer> o2){
return o1.getValue()-o2.getValue();
}
};
Queue<Map.Entry<Integer,Integer>> queue = new PriorityQueue<Map.Entry<Integer,Integer>>(com);
for(Map.Entry<Integer,Integer> e : map.entrySet()){
if(queue.isEmpty()||queue.size()<k)
queue.add(e);
else{
if(queue.peek().getValue()<e.getValue()){
queue.poll();
queue.add(e);
}
}
}
int[] res = new int[queue.size()];
int index = 0;
while(!queue.isEmpty()){
res[index++] = queue.poll().getKey();
}
return res;
}
}
Map 和 优先队列的配合使用
class Solution {
public String frequencySort(String s) {
Map<Character,Integer> map = new HashMap<Character,Integer>();
for(int i=0;i<s.length();i++){
map.put(s.charAt(i),map.getOrDefault(s.charAt(i),0)+1);
}
Queue<Map.Entry<Character,Integer>> queue = new PriorityQueue<Map.Entry<Character,Integer>>((o1,o2)->(o2.getValue()-o1.getValue()));
for(Map.Entry<Character,Integer> e:map.entrySet()){
queue.add(e);
}
StringBuilder str = new StringBuilder();
while(!queue.isEmpty()){
Map.Entry<Character,Integer> e = queue.poll();
char c = e.getKey();
for(int i =0;i<e.getValue();i++){
str.append(c);
}
}
return str.toString();
}
}
(9)75. 颜色分类
数组三区间划分
index指向当前待确定位置的元素,l指向第一个分组已有序的后一个位置,r指向已有序的前一个位置。
已全为0 | l | r | 已全为2 |
(1)index位置元素0时,需要将其替换到 l位置,l++,因为就算交换也是前面已遍历过的元素。故替换过来的一定是1。所以index++;
(2)index位置元素为2时,需要与r位置元素交换,交换过来的为未遍历过的元素故index不变,要对此位置元素再次估算其位置
(3)index位置元素为1时,不用做任何交换。index++;
class Solution {
public void sortColors(int[] nums) {
int l = 0, r = nums.length-1;
int index = 0;
while(index<=r){
if(nums[index]==0){
swap(nums,index,l);
l++;
index++;
}
else if(nums[index]==2){
swap(nums,index,r);
r--;
}else
index++;
}
}
public void swap(int[] nums, int a, int b){
int t = nums[a];
nums[a] =nums[b];
nums[b] = t;
}
}
贪心算法
(10)455. 分发饼干
这里运用的思想,经过排序后,每次从饼干列表选一个最优解(当前最小可满足当前孩子胃口的饼干)
这里注意,因为经过了排序,下一个孩子胃口一定比当前大,故下次寻找饼干,直接从当前最优解下一个开始找
public int findContentChildren(int[] g, int[] s) {
Arrays.sort(g);
Arrays.sort(s);
int index = 0;
int count=0;
for(int i = 0;i<g.length;i++){
while(index<s.length&&s[index]<g[i]) index++;
if(index<s.length){
count++;
index++;
}
}
return count;
}
(11)435. 无重叠区间
用贪心解决(计算最多可加入多少序列,总长减去最多可加入既最少可删除)
本着选择end值越小,后面就可以有更多可能加入更多的序列。
故每次用s,e更新最新加入的序列的开始与结尾,当前一个序列的开始小于当前e则说明有重叠。
public int eraseOverlapIntervals(int[][] intervals) {
Arrays.sort(intervals,new Comparator<int[]>(){
public int compare(int[] o1,int[] o2){
return o1[1]-o2[1];
}
});
int count = 1;
int s = intervals[0][0],e = intervals[0][1];
for(int i = 1;i<intervals.length;i++){
if(intervals[i][0]<e)
continue;
else{
s = intervals[i][0];
e = intervals[i][1];
count++;
}
}
return intervals.length-count;
}
解题思路:
在安排位置时应先安排身高高的,因为低个插入高个前不会影响高个前比他高的个数。对于身高相同的,要先安排k小的,因为k大一定在k小之后。
所以先按身高从高到低,身高相同,k从小到大排序。
一次插入队列,将元素一次插入第k个位置。因为后插的前面的元素一定都大于他,所以插在k位置可保证前面有k个值大于他。原来k位置的元素不会影响她们,因为新插入的h都比前面插入的小故不影响已插入元素前的高个个数。
具体步骤:
我们先按照身高从大到小排序(身高相同的情况下K小的在前面),这样的话,无论哪个人的身高都小于等于他前面人的身高。所以接下来只要按照K值将他插入相应的位置就可以了。
例如:示例1排完序:[[7,0],[7,1],[6,1],[5,0],[5,2],[4,4]]
新建一个一个队列:
[7,0]插入第0的位置
[7,1]插入第1的位置
[6,1]插入第1的位置,这时[7,1]就往后移一位了
class Solution {
public int[][] reconstructQueue(int[][] people) {
Arrays.sort(people,new Comparator<int[]>(){
public int compare(int[] o1, int[]o2){
return (o1[0]==o2[0])?o1[1]-o2[1]:o2[0]-o1[0];
}
});
List<int[]> queue = new LinkedList<int[]>();
for(int i=0;i<people.length;i++){
queue.add(people[i][1],people[i]);
}
//注意这里如何返回数组
return queue.toArray(new int[queue.size()][]);
}
}
/*
toArray() 方法的语法为:
arraylist.toArray(T[] arr)
注:arraylist 是 ArrayList 类的一个对象。
参数说明:
T [] arr(可选参数)- 用于存储数组元素的数组
注意:这里 T 指的是数组的类型。
返回值
如果参数 T[] arr 作为参数传入到方法,则返回 T 类型的数组。
如果未传入参数,则返回 Object 类型的数组。
*/
(13)121. 买卖股票的最佳时机
因为每个时间段只能持有一张股票,实际就是能将可获取收益的段加入。eg 1 ,2 ,3 (2-1+3-2=3-1)
即prices[i]-prices[i-1]>0,即该段时间可以获得收益,此段可以持有
class Solution {
public int maxProfit(int[] prices) {
int max = 0;
for(int i = 1;i<prices.length;i++){
if(prices[i]-prices[i-1]>0)
max += prices[i]-prices[i-1];
}
return max;
}
}
(15)605. 种花问题
用pre,next去确定是否该位置可以种花。均为0则可种
如果当前下表为0,说明前面无影响即pre为0,若下标>0则要看前一位是否为0,为了pre=0;
如果当前下表为length-1,说明后面无影响即next为0,若下标<length-1则要看后一位是否为0,为了pre=0;
当前后均为0则此位置可以种花。将此位置置1种上花。
注意n=0情况,可能没有又个位置可种花但是n=0故也为true;
class Solution {
public boolean canPlaceFlowers(int[] flowerbed, int n) {
int cnt = 0;
for(int i = 0;i<flowerbed.length;i++){
if(flowerbed[i]!=1){
int pre = i==0?0:flowerbed[i-1];
int next = i==flowerbed.length-1?0:flowerbed[i+1];
if(pre==0&&next==0){
cnt++;
flowerbed[i] =1;
}
}
if(cnt>=n)
return true;
}
return false;
}
}
(16)392. 判断子序列
(17)665. 非递减数列
修改一个数的值时期非递减
当i对应值>i+1时,考虑用i+1替换i还是用i替换i+1
需要跟i-1比较,如果i+1>=i-1,应用i+1替换i
如果i+1<i-1,应用i替换i+1
注意对第一位进行特殊处理
class Solution {
public boolean checkPossibility(int[] nums) {
if(nums.length==1)
return true;
boolean sign = nums[0]>nums[1]?false:true;
for(int i=1;i<nums.length-1;i++){
if(nums[i]>nums[i+1]){
if(sign){
if(nums[i+1]>=nums[i-1]){
nums[i]=nums[i+1];
}
else
nums[i+1] =nums[i];
sign = false;
}
else
return false;
}
}
return true;
}
}
(18)53. 最大子数组和
当sum+当前比当前还小时就应该从当前为起点重新开始算