java算法总结

知识点集合

1. 异或

​ a^b =c , b = c^a;

​ a^a = 0, aba = 0^b=b;

​ prev[3+1] = abc^d, prev[2] = a^b, 那么prev[3+1]^prev[2] = c^d, 即为第2个到第3个数的异或结果; (前缀和)

2. 同余定理

​ a %b = c%b, 那么(a-b)%b = 0; 已知前缀和为a,他对b的区域结果是mod, 那么在之前的前缀和结果中寻找前缀和c, c对b求余结果也是mod,由此可知index_c 到index_a-1之间的数的和能够整除b;

​ (a+b) %c = (a%c + b%c) %c;

​ (a-b) %c = (a%c - b%c) %c;

​ (a*b) %c = (a%c * b%c) %c;

(1)先加起来 等于 边加边模
(2)先乘起来 等于 边乘边模

不用看上面的公式,直接抽象的理解其含义:我乘的时候在哪模都行,只要我边乘边模就行。也就是我1000×1000×1000×1000可以改成

(1000 % 5)×(1000 % 5)×(1000 % 5)×(1000 % 5),也可以改成(1000 % 5)×1000×(1000 % 5)×(1000 % 5),也可以改成1000×1000×1000 ×(1000 % 5),也可以改成…
只要我乘的过程中用模来减小我的数了就行。

这一切的抽象基于最后还要模一次

应用如下:

int mod = 0;
for(int i=0; i<nums.length; i++){
	sum+=nums[i];   //计算过程容易溢出
}
mod = sum%p;

for(int i=0; i<nums.length; i++){
	mod = (mod+nums[i])%p; //不会溢出,且取余结果不变
    //等价于先循环mod = mod+ nums[i]%p; 循环结束后,在外面再计算mod = mod%p;就是上面说的最后再模一次;
	
}


3.二分法的边界问题

(32条消息) 二分查找算法细节与查找左右侧边界_许诗宇的博客-CSDN博客_寻找左侧边界的二分搜索

第一个,最基本的二分查找算法:

因为我们初始化 right = nums.length - 1, 所以决定了我们的「搜索区间」是 [left, right], 所以决定了 while (left <= right),同时也决定了 left = mid+1 和 right = mid-1,因为我们只需找到一个 target 的索引即可, 所以当 nums[mid] == target 时可以立即返回;

第二个,寻找左侧边界的二分查找:value的左侧边界 如果value不在数组里,那么返回的是比value大的第一个数(在value右边)的下标;

因为我们初始化 right = nums.length,所以决定了我们的「搜索区间」是 [left, right),所以决定了 while (left < right),同时也决定了 left = mid+1 和 right = mid因为我们需找到 target 的最左侧索引,所以当 nums[mid] == target 时不要立即返回,而要收紧右侧边界以锁定左侧边界

public static int left_bound(int[] nums, int target) {
    if (nums.length == 0) return -1;
    int left = 0;
    int right = nums.length; // 注意
 
    while (left < right) { // 注意
        int mid = (left + right) / 2;
        if (nums[mid] == target) {
            right = mid;
        } else if (nums[mid] < target) {
            left = mid + 1;
        } else if (nums[mid] > target) {
            right = mid; // 注意
        }
    }
    return left;
}

第三个,寻找右侧边界的二分查找:value的右侧边界,如果value不在数组里,那么返回的是比value小的第一个数(在value左)的下标

因为我们初始化 right = nums.length,所以决定了我们的「搜索区间」是 [left, right),所以决定了 while (left < right),同时也决定了 left = mid+1 和 right = mid,因为我们需找到 target 的最右侧索引,所以当 nums[mid] == target 时不要立即返回,而要收紧左侧边界以锁定右侧边界,又因为收紧左侧边界时必须 left = mid + 1,所以最后无论返回 left 还是 right,必须减一

public static int right_bound(int[] nums, int target) {
    if (nums.length == 0) return -1;
    int left = 0, right = nums.length;
 
    while (left < right) {
        int mid = (left + right) / 2;
        if (nums[mid] == target) {
            left = mid + 1; // 注意
        } else if (nums[mid] < target) {
            left = mid + 1;
        } else if (nums[mid] > target) {
            right = mid;
        }
    }
    return left - 1; // 注意
}

找>=target的第一个数的下标;

    public int search(int[] nums,int target){
        int l=0,r=nums.length;
        while(l<r){
            int mid=(r+l)>>1;
            if(nums[mid]>=target)
                r=mid;
            else
                l=mid+1;
        }
        return l;
    }
  1. 前缀和

    具体做法为,我们使用一个与字符串 ss 等长的哈希数组 h[]h[],以及次方数组 p[]p[]。
    
    由字符串预处理得到这样的哈希数组和次方数组复杂度为 O(n)O(n)。当我们需要计算子串 s[i...j]s[i...j] 的哈希值,只需要利用前缀和思想 h[j] - h[i - 1] * p[j - i + 1]h[j]−h[i−1]∗p[j−i+1] 即可在 O(1)O(1) 时间内得出哈希值(与子串长度无关)。
    
    class Solution {
        int N = (int)1e5+10, P = 131313;
        int[] h = new int[N], p = new int[N];
        public List<String> findRepeatedDnaSequences(String s) {
            int n = s.length();
            List<String> ans = new ArrayList<>();
            p[0] = 1;
            for (int i = 1; i <= n; i++) {
                h[i] = h[i - 1] * P + s.charAt(i - 1);
                p[i] = p[i - 1] * P;
            }
            Map<Integer, Integer> map = new HashMap<>();
            for (int i = 1; i + 10 - 1 <= n; i++) {
                int j = i + 10 - 1;
                int hash = h[j] - h[i - 1] * p[j - i + 1];
                int cnt = map.getOrDefault(hash, 0);
                if (cnt == 1) ans.add(s.substring(i - 1, i + 10 - 1));
                map.put(hash, cnt + 1);
            }
            return ans;
        }
    }
    
    作者:AC_OIer
    链接:https://leetcode-cn.com/problems/repeated-dna-sequences/solution/gong-shui-san-xie-yi-ti-shuang-jie-hua-d-30pg/
    来源:力扣(LeetCode)
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    
4. 并查集
class UnionFind{
    int[] feather;
    int[] rank;
    public UnionFind(int n){
        feather = new int[n];
        rank = new int[n];
        for(int i=0; i<n; i++){
        	feather[i] = i;
        	rank[i] = 1;       
        }
    }
    public void union(int a, int b){
        int fa = find(a);
        int fb = find(b);

        if(rank[fa]<rank[fb]){
            feather[fa] = fb;
        }else if(rank[fa] > rank[fb]){
            feather[fb] = fa;
        }else{
            feather[fa] = fb;
            rank[fb] +=1;
        }
        
    }
    public int find(int x){
        if(feather[x] == x){
            return feather[x];
        }
        return find(feather[x]);
    }
5. 图
1. bfs
public void bfs(int[][] G){
    int n = G.length;
    LinkedList<Integer> queue = new LinkedList<>();
    boolean[] used = new boolean[n];
    int start = 0;
    queue.add(start);
    while(queue.size() != 0){
        int size = queue.size();
        for(int i=0; i<size; i++){
            int cur = queue.poll();
            System.out.println(cur);
            //遍历寻找当前节点能够到达的所有节点;
           for(int i=0; i<n; i++){
                if(!used[i] && G[cur][i] != Integer.MAX_VALUE){//cur能够到达i;且之前没有走过的路;
                    queue.add(i);
                }
            }
        }
        
    }
}
2.dfs
3.拓扑排序

现在你总共有 numCourses 门课需要选,记为 0 到 numCourses - 1。给你一个数组 prerequisites ,其中 prerequisites[i] = [ai, bi] ,表示在选修课程 ai 前 必须 先选修 bi 。

例如,想要学习课程 0 ,你需要先完成课程 1 ,我们用一个匹配来表示:[0,1] 。
返回你为了学完所有课程所安排的学习顺序。可能会有多个正确的顺序,你只要返回 任意一种 就可以了。如果不可能完成所有课程,返回 一个空数组 。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/course-schedule-ii

public int[] findOrder(int numCourses, int[][] prerequisites) {
        List<List<Integer>> edges = new ArrayList<>();
        int[] in = new int[numCourses];
        for(int i=0; i<numCourses; i++){
            edges.add(new ArrayList<>());
        }

        for(int[] subject:prerequisites){
            edges.get(subject[1]).add(subject[0]);//b-->a  b出去的边
            in[subject[0]]++;//;b--->a,  指向a的边有多少条;
        }

        Queue<Integer> queue = new LinkedList<>();
        for(int i=0; i<numCourses; i++){
            if(in[i] == 0){
                queue.add(i);
            }
        }
        int[] res = new int[numCourses];
        int j=0;
        while(!queue.isEmpty()){
            int node = queue.poll();
            res[j++] = node;
            List<Integer> list = edges.get(node);
            for(int i=0; i<list.size(); i++){
                in[list.get(i)]--;
                if(in[list.get(i)] ==0){
                    queue.add(list.get(i));
                }
            }
        }
        return j==numCourses?  res:new int[0];
    }
最小生成树:

图的所有的点连起来, 不构成回路,且权值累加和最小。

也就是说,从任何点出发都可以得到最小生成树!!!

4.Kruskal 算法

将连通网中所有的边按照权值大小做升序排序,从权值最小的边开始选择,只要此边不和已选择的边一起构成环路(有并查集判断两个点的根不能相同),就可以选择它组成最小生成树。对于 N 个顶点的连通网,挑选出 N-1 条符合条件的边,这些边组成的生成树就是最小生成树。


5.Prim

最小生成树的过程,采用了贪心算法的思想。对于包含 N 个顶点的连通网,普里姆算法每次从与已经生成的树 相连的边里面找出一个权值最小的边,这样的操作重复 N-1 次,由 N-1 条权值最小的边组成的生成树就是最小生成树。

package com.liuzhen.chapter8;

import java.util.ArrayList;

public class Prim {
    /*
     * 参数G:给定的图,其顶点分别为0~G.length-1,相应权值为具体元素的值
     * 函数功能:返回构造生成的最小生成树,以二维数组形式表示,其中元素为0表示最小生成树的边
     */
    public void getMinTree(int[][] G,int start) {
        int n = G.length;
        ArrayList<Integer> path = new ArrayList<>();
        
        path.add(start);
        int[] dis = new int[n];
        dis = G[start];
        while(path.size() < n){
            //寻找下一个距离集团最小的点;
            int next = 0;
            int Min = Integer.MAX_VALUE;
            for(int i=0; i<n; i++){
                if(!path.contains(i) && dis[i] < min){
                    min = dis[i];
                    next = i;
                }
            }
            path.add(next);
            //更新剩下的点到集团的最小距离;
            for(int i=0; i<n; i++){
                if(!path.contains(i) && dis[i] > G[next][i]){
                    dis[i] = G[next][i];
                }
            }
        }
        for(Integer p: path){
            System.out.println(p);
        }      
  
    }
    
    public static void main(String[] args) {
        Prim test = new Prim();
        int[][] G = {{-1,3,-1,-1,6,5},
                {3,-1,1,-1,-1,4},
                {-1,1,-1,6,-1,4},
                {-1,-1,6,-1,8,5},
                {6,-1,-1,8,-1,2},
                {5,4,4,5,2,-1}};
        test.getMinTree(G, 3);
    }
}

最短路径
6. Dijkstra

找到某个点到图上其他点的最小距离。权值不能是负的。

public class Dijkstra{
    
    public void text(int[][] edges, int n, int start){ 
        //edges=[[2,1,1],[2,3,1],[3,4,1]],n为节点个数
        int INF = Integer.MAX_VALUE;
        int[][] weight = new int[n][n];
        //领结矩阵初始化;
        for(int i=0; i<n; i++){
            for(int j=i; j<n; j++){
                weight[i][j] = i==j? 0:INF;
                weight[j][i] = i==j? 0:INF;
            }
        }
        
        //根据输入构建领结矩阵
        for(int[] edge:edges){
            int from = edge[0], to = edge[1], w = edge[2];
            weight[from][to] = w;
            weight[to][from] = w;
        }
        int[] dis;
        digstra(weight, n, start);
        int ans = 0;
        for (int i = 1; i <= n; i++) {
            ans = Math.max(ans, dis[i]);
            System.out.println(dis[i]);
        }
     
    }
    
    public void digstra(int[][] weight, int n, int start){
        dis = new int[n]; 
        boolean[] used = new boolean[n]; 
        used[start] = true;
        dis = weight[start];
        dis[start] = 0;   
        for(int j=1; j<n; j++){
            int next=-1;
            for(int i=0; i<n; i++){
                if(!used[i] && (next==-1 || dis[next] > dis[i])){
                next = i;//找到下一轮最近的点;
            }
            used[next] = true;//跳到下一步上;
            //更新距离
            for(int i=0; i<n; i++ ){//更新从start到i的距离
                if(!used[i] && dis[i] > dis[next]+weight[next][i]){
                    dis[i] = dis[next]+weight[next][i];
                }
            }
            
        }
    }
}
7. Bellman ford

最多中转K次, src 到其他点的最小距离。

支持权值为负, 某个点到其他点的最小距离;

Bellman-ford 算法比dijkstra算法更具普遍性,因为它对边没有要求,可以处理负权边与负权回路。

缺点是时间复杂度过高,高达O(VE), V为顶点数,E为边数。

其主要思想:对所有的边进行n-1轮松弛操作,因为在一个含有n个顶点的图中,任意两点之间的最短路径最多包含n-1边。

换句话说,第1轮在对所有的边进行松弛后,得到的是源点最多经过一条边到达其他顶点的最短距离;

第2轮在对所有的边进行松弛后,得到的是源点最多经过两条边到达其他顶点的最短距离;

第3轮在对所有的边进行松弛后,得到的是源点最多经过一条边到达其他顶点的最短距离…

public int findCheapestPrice(int n, int[][] flights, int src, int dst, int k) {
        int[][] weight = new int[n][n];
        int INF = 100000;
        for(int i=0; i<n; i++){
            for(int j=0; j<n; j++){
                weight[i][j] = i==j? 0:INF;
                //weight[j][i] = i==j? 0:INF;
            }
        }
        for(int[] flight: flights){
            weight[flight[0]][flight[1]] = flight[2];
        }
        
        
        int[] dis = weight[src];
        //Arrays.fill(dis, INF);
        dis[src] = 0;
        for(int limit = 1; limit<=k; limit++){
            int[] clone = dis.clone();
            for(int i=0; i<n; i++){
                for(int j=0; j<n; j++){
                    dis[j] = Math.min(dis[j], clone[i] + weight[i][j]);
                }
            }
            
        }
        return dis[dst] == INF? -1:dis[dst];

    }
8. Floyd

Floyd 属于多源最短路径算法,能够求出任意2个顶点之间的最短路径支持负权边

int[][] dis = new int[n][n]; // n是有n个点;
for(int k=0; k<n; k++){
    for(int i=0; i<n; i++){
        for(int j=0; j<n; j++){
            dis[i][j] = Math.min(dis[i][j], Math.min(grid[i][j], dp[i][k] + grid[k][j]));
            // 从i到j 有两种路, 1, i直接去j;   2, i经过k, 再从k到j;
            // 但是注意此处还是要和原来的dp[i][j]比较一下,说不定别的k'更近!!!
        }
    }
}
6. 字典树
class Tire{
	Tire[] nextNodes;
	Boolean isEND;
	Ppublic Tire(){
		nextNodes = new Tire[26];
		isEnd = false;
	}
	public void insert(String word){
		Tire  root = this;
		if(word.length == 0 || root == null) return;
		for(int i=0; i<word.length(); i++){
			int index = word.charAt(i) -'a';
			if(root.nextNodes[index] == null){
				root.nextNodes[index] = new Tire();
			}
			root = root.nextNodes[index];
		}
		root.isEnd = true;
	}
	public Tire seachPre(String pre){
		Tire root = this;
		if(word.length == 0 || root == null) return null;
		for(int i=0; i<word.length(); i++){
			int index = word.charAt(i) -'a';
			if(root.nextNodes[index] == null){
				return null;
			}
			root = root.nextNodes[index];
		}
		return root;
		
	}
}
7. 排序
1. 归并排序
//归并排序
    public void guibingSort(int[] nums,int low, int high, int[] temp){
        if(low == high){
            return;
        } 
        int mid = low + (high-low) /2;
        guibingSort( nums, low, mid,temp); //左边归并
        guibingSort(nums, mid+1, high,temp);//右边归并
        int l = low, r = mid+1;
        int i=0;
        while(l<=mid && r <=high){
            if(nums[l] < nums[r]){
                temp[i++] = nums[l++];
                
            }else{
                temp[i++] = nums[r++];
            }
        }
        while(l<=mid ){
            temp[i++] = nums[l++];
        }
        while(r<=high ){
            temp[i++] = nums[r++];
        }
        for(int t=0; t<i; t++){
            nums[low+t] = temp[t];
        }
    }
2. 快速排序
3. 堆排序
4. 插入排序
5. 选择排序

1.前中后序遍历二叉树

中序遍历

剑指 Offer II 054. 所有大于等于节点的值之和

2.dfs (深度遍历)

题目1 出现次数最多的子树元素和
class Solution {
    //ArrayList<Integer> res = new ArrayList<>();
    HashMap<Integer, Integer> res = new HashMap<>();
    public int[] findFrequentTreeSum(TreeNode root) {
        if(root == null) return null;
        dfs(root);
        System.out.println(res.toString());
        
        List<Integer> ans = new ArrayList<>();
        int Biggest = 0;
        for(Integer key:res.keySet()){
            int showTimes = res.get(key);
            if(showTimes == Biggest){
                ans.add(key);
            }else if(showTimes > Biggest){
                ans =new ArrayList<>();
                ans.add(key);
                Biggest = showTimes;
            }
        }
        
        int[] returnRes = new int[ans.size()];
        for(int i=0; i<ans.size(); i++){
            returnRes[i] = ans.get(i);
        }
        return returnRes;

    }
    
    public int dfs(TreeNode root){
        if(root == null){
            return 0;
        }
        if(root.left == null && root.right == null){
            res.put(root.val, res.getOrDefault(root.val,0)+1);
            return root.val;
        }
        int left = dfs(root.left );
        int right = dfs(root.right);
        int temSum = root.val+left+right;
        res.put(temSum, res.getOrDefault(temSum,0)+1);
        return temSum;
    }
}
题目2 654. 最大二叉树
题目3 687. 最长同值路径
题目4.剑指 Offer 26. 树的子结构
	public boolean isSubStructure(TreeNode A, TreeNode B) {
        if(A == null || B == null) return false;
        if(A.val == B.val){
           if(isSame(A,B))  return true;
        }
        return isSubStructure(A.left, B) || isSubStructure(A.right, B);

    }
    public boolean isSame(TreeNode A, TreeNode B){
        if(B == null) return true;      
        if(A == null || A.val != B.val) return false;      
        return isSame(A.left,B.left) && isSame(A.right, B.right);
    }
//抄袭题解的;
class Solution {
    int Long=0;
    public int longestUnivaluePath(TreeNode root) {
        dfs(root);
        return Long;


    }
    public int dfs(TreeNode root){
        if(root == null) return 0;
        int left = dfs(root.left);
        int right = dfs(root.right);

        int arrowLeft = 0, arrowRight = 0;
        if(root.left != null && root.left.val == root.val){
            arrowLeft+=left+1;
        }
        if(root.right != null && root.right.val == root.val){
            arrowRight+=right+1;
        }
        Long = Math.max(Long, arrowLeft+arrowRight);
        return Math.max(arrowLeft,arrowRight);
    }
    
}
题目5. 剑指 Offer 34. 二叉树中和为某一值的路径

做了几次了,逻辑一直有点乱;

List<List<Integer>> res = new ArrayList<>();
    public List<List<Integer>> pathSum(TreeNode root, int target) {
        dfs(root, target,0,new ArrayList<>());
        return res;

    }
    public void dfs(TreeNode root, int target, int prevSum,List<Integer> path){
        if(root == null ){
            return;
        }
        int sum = prevSum + root.val;
        path.add(root.val);
        if(root.left == null && root.right == null && sum == target){
            res.add(new ArrayList<>(path));
        }
        dfs(root.left, target, sum, path);
        
        dfs(root.right, target, sum, path);
        path.remove(path.size()-1);
    }
题目6. 剑指 Offer II 047. 二叉树剪枝

我感觉我只会一种垃圾dfs…

//模仿高赞题解做的。。。。。。
public TreeNode pruneTree(TreeNode root) {
        if(root == null) return root;
        
        root.left = pruneTree(root.left);
        root.right = pruneTree(root.right);

        if(root.left == null && root.right == null && root.val ==0) root = null;
        
        return root;
        

    }
题目7 剑指 Offer 36. 二叉搜索树与双向链表

算法流程:
dfs(cur): 递归法中序遍历;

终止条件: 当节点 cur 为空,代表越过叶节点,直接返回;
递归左子树,即 dfs(cur.left) ;
构建链表:
当 pre 为空时: 代表正在访问链表头节点,记为 head ;
当 pre 不为空时: 修改双向节点引用,即 pre.right = cur , cur.left = pre ;
保存 cur : 更新 pre = cur ,即节点 cur 是后继节点的 pre ;
递归右子树,即 dfs(cur.right) ;
treeToDoublyList(root):

特例处理: 若节点 root 为空,则直接返回;
初始化: 空节点 pre ;
转化为双向链表: 调用 dfs(root) ;
构建循环链表: 中序遍历完成后,head 指向头节点, pre 指向尾节点,因此修改 head 和 pre 的双向节点引用即可;
返回值: 返回链表的头节点 head 即可;

class Solution {
    Node head, pre;
    public Node treeToDoublyList(Node root) {
        if(root == null) return root;
        dfs(root);
        pre.right = head;
        head.left = pre;
        return head;
        
    }
    public void dfs(Node cur){
        if(cur == null) return;
        dfs(cur.left);
        if(pre != null){
            pre.right = cur;
        } else{
            head = cur;
        }

        cur.left = pre;
        pre =cur;
        dfs(cur.right);

    }
}
题目8 1026. 节点与其祖先之间的最大差值
class Solution {
    public int maxAncestorDiff(TreeNode root) {
        int[] res = dfs(root);
        return res[2];

    }
    public int[] dfs(TreeNode root){
        if(root == null) return new int[3];
        int val = root.val;
        int min = val;
        int max = val;
       
        int ans = 0;
        if(root.left !=null){
            int[] res = dfs(root.left);
            min = Math.min(min,res[0]);
            max = Math.max(max, res[1]);
            ans = Math.max(res[2], Math.max(Math.abs(val-res[0]), Math.abs(val-res[1])));
        }
        if(root.right !=null){
            int[] res = dfs(root.right);
            min = Math.min(min,res[0]);
            max = Math.max(max, res[1]);
            ans = Math.max(Math.max(ans,res[2]), Math.max(Math.abs(val-res[0]), Math.abs(val-res[1])));
        }
        return new int[]{min, max,ans};

    }
}

3.bfs (层序遍历)

1. 623. 在二叉树中增加一行

https://leetcode-cn.com/problems/add-one-row-to-tree/

给定一个二叉树的根 root 和两个整数 val 和 depth ,在给定的深度 depth 处添加一个值为 val 的节点行。

注意,根节点 root 位于深度 1 。

加法规则如下:

给定整数 depth,对于深度为 depth - 1 的每个非空树节点 cur ,创建两个值为 val 的树节点作为 cur 的左子树根和右子树根。
cur 原来的左子树应该是新的左子树根的左子树。
cur 原来的右子树应该是新的右子树根的右子树。
如果 depth == 1 意味着 depth - 1 根本没有深度,那么创建一个树节点,值 val 作为整个原始树的新根,而原始树就是新根的左子树。

public TreeNode addOneRow(TreeNode root, int val, int depth) {
        if(depth == 1){
            TreeNode node = new TreeNode(val);
            node.left = root;
            return node;
        }

        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        int d=1;
        while(d<depth-1){
            int size = queue.size();
            for(int i=0; i<size; i++){
                TreeNode node = queue.poll();
                if(node.left != null) queue.add(node.left);
                if(node.right != null) queue.add(node.right);
            }
            d++;
            
        }
        while(!queue.isEmpty()){
            TreeNode node = queue.poll();
            TreeNode temp = node.left;
            TreeNode addLeftNode = new TreeNode(val);
            node.left = addLeftNode;
            addLeftNode.left = temp;

            temp = node.right;
             TreeNode addRightNode = new TreeNode(val);
            node.right = addRightNode;
            addRightNode.right = temp;

        }
        return root;
    }
2.在每个树行中找最大值

https://leetcode-cn.com/problems/find-largest-value-in-each-tree-row/solution/

题目1

4.重建二叉树

题目1. 前序和中序遍历重构二叉树

(32条消息) 根据前序遍历与中序遍历构造二叉树_Nan_Feng726的博客-CSDN博客_根据前序遍历和中序遍历构建二叉树

1008. 前序遍历构造二叉搜索树

​ 二叉搜索树的中序遍历是一个递增序列,对于给定的前序遍历进行sort就得到了一个递增序列即该二叉树的中序遍历, 因此转化为前序和中序遍历构建二叉树,构建的二叉树也是二叉搜索树;

题目2. 中序和后序遍历重构二叉树

(32条消息) 根据中序遍历和后序遍历构造二叉树_Nan_Feng726的博客-CSDN博客_根据后序遍历和中序遍历构造二叉树

题目3. 前序和后序遍历重构二叉树

(32条消息) 根据先序遍历和后序遍历构建二叉树_外婆家的大灰狼-CSDN博客_怎么根据前序和后序确定树

public TreeNode constructFromPrePost(int[] preorder, int[] postorder) {
        if(preorder.length == 0) return null;
	    int rootVal = preorder[0];
	    TreeNode root = new TreeNode(rootVal);
        if(preorder.length == 1) return root;

        int L = 0;
        int LeftRootVal = preorder[1];
        for(int i=0; i<postorder.length; i++){
            if(postorder[i] == LeftRootVal){
                L = i;
                break;
            }
        }

        root.left = constructFromPrePost(Arrays.copyOfRange(preorder,1,L+2),
                                            Arrays.copyOfRange(postorder, 0,L+1));
        root.right = constructFromPrePost(Arrays.copyOfRange(preorder,L+2,preorder.length),
                                            Arrays.copyOfRange(postorder, L+1,postorder.length-1));
        return root;
    }
题目4 109. 有序链表转换二叉搜索树

给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树。

本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/convert-sorted-list-to-binary-search-tree
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

class Solution {
    public TreeNode sortedListToBST(ListNode head) {
        return constru(head,null);

    }
    public TreeNode constru( ListNode head, ListNode tail){
        if(head == tail) return null;

        ListNode slow = head, fast = head;
        while(fast !=tail && fast.next != tail){
            slow = slow.next;
            fast = fast.next.next;
        }//寻找中间节点;

        TreeNode root = new TreeNode(slow.val);
        root.left = constru(head, slow);
        root.right = constru(slow.next, tail);

        return root;

    }
}

5.先序,中序,后序两两组合重构二叉树

先序,中序重构后序

6.搜索二叉树

7. 特殊的数结构

题目1. 222. 完全二叉树的节点个数

完全二叉树 的定义如下:在完全二叉树中,除了最底层节点可能没填满外,其余每层节点数都达到最大值,并且最下面一层的节点都集中在该层最左边的若干位置。若最底层为第 h 层,则该层包含 1~ 2h 个节点。

再来回顾一下满二叉的节点个数怎么计算,如果满二叉树的层数为h,则总节点数为:2^h - 1.
那么我们来对 root 节点的左右子树进行高度统计,分别记为 left 和 right,有以下两种结果:

left == right。这说明,左子树一定是满二叉树,因为节点已经填充到右子树了,左子树必定已经填满了。所以左子树的节点总数我们可以直接得到,是 2^left - 1,加上当前这个 root 节点,则正好是 2^left。再对右子树进行递归统计。
left != right。说明此时最后一层不满,但倒数第二层已经满了,可以直接得到右子树的节点个数。同理,右子树节点 +root 节点,总数为 2^right。再对左子树进行递归查找。

public int countNodes(TreeNode root) {
        if(root == null){
           return 0;
        } 
        int left = countLevel(root.left);
        int right = countLevel(root.right);
        if(left == right){//左子树填满
            return countNodes(root.right) + (1<<left);
        }else{//左子树不满
            return countNodes(root.left) + (1<<right);
        }
    }
    private int countLevel(TreeNode root){
        int level = 0;
        while(root != null){
            level++;
            root = root.left;
        }
        return level;
    }

作者:xiao-ping-zi-5
链接:https://leetcode-cn.com/problems/count-complete-tree-nodes/solution/chang-gui-jie-fa-he-ji-bai-100de-javajie-fa-by-xia/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

位运算

HJ15 求int型正整数在内存中存储时1的个数

求int型正整数在内存中存储时1的个数_牛客题霸_牛客网 (nowcoder.com)

输入一个 int 型的正整数,计算出该 int 型数据在内存中存储时 1 的个数。

数据范围:保证在 32 位整型数字范围内

输入描述:

输入一个整数(int类型)

输出描述:

这个数转换成2进制后,输出1的个数

import java.util.*;
public class Main{
    public static void main(String[] str){
        Scanner sc = new Scanner(System.in);
        int num = sc.nextInt();
        int res = 0;
        while(num>0){
            if((num &1) == 1){
                res++;
            }
            num = num >>1;
        }
        System.out.println(res);
        
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值