数组有两种写法:
int [] a={3,4};
int [] b=new int[]{3,4};
/*返回的话
return new int[]{2,3}; //正确
return b;//一个定义好的数组类型
return new {3,4};//错误
*/
使用Math.pow(c,2)函数会增加消耗内存,不如直接c*c直接运算
HashSet<Character> vowels = new HashSet<>(
Arrays.asList('a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O','U'));
Set<Character> set=new HashSet<>();
for(char i:cha)
set.add(i);
char[] sb=s.toCharArray();
sb.toString()//返回的不是字符串
String.valueOf(sb);
StringBuffer和StringBuilder
String中的对象是不可变得,理解为常量,线程安全。AbstractStringBuilder是StringBuilder与StringBuffer的公共父类,定义了字符串的一些基本操作。如indexof,insert,append,expandCapacity,StringBuffer对方法加了同步锁或对调用的方法加了同步锁,所以是线程安全的。StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。
性能:String类型进行改变的时候,都会生成一个新的String对象,然后将指针指向新的String对象。StringBuffer是对对象本身进行操作。
StringBuilder用法// Collections方法
有reverse字符串功能
toString方法为返回本身字符串
set集合// 快慢指针
自己解答未用到双指针
自己傻逼啦
冒泡排序 选择 插入 希尔
快排 归并 堆排序
计数排序 桶排序
非递归一般用栈解决
堆排序
大顶堆:arr[i] >= arr[2i+1] && arr[i] >= arr[2i+2]
小顶堆:arr[i] <= arr[2i+1] && arr[i] <= arr[2i+2]
堆排序的基本思想是:将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了
大顶堆为升序排序,小顶堆为降序排序
要学会把每次排序过程剥离出来
判断partition的位置,然后与k对比,选择性执行相应部分的快速排序
Java中PriorityQueue通过二叉小顶堆实现
PriorityQueue<Integer> pq = new PriorityQueue<>(); // 小顶堆
for (int val : nums) {
pq.add(val);
上次map用法,转化成list排序
另一种思路
将map的value作为变量写入**list数组**
List<Integer>[] buckets = new ArrayList[nums.length + 1];
for (int key : frequencyForNum.keySet()) {
int frequency = frequencyForNum.get(key);
if (buckets[frequency] == null) {
buckets[frequency] = new ArrayList<>();
}
buckets[frequency].add(key);
}
雷同
其实是三向切分快速排序的一种变种,在三向切分快速排序中,每次切分都将数组分成三个区间:小于切分元素、等于切分元素、大于切分元素
class Solution {
public void sortColors(int[] nums) {
int left=0,mid=0,right=nums.length-1;
while(mid<=right){
System.out.println(left+" "+mid+" "+right);
if(nums[mid]==0){
swap(nums,left++,mid++);
}else if(nums[mid]==2){
//从后面往前置换一定mid不能往前加,一定要判断置换后的mid元素
swap(nums,mid,right--);
}else{
mid++;
}
}
}
public static void swap(int[] nums,int left,int right){
// System.out.println(left+" "+right);
int temp=nums[left];
nums[left]=nums[right];
nums[right]=temp;
}
}
双指针,注意等于null的情况,规范输入输出
思路:以结尾进行排序,这样对于横跨区间过长的好解决;
Arrays.sort([][])的运用
重叠区间
同上一题还是判断右区间,
1.还是要找到一个合适的排序方案,然后继续考虑
2.list和数组之间的转换,两种方法LinkedList, List
List<String> list = new ArrayList<>();
String[] str = list.toArray(new String[list.size()]);
List<int[]> queue = new ArrayList<>();
return queue.toArray(new int[queue.size()][]);
LinkedList<int[]> queue=new LinkedList<>();
for(int i=0;i<people.length;i++){
queue.add(people[i][1], people[i]);
}
int[][] a=new int[][];//二维数组的创建方法
//people为一个[][]数组,要保证它的size和queue的size相同,是否为空不介意
return (int[][]) queue.toArray(people);
找保留前面最小股票,然后与后面比较取最大利润
indexof(String/char,fromIndex)–position
只需要考虑特别的情况就是 nums[i] < nums[i - 2],其他情况就是nums[i]=nums[i-1];要不就是nums[i-1]=nums[i]
目的就是保证nums[i]为最小值,保证后面操作
//要考虑好逻辑再下笔,否则会出错
过
除法比乘法更好,要考虑溢出的情况
过
1.set获取元素只能iterator获取
2.list中remove元素根据索引移除,而set是根据值移除
还是要好好判断一下二分查找的mid与left,right关系,不止一种,h=length||h=length-1,mid=h,mid=left+1;return left
int mid=left+(right-left)/2;//更好,不会超出时间限制
int mid=(left+right)/2; //会超出时间限制,选择上面的比较好
二分查找新类型:mid与right比较而不是与mid+1,mid-1比较,还是要抓住题的特征。
1用两种查找方法分别查找first,last
2.一种方法查找target和target+1
//最后一个 判断条件和返回值不同
private int findLast(int[] nums, int target) {
int l = 0, h = nums.length; // 注意 h 的初始值
while (l < h) {
int m = l + (h - l) / 2;
if (nums[m] <=target) {
l= m+1;
} else{
h = m;
}
}
return l-1;
}
//第一个
private int findFirst(int[] nums, int target) {
int l = 0, h = nums.length; // h=nums.length-1也对;
while (l < h) {
int m = l + (h - l) / 2;
if (nums[m] >=target) {
h = m;
} else{
l = m + 1;
}
}
return l;
}
String[] nums = input.split("[\\+\\-\\*]")
分治往下推进就是动态规划
广度优先遍历用队列,深度优先遍历用递归
//考虑8个方向的转换,用数组确定8个方向;然后走过进行的标记
void BFS()
{
定义队列;
定义备忘录,用于记录已经访问的位置;
判断边界条件,是否能直接返回结果的。
将起始位置加入到队列中,同时更新备忘录。
while (队列不为空) {
获取当前队列中的元素个数。
for (元素个数) {
取出一个位置节点。
判断是否到达终点位置。
获取它对应的下一个所有的节点。
条件判断,过滤掉不符合条件的位置。
新位置重新加入队列。
}
}
}
完全平方数转换成找平方数的最短路径问题
BFS问题,动态规划
空指针异常:数组未初始化,找不到所在位置的内容
两题均用bfs过,也可以dfs查找
控制边界好方法:反向操作不满足的return,剩下的一定满足
if(x<0||x>=board.length||y<0||y>=board[0].length||board[x][y]=='X'||flag[x][y]){
return;
}
方法里面传递参数,是可以得到的。相当于全局变量
int[][] po = new int[m][n], ao = new int[m][n]; //po 太平洋,ao 大西洋
//只需要搞定太平洋,大西洋的边界即可
for (int i = 0; i < n; ++i){
dfs(matrix, 0, i, po);
dfs(matrix, m - 1, i, ao);
}
(1)main 函数内的变量不是全局变量,而是局部变量
(2)只不过它的生命周期和全局变量一样长而已
(3)全局变量一定是定义在函数外部的
方法一
for(int i=0;i
方法二
private void doCombination(StringBuilder prefix, List combinations, final String digits) {
if (prefix.length() == digits.length()) {
combinations.add(prefix.toString());
return;
}
int curDigits = digits.charAt(prefix.length()) - '0';
String letters = KEYS[curDigits];
for (char c : letters.toCharArray()) {
prefix.append(c); // 添加
doCombination(prefix, combinations, digits);
prefix.deleteCharAt(prefix.length() - 1); // 删除
}
flag[][]==true;回溯要返回原来的flag,每条路径都是重新开始。
对于有返回值的dfs,一定要if(dfs)才return,直接返回如果false会返回导致执行某一条路径错误直接返回;
Arraylist数组也可以回溯,直接删除remove(list.szie()-1);
对于返回List list,add添加的时候一定要list.add(new ArrayList<>(list)),直接添加的话会默认一个空间,仔细想一想内存问题,list永远一个内存地址,所以只会得到一个答案
判断重复元素
1.用set集合进行判断
2.在添加一个元素时,判断这个元素是否等于前一个元素,如果等于,并且前一个元素还未访问,那么就跳过这个元素。
数独问题
n皇后问题
状态转换方程很重要
关于增加子集问题,都是先求dp[i]=dp[i-1]+1,然后dp和才是最后的所有子集和
nums = [1, 2, 3]
target = 4,求所有组合可能
顺序不同的序列被视作相同的组合。
public int change(int amount, int[] coins) {
if (coins == null) {
return 0;
}
int[] dp = new int[amount + 1];
dp[0] = 1;
for (int coin : coins) {
for (int i = coin; i <= amount; i++) {
dp[i] += dp[i - coin];
}
}
return dp[amount];
}
顺序不同的序列被视作不同的组合。
public int combinationSum4(int[] nums, int target) {
if (nums == null || nums.length == 0) {
return 0;
}
int[] maximum = new int[target + 1];
maximum[0] = 1;
Arrays.sort(nums);
for (int i = 1; i <= target; i++) {
for (int j = 0; j < nums.length && nums[j] <= i; j++) {
maximum[i] += maximum[i - nums[j]];
}
}
return maximum[target];
}
public int maxProfit(int k, int[] prices) {
if(prices.length<=1)
return 0;
int[][][] dp=new int[prices.length][k+1][2];
k=Math.min(k,prices.length/2);
for(int i=0;i<=k;i++){
dp[0][i][1]=0;
dp[0][i][0]=-prices[0];
}
//分为买入状态(上一次买入,刚从上一个卖出这一个买入),卖出状态(上一次卖出,刚卖出)
for(int i=1;i
121. 买卖股票的最佳时机:限定交易次数 k=1
我们可以直接设置二维dp[i][j],其含义是第i天,股票持有状态为j时 能够获取的最大利润
然后设定初始值,找到状态方程:
dp[i][0]=max(dp[i-1][0] , dp[i-1][1]+prices[i])
dp[i][1]=max(dp[i-1][1] , -prices[i]) //因为限定交易次数,所以要进行交易的时候要注意
122. 买卖股票的最佳时机 II:交易次数无限制
直接设置二维dp[i][j],其含义是第i天,股票持有状态为j时 能够获取的最大利润
然后设定初始值,找到状态方程:
dp[i][0]=max(dp[i-1][0] , dp[i-1][1]+prices[i])
dp[i][1]=max(dp[i-1][1] , dp[i-1][0]-prices[i]) //因为没有限定交易次数,所以按照常规思路
123. 买卖股票的最佳时机 III:限定交易次数 k=2
和本题188思路一致,就是直接将k值设定为2罢了
309. 最佳买卖股票时机含冷冻期:含有交易冷冻期
冷冻期就是:卖出股票后,你无法在第二天买入股票,那又有何难
直接设置三个持有状态:
dp[i][0]:持有股票
dp[i][1]:不持有股票,处于冷冻期
dp[i][2]:不持有股票,不处于冷冻期
dp[i][0]=max(dp[i-1][0] , dp[i-1][2]+prices[i]) //当天持有股票,前一天不可能是冷冻期,也就没有dp[i-1][1]
dp[i][1]=dp[i-1][0]+prices[i] //当天是冷冻期,只可能是前一天持有股票,然后今天卖出股票了
dp[i][2]=max(dp[i-1][1],dp[i-1][2]) //当天是非冷冻期,前一天不可能持有股票
714. 买卖股票的最佳时机含手续费:每次交易含手续费
dp[i][0]=max(dp[i-1][0] , dp[i-1][1]+prices[i]-fee) //卖出股票时完成一次交易,支付手续费
dp[i][1]=max(dp[i-1][1] , dp[i-1][0]-prices[i])
等于求最长公共字符串
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if (word1.charAt(i - 1) == word2.charAt(j - 1)) {
dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
dp[i][j] = Math.max(dp[i][j - 1], dp[i - 1][j]);
}
}
}
return m + n - 2 * dp[m][n];
直接求需要删除次数
for (int i = 0; i <= s1.length(); i++) {
for (int j = 0; j <= s2.length(); j++) {
if (i == 0 || j == 0)
dp[i][j] = i + j;
else if (s1.charAt(i - 1) == s2.charAt(j - 1))
dp[i][j] = dp[i - 1][j - 1];
else
dp[i][j] = 1 + Math.min(dp[i - 1][j], dp[i][j - 1]);
}
}
可删除,替换,插入字符串
for (int i = 0; i <= m; i++) {
for (int j = 0; j <= n; j++) {
if (i == 0 || j == 0)
dp[i][j] = i + j;
else if (word1.charAt(i - 1) == word2.charAt(j - 1)) {
dp[i][j] = dp[i - 1][j - 1];
} else {
//与只能删除字符串区别所在,考虑
dp[i][j] =Math.min(dp[i-1][j-1], Math.min(dp[i][j - 1], dp[i - 1][j]))+1;
}
}
}
递归反转
class Solution {
public ListNode reverseList(ListNode head) {
if (head == null || head.next == null) {
return head;
}
ListNode newHead = reverseList(head.next);
head.next.next = head;
head.next = null;
return newHead;
}
}
递归好思路
先遍历到最后,然后判断最后两个元素相等与否,依次往前判断
class Solution {
public ListNode deleteDuplicates(ListNode head) {
if (head == null || head.next == null) {
return head;
}
head.next = deleteDuplicates(head.next);
return head.val == head.next.val ? head.next : head;
}
}
补充一个元素