序列二叉树
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
public class Codec { //基于 bfs 借助队列,还可以 基于先序,借助递归
// Encodes a tree to a single string.
public String serialize(TreeNode root) {
if(root==null){
return "[]";
}
StringBuilder res = new StringBuilder();
res.append("[");
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
res.append(root.val+",");
while(!queue.isEmpty()){
TreeNode node = queue.poll();
if(node.left!=null){
res.append(node.left.val+",");
queue.offer(node.left);
}else{
res.append("null"+",");
}
if(node.right!=null){
res.append(node.right.val+",");
queue.offer(node.right);
}else{
res.append("null"+",");
}
}
res.deleteCharAt(res.length()-1);
res.append("]");
return res.toString();
}
// Decodes your encoded data to tree.
public TreeNode deserialize(String data) {
if(data=="[]"){
return null;
}
String[] val = data.substring(1,data.length()-1).split(",");
Queue<TreeNode> queue = new LinkedList<>();
TreeNode head = new TreeNode(Integer.valueOf(val[0]));
queue.offer(head);
int i=1;
while(!queue.isEmpty()){
TreeNode root = queue.poll();
if(!val[i].equals("null")){
root.left = new TreeNode(Integer.parseInt(val[i]));
queue.offer(root.left);
}
i++;
if(!val[i].equals("null")){
root.right = new TreeNode(Integer.parseInt(val[i]));
queue.offer(root.right);
}
i++;
}
return head;
}
}
// Your Codec object will be instantiated and called as such:
// Codec codec = new Codec();
// codec.deserialize(codec.serialize(root));
字符串的排列
class Solution { //如果没有重复单词 ,普通的回溯 过程记住
ArrayList<String> ans = new ArrayList<>();
public String[] permutation(String s) {
char[] strs = s.toCharArray();
StringBuilder res = new StringBuilder();
boolean[] used = new boolean[strs.length];
for(int i = 0;i<strs.length;i++){
used[i] = true;
process(res.append(strs[i]),used,1,strs);
res.deleteCharAt(res.length()-1);
used[i] = false;
}
return ans.toArray(new String[strs.length-1]);
}
public void process(StringBuilder res,boolean[] used, int len, char[] strs){
if(len==strs.length){
ans.add(new StringBuilder(res).toString());
return ;
}
for(int i =0;i<strs.length;i++){
if(used[i]==false){
used[i]=true;
process(res.append(strs[i]),used,len+1,strs);
res.deleteCharAt(res.length()-1);
used[i]=false;
}
}
}
}
//有重复单词, 要去重!
class Solution {
ArrayList<String> ans = new ArrayList<>();
public String[] permutation(String s) {
char[] strs = s.toCharArray();
StringBuilder res = new StringBuilder();
boolean[] used = new boolean[strs.length];
Arrays.sort(strs);
process(res,used,0,strs);
return ans.toArray(new String[strs.length-1]);
}
public void process(StringBuilder res,boolean[] used, int len, char[] strs){
if(len==strs.length){
ans.add(new StringBuilder(res).toString());
return ;
}
for(int i =0;i<strs.length;i++){
if(used[i]==false){
if(i>0&&strs[i-1]==strs[i]&&!used[i-1]){
continue;
}
used[i]=true;
process(res.append(strs[i]),used,len+1,strs);
res.deleteCharAt(res.length()-1);
used[i]=false;
}
}
}
}
1、在图中 ② 处,搜索的数也和上一次一样,但是上一次的 1 还在使用中;
2、在图中 ① 处,搜索的数也和上一次一样,但是上一次的 1 刚刚被撤销,正是因为刚被撤销,下面的搜索中还会使用到,因此会产生重复,剪掉的就应该是这样的分支。
if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) {
continue;
}
数组中出现超过次数超过一半的数
class Solution { // 摩尔投票法
public int majorityElement(int[] nums) {
if(nums.length==1){
return nums[0];
}
int i=0;
int result=0;
for(int k=0;k<nums.length;k++){
if(i==0){
result= nums[k];
}
if(nums[k]==result){
i++;
}else{
i--;
}
}
return result;
}
}
算法流程:
初始化: 票数统计 votes = 0, 众数 x;
循环抵消: 遍历数组 nums 中的每个数字 num ;
当 票数 votes等于 00 ,则假设 当前数字 num 为 众数 xx ;
当 num = x 时,票数 votes 自增 1 ;否则,票数 votes自减 1。
返回值: 返回 众数 x 即可。
最小的K个数
- 计数排序法
class Solution {
public int[] getLeastNumbers(int[] arr, int k) {
//计数排序
if(arr.length==0||k==0){
return new int[0];
}
int[] counter = new int[10001];
for(int num:arr){
counter[num]++;
}
int[] res = new int[k];
int x =0;
for(int i =0;i<counter.length;i++){
while(counter[i]-->0&&x<k){
res[x++]=i;
}
if(x==k){
break;
}
}
return res;
}
}
- 堆
//使用优先队列 本题是求前 K 小,因此用一个容量为 K 的大根堆,
//每次 poll 出最大的数,那堆中保留的就是前 K 小啦(注意不是小根堆!小根堆的话需要把全部的元素都入堆,那是 O(NlogN)O(NlogN)😂,
//就不是 O(NlogK)O(NlogK)啦~~)
//因为 Java 中提供了现成的 PriorityQueue(默认小根堆),所以要重写比较器
if(arr.length==0||k==0){
return new int[0];
}
Queue<Integer> queue = new PriorityQueue<>(new Comparator<Integer>(){
public int compare(Integer t1,Integer t2){
return t2-t1;
}
});
int[] ans = new int[k];
int m=0;
for(int num:arr){
if(m<k){
queue.offer(num);
m++;
}else if(num<queue.peek()){
queue.poll();
queue.offer(num);
}
}
int x=0;
while(!queue.isEmpty()){
ans[x++] = queue.poll();
}
return ans;
//使用数组自己实现堆
if(arr.length==0||k==0){
return new int[0];
}
int[] heap = new int[k];
for(int i = 0;i<k;i++){
heapInsert(heap,arr[i],i);
}
for(int i = k;i<arr.length;i++){
if(arr[i]<heap[0]){
heap[0] = arr[i];
heapify(heap,0,k);
}
}
return heap;
}
public void heapInsert(int[] heap,int val,int index){ //建堆
heap[index] = val;
while(index!=0){
int parent = (index-1)/2;
if(heap[index]>heap[parent]){
swap(heap,index,parent);
index = parent;
}else{
break;
}
}
}
public void heapify(int[] heap,int index, int len){ //调整堆
int left = index*2+1;
int right = index*2+2;
int max = index;
while(left<len){
if(heap[left]>heap[index]){
max = left;
}
if(right<len&&heap[right]>heap[max]){
max = right;
}
if(index!= max){
swap(heap,index,max);
}else{
break;
}
index = max;
left = index*2+1;
right =index*2+2;
}
}
public void swap(int[] arr, int i, int j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
- 快速选择
//快速排序要清楚!
if(arr.length==0||k==0){
return new int[0];
}
partitionArray(arr,0,arr.length-1,k-1);
int[] ans = new int[k];
for(int i =0;i<k;i++){
ans[i] = arr[i];
}
return ans;
}
public void partitionArray(int[] arr,int lo,int hi,int k){
int i =partition(arr, lo, hi);
if(k==i){
return ;
}else if(i>k){
partitionArray(arr,lo,i-1,k);
}else{
partitionArray(arr,i+1,hi,k);
}
}
public int partition(int[] arr,int lo,int hi){
int v = arr[lo];
int i=lo;
int j =hi;
while(i<j){
while(i<j&&arr[j]>=v){
j--;
}
while(i<j&&arr[i]<=v){
i++;
}
if(i<j){
int temp = arr[i];
arr[i] = arr[j];
arr[j] =temp;
}
}
arr[lo] = arr[i];
arr[i] = v;
return i;
}
数据流中的中位数
//大顶堆 +小顶堆
class MedianFinder {
private Queue<Double> maxHeap;
private Queue<Double> minHeap;
/** initialize your data structure here. */
public MedianFinder() {
maxHeap = new PriorityQueue<>(new Comparator<Double>(){
public int compare(Double t1, Double t2){
return (int)(t2-t1);
}
});
minHeap = new PriorityQueue<>();
}
public void addNum(int num) {
if(maxHeap.isEmpty()||num<=maxHeap.peek()){
maxHeap.add((double)num);
}else{
minHeap.add((double)num);
}
modifyTwoHeaps();
}
public double findMedian() {
if(maxHeap.isEmpty()){
return 0.0;
}
if(maxHeap.size()==minHeap.size()){
return (maxHeap.peek()+minHeap.peek())/2;
}else{
return maxHeap.size()>minHeap.size()?maxHeap.peek():minHeap.peek();
}
}
private void modifyTwoHeaps(){
if(maxHeap.size()==minHeap.size()+2){
minHeap.add(maxHeap.poll());
}
if(minHeap.size()==maxHeap.size()+2){
maxHeap.add(minHeap.poll());
}
}
}
/**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder obj = new MedianFinder();
* obj.addNum(num);
* double param_2 = obj.findMedian();
*/
连续子数组的最大和
class Solution{ //动态规划
public int maxSubArray(int[] nums) {
if(nums==null||nums.length==0){
return 0;
}
int cur=0;
int res=Integer.MIN_VALUE;
for(int i =0;i<nums.length;i++){
cur += nums[i];
res =Math.max(res,cur);
cur = cur<0?0:cur;
}
return res;
}
}
1-n中1出现的次数
class Solution { //精简版
public int countDigitOne(int n) {
int count = 0;
for(long k =1;k<=n;k*=10){
long r = n/k;
long m = n%k;
count+=(r+8)/10*k+(r%10==1?m+1:0);
}
return count;
}
}
//初始版本 ,易于理解
public int countDigitOne(int n) {
int count = 0;
for (long k = 1; k <= n; k *= 10) {
// xyzdabc
int abc = n % k;
int xyzd = n / k;
int d = xyzd % 10;
int xyz = (xyzd + 8) / 10;
count += xyz * k;
if (d == 1) {
count += abc + 1;
}
}
return count;
}
- 类似题 : 阶乘中0的个数
//出现5 因子的的个数
public int trailingZeroes(int n) {
int count = 0;
while (n > 0) {
count += n / 5;
n = n / 5;
}
return count;
}
两者都为数学规律题
第N个数字
class Solution { // 也是数学规律题
public int findNthDigit(int n) {
long num =n;
long max = 9;
long size = 1;
while(num>0){
if(num-size*max>0){
num=num-size*max;
max *=10;
size++;
}else{
long count = num/size;
long left = num%size;
if(left==0){
return (int)(((long)(Math.pow(10,size-1))+count-1)%10);
}else{
return (int)(((long)(Math.pow(10,size-1)+count)/(long)(Math.pow(10,size-left)))%10);
}
}
}
return 0;
}
}
把数组排成最小的数
依据贪心的思想,越小的字符就排得越前
但是有一个问题,对于字符串的排序很难确定,因为长度不等,比较起来很繁琐
于是对于o1 o2,要比较它们,不妨比较o1+o2 o2+o1
通过相加,可以比较两个等长的字符串,从而确定哪个字符串具有的字符值较小。
class Solution {
public String minNumber(int[] nums) {
StringBuilder res = new StringBuilder();
String[] strs = new String[nums.length];
for(int i =0;i<nums.length;i++){
strs[i] = String.valueOf(nums[i]);
}
Arrays.sort(strs,new Comparator<String>(){
public int compare(String s1,String s2){
if((s1+s2).compareTo(s2+s1)<0){
return -1;
}else return 1;
}
});
for(String str:strs){
res.append(str);
}
return res.toString();
}
}
把数字翻译成字符串
class Solution { // 加上注释的内容即便为0不可翻译版本 ,不加为0可以翻译的版本
public int translateNum(int num) {
String str = String.valueOf(num);
char[] chs = str.toCharArray();
// int cur = chs[chs.length-1]=='0'?0:1;
int cur = 1;
int next =1 ;
int temp = 0;
for(int i =chs.length-2;i>=0;i--){
// if(chs[i]=='0'){
// next = cur;
// cur = 0;
// }else{
temp = cur;
if(chs[i]!='0'&&((chs[i]-'0')*10+(chs[i+1]-'0')<26)){
cur+= next;
}
next = temp;
// }
}
return cur;
}
}
礼物的最大价值
回溯适用于遍历所有情况,而动态规划则是找最优情况
class Solution { //普通回溯 递归 ,超时!
int ans = 0;
int[][] directions ={{1,0},{0,1}};
public int maxValue(int[][] grid) {
boolean[][] used = new boolean[grid.length][grid[0].length];
int res = 0;
process(grid,res,used,0,0);
return ans;
}
public void process(int[][] grid,int res,boolean[][] used,int x,int y){
if(x==grid.length-1&&y==grid[0].length-1){
ans = Math.max(ans,res+grid[x][y]);
return ;
}
if(check(x,y,grid)&&!used[x][y]){
used[x][y] = true;
for(int i = 0;i<=1;i++){
int newX = x+ directions[i][0];
int newY = y+ directions[i][1];
process(grid,res+grid[x][y],used,newX,newY);
}
used[x][y] = false;
}
}
public boolean check(int x,int y,int[][]grid){
return x>=0&&x<grid.length&&y>=0&&y<grid[0].length;
}
}
//动态规划
for(int i =1;i<grid.length;i++){
grid[i][0] +=grid[i-1][0];
}
for(int i =1;i<grid[0].length;i++){
grid[0][i]+=grid[0][i-1];
}
for(int i = 1;i<grid.length;i++){
for(int j =1;j<grid[0].length;j++){
grid[i][j] += Math.max(grid[i-1][j],grid[i][j-1]);
}
}
return grid[grid.length-1][grid[0].length-1];
}
}
最长不含重复数字的子字符串
//滑动窗口一般套路
while(right<n){
window.add() right++
if(){
window.remove() left++
}
class Solution { //滑动窗口 一般套路
public int lengthOfLongestSubstring(String s) {
if(s==null||s.length()<1){
return 0;
}
if(s.length()==1){
return 1;
}
int n = s.length();
Set<Character> set = new HashSet<>();
int i=0,j=0,ans =0;
while(i<n&&j<n){
if(!set.contains(s.charAt(j))){
set.add(s.charAt(j++));
ans = Math.max(ans,j-i);
}else{
set.remove(s.charAt(i++));
}
}
return ans;
}
}
//配合 hashmap 优化版本
public int lengthOfLongestSubstring(String s) {
if(s==null||s.length()<1){
return 0;
}
if(s.length()==1){
return 1;
}
int n = s.length();
HashMap<Character,Integer> map = new HashMap<>();
int i=0,j=0,ans = 0;
for(j=0;j<n;j++){
if(map.containsKey(s.charAt(j))){
i =Math.max(map.get(s.charAt(j)),i); //防止i左移,abcdefcma 到第二个c的时候 i变成了 3,
//但到第二个 a的时候如果取第一个a的位置,而不是a和当前i的最大值, i就变成了1,左侧索引左移
}
ans = Math.max(ans,j-i+1);
map.put(s.charAt(j),j+1);
}
return ans;
}
//还可以用数组来代替map,思路类似
丑数
class Solution { 动态规划
public int nthUglyNumber(int n) {
int[] dp = new int[n];
dp[0] =1 ;
int a =0,b=0,c=0;
for(int i =1;i<n;i++){
int n1 = dp[a]*2;
int n2 = dp[b]*3;
int n3 = dp[c]*5;
dp[i] = Math.min(Math.min(n1,n2),n3);
if(dp[i]==n1){
a++;
}
if(dp[i]==n2){
b++;
}
if(dp[i]==n3){
c++;
}
}
return dp[n-1];
}
}
第一个只出现一次的字符
class Solution { //LinkedHashMap 也可以用HashMap,效率稍低
public char firstUniqChar(String s) {
if(s.length()==0){
return ' ';
}
Map<Character,Boolean> map = new LinkedHashMap<>();
char[] chs = s.toCharArray();
for(char ch:chs){
map.put(ch,!map.containsKey(ch));
}
for(Map.Entry<Character,Boolean> a : map.entrySet()){
if(a.getValue()){
return a.getKey();
}
}
return ' ';
}
}
数组中的逆序对
class Solution {
//基于归并排序
//前一部分出队,还可以后一部分
public int reversePairs(int[] nums) {
int len = nums.length;
if(len<2){
return 0;
}
int[] temp = new int[len];
return process( nums,0,len-1,temp);
}
public int process(int[] nums,int l,int r,int[] temp){
if(l==r){
return 0;
}
int mid = (l+r)/2;
int left = process(nums,l,mid,temp);
int right = process(nums,mid+1,r,temp);
return left+right+merge(nums,l,mid,r,temp);
}
public int merge(int[] nums,int l, int mid,int r,int[] temp){
for(int i =l;i<=r;i++){
temp[i] = nums[i];
}
int i = l;
int j =mid+1;
int count = 0;
for(int k =l;k<=r;k++){
if(i>mid){
nums[k] = temp[j];
j++;
}else if(j>r){
nums[k] = temp[i];
i++;
count+= (r-mid);
}else if(temp[j]>=temp[i]){
nums[k] = temp[i];
i++;
count+=(j-mid-1);
}else{
nums[k] = temp[j];
j++;
}
}
return count;
}
}
还有leetcode 315题 (困难) 类似
归并排序 + 索引数组 (要记住每个数的位置,不能更改数组,所以用索引数组,索引进行交换)
两个链表的第一个公共节点
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution { //快慢指针问题
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if(headA==null||headB==null){
return null;
}
ListNode h1= headA;
int l1=0;
while(h1!=null){
l1++;
h1 = h1.next;
}
ListNode h2 = headB;
int l2 = 0;
while(h2!=null){
l2++;
h2 = h2.next;
}
if(l1>l2){
for(int i = l1-l2;i>0;i--){
headA = headA.next;
}
}
if(l1<l2){
for(int i = l2-l1;i>0;i--){
headB = headB.next;
}
}
while(headA!=null&&headB!=null){
if(headB==headA){
return headB;
}
headA = headA.next;
headB = headB.next;
}
return null;
}
}
还有一种思路: 两个同时遍历,到头后从另一方遍历, a+all+b = b+all+a