在排序数组中查找数字
(排序数组首先想二分法的各种用法)
class Solution {
public int search(int[] nums, int target) {
if(nums.length==0){
return 0;
}
int i=0,j = nums.length-1;
while(i<=j){
int mid =(i+j)/2;
if(nums[mid]>=target){
j= mid-1;
}else{
i=mid+1;
}
}
int left = j;
i = 0;
j = nums.length-1;
while(i<=j){
int mid = (i+j)/2;
if(nums[mid]>target){
j=mid-1;
}else{
i =mid+1;
}
}
return i-left-1;
}
}
循环二分: 当 i \leq ji≤j 时循环 (即当闭区间 [i, j][i,j] 为空时跳出) ;
计算中点 m = (i + j) // 2,其中 “” 为向下取整除法;
若 nums[m] < target,则数字 target一定在闭区间 [m + 1, j]中,因此执行 i = m + 1;
若 nums[m] > target ,则数字 target一定在闭区间 [i, m - 1] 中,因此执行 j = m - 1;
若 nums[m] = target,则右边界 right 在闭区间 [m+1, j]中;左边界 leftleft 在闭区间 [i, m-1] 中。因此分为以下两种情况:
若查找 右边界 right,则执行 i = m + 1;(跳出时 ii指向右边界)
若查找 左边界 left,则执行 j = m - 1;(跳出时 j 指向左边界)
返回值: 应用两次二分,分别查找 right 和 left ,最终返回 right - left - 1 即可。
0~n-1中缺失的数字
//二分法 二分法不一定是比较中间大小,还有别的形式,只要是每次取一半。
class Solution {
public int missingNumber(int[] nums) {
int i =0;
int j = nums.length-1;
while(i<=j){
int mid = (i+j)/2;
if(nums[mid] == mid){
i = mid+1;
}else{
j=mid-1;
}
}
return i;
}
}
二叉搜索树的第K大节点
```java
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution { //中序遍历逆序+提前终止
int k,res;
public int kthLargest(TreeNode root, int k) {
this.k = k;
process(root);
return res;
}
public void process(TreeNode root){
if(root==null){
return ;
}
process(root.right);
if(--k==0){
res = root.val;
return;
}
process(root.left);
}
}
二叉树的深度
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public int maxDepth(TreeNode root) {
if(root==null){
return 0;
}
int depth = 0;
return process(root);
}
public int process(TreeNode root){
if(root==null){
return 0;
}
int left = process(root.right);
int right = process(root.left);
return Math.max(left,right)+1;
}
}
//另一种递归
class Solution {
int maxDepth = 0;
public int maxDepth(TreeNode root) {
if(root==null){
return 0;
}
process(root,1);
return maxDepth;
}
public void process(TreeNode root, int depth){
if(root==null){
return;
}
if(depth>maxDepth){
maxDepth =depth;
}
process(root.left,depth+1);
process(root.right,depth+1);
}
}
平衡二叉树
/** //需要判断深度 ,参考上一题
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public boolean isBalanced(TreeNode root) {
return !(process(root)==-1);
}
public int process(TreeNode root){
if(root ==null){
return 0 ;
}
int left = process(root.left);
if(left==-1)return -1;
int right = process(root.right);
if(right==-1) return -1;
return Math.abs(left-right)<2?Math.max(left,right)+1:-1;
}
}
数组中数字出现的次数
class Solution {
public int[] singleNumbers(int[] nums) {
int[] ans = new int[2];
int eO = 0,eOhasOne= 0;
for(int num: nums){
eO ^= num;
}
int right = eO&(~eO+1); //重要,从右看第一个1;只保留最右边的1.等同n&(-n)
for(int cur:nums){
if((cur&right)!=0){
eOhasOne ^= cur;
}
}
ans[0] = eOhasOne;
ans[1] = eO^eOhasOne;
return ans;
}
}
位运算技巧: n&(-n) 和 n&(~n+1)
n&(n-1) 把最后1个1 置零,用于统计1 的个数
数组中数字出现的次数Ⅱ
class Solution { // 题中已知数都大于1,最后不用考虑符号,如果没给,则判断最高位。
//本题还可以真正使用进制,转化为三进制,在转化为十进制返回
public int singleNumber(int[] nums) {
int[] count = new int[32];
for(int num:nums){
for(int i =0;i<=31;i++){
count[i]+=num&1;
num>>=1;
}
}
int res = 0;
for(int i=1;i<=31;i++){
res<<=1;
res|=count[31-i]%3;
}
return res;
}
}
和为s的两个数字
class Solution { //因为有序,所以双指针
public int[] twoSum(int[] nums, int target) {
if(nums.length==1){
return new int[0];
}
int i =0;
int j = nums.length-1;
int[] ans = new int[2];
while(i<=j){
if(nums[i]+nums[j]>target){
j--;
}
else if(nums[i]+nums[j]==target){
ans[0] = nums[i];
ans[1] = nums[j];
return ans;
}else{
i++;
}
}
return new int[0];
}
}
和为s的连续正数序列
class Solution { //要返回数组,看如何转换
ArrayList<int[]> ans = new ArrayList<>();
public int[][] findContinuousSequence(int target) {
int i =1,j=2;
int sum = i+j;
while(i<j&&i<target){
if(sum>target){
sum = sum-i;
i++;
}else if(sum==target){
int[] temp = new int[j-i+1];
int index = 0;
for(int k = i;k<=j;k++){
temp[index] = k;
index++;
}
ans.add(temp);
sum = sum-i;
i++;
}else{
j++;
sum = sum +j;
}
}
return ans.toArray(new int[ans.size()][]);
}
}
反转单词顺序
class Solution { //利用stringbuilder的reverse(),面试时建议不用
public String reverseWords(String s) {
if(s==null||s.length()==0){
return s;
}
StringBuilder ans = new StringBuilder();
String s1 = s.trim();
if(s1.equals("")){
return s1;
}
String s2 = reverse(s1);
String[] strs = s2.split(" ");
for(String str :strs){
if(!str.equals(" ")&&str.length()!=0){
ans.append(reverse(str));
ans.append(" ");
}
}
ans.deleteCharAt(ans.length() -1);
return ans.toString();
}
public String reverse(String s ){
StringBuilder sb = new StringBuilder(s);
return sb.reverse().toString();
}
}
class Solution { //利用指针
public String reverseWords(String s) {
s = s.trim(); // 删除首尾空格
int j = s.length() - 1, i = j;
StringBuilder res = new StringBuilder();
while(i >= 0) {
while(i >= 0 && s.charAt(i) != ' ') i--; // 搜索首个空格
res.append(s.substring(i + 1, j + 1) + " "); // 添加单词
while(i >= 0 && s.charAt(i) == ' ') i--; // 跳过单词间空格
j = i; // j 指向下个单词的尾字符
}
return res.toString().trim(); // 转化为字符串并返回
}
}
左旋转字符串
class Solution { //两个s拼接成一个,方便操作
public String reverseLeftWords(String s, int n) {
String s1 = s+s;
int len =s.length();
n = n%len;
return s1.substring(n,n+len);
}
}
方法还有: 字符串切片
列表遍历拼接
字符串遍历拼接
滑动窗口的最大值
最主要的就是获取最大值。 因此可以想到,优先队列,最大堆,单调队列
class Solution { //使用单调队列
public int[] maxSlidingWindow(int[] nums, int k) {
if(nums==null||nums.length<k||k<1){
return new int[0];
}
int index = 0;
LinkedList<Integer> maxque = new LinkedList<>();
int[] res = new int[nums.length-k+1];
for(int i =0;i<nums.length;i++){
while(!maxque.isEmpty()&&nums[maxque.peekLast()]<=nums[i]){
maxque.pollLast();
}
maxque.add(i);
if(maxque.peekFirst()==(i-k)){
maxque.pollFirst();
}
if(i>=(k-1)){
res[index++] = nums[maxque.peekFirst()];
}
}
return res;
}
}
队列的最大值
class MaxQueue {
Queue<Integer> que ;
Deque<Integer> deq;
public MaxQueue() {
que = new LinkedList<>();
deq = new LinkedList<>();
}
public int max_value() {
if(!deq.isEmpty()){
return deq.peek();
}
return -1;
}
public void push_back(int value) {
que.add(value);
while(!deq.isEmpty()&&deq.peekLast()<value){
deq.pollLast();
}
deq.offerLast(value);
}
public int pop_front() {
int temp = -1;
if(!que.isEmpty()){
temp = que.poll();
}
if(!deq.isEmpty()&& temp==deq.peek()){
deq.poll();
}
return temp;
}
}
/**
* Your MaxQueue object will be instantiated and called as such:
* MaxQueue obj = new MaxQueue();
* int param_1 = obj.max_value();
* obj.push_back(value);
* int param_3 = obj.pop_front();
*/
n个骰(tou)子的点数
class Solution { //动态规划
public double[] twoSum(int n) {
int dp[][] = new int [n+1][n*6+1];
for(int i =1;i<=6;i++){
dp[1][i] = 1;
}
for(int i =2;i<=n;i++){
for(int j =i;j<=6*n;j++){
for(int cur =1 ;cur<=6;cur++){
if(j<=cur){
break;
}
dp[i][j] += dp[i-1][j-cur];
}
}
}
double all = Math.pow(6,n);
double[] res = new double[6*n-n+1];
for(int i =0;i<6*n-n+1;i++){
res[i] = dp[n][i+n]/all;
}
return res;
}
}
扑克牌中的顺子
class Solution { //不排序版本,还可以排序,就不需要set了
public boolean isStraight(int[] nums) {
Set<Integer> set = new HashSet<>();
int max= 0;
int min = 14;
for(int num:nums){
if(num==0){
continue;
}
max = Math.max(max,num);
min = Math.min(min,num);
if(set.contains(num)){
return false;
}
set.add(num);
}
return max-min<5?true:false;
}
}
求1+2+。。+n
class Solution { //借助短路与&&判断
int res;
public int sumNums(int n) {
boolean x = (n>0) && sumNums(n-1)>0;
res+=n;
return res;
}
}
//位运算,待做
搜索二叉树的最近公共祖先
说是搜索二叉树,要利用好。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution { //迭代版本
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root==null){
return null;
}
while(root!=null){
if(root.val>p.val&&root.val>q.val){
root = root.left;
}else if(root.val<p.val&&root.val<q.val){
root = root.right;
}else{
break;
}
}
return root;
}
}
if(root==null){ //递归版本
return null;
}
if(root.val>p.val&&root.val>q.val){
return lowestCommonAncestor(root.left,p,q);
}else if(root.val<p.val&&root.val<q.val){
return lowestCommonAncestor(root.right,p,q);
}
return root;
如果没有说是搜索二叉树,就需要遍历所有的。后序遍历
if(head==null||head==p||head==q){
return head;
}
TreeNode left = lowestCommonAncestor(head.left,p,q);
TreeNode right = lowestCommonAncestor(head.right,p,q);
if(left!=null&&right!=null){
return head;
}
return left!=null?left:right;
股票最大利润
class Solution {
public int maxProfit(int[] prices) {
if(prices.length<2){
return 0;
}
int res = 0;
int cost = Integer.MAX_VALUE;
for(int i =0;i<prices.length;i++){
cost = Math.min(cost,prices[i]);
res= Math.max(res,prices[i]-cost);
}
return res;
}
}
系列问题 动态规划通用套路
dp[i][k][0 or 1]
0 <= i <= n-1, 1 <= k <= K
n 为天数,大 K 为最多交易数
此问题共 n × K × 2 种状态,全部穷举就能搞定。
for 0 <= i < n:
for 1 <= k <= K:
for s in {0, 1}:
dp[i][k][s] = max(buy, sell, rest)
把字符串转换为整数
class Solution {
public int strToInt(String str) {
str= str.trim();
if(str==null||str.length()<1){
return 0;
}
char[] chs = str.toCharArray();
long res= 0;
int signal = 1;
int i =1;
if(chs[0]=='-'){
signal = -1;
}else if(chs[0]!='+'){
i =0;
}
for(int k =i;k<chs.length;k++){
if(chs[k]<'0'||chs[k]>'9'){
break;
}
res = res*10+(chs[k]-'0');
if(res>Integer.MAX_VALUE){
return signal==-1?Integer.MIN_VALUE:Integer.MAX_VALUE;
}
}
return signal*(int)res;
}
}
不用加减乘除做加法
class Solution { // a^b 不进位加法, (a&b)<<1 只看进位 循环加到没进位
public int add(int a, int b) {
int sum = a;
while(b!=0){
sum = a^b;
b= (a&b)<<1;
a = sum;
}
return sum;
}
}
减法通用
构建乘积数组
class Solution { //正反两次遍历,类似 分糖果
public int[] constructArr(int[] a) {
if(a.length<=1){
return new int[0];
}
int[] res = new int[a.length];
res[0] = 1;
for(int i = 1;i<a.length;i++){
res[i] = a[i-1]*res[i-1];
}
int temp =1;
for(int i = a.length-2;i>=0;i--){
temp = temp*a[i+1] ;
res[i] = temp*res[i];
}
return res;
}
}