├── README.md └── StructandAlgorithm ├── src ├── Test │ ├── test2.java │ ├── findDepth.java │ ├── shunXuABC.java │ └── test1.java ├── DP │ ├── beibao │ │ └── beibao1.java │ ├── 动态规划 │ ├── IntChaiFen.java │ ├── lujinInMatrix │ │ ├── lujinInMatrix1.java │ │ └── lujinInMatrix2.java │ ├── paLouTiWithCost.java │ └── numOfSearchTree.java ├── util │ ├── swap.java │ ├── printArray.java │ ├── printMatrix.java │ ├── generateRandomArray.java │ ├── generateRandomMatrix.java │ ├── Array2ListNode.java │ └── PrintTree.java ├── Tree │ ├── TreeNode.java │ ├── findMaxHeight.java │ ├── UniqueBST.java │ ├── IsComplateTree.java │ └── tongGouTree.java ├── LinkedList │ ├── DoubleNode.java │ ├── ListNode.java │ ├── Find_daoshuK.java │ ├── mergeTwoLists.java │ └── findLoopNode.java ├── facing │ ├── DeleteS1FromS2.java │ ├── TenjinzhiTurn.java │ ├── zhezhi2Tree.java │ ├── mirrorTree_27.java │ ├── digui_getMax.java │ ├── sortArrayFindNum.java │ ├── BuLongFilter.java │ ├── MyRunnable.java │ ├── magicIsZhengchu3.java │ ├── didi │ │ ├── kuohao.java │ │ └── noDuplicationZuiChang.java │ ├── resverseList.java │ ├── yiDongYiCi.java │ ├── overhalfprint.java │ ├── findMinInDiZeng.java │ ├── combineAndpermute │ │ ├── combinaSumAsTarget3.java │ │ ├── combinaSumAsTarget4.java │ │ ├── permute1.java │ │ ├── dianHua.java │ │ ├── permute2.java │ │ ├── combine.java │ │ └── combinaSumAsTarget2.java │ ├── yihuo_Find_jishuge_shu.java │ ├── ArrayMaxAdd.java │ ├── meituan │ │ ├── minOption_04.java │ │ ├── manyidu.java │ │ ├── xulie_03.java │ │ └── quKongGe_2.java │ ├── huiwenshu.java │ ├── zhuanQiang.java │ ├── singleNonDuplicate.java │ ├── xiaogengdui_sort.java │ ├── orderArrayFinNum.java │ ├── compareVersion.java │ ├── MyCAS.java │ ├── CharMaxCommon.java │ ├── chiTang.java │ ├── ReverseNum.java │ ├── wanggeMinPath.java │ └── xiaYiGePaiLie.java ├── Graph │ ├── Graph.java │ ├── Edge.java │ ├── Node.java │ ├── BFS_graph.java │ ├── DFS_graph.java │ ├── GraphGenerator.java │ └── TopoSort.java ├── SwordOffer │ ├── jiaFa_65.java │ ├── bitCount_15.java │ ├── tiaoTaijie_10.java │ ├── multiply_66.java │ ├── LeiJia_64.java │ ├── Fibonaqi_10.java │ ├── lastNumInQuan_62.java │ ├── daoShuKInList_22.java │ ├── firstNoDuplicationInIO_41.java │ ├── missInSortArray_53.java │ ├── intPower_16.java │ ├── searchTreeK_54.java │ ├── duplicateNum_03.java │ ├── isShunZi_61.java │ ├── firstUniqChar_50.java │ ├── TwostackOnequeue_09.java │ ├── rectangleFuGai.java │ ├── IsPopOrder_31.java │ ├── deleteDuplicationAndBuBaoLiu_18.java │ ├── sumAsSInSortArray_57.java │ ├── reverseList_24.java │ ├── isNumber_20.java │ ├── HuanInLinkList_23.java │ ├── leftReverseString_58.java │ ├── choushu_49.java │ ├── minNumFromArray_45.java │ ├── ReverseSentence_58.java │ ├── isBalanced_55.java │ ├── isDuiChenTree_28.java │ ├── minCommonInTree_68.java │ ├── FindPathInTree_34.java │ ├── num2Str_46.java │ ├── PrintTreeFromTopToBottom_32.java │ ├── minCommonInSearchTree_68.java │ ├── middleInIO_41.java │ ├── replaceSpace_05.java │ ├── rebuildTree_07.java │ ├── overhalfprint_39.java │ ├── FindInMatrix_04.java │ ├── SouSuo2LinkNode_36.java │ ├── deleteLinkedListNode_18.java │ ├── maxLiRun_63.java │ ├── ShaiZi_60.java │ ├── firstCommon_52.java │ ├── RandomListNodeCopy_35.java │ ├── jiShuBeforeOushu_21.java │ ├── sumAsSXuLie_57.java │ ├── isHouXuInTree_33.java │ ├── timesInSortArray_53.java │ ├── zhiPrintTree_32.java │ ├── print1ToMaxOfNDigits_17.java │ ├── merge2SortList_25.java │ ├── ziDianXu_38.java │ ├── countDigitOne_43.java │ ├── jainShenZi_14.java │ └── onceInArray_56.java ├── BigData │ ├── is4Or2Power.java │ └── getMax.java ├── sort │ ├── InsertSort.java │ ├── SelectSort.java │ ├── BubbleSort.java │ └── MergeSort.java ├── MiddleClass │ ├── Quchongshuzidui.java │ ├── MaxSumInTree.java │ ├── xuanZhuanCi.java │ ├── nearMultiple4Times.java │ ├── ziJuZhenMaxhe.java │ ├── Rand5ToRand7.java │ ├── CordCoverMaxPoint.java │ ├── printNumberNoInArray.java │ ├── useOneStackToSort.java │ ├── isContainsIn2Matrix.java │ ├── eatGrass.java │ ├── beibaoBianzhong.java │ └── optionWithRule.java ├── recursion │ ├── Hannuo.java │ ├── reverseStack.java │ ├── num2String.java │ └── printAllSubsquence.java ├── Greedy │ ├── BestArrange.java │ └── lowestZiDianXu.java ├── exam │ └── TXexam1.java └── SQL │ └── 同时选2门 └── StructandAlgorithm.iml /README.md: -------------------------------------------------------------------------------- 1 | # StrcuctandAlgorithm 2 | 左神的初级和中级班代码 一步步跟着敲 22秋招结束 3 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/Test/test2.java: -------------------------------------------------------------------------------- 1 | package Test; 2 | 3 | /** 4 | * @author renyujie518 5 | * @version 1.0.0 6 | * @ClassName test2.java 7 | * @Description TODO 8 | * @createTime 2021年09月04日 16:23:00 9 | */ 10 | public class test2 { 11 | 12 | 13 | } 14 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/DP/beibao/beibao1.java: -------------------------------------------------------------------------------- 1 | package DP.beibao; 2 | 3 | /** 4 | * @author renyujie518 5 | * @version 1.0.0 6 | * @ClassName beibao1.java 7 | * @Description 01 背包 8 | * 有N件物品和一个最多能被重量为W 的背包。第i件物品的重量是weight[i],得到的价值是value[i] 。 9 | * 每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。 10 | * @createTime 2021年09月06日 16:43:00 11 | */ 12 | public class beibao1 { 13 | } 14 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/util/swap.java: -------------------------------------------------------------------------------- 1 | package util; 2 | 3 | /** 4 | * @author admin 5 | * @version 1.0.0 6 | * @ClassName swap.java 7 | * @Description 工具类 交换数组的两个元素 8 | * @createTime 2021年02月27日 19:45:00 9 | */ 10 | public interface swap { 11 | public static void swap(T[] list,int i,int j){ 12 | T temp = list[i]; 13 | list[i]= list[j]; 14 | list[j] = temp; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/Tree/TreeNode.java: -------------------------------------------------------------------------------- 1 | package Tree; 2 | 3 | /** 4 | * @author admin 5 | * @version 1.0.0 6 | * @ClassName TreeNode.java 7 | * @Description 二叉树 8 | * @createTime 2021年02月28日 17:06:00 9 | */ 10 | public class TreeNode { 11 | public int val; 12 | public TreeNode left; 13 | public TreeNode right; 14 | 15 | public TreeNode(int val) { 16 | this.val = val; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/LinkedList/DoubleNode.java: -------------------------------------------------------------------------------- 1 | package LinkedList; 2 | 3 | /** 4 | * @author admin 5 | * @version 1.0.0 6 | * @ClassName DoubleNode.java 7 | * @Description 双链表模拟 8 | * @createTime 2021年02月26日 15:03:00 9 | */ 10 | public class DoubleNode { 11 | int val; 12 | DoubleNode last; // 上一个 13 | DoubleNode next; 14 | 15 | DoubleNode(int val) { 16 | this.val = val; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/DP/动态规划: -------------------------------------------------------------------------------- 1 | 暴力递归到动态规划动态规划就是暴力尝试减少重复计算的技巧整, 2 | 而已这种技巧就是一个大型套路先写出用尝试的思路解决问题的递归函数, 3 | 而不用操心时间复杂度这个过程是无可替代的,没有套路的,只能依靠个人智慧,或者足够多的经验 4 | 5 | 但是怎么把尝试的版本,优化成动态规划,是有固定套路的,大体步骤如下 6 | 1)找到什么可变参数可以代表一个递归状态,也就是哪些参数一旦确定,返回值就确定了 7 | 2)把可变参数的所有组合映射成一张表,有1个可变参数就是一维表,2个可变参数就是二维表,...... 8 | 3)最终答案要的是表中的哪个位置,在表中标出 9 | 4)根据递归过程的basecase,把这张表的最简单、不需要依赖其他位置的那些位置填好值 10 | 5)根据递归过程非basecase的部分,也就是分析表中的普遍位置需要怎么计算得到,那么这张表的填写顺序也就确定了 11 | 6)填好表,返回最终答案在表中位置的值 -------------------------------------------------------------------------------- /StructandAlgorithm/src/facing/DeleteS1FromS2.java: -------------------------------------------------------------------------------- 1 | package facing; 2 | 3 | /** 4 | * @author admin 5 | * @version 1.0.0 6 | * @ClassName DeleteS1FromS2.java 7 | * @Description 删除字符串s1中s2出现的字符 8 | * 已知字符串 s1 s2: 9 | * string s1 = “aaskdauasdhdfhjbv”; 10 | * string s2 = “askdhjv”; 11 | * 12 | * 将s2的字符串单独作为一个字符,与s1的每一个字符串对比,如果都不是s2中出现的字符,用一个新字符串接收 13 | * @createTime 2021年03月17日 18:41:00 14 | */ 15 | public class DeleteS1FromS2 { 16 | 17 | } 18 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/LinkedList/ListNode.java: -------------------------------------------------------------------------------- 1 | package LinkedList; 2 | 3 | /** 4 | * @author admin 5 | * @version 1.0.0 6 | * @ClassName ListNode.java 7 | * @Description 单链表模拟 8 | * @createTime 2021年02月26日 14:26:00 9 | */ 10 | public class ListNode { 11 | public int val; 12 | public ListNode next; 13 | public ListNode() {} 14 | public ListNode(int val) { this.val = val; } 15 | ListNode(int val, ListNode next) { this.val = val; this.next = next; } 16 | } -------------------------------------------------------------------------------- /StructandAlgorithm/StructandAlgorithm.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/Graph/Graph.java: -------------------------------------------------------------------------------- 1 | package Graph; 2 | 3 | import java.util.HashMap; 4 | import java.util.HashSet; 5 | 6 | /** 7 | * @author admin 8 | * @version 1.0.0 9 | * @ClassName Graph.java 10 | * @Description TODO 11 | * @createTime 2021年03月14日 14:06:00 12 | */ 13 | public class Graph { 14 | public HashMap nodes; 15 | public HashSet edges; 16 | 17 | public Graph() { 18 | nodes = new HashMap<>(); 19 | edges = new HashSet<>(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/Graph/Edge.java: -------------------------------------------------------------------------------- 1 | package Graph; 2 | 3 | /** 4 | * @author admin 5 | * @version 1.0.0 6 | * @ClassName Edge.java 7 | * @Description TODO 8 | * @createTime 2021年03月14日 14:07:00 9 | */ 10 | public class Edge { 11 | public int weight; //权值 12 | public Node from; //注意,这个from 和to是针对有向图的,若是无向图就两个node互指即可 13 | public Node to; 14 | 15 | public Edge(int weight, Node from, Node to) { 16 | this.weight = weight; 17 | this.from = from; 18 | this.to = to; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/util/printArray.java: -------------------------------------------------------------------------------- 1 | package util; 2 | 3 | /** 4 | * @author renyujie518 5 | * @version 1.0.0 6 | * @ClassName printArray.java 7 | * @Description TODO 8 | * @createTime 2021年08月11日 17:07:00 9 | */ 10 | public class printArray { 11 | public static void printArray(int[] arr) { 12 | if (arr == null) { 13 | return; 14 | } 15 | for (int i = 0; i != arr.length; i++) { 16 | System.out.print(arr[i] + " "); 17 | } 18 | System.out.println(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/jiaFa_65.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | /** 4 | * @author renyujie518 5 | * @version 1.0.0 6 | * @ClassName jiaFa_65.java 7 | * @Description 不用加减乘除做加法 8 | * 写一个函数,求两个整数之和,要求不得使用 +、-、*、/ 四则运算符号。 9 | * 10 | * a ^ b 表示没有考虑进位的情况下两数的和,(a & b) << 1 就是进位。 11 | * 递归会终止的原因是 (a & b) << 1 最右边会多一个 0,那么继续递归,进位最右边的 0 会慢慢增多,最后进位会变为 0,递归终止。 12 | * @createTime 2021年08月28日 18:13:00 13 | */ 14 | public class jiaFa_65 { 15 | public int Add(int a, int b) { 16 | return b == 0 ? a : Add(a ^ b, (a & b) << 1); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/Tree/findMaxHeight.java: -------------------------------------------------------------------------------- 1 | package Tree; 2 | 3 | /** 4 | * @author admin 5 | * @version 1.0.0 6 | * @ClassName findMaxHeight.java 7 | * @Description 寻找二叉树最大深度 8 | * @createTime 2021年03月07日 23:11:00 9 | */ 10 | public class findMaxHeight { 11 | public int maxDepth(TreeNode root) { 12 | if(root == null){ 13 | return 0; 14 | } 15 | int leftHeight = maxDepth(root.left); 16 | int rightHeight = maxDepth(root.right); 17 | return Math.max(leftHeight,rightHeight)+1; 18 | } 19 | 20 | 21 | } 22 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/util/printMatrix.java: -------------------------------------------------------------------------------- 1 | package util; 2 | 3 | /** 4 | * @author renyujie518 5 | * @version 1.0.0 6 | * @ClassName printMatrix.java 7 | * @Description 打印一个二维矩阵 8 | * @createTime 2021年07月28日 16:53:00 9 | */ 10 | public class printMatrix { 11 | public static void printMatrix(int[][] matrix) { 12 | for (int i = 0; i != matrix.length; i++) { 13 | for (int j = 0; j != matrix[0].length; j++) { 14 | System.out.print(matrix[i][j] + " "); 15 | } 16 | System.out.println(); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/util/generateRandomArray.java: -------------------------------------------------------------------------------- 1 | package util; 2 | 3 | /** 4 | * @author admin 5 | * @version 1.0.0 6 | * @ClassName generateRandomArray.java 7 | * @Description 指定数组的长度和最大值 生成一个随机数组 8 | * @createTime 2021年07月25日 20:07:00 9 | */ 10 | public class generateRandomArray { 11 | public static String[] generateRandomArray(int len, int max) { 12 | String[] res = new String[len]; 13 | for (int i = 0; i != len; i++) { 14 | res[i] = String.valueOf((int) (Math.random() * (max + 1))); 15 | } 16 | return res; 17 | } 18 | } 19 | 20 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/bitCount_15.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | /** 4 | * @author renyujie518 5 | * @version 1.0.0 6 | * @ClassName bitCount_15.java 7 | * @Description 二进制中 1 的个数 8 | * 输入一个整数,输出该数二进制表示中 1 的个数。 9 | * 技巧: 10 | * n&(n-1) 11 | * 该位运算去除 n 的位级表示中最低的那一位的1。 12 | * n : 10110100 13 | * n-1 : 10110011 14 | * n&(n-1) : 10110000 15 | * @createTime 2021年08月18日 16:32:00 16 | */ 17 | public class bitCount_15 { 18 | public int NumberOf1(int n) { 19 | int cnt = 0; 20 | while (n != 0) { 21 | //得先++,否恩泽就被消掉了 22 | cnt++; 23 | n &= (n - 1); 24 | } 25 | return cnt; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/tiaoTaijie_10.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | import java.util.Arrays; 4 | 5 | /** 6 | * @author renyujie518 7 | * @version 1.0.0 8 | * @ClassName tiaoTaijie_10.java 9 | * @Description 10 | * 一只青蛙一次可以跳上 1 级台阶,也可以跳上 2 级... 它也可以跳上 n 级。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。 11 | * @createTime 2021年08月17日 21:56:00 12 | */ 13 | public class tiaoTaijie_10 { 14 | 15 | 16 | public static int JumpFloorII(int target) { 17 | int[] dp = new int[target]; 18 | Arrays.fill(dp, 1); 19 | for (int i = 1; i < target; i++) 20 | for (int j = 0; j < i; j++) 21 | dp[i] += dp[j]; 22 | return dp[target - 1]; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/Graph/Node.java: -------------------------------------------------------------------------------- 1 | package Graph; 2 | 3 | import java.util.ArrayList; 4 | 5 | /** 6 | * @author admin 7 | * @version 1.0.0 8 | * @ClassName Node.java 9 | * @Description TODO 10 | * @createTime 2021年03月14日 14:07:00 11 | */ 12 | public class Node { 13 | public int value; //自己的数据项 点上的值 14 | public int in; //入度 有多少点直接接入(无向图就是连的边数) 15 | public int out; 16 | public ArrayList nexts; //发散出去的直连的点 17 | public ArrayList edges; //发散出去的边 18 | 19 | public Node(int value) { 20 | this.value = value; 21 | in = 0; 22 | out = 0; 23 | nexts = new ArrayList<>(); 24 | edges = new ArrayList<>(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/facing/TenjinzhiTurn.java: -------------------------------------------------------------------------------- 1 | package facing; 2 | 3 | import java.util.Stack; 4 | 5 | /** 6 | * @author admin 7 | * @version 1.0.0 8 | * @ClassName TenjinzhiTurn.java 9 | * @Description 将十进制数转换为n(2<=n<=16)进制数,原理跟数学上的转换方法相同, 10 | * 即对一个数不断的进行除n取余运算,直至商为0,将余数倒序排出即为转换结果。 11 | * @createTime 2021年03月17日 18:22:00 12 | */ 13 | public class TenjinzhiTurn { 14 | public static void convert(int number, int system) { 15 | Stack stack = new Stack(); 16 | 17 | while (number > 0) { 18 | stack.add(number % system); 19 | number /= system; 20 | } 21 | 22 | while (!stack.empty()) { 23 | System.out.print(stack.pop()); 24 | } 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/facing/zhezhi2Tree.java: -------------------------------------------------------------------------------- 1 | package facing; 2 | 3 | /** 4 | * @author admin 5 | * @version 1.0.0 6 | * @ClassName zhezhi2Tree.java 7 | * @Description 张纸条对折后的结果(中序遍历一棵树) 8 | * @createTime 2021年03月06日 22:56:00 9 | */ 10 | public class zhezhi2Tree { 11 | public static void main(String[] args) { 12 | int N = 3; 13 | printProcess(1,N,true); //先对折一下是凹 14 | } 15 | 16 | //递归过程 来到某一个节点 i是层数 N 一共的层数 down = true 凹 down = false 凸 17 | public static void printProcess(int i,int N,boolean down){ 18 | if (i>N){ 19 | return; 20 | } 21 | printProcess(i+1,N,true); //先是凹 比如对折一下 22 | System.out.println(down ? "凹" : "凸"); 23 | printProcess(i+1,N,false); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/util/generateRandomMatrix.java: -------------------------------------------------------------------------------- 1 | package util; 2 | 3 | /** 4 | * @author renyujie518 5 | * @version 1.0.0 6 | * @ClassName generateRandomMatrix.java 7 | * @Description 创建随机值得二维矩阵 8 | * @createTime 2021年07月28日 16:52:00 9 | */ 10 | public class generateRandomMatrix { 11 | public static int[][] generateRandomMatrix(int rowSize, int colSize) { 12 | if (rowSize < 0 || colSize < 0) { 13 | return null; 14 | } 15 | int[][] result = new int[rowSize][colSize]; 16 | for (int i = 0; i != result.length; i++) { 17 | for (int j = 0; j != result[0].length; j++) { 18 | result[i][j] = (int) (Math.random() * 10); 19 | } 20 | } 21 | return result; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/multiply_66.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | /** 4 | * @author renyujie518 5 | * @version 1.0.0 6 | * @ClassName multiply_66.java 7 | * @Description 构建乘积数组 8 | * 给定一个数组 A[0, 1,..., n-1],请构建一个数组 B[0, 1,..., n-1], 9 | * 其中 B 中的元素 B[i]=A[0]*A[1]*...*A[i-1]*A[i+1]*...*A[n-1]。要求不能使用除法。(把i位置扣掉了) 10 | * @createTime 2021年08月28日 18:15:00 11 | */ 12 | public class multiply_66 { 13 | 14 | public int[] multiply(int[] A) { 15 | int n = A.length; 16 | int[] B = new int[n]; 17 | for (int i = 0, product = 1; i < n; product *= A[i], i++) /* 从左往右累乘 */ 18 | B[i] = product; 19 | for (int i = n - 1, product = 1; i >= 0; product *= A[i], i--) /* 从右往左累乘 */ 20 | B[i] *= product; 21 | return B; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/LeiJia_64.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | /** 4 | * @author renyujie518 5 | * @version 1.0.0 6 | * @ClassName LeiJia_64.java 7 | * @Description 求 1+2+3+...+n 8 | * 要求不能使用乘除法、for、while、if、else、switch、case 等关键字及条件判断语句 A ? B : C。 9 | * 10 | * 使用递归解法最重要的是指定返回条件,但是本题无法直接使用 if 语句来指定返回条件。 11 | * 条件与 && 具有短路原则,即在第一个条件语句为 false 的情况下不会去执行第二个条件语句。 12 | * 利用这一特性,将递归的返回条件取非然后作为 && 的第一个条件语句,递归的主体转换为第二个条件语句 13 | * ,那么当递归的返回条件为 true 的情况下就不会执行递归的主体部分,递归返回。 14 | * 15 | * 本题的递归返回条件为 n <= 0,取非后就是 n > 0; 16 | * 递归的主体部分为 sum += Sum_Solution(n - 1),转换为条件语句后就是 (sum += Sum_Solution(n - 1)) > 0。 17 | * @createTime 2021年08月28日 18:07:00 18 | */ 19 | public class LeiJia_64 { 20 | 21 | public int Sum_Solution(int n) { 22 | int sum = n; 23 | boolean b = (n > 0) && ((sum += Sum_Solution(n - 1)) > 0); 24 | return sum; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/util/Array2ListNode.java: -------------------------------------------------------------------------------- 1 | package util; 2 | 3 | import LinkedList.ListNode; 4 | 5 | /** 6 | * @author renyujie518 7 | * @version 1.0.0 8 | * @ClassName Array2ListNode.java 9 | * @Description TODO 10 | * @createTime 2021年08月08日 16:36:00 11 | */ 12 | public class Array2ListNode { 13 | public static ListNode Array2ListNode(String[] str) { 14 | int[] data = new int[str.length]; 15 | for (int i = 0; i < str.length; i++) { 16 | data[i] = Integer.parseInt(str[i]); 17 | } 18 | //这里想把输入的一个数组变为链表 19 | ListNode head = new ListNode(0); 20 | ListNode p = head; 21 | for(int i = 0; i < data.length; i++){ 22 | p.next = new ListNode(data[i]); 23 | p = p.next; 24 | } 25 | //保证最后一位是null 26 | head = head.next; 27 | return head; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/facing/mirrorTree_27.java: -------------------------------------------------------------------------------- 1 | package facing; 2 | 3 | import Tree.TreeNode; 4 | 5 | /** 6 | * @author renyujie518 7 | * @version 1.0.0 8 | * @ClassName mirrorTree_27.java 9 | * @Description T请完成一个函数,输入一个二叉树,该函数输出它的镜像。 10 | * ,递归地对树进行遍历,并从叶子节点先开始翻转得到镜像。如果当前遍历到的节点 root 的左右两棵子树都已经翻转得到镜像, 11 | * 那么我们只需要交换两棵子树的位置,即可得到以 \textit{root}root 为根节点的整棵子树的镜像。 12 | 13 | * @createTime 2021年08月19日 21:53:00 14 | */ 15 | public class mirrorTree_27 { 16 | public static void mirrorWithRecorsion(TreeNode root) { 17 | if (root == null) 18 | return; 19 | swap(root); 20 | mirrorWithRecorsion(root.left); 21 | mirrorWithRecorsion(root.right); 22 | } 23 | 24 | private static void swap(TreeNode root) { 25 | TreeNode t = root.left; 26 | root.left = root.right; 27 | root.right = t; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/facing/digui_getMax.java: -------------------------------------------------------------------------------- 1 | package facing; 2 | 3 | import util.bijiaoqi_arrays_sort; 4 | 5 | /** 6 | * @author admin 7 | * @version 1.0.0 8 | * @ClassName digui_getMax.java 9 | * @Description 递归寻找最大数 10 | * @createTime 2021年02月08日 21:35:00 11 | */ 12 | public class digui_getMax { 13 | public static void main(String[] args) { 14 | int[] arrs = {3,4,5,123,45,13,435,765,12,1,2,3,345,56,78,9,3}; 15 | int result = process(arrs, 0, arrs.length-1); 16 | System.out.println(result); 17 | 18 | } 19 | 20 | public static int process(int[] arrs,int L,int R){ 21 | if (L==R){ 22 | return arrs[L]; 23 | } 24 | 25 | int mid = L+((R-L)>>1); 26 | int leftMax = process(arrs, L, mid); 27 | int rightMax = process(arrs, mid+1, R); 28 | return Math.max(leftMax, rightMax); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/Fibonaqi_10.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | /** 4 | * @author renyujie518 5 | * @version 1.0.0 6 | * @ClassName Fibonaqi_10.java 7 | * @Description 求斐波那契数列的第 n 项,n <= 39。 8 | * 如果使用递归求解,会重复计算一些子问题。例如,计算 f(4) 需要计算 f(3) 和 f(2), 9 | * 计算 f(3) 需要计算 f(2) 和 f(1),可以看到 f(2) 被重复计算了。 10 | * 11 | * 递归是将一个问题划分成多个子问题求解,动态规划也是如此,但是动态规划会把子问题的解缓存起来,从而避免重复求解子问题。 12 | * 考虑到第 i 项只与第 i-1 和第 i-2 项有关,因此只需要存储前两项的值就能求解第 i 项,从而将空间复杂度由 O(N) 降低为 O(1)。 13 | * 由于待求解的 n 小于 40,因此可以将前 40 项的结果先进行计算,之后就能以 O(1) 时间复杂度得到第 n 项的值。 14 | * @createTime 2021年08月17日 20:29:00 15 | */ 16 | public class Fibonaqi_10 { 17 | private static int[] fib = new int[40]; 18 | 19 | public static int Fibonaqi(int n) { 20 | fib[1] = 1; 21 | for (int i = 2; i < fib.length; i++) { 22 | fib[i] = fib[i - 1] + fib[i - 2]; 23 | } 24 | return fib[n]; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/lastNumInQuan_62.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | /** 4 | * @author renyujie518 5 | * @version 1.0.0 6 | * @ClassName lastNumInQuan_62.java 7 | * @Description 8 | * 0, 1,···,n-1这n个数字排成一个圆圈,从数字0开始,每次从这个圆圈里删除第m个数字(删除后从下一个数字开始计数)。 9 | * 求出这个圆圈里剩下的最后一个数字。 10 | * 11 | * 例如,0、1、2、3、4这5个数字组成一个圆圈,从数字0开始每次删除第3个数字,则删除的前4个数字依次是2、0、4、1,因此最后剩下的数字是3。 12 | * “约瑟夫环” 问题,可使用 动态规划 解决 13 | * 数学证明: 14 | * https://leetcode-cn.com/problems/yuan-quan-zhong-zui-hou-sheng-xia-de-shu-zi-lcof/solution/si-bu-he-xin-gong-shi-qing-song-nong-don-3vln/ 15 | * 设「i, m问题」的解为 dp[i]; 16 | * dp[i]=(dp[i−1]+m)%i 17 | * dp[1]=0 18 | * @createTime 2021年08月28日 17:45:00 19 | */ 20 | public class lastNumInQuan_62 { 21 | public int lastRemaining(int n, int m) { 22 | int x = 0; 23 | for (int i = 2; i <= n; i++) { 24 | x = (x + m) % i; 25 | } 26 | return x; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/facing/sortArrayFindNum.java: -------------------------------------------------------------------------------- 1 | package facing; 2 | 3 | /** 4 | * @author renyujie518 5 | * @version 1.0.0 6 | * @ClassName sortArrrayFindNum.java 7 | * @Description 8 | * 统计一个数字在排序数组中出现的次数。 9 | * 10 | * 一看到排序,首先想到二分 11 | * @createTime 2021年08月30日 17:46:00 12 | */ 13 | public class sortArrayFindNum { 14 | public int sortArrayFindNum(int[] nums, int k) { 15 | int n = nums.length; 16 | int l = 0, r = n - 1; 17 | while (l < r) { 18 | int mid = l + (r - l) / 2; 19 | if (nums[mid] <= k) { 20 | l = mid; 21 | } else { 22 | r = mid - 1; 23 | } 24 | } 25 | //这时候l= mid = r, 而且 nums[mid] = k 26 | //要找出现的次数 比如k = 3 3 3 3 4 27 | int cishu = 0; 28 | while (r > 0 && nums[r] == k && r-- > 0) { 29 | cishu++; 30 | } 31 | return cishu; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/daoShuKInList_22.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | import LinkedList.ListNode; 4 | 5 | /** 6 | * @author renyujie518 7 | * @version 1.0.0 8 | * @ClassName daoShuKInList.java 9 | * @Description 链表中倒数第 K 个结点 10 | * 思路: 11 | * 设链表的长度为 N。设置两个指针 P1 和 P2,先让 P1 移动 K 个节点,则还有 N - K 个节点可以移动。 12 | * 此时让 P1 和 P2 同时移动,可以知道当 P1 移动到链表结尾时,P2 移动到第 N - K 个节点处,该位置就是倒数第 K 个节点。(返回p2) 13 | * @createTime 2021年08月19日 21:06:00 14 | */ 15 | public class daoShuKInList_22 { 16 | public static ListNode daoShuKInList(ListNode head, int k) { 17 | if (head == null) { 18 | return null; 19 | } 20 | ListNode p1 = head; 21 | while (p1 != null && (k--) > 0) { 22 | p1 = p1.next; 23 | } 24 | ListNode p2 = head; 25 | while (p1 != null) { 26 | p1 = p1.next; 27 | p2 = p2.next; 28 | 29 | } 30 | return p2; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/BigData/is4Or2Power.java: -------------------------------------------------------------------------------- 1 | package BigData; 2 | 3 | /** 4 | * @author admin 5 | * @version 1.0.0 6 | * @ClassName is4Or2Power.java 7 | * @Description 判断一个32位正数是不是2的幂、4的幂 8 | * @createTime 2021年03月26日 22:21:00 9 | */ 10 | public class is4Or2Power { 11 | public static boolean is2Power(int n) {//2进制中只能有一个数是1就是2的幂 比如8421 1000 = 8 0100 =4 12 | //一个思路是:先拿出一个数最右侧的1,和原来数相等,代表只有1个1 就是2的幂 13 | return (n & (n - 1)) == 0;//还有一个思路是:假设一个数2进制只有一个1 如果-1,则会把这个唯一的1打散 ,比如1000 -1 = 0111 14 | //这时候再与原数& 1000&0111 = 0000 必须为0 15 | } 16 | 17 | public static boolean is4Power(int n) {//是4的幂的前提得是2的幂(二进制只有一个1 )00100 ,10000都得是奇数位上 18 | return (n & (n - 1)) == 0 && (n & 0x55555555) != 0; //0x55555555 = 01..10101 一旦&且结果不为0说明奇数位都是1 19 | } 20 | 21 | public static void main(String[] args) { 22 | System.out.println(is2Power(8)); 23 | System.out.println(is4Power(16)); 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/firstNoDuplicationInIO_41.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | import java.util.LinkedList; 4 | import java.util.Queue; 5 | 6 | /** 7 | * @author renyujie518 8 | * @version 1.0.0 9 | * @ClassName firstNoDuplicationInIO_41.java 10 | * @Description 字符流中第一个不重复的字符 11 | * 例如,当从字符流中只读出前两个字符 "go" 时,第一个只出现一次的字符是 "g"。 12 | * 当从该字符流中读出前六个字符“google" 时,第一个只出现一次的字符是 "l"。 13 | * @createTime 2021年08月24日 11:48:00 14 | */ 15 | public class firstNoDuplicationInIO_41 { 16 | private int[] cnts = new int[256]; 17 | private Queue queue = new LinkedList<>(); 18 | 19 | public void Insert(char ch) { 20 | cnts[ch]++;//构建字典表 对应的索引是相应的AscII,对应的值是出现的次数 21 | queue.add(ch); 22 | while (!queue.isEmpty() && cnts[queue.peek()] > 1) {//第一次出现的在队列的首(先进先出),该位置的值大于1就不是第一次出现,弹出 23 | queue.poll(); 24 | } 25 | } 26 | 27 | public char firstNoDuplicationInIO(){ 28 | return queue.isEmpty() ? '@' : queue.peek(); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/sort/InsertSort.java: -------------------------------------------------------------------------------- 1 | package sort; 2 | 3 | /** 4 | * @author admin 5 | * @version 1.0.0 6 | * @ClassName InsertSort.java 7 | * @Description 插入排序 8 | * @createTime 2021年02月08日 20:16:00 9 | */ 10 | public class InsertSort { 11 | public static void main(String[] args) { 12 | int[] arrs = {3,4,5,123,45,13,435,765,12,1,2,3,345,56,78,9,3}; 13 | inserttsort(arrs); 14 | } 15 | 16 | public static void inserttsort(int[] arrs){ 17 | if (arrs ==null || arrs.length<2){ 18 | return; 19 | } 20 | for (int i = 1; i < arrs.length; i++) {//0~0已经有序,不用排 21 | for (int j = i-1;j >= 0 && arrs[j]>arrs[j+1];j--){ //j是i的前一个数 22 | swap(arrs,j,j+1); 23 | } 24 | } 25 | for (int arr : arrs) { 26 | System.out.println(arr); 27 | } 28 | } 29 | 30 | public static void swap(int[] arrs,int i,int j){ 31 | int temp = arrs[i]; 32 | arrs[i]= arrs[j]; 33 | arrs[j] = temp; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/MiddleClass/Quchongshuzidui.java: -------------------------------------------------------------------------------- 1 | package MiddleClass; 2 | 3 | import LinkedList.ListNode; 4 | 5 | import java.util.ArrayList; 6 | import java.util.Arrays; 7 | import java.util.HashSet; 8 | import java.util.List; 9 | 10 | /** 11 | * @author admin 12 | * @version 1.0.0 13 | * @ClassName Quchongshuzidui.java 14 | * @Description 给定一个数组arr,求差值为k的去重数字对。 15 | * 比如[3,2,5,0,7,0] k=2 (0,2)这个数字对只能出现一次 (5,7) (3,5) 16 | * 不重复 利用hashset 遍历每个元素 看arr[i]+k在不在set中 17 | * @createTime 2021年03月31日 21:08:00 18 | */ 19 | public class Quchongshuzidui { 20 | public static List> Quchongshuzidui(int[] arr,int k){ 21 | HashSet set = new HashSet<>(); 22 | for (int i = 0; i < arr.length; i++) { 23 | set.add(arr[i]); 24 | } 25 | List> res = new ArrayList<>(); 26 | for (Integer curr : set) { 27 | if (set.contains(curr+k)){ 28 | res.add(Arrays.asList(curr, curr + k)); 29 | } 30 | } 31 | return res; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/Test/findDepth.java: -------------------------------------------------------------------------------- 1 | package Test; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Comparator; 5 | import java.util.List; 6 | 7 | /** 8 | * @author admin 9 | * @version 1.0.0 10 | * @ClassName findDepth.java 11 | * @Description 阿里笔试题 找到一个多维数组的维度 12 | * @createTime 2021年03月16日 00:00:00 13 | */ 14 | public class findDepth { 15 | 16 | public static int findDepth(ArrayList array){ 17 | List depths = new ArrayList<>(); 18 | for (int i = 0; i () { 28 | @Override 29 | public int compare(Integer o1, Integer o2) { 30 | return o1-o2; //升序 31 | } 32 | }); 33 | return depths.get(depths.size() - 1) + 1; 34 | } 35 | } 36 | 37 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/missInSortArray_53.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | /** 4 | * @author renyujie518 5 | * @version 1.0.0 6 | * @ClassName missInSortArray_53.java 7 | * @Description 0~n-1中缺失的数字 8 | 一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0~n-1之内。 9 | 在范围0~n-1内的n个数字中有且只有一个数字不在该数组中,请找出这个数字。 10 | 输入: [0,1,2,3,4,5,6,7,9] 11 | 输出: 8 12 | 13 | 14 | 排序数组中的搜索问题,首先想到 二分法 解决。 15 | 根据题意,数组可以按照以下规则划分为两部分。 16 | 左子数组: nums[i] = i ; 17 | 右子数组: nums[i] != i 18 | 缺失的数字等于 “右子数组的首位元素” 对应的索引;因此考虑使用二分法查找 “右子数组的首位元素” 。 19 | 。 20 | * @createTime 2021年08月27日 16:12:00 21 | */ 22 | public class missInSortArray_53 { 23 | public static int missInSortArray(int[] nums) { 24 | int i = 0; 25 | int j = nums.length - 1; 26 | while(i <= j) { 27 | int m = i + (j - i) / 2; 28 | if(nums[m] == m){ 29 | i = m + 1; 30 | } 31 | else{ 32 | j = m - 1; 33 | } 34 | } 35 | // 变量 i 和 j 分别指向 “右子数组的首位元素” 和 “左子数组的末位元素” 。因此返回 i 即可 36 | return i; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/facing/BuLongFilter.java: -------------------------------------------------------------------------------- 1 | package facing; 2 | 3 | /** 4 | * @author admin 5 | * @version 1.0.0 6 | * @ClassName BuLongFilter.java 7 | * @Description 模拟布隆过滤器 8 | * @createTime 2021年03月20日 21:34:00 9 | */ 10 | public class BuLongFilter { 11 | 12 | //首先设置位图 13 | public static void main(String[] args) { 14 | int a = 0; 15 | //一个int 4字节 1个字节8bit 16 | int[] arr = new int[10]; //32*10 bit 17 | //想取得178bit的状态 18 | int i= 178; 19 | //先定义在10个数中哪个数去找 20 | int numIndex = i / 32; 21 | //这个数有32位,具体在哪个数的第几位 22 | int bitIndex = i % 32; 23 | //拿到第178位的状态 首先把这个数右移bitIndex 位,就是把目标移到最右了,再拿出来,s不是1就是0 24 | int s = ((arr[numIndex] >> (bitIndex)) & 1); 25 | //把i位的状态改为1 (注意 其他位保持不变) 把1左移bitIndex这么多位再或arr[numIndex] 26 | arr[numIndex] = arr[numIndex] | (1 << (bitIndex)); 27 | //把i位的状态改为0 (注意 其他位保持不变) 把1左移bitIndex这么多位再取反,那得到了除了第i位为0其余都是1这样的数 再与arr[numIndex] 28 | arr[numIndex] = arr[numIndex] & (~(1 << (bitIndex))); 29 | 30 | 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/intPower_16.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | /** 4 | * @author renyujie518 5 | * @version 1.0.0 6 | * @ClassName intPower_16.java 7 | * @Description 数值的整数次方 8 | * 给定一个 double 类型的浮点数 base 和 int 类型的整数 exponent,求 base 的 exponent 次方。 base^exp 9 | *比如要求x^11 正常的乘积需要循环乘11次,时间复杂度为O(n) 10 | * 将指数11 可以转成二进制数1011,则原来的式子可以转化成 11 | * x^11 = x^(2^3+2^1+2^0) = x^(2^3) * x(2^1) * x(2^0) 12 | * 此时只运算了3次乘积,时间复杂度降至O(logn) 13 | * @createTime 2021年08月18日 16:34:00 14 | */ 15 | public class intPower_16 { 16 | public static double myPow(double x, int n) { 17 | if (x == 0) { 18 | return 0; 19 | } 20 | long b = n; 21 | double res = 1.0; 22 | if (b < 0) { 23 | x = 1 / x; 24 | b = -b; 25 | } 26 | while (b > 0) { 27 | // 最后一位为1,需要乘上该位上的权重 28 | if ((b & 1) == 1) { 29 | res *= x; 30 | } 31 | //累乘 32 | x *= x; 33 | b = b >> 1; 34 | } 35 | return res; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/recursion/Hannuo.java: -------------------------------------------------------------------------------- 1 | package recursion; 2 | 3 | /** 4 | * @author admin 5 | * @version 1.0.0 6 | * @ClassName Hannuo.java 7 | * @Description 汉诺塔 只能小压大 8 | * @createTime 2021年03月18日 13:35:00 9 | */ 10 | public class Hannuo { 11 | //1-i的圆盘(i越大,圆盘越大) 目标时from ->to other是另外一个柱子 !!每个子问题都保证自己的底满足规则即可 12 | public static void func(int i,String start,String end,String other){ 13 | if (i == 1){//base case 只剩下最小的盘 直接放就可以 14 | System.out.println("移动 1 号盘,从"+start+"到"+end); 15 | }else { 16 | func(i-1,start,other,end);//先把i-1(大盘子i的上一个)放到辅助柱子上 17 | System.out.println("移动"+i+" 号盘,从"+start+"到"+end);//第i-1的先搁置一边,这时候代表压在(i-1)下的大盘子i可以正确的放到目标柱子 18 | func(i - 1, other, end, start);//再把i-1从辅助柱子移到目标柱子。这时候满足规则,只能小压大 19 | 20 | } 21 | } 22 | 23 | public static void hannuo(int n){ 24 | if (n>0){ 25 | func(n,"左","右","中"); 26 | } 27 | } 28 | 29 | public static void main(String[] args) { 30 | int n =3; 31 | hannuo(3); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/Graph/BFS_graph.java: -------------------------------------------------------------------------------- 1 | package Graph; 2 | 3 | import java.util.HashSet; 4 | import java.util.LinkedList; 5 | import java.util.Queue; 6 | 7 | /** 8 | * @author admin 9 | * @version 1.0.0 10 | * @ClassName BFS_graph.java 11 | * @Description 图的宽度优先遍历 利用队列 12 | * @createTime 2021年03月14日 15:29:00 13 | */ 14 | public class BFS_graph { 15 | public static void bfs(Node node){ 16 | if (node == null){ 17 | return; 18 | } 19 | 20 | Queue queue = new LinkedList(); 21 | HashSet set = new HashSet(); //利用set的key唯一机制,保证在下一层的时候不会再去考虑上一层的连接,避免死循环 22 | queue.add(node); 23 | set.add(node); 24 | while (!queue.isEmpty()){ 25 | Node curr = queue.poll(); 26 | System.out.println(curr.value);//弹出就打印 27 | for (Node next : curr.nexts) { //往下一节点走 28 | if (!set.contains(next)){ //不包含才继续往队列和set里添加 29 | set.add(next); 30 | queue.add(next); 31 | } 32 | } 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/searchTreeK_54.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | import Tree.TreeNode; 4 | 5 | /** 6 | * @author renyujie518 7 | * @version 1.0.0 8 | * @ClassName searchTreeK_54.java 9 | * @Description 二叉查找树的第 K 大结点 10 | * 二叉搜索树(左小右大)的中序遍历(左中右)为 递增序列 11 | * 二叉搜索树的 中序遍历倒序(右中左) 为 递减序列 12 | * “二叉搜索树第 k 大的节点” 可转化为求 “此树的中序遍历倒序的第 k个节点”。 13 | * @createTime 2021年08月27日 16:26:00 14 | */ 15 | public class searchTreeK_54 { 16 | private int res; 17 | private int k; 18 | 19 | public int searchTreeK(TreeNode root, int k) { 20 | this.k = k; 21 | dfs(root); 22 | return res; 23 | } 24 | 25 | public void dfs(TreeNode root) { 26 | if (root == null) { 27 | return; 28 | } 29 | //中序遍历倒序(右中左) 30 | dfs(root.right); 31 | if (k == 0) {//先设置终止条件,因为后面会--k 32 | return; 33 | } 34 | //判断第k个节点可以想象为使用,我每使用个节点,就-1,减完了就是第k个 35 | //为什么先--,很明显,上面的right已经先用过了 36 | if (--k == 0) { 37 | res = root.val; 38 | } 39 | dfs(root.right); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/duplicateNum_03.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | import java.util.HashSet; 4 | 5 | /** 6 | * @author renyujie518 7 | * @version 1.0.0 8 | * @ClassName duplicateNum.java 9 | * @Description 10 | * 11 | * 在一个长度为 n 的数组里的所有数字都在 0 到 n-1 的范围内。 12 | * 数组中某些数字是重复的,但不知道有几个数字是重复的,也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 13 | * Input: 14 | * {2, 3, 1, 0, 2, 5} 15 | * 16 | * Output: 17 | * 2 18 | * 19 | 20 | * 要求时间复杂度 O(N),空间复杂度 O(1)。因此不能使用排序的方法,也不能使用额外的标记数组。 21 | * 对于这种数组元素在 [0, n-1] 范围内的问题,可以将值为 i 的元素调整到第 i 个位置上进行求解。 22 | * 以 (2, 3, 1, 0, 2, 5) 为例,遍历到位置 4 时,该位置上的数为 2,但是第 2 个位置上已经有一个 2 的值了,因此可以知道 2 重复: 23 | * @createTime 2021年08月17日 14:17:00 24 | */ 25 | public class duplicateNum_03 { 26 | public int duplicateNum(int[] nums) { 27 | //由于只需要找出数组中任意一个重复的数字,因此遍历数组,遇到重复的数字即返回 28 | HashSet set = new HashSet<>(); 29 | int repeat = -1; 30 | for (int num : nums) { 31 | if (set.contains(num)) { 32 | return num; 33 | } 34 | set.add(num); 35 | } 36 | return -1;//否则返回-1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/isShunZi_61.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | import java.util.HashSet; 4 | import java.util.Set; 5 | 6 | /** 7 | * @author renyujie518 8 | * @version 1.0.0 9 | * @ClassName shunZi_61.java 10 | * @Description 五张牌,其中大小鬼为癞子,牌面为 0。判断这五张牌是否能组成顺子。 11 | * 1 0 3 4 5 true 12 | * 1 2 4 6 7 false 13 | * 14 | * 遍历五张牌,遇到大小王(即 0 )直接跳过。 15 | * 判别重复: 利用 Set 实现遍历判重, Set 的查找方法的时间复杂度为 O(1) 16 | * 获取最大 / 最小的牌 最大牌 - 最小牌 < 5 则可构成顺子 17 | * @createTime 2021年08月28日 17:39:00 18 | */ 19 | public class isShunZi_61 { 20 | public boolean isShunZi(int[] nums) { 21 | Set set = new HashSet<>(); 22 | int max = 0, min = 14; 23 | for(int num : nums) { 24 | if(num == 0){ 25 | continue; // 跳过大小王 26 | } 27 | max = Math.max(max, num); // 最大牌 28 | min = Math.min(min, num); // 最小牌 29 | if (set.contains(num)) { 30 | return false;//有重复肯定不是顺子 31 | } 32 | set.add(num); 33 | } 34 | return max - min < 5; // 最大牌 - 最小牌 < 5 则可构成顺子 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/facing/MyRunnable.java: -------------------------------------------------------------------------------- 1 | package facing; 2 | 3 | import java.util.Date; 4 | 5 | /** 6 | * @author admin 7 | * @version 1.0.0 8 | * @ClassName MyRunnable.java 9 | * @Description TODO 10 | * @createTime 2021年03月15日 14:35:00 11 | */ 12 | public class MyRunnable implements Runnable{ 13 | private String command; 14 | 15 | public MyRunnable(String s) { 16 | this.command = s; 17 | } 18 | 19 | @Override 20 | public void run() { 21 | System.out.println(Thread.currentThread().getName() + " Start. Time = " + new Date()); 22 | processCommand(); 23 | System.out.println(Thread.currentThread().getName() + " End. Time = " + new Date()); 24 | 25 | } 26 | private void processCommand() { 27 | try { 28 | Thread.sleep(5000); 29 | } catch (InterruptedException e) { 30 | e.printStackTrace(); 31 | } 32 | } 33 | 34 | @Override 35 | public String toString() { 36 | return "MyRunnable{" + 37 | "command='" + command + '\'' + 38 | '}'; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/facing/magicIsZhengchu3.java: -------------------------------------------------------------------------------- 1 | package facing; 2 | 3 | /** 4 | * @author renyujie518 5 | * @version 1.0.0 6 | * @ClassName magicIsZhengchu3.java 7 | * @Description 8 | * 小Q得到一个神奇数列: 1 , 12, 123, . . . 12345678910, 1234567891011. . . 。 9 | * 并且小Q对于能否被3整除这个性质很感兴趣。 10 | * 小Q现在希望你能帮他计算一下从数列的第l个到第r个(包含端点)有多少个数可以被3整除。 11 | * 输入描述: 输入包括两个整数l和r(1 <= l <= r <= 1 e9) , 表示要求解的区间两端。 12 | * 输出描述: 输出一个整数, 表示区间内能被3整除的数字个数。 13 | * 示例1: 输入 2 5 14 | * 输出 3 15 | * 解题思路: 判断一个数能不能被3整除, 等价于一个数的每位之和能否被3整除。 16 | * 刚开始想打表, 但发现数据 量是太大, 一维数组最多只能开到1e8.所以就不能纯暴力判断了, 17 | * 不过数据是有规律的, 第一个数是1、 第二个数 是12, 第三个数是123, 所以只用判断n*(n+1)/2%3即可(等差数列)。 18 | * 因为数量太大了, 所以用long 19 | * @createTime 2021年08月12日 16:29:00 20 | */ 21 | public class magicIsZhengchu3 { 22 | public static int magicIsZhengchu3(int l, int r) { 23 | int sum = 0; 24 | for (int i = l; i <= r; i++) { 25 | long tmp = (long) (i + 1) * (long) i / 2L; 26 | if (tmp % 3 == 0) { 27 | sum++; 28 | } 29 | } 30 | return sum; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/DP/IntChaiFen.java: -------------------------------------------------------------------------------- 1 | package DP; 2 | 3 | /** 4 | * @author renyujie518 5 | * @version 1.0.0 6 | * @ClassName IntChaiFen.java 7 | * @Description 8 | * 给定一个正整数 n,将其拆分为至少两个正整数的和,并使这些整数的乘积最大化。 返回你可以获得的最大乘积。 9 | * 示例 1: 输入: 2 输出: 1 解释: 2 = 1 + 1, 1 × 1 = 1。 10 | * 示例 2: 输入: 10 输出: 36 解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36。 11 | * 说明: 你可以假设 n 不小于 2 且不大于 58 12 | * 13 | * dp[i]:分拆数字i,可以得到的最大乘积为dp[i]。 14 | * dp[i] = max({dp[i], (i - j) * j, dp[i - j] * j}); 15 | * 从dp[i]的定义来说,拆分数字2,得到的最大乘积是1 所以dp[2] =1 16 | * 17 | * @createTime 2021年09月02日 16:43:00 18 | */ 19 | public class IntChaiFen { 20 | public int IntChaiFen(int n) { 21 | //dp[i]为正整数i拆分结果的最大乘积 22 | int[] dp = new int[n + 1]; 23 | dp[2] = 1; 24 | for (int i = 3; i <= n; i++) { 25 | for (int j = 1; j <= i; j++) { 26 | //j*(i-j)代表把i拆分为j和i-j两个数相乘 27 | //j*dp[i-j]代表把i拆分成j和继续把(i-j)这个数拆分,取(i-j)拆分结果中的最大乘积与j相乘 28 | dp[i] = Math.max(dp[i], Math.max(j * (i - j), j * dp[i - j])); 29 | } 30 | } 31 | return dp[n]; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/firstUniqChar_50.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | import java.util.HashMap; 4 | 5 | /** 6 | * @author renyujie518 7 | * @version 1.0.0 8 | * @ClassName firstUniqChar_50.java 9 | * @Description 第一个只出现一次的字符 10 | * 在字符串 s 中找出第一个只出现一次的字符。如果没有,返回一个单空格。 s 只包含小写字母。 11 | * 12 | * 示例: 13 | * s = "abaccdeff" 14 | * 返回 "b" 15 | * 16 | * s = "" 17 | * 返回 " " 18 | 19 | 我们可以对字符串进行两次遍历。 20 | 21 | 在第一次遍历时,我们使用哈希映射统计出字符串中每个字符出现的次数。 22 | 在第二次遍历时,我们只要遍历到了一个只出现一次的字符,那么就返回该字符,否则在遍历结束后返回空格。 23 | 24 | * @createTime 2021年08月25日 23:06:00 25 | */ 26 | public class firstUniqChar_50 { 27 | public static char firstUniqChar(String s) { 28 | HashMap map = new HashMap<>();//key = 字符串 value = 次数 29 | for (int i = 0; i < s.length(); i++) { 30 | char ch = s.charAt(i); 31 | map.put(ch, map.getOrDefault(ch, 0) + 1); 32 | } 33 | for (int i = 0; i < s.length(); ++i) { 34 | if (map.get(s.charAt(i)) == 1) { 35 | return s.charAt(i); 36 | } 37 | } 38 | return ' '; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/TwostackOnequeue_09.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | import java.util.Stack; 4 | 5 | /** 6 | * @author renyujie518 7 | * @version 1.0.0 8 | * @ClassName TwostackOnequeue.java 9 | * @Description 10 | * 两个栈来实现一个队列,完成队列的 Push 和 Pop 操作。 11 | * 12 | *思路: 13 | * in 栈用来处理入栈(push)操作,out 栈用来处理出栈(pop)操作。 14 | * 一个元素进入 in 栈之后,出栈的顺序被反转。 15 | * 当元素要出栈时,需要先进入 out 栈,此时元素出栈顺序再一次被反转,因此出栈顺序就和最开始入栈顺序是相同的, 16 | * 先进入的元素先退出,这就是队列的顺序。 17 | * @createTime 2021年08月17日 19:43:00 18 | */ 19 | public class TwostackOnequeue_09 { 20 | private static Stack in = new Stack(); 21 | private static Stack out = new Stack(); 22 | 23 | public static void push(int node) { 24 | in.push(node); 25 | } 26 | 27 | public static int pop() throws Exception { 28 | if (out.isEmpty()) { 29 | while (!in.isEmpty()) { 30 | out.push(in.pop()); 31 | } 32 | } 33 | if (out.isEmpty()) { 34 | throw new Exception("queue is enppty"); 35 | } 36 | return out.pop(); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/facing/didi/kuohao.java: -------------------------------------------------------------------------------- 1 | package facing.didi; 2 | 3 | import java.util.Stack; 4 | 5 | /** 6 | * @author renyujie518 7 | * @version 1.0.0 8 | * @ClassName kuohao.java 9 | * @Description 有效的括号 10 | * 给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。 11 | * 12 | * 有效字符串需满足: 13 | * 14 | * 左括号必须用相同类型的右括号闭合。 15 | * 左括号必须以正确的顺序闭合。 16 | * 示例 2: 17 | * 输入:s = "()[]{}" 18 | * 输出:true 19 | * 20 | * 示例 3: 21 | * 输入:s = "(]" 22 | * 输出:false 23 | * @createTime 2021年09月17日 14:06:00 24 | */ 25 | public class kuohao { 26 | //建立一个新的栈,然后遍历字符串的字符,进行比较 27 | public static boolean kuohao(String s) { 28 | Stack stack = new Stack<>(); 29 | for (char c : s.toCharArray()) { 30 | if (c == '(') { 31 | stack.push(')'); 32 | } else if (c == '[') { 33 | stack.push(']'); 34 | } else if (c == '{') { 35 | stack.push('}'); 36 | } else if (stack.isEmpty() || c != stack.pop()) { 37 | return false; 38 | } 39 | } 40 | return true; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/sort/SelectSort.java: -------------------------------------------------------------------------------- 1 | package sort; 2 | 3 | /** 4 | * @author admin 5 | * @version 1.0.0 6 | * @ClassName SelectSort.java 7 | * @Description 选择排序 8 | * @createTime 2021年02月08日 19:47:00 9 | */ 10 | public class SelectSort { 11 | public static void main(String[] args) { 12 | int[] arrs = {3,4,5,123,45,13,435,765,12,1,2,3,345,56,78,9,3}; 13 | selectsort(arrs); 14 | } 15 | 16 | 17 | 18 | public static void selectsort(int[] arrs){ 19 | if (arrs ==null || arrs.length<2){ 20 | return; 21 | } 22 | for (int i = 0; i < arrs.length - 1; i++) { 23 | int minIndex =i; 24 | for (int j= i+1;j1时 f(n) = f(n−1)+f(n−2) 16 | * 17 | * 一模一样的题: 18 | * 一只青蛙一次可以跳上 1 级台阶,也可以跳上 2 级。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。 19 | * 20 | * @createTime 2021年08月17日 20:44:00 21 | */ 22 | public class rectangleFuGai { 23 | 24 | public static int rectangleFuGai(int n) { 25 | //n为数量 返回方法数 26 | if (n <= 2) { 27 | return n; 28 | } 29 | int pre1 = 1; 30 | int pre2 = 2; 31 | int result = 0; 32 | for (int i = 3; i <= n; i++) { 33 | result = pre1 + pre2; 34 | pre2 = pre1; 35 | pre1 = result; 36 | } 37 | 38 | return result; 39 | 40 | } 41 | 42 | 43 | } 44 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/IsPopOrder_31.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | import java.util.Stack; 4 | 5 | /** 6 | * @author renyujie518 7 | * @version 1.0.0 8 | * @ClassName IsPopOrder_31.java 9 | * @Description 栈的压入、弹出序列 10 | * 输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。 11 | * 假设压入栈的所有数字均不相等。 12 | * 例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。 13 | * (注意:这两个序列的长度是相等的) 14 | * 15 | * 思路: 16 | * 使用一个栈来模拟压入弹出操作 17 | * @createTime 2021年08月21日 15:13:00 18 | */ 19 | public class IsPopOrder_31 { 20 | public static boolean IsPopOrder(int[] pushQuene, int[] popQuene) { 21 | int n = pushQuene.length; 22 | Stack stack = new Stack<>(); 23 | for (int pushIndex = 0, popIndex = 0; pushIndex < n; pushIndex++) { 24 | stack.push(pushQuene[pushIndex]); 25 | while (popIndex < n && !stack.isEmpty() 26 | && stack.peek() == popQuene[popIndex]) { //关键就是这里,查看popQuene和stack栈顶的是否一致 27 | stack.pop();//一致的话就相当于验证过了 28 | popIndex++; 29 | } 30 | } 31 | return stack.isEmpty();//最终的判断依据就是stack中有没有剩余 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/deleteDuplicationAndBuBaoLiu_18.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | import LinkedList.ListNode; 4 | 5 | /** 6 | * @author renyujie518 7 | * @version 1.0.0 8 | * @ClassName deleteDuplicationAndBuBaoLiu_18.java 9 | * @Description 10 | * 在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 11 | * 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5 12 | * @createTime 2021年08月18日 17:58:00 13 | */ 14 | public class deleteDuplicationAndBuBaoLiu_18 { 15 | public static ListNode deleteDuplicationAndBuBaoLiu(ListNode pHead) { 16 | if (pHead == null || pHead.next == null){ 17 | return pHead; 18 | } 19 | ListNode next = pHead.next; 20 | if (pHead.val == next.val) { //有重复 21 | while (next != null && pHead.val == next.val){ 22 | //因为升序 所以即使相同也在一堆 所以通过"跳跃"的方式(pHeap.next = pHeap.next.nexts)就相当于删掉了 23 | next = next.next; 24 | } 25 | return deleteDuplicationAndBuBaoLiu(next); //递归 26 | } else {//没重复 啥都不操作 只是对next递归 27 | pHead.next = deleteDuplicationAndBuBaoLiu(pHead.next); 28 | return pHead; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/Graph/DFS_graph.java: -------------------------------------------------------------------------------- 1 | package Graph; 2 | 3 | import java.util.HashSet; 4 | import java.util.Stack; 5 | 6 | /** 7 | * @author admin 8 | * @version 1.0.0 9 | * @ClassName DFS_graph.java 10 | * @Description 深度优先遍历图 利用栈 11 | * @createTime 2021年03月14日 15:53:00 12 | */ 13 | public class DFS_graph { 14 | public static void dfs(Node node){ 15 | if (node == null){ 16 | return; 17 | } 18 | Stack stack = new Stack(); 19 | HashSet set = new HashSet(); 20 | stack.add(node); 21 | set.add(node); 22 | System.out.println(node.value); //先把首先选定的节点打印 23 | while (!stack.isEmpty()){ 24 | Node curr = stack.pop(); 25 | for (Node next : curr.nexts) { 26 | if (!set.contains(next)){ 27 | stack.push(curr); //把上次弹出的点再压回去 28 | stack.push(next); // 把next也压回去 29 | set.add(next); 30 | System.out.println(next.value); //两次压完再打印 31 | break; //这个break就是要一条道走到黑(深度优先) 32 | } 33 | } 34 | } 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/sumAsSInSortArray_57.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | 6 | /** 7 | * @author renyujie518 8 | * @version 1.0.0 9 | * @ClassName sumAsSInSortArray.java 10 | * @Description 和为 S 的两个数字 11 | * 输入一个递增排序的数组和一个数字 S,在数组中查找两个数,使得他们的和正好是 S。如果有多对数字的和等于 S,输出两个数的乘积最小的。 12 | * 13 | * 使用双指针,一个指针指向元素较小的值,一个指针指向元素较大的值。指向较小元素的指针从头向尾遍历,指向较大元素的指针从尾向头遍历。 14 | * 15 | * 如果两个指针指向元素的和 sum == target,那么得到要求的结果; 16 | * 如果 sum > target,移动较大的元素,使 sum 变小一些; 17 | * 如果 sum < target,移动较小的元素,使 sum 变大一些。 18 | * @createTime 2021年08月27日 21:05:00 19 | */ 20 | public class sumAsSInSortArray_57 { 21 | public static ArrayList sumAsSInSortArray(int[] arr, int sum) { 22 | int i = 0; 23 | int j = arr.length - 1; 24 | while (i < j) { 25 | int curr = arr[i] + arr[j]; 26 | if (curr == sum) { 27 | return new ArrayList<>(Arrays.asList(arr[i], arr[j])); 28 | } 29 | if (curr < sum) { 30 | i++; 31 | } else { 32 | j--; 33 | } 34 | } 35 | return new ArrayList<>(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/reverseList_24.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | import LinkedList.ListNode; 4 | 5 | /** 6 | * @author renyujie518 7 | * @version 1.0.0 8 | * @ClassName reverseList.java 9 | * @Description 反转链表 10 | * @createTime 2021年08月19日 21:22:00 11 | */ 12 | public class reverseList_24 { //递归 13 | public static ListNode reverseListWithRecursion(ListNode head) { 14 | //边界 15 | if (head == null || head.next == null) { 16 | return null; 17 | } 18 | ListNode next = head.next; 19 | head.next = null; 20 | ListNode newHead = reverseListWithRecursion(next); 21 | //拼接 22 | next.next = head; 23 | return newHead; 24 | } 25 | 26 | //迭代 头插法 27 | public static ListNode reverseListWithDieDai(ListNode head) { 28 | //首先设立个虚节点 29 | ListNode newList = new ListNode(-1); 30 | while (head != null) { 31 | //取出下一个节点 32 | ListNode next = head.next; 33 | //拼接 34 | head.next = newList.next; 35 | newList.next = head; 36 | head = next; 37 | } 38 | //再去掉虚节点 39 | return newList.next; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/isNumber_20.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | /** 4 | * @author renyujie518 5 | * @version 1.0.0 6 | * @ClassName isNumber_20.java 7 | * @Description 请实现一个函数用来判断字符串是否表示数值(包括整数和小数) 8 | * \数值(按顺序)可以分成以下几个部分: 9 | * 10 | * 若干空格 11 | * 一个小数或者整数 12 | * (可选)一个'e'或'E',后面跟着一个整数 13 | * 若干空格 14 | * 小数(按顺序)可以分成以下几个部分: 15 | * 16 | * (可选)一个符号字符('+' 或 '-') 17 | * 下述格式之一: 18 | * 至少一位数字,后面跟着一个点 '.' 19 | * 至少一位数字,后面跟着一个点 '.' ,后面再跟着至少一位数字 20 | * 一个点 '.' ,后面跟着至少一位数字 21 | * 整数(按顺序)可以分成以下几个部分: 22 | * 23 | * (可选)一个符号字符('+' 或 '-') 24 | * 至少一位数字 25 | * 部分数值列举如下: 26 | * 27 | * ["+100", "5e2", "-123", "3.1416", "-1E-16", "0123"] 28 | * 部分非数值列举如下: 29 | * 30 | * ["12e", "1a3.14", "1.2.3", "+-5", "12e+5.4"] 31 | * 32 | * 33 | *思路: 34 | * 使用正则表达式进行匹配。 35 | * [] : 字符集合 36 | * () : 分组 37 | * ? : 重复 0 ~ 1 次 38 | * + : 重复 1 ~ n 次 39 | * * : 重复 0 ~ n 次 40 | * . : 任意字符 41 | * \\. : 转义后的 . 42 | * \\d : 数字 43 | 44 | * @createTime 2021年08月18日 18:07:00 45 | */ 46 | public class isNumber_20 { 47 | public boolean isNumeric(char[] str) { 48 | if (str == null || str.length == 0) 49 | return false; 50 | return new String(str).matches("[+-]?\\d*(\\.\\d+)?([eE][+-]?\\d+)?"); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/sort/BubbleSort.java: -------------------------------------------------------------------------------- 1 | package sort; 2 | 3 | 4 | import util.bijiaoqi_arrays_sort; 5 | 6 | import java.util.Arrays; 7 | 8 | /** 9 | * @author admin 10 | * @version 1.0.0 11 | * @ClassName bubbleSort.java 12 | * @Description 冒泡排序 13 | * @createTime 2021年02月08日 19:24:00 14 | */ 15 | public class BubbleSort { 16 | public static void main(String[] args) { 17 | int[] arrs = {3,4,5,123,45,13,435,765,12,1,2,3,345,56,78,9,3}; 18 | int[] bubblesort = bubblesort(arrs); 19 | for (int arr : bubblesort) { 20 | System.out.println(arr); 21 | } 22 | 23 | 24 | } 25 | 26 | 27 | public static int[] bubblesort(int[] arrs){ 28 | if (arrs ==null || arrs.length<2){ 29 | return arrs; 30 | } 31 | for (int i =arrs.length-1;i>0;i--) { 32 | for (int j = 0; j < i; j++) { 33 | if (arrs[j]>arrs[j+1]){ 34 | swap(arrs,j,j+1); 35 | } 36 | } 37 | } 38 | return arrs; 39 | } 40 | 41 | public static void swap(int[] arrs,int i,int j){ 42 | arrs[i] = arrs[i]^arrs[j]; 43 | arrs[j] = arrs[i]^arrs[j]; 44 | arrs[i] = arrs[i]^arrs[j]; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/HuanInLinkList_23.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | import LinkedList.ListNode; 4 | 5 | /** 6 | * @author renyujie518 7 | * @version 1.0.0 8 | * @ClassName HuanInLinkList.java 9 | * @Description 10 | * 链表中环的入口结点作者:CyC2018 11 | * 思路: 12 | *主要使用快慢指针的做法 13 | * *归结一句话: 14 | * *快走2,慢走1,如果有环,一定会相遇。且走过的路一定<2*list.length(追逐赛,速度两倍,不可能超过2圈) 15 | * *相遇后,就让其中一个指针回到链表的头部,另一个不动,然后两个指针再一起走,每次1步,再相遇的节点就是入环节点 16 | * 17 | * @createTime 2021年08月19日 21:12:00 18 | */ 19 | public class HuanInLinkList_23 { 20 | public static ListNode HuanInLinkList(ListNode head) { 21 | //有环的长度至少为3 22 | if (head == null || head.next == null || head.next.next == null) { 23 | return null; 24 | } 25 | ListNode slow = head.next; 26 | ListNode fast = head.next.next; 27 | while (fast != slow) { 28 | if (fast.next == null | fast.next.next == null) { 29 | return null; 30 | } 31 | slow = slow.next; 32 | fast = fast.next.next; 33 | } 34 | fast = head; 35 | while (fast != slow) { 36 | slow = slow.next; 37 | fast = fast.next; 38 | } 39 | return slow; 40 | } 41 | 42 | 43 | 44 | } 45 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/MiddleClass/MaxSumInTree.java: -------------------------------------------------------------------------------- 1 | package MiddleClass; 2 | 3 | import Tree.TreeNode; 4 | 5 | /** 6 | * @author admin 7 | * @version 1.0.0 8 | * @ClassName MaxSumInTree.java 9 | * @Description 二叉树每个结点都有一个int型权值, 给定一棵二叉树, 10 | * 要求计算出从根结点到 叶结点的所有路径中, 权值和最大的值为多少。 11 | * 12 | * 注意,这里是根节点出发 13 | * 14 | * 涉及到二叉树的路径问题 适合用左神的关于树的动态规划 详见IsBalenceTree.java中的总结 15 | * @createTime 2021年07月22日 16:13:00 16 | */ 17 | public class MaxSumInTree { 18 | //法一 逻辑思考 19 | //全局变量 只在到达叶节点的时候可能跟新 20 | public static int maxSum; 21 | public static int maxPathWithoutRecursion(TreeNode head) { 22 | process1(head, 0); 23 | return maxSum; 24 | } 25 | 26 | //pre的含义是从上往下,从根节点出发到当前节点的"上方"节点所产生的路径和 注意不包括curr, 27 | public static void process1(TreeNode curr, int pre) { 28 | if (curr.left == null && curr.right == null) { //处于叶节点的位置 29 | maxSum = Math.max(maxSum, pre + curr.val); 30 | } 31 | //非叶节点,如果有左树,递归,状态跟新,当前节点变为左节点,路径和变为 已有值+本身值 32 | if (curr.left != null) { 33 | process1(curr.left, pre + curr.val); 34 | } 35 | if (curr.right != null) { 36 | process1(curr.right, pre + curr.val); 37 | } 38 | //递归完成,maxSum必然是最大的 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/MiddleClass/xuanZhuanCi.java: -------------------------------------------------------------------------------- 1 | package MiddleClass; 2 | 3 | import facing.KMP; 4 | 5 | /** 6 | * @author renyujie518 7 | * @version 1.0.0 8 | * @ClassName xuanZhuanCi.java 9 | * @Description 10 | * 11 | *如果一个字符串为str, 把字符串str前面任意的部分挪到后面形成的字符串叫 作str的旋转词。 12 | * 比如str="12345", str的旋转词有"12345"、 "23451 " 、 "34512"、 "45123"和"51234"。 13 | * 给定两个字符串a和b, 请判断a和b是否互为旋转 词。 14 | * 比如: a="cdab", b="abcd", 返回true。 15 | * a="1ab2", b="ab12", 返回false。 16 | * a="2ab1", b="ab12", 返回true。 17 | * 18 | * 思路: 19 | * 复制一遍紧接着后面 比如abcd复制完就是abcdabcd,那么这个大字符串中每个长度为4的子字符串都是旋转词 20 | * 怎么判断互为旋转词 可以看是不是大字符串的子串 判断子串 优先想到KMP 21 | * 22 | * @createTime 2021年07月29日 16:18:00 23 | */ 24 | public class xuanZhuanCi { 25 | public static boolean isRotation(String a, String b) { 26 | if (a == null || b == null || a.length() != b.length()) { 27 | return false; 28 | } 29 | String b2 = b + b; 30 | //KMP算法解决的问题字符串str1和str2,str1是否包含str2,如果包含 返回str2在str1中开始的位置。 如果不包含返回-1 31 | return KMP.getIndexOf(b2, a) != -1; 32 | } 33 | 34 | public static void main(String[] args) { 35 | String str1 = "cdab"; 36 | String str2 = "abcd"; 37 | System.out.println(isRotation(str1, str2)); 38 | 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/leftReverseString_58.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | /** 4 | * @author renyujie518 5 | * @version 1.0.0 6 | * @ClassName leftReverseString_58.java 7 | * @Description 左旋转字符串 8 | * 字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。 9 | * 比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。 10 | * Input: 11 | * S="abcXYZdef" 12 | * K=3 13 | * 14 | * Output: 15 | * "XYZdefabc" 16 | * 17 | * 思路:先将 "abc" 和 "XYZdef" 分别翻转,得到 "cbafedZYX",然后再把整个字符串翻转得到 "XYZdefabc"。 18 | * 字符串为不可变对象 19 | * @createTime 2021年08月27日 23:01:00 20 | */ 21 | public class leftReverseString_58 { 22 | 23 | private void reverse(char[] chars, int i, int j) { 24 | while (i < j) 25 | swap(chars, i++, j--); 26 | } 27 | 28 | private void swap(char[] chars, int i, int j) { 29 | char t = chars[i]; 30 | chars[i] = chars[j]; 31 | chars[j] = t; 32 | } 33 | 34 | public String leftReverseString(String str, int k) { 35 | if (k >= str.length()) { 36 | return str; 37 | } 38 | char[] chars = str.toCharArray(); 39 | reverse(chars, 0, k - 1); 40 | reverse(chars, k, chars.length - 1); 41 | reverse(chars, 0, chars.length - 1); 42 | return new String(chars); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/choushu_49.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | /** 4 | * @author renyujie518 5 | * @version 1.0.0 6 | * @ClassName choushu_49.java 7 | * @Description 丑数 8 | 9 | * 把只包含因子 2、3 和 5 的数称作丑数(Ugly Number)。例如 6、8 都是丑数,但 14 不是,因为它包含因子 7。 10 | * 习惯上我们把 1 当做是第一个丑数。求按从小到大的顺序的第 N 个丑数。 11 | * 12 | * 定义数组dp,其中dp[i] 表示第 i个丑数,第 n个丑数即为dp[n] 13 | * 由于最小的丑数是 1,因此 dp[1]=1 14 | * 定义三个指针 p2,p3,p5表示下一个丑数是当前指针指向的丑数乘以对应的质因数。初始时,三个指针的值都是 1。 15 | * 16 | * 当 2≤i≤n 时,令 dp[i]=min(dp[p2]*2,dp[p3]*3,dp[p5]*5) 17 | * 然后分别比较 是否相等,如果相等则将对应的指针加 11。 18 | 19 | 20 | * @createTime 2021年08月25日 23:00:00 21 | */ 22 | public class choushu_49 { 23 | public int nthUglyNumber(int n) { 24 | int[] dp = new int[n + 1]; 25 | dp[1] = 1; 26 | int p2 = 1, p3 = 1, p5 = 1; 27 | for (int i = 2; i <= n; i++) { 28 | int num2 = dp[p2] * 2; 29 | int num3 = dp[p3] * 3; 30 | int num5 = dp[p5] * 5; 31 | dp[i] = Math.min(Math.min(num2, num3), num5); 32 | if (dp[i] == num2) { 33 | p2++; 34 | } 35 | if (dp[i] == num3) { 36 | p3++; 37 | } 38 | if (dp[i] == num5) { 39 | p5++; 40 | } 41 | } 42 | return dp[n]; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/MiddleClass/nearMultiple4Times.java: -------------------------------------------------------------------------------- 1 | package MiddleClass; 2 | 3 | /** 4 | * @author renyujie518 5 | * @version 1.0.0 6 | * @ClassName nearMultiple4Times.java 7 | * @Description 8 | * 给定一个数组arr, 如果通过调整可以做到arr中任意两个相邻的数字相乘是4的倍数, 返回true; 如果不能返回false 9 | * 10 | * 思路: 11 | * 遍历数组 找到四种数:奇数 a个 偶数只有一个2因子 b个 偶数包含4因子 c 个 12 | * 思路: 13 | * 当b = 0的时候 什么情况下用到c的个数最少: 奇4奇4... 观察发现 a>=1 c就要>=a-1 14 | * 当b!=0 22...4奇4奇.... 在2都放在前面的情况下 a = 0 c>=0;a>=1 c>=a 15 | * 16 | * 17 | * @createTime 2021年07月30日 16:49:00 18 | */ 19 | public class nearMultiple4Times { 20 | public static boolean nearMultiple4Times(int[] arr) { 21 | //奇数 a个 22 | int a = 0; 23 | //偶数只有一个2因子(是偶数但不是4的倍数的数) b个 24 | int b = 0; 25 | //是4的倍数的数 c个 26 | int c = 0; 27 | for (int i = 0; i < arr.length; i++) { 28 | if ((arr[i] & 1) != 0) { //&按位与 (两个为真才为真 一个数 & 1的结果就是取二进制的最末位。 29 | // 这可以用来判断一个整数的奇偶,二进制的最末位为0表示该数为偶数,最末位为1表示该数为奇数 30 | a++; 31 | } else { //一定是偶数 32 | if (arr[i] % 4 == 0) { 33 | c++; 34 | } else { 35 | b++; 36 | } 37 | } 38 | } 39 | return b == 0 ? (c >= a - 1) : (c >= a); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/minNumFromArray_45.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | import java.util.Arrays; 4 | 5 | /** 6 | * @author renyujie518 7 | * @version 1.0.0 8 | * @ClassName minNumFromArray_45.java 9 | * @Description 把数组排成最小的数 10 | * 输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。 11 | * 例如输入数组 {3,32,321},则打印出这三个数字能排成的最小数字为 321323。 12 | * 13 | * 解题思路 14 | * 可以看成是一个排序问题,在比较两个字符串 S1 和 S2 的大小时,应该比较的是 S1+S2 和 S2+S1 的大小,如果 S1+S2 < S2+S1, 15 | * 那么应该把 S1 排在前面,否则应该把 S2 排在前面。 16 | * @createTime 2021年08月24日 17:31:00 17 | */ 18 | public class minNumFromArray_45 { 19 | public static String minNumFromArray(int[] nums) { 20 | if (nums == null || nums.length == 0) { 21 | return ""; 22 | } 23 | 24 | int n = nums.length; 25 | String[] nums2String = new String[n]; 26 | for (int i = 0; i < n; i++) { 27 | nums2String[i] = nums[i] + ""; 28 | } 29 | //compareTo用于两个相同数据类型的比较 如果指定的数与参数相等返回0。如果指定的数小于参数返回 -1。如果指定的数大于参数返回 1。 30 | //如果0说明o1和o2相等,如果返回负值,那么o1和o2会倒序排序,返回正值,那么o1和o2会正序排序 31 | //最后是以s1+s2为序的升序结果 32 | Arrays.sort(nums2String, (s1, s2) -> (s1 + s2).compareTo(s2 + s1)); 33 | String ret = ""; 34 | for (String str : nums2String) 35 | ret += str; 36 | return ret; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/facing/didi/noDuplicationZuiChang.java: -------------------------------------------------------------------------------- 1 | package facing.didi; 2 | 3 | import java.util.HashMap; 4 | 5 | /** 6 | * @author renyujie518 7 | * @version 1.0.0 8 | * @ClassName noDuplicationZuiChang.java 9 | * @Description 给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。 10 | * 11 | * 示例 1: 12 | * 输入: s = "abcabcbb" 13 | * 输出: 3 14 | * 解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。 15 | * 16 | * 示例 3: 17 | * 输入: s = "pwwkew" 18 | * 输出: 3 19 | * 解释: 因为无重复字符的最长子串是"wke",所以其长度为 3。 20 | * 请注意,你的答案必须是 子串 的长度,"pwke"是一个子序列,不是子串。 21 | * 22 | * 滑动窗口 23 | 24 | * @createTime 2021年09月17日 14:17:00 25 | */ 26 | public class noDuplicationZuiChang { 27 | public int lengthOfLongestSubstring(String s) { 28 | //定义返回结果 29 | int res = 0; 30 | //定义窗口左边界坐标 31 | int start = 0; 32 | //key = 对应字符 value = 所在的位置 33 | HashMap map = new HashMap<>(); 34 | 35 | for (int i = 0; i < s.length(); i++) { 36 | char c = s.charAt(i); 37 | 38 | if (map.containsKey(c)){ 39 | //start右移到前一个重复字符的下一个坐标,若该坐标小于start则不移动,防止start左移 40 | start = Math.max(map.get(c) + 1, start); 41 | } 42 | map.put(c, i); 43 | res = Math.max(res, i - start + 1); 44 | } 45 | return res; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/DP/lujinInMatrix/lujinInMatrix1.java: -------------------------------------------------------------------------------- 1 | package DP.lujinInMatrix; 2 | 3 | /** 4 | * @author renyujie518 5 | * @version 1.0.0 6 | * @ClassName lujinInMatrix1.java 7 | * @Description 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。 8 | * 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish” )。 9 | * 问总共有多少条不同的路径? 10 | * 11 | * dp[i][j] :表示从(0 ,0)出发,到(i, j) 有dp[i][j]条不同的路径。 12 | * 想要求dp[i][j],只能有两个方向来推导出来,即dp[i - 1][j] 和 dp[i][j - 1]。 13 | * dp[i][j] = dp[i - 1][j] + dp[i][j - 1],因为dp[i][j]只有这两个方向过来 14 | * 15 | * 初始化: 16 | * 首先dp[i][0]一定都是1,因为从(0, 0)的位置到(i, 0)的路径只有一条,那么dp[0][j]也同理。 17 | * 18 | * 遍历顺序: 19 | * dp[i][j]都是从其上方和左方推导而来,那么从左到右一层一层遍历就可以了。 20 | * 这样就可以保证推导dp[i][j]的时候,dp[i - 1][j] 和 dp[i][j - 1]一定是有数值的。 21 | * @createTime 2021年09月02日 16:11:00 22 | */ 23 | public class lujinInMatrix1 { 24 | public static int uniquePaths(int m, int n) { 25 | int[][] dp = new int[m][n]; 26 | //初始化 27 | for (int i = 0; i < m; i++) { 28 | dp[i][0] = 1; 29 | } 30 | for (int i = 0; i < n; i++) { 31 | dp[0][i] = 1; 32 | } 33 | 34 | for (int i = 1; i < m; i++) { 35 | for (int j = 1; j < n; j++) { 36 | dp[i][j] = dp[i-1][j]+dp[i][j-1]; 37 | } 38 | } 39 | return dp[m-1][n-1]; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/MiddleClass/ziJuZhenMaxhe.java: -------------------------------------------------------------------------------- 1 | package MiddleClass; 2 | 3 | /** 4 | * @author renyujie518 5 | * @version 1.0.0 6 | * @ClassName ziJuZhenMaxhe.java 7 | * @Description 给定一个整型矩阵, 返回子矩阵的最大累计和。 8 | * 思路: 9 | * 之前做过子数组最大累加和 想着利用一下,即每行每行考虑 10 | * 比如一个3行4列的矩阵 暴力的话依次要 计算 0,0-1,0-1-2,1,1-2,2 这么多行数组合 11 | * 可以利用矩阵压缩的技巧 0行和1行累加就是考虑的0-1矩阵下的最大累加和,在在这个基础上加上2行,就是0-1-2这个大矩阵的累加和 12 | * @createTime 2021年08月10日 22:07:00 13 | */ 14 | public class ziJuZhenMaxhe { 15 | public static int ziJuZhenMaxheWithZS(int[][] m) { 16 | if (m == null || m.length == 0 || m[0].length == 0) { 17 | return 0; 18 | } 19 | int max = Integer.MIN_VALUE; 20 | int cur = 0; 21 | int[] s = null;//用作考虑行的 22 | for (int i = 0; i != m.length; i++) {//开始的行号 23 | //这里开始就退化为"子数组最大累加和" 24 | s = new int[m[0].length]; 25 | for (int j = i; j != m.length; j++) {//结束的行号 i~j行是我讨论的范围 26 | cur = 0; 27 | for (int k = 0; k != s.length; k++) { 28 | s[k] += m[j][k]; //当前j行k列 压缩矩阵 29 | cur += s[k]; 30 | max = Math.max(max, cur); 31 | cur = cur < 0 ? 0 : cur; 32 | } 33 | } 34 | } 35 | return max; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/Test/shunXuABC.java: -------------------------------------------------------------------------------- 1 | package Test; 2 | 3 | import java.util.concurrent.locks.Lock; 4 | import java.util.concurrent.locks.ReentrantLock; 5 | 6 | /** 7 | * @author renyujie518 8 | * @version 1.0.0 9 | * @ClassName shunXuABC.java 10 | * @Description TODO 11 | * @createTime 2021年08月31日 17:59:00 12 | */ 13 | public class shunXuABC { 14 | public shunXuABC(int times) { 15 | this.times = times; 16 | } 17 | 18 | public static void main(String[] args) { 19 | shunXuABC duixiang = new shunXuABC(1); 20 | new Thread(() -> { 21 | duixiang.printABC("A", 0); 22 | }).start(); 23 | new Thread(() -> { 24 | duixiang.printABC("B", 1); 25 | }).start(); 26 | new Thread(() -> { 27 | duixiang.printABC("C", 2); 28 | }).start(); 29 | 30 | } 31 | 32 | private int state; //当前状态值 33 | private Lock lock = new ReentrantLock(); 34 | //循环次数 35 | private int times; 36 | 37 | 38 | public void printABC(String name, int target) { 39 | 40 | for (int i = 0; i < times; i++) { 41 | lock.lock(); 42 | if (state % 3 == target) { 43 | state++; 44 | System.out.println(name); 45 | } 46 | lock.unlock(); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/ReverseSentence_58.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | /** 4 | * @author renyujie518 5 | * @version 1.0.0 6 | * @ClassName ReverseSentence_58.java 7 | * @Description 翻转单词顺序列 8 | * Input: 9 | * "I am a student." 10 | * 11 | * Output: 12 | * "student. a am I" 13 | * 14 | * 这里的优化解是不使用额外空间 15 | * 创建一个字符数组使得空间复杂度为 O(N) 16 | * 先旋转每个单词,再旋转整个字符串。 17 | * @createTime 2021年08月27日 22:30:00 18 | */ 19 | public class ReverseSentence_58 { 20 | private void swap(char[] c, int i, int j) { 21 | char t = c[i]; 22 | c[i] = c[j]; 23 | c[j] = t; 24 | } 25 | 26 | //反转一个单词 27 | private void reverse(char[] c, int i, int j) { 28 | while (i < j) 29 | swap(c, i++, j--); 30 | } 31 | 32 | public String ReverseSentence(String str) { 33 | int n = str.length(); 34 | char[] chars = str.toCharArray(); 35 | //开始的时候都在头 这点注意 36 | int i = 0, j = 0; 37 | while (j <= n) { 38 | if (j == n || chars[j] == ' ') {//遇到空格和末尾,把这个单词反转下 39 | reverse(chars, i, j - 1); 40 | //起点跳到另一个单词 41 | i = j + 1; 42 | } 43 | j++; 44 | } 45 | //再反转整个句子 46 | reverse(chars, 0, n - 1); 47 | //构造函数的兼容性 48 | return new String(chars); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/Graph/GraphGenerator.java: -------------------------------------------------------------------------------- 1 | package Graph; 2 | 3 | /** 4 | * @author admin 5 | * @version 1.0.0 6 | * @ClassName GraphGenerator.java 7 | * @Description 由一个矩阵构造图 8 | * 9 | *N*3的矩阵 【weight,from节点上的值,to节点上的值】 10 | * @createTime 2021年03月14日 15:03:00 11 | */ 12 | public class GraphGenerator { 13 | public static Graph createGraph(Integer[][] matrix) { 14 | Graph graph = new Graph(); 15 | for (int i = 0; i < matrix.length; i++) { 16 | Integer weight = matrix[i][0]; 17 | Integer from = matrix[i][1]; 18 | Integer to = matrix[i][2]; 19 | if (!graph.nodes.containsKey(from)) { //HashMap nodes; key是节点所带的值 value是这个节点 20 | graph.nodes.put(from, new Node(from)); 21 | } 22 | if (!graph.nodes.containsKey(to)) { 23 | graph.nodes.put(to, new Node(to)); 24 | } 25 | Node fromNode = graph.nodes.get(from); 26 | Node toNode = graph.nodes.get(to); 27 | Edge newEdge = new Edge(weight, fromNode, toNode); //把边建立出来 28 | 29 | fromNode.nexts.add(toNode); 30 | fromNode.out++; 31 | toNode.in++; 32 | fromNode.edges.add(newEdge); 33 | graph.edges.add(newEdge); 34 | } 35 | return graph; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/isBalanced_55.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | import Tree.TreeNode; 4 | 5 | /** 6 | * @author renyujie518 7 | * @version 1.0.0 8 | * @ClassName isBalanced_55.java 9 | * @Description 平衡二叉树 10 | * 对二叉树做后序遍历(左右中),从底至顶返回子树深度,若判定某子树不是平衡树则 “剪枝” ,直接向上返回。 11 | * 当节点root 左 / 右子树的深度差 ≤1 :则返回当前子树的深度,即节点 root 的左 / 右子树的深度最大值 +1 12 | * 当节点root 左 / 右子树的深度差 >2 :则返回 -1 ,代表 此子树不是平衡树 。 13 | * 14 | * 这个我建议用左神的递归套路: 15 | * Tree.IsBalenceTree 16 | * @createTime 2021年08月27日 17:20:00 17 | */ 18 | public class isBalanced_55 { 19 | public boolean isBalanced(TreeNode root) { 20 | return dfs(root) == -1 ? false : true; 21 | } 22 | 23 | //用left,right记录root左右子节点的深度,避免遍历root时对左右节点的深度进行重复计算。 24 | //考虑到需要同时记录各个节点的深度和其是否符合平衡性要求,这里的返回值设为int, 25 | //用一个特殊值-1来表示出现不平衡的节点的情况,而不是一般采用的boolean 26 | public int dfs(TreeNode root){ 27 | //用后序遍历的方式遍历二叉树的每个节点(从底至顶),先左子树,再右子树,最后根节点, 28 | if (root == null) { 29 | return 0;//叶节点,因此返回高度 0; 30 | } 31 | int left = dfs(root.left); 32 | if (left == -1) { 33 | return -1;//剪枝,开始向上返回,之后的迭代不再进行 34 | } 35 | int right = dfs(root.right); 36 | if(right==-1){ 37 | return -1; 38 | } 39 | return Math.abs(right - left) < 2 ? Math.max(left, right) + 1 : -1; 40 | 41 | 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/isDuiChenTree_28.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | import Tree.TreeNode; 4 | 5 | /** 6 | * @author renyujie518 7 | * @version 1.0.0 8 | * @ClassName isDuiChenTree.java 9 | * @Description 判断两棵树是不是对称二叉树 10 | * 终止条件: 11 | * 当 LL 和 RR 同时越过叶节点: 此树从顶至底的节点都对称,因此返回 true ; 12 | * 当 LL 或 RR 中只有一个越过叶节点: 此树不对称,因此返回 false ; 13 | * 当节点 LL 值 \ 节点 RR 值: 此树不对称,因此返回 false; 14 | * 递推工作: 15 | * 判断两节点 L.leftL.left 和 R.rightR.right 是否对称,即 recur(L.left, R.right) ; 16 | * 判断两节点 L.rightL.right 和 R.leftR.left 是否对称,即 recur(L.right, R.left) ; 17 | * 返回值: 两对节点都对称时,才是对称树,因此用与逻辑符 && 连接。 18 | * @createTime 2021年08月19日 22:07:00 19 | */ 20 | public class isDuiChenTree_28 { 21 | public static boolean isDuiChenTree(TreeNode root) { 22 | if (root == null) { 23 | return true; 24 | } 25 | return isDuiChenTreeWithDG(root.left, root.right); 26 | 27 | } 28 | 29 | public static boolean isDuiChenTreeWithDG(TreeNode t1, TreeNode t2){ 30 | if (t1 == null & t2 == null) { 31 | return true; 32 | } 33 | if (t1 == null || t2 == null) { 34 | return false; 35 | } 36 | if (t1.val != t2.val){ 37 | return false; 38 | } 39 | return isDuiChenTreeWithDG(t1.left, t2.right) && isDuiChenTreeWithDG(t1.right, t1.left); 40 | 41 | 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/LinkedList/Find_daoshuK.java: -------------------------------------------------------------------------------- 1 | package LinkedList; 2 | 3 | /** 4 | * @author admin 5 | * @version 1.0.0 6 | * @ClassName Find_daoshuK.java 7 | * @Description 单向链表找倒数第K个数(删除倒数第K个数就多个步骤) 8 | * 采取双重遍历肯定是可以解决问题的,但题目要求我们一次遍历解决问题,那我们的思路得发散一下。 9 | * 我们可以设想假设设定了双指针p和q的话,当q指向末尾的NULL,p与q之间相隔的元素个数为n时,那么删除掉p的下一个指针就完成了要求 10 | * 设置虚拟节点 dummyHead指向head(在head之前) 11 | * 设定双指针p和q,初始都指向虚拟节点 dummyHead 12 | * 移动q,直到p与q之间相隔的元素个数为n,同时移动p与q,直到q指向的为NULL 13 | * 将p的下一个节点指向下下个节点 14 | * @createTime 2021年03月23日 15:24:00 15 | */ 16 | public class Find_daoshuK { 17 | public ListNode Find_daoshuK(ListNode head,int K){ 18 | ListNode xu = new ListNode(0, head);//虚节点 相当于再head前再加一个虚节点 19 | ListNode first = head; 20 | ListNode second = xu; //构建双指针 21 | for (int i = 0; i < K; i++) { 22 | first = first.next; //先移动首指针,直到两者相差K 23 | } 24 | //然后两个指针一起移动,first会先触底,所以拿first作为边界 25 | while (first!=null){ 26 | first = first.next; 27 | second = second.next; 28 | } 29 | //这时候!!!second就是倒数第K个节点 30 | //return second; 31 | //如果要删除,将secnd指向下下节点,相当于把这个节点删除了 32 | second.next = second.next.next; 33 | ListNode ans = xu.next; //xu是指向头结点的,而删除的操作是针对second(ListNode second = xu) 34 | //dummy.next相当于对操作完后的head返回 35 | return ans; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/minCommonInTree_68.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | import Tree.TreeNode; 4 | 5 | /** 6 | * @author renyujie518 7 | * @version 1.0.0 8 | * @ClassName minCommonInTree_68.java 9 | * @Description 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 10 | * @createTime 2021年08月28日 18:36:00 11 | */ 12 | public class minCommonInTree_68 { 13 | public TreeNode minCommonInTree(TreeNode root, TreeNode p, TreeNode q) { 14 | if(root == null) { 15 | return null; // 如果树为空,直接返回null 16 | } 17 | if(root == p || root == q) { 18 | return root; // 如果 p和q中有等于 root的,那么它们的最近公共祖先即为root(一个节点也可以是它自己的祖先) 19 | } 20 | // 递归遍历左子树,只要在左子树中找到了p或q,则先找到谁就返回谁 21 | TreeNode left = minCommonInTree(root.left, p, q); 22 | // 递归遍历右子树,只要在右子树中找到了p或q,则先找到谁就返回谁 23 | TreeNode right = minCommonInTree(root.right, p, q); 24 | if(left == null) { 25 | return right; 26 | // 如果在左子树中 p和 q都找不到,则 p和 q一定都在右子树中,右子树中先遍历到的那个就是最近公共祖先(一个节点也可以是它自己的祖先) 27 | } else if(right == null){ 28 | return left; 29 | // 否则,如果 left不为空,在左子树中有找到节点(p或q),这时候要再判断一下右子树中的情况,如果在右子树中,p和q都找不到,则 p和q一定都在左子树中,左子树中先遍历到的那个就是最近公共祖先(一个节点也可以是它自己的祖先) 30 | } else { 31 | //否则,当 left和 right均不为空时,说明 p、q节点分别在 root异侧, 最近公共祖先即为 root 32 | return root; 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/LinkedList/mergeTwoLists.java: -------------------------------------------------------------------------------- 1 | package LinkedList; 2 | 3 | /** 4 | * @author admin 5 | * @version 1.0.0 6 | * @ClassName mergeTwoLists.java 7 | * @Description 合并两个有序链表 8 | * 首先,我们设定一个哨兵节点 prehead ,这可以在最后让我们比较容易地返回合并后的链表。我们维护一个 prev 指针, 9 | * 我们需要做的是调整它的 next 指针。然后,我们重复以下过程,直到 l1 或者 l2 指向了 null : 10 | * 如果 l1 当前节点的值小于等于 l2 ,我们就把 l1 当前的节点接在 prev 节点的后面同时将 l1 指针往后移一位。 11 | * 否则,我们对 l2 做同样的操作。不管我们将哪一个元素接在了后面,我们都需要把 prev 向后移一位。 12 | 13 | * 在循环终止的时候, l1 和 l2 至多有一个是非空的。由于输入的两个链表都是有序的, 14 | * 所以不管哪个链表是非空的,它包含的所有元素都比前面已经合并链表中的所有元素都要大。 15 | * 这意味着我们只需要简单地将非空链表接在合并链表的后面,并返回合并链表即可。 16 | 17 | * 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。 18 | * @createTime 2021年03月07日 20:06:00 19 | */ 20 | public class mergeTwoLists { 21 | public static ListNode mergeTwoSortList(ListNode l1,ListNode l2){ 22 | 23 | ListNode prehead = new ListNode(-1); 24 | ListNode pre = prehead; 25 | while (l1 != null && l2 != null){ 26 | if (l1.val <= l2.val){ 27 | pre.next = l1; 28 | l1 = l1.next; 29 | }else { 30 | pre.next = l2; 31 | l2 = l2.next; 32 | } 33 | pre = pre.next; 34 | } 35 | // 合并后 l1 和 l2 最多只有一个还未被合并完, 36 | //我们直接将链表末尾指向未合并完的链表即可 37 | pre.next = l1 == null ? l2 : l1; 38 | return prehead.next; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/facing/resverseList.java: -------------------------------------------------------------------------------- 1 | package facing; 2 | 3 | import LinkedList.ListNode; 4 | 5 | /** 6 | * @author renyujie518 7 | * @version 1.0.0 8 | * @ClassName resverseList.java 9 | * @Description 反转链表 10 | * class Node { 11 | * public int value; 12 | * public Node next; 13 | * public Node(int data) { 14 | * this.value = data; 15 | * } 16 | * } 17 | * @createTime 2021年08月19日 15:10:00 18 | */ 19 | public class resverseList { 20 | //递归 21 | public static ListNode reverseListWithRecursion(ListNode head) { 22 | //边界 23 | if (head == null || head.next == null) { 24 | return null; 25 | } 26 | ListNode next = head.next; 27 | head.next = null; 28 | ListNode newHead = reverseListWithRecursion(next); 29 | //拼接 30 | next.next = head; 31 | return newHead; 32 | } 33 | 34 | //迭代 头插法 35 | public static ListNode reverseListWithDieDai(ListNode head) { 36 | //首先设立个虚节点 37 | ListNode newList = new ListNode(-1); 38 | while (head != null) { 39 | //取出下一个节点 40 | ListNode next = head.next; 41 | //拼接 42 | head.next = newList.next; 43 | newList.next = head; 44 | head = next; 45 | } 46 | //再去掉虚节点 47 | return newList.next; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/FindPathInTree_34.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | import Tree.TreeNode; 4 | 5 | import java.util.ArrayList; 6 | 7 | /** 8 | * @author renyujie518 9 | * @version 1.0.0 10 | * @ClassName FindPath_34.java 11 | * @Description 12 | * 输入一颗二叉树和一个整数,打印出二叉树中结点值的和为输入整数的所有路径。 13 | * 路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径。 14 | * @createTime 2021年08月21日 19:00:00 15 | */ 16 | public class FindPathInTree_34 { 17 | private static ArrayList> result = new ArrayList<>(); 18 | 19 | public static ArrayList> FindPathInTree(TreeNode root, int target) { 20 | backtrace(root, target, new ArrayList<>()); 21 | return result; 22 | } 23 | 24 | public static void backtrace(TreeNode node, int target, ArrayList path) { 25 | if (node == null) { 26 | return; 27 | } 28 | path.add(node.val); 29 | target = target - node.val; 30 | if (target == 0 && node.left == null && node.right == null) { 31 | //如果走到底的同时恰好target用光了(路径定义为从树的根结点开始往下一直到叶结点所经过的结点形成一条路径)说明找到一条 32 | result.add(new ArrayList<>(path)); 33 | } else { 34 | backtrace(node.left, target, path); 35 | backtrace(node.right, target, path); 36 | } 37 | //最后别忘了回溯还要退一步 即把path.add的末尾的val删除 38 | path.remove(path.size() - 1); 39 | 40 | 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/facing/yiDongYiCi.java: -------------------------------------------------------------------------------- 1 | package facing; 2 | 3 | import util.swap; 4 | 5 | import java.util.Stack; 6 | 7 | /** 8 | * @author renyujie518 9 | * @version 1.0.0 10 | * @ClassName max.java 11 | * @Description 12345 只移动一位 12 | * 一个正整数,移动一位,使得正整数的值最大 13 | * 14 | * 思路: 15 | * 从高位开始,遍历每一位,找到它后面 最大的数 交换一下 16 | * 注意要尽可能的靠后找,要把这个小数尽可能放到后面去 17 | 18 | * @createTime 2021年08月05日 20:15:00 19 | */ 20 | public class yiDongYiCi { 21 | public static int yidongyiwei(int num) { 22 | String s = String.valueOf(num); 23 | char[] data = s.toCharArray(); 24 | for (int i = 0; i < s.length(); i++) { 25 | //这个maxTempIndex 就是当前位 ,默认为最大数 26 | char maxTemp = data[i]; 27 | int maxTempIndex = i; 28 | //从i之后开始遍历,更新上面两个变量 29 | for (int j = i + 1; j < s.length(); j++) { 30 | if (data[j] > maxTemp) { 31 | maxTemp = data[j]; 32 | maxTempIndex = j; 33 | } 34 | } 35 | 36 | //注意,这里是是关键 目前相当于找到i位置之后最大的数,这时候最好把大数往前提 37 | if (maxTemp > data[i]) { 38 | char temp = data[i]; 39 | data[i] = data[maxTempIndex]; 40 | data[maxTempIndex] = temp; 41 | break;//只交换一次 42 | } 43 | } 44 | return Integer.parseInt(new String(data)); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/num2Str_46.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | /** 4 | * @author renyujie518 5 | * @version 1.0.0 6 | * @ClassName num2Str_46.java 7 | * @Description 把数字翻译成字符串 8 | * 给定一个数字,按照如下规则翻译成字符串:1 翻译成“a”,2 翻译成“b”... 26 翻译成“z”。 9 | * 一个数字有多种翻译可能,例如 12258 一共有 5 种,分别是 abbeh,lbeh,aveh,abyh,lyh。 10 | * 实现一个函数,用来计算一个数字有多少种不同的翻译方法。 11 | *

12 | * 动态规划:dp[i]代表以 xi为结尾的数字的翻译方案数量。 13 | * 转移方程: 若 xi和xi-1组成的两位数字可以被翻译,则 dp[i] = dp[i - 1] + dp[i - 2];否则dp[i]=dp[i−1] 14 | *

15 | * 但是得注意区间 当 xi-1=0 时,组成的两位数是无法被翻译的 例如 00, 01, 02,因此区间为 [10, 25]。 16 | * dp[2]=dp[1]+dp[0]=2 dp[2]代表总共两位数字的翻译方式 有2种组合 而dp[0]无数字的翻译方法就肯定是1,所以dp[1] = 1 17 | * @createTime 2021年08月25日 14:45:00 18 | */ 19 | public class num2Str_46 { 20 | public static int num2StrWithDP(int num) { 21 | String str = String.valueOf(num); 22 | int[] dp = new int[str.length() + 1]; 23 | dp[0] = dp[1] = 1; 24 | for (int i = 2; i <= str.length(); i++) { 25 | String strTemp = str.substring(i - 2, i); //substring左闭右开 所以是【i-2,i-1】 取出i位置的前两位 26 | //翻译时,翻译到第i位时有两种可能,仅采用第i位数字进行翻译或者结合i-1位进行翻译 27 | if (strTemp.compareTo("10") >= 0 && strTemp.compareTo("25") <= 0) { 28 | dp[i] = dp[i - 1] + dp[i - 2]; 29 | } else { 30 | dp[i] = dp[i - 1]; 31 | } 32 | } 33 | return dp[dp.length - 1]; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/Greedy/BestArrange.java: -------------------------------------------------------------------------------- 1 | package Greedy; 2 | 3 | import java.util.Arrays; 4 | import java.util.Comparator; 5 | 6 | /** 7 | * @author admin 8 | * @version 1.0.0 9 | * @ClassName BestArrange.java 10 | * @Description 11 | * 一些项目要占用一个会议室宣讲,会议室不能同时容纳两个项目的宣讲。 12 | * 给你每一个项目开始的时间和结束的时间(给你一个数组,里面是一个个具体的项目),你来安排宣讲的日程, 13 | * 要求会议室进行的宣讲的场次最多。返回这个最多的宣讲场次 14 | * 15 | * 按照会议结束的早来安排 16 | * @createTime 2021年03月16日 16:18:00 17 | */ 18 | public class BestArrange { 19 | 20 | //会议的结构体 21 | public static class Program{ 22 | public int start; 23 | public int end; 24 | 25 | public Program(int start, int end) { 26 | this.start = start; 27 | this.end = end; 28 | } 29 | } 30 | 31 | public static int bestArrange(Program[] programs,int start){ //最开始的时间 32 | Arrays.sort(programs, new Comparator() { 33 | @Override 34 | public int compare(Program o1, Program o2) { 35 | return o1.end - o2.end; //以结束时间升序,先结束的排前面 36 | } 37 | }); 38 | int result = 0; 39 | for (int i = 0; i < programs.length; i++) { 40 | if (start <= programs[i].start){ //准备安排会议的那个时间点(目标时间点) <= 会议开始时间 说明可以单排,大不了空闲一会 41 | result++; 42 | start = programs[i].end; //跟新目标时间点 43 | } 44 | } 45 | return result; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/PrintTreeFromTopToBottom_32.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | import Tree.TreeNode; 4 | 5 | import java.util.ArrayList; 6 | import java.util.LinkedList; 7 | import java.util.Queue; 8 | 9 | /** 10 | * @author renyujie518 11 | * @version 1.0.0 12 | * @ClassName PrintTreeFromTopToBottom_32.java 13 | * @Description 层序遍历树 14 | * 从上往下打印出二叉树的每个节点,同层节点从左至右打印。 15 | * 16 | * 使用队列来进行层次遍历。 17 | * 不需要使用两个队列分别存储当前层的节点和下一层的节点,因为在开始遍历一层的节点时,当前队列中的节点数就是当前层的节点数, 18 | * 只要控制遍历这么多节点数,就能保证这次遍历的都是当前层的节点。 19 | * @createTime 2021年08月21日 16:17:00 20 | */ 21 | public class PrintTreeFromTopToBottom_32 { 22 | public static ArrayList PrintTreeFromTopToBottom(TreeNode root) { 23 | Queue queue = new LinkedList<>(); 24 | ArrayList result = new ArrayList<>(); 25 | queue.add(root); 26 | while (!queue.isEmpty()) { 27 | int size = queue.size(); 28 | while (size > 0) { 29 | TreeNode curr = queue.poll(); 30 | if (curr == null) { 31 | continue; 32 | } 33 | result.add(curr.val); 34 | if(curr.left != null) 35 | queue.add(curr.left); 36 | if(curr.right != null) 37 | queue.add(curr.right); 38 | size--; 39 | 40 | } 41 | } 42 | return result; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/minCommonInSearchTree_68.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | import Tree.TreeNode; 4 | 5 | /** 6 | * @author renyujie518 7 | * @version 1.0.0 8 | * @ClassName minCommonInTree_68.java 9 | * @Description 二叉搜索树((对于每个子树,左边的小,右边的大))的最近公共祖先 10 | * 近公共祖先的定义: 设节点 root为节点 p,q 的某公共祖先,若其左子节点 root.left和右子节点 root.right 都不是 p,q 的公共祖先, 11 | * 则称 rootroot 是 “最近的公共祖先” 。 12 | * 根据以上定义,若 root 是 p,q 的 最近公共祖先 ,则只可能为以下情况之一: 13 | * 14 | * p 和 q在 root的子树中,且分列 root 的 异侧(即分别在左、右子树中); 15 | * p = root,且 q 在 root的左或右子树中; 16 | * q=root,且 pp在 root 的左或右子树中; 17 | * 18 | * 19 | * 给定条件:① 树为 二叉搜索树 ,② 树的所有节点的值都是 唯一 的。 20 | * 根据以上条件,可方便地判断 p,q 与 root的子树关系,即: 21 | * 22 | * 若 root.valp.val ,则 p 在 root 左子树 中; 24 | * 若 root.val=p.val ,则 p 和 root 指向 同一节点 。 25 | 26 | 27 | * @createTime 2021年08月28日 18:29:00 28 | */ 29 | public class minCommonInSearchTree_68 { 30 | public TreeNode minCommonInSearchTree(TreeNode root, TreeNode p, TreeNode q) { 31 | if(root.val < p.val && root.val < q.val){ 32 | //当 p,q 都在 root 的 右子树 中,则开启递归 root.right 并返回; 33 | return minCommonInSearchTree(root.right, p, q); 34 | } 35 | 36 | if(root.val > p.val && root.val > q.val){ 37 | //当p,q 都在 root 的 左子树 中,则开启递归 root.left 并返回; 38 | return minCommonInSearchTree(root.left, p, q); 39 | } 40 | 41 | return root; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/Tree/UniqueBST.java: -------------------------------------------------------------------------------- 1 | package Tree; 2 | 3 | /** 4 | * @author admin 5 | * @version 1.0.0 6 | * @ClassName UniqueBST.java 7 | * @Description 给定一个非负整数n,代表二叉树的节点个数。返回能形成多少种不同的二叉树结构 8 | * @createTime 2021年03月31日 15:25:00 9 | */ 10 | public class UniqueBST { 11 | public static int process(int n){ 12 | if (n<0){ 13 | return 0; 14 | } 15 | if (n == 0){ 16 | return 1; //空树 17 | } 18 | if (n==1){ 19 | return 1;//只有一个节点,只能一种结构 20 | } 21 | if (n==2){ 22 | return 2;//两个节点,要么连在一起,要么各自为树 23 | } 24 | int res = 0; 25 | for (int leftNum = 0;leftNum<=n-1;leftNum++){ //leftNum 给左树分配的节点数 26 | int leftways = process(leftNum); 27 | int rightWays = process(n - 1 - leftNum); //-1是因为有个头结点 28 | res += leftways * rightWays; //注意 是乘 29 | } 30 | return res; 31 | } 32 | 33 | public static int numTreesDp(int n) { 34 | if (n<2){ 35 | return 1; 36 | } 37 | int[] dp = new int[n + 1]; 38 | dp[0] = 1; //basecase 空树 39 | for (int i = 1; i < n + 1; i++) { //节点数为i的情况 40 | for (int j = 0; j <= i - 1; j++) { //左侧节点个数为j,j最多不能超过总计点数i再减1(头结点),右侧节点个数i-j-1 41 | dp[i] += dp[j] * dp[i - j - 1]; 42 | } 43 | } 44 | 45 | return dp[n];//返回节点数在n的时候的情况 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/middleInIO_41.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | import java.util.PriorityQueue; 4 | import java.util.Queue; 5 | 6 | /** 7 | * @author renyujie518 8 | * @version 1.0.0 9 | * @ClassName middleInIO_41.java 10 | * @Description 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。 11 | * 如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。 12 | * 我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。 13 | * 14 | 建立一个 小顶堆 A 和 大顶堆 B ,各保存列表的一半元素 15 | A 保存 较大 的一半,B 保存 较小 的一半 16 | 这样中位数很明显 若是奇数个 在偏后的A的堆顶 若是偶数个,两个堆顶元素相加除2 17 | 18 | 插入的时候注意,不是直接往A或者B里插入,比如总数是偶数个,假设插入数字 num,可能属于 “较小的一半” (即属于 B ) 19 | 因此不能将 nums直接插入至 A 。而应先将 num 插入至 B ,再将 B堆顶元素插入至 A 。 20 | 这样就可以始终保持 A 保存较大一半、 B 保存较小一半。 21 | (由于B是大根堆,堆顶是大元素,将经过筛选的大元素放入小根堆,小根堆就做到了存储较大的元素,只不过在堆底而已)对抗思想 22 | * @createTime 2021年08月24日 10:43:00 23 | */ 24 | public class middleInIO_41 { 25 | private Queue A, B; 26 | public middleInIO_41(){ 27 | A = new PriorityQueue<>();//小根堆,保存较大 的一半 28 | B = new PriorityQueue<>((o1, o2) -> (o2 - o1));//大根堆,储存较小的一半 29 | } 30 | 31 | public void addNum(int num) { 32 | if (A.size() != B.size()) {//目前是奇数个 33 | A.add(num); 34 | B.add(A.poll()); 35 | } else {//偶数个 36 | B.add(num); 37 | A.add(B.poll()); 38 | } 39 | } 40 | public double findMedian() { 41 | return A.size() != B.size() ? A.peek() : (A.peek() + B.peek()) / 2.0; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/facing/overhalfprint.java: -------------------------------------------------------------------------------- 1 | package facing; 2 | 3 | import java.util.HashMap; 4 | import java.util.LinkedList; 5 | import java.util.List; 6 | 7 | /** 8 | * @author renyujie518 9 | * @version 1.0.0 10 | * @ClassName DayuHalf.java 11 | * @Description 找出数组中出现次数大于数组长度一半的数,实现moreThanHaft函数 12 | * 思路: 13 | * 建立一个hashmap 记录数组中出现的次数 key = 那个数 value = 次数 14 | * 再用一个list存储符合条件的数 15 | * @createTime 2021年08月19日 15:17:00 16 | */ 17 | public class overhalfprint { 18 | public static void main(String[] args) { 19 | int[] arr = {1, 3, 2, 2, 6, 2, 2, 7, 2, 2, 2, 2}; 20 | moreThanHaft(arr); 21 | } 22 | 23 | public static void moreThanHaft(int[] num) { 24 | HashMap map = new HashMap<>(); 25 | List list = new LinkedList<>(); 26 | int length = num.length ; 27 | for (int i = 0; i < num.length; i++) { 28 | if (map.containsKey(num[i])) {//说明添加过 29 | map.put(num[i], map.get(num[i]) + 1); //次数加1 30 | } else {//第一次遇见 31 | map.put(num[i], 1); //初始值设为1 32 | } 33 | } 34 | //map构建完毕 35 | for (Integer key : map.keySet()) { 36 | if (map.get(key) > (length / 2)) { 37 | list.add(key); 38 | } 39 | } 40 | 41 | 42 | 43 | for (int i = 0; i < list.size(); i++) { 44 | System.out.println(list.get(i)); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/MiddleClass/Rand5ToRand7.java: -------------------------------------------------------------------------------- 1 | package MiddleClass; 2 | 3 | /** 4 | * @author admin 5 | * @version 1.0.0 6 | * @ClassName Rand5ToRand7.java 7 | * @Description 8 | * 给定一个函数f,可以1~5的数字等概率返回一个。请加工出1~7的数字等概率返回一个的函数g。 9 | * 给定一个函数f,可以a~b的数字等概率返回一个。请加工出c~d的数字等概率返回一个的函数g。 10 | * 给定一个函数f,以p概率返回0,以1-p概率返回1。请加工出等概率返回0和1的函数g 11 | * 12 | * 用二进制去做 13 | * @createTime 2021年03月31日 14:39:00 14 | */ 15 | public class Rand5ToRand7 { 16 | //给定 1~5的数字等概率返回一个 17 | public static int f(){ 18 | return (int) (Math.random() * 5) + 1; 19 | } 20 | 21 | //首先创造一个等概率返回0 1的函数 22 | //思路是利用f 3就继续循环 1,2返回0 4,5返回1 23 | public static int r01(){ 24 | int res = 0; 25 | do { 26 | res = f(); 27 | } while (res == 3); 28 | return res < 3 ? 0 : 1; 29 | } 30 | 31 | //有了r01(),如何1~7的数字等概率返回一个(其实是0-6) 32 | //利用二进制 8421 33 | //主要是几个二进制位够 1个二进制可以返回0-1 2个二进制可以返回0-3 3个二进制可以返回0-7 34 | public static int r07(){ 35 | int res = 0; 36 | do { 37 | res = (r01() << 2) + (r01() << 1) + r01(); 38 | } while (res == 7); 39 | return res + 1; 40 | } 41 | 42 | //给定一个函数f,可以a~b的数字等概率返回一个。请加工出c~d的数字等概率返回一个的函数g。 43 | //还是一样的思路 先把f加工成01发生器(一半一半,分界点dowhile),然后把c~d减去x变成0-y,看几个二进制位够用就roll几次,最后+x返回 44 | 45 | 46 | //给定一个函数f,以p概率返回0,以1-p概率返回1。请加工出等概率返回0和1的函数g 47 | //一个道理 roll两次 00或11dowhile,,01和10的概率都是p(1-p),所以定下来 01返回0 10返回1 48 | 49 | 50 | 51 | 52 | } 53 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/replaceSpace_05.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | /** 4 | * @author renyujie518 5 | * @version 1.0.0 6 | * @ClassName replaceSpace_05.java 7 | * @Description 8 | * 将一个字符串中的空格替换成 "%20"。 9 | * Input: 10 | * "A B" 11 | * 12 | * Output: 13 | * "A%20B" 14 | * 15 | * 思路: 16 | * 在字符串尾部填充任意字符,使得字符串的长度等于替换之后的长度。 17 | * 因为一个空格要替换成三个字符(%20),因此当遍历到一个空格时,需要在尾部填充两个任意字符。 18 | * 令 P1 指向字符串原来的末尾位置,P2 指向字符串现在的末尾位置。 19 | * P1 和 P2 从后向前遍历,当 P1 遍历到一个空格时,就需要令 P2 指向的位置依次填充 02%(注意是逆序的),否则就填充上 P1 指向字符的值。 20 | * 从后向前遍是为了在改变 P2 所指向的内容时,不会影响到 P1 遍历原来字符串的内容。 21 | * @createTime 2021年08月17日 14:51:00 22 | */ 23 | public class replaceSpace_05 { 24 | public static String replaceSpace(StringBuffer str) { 25 | int p1 = str.length() - 1; 26 | //首先填充一下原数组 27 | for (int i = 0; i <= p1; i++) { 28 | if (str.charAt(i) == ' ') { 29 | str.append(" ");//一个空格要替换成三个字符,这里先把原数组改造成一个空格对应三个空格,但是同时也包括了末尾 30 | } 31 | } 32 | int p2 = str.length() - 1; 33 | while (p1 >= 0 && p2 > p1) { 34 | char curr = str.charAt(p1--); 35 | if (curr == ' ') { 36 | //注意倒序 37 | str.setCharAt(p2--, '0'); 38 | str.setCharAt(p2--, '2'); 39 | str.setCharAt(p2--, '%'); 40 | } else { 41 | str.setCharAt(p2--, curr); 42 | } 43 | } 44 | return String.valueOf(str); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/LinkedList/findLoopNode.java: -------------------------------------------------------------------------------- 1 | package LinkedList; 2 | 3 | /** 4 | * @author admin 5 | * @version 1.0.0 6 | * @ClassName findLoopNode.java 7 | * @Description 判断单向链表有没有环,没有返回null,有的话返回入环的节点 8 | * 注意: 单向链表如果有环就一定走不出去了,因为只有一个next。所以有环的链表没有null 9 | * 主要使用快慢指针的做法 10 | * 归结一句话: 11 | * 快走2,慢走1,如果有环,一定会相遇。且走过的路一定< 2*list.length(追逐赛,速度两倍,不可能超过2圈) 12 | * 相遇后,就让其中一个指针回到链表的头部,另一个不动,然后两个指针再一起走,每次1步,再相遇的节点就是入环节点 13 | * @createTime 2021年02月28日 13:02:00 14 | */ 15 | public class findLoopNode { 16 | 17 | public static ListNode findLoopNode(ListNode head){ 18 | if (head == null || head.next == null || head.next.next == null){ //有环的链表长度至少为3 19 | return null; 20 | } 21 | 22 | ListNode slow = head.next; 23 | ListNode fast = head.next.next; 24 | while (fast != slow){ // 快指针和慢指针在环中相遇的时候就停 25 | //快指针肯定走的快,要是快指针首先触碰到null就代表没环 26 | if(fast.next == null || fast.next.next == null){ 27 | return null; 28 | } 29 | fast = fast.next.next; 30 | slow = slow.next; 31 | } 32 | //此时,如果运行到这里,代表快慢指针在环中相遇(同时验证必有还) 33 | //相遇后,就让其中一个指针回到链表的头部,另一个不动 34 | fast = head; 35 | //然后两个指针再一起走,每次1步,再相遇的节点就是入环节点 36 | while (fast != slow){ 37 | slow = slow.next; 38 | fast = fast.next; 39 | } 40 | //运行到这里,已经找到入环节点了且fast = slow,返回哪个指针都可以 41 | return slow; 42 | 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/rebuildTree_07.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | import Tree.TreeNode; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | /** 9 | * @author renyujie518 10 | * @version 1.0.0 11 | * @ClassName rebuildTree.java 12 | * @Description 重建二叉树 13 | * 根据二叉树的前序遍历和中序遍历的结果,重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。 14 | * 前:中左右 15 | * 中:左中右 16 | *

17 | * 前序遍历的第一个值为根节点的值,使用这个值将中序遍历结果分成两部分,左部分为树的左子树中序遍历结果,右部分为树的右子树中序遍历的结果 18 | * @createTime 2021年08月17日 16:51:00 19 | */ 20 | public class rebuildTree_07 { 21 | //中序入map,前序遍历 22 | private static Map inOrderMap = new HashMap(); 23 | 24 | public static TreeNode rebuildTree(int[] pre, int[] in) { 25 | for (int i = 0; i < in.length; i++) { 26 | inOrderMap.put(in[i], i); // key:中序中对应的值 value:对应的index 27 | } 28 | return process(pre, 0, pre.length - 1, 0); 29 | } 30 | 31 | public static TreeNode process(int[] pre, int preL, int preR, int inL) { 32 | //终止条件 33 | if (preL > preR) { 34 | return null; 35 | } 36 | TreeNode root = new TreeNode(pre[preL]); 37 | Integer rootIndex = inOrderMap.get(root); 38 | int leftTreeSize = rootIndex - inL; 39 | root.left = process(pre, preL + 1, preL + leftTreeSize, inL); 40 | root.right = process(pre, preL + leftTreeSize + 1, preR, inL + leftTreeSize + 1); 41 | return root; 42 | } 43 | 44 | 45 | } 46 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/overhalfprint_39.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | import java.util.HashMap; 4 | import java.util.LinkedList; 5 | import java.util.List; 6 | 7 | /** 8 | * @author renyujie518 9 | * @version 1.0.0 10 | * @ClassName overhalfprint_39.java 11 | * @Description 12 | * 找出数组中出现次数大于数组长度一半的数,实现moreThanHaft函数 13 | * * 思路: 14 | * * 建立一个hashmap 记录数组中出现的次数 key = 那个数 value = 次数 15 | * * 再用一个list存储符合条件的数 16 | * @createTime 2021年08月19日 22:01:00 17 | */ 18 | public class overhalfprint_39 { 19 | public static void main(String[] args) { 20 | int[] arr = {1, 3, 2, 2, 6, 2, 2, 7, 2, 2, 2, 2}; 21 | moreThanHaft(arr); 22 | } 23 | 24 | public static void moreThanHaft(int[] num) { 25 | HashMap map = new HashMap<>(); 26 | List list = new LinkedList<>(); 27 | int length = num.length ; 28 | for (int i = 0; i < num.length; i++) { 29 | if (map.containsKey(num[i])) {//说明添加过 30 | map.put(num[i], map.get(num[i]) + 1); //次数加1 31 | } else {//第一次遇见 32 | map.put(num[i], 1); //初始值设为1 33 | } 34 | } 35 | //map构建完毕 36 | for (Integer key : map.keySet()) { 37 | if (map.get(key) > (length / 2)) { 38 | list.add(key); 39 | } 40 | } 41 | 42 | 43 | 44 | for (int i = 0; i < list.size(); i++) { 45 | System.out.println(list.get(i)); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/FindInMatrix_04.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | /** 4 | * @author renyujie518 5 | * @version 1.0.0 6 | * @ClassName FindInMatrix_04.java 7 | * @Description 8 | * 给定一个二维数组,其每一行从左到右递增排序,从上到下也是递增排序。给定一个数,判断这个数是否在该二维数组中。 9 | * 10 | * Consider the following matrix: 11 | * [ 12 | * [1, 4, 7, 11, 15], 13 | * [2, 5, 8, 12, 19], 14 | * [3, 6, 9, 16, 22], 15 | * [10, 13, 14, 17, 24], 16 | * [18, 21, 23, 26, 30] 17 | * ] 18 | * 19 | * Given target = 5, return true. 20 | * Given target = 20, return false. 21 | * 22 | * 思路: 23 | * 要求时间复杂度 O(M + N),空间复杂度 O(1)。其中 M 为行数,N 为 列数。 24 | * 该二维数组中的一个数,小于它的数一定在其左边,大于它的数一定在其下边。 25 | * 因此,从右上角开始查找,就可以根据 target 和当前元素的大小关系来缩小查找区间,当前元素的查找区间为左下角的所有元素。 26 | * @createTime 2021年08月17日 14:36:00 27 | */ 28 | public class FindInMatrix_04 { 29 | public static boolean FindInMatrix(int[][] matrix, int target) { 30 | if (matrix == null || matrix.length == 0 || matrix[0].length== 0) { 31 | return false; 32 | } 33 | int row = matrix.length; 34 | int col = matrix[0].length; 35 | //从右上角开始,往左下角走 36 | int r = 0; 37 | int c = col - 1; 38 | while (r <= row - 1 && c > 0) { 39 | if (matrix[r][c] == target) { 40 | return true; 41 | } else if (target > matrix[r][c]) { 42 | r++; 43 | } else { 44 | col--; 45 | } 46 | } 47 | return false; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/facing/findMinInDiZeng.java: -------------------------------------------------------------------------------- 1 | package facing; 2 | 3 | /** 4 | * @author renyujie518 5 | * @version 1.0.0 6 | * @ClassName findMinInDiZeng.java 7 | * @Description 8 | * 给定一个严格递增循环整数数组,从里面找出最小的元素,使用的算法越快越好。 9 | * 特别地,最小的元素可能出现在数组中间。比如:50, 52, 63, 90, 3, 8, 15, 44。 10 | * 11 | * 这道题就是二分而已了 12 | * @createTime 2021年08月20日 14:06:00 13 | */ 14 | public class findMinInDiZeng { 15 | public static int findMinInDiZeng(int[] num, int begin, int end){ 16 | 17 | //当数组只有一个元素时,num[begin] == num[end] 直接返回 18 | //当数组第一位小于最后一位时,第一位即为最小,因为数组循环递增 19 | if(num[begin] <= num[end]){ 20 | return num[begin]; 21 | } 22 | //数组只有两位时,例如2,1这时候直接范围最后一位,如果是1,2则上边的If语句已经做出判断,这里不用考虑 23 | if(end - begin == 1){ 24 | return num[end]; 25 | } 26 | //算出中间位 27 | int middle = (begin + end) / 2; 28 | //如果中间位小于左边的,那么中间位便是最小值,因为数组循环递增 29 | if(num[middle] < num[middle - 1]){ 30 | return num[middle]; 31 | } 32 | //当中间位小于第一位时,中间位左边为循环递增,右边为严格递增,最小值一定在循环递增处 33 | //反之,左边为循环递增,最小值一定在循环递增处 34 | if(num[middle] > num[begin]){ 35 | return findMinInDiZeng(num,middle+1,end); 36 | }else{ 37 | return findMinInDiZeng(num,begin, middle-1); 38 | } 39 | 40 | 41 | } 42 | 43 | public static void main(String[] args) { 44 | int[] a = new int[]{3,4,5,2}; 45 | System.out.println(findMinInDiZeng(a, 0, 3)); 46 | 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/MiddleClass/CordCoverMaxPoint.java: -------------------------------------------------------------------------------- 1 | package MiddleClass; 2 | 3 | /** 4 | * @author admin 5 | * @version 1.0.0 6 | * @ClassName CordCoverMaxPoint.java 7 | * @Description 8 | * 给定一个有序数组arr,代表数轴上从左到右有n个点arr[0]、arr[1]...arr[n-1], 9 | * 给定一个正数x,代表一根长度为x的绳子,求绳子最多能覆盖其中的几个点。 10 | * 11 | * 滑动窗口 L到达arr的每个点后不动 R移动每个点,看超没超x的距离(单调不递减的方式确定终点) 12 | * 思路2: 贪心:在arr[0..R]范围上,找满足>=value的最左位置 13 | * @createTime 2021年03月27日 14:00:00 14 | */ 15 | public class CordCoverMaxPoint { 16 | //在arr[0..R]范围上,找满足>=value的最左位置(对一个有序的数组arr来说,就是找到从左到右第一个大于value的位置) 17 | public static int nearstIndex(int[] arr,int R,int value){ 18 | int L = 0; 19 | int index = R; //因为要找>=value的最左位置,对一个有序数组来说最可能的位置是R,R会不断的往前走 20 | while (L= value) {//什么时候R往前移,就是在中间位置都找到了>=value,那肯定这个index就是目标,最左位置,R再减1 24 | index = mid; 25 | R = mid - 1; 26 | }else { 27 | L = mid + 1; 28 | } 29 | } 30 | return index; 31 | } 32 | 33 | // 长度为L的绳子最多覆盖几个点 34 | public static int maxPoint(int[] arr, int L) { 35 | int res = 1; //因为先把右节点固定在某个arr[i]上,相当于至少也会覆盖1个 36 | for (int i = 0; i < arr.length; i++) { 37 | int nearest = nearstIndex(arr, i, arr[i] - L);//在arr[0..i]范围上,找满足>=(arr[i] - L)的最左位置索引 38 | res = Math.max(res, i - nearest + 1); 39 | } 40 | return res; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/util/PrintTree.java: -------------------------------------------------------------------------------- 1 | package util; 2 | 3 | import Tree.TreeNode; 4 | 5 | /** 6 | * @author admin 7 | * @version 1.0.0 8 | * @ClassName PrintTree.java 9 | * @Description 这是左神提供的打印数结构的一个工具,虽然不能直接看出来,但可以在纸上写出来然后顺时针旋转90度即可 10 | *注释说明: 11 | * 1.两个符号之间夹的是具体的值 12 | * 2.H_H代表根节点,v_v代表该数的父结点在该数的左方偏下(以该数的角度看) ^_^ 代表父节点在该数的左方偏上(以该数的角度看) 13 | * 3.别忘了顺时针旋转90度再看 14 | * @createTime 2021年02月28日 20:48:00 15 | */ 16 | public class PrintTree { 17 | 18 | public static void printTree(TreeNode head) { 19 | System.out.println("Binary Tree:"); 20 | printInOrder(head, 0, "H", 17); 21 | System.out.println(); 22 | } 23 | 24 | public static void printInOrder(TreeNode head, int height, String to, int len) { 25 | if (head == null) { 26 | return; 27 | } 28 | printInOrder(head.right, height + 1, "v", len); 29 | String val = to + head.val + to; 30 | int lenM = val.length(); 31 | int lenL = (len - lenM) / 2; 32 | int lenR = len - lenM - lenL; 33 | val = getSpace(lenL) + val + getSpace(lenR); 34 | System.out.println(getSpace(height * len) + val); 35 | printInOrder(head.left, height + 1, "^", len); 36 | } 37 | 38 | public static String getSpace(int num) { 39 | String space = " "; 40 | StringBuffer buf = new StringBuffer(""); 41 | for (int i = 0; i < num; i++) { 42 | buf.append(space); 43 | } 44 | return buf.toString(); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/exam/TXexam1.java: -------------------------------------------------------------------------------- 1 | package exam; 2 | 3 | import java.util.HashMap; 4 | import java.util.LinkedList; 5 | import java.util.List; 6 | 7 | /** 8 | * @author renyujie518 9 | * @version 1.0.0 10 | * @ClassName exam1.java 11 | * @Description 12 | * 腾讯光子面试 13 | * 一个数量为N的整数数组,其中一个数字出现次数超过N/2,请将该数字找出来。 14 | * 超过一半 15 | * 16 | * 建立一个map,统计次数 key = 那个数 value = 次数 17 | * @createTime 2021年09月23日 09:22:00 18 | */ 19 | public class TXexam1 { 20 | public static List exam1(int[] num) { 21 | HashMap countMap = new HashMap<>(); 22 | LinkedList result = new LinkedList<>(); 23 | int n = num.length; 24 | //先统计 25 | for (int i = 0; i < n; i++) { 26 | if (countMap.containsKey(num[i])) { 27 | //说明添加过,+1 28 | countMap.put(num[i], countMap.get(num[i]) + 1); 29 | } else {//第一次遇见 30 | countMap.put(num[i], 1); 31 | 32 | } 33 | } 34 | //map构建完毕 35 | for (Integer key : countMap.keySet()) {//num[i] 36 | if (countMap.get(key) > (n / 2)) { 37 | result.add(key); 38 | } 39 | 40 | } 41 | return result; 42 | } 43 | 44 | public static void main(String[] args) { 45 | int[] arr = {1, 2, 3, 3, 3,3,3,3,6, 7}; 46 | List res = exam1(arr); 47 | for (int i = 0; i < res.size(); i++) { 48 | System.out.println(res.get(i)); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/facing/combineAndpermute/combinaSumAsTarget3.java: -------------------------------------------------------------------------------- 1 | package facing.combineAndpermute; 2 | 3 | import java.util.ArrayList; 4 | import java.util.LinkedList; 5 | import java.util.List; 6 | 7 | /** 8 | * @author renyujie518 9 | * @version 1.0.0 10 | * @ClassName combinaSumAsTarget3.java 11 | * @Description 12 | * 出所有相加之和为n 的k个数的组合。组合中只允许含有 1 -9 的正整数,并且每种组合中不存在重复的数字。 13 | * 说明: 14 | * 所有数字都是正整数。 15 | * 解集不能包含重复的组合。 16 | * 输入: k = 3, n = 9 17 | * 输出: [[1,2,6], [1,3,5], [2,3,4]] 18 | * 19 | * 回溯算法 20 | * 21 | * @createTime 2021年09月01日 17:17:00 22 | */ 23 | public class combinaSumAsTarget3 { 24 | List> res = new ArrayList<>(); 25 | LinkedList list = new LinkedList<>(); 26 | 27 | //无重复元素的数组中组合出不重复的的解,但是元素限定了范围 28 | //这里为了好看 把目标设为target,把需要几个数组成设为n 29 | public List> combinaSumAsTarget3(int n, int target) { 30 | backtrace(n, target, 1); 31 | return res; 32 | } 33 | 34 | 35 | private void backtrace(int n, int target, int start) { 36 | if (list.size() == n || target == 0) { 37 | res.add(new ArrayList<>(list)); 38 | return; 39 | } 40 | //注意这里,因为不能有重复的集合以及集合中不能有重复的数字,所以这里的i不能从0开始, 41 | // 要从上一个选择之后的下一个值开始 i+1 42 | for (int i = start; i <= 9; i++) { 43 | //选择当前值 44 | list.add(i); 45 | //递归 46 | backtrace(n, target - i, i + 1); 47 | //撤销选择 48 | list.removeLast(); 49 | } 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SQL/同时选2门: -------------------------------------------------------------------------------- 1 | MySql数据库查询同时选修了两门课的学生姓名、学号 2 | 这个查询要用到含有学生姓名、学号的表和含有学生选课情况的表, 3 | 4 | 我定义的含有学生姓名sname和学号sno的表是student表(sno学号,sname姓名,sdept专业,sage年龄,ssex性别) 5 | 含有学生选课情况的表是sc表(sno学号,cno选修课程号,grade成绩),。 6 | 7 | 假设查询选修课程为2和3的学生姓名和学号,方法如下: 8 | 集合查询 9 | where语句下的and不能用于连接两个相同属性的查询,如该题不能写为where cno=‘002’ and cno=‘003’ 。 10 | 所以首先要从sc表查询选修课程为2号(或者3号)的学生学号,然后用and连接查询课程号为2号(或3号)的课程: 11 | 12 | WHERE sc.sno IN 13 | ( SELECT sno FROM sc 14 | WHERE cno='002' 15 | AND sno IN // ---+ 16 | ( SELECT sno FROM sc // |查询选课课号为3的学生学号 17 | WHERE cno='003' // ---+ 18 | ) 19 | ) 20 | 21 | 以上为在sc表中查询出选修课程为2号和3号的学生学号,然后,我们要将查询出来的学号集合和student表结合,在student表中找出和以上查询出的学生学号相同的学生,最后输出其学号和姓名。 22 | 因此,总的查询语句为: 23 | 24 | SELECT student.sno,sname FROM student,sc 25 | WHERE sc.sno IN 26 | ( SELECT sno FROM sc 27 | WHERE cno='002' 28 | AND sno IN // ---+ 29 | ( SELECT sno FROM sc// |查询选课课号为3的学生学号 30 | WHERE cno='003' // ---+ 31 | ) 32 | )AND student.sno=sc.sno 33 | group by student.sno; 34 | 因为两个表中都有学号sno属性,所以要表明所要查询的学号出自哪个表,最后按学号分组,以免重复出现同一名学生的信息。 35 | 输出结果如图所示: 36 | 37 | +-------+-------+ 38 | | sno | sname | 39 | +-------+-------+ 40 | | 08001 | 张力 | 41 | | 08002 | 李丽 | 42 | | 08003 | 赵海 | 43 | +-------+-------+ 44 | 3 rows in set 45 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/facing/combineAndpermute/combinaSumAsTarget4.java: -------------------------------------------------------------------------------- 1 | package facing.combineAndpermute; 2 | 3 | /** 4 | * @author renyujie518 5 | * @version 1.0.0 6 | * @ClassName combinaSumAsTarget3.java 7 | * @Description 给你一个由 不同 整数组成的数组 nums ,和一个目标整数 target 。 8 | * 请你从 nums 中找出并返回总和为 target 的元素组合的个数。 9 | * 输入:nums = [1,2,3], target = 4 10 | * 输出:7 11 | * 解释: 12 | * 所有可能的组合为: 13 | * (1, 1, 1, 1) 14 | * (1, 1, 2) 15 | * (1, 2, 1) 16 | * (1, 3) 17 | * (2, 1, 1) 18 | * (2, 2) 19 | * (3, 1) 20 | * 请注意,顺序不同的序列被视作不同的组合。 21 | * 22 | * 如果还是使用combinaSumAsTarget1,2,3的回溯 23 | * 如果本题要把排列都列出来的话,只能使用回溯算法爆搜。 24 | * 25 | * 背包问题 dp 26 | * dp[i]: 凑成目标正整数为i的排列个数为dp[i] 27 | * dp[i] += dp[i - nums[j]] 28 | * dp[i](考虑nums[j])可以由 dp[i - nums[j]](不考虑nums[j]) 推导出来。 29 | * 因为只要得到nums[j],排列个数dp[i - nums[j]],就是dp[i]的一部分。 30 | 31 | *因为递推公式dp[i] += dp[i - nums[j]]的缘故,dp[0]要初始化为1,这样递归其他dp[i]的时候才会有数值基础。 32 | * 至于dp[0] = 1 有没有意义呢? 33 | * 其实没有意义 34 | * 35 | * @createTime 2021年09月01日 17:01:00 36 | */ 37 | public class combinaSumAsTarget4 { 38 | public int combinaSumAsTarget4(int[] nums, int target) { 39 | //dp[i]: 凑成target为i的排列个数为dp[i] 40 | int[] dp = new int[target + 1]; 41 | // 这个值被其它状态参考,设置为 1 是合理的 42 | dp[0] = 1; 43 | for (int i = 1; i <= target; i++) { 44 | for (int num : nums) { 45 | if (num <= i) { 46 | dp[i] = dp[i] + dp[i - num]; 47 | } 48 | } 49 | } 50 | return dp[target]; 51 | } 52 | 53 | 54 | 55 | } 56 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/SouSuo2LinkNode_36.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | import Tree.TreeNode; 4 | 5 | /** 6 | * @author renyujie518 7 | * @version 1.0.0 8 | * @ClassName SouSuo2LinlNode_36.java 9 | * @Description 二叉搜索树与双向链表 10 | * 输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。 11 | * 12 | * 二叉搜索树的中序遍历为 递增序列 。 13 | * 使用中序遍历访问树的各节点 cur ;并在访问每个节点时构建 cur 和前驱节点 pre 的引用指向; 14 | * 中序遍历完成后,最后构建头节点和尾节点的引用指向即可。 15 | * @createTime 2021年08月21日 21:45:00 16 | */ 17 | public class SouSuo2LinkNode_36 { 18 | private static TreeNode pre = null;//pre用于记录双向链表中位于cur左侧的节点 19 | private static TreeNode head = null; 20 | public static TreeNode SouSuo2LinkNode(TreeNode root) { 21 | if(root == null) return null; 22 | inOrder(root); 23 | //中序遍历完成后,head 指向头节点, pre 指向尾节点 24 | head.left = pre; 25 | pre.right = head; 26 | return root; 27 | } 28 | 29 | 30 | public static void inOrder(TreeNode curr) { 31 | if(curr==null){ 32 | return; 33 | } 34 | inOrder(curr.left); 35 | //pre用于记录双向链表中位于cur左侧的节点,即上一次迭代中的cur,当pre==null时,cur左侧没有节点,即此时cur为双向链表中的头节点 36 | if(pre==null){ 37 | head = curr; 38 | } else{ 39 | //反之,pre!=null时,cur左侧存在节点pre,需要进行pre.right=cur的操作。 40 | pre.right = curr; 41 | } 42 | curr.left = pre;//pre是否为null对这句没有影响,且这句放在上面两句if else之前也是可以的。 43 | 44 | pre = curr;//更新 pre = cur ,即节点 cur 是后继节点的 pre 45 | inOrder(curr.right);//全部迭代完成后,pre指向双向链表中的尾节点 46 | 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/Greedy/lowestZiDianXu.java: -------------------------------------------------------------------------------- 1 | package Greedy; 2 | 3 | import java.util.Arrays; 4 | import java.util.Comparator; 5 | 6 | /** 7 | * @author admin 8 | * @version 1.0.0 9 | * @ClassName lowestZiDianXu.java 10 | * @Description 11 | * 给定一个字符串类型的数组strs,找到一种拼接方式,使得把所有字符串拼起来之后形成的字符串具有最小的字典序。 12 | * 比如 abc > aaa c < cda c >ba 13 | * 错的想法: a,b a<=b, a放前 否则b放前 反例: b,ba 如果按照这个意思这么想 拼接后应该是 bba 但实际上是bab最小 14 | * 15 | * 正确: a,b a.b<=b.a a放前,否则b放前 (.是拼接的意思) 16 | * @createTime 2021年03月16日 16:56:00 17 | */ 18 | public class lowestZiDianXu { 19 | public static String lowestString(String[] strs){ 20 | if (strs == null || strs.length ==0){ 21 | return ""; 22 | } 23 | Arrays.sort(strs, new Comparator() { 24 | @Override 25 | public int compare(String a, String b) { 26 | return (a + b).compareTo(b + a); //如果指定的数与参数相等返回0。如果指定的数小于参数(括号内)返回 -1。如果指定的数大于参数返回 1。 27 | //比较器 obj1和obj2是要比较的对象。如果对象相等,则此方法返回零。如果obj1大于obj2,则返回正值。否则返回负值。 28 | } 29 | }); 30 | String res = ""; 31 | for (int i = 0; i < strs.length; i++) { 32 | res = res + strs[i]; //拼接 33 | } 34 | return res; 35 | } 36 | 37 | public static void main(String[] args) { 38 | String[] strs1 = { "jibw", "ji", "jp", "bw", "jibw" }; 39 | System.out.println(lowestString(strs1));//bw jibw jibw ji jp 40 | 41 | String[] strs2 = { "ba", "b" }; 42 | System.out.println(lowestString(strs2)); 43 | 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/deleteLinkedListNode_18.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | import LinkedList.ListNode; 4 | 5 | /** 6 | * @author renyujie518 7 | * @version 1.0.0 8 | * @ClassName deleteLinkedListNode.java 9 | * @Description 在 O(1) 时间内删除链表节点 10 | * ① 如果该节点不是尾节点,那么可以直接将下一个节点的值赋给该节点,然后令该节点指向下下个节点,再删除下一个节点,时间复杂度为 O(1)。 11 | * ② 如果是尾结点,就需要先遍历链表,找到节点的前一个节点,然后让前一个节点指向 null,时间复杂度为 O(N)。 12 | * 综上,如果进行 N 次操作,那么大约需要操作节点的次数为 N-1+N=2N-1, 13 | * 其中 N-1 表示 N-1 个不是尾节点的每个节点以 O(1) 的时间复杂度操作节点的总次数, 14 | * N 表示 1 个尾节点以 O(N) 的时间复杂度操作节点的总次数。(2N-1)/N ~ 2,因此该算法的平均时间复杂度为 O(1) 15 | * @createTime 2021年08月18日 17:47:00 16 | */ 17 | public class deleteLinkedListNode_18 { 18 | 19 | public static ListNode deleteNode(ListNode head, ListNode tobeDelete) { 20 | if (head == null || tobeDelete == null) { 21 | return null; 22 | } 23 | if (tobeDelete.next != null) { // 要删除的节点不是尾节点 24 | ListNode next = tobeDelete.next; 25 | tobeDelete.val = next.val; 26 | tobeDelete.next = next.next; 27 | } else {// 要删除的节点是尾节点 28 | if (head == tobeDelete) { 29 | // 只有一个节点 30 | head = null; 31 | } else { 32 | ListNode copy = head;//复制一份出来 33 | while (copy.next != tobeDelete) { 34 | copy = copy.next; 35 | } 36 | //此时 指针到了copy.next = tobeDelete即copy指针到了要删除结点(还是尾结点)的前一个节点 37 | copy.next = null; 38 | } 39 | } 40 | return head; 41 | } 42 | } 43 | 44 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/DP/paLouTiWithCost.java: -------------------------------------------------------------------------------- 1 | package DP; 2 | 3 | /** 4 | * @author renyujie518 5 | * @version 1.0.0 6 | * @ClassName paLouTiWithCost.java 7 | * @Description 数组的每个下标作为一个阶梯,第 i 个阶梯对应着一个非负数的体力花费值 cost[i](下标从 0 开始)。 8 | * 每当你爬上一个阶梯你都要花费对应的体力值,一旦支付了相应的体力值,你就可以选择向上爬一个阶梯或者爬两个阶梯。 9 | * 请你找出达到楼层顶部的最低花费。在开始时,你可以选择从下标为 0 或 1 的元素作为初始阶梯。 10 | * 11 | * 示例 1: 12 | * 输入:cost = [10, 15, 20] 输出:15 解释:最低花费是从 cost[1] 开始,然后走两步即可到阶梯顶,一共花费 15 。 13 | * 示例 2: 14 | * 输入:cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1] 输出:6 解释:最低花费方式是从 cost[0] 开始,逐个经过那些 1 , 15 | * 跳过 cost[3] ,一共花费 6 。 16 | * 提示: 17 | * cost 的长度范围是 [2, 1000]。 18 | * cost[i] 将会是一个整型数据,范围为 [0, 999] 。 19 | * 20 | * dp[i]的定义:到达第i个台阶所花费的最少体力为dp[i]。 21 | * dp[i] = min(dp[i - 1], dp[i - 2]) + cost[i]; 22 | * 初始化: 23 | * dp[0] = cost[0] 24 | * dp[1] = cost[1] 25 | * @createTime 2021年09月02日 15:55:00 26 | */ 27 | public class paLouTiWithCost { 28 | public static int paLouTiWithCost(int[] cost) { 29 | if (cost == null || cost.length == 0) { 30 | return 0; 31 | } 32 | if (cost.length == 2) { 33 | return cost[0]; 34 | } 35 | 36 | int[] dp = new int[cost.length]; 37 | dp[0] = cost[0]; 38 | dp[1] = cost[1]; 39 | for (int i = 2; i < cost.length; i++) { 40 | dp[i] = Math.min(dp[i - 1], dp[i - 2]) + cost[i]; 41 | } 42 | //最后一步,如果是由倒数第二步爬,则最后一步的体力花费可以不用算 43 | //比如示例2中的最后那个1就没被算 44 | //就可以理解为到楼顶了就不用考虑cost 45 | return Math.min(dp[cost.length - 1], dp[cost.length - 2]); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/maxLiRun_63.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | /** 4 | * @author renyujie518 5 | * @version 1.0.0 6 | * @ClassName maxLiRun_63.java 7 | * @Description 假设把某股票的价格按照时间先后顺序存储在数组中,请问买卖该股票一次可能获得的最大利润是多少? 8 | * 输入: [7,1,5,3,6,4] 9 | * 输出: 5 10 | * 解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。 11 | * 注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格。 12 | * dp[i]代表以 prices[i]为结尾的子数组的最大利润 13 | * 由于题目限定 “买卖该股票一次” ,因此前 i 日最大利润 dp[i]等于前 i−1 日最大利润 dp[i-1]和第 i 日卖出的最大利润中的最大值。 14 | * dp[i]=max(dp[i−1],prices[i]−min(prices[0:i])) 15 | * 16 | * 初始状态: dp[0] = 0 ,即首日利润为 0 ; 17 | * 返回值: dp[n−1] ,其中 n 为 dp 列表长度。 18 | * https://leetcode-cn.com/problems/gu-piao-de-zui-da-li-run-lcof/solution/mian-shi-ti-63-gu-piao-de-zui-da-li-run-dong-tai-2/ 19 | 20 | 21 | 前i日的收益最大值 = Math.max(前i - 1日的收益的最大值,第i日收益的最大值) 22 | 第i日收益的最大值 = 第i日价格price - 前(i-1)日 价格的最小值minPrice 23 | 因此需要维护一个变量minPrice 24 | 状态转移方程 : dp[i] = Math.max(dp[i-1], price - minPrice),初始状态为0 25 | 状态转移的时机:当minPrice没有更新时进行状态转移 26 | 27 | * @createTime 2021年08月28日 17:51:00 28 | */ 29 | public class maxLiRun_63 { 30 | public int maxProfit(int[] prices) { 31 | if (prices.length < 1) { 32 | return 0; 33 | } 34 | int maxProfit = 0; 35 | int minPrice = Integer.MAX_VALUE; 36 | for (int price : prices) { 37 | if (minPrice > price) { 38 | minPrice = price; 39 | } else { 40 | maxProfit = Math.max(maxProfit, price - minPrice); 41 | } 42 | } 43 | return maxProfit; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/facing/yihuo_Find_jishuge_shu.java: -------------------------------------------------------------------------------- 1 | package facing; 2 | 3 | /** 4 | * @author admin 5 | * @version 1.0.0 6 | * @ClassName z1.java 7 | * @Description TODO 8 | * @createTime 2021年02月08日 18:34:00 9 | * 10 | * []arr int 11 | * (1) 一种数 奇数次 other 偶数次,找到这个数 12 | * (2)2种数 奇数次 other 偶数次,找到这2个数 13 | * 要求:o(n) 14 | * 15 | *异或(相同为0,不同为1 无进制相加)和顺序无关 a^a =0 0^b = b 16 | */ 17 | public class yihuo_Find_jishuge_shu { 18 | public static void main(String[] args) { 19 | int[] arr1 = {2, 1, 3, 1, 3, 1, 3, 2, 1};//找到3 20 | method1(arr1); 21 | 22 | int[] arr2 = {2, 1, 3, 1, 3, 1,4, 3, 2, 1};//找到3,4 23 | method2(arr2); 24 | 25 | } 26 | 27 | public static void method1(int[] arrs){ 28 | int eor = 0; 29 | for (int arr : arrs) { 30 | eor =eor^arr; 31 | } 32 | System.out.println(eor); 33 | } 34 | 35 | 36 | public static void method2(int[] arrs){ 37 | int eor =0; 38 | for (int arr : arrs) { 39 | eor ^= arr; 40 | } 41 | //循环完毕,此时由于a!=b,eor = a^b,eor!=0 42 | //eor必然在某一位上是1 43 | //& 遇0则0 44 | int rightOne = eor & (~eor + 1);//提取这个数二进制最右边为1,其余都是0 11001100 -> 00000100 45 | //现在就假设这一位1是"eor必然在某一位上是1"的那一位 46 | 47 | int aOrb = 0; 48 | for (int arr : arrs) { 49 | if ((arr & rightOne) ==1){ //==0也可以 50 | aOrb ^= arr; //找到a,b其中的一个放到aorb变量中 51 | } 52 | } 53 | //另一个奇数个找到就简单了 a^b^(aOrb) 54 | System.out.println(aOrb+" "+(eor^aOrb)); 55 | 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/facing/ArrayMaxAdd.java: -------------------------------------------------------------------------------- 1 | package facing; 2 | 3 | /** 4 | * @author admin 5 | * @version 1.0.0 6 | * @ClassName ArrayMaxAdd.java 7 | * @Description 8 | * 在一维数组中,求出连续子数组的最大和。如果数组中全是整数, 9 | * 那么最大和为所有元素之和,那么存在负数呢? 10 | * 例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。 11 | * 12 | * 解题思路: 13 | * 14 | * 1. 首先,我们需要定义一个变量currentSum,用for循环来记录前i项的和,currentSum每次都会更改, 15 | * 如果currentSum的值小于0,我们再往后加只有减小最大和,所以我们需要将array[i+1]项的值重新赋值给currentSum。 16 | * 17 | * 18 | * 2. 我们需要定义一个最大值max,每次改变currentSum的值时,我们都需要将max和currentSum进行比较, 19 | * 如果currentSum大于max,我们则将currentSum的值赋值给max。 20 | 21 | * @createTime 2021年03月17日 18:20:00 22 | */ 23 | public class ArrayMaxAdd { 24 | 25 | public static int FindGreatestSumOfSubArray(int[] array) { 26 | if (array.length==0 || array==null) { 27 | return 0; 28 | } 29 | int currentSum = 0; //存储当前连续n项的和 30 | int max = 0; //存储连续子元素和的最大值 31 | for (int i = 0; i < array.length; i++) { 32 | //核心部分,好好理解. 33 | if(currentSum<=0){ //如过当前连续n项的和小于等于0,则没必要与后面的元素相加 34 | currentSum = array[i]; //currentSum重新赋值 35 | }else{ 36 | currentSum += array[i]; //如果currentSum的值大于0,则继续与后面的元素相加, 37 | } 38 | if(currentSum>max){ //每次改变currentSum的值都有与max进行比较 39 | max = currentSum; //如果currentSum的值大于max,则将currentSum的值赋值给max 40 | } 41 | } 42 | return max; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/facing/meituan/minOption_04.java: -------------------------------------------------------------------------------- 1 | package facing.meituan; 2 | 3 | import java.util.Scanner; 4 | 5 | /** 6 | * @author renyujie518 7 | * @version 1.0.0 8 | * @ClassName minOption_04.java 9 | * @Description 10 | * 小美给了小团一个长度为 n(n为偶数)的序列 A,序列中的数都是介于 [1,100000] 的整数。 11 | * 小团想把这个序列变得漂亮后再送回给小美。小美觉得一个序列是漂亮的当且仅当这个序列的前一半和后一半是一样的, 12 | * 即对于 1<=i<=n/2 都满足 A[i]==A[i+n/2]。 13 | * 小团可以按进行以下操作任意次: 14 | * 选择两个介于 [1, 100000] 之间的数 x 和 y,然后将序列 A 中所有值为 x 的数替换为 y。 15 | * 注意,每次操作都会在上一次操作后得到的序列上进行。小团想知道他最少需要操作多少次可以把序列变成漂亮的。 16 | * 17 | * 输入描述 18 | * 第一行是一个整数 n,表示序列的长度。数据保证 n 为偶数。 19 | * 第二行有 n 个用空格隔开的整数,第 i 个数表示 A[i] 的值。数据保证 1<=A[i]<=100000。 20 | * 21 | * 输出描述 22 | * 输出小团需要的最少操作次数。 23 | * 24 | * 输入样例 25 | * 10 26 | * 4 2 1 5 2 10 2 1 5 8 27 | * 输出样例 28 | * 2 29 | * :将一个数组左半和右半变成一样的操作数。 30 | * @createTime 2021年08月28日 23:51:00 31 | */ 32 | public class minOption_04 { 33 | 34 | public static void main(String[] args) { 35 | 36 | Scanner scanner = new Scanner(System.in); 37 | int n = scanner.nextInt(); 38 | int[] ints1 = new int[n/2]; 39 | int[] ints2 = new int[n/2]; 40 | int res = 0; 41 | for(int i = 0;i < n;i ++){ 42 | if(i < n/2){ 43 | ints1[i] = scanner.nextInt(); 44 | }else{ 45 | ints2[i - n/2] = scanner.nextInt(); 46 | } 47 | } 48 | for(int i = 0;i < n/2;i ++){ 49 | if (ints1[i] != ints2[i]){ 50 | //不一样就直接替换 51 | res ++; 52 | } 53 | } 54 | System.out.println(res); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/ShaiZi_60.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | 6 | /** 7 | * @author renyujie518 8 | * @version 1.0.0 9 | * @ClassName ShaiZi_60.java 10 | * @Description n 个骰子的点数 11 | * 把n个骰子扔在地上,所有骰子朝上一面的点数之和为s。输入n,打印出s的所有可能的值出现的概率。 12 | * 动态规划 13 | * 使用一个二维数组 dp 存储点数出现的次数,其中 dp[i][j] 表示前 i 个骰子产生点数(前 n枚骰子的点数和) j 的次数。 14 | * dp[n][j] 来表示最后一个阶段点数 j出现的次数。 15 | * 单单看第 n 枚骰子,它的点数可能为 1 , 2, 3, ... , 6,因此投掷完 n 枚骰子后点数 j 出现的次数, 16 | * 可以由投掷完 n-1 枚骰子后出现的次数之和转化过来 17 | * 第一阶段的状态:投掷完 11枚骰子后,它的可能点数分别为 1, 2, 3, ... , 6 ,并且每个点数出现的次数都是 1 . 18 | * 19 | * https://leetcode-cn.com/problems/nge-tou-zi-de-dian-shu-lcof/solution/nge-tou-zi-de-dian-shu-dong-tai-gui-hua-ji-qi-yo-3/ 20 | * @createTime 2021年08月28日 17:08:00 21 | */ 22 | public class ShaiZi_60 { 23 | public double[] ShaiZi(int n) { 24 | //点数和最多是6*n,时刻注意第二维是点数和,dp的值代表的是次数,产生这么多点数和的次数 25 | int[][] dp = new int[n + 1][6 * n + 1]; 26 | for(int i = 1; i <= 6; i++){ 27 | dp[1][i] = 1; 28 | } 29 | for(int i = 2; i <= n; i++){ 30 | //考虑第i个筛子所导致的 31 | for (int j = i; j <= 6 * i; j++){ 32 | for(int k = 1; k <= 6 && k <= j; k++){ 33 | dp[i][j] = dp[i][j] + dp[i - 1][j - k]; 34 | } 35 | } 36 | } 37 | double[] ans = new double[6 * n - n + 1]; 38 | for(int i = n; i <= 6 * n; i++){ 39 | //所有点数出现的总次数是 6^n 40 | ans[i - n] = ((double) dp[n][i]) / (Math.pow(6, n)); 41 | } 42 | return ans; 43 | 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/firstCommon_52.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | import LinkedList.ListNode; 4 | 5 | import java.util.HashSet; 6 | 7 | /** 8 | * @author renyujie518 9 | * @version 1.0.0 10 | * @ClassName firstCommmon_52.java 11 | * @Description 12 | * 两个链表的第一个公共结点 13 | 14 | * 设 A 的长度为 a + c,B 的长度为 b + c,其中 c 为尾部公共部分长度,可知 a + c + b = b + c + a。 15 | 两个链表长度分别为L1+C、L2+C, C为公共部分的长度 16 | 第一个人走了L1+C步后,回到第二个人起点走L2步; 17 | 第2个人走了L2+C步后,回到第一个人起点走L1步。 18 | 当两个人走的步数都为L1+L2+C时即第一个公共节点 19 | * @createTime 2021年08月27日 11:39:00 20 | */ 21 | public class firstCommon_52 { 22 | public static ListNode firstCommon1(ListNode headA, ListNode headB) { 23 | if (headA == null || headB == null) { 24 | return null; 25 | } 26 | ListNode pa = headA; 27 | ListNode pb = headB; 28 | while (pa != pb) { 29 | pa = pa == null ? headB : pa.next; 30 | pb = pb == null ? headA : pb.next; 31 | } 32 | //至此两节点相遇在第一个公共节点 33 | return pa;//return pb; 34 | } 35 | 36 | 37 | //hashmap法 38 | public static ListNode firstCommon2(ListNode headA, ListNode headB) { 39 | HashSet visited = new HashSet<>(); 40 | ListNode tmp = headA; 41 | while (tmp != null) { 42 | visited.add(tmp); 43 | tmp = tmp.next; 44 | } 45 | tmp = headB; 46 | while (tmp != null) { 47 | if (visited.contains(tmp)) { 48 | return tmp; 49 | } 50 | tmp = tmp.next; 51 | } 52 | return null; 53 | } 54 | } 55 | 56 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/RandomListNodeCopy_35.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | import java.util.HashMap; 4 | 5 | /** 6 | * @author renyujie518 7 | * @version 1.0.0 8 | * @ClassName RandomListNodeCopy_35.java 9 | * @Description 复杂链表的复制 10 | * 输入一个复杂链表(每个节点中有节点值,以及两个指针,一个指向下一个节点,另一个特殊指针指向任意一个节点),返回结果为复制后复杂链表的 head。 11 | * 12 | * 思路: 13 | * 运用hashmap 建立 原节点 -> 新节点 的映射关系来建立新链表。 14 | * 因为每个新节点都和原节点是对应的,建立这个映射关系后, 15 | * 新链表每个节点的 next 和 random 都可以通过对应原链表节点的 next 和 random 来获取。 16 | 17 | * @createTime 2021年08月21日 19:18:00 18 | */ 19 | public class RandomListNodeCopy_35 { 20 | public class RandomListNode { 21 | int val; 22 | RandomListNode next = null; 23 | RandomListNode random = null; 24 | 25 | RandomListNode(int val) { 26 | this.val = val; 27 | } 28 | } 29 | 30 | public RandomListNode RandomListNodeCopy(RandomListNode head) { 31 | if (head == null) { 32 | return null; 33 | } 34 | RandomListNode curr = head; 35 | HashMap map = new HashMap<>(); //key =原链表节点 value =新链表对应节点 36 | while (curr != null) { 37 | map.put(curr, new RandomListNode(curr.val)); 38 | curr = curr.next; 39 | } 40 | curr = head; 41 | //构建新链表的 next 和 random 指向 42 | while (curr != null) { 43 | map.get(curr).next = map.get(curr.next); 44 | map.get(curr).random = map.get(curr.random); 45 | curr = curr.next; 46 | } 47 | //返回新链表头 48 | return map.get(head); 49 | } 50 | 51 | 52 | } 53 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/facing/combineAndpermute/permute1.java: -------------------------------------------------------------------------------- 1 | package facing.combineAndpermute; 2 | 3 | import java.util.ArrayList; 4 | import java.util.LinkedList; 5 | import java.util.List; 6 | 7 | /** 8 | * @author renyujie518 9 | * @version 1.0.0 10 | * @ClassName permute1.java 11 | * @Description 给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。 12 | * 13 | * 示例 1: 14 | * 输入:nums = [1,2,3] 15 | * 输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] 16 | * 与组合不同:排列是有序的,也就是说[1,2] 和[2,1] 是两个集合 17 | * 18 | * 注意点: 19 | * 每层都是从0开始搜索而不是startIndex 20 | * 需要used数组记录path里都放了哪些元素了 21 | * @createTime 2021年09月01日 22:47:00 22 | */ 23 | public class permute1 { 24 | List> result = new ArrayList<>();// 存放符合条件结果的集合 25 | LinkedList path = new LinkedList<>();// 用来存放符合条件结果 26 | boolean[] used;//用于判断放过哪些元素 27 | 28 | public List> permute1(int[] nums) { 29 | if (nums.length == 0){ 30 | return result; 31 | } 32 | used = new boolean[nums.length]; 33 | backtrack(nums); 34 | return result; 35 | } 36 | 37 | private void backtrack(int[] nums){ 38 | if (path.size() == nums.length){ 39 | result.add(new ArrayList<>(path)); 40 | return; 41 | } 42 | for (int i = 0; i < nums.length; i++){ 43 | if (used[i]){//保证结果里没有重复元素 44 | continue; 45 | } 46 | used[i] = true; 47 | path.add(nums[i]); 48 | backtrack(nums); 49 | path.removeLast(); 50 | used[i] = false; 51 | } 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/jiShuBeforeOushu_21.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | /** 4 | * @author renyujie518 5 | * @version 1.0.0 6 | * @ClassName jiShuBeforeOushu.java 7 | * @Description 8 | * 调整数组顺序使奇数位于偶数前面 9 | * 需要保证奇数和奇数,偶数和偶数之间的相对位置不变 10 | * @createTime 2021年08月19日 20:18:00 11 | */ 12 | public class jiShuBeforeOushu_21 { 13 | //使用冒泡思想,每次都当前偶数上浮到当前最右边 14 | public static void jiShuBeforeOushu1(int[] nums) { 15 | int N = nums.length; 16 | for (int i = N; i > 0; i--) { 17 | for (int j = 0; j < i; j++) { 18 | if (isOuShu(nums[j]) && !isOuShu(nums[j + 1])) { //当前是偶数,后一个是奇数,交换 19 | swap(nums, j, j + 1); 20 | } 21 | } 22 | } 23 | } 24 | 25 | private static boolean isOuShu(int x) { 26 | return x % 2 == 0; 27 | } 28 | 29 | private static void swap(int[] nums, int i, int j) { 30 | int temp = nums[i]; 31 | nums[i] = nums[j]; 32 | nums[j] = temp; 33 | } 34 | 35 | public static int[] jiShuBeforeOushu2(int[] nums) { 36 | int jishuCount = 0; 37 | for (int num : nums) { 38 | if (!isOuShu(num)) { 39 | jishuCount++; 40 | } 41 | } 42 | //创建一个新数组 43 | int[] copy = nums.clone(); 44 | int i = 0; 45 | int j = jishuCount; 46 | for (int newnum : copy) { 47 | if (!isOuShu(newnum)) { 48 | copy[i++] = newnum; 49 | } else { 50 | copy[j++] = newnum; 51 | } 52 | } 53 | return copy; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/facing/huiwenshu.java: -------------------------------------------------------------------------------- 1 | package facing; 2 | 3 | /** 4 | * @author admin 5 | * @version 1.0.0 6 | * @ClassName huiwenshu.java 7 | * @Description 8 | * 首先,我们应该处理一些临界情况。所有负数都不可能是回文,例如:-123 不是回文,因为 - 不等于 3。 9 | * 所以我们可以对所有负数返回 false。除了 0 以外,所有个位是 0 的数字不可能是回文,因为最高位不等于 0。所以我们可以对所有大于 0 且个位是 0 的数字返回 false。 10 | * 11 | * 现在,让我们来考虑如何反转后半部分的数字。 12 | 13 | * 对于数字 1221,如果执行 1221 % 10,我们将得到最后一位数字 1,要得到倒数第二位数字, 14 | * 我们可以先通过除以 10 把最后一位数字从 1221 中移除,1221 / 10 = 122,再求出上一步结果除以 10 的余数,122 % 10 = 2, 15 | * 就可以得到倒数第二位数字。如果我们把最后一位数字乘以 10,再加上倒数第二位数字,1 * 10 + 2 = 12, 16 | * 就得到了我们想要的反转后的数字。如果继续这个过程,我们将得到更多位数的反转数字。 17 | * 18 | * 现在的问题是,我们如何知道反转数字的位数已经达到原始数字位数的一半? 19 | * 20 | * 由于整个过程我们不断将原始数字除以 10,然后给反转后的数字乘上 10,所以, 21 | * 当原始数字小于或等于反转后的数字时,就意味着我们已经处理了一半位数的数字了。 22 | 23 | */ 24 | public class huiwenshu { 25 | public boolean isPalindrome(int x) { 26 | // 特殊情况: 27 | // 如上所述,当 x < 0 时,x 不是回文数。 28 | // 同样地,如果数字的最后一位是 0,为了使该数字为回文, 29 | // 则其第一位数字也应该是 0 30 | // 只有 0 满足这一属性 31 | if (x < 0 || (x % 10 == 0 && x != 0)) { 32 | return false; 33 | } 34 | 35 | int revertedNumber = 0; 36 | while (x > revertedNumber) { //当原始数字小于或等于反转后的数字时,就意味着我们已经处理了一半位数的数字了。 37 | revertedNumber = revertedNumber * 10 + x % 10; 38 | x /= 10; 39 | } 40 | 41 | // 当数字长度为奇数时,我们可以通过 revertedNumber/10 去除处于中位的数字。 42 | // 例如,当输入为 12321 时,在 while 循环的末尾我们可以得到 x = 12,revertedNumber = 123, 43 | // 由于处于中位的数字不影响回文(它总是与自己相等),所以我们可以简单地将其去除。 44 | return x == revertedNumber || x == revertedNumber / 10; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/facing/meituan/manyidu.java: -------------------------------------------------------------------------------- 1 | package facing.meituan; 2 | import java.util.HashSet; 3 | import java.util.Scanner; 4 | 5 | /** 6 | * @author renyujie518 7 | * @version 1.0.0 8 | * @ClassName Q1.java 9 | * @Description 10 | * 题目描述 11 | * 小团每天都会走过一条路去上课。这条路旁种有丁香树,从左向右排成一排并编号为1…n。又是一年一度的丁香季,所有丁香都开花了,第i棵丁香树的芳香值为ai,小团要从第一棵丁香树走到最后一棵。当走到第i棵丁香树时,如果这棵丁香树的芳香值比之前经过的i-1棵丁香树中棵的芳香值高,她的满意度就要加上那x棵丁香树的不同的芳香值个数。 12 | * 小团知道了这n程丁香树的芳香值,她想知道走过这n棵工香树后自己的满音度是多少。 13 | * 14 | * 输入描述 15 | * 第一行一个正整数n,为丁香树数目: 16 | * 第二行n个致ai第i个数代表第i棵丁香树的芳香度。对于10%的数据,n<=100; 17 | * 18 | * 输入描述 19 | * 16 20 | * 1122233331 21 | * 22 | * 输出描述 23 | * 一行一个整数,表示小团的满意度, 24 | * 提示 25 | * 当走到第34。5棵丁香树时,因为它们的芳香值为2,大于前两棵丁香树的芳香值1, 26 | * 因为只有一种芳香值,所以经过3、4、5 中的每棵树都会产生1的满意度,总满意度累计为 3; 27 | * 当走到第67,8,9棵丁香树时,因为它们的芳香值为3,大于前面两种芳香值12, 28 | * 所以经过6,7,8,9中的每棵树都会产生2的满意度,总满意度累计为 8; 29 | * 综上,一共会产生11点满意度。 30 | * 31 | * @createTime 2021年08月29日 10:12:00 32 | */ 33 | public class manyidu { 34 | public static void main(String[] args) { 35 | Scanner sc = new Scanner(System.in); 36 | int n = sc.nextInt(); 37 | int[] nums = new int[n]; 38 | for (int i = 0; i < n; i++) { 39 | nums[i] = sc.nextInt(); 40 | } 41 | //开始判断 42 | HashSet memo = new HashSet<>(); 43 | int res = 0; 44 | for (int i = 0; i < nums.length; i++) { 45 | memo.add(nums[i]); 46 | for (Integer i1 : memo) { 47 | if (i1> wall) { 29 | HashMap map = new HashMap<>(); 30 | for (List width : wall) { 31 | int n = width.size(); 32 | int zhuanGeShu = 0; 33 | for (int i = 0; i < n-1; i++) {//排除除了最右侧的砖块(防止把最左右两个竖线加进去) 34 | zhuanGeShu = zhuanGeShu + map.get(i); 35 | map.put(zhuanGeShu, map.getOrDefault(zhuanGeShu, 0) + 1); 36 | //key = 到左边界的距离和(相当于砖块的个数) value 距离每次累加1 37 | } 38 | } 39 | int maxLength = 0; 40 | for (Map.Entry sum : map.entrySet()) { 41 | maxLength = Math.max(maxLength, sum.getValue()); 42 | } 43 | return wall.size() - maxLength; 44 | 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/facing/singleNonDuplicate.java: -------------------------------------------------------------------------------- 1 | package facing; 2 | 3 | /** 4 | * @author renyujie518 5 | * @version 1.0.0 6 | * @ClassName singleNonDuplicate.java 7 | * @Description 给定一个只包含整数的有序数组,每个元素都会出现两次,唯有一个数只会出现一次,找出这个数。 8 | * 输入: nums = [1,1,2,3,3,4,4,8,8] 9 | * 输出: 2 10 | * @createTime 2021年09月14日 20:40:00 11 | */ 12 | public class singleNonDuplicate { 13 | 14 | //二分法 15 | public static int singleNonDuplicate1(int[] nums) { 16 | int left = 0; 17 | int right = nums.length - 1; 18 | while (left < right) {//注意 总数组个数始终是奇数,因为有一个元素出现一次,其余元素出现两次。 19 | int mid = left + (right - left) / 2; 20 | if (nums[mid] == nums[mid - 1]) {//中点跟左边的相等,则判断除开中点,左边还剩几位数 21 | if ((mid - left) % 2 == 0) {//若为偶数,则说明左边的存在答案值,改变right的值 22 | right = mid - 2; 23 | } else {//若为奇数,则说明右边的存在答案值,改变left的值 24 | left = mid + 1; 25 | } 26 | } else if (nums[mid] == nums[mid + 1]) {//中点跟右边的相等,则判断除开中点,右边还剩几位数; 27 | if ((right - mid) % 2 == 0) {//若为偶数,则说明右边的存在答案值,改变left的值 28 | left = mid + 2; 29 | } else {//若为奇数,则说明左边的存在答案值,改变right的值 30 | right = mid - 1; 31 | } 32 | } else {//中点跟左右都不相等,直接返回 33 | return nums[mid]; 34 | 35 | } 36 | } 37 | return nums[right]; 38 | } 39 | //按位于 40 | public int singleNonDuplicate2(int[] nums) { 41 | int result = 0; 42 | for(int i : nums) 43 | result ^= i; 44 | 45 | return result; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/recursion/reverseStack.java: -------------------------------------------------------------------------------- 1 | package recursion; 2 | 3 | import java.util.Stack; 4 | 5 | /** 6 | * @author admin 7 | * @version 1.0.0 8 | * @ClassName reverseStack.java 9 | * @Description 给你一个栈,请你逆序这个栈,不能申请额外的数据结构,只能使用递归函数。如何实现? 10 | * @createTime 2021年03月18日 21:51:00 11 | */ 12 | public class reverseStack { 13 | //移除栈底元素并返回,剩下的盖下来 14 | public static int getAndRemoveLastElement(Stack stack){ 15 | Integer result = stack.pop(); 16 | if (stack.isEmpty()){ 17 | return result; 18 | }else { 19 | int last = getAndRemoveLastElement(stack); 20 | stack.push(result); 21 | return last; 22 | } 23 | } 24 | 25 | public static void reverseStack(Stackstack){ 26 | if (stack.isEmpty()){ 27 | return; 28 | } 29 | int i = getAndRemoveLastElement(stack); 30 | reverseStack(stack); 31 | stack.push(i); 32 | } 33 | public static void main(String[] args) { 34 | Stack test = new Stack(); 35 | test.push(1); 36 | test.push(2); 37 | test.push(3); 38 | test.push(4); 39 | test.push(5); 40 | while (!test.isEmpty()) { 41 | System.out.println(test.pop()); 42 | } 43 | System.out.println("======"); 44 | test.push(1); 45 | test.push(2); 46 | test.push(3); 47 | test.push(4); 48 | test.push(5); 49 | reverseStack(test); 50 | while (!test.isEmpty()) { 51 | System.out.println(test.pop()); 52 | } 53 | 54 | } 55 | 56 | 57 | } 58 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/facing/meituan/xulie_03.java: -------------------------------------------------------------------------------- 1 | package facing.meituan; 2 | 3 | import java.util.Scanner; 4 | 5 | /** 6 | * @author renyujie518 7 | * @version 1.0.0 8 | * @ClassName xulie_03.java 9 | * @Description 10 | 11 | * 小美有一个长度为 n 的序列 A,A[i] 表示序列中第 i 个数(1<=i<=n)。 12 | * 她定义序列中第 i 个数的 prev[i] 值 为前 i-1 个数中比 A[i] 小的最大的值,即满足 1<=j res = new ArrayList<>(); 29 | 30 | while (i <= target / 2) { 31 | if (sum < target) { 32 | // 右边界向右移动 33 | sum += j; 34 | j++; 35 | } else if (sum > target) { 36 | // 左边界向右移动 37 | sum -= i; 38 | i++; 39 | } else { 40 | // 记录结果 41 | int[] arr = new int[j-i]; 42 | for (int k = i; k < j; k++) { 43 | arr[k-i] = k; 44 | } 45 | res.add(arr); 46 | // 左边界向右移动 47 | sum -= i; 48 | i++; 49 | } 50 | } 51 | 52 | return res.toArray(new int[res.size()][]); 53 | } 54 | 55 | 56 | 57 | } 58 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/Graph/TopoSort.java: -------------------------------------------------------------------------------- 1 | package Graph; 2 | 3 | import java.util.*; 4 | 5 | /** 6 | * @author admin 7 | * @version 1.0.0 8 | * @ClassName TopoSort.java 9 | * @Description 拓扑排序 (寻找依赖关系 有向图 类似编译过程中需要的依赖顺序) 10 | * 11 | * 思路 : 先找如度为0的点,然后去除所有其所有联系 依次类推 12 | * @createTime 2021年03月14日 16:18:00 13 | */ 14 | public class TopoSort { 15 | public static List TopoSort(Graph graph){ 16 | //首先创建一个hashmap用于记录这个图的状况 key = node value = 该node剩余的入度 17 | HashMap inMap = new HashMap(); 18 | //在建立一个队列,刚在思路里说明了,入度为0的点才能进入这个队列 19 | Queue ZeroInqueue = new LinkedList(); 20 | //先找到这个图里入度为0的点当做起始 21 | for (Node node : graph.nodes.values()) { //Collection values() 这个values()是工具类里直接获取值的函数 22 | inMap.put(node, node.in); //循环所有node的时候把map构建好,主要是每个node对应的入度 23 | //构建完后找到入度为0的点当做起始 24 | if (node.in == 0){ 25 | ZeroInqueue.add(node); 26 | } 27 | } 28 | //下面开始排序,最终结果放入result 29 | ArrayList result = new ArrayList(); 30 | while (!ZeroInqueue.isEmpty()){ 31 | Node curr = ZeroInqueue.poll(); 32 | result.add(curr); //每弹出就加入result里 33 | for (Node next : curr.nexts) { 34 | //这里十分重要 怎么去除所有其所有联系 即 该node的下一个节点next的剩余的入度-1 后再放回这个map,相当于一种跟新 35 | inMap.put(next, inMap.get(next.in) - 1); 36 | //一旦发信有某个节点的(这里所说的是next是0)放入那个队列 37 | if (inMap.get(next) == 0){ 38 | ZeroInqueue.add(next); 39 | } 40 | } 41 | } 42 | return result; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/isHouXuInTree_33.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | /** 4 | * @author renyujie518 5 | * @version 1.0.0 6 | * @ClassName isHouXuInTree.java 7 | * @Description 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历的结果。假设输入的数组的任意两个数字都互不相同。 8 | * 注意 这棵树还是二叉搜索书 9 | * 二叉搜索树定义: 左子树中所有节点的值 < 根节点的值;右子树中所有节点的值 > 根节点的值;其左、右子树也分别为二叉搜索树。 10 | * 11 | * https://leetcode-cn.com/problems/er-cha-sou-suo-shu-de-hou-xu-bian-li-xu-lie-lcof/solution/di-gui-he-zhan-liang-chong-fang-shi-jie-jue-zui-ha/ 12 | * 13 | * 后续 左右中 14 | * @createTime 2021年08月21日 17:22:00 15 | */ 16 | public class isHouXuInTree_33 { 17 | public static boolean VerifySquenceOfHouXu(int[] quence) { 18 | if (quence == null || quence.length == 0) { 19 | return true; 20 | } 21 | return verify(quence, 0, quence.length - 1); 22 | } 23 | 24 | 25 | public static boolean verify(int[] quence, int first, int last) { 26 | 27 | if (last - first <= 1) { 28 | return true;// 当前区域不合法的时候直接返回true就好 29 | } 30 | //后序的最后一个节点是root 31 | int rootVal = quence[last]; 32 | int currIndex = first; 33 | while (currIndex < last && quence[currIndex] < rootVal) {//找到左树的终结位置(左树已验证完毕) 34 | currIndex++; 35 | } 36 | //注意这里currIndex在超出条件后由于++的缘故已经在左树的终结位置+1了,所以是右树的第一个位置 37 | for (int i = currIndex; i <= last - 1; i++) { 38 | if (quence[i] < rootVal) {//右树一定比rootVal大,一旦出现小的,说明不是 39 | return false; 40 | } 41 | } 42 | //目前currIndex还指向右树第一个位置 43 | //左子树也要二叉搜索书 右子树也要二叉搜索书 44 | return verify(quence, first, currIndex-1) && verify(quence, currIndex , last - 1); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/facing/xiaogengdui_sort.java: -------------------------------------------------------------------------------- 1 | package facing; 2 | 3 | import java.util.Comparator; 4 | import java.util.PriorityQueue; 5 | 6 | /** 7 | * @author admin 8 | * @version 1.0.0 9 | * @ClassName xiaogengdui_sort.java 10 | * @Description 11 | * 已知一个几乎有序的数组,几乎有序是指,如果把数组排好顺序的话,每个元素移动的距离可以不超过k, 12 | * 并且k相对于数组来说比较小。请选择一个合适的排序算法针对这个数据进行排序 13 | * 14 | * 15 | * 直接用小根堆,在一取一放的过程中,不断顶替,取出的就是有序的数 16 | * 17 | * 再接着把比较器尝试放进去 18 | * @createTime 2021年02月14日 16:41:00 19 | */ 20 | public class xiaogengdui_sort { 21 | public static void main(String[] args) { 22 | int[] arrs = {1, 3, 2, 6, 4, 2, 6, 3, 2}; 23 | int[] xx = sortedArrDistancelessK(arrs, 5); 24 | for (int x : xx) { 25 | System.out.println(x); 26 | } 27 | } 28 | 29 | public static int[] sortedArrDistancelessK(int[] arrs,int k) { 30 | //PriorityQueue heap = new PriorityQueue<>();//直接默认生成小根堆 31 | PriorityQueue heap = new PriorityQueue<>(new AComp()); 32 | int index = 0; 33 | for (;index<=Math.min(arrs.length,k);index++){ //防止给的k过大 34 | heap.add(arrs[index]); 35 | } 36 | int i =0; 37 | for (;index { 49 | @Override 50 | public int compare(Integer o1, Integer o2) { 51 | // return o1-o2; 52 | return o2-o1; 53 | } 54 | } 55 | 56 | 57 | } 58 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/facing/combineAndpermute/dianHua.java: -------------------------------------------------------------------------------- 1 | package facing.combineAndpermute; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * @author renyujie518 8 | * @version 1.0.0 9 | * @ClassName dianHua.java 10 | * @Description 电话按键的组合 11 | * 输入:"23" 输出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"]. 12 | * https://leetcode-cn.com/problems/letter-combinations-of-a-phone-number/ 13 | * 14 | * 组合问题 回溯 15 | * @createTime 2021年09月17日 12:52:00 16 | */ 17 | public class dianHua { 18 | 19 | List list = new ArrayList(); 20 | StringBuilder temp = new StringBuilder(); 21 | 22 | public List dianHua(String digits) { 23 | if (digits == null || digits.length() == 0) { 24 | return null; 25 | } 26 | //初始对应所有的数字,为了直接对应2-9,新增了两个无效的字符串""对应0,1 27 | String[] numString = {"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"}; 28 | //回溯 29 | backtrace(digits, numString, 0); 30 | return list; 31 | } 32 | 33 | public void backtrace(String digits, String[] numString, int num) { 34 | //遍历全部一次记录一次得到的字符串 35 | if (num == digits.length()) { 36 | list.add(temp.toString()); 37 | return; 38 | } 39 | //str 表示当前输入的按键数字num对应的字符串 比如2对应'abc' 40 | String str = numString[digits.charAt(num) - '0']; 41 | //下面就是组合问题 42 | for (int i = 0; i < str.length(); i++) { 43 | temp.append(str.charAt(i)); 44 | //递归 45 | backtrace(digits, numString, num + 1); 46 | //移除末尾继续尝试 47 | temp.deleteCharAt(temp.length() - 1); 48 | } 49 | } 50 | 51 | 52 | } 53 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/DP/numOfSearchTree.java: -------------------------------------------------------------------------------- 1 | package DP; 2 | 3 | /** 4 | * @author renyujie518 5 | * @version 1.0.0 6 | * @ClassName numOfSearchTree.java 7 | * @Description 给定一个整数 n,求以 1 ... n 为节点组成的二叉搜索树(左小右大)有多少种? 8 | * 9 | * 具体的分析步骤建议去看: 10 | * https://programmercarl.com/0096.%E4%B8%8D%E5%90%8C%E7%9A%84%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91.html#%E6%80%9D%E8%B7%AF 11 | * 这里需要背诵下结论: 12 | * dp[i] : 1到i为节点组成的二叉搜索树的个数为dp[i]。 13 | * dp[3],就是 元素1为头结点搜索树的数量 + 元素2为头结点搜索树的数量 + 元素3为头结点搜索树的数量 14 | * 15 | * 元素1为头结点搜索树的数量 = 右子树有2个元素的搜索树数量 * 左子树有0个元素的搜索树数量 16 | * 元素2为头结点搜索树的数量 = 右子树有1个元素的搜索树数量 * 左子树有1个元素的搜索树数量 17 | * 元素3为头结点搜索树的数量 = 右子树有0个元素的搜索树数量 * 左子树有2个元素的搜索树数量 18 | * 19 | * 有2个元素的搜索树数量就是dp[2]。 20 | * 有1个元素的搜索树数量就是dp[1]。 21 | * 有0个元素的搜索树数量就是dp[0]。 22 | * 所以dp[3] = dp[2] * dp[0] + dp[1] * dp[1] + dp[0] * dp[2] 23 | * 24 | * 递推公式: 25 | * dp[i] += dp[以j为头结点左子树节点数量] * dp[以j为头结点右子树节点数量] 26 | * j相当于是头结点的元素,从1遍历到i为止。 27 | * 所以递推公式:dp[i] += dp[j - 1] * dp[i - j]; (时刻注意【】里的含义,是个数) 28 | * j-1 为j为头结点左子树节点数量,i-j 为以j为头结点右子树节点数量 29 | * (j-1)+(i-j) = i-1 代表出去头结点所有的个数,分配给左右子树,先给左树分j个和1个头节点(所以-1),剩余i - j个分给右树 30 | * @createTime 2021年09月02日 16:54:00 31 | */ 32 | public class numOfSearchTree { 33 | public int numOfSearchTree(int n) { 34 | //初始化 dp 数组 35 | int[] dp = new int[n + 1]; 36 | //初始化0个节点和1个节点的情况 37 | dp[0] = 1; 38 | dp[1] = 1; 39 | for (int i = 2; i <= n; i++) { 40 | for (int j = 1; j <= i; j++) { 41 | //对于第i个节点,需要考虑1作为根节点直到i作为根节点的情况,所以需要累加 42 | //一共i个节点,对于根节点j时,左子树的节点个数为j-1,右子树的节点个数为i-j 43 | dp[i] += dp[j - 1] * dp[i - j]; 44 | } 45 | } 46 | return dp[n]; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/timesInSortArray_53.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | /** 4 | * @author renyujie518 5 | * @version 1.0.0 6 | * @ClassName timesInSortArray_53.java 7 | * @Description 数字在排序数组中出现的次数 8 | * Input: 9 | * nums = 1, 2, 3, 3, 3, 3, 4, 6 10 | * K = 3 11 | * 12 | * Output: 13 | * 4 14 | * 15 | * 16 | * 二分法: 17 | * 排序数组 nums中的所有数字 target形成一个窗口,记窗口的 左 / 右边界 索引分别为 left 和 right , 18 | * 分别对应窗口左边 / 右边的首个元素。 19 | * 本题要求统计数字 target 的出现次数,可转化为:使用二分法分别找到 左边界 left 和 右边界 right 20 | * 易得数字 target 的数量为right−left−1 。 21 | * @createTime 2021年08月27日 15:50:00 22 | */ 23 | public class timesInSortArray_53 { 24 | public int timesInSortArray(int[] nums, int target) { 25 | //搜索右边界 26 | int i = 0; 27 | int j = nums.length - 1; 28 | while (i <= j) { 29 | int mid = i + (j - i) / 2; 30 | if (nums[mid] <= target) {//target在右半侧 31 | i = mid + 1; 32 | } else { 33 | j = mid - 1; 34 | } 35 | } 36 | //这时候i = j ,而且i满足的条件带<=,由于是增加趋势,会一直满足到最右边,所以即使有多个target也是在偏最右侧的 37 | int right = i; 38 | // 若数组中无 target ,则提前返回 39 | if (j >= 0 && nums[j] != target) { 40 | return 0; 41 | } 42 | //搜索左边界 43 | i = 0; 44 | j = nums.length - 1; 45 | while (i <= j) { 46 | int mid = i + (j - i) / 2; 47 | if (nums[mid] < target) { 48 | i = mid + 1; 49 | } else { 50 | j = mid - 1; 51 | 52 | } 53 | } 54 | //这时候i = j ,而且j满足的条件带>=,由于是j是减小趋势,会一直满足到最左边,所以即使有多个target也是在偏最左侧的 55 | int left = j; 56 | return right - left + 1; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/DP/lujinInMatrix/lujinInMatrix2.java: -------------------------------------------------------------------------------- 1 | package DP.lujinInMatrix; 2 | 3 | /** 4 | * @author renyujie518 5 | * @version 1.0.0 6 | * @ClassName lujinInMatrix2.java 7 | * @Description 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。 8 | * 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。 9 | * 现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径? 10 | * 网格中的障碍物和空位置分别用 1 和 0 来表示。 11 | * 12 | * dp[i][j] :表示从(0 ,0)出发,到(i, j) 有dp[i][j]条不同的路径。 13 | * 有障碍的话,其实就是标记对应的dp = 0 14 | * 15 | * 初始化的时候注意一点: 16 | * 没有障碍的时候,因为从(0, 0)的位置到(i, 0)的路径只有一条,所以都初始化为1 17 | * 但以第一列为例子,(i, 0) 这条边有了障碍之后,障碍之后(包括障碍)都是走不到的位置了,所以障碍之后的dp[i][0]应该还是初始值0。 18 | * @createTime 2021年09月02日 16:16:00 19 | */ 20 | public class lujinInMatrix2 { 21 | public static int uniquePathsWithObstacles(int[][] matrix) { 22 | int n = matrix.length; 23 | int m = matrix[0].length; 24 | int[][] dp = new int[n][m]; 25 | dp[0][0] = 1 - matrix[0][0]; 26 | for (int i = 1; i < m; i++) {//初始化第0行,当前没有障碍物,而且从左边能走过来(即左边被初始化过) 27 | if (matrix[0][i] == 0 && dp[0][i - 1] == 1) { 28 | dp[0][i] = 1; 29 | } 30 | } 31 | for (int i = 1; i < n; i++) {//初始化第0列,当前没有障碍物,而且从上边能走过来(即上边被初始化过) 32 | if (matrix[i][0] == 0 && dp[i-1][0] == 1) { 33 | dp[i][0] = 1; 34 | 35 | } 36 | } 37 | //经过上述的初始化,只有能走到的第一行和第一列是1,其余由于new的性质是0 38 | for (int i = 1; i < n; i++) { 39 | for (int j = 1; j < m; j++) { 40 | if (matrix[i][j] == 1) { 41 | continue; 42 | } 43 | dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; 44 | } 45 | } 46 | return dp[n - 1][m - 1]; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/MiddleClass/printNumberNoInArray.java: -------------------------------------------------------------------------------- 1 | package MiddleClass; 2 | 3 | /** 4 | * @author renyujie518 5 | * @version 1.0.0 6 | * @ClassName printNumberNoInArray.java 7 | * @Description 8 | * 给定一个整数数组A, 长度为n, 有 1 <= A[i] <= n, 且对于[1,n]的整数, 其中部分整数会重复出现而部分不会出现。 9 | * 实现算法找到[1,n]中所有未出现在A中的整数。 10 | * 提示: 尝试实现O(n)的时间复杂度和O(1)的空间复杂度 (返回值不计入空间复 杂度) 。 11 | * 输入描述: 一行数字, 全部为整数, 空格分隔 A0 A1 A2 A3. . . 输出描述: 一行数字, 全部为整数, 空格分隔R0 R1 R2 R3. . . 12 | * 示例1: 13 | * 输入 1 3 4 3 14 | * 输出 2 15 | * 最容易想到的是hashmap 但要求O(1)的空间复杂度即用有限几个变量 16 | * 思路: 17 | * 争取做到i位置上的值是i+1(也可以说i位置的值是i-1) 没做到的位置就知道缺了的数字 18 | * 注意: 19 | * 这里题目中说道可能会有重复值,那么重复值会不会太影响呢 20 | * 不会,因为一个萝卜一个坑,只要这个位置有相应的数保证占住了,重复的值并不影响 21 | * 关注的只是没做到的位置 22 | * @createTime 2021年08月12日 18:20:00 23 | */ 24 | public class printNumberNoInArray { 25 | public static void printNumberNoInArray(int[] arr) { 26 | //已知arr[0~N-1]上的数字都在【1~n】之间 27 | if (arr == null || arr.length == 0) { 28 | return; 29 | } 30 | for (int value : arr) { 31 | process(value, arr); 32 | } 33 | //没做到的位置就知道缺了的数字 34 | for (int i = 0; i < arr.length; i++) { 35 | if (arr[i] != i + 1) { 36 | System.out.println(i + 1); 37 | } 38 | } 39 | } 40 | 41 | //实际上是交换,把值为i的数放到i-1位置上 42 | public static void process(int value, int[] arr) { 43 | while (arr[value - 1] != value) { 44 | int tmp = arr[value - 1]; 45 | arr[value - 1] = value; 46 | value = tmp; 47 | } 48 | } 49 | public static void main(String[] args) { 50 | int[] test = { 3, 2, 3, 5, 6, 1, 6 }; 51 | printNumberNoInArray(test); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/zhiPrintTree_32.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | import Tree.TreeNode; 4 | 5 | import java.util.*; 6 | 7 | /** 8 | * @author renyujie518 9 | * @version 1.0.0 10 | * @ClassName zhiPrintTree_32.java 11 | * @Description 按之字形顺序打印二叉树 12 | * 请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印, 13 | * 其他行以此类推。 14 | * @createTime 2021年08月21日 16:37:00 15 | */ 16 | public class zhiPrintTree_32 { 17 | public static List> zhiPrintTree(TreeNode root) { 18 | List> res = new ArrayList<>(); 19 | if (root == null){ 20 | return res; 21 | } 22 | int level = 0; 23 | LinkedList queue = new LinkedList<>(); 24 | queue.offer(root); 25 | //add()和offer()都是向队列中添加一个元素。一些队列有大小限制, 26 | //因此如果想在一个满的队列中加入一个新项,调用 add() 方法就会抛出一个 unchecked 异常, 27 | //而调用 offer() 方法会返回 false。因此就可以在程序中进行有效的判断! 28 | while (!queue.isEmpty()){ 29 | level++; 30 | List list = new ArrayList<>(); //这个list储存的是每一层的元素 31 | int size = queue.size(); 32 | for (int i = 0; i < size; i++) { 33 | root = queue.poll(); 34 | list.add(root.val); 35 | if (root.left != null){ 36 | queue.offer(root.left); 37 | } 38 | if (root.right != null){ 39 | queue.offer(root.right); 40 | } 41 | } 42 | //偶数层就利用Collections反转一下 43 | if ((level&1)==0){ //level是先++后来到这里的,所以不应该是2 而是1 44 | Collections.reverse(list); 45 | } 46 | res.add(list); 47 | } 48 | return res; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/print1ToMaxOfNDigits_17.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | /** 4 | * @author renyujie518 5 | * @version 1.0.0 6 | * @ClassName print1ToMaxOfNDigits_17.java 7 | * @Description 打印从 1 到最大的 n 位数 8 | * 输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。 9 | * 比如输入 3,则打印出 1、2、3 一直到最大的 3 位数即 999。 10 | 11 | * 解题思路 12 | * 由于 n 可能会非常大,因此不能直接用 int 表示数字,而是用 char 数组进行存储。 13 | * 使用回溯法得到所有的数。 14 | * @createTime 2021年08月18日 16:51:00 15 | */ 16 | public class print1ToMaxOfNDigits_17 { 17 | public static void print1ToMaxOfNDigitsWithDFS(int n) { 18 | if (n < 0) { 19 | return; 20 | } 21 | char[] number = new char[n]; 22 | dfs(number, 0); 23 | } 24 | 25 | //number代表几位数,表示每次的数字 digit代表在个位还是十位... 相当于index 26 | public static void dfs(char[] number, int digit) { 27 | //结束条件 28 | if (digit == number.length) { //实际上是digit == n 29 | printNumber(number); 30 | return; //跳出函数 31 | } 32 | for (int i = 0; i < 10; i++) { 33 | //每一位都是字符'0'~'9' 34 | number[digit] = (char) (i + '0'); // '0'的 ASCII 编码 48 这里相当于把一个整形数int转化为字符char类型 35 | dfs(number, digit + 1); 36 | 37 | } 38 | } 39 | 40 | //给定是几位数,打印从1到10^n -1 41 | public static void printNumber(char[] number) { 42 | int index = 0;//统计出几位数 43 | while (index < number.length && number[index] == '0') { 44 | index++; 45 | } 46 | //打印 47 | while (index < number.length) { 48 | System.out.println(number[index++]); 49 | 50 | } 51 | System.out.println(); 52 | } 53 | 54 | public static void main(String[] args) { 55 | print1ToMaxOfNDigitsWithDFS(3); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/facing/orderArrayFinNum.java: -------------------------------------------------------------------------------- 1 | package facing; 2 | 3 | /** 4 | * @author renyujie518 5 | * @version 1.0.0 6 | * @ClassName orderArrayFinNum.java 7 | * @Description 8 | * 在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。 9 | * 请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数 10 | * 两种思路 11 | * 一种是: 12 | * 把每一行看成有序递增的数组, 13 | * 利用二分查找, 14 | * 通过遍历每一行得到答案, 15 | * 时间复杂度是nlogn 16 | * 17 | * 18 | * 另外一种思路是: 19 | * 利用二维数组由上到下,由左到右递增的规律, 20 | * 那么选取右上角或者左下角的元素a[row][col]与target进行比较, 21 | * 当target小于元素a[row][col]时,那么target必定在元素a所在行的左边, 22 | * 即col--; 23 | * 当target大于元素a[row][col]时,那么target必定在元素a所在列的下边, 24 | * 即row++; 25 | * @createTime 2021年08月20日 14:21:00 26 | */ 27 | public class orderArrayFinNum { 28 | 29 | public boolean Find1(int [][] array,int target) { 30 | 31 | for(int i=0;iarray[i][mid]) 37 | low=mid+1; 38 | else if(target=0){ 52 | if(target==array[row][col]) 53 | return true; 54 | else if(target>array[row][col]) 55 | row++; 56 | else 57 | col--; 58 | } 59 | return false; 60 | 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/merge2SortList_25.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | import LinkedList.ListNode; 4 | 5 | /** 6 | * @author renyujie518 7 | * @version 1.0.0 8 | * @ClassName merge2SortList.java 9 | * @Description 合并两个排序的链表 10 | * 思路: 11 | * 链表递增 的,因此容易想到使用双指针遍历两链表,根据val 的大小关系确定节点添加顺序,两节点指针交替前进,直至遍历完毕。 12 | * 13 | * 引入伪头节点: 由于初始状态合并链表中无节点,因此循环第一轮时无法将节点添加到合并链表中。 14 | * 解决方案:初始化一个辅助节点 dum 作为合并链表的伪头节点,将各节点添加至 dum 之后 15 | * @createTime 2021年08月19日 21:23:00 16 | */ 17 | public class merge2SortList_25 { 18 | public static ListNode mergeTwoLists(ListNode l1, ListNode l2) { 19 | if (l1 == null) { 20 | return l2; 21 | } 22 | if (l2 == null) { 23 | return l1; 24 | } 25 | ListNode dum = new ListNode(0); 26 | ListNode curr = dum; 27 | while (l1 != null && l2 != null) { 28 | if (l1.val < l2.val) { 29 | curr.next = l1; 30 | l1 = l1.next; 31 | } else { 32 | curr.next = l2; 33 | l2 = l2.next; 34 | } 35 | curr = curr.next; 36 | } 37 | //走到这里需要判定下谁长,谁先到,把另外的全放进去 38 | curr.next = l1 == null ? l2 : l1; 39 | return dum.next; 40 | } 41 | 42 | public static ListNode mergeTwoListsWithRecursion(ListNode l1, ListNode l2) { 43 | if (l1 == null) { 44 | return l2; 45 | } 46 | if (l2 == null) { 47 | return l1; 48 | } 49 | if (l1.val <= l2.val) { 50 | l1.next = mergeTwoListsWithRecursion(l1.next, l2); 51 | return l1; 52 | } else { 53 | l2.next = mergeTwoListsWithRecursion(l1, l2.next); 54 | return l2; 55 | } 56 | 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/MiddleClass/useOneStackToSort.java: -------------------------------------------------------------------------------- 1 | package MiddleClass; 2 | 3 | import java.util.Stack; 4 | 5 | /** 6 | * @author admin 7 | * @version 1.0.0 8 | * @ClassName useOneStackToSort.java 9 | * @Description 对一个栈里的整型数据, 按升序进行排序(即排序前, 栈里 的数据是无序的, 排序后最大元素位于栈顶) , 10 | * 要求最多只能使用一个额外的 栈存放临时数据, 但不得将元素复制到别的数据结构中 11 | * 12 | * 这题的关键是只能使用一个额外的stack,这是个临时栈,用于倒转,还有一个本身的原有栈 13 | * 大致思路是临时栈中由栈底从大到小排列(降序),遇到不符合规则的(从原始栈提取的大于临时栈顶元素),将临时栈中不符合规则的,返回原有栈,最后在倒序 14 | * @createTime 2021年07月22日 10:10:00 15 | */ 16 | public class useOneStackToSort { 17 | public static void sortStackByStack(Stack stack) { 18 | Stack temp = new Stack<>(); 19 | while (!stack.isEmpty()) { 20 | //stack.peek返回的是一个值,相当于获取一个数, 21 | //但是stack.pop会在这个stack里面删除一个元素。 22 | Integer curr = stack.pop(); 23 | while (!temp.isEmpty() && curr > temp.peek()) { 24 | stack.push(temp.pop()); 25 | } 26 | //否则的话就把原始栈顶的元素插到临时栈 27 | temp.push(curr); 28 | } 29 | //最后,舍弃临时栈,把临时栈的倒转回原始栈,就是"对一个栈里的整型数据,按升序进行排序" 30 | while (!temp.isEmpty()){ 31 | stack.push(temp.pop()); 32 | } 33 | } 34 | 35 | public static void main(String[] args) { 36 | Stack test = new Stack<>(); 37 | test.push(3); 38 | test.push(1); 39 | test.push(6); 40 | test.push(2); 41 | test.push(5); 42 | test.push(4); 43 | sortStackByStack(test); 44 | System.out.println(test.pop()); 45 | System.out.println(test.pop()); 46 | System.out.println(test.pop()); 47 | System.out.println(test.pop()); 48 | System.out.println(test.pop()); 49 | System.out.println(test.pop()); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/ziDianXu_38.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.HashSet; 6 | import java.util.Set; 7 | 8 | /** 9 | * @author renyujie518 10 | * @version 1.0.0 11 | * @ClassName ziDianXu_38.java 12 | * @Description 13 | * 输入一个字符串,按字典序打印出该字符串中字符的所有排列 14 | * 。例如输入字符串 abc,则打印出由字符 a, b, c 所能排列出来的所有字符串 abc, acb, bac, bca, cab 和 cba。 15 | * *保证在填每一个空位的时候重复字符只会被填入一次 16 | * 我们首先对原字符串排序,保证相同的字符都相邻, 17 | * 在递归函数中,我们限制每次填入的字符一定是这个字符所在重复字符集合中 从左往右第一个未被填入的字符 18 | * @createTime 2021年08月21日 22:35:00 19 | */ 20 | public class ziDianXu_38 { 21 | private static ArrayList result = new ArrayList<>(); 22 | public static ArrayList ziDianXu(String str) { 23 | if (str.length() == 0) { 24 | return result; 25 | } 26 | char[] chars = str.toCharArray(); 27 | Arrays.sort(chars); 28 | tracrback(chars, new boolean[chars.length], new StringBuilder()); 29 | return result; 30 | } 31 | 32 | public static void tracrback(char[] chars, boolean[] hasUsed, StringBuilder s) { 33 | if (s.length() == chars.length) { 34 | result.add(s.toString()); 35 | return; 36 | } 37 | for (int i = 0; i < chars.length; i++) { 38 | if (hasUsed[i]) { 39 | continue; 40 | } 41 | if (i != 0 && chars[i] == chars[i - 1] && !hasUsed[i - 1]) {//保证不重复(从左往右第一个未被填入的字符) 42 | continue; 43 | } 44 | hasUsed[i] = true; 45 | s.append(chars[i]); 46 | //回溯 47 | tracrback(chars, hasUsed, s); 48 | s.deleteCharAt(s.length() - 1); 49 | hasUsed[i] = false; 50 | } 51 | 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/facing/compareVersion.java: -------------------------------------------------------------------------------- 1 | package facing; 2 | 3 | /** 4 | * @author renyujie518 5 | * @version 1.0.0 6 | * @ClassName compareVersion.java 7 | * @Description 8 | * 给你两个版本号 version1 和 version2 ,请你比较它们。 9 | * 版本号由一个或多个修订号组成,各修订号由一个 '.' 连接。 10 | * 每个修订号由 多位数字 组成,可能包含 前导零 。 11 | * 每个版本号至少包含一个字符。修订号从左到右编号,下标从 0 开始,最左边的修订号下标为 0 ,下一个修订号下标为 1 ,以此类推。 12 | * 例如,2.5.33 和 0.1 都是有效的版本号。 13 | *比较版本号时,请按从左到右的顺序依次比较它们的修订号。比较修订号时,只需比较 忽略任何前导零后的整数值 。 14 | * 也就是说,修订号 1 和修订号 001 相等 。如果版本号没有指定某个下标处的修订号,则该修订号视为 0 。 15 | * 例如,版本 1.0 小于版本 1.1 ,因为它们下标为 0 的修订号相同,而下标为 1 的修订号分别为 0 和 1 ,0 < 1 。 16 | * 17 | * 返回规则如下: 18 | * 19 | * 如果version1>version2返回1 20 | * 如果version1 v2){ 49 | return 1; 50 | }else if(v2>v1){ 51 | return -1; 52 | } 53 | } 54 | p1++; 55 | p2++; 56 | } 57 | return 0; 58 | 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/Tree/IsComplateTree.java: -------------------------------------------------------------------------------- 1 | package Tree; 2 | 3 | 4 | import java.util.LinkedList; 5 | import java.util.Queue; 6 | 7 | /** 8 | * @author admin 9 | * @version 1.0.0 10 | * @ClassName ComplateTree.java 11 | * @Description complete binary tree 完全二叉树: 12 | * 从根往下数,除了最下层外都是全满(都有两个子节点),而最下层所有叶结点都向左边靠拢填满。 13 | * 按照宽度遍历 14 | * 1. 按层遍历二叉树,如果当前节点有右孩子而没有左孩子,则一定不是二叉树;(左边有个洞) 15 | * 2.在1的条件下 如果当前遇到的第一个节点左右孩子不齐全,后续的都得是叶节点 16 | * @createTime 2021年03月06日 13:13:00 17 | */ 18 | public class IsComplateTree { 19 | 20 | public static boolean IsCBT(TreeNode root){ 21 | if (root == null){ 22 | return true; 23 | } 24 | //宽度优先用队列 25 | Queue queue = new LinkedList<>(); 26 | boolean key = false; //是否遇到不双全的节点 27 | TreeNode L = null; 28 | TreeNode R = null; 29 | queue.add(root); 30 | while (!queue.isEmpty()){ 31 | root = queue.poll(); 32 | L = root.left; 33 | R = root.right; 34 | // ps 判断叶节点 L==null && R==null 35 | if ( 36 | (key && (L != null || R != null))//当前遇到的第一个节点左右孩子不齐全。且孩子们都不是叶节点(当且节点居然有孩子) 37 | || 38 | (R != null && L == null) //条件1 有右无左直接返回false 39 | ){ 40 | return false; 41 | } 42 | if (L!=null){ //先左后右 43 | queue.add(L); 44 | } 45 | if (R!= null){ 46 | queue.add(R); 47 | } 48 | //遇到不双全的情况要改key值 49 | if (L == null || R == null){ 50 | key = true; //key只有从flase变成true,没有形成闭环,一旦改变,就是思路里提到的遇到第一个不全的节点 51 | } 52 | 53 | 54 | } 55 | //这些if在遍历的过程中都没碰到,就返回true 56 | return true; 57 | 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/MiddleClass/isContainsIn2Matrix.java: -------------------------------------------------------------------------------- 1 | package MiddleClass; 2 | 3 | /** 4 | * @author admin 5 | * @version 1.0.0 6 | * @ClassName isContainsiin2Matrix.java 7 | * @Description 给定一个元素为非负整数的二维数组matrix, 8 | *每行和每列都是从小到大有序的。 再给定一个非负整数aim, 请判断aim是否在matrix中。 9 | * 10 | * 思路类似于猜价格的游戏,高了,低了这种方式,这样最多走(n+m)次,n,m代表二维矩阵的行数和列数 11 | * 可以预料到 这个矩阵右下角最大 左上角最小 12 | * 从右上角开始 如果K比curr小,curr这条列下的都可以不要(每列从小到大排序)可以往左走一步 13 | * 如果K比curr大,curr这条行左边的都可以不要(每行从小到大排列)可以向下走一步 14 | * 直到边境还没找到就是没有 15 | * 16 | * @createTime 2021年07月22日 17:30:00 17 | */ 18 | public class isContainsIn2Matrix { 19 | public static boolean isContainsIn2Matrix(int[][] matrix, int K) { 20 | //从右上角 往左下角走 21 | int row = 0; 22 | int col = matrix[0].length - 1; 23 | while (row < matrix.length && col > -1) {//边界范围内 24 | if (matrix[row][col] == K) { 25 | return true; 26 | } else if (matrix[row][col] > K) {//宗旨的方向是往左下角走 27 | col--; 28 | } else { 29 | row++; 30 | } 31 | } 32 | return false; 33 | } 34 | 35 | public static void main(String[] args) { 36 | int[][] matrix = new int[][] { { 0, 1, 2, 3, 4, 5, 6 },// 0 37 | { 10, 12, 13, 15, 16, 17, 18 },// 1 38 | { 23, 24, 25, 26, 27, 28, 29 },// 2 39 | { 44, 45, 46, 47, 48, 49, 50 },// 3 40 | { 65, 66, 67, 68, 69, 70, 71 },// 4 41 | { 96, 97, 98, 99, 100, 111, 122 },// 5 42 | { 166, 176, 186, 187, 190, 195, 200 },// 6 43 | { 233, 243, 321, 341, 356, 370, 380 } // 7 44 | }; 45 | //int K = 233; 46 | int K = 22; 47 | System.out.println(isContainsIn2Matrix(matrix, K)); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/MiddleClass/eatGrass.java: -------------------------------------------------------------------------------- 1 | package MiddleClass; 2 | 3 | /** 4 | * @author admin 5 | * @version 1.0.0 6 | * @ClassName eatGrass.java 7 | * @Description 牛牛和羊羊都很喜欢青草。今天他们决定玩青草游戏。最初有一个装有n份青草的箱子,牛牛和羊羊依次进行,牛牛先开始。 8 | * 在每个回合中,每个玩家必须吃一些箱子中的青草,所吃的青草份数必须是4的x次幂,比如1,4,16,64等等。 9 | * 不能在箱子中吃到有效份数青草的玩家落败。假定牛牛和羊羊都是按照最佳方法进行游戏,请输出胜利者的名字 10 | * @createTime 2021年03月30日 13:56:00 11 | */ 12 | public class eatGrass { 13 | //n份青草放在一堆 14 | //先手和后手的决定都是聪明的 String返回"先手""后手" 15 | public static String winner1(int n){ 16 | //首先 basecase很好判断 谁先遇到0草,对手赢 17 | // 0 1 2 3 4 18 | // 后 先 后 先 先 19 | if (n<5){ 20 | return (n == 0 || n == 2) ? "后手" : "先手"; 21 | } 22 | //n>5时 23 | int base = 1; //先手决定吃的草 24 | while (base<=n){ 25 | //当前一共n份草,先手吃掉base份,n-base留给后手 26 | //母过程里 先手,子过程里 后手 27 | if (winner1(n-base).equals("后手")){//当前是先手,先手先吃base份 ,递归,代表我要用子过程去判定谁赢 28 | // 如果子过程里显示是后手赢(equals)但这是子过程的后手。就代表母过程先手赢(子过程的后手和母过程的先手一个人) 29 | return "先手"; 30 | } 31 | if (base>n / 4){ 32 | break;//防止base*4后溢出,因为base不断*4,可能超过整数最大边界,发生不可预知的错误,所以在bade*4前判断,*4之后不要大于n 33 | } 34 | base *= 4;//如果在上面那个if没有的进入,那你就尝试吃4份,16份..去尝试赢 35 | } 36 | return "后手";//试了所有的分支,都不能return那只能是后手赢了 37 | } 38 | 39 | //观察winner1得出的结果 40 | public static void Winner2(int n) { 41 | if (n % 5 == 0 || n % 5 == 2) { 42 | System.out.println("yang"); 43 | } else { 44 | System.out.println("niu"); 45 | } 46 | } 47 | 48 | public static void main(String[] args) { 49 | for (int i = 0; i < 50; i++) { 50 | System.out.println(i + ":" + winner1(50)); 51 | } 52 | } 53 | 54 | 55 | } 56 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/facing/meituan/quKongGe_2.java: -------------------------------------------------------------------------------- 1 | package facing.meituan; 2 | 3 | import java.util.Scanner; 4 | 5 | /** 6 | * @author renyujie518 7 | * @version 1.0.0 8 | * @ClassName quKongGe_2.java 9 | * @Description 10 | * 小美得到了一个奇怪的键盘,上面一共有 53 个按键,包括 26 个小写字母、26 个大写字母和空格。这个键盘的奇怪之处如下: 11 | * 当小美按下一个按键时,该按键可能会被多次触发,即输出一连串按下按键所对应的字符。 12 | * 键盘会时不时地自动按下空格键。 13 | * 在使用这个键盘来进行输入时,小美保证了相邻两次按下的按键一定不同以及不主动按下空格键, 14 | * 现在给你小美使用这个键盘输入一个字符串后得到的结果,请你还原小美原本想要输入的这个字符串。 15 | * 输入描述 16 | * 一行,一个包含小写字母、大写字母和空格的字符串,表示小美输入后得到的结果。 17 | * 输出描述 18 | * 输出一行,表示小美原本想要输入的字符串。 19 | * 20 | * 样例输入 21 | * a iC C C GmyyyySp p(注意,a之前还有空格呢!!!!!所以trim() ) 22 | * 样例输出 23 | * aiCGmySp 24 | * @createTime 2021年08月28日 23:19:00 25 | */ 26 | public class quKongGe_2 { 27 | //实际上就是去空格和连续重 28 | public static void main (String[] args) { 29 | Scanner sc = new Scanner(System.in); 30 | String s = sc.nextLine(); 31 | //trim() 方法用于删除字符串的头尾空白符。 32 | String b = s.trim(); 33 | StringBuilder sb = new StringBuilder(b); 34 | for(int i = 1; i < sb.length(); i++){ 35 | if(sb.charAt(i) == ' '){ 36 | sb.deleteCharAt(i); 37 | //用于抵消下一步会执行的i++ 38 | i--; 39 | } 40 | } 41 | //连续重复只保留第一次出现 42 | for (int i = 1; i < sb.length(); i++) { 43 | if(sb.charAt(i) == sb.charAt(i-1)){ 44 | //replace(int start,int end,String str):子字符串从指定的索引开头开始,并扩展到索引结尾– 1处的字符 45 | //相当于只将i-1位置的替换为了空格 46 | sb.replace(i-1, i, " "); 47 | } 48 | } 49 | //由于上面替换了空格,这时候再执行遍删除空格的操作 50 | for(int i = 1; i < sb.length(); i++){ 51 | if(sb.charAt(i) == ' '){ 52 | sb.deleteCharAt(i); 53 | i--; 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/BigData/getMax.java: -------------------------------------------------------------------------------- 1 | package BigData; 2 | 3 | /** 4 | * @author admin 5 | * @version 1.0.0 6 | * @ClassName getMax.java 7 | * @Description 8 | * 给定两个有符号32位整数a和b,返回a和b中较大的 9 | * 【要求】不用做任何比较判断。 10 | * 技巧 位运算 11 | * @createTime 2021年03月26日 21:40:00 12 | */ 13 | public class getMax { 14 | public static int flip(int n) { //保证输入的参数不是1就是0的情况下 ^1 1变0 0变1 15 | return n ^ 1; 16 | } 17 | 18 | //n>=0 返回1 n是负数 返回0 19 | public static int sign(int n) { 20 | return flip((n >> 31) & 1);//一个整数向右移动32位,其符号位就移动最右,对于整形 非负数的符号位是0,负数的符号位是1 21 | //再与1& 非负数仍是0 负数为1 再经过filp 非负数1 负数0 22 | } 23 | 24 | //这个函数是有问题的 因为a - b有可能溢出 25 | public static int getMax1(int a, int b) { 26 | int c = a - b; 27 | int scA = sign(c); //a-b 为负 scA为0 28 | int scB = flip(scA);//a-b 为负 scB为1 29 | return a * scA + b * scB;//互斥条件相加实现类似if-else的作用 scA为0的时候scB必为1,反之同理 30 | } 31 | 32 | public static int getMax2(int a, int b) { 33 | int c = a - b; 34 | int sa = sign(a); 35 | int sb = sign(b); 36 | int sc = sign(c); 37 | int difSab = sa ^ sb; //a和b的符号一样为0 不一样为1 38 | int sameSab = flip(difSab); //a和b的符号一样为1 不一样为0 39 | int returnA = difSab * sa + sameSab * sc;//两加号互斥 要a是max条件: 在ab不同时且a>=0返回a 或者 在ab同号时且两差值>=0(sc =1,不溢出)返回a 40 | int returnB = flip(returnA);//取互斥 41 | return a * returnA + b * returnB; 42 | } 43 | 44 | public static void main(String[] args) { 45 | int a = -16; 46 | int b = 1; 47 | System.out.println(getMax1(a, b)); 48 | System.out.println(getMax2(a, b)); 49 | a = 2147483647; 50 | b = -2147480000; 51 | System.out.println(getMax1(a, b)); // wrong answer because of overflow 52 | System.out.println(getMax2(a, b)); 53 | 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/facing/MyCAS.java: -------------------------------------------------------------------------------- 1 | package facing; 2 | 3 | /** 4 | * @author admin 5 | * @version 1.0.0 6 | * @ClassName MyCAS.java 7 | * @Description 手写CAS 8 | * CAS是英文单词CompareAndSwap的缩写,中文意思是:比较并替换。C 9 | * AS需要有3个操作数:内存地址V,旧的预期值A,即将要更新的目标值B。 10 | * 11 | * CAS指令执行时,当且仅当内存地址V的值与预期值A相等时,将内存地址V的值修改为B,否则就什么都不做。整个比较并替换的操作是一个原子操作。 12 | * CAS是乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值, 13 | * 而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。 14 | 15 | * @createTime 2021年03月15日 14:56:00 16 | */ 17 | public class MyCAS { 18 | private int value; //内存值 19 | public static int count = 0; 20 | 21 | public synchronized int get(){ 22 | return this.value; 23 | } 24 | 25 | //比较交换值 26 | public synchronized int CompareAndSwap(int excepted,int newvalue){ 27 | int oldValue = excepted; 28 | //如果预期值等于内存值,则可以修改 29 | if(excepted == value){ 30 | value = newvalue; 31 | } 32 | //返回旧值 33 | return oldValue; 34 | } 35 | //判断CAS机制 36 | public synchronized boolean CompareAndSet(int excepted,int newvalue){ 37 | return excepted == CompareAndSwap(excepted, newvalue); 38 | } 39 | 40 | public static void main(String[] args) { 41 | MyCAS cas = new MyCAS(); 42 | for(int i=0;i<100;i++){ 43 | new Thread(new Runnable() { 44 | @Override 45 | public void run() { 46 | boolean flag = false; 47 | while (!flag){ 48 | //如果没有成功 进行自旋 知道设置成功为止 49 | int excepeted = cas.get(); 50 | flag = cas.CompareAndSet(excepeted, (int)(Math.random())); 51 | System.out.println(flag); 52 | } 53 | } 54 | }).start(); 55 | } 56 | 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/facing/combineAndpermute/permute2.java: -------------------------------------------------------------------------------- 1 | package facing.combineAndpermute; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | 7 | /** 8 | * @author renyujie518 9 | * @version 1.0.0 10 | * @ClassName permute2.java 11 | * @Description 给定一个可包含重复数字的序列 nums ,按任意顺序 返回所有不重复的全排列。 12 | * 输入:nums = [1,1,2] 13 | * 输出: 14 | * [[1,1,2], 15 | * [1,2,1], 16 | * [2,1,1]] 17 | * @createTime 2021年09月01日 22:54:00 18 | */ 19 | public class permute2 { 20 | //存放结果 21 | List> result = new ArrayList<>(); 22 | //暂存结果 23 | List path = new ArrayList<>(); 24 | 25 | public List> permuteUnique(int[] nums) { 26 | boolean[] used = new boolean[nums.length]; 27 | Arrays.fill(used, false); 28 | //排序用于去重 29 | Arrays.sort(nums); 30 | backTrack(nums, used); 31 | return result; 32 | } 33 | 34 | private void backTrack(int[] nums, boolean[] used) { 35 | if (path.size() == nums.length) { 36 | result.add(new ArrayList<>(path)); 37 | return; 38 | } 39 | for (int i = 0; i < nums.length; i++) { 40 | // used[i - 1] == true,说明同⼀树⽀nums[i - 1]使⽤过 41 | // used[i - 1] == false,说明同⼀树层nums[i - 1]使⽤过 42 | // 如果同⼀树层nums[i - 1]使⽤过则直接跳过 43 | if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == true) { 44 | continue; 45 | } 46 | //如果同⼀树⽀nums[i]没使⽤过开始处理 47 | if (used[i] == false) { 48 | used[i] = true;//标记同⼀树⽀nums[i]使⽤过,防止同一树支重复使用 49 | path.add(nums[i]); 50 | backTrack(nums, used); 51 | path.remove(path.size() - 1);//回溯,说明同⼀树层nums[i]使⽤过,防止下一树层重复 52 | used[i] = false;//回溯 53 | } 54 | } 55 | } 56 | 57 | 58 | } 59 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/countDigitOne_43.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | /** 4 | * @author renyujie518 5 | * @version 1.0.0 6 | * @ClassName countDigitOne_43.java 7 | * @Description 1~n 整数中 1 出现的次数 8 | * 输入一个整数 n ,求1~n这n个整数的十进制表示中1出现的次数。 9 | * 10 | * 例如,输入12,1~12这些整数中包含1 的数字有1、10、11和12,1一共出现了5次。 11 | * 12 | * 思路: 13 | * f(n))函数的意思是1~n这n个整数的十进制表示中1出现的次数,将n拆分为两部分, 14 | * 最高一位的数字high和其他位的数字last,分别判断情况后将结果相加,看例子更加简单。 15 | * 例子如n=1234,high=1, pow=1000, last=234 16 | * 可以将数字范围分成两部分1~999和1000~1234 17 | * 18 | * 1~999这个范围1的个数是f(pow-1) 19 | * 1000~1234这个范围1的个数需要分为两部分: 20 | * 千分位是1的个数:千分位为1的个数刚好就是234+1(last+1),注意,这儿只看千分位,不看其他位 21 | * 其他位是1的个数:即是234中出现1的个数,为f(last) 22 | * 所以全部加起来是f(pow-1) + last + 1 + f(last); 23 | * 24 | * 例子如3234,high=3, pow=1000, last=234 25 | * 可以将数字范围分成两部分1~999,1000~1999,2000~2999和3000~3234 26 | * 1~999这个范围1的个数是f(pow-1) 27 | * 1000~1999这个范围1的个数需要分为两部分: 28 | * 千分位是1的个数:千分位为1的个数刚好就是pow,注意,这儿只看千分位,不看其他位 29 | * 其他位是1的个数:即是999中出现1的个数,为f(pow-1) 30 | * 2000~2999这个范围1的个数是f(pow-1) 31 | * 3000~3234这个范围1的个数是f(last) 32 | * 所以全部加起来是pow + high*f(pow-1) + f(last); 33 | * 34 | https://leetcode-cn.com/problems/1nzheng-shu-zhong-1chu-xian-de-ci-shu-lcof/solution/javadi-gui-by-xujunyi/ 35 | * @createTime 2021年08月24日 15:46:00 36 | */ 37 | public class countDigitOne_43 { 38 | public static int countDigitOne(int n) { 39 | return f(n); 40 | } 41 | 42 | public static int f(int n) { 43 | if (n < 0) { 44 | return 0; 45 | } 46 | String s = String.valueOf(n); 47 | int high = s.charAt(0) - '0'; 48 | int pow = (int) Math.pow(10, s.length() - 1); 49 | int last = n - high * pow; 50 | if (high == 1) { 51 | return f(pow - 1) + last + 1 + f(last); 52 | } else { 53 | //pow比如2000-2999 1就出现了1000次 54 | return pow + high * f(pow - 1) + f(last); 55 | } 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/Test/test1.java: -------------------------------------------------------------------------------- 1 | package Test; 2 | 3 | import java.util.PriorityQueue; 4 | 5 | /** 6 | * @author admin 7 | * @version 1.0.0 8 | * @ClassName test1.java 9 | * @Description TODO 10 | * @createTime 2021年03月12日 15:41:00 11 | */ 12 | public class test1 { 13 | class Solution { 14 | public int maxSumDivThree(int[] nums) { 15 | PriorityQueue list1 = new PriorityQueue<>(); 16 | PriorityQueue list2 = new PriorityQueue<>(); 17 | int total = 0; 18 | for (int i = 0; i < nums.length; i++) { 19 | int result = nums[i] % 3; 20 | if(result==1)list1.offer(nums[i]); 21 | if(result==2)list2.offer(nums[i]); 22 | total += nums[i]; 23 | } 24 | int result = total % 3; 25 | while(result!=0){ 26 | if(result==1){ 27 | if(list1.isEmpty() && list2.size()<2){ 28 | return 0; 29 | } 30 | int total1 = minus(list1,total); 31 | int total2 = minus(list2,minus(list2,total)); 32 | total = Math.max(total1,total2); 33 | } 34 | if(result==2){ 35 | if(list2.isEmpty() && list1.size()<2){ 36 | return 0; 37 | } 38 | int total1 = minus(list1,minus(list1,total)); 39 | int total2 = minus(list2,total); 40 | total = Math.max(total1,total2); 41 | } 42 | result = total % 3; 43 | } 44 | return total; 45 | } 46 | private int minus(PriorityQueue list,int total){ 47 | if(list.isEmpty())return -1; 48 | total = total - list.poll(); 49 | return total; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/facing/CharMaxCommon.java: -------------------------------------------------------------------------------- 1 | package facing; 2 | 3 | /** 4 | * @author admin 5 | * @version 1.0.0 6 | * @ClassName CharMaxCommon.java 7 | * @Description 8 | * 问题: 9 | * 有两个字符串str和str2,求出两个字符串中最长公共子串长度。 10 | * 11 | * 比如:str=acbcbcef,str2=abcbced,则str和str2的最长公共子串为bcbce,最长公共子串长度为5。 12 | * 13 | * 算法思路: 14 | * 1、把两个字符串分别以行和列组成一个二维矩阵。 15 | * 2、比较二维矩阵中每个点对应行列字符中否相等,相等的话值设置为1,否则设置为0。 16 | * 3、通过查找出值为1的最长对角线就能找到最长公共子串。 17 | * 了进一步优化算法的效率,我们可以再计算某个二维矩阵的值的时候顺便计算出来当前最长的公共子串的长度, 18 | * 即某个二维矩阵元素的值由dp[i][j]=1演变为dp[i][j]=1 +dp[i-1][j-1],这样就避免了后续查找对角线长度的操作了。 19 | * 20 | * @createTime 2021年03月17日 18:27:00 21 | */ 22 | 23 | 24 | public class CharMaxCommon { 25 | public static void main(String[] args) { 26 | CharMaxCommon lcs = new CharMaxCommon(); 27 | String str = lcs.CharMaxCommon("acbcbcef","abcbced"); 28 | System.out.println(str); 29 | } 30 | 31 | private String CharMaxCommon(String A, String B) { 32 | int[][] dp = new int[B.length()][A.length()]; 33 | int end_index = 0; 34 | int maxLength = 0; 35 | for(int i = 0; i < A.length(); i++) { 36 | for(int j = 0; j < B.length(); j++) { 37 | if(B.charAt(j) == A.charAt(i)) { 38 | if(i == 0 || j == 0) { 39 | dp[i][j] = 1; 40 | } else { 41 | dp[i][j] = dp[i - 1][j - 1] + 1; 42 | } 43 | 44 | if(dp[i][j] > maxLength) { 45 | maxLength = dp[i][j]; 46 | end_index = j; 47 | } 48 | } 49 | } 50 | } 51 | System.out.println("maxLength = " + maxLength); 52 | System.out.println("end_index = " + end_index); 53 | end_index += 1; 54 | return B.substring(end_index - maxLength,end_index); 55 | } 56 | } 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/MiddleClass/beibaoBianzhong.java: -------------------------------------------------------------------------------- 1 | package MiddleClass; 2 | 3 | /** 4 | * @author renyujie518 5 | * @version 1.0.0 6 | * @ClassName beibaoBianzhong.java 7 | * @Description 8 | * 牛牛准备参加学校组织的春游, 出发前牛牛准备往背包里装入一些零食, 牛牛的背包容 量为w。 9 | * 牛牛家里一共有n袋零食, 第i袋零食体积为v[i]。 牛牛想知道在总体积不超过背包容量的情况下, 他一共有多少种零食放法(总体积为0也 算一种放法)。 10 | * 11 | * 背包问题的变种 12 | * dp解决 dp[i][j]代表的含义:arr[0...i]上的数每个只用一次 (也可以不用),累加和正好为j 13 | * 就两种可能 不用i位置的数 0...i-1的数搞定j 用i位置的数 0...i-1的数搞定j -arr[i] 14 | *这样这个二维表的最后一行就是 0..i这么多数凑出来j=0,j=1,j=2..有多少种方法 题目是不超过容量 所以累加即可 15 | * @createTime 2021年08月08日 13:31:00 16 | */ 17 | public class beibaoBianzhong { 18 | public static int ways(int[] arr, int w) { 19 | if (arr == null || arr.length == 0 || w < 0) { 20 | return 0; 21 | } 22 | // dp[i][j]代表的含义:arr[0...i]上的数每个只用一次 (也可以不用),累加和正好为j 23 | int[][] dp = new int[arr.length][w + 1]; 24 | for (int i = 0; i < arr.length; i++) { 25 | dp[i][0] = 1; //相当与背包容量为0 不管有多少零食,题目中说:(背包总体积为0也 算一种放法)。 26 | } 27 | //初始化完第一列再初始化第一行(第一行代表的是容量在变,零食只有一个arr[0]) 28 | for (int j = 1; j <= w; j++) { 29 | //有可放入和不放入两种,判断的关键点是包的容量是否>=零食,如果大于,对于不同的包来说两种选择,如果不是,只能不放 30 | dp[0][j] = j >= arr[0] ? 2 : 1; 31 | } 32 | //填其他的值 33 | for (int i = 1; i < arr.length; i++) { 34 | for (int j = 1; j < w; j++) { 35 | //是第i个不放入时,前i-1个零食放入背包的状态等于前i-1个,但是w的,即此时的j值是不变的 36 | dp[i][j] = dp[i - 1][j]; 37 | if (j - arr[i] >= 0) {//因为是贪心,所以假设的前提是能放尽量放 38 | //是在第i个放入的情况下,更新状态,依赖于i-1数量的零食体积与w-V[i]情况下的值 39 | //同时注意是累加 所以有dp[i][j] + 40 | dp[i][j] = dp[i][j] + dp[i - 1][j - arr[i]]; 41 | } 42 | } 43 | } 44 | 45 | //看分析,不超过背包容量的情况下一共,在上面加过了,所以返回在w的限定下最右下角 46 | return dp[arr.length - 1][w]; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/sort/MergeSort.java: -------------------------------------------------------------------------------- 1 | package sort; 2 | 3 | import util.bijiaoqi_arrays_sort; 4 | 5 | /** 6 | * @author admin 7 | * @version 1.0.0 8 | * @ClassName MergeSort.java 9 | * @Description 归并排序 左边有序 右边有序,再让整体有序 10 | * @createTime 2021年02月09日 14:37:00 11 | */ 12 | public class MergeSort { 13 | public static void main(String[] args) { 14 | int[] arrs = {3,4,5,123,45,13,435,765,12,1,2,3,345,56,78,9,3}; 15 | int[] mergesort = mergesort(arrs); 16 | for (int arr : mergesort ) { 17 | System.out.println(arr); 18 | } 19 | 20 | //test 21 | bijiaoqi_arrays_sort.test("sort.MergeSort"); 22 | } 23 | 24 | public static void process(int[] arrs, int L, int R){ 25 | if (L==R){ 26 | return; 27 | } 28 | int mid = L+((R-L)>>1); 29 | process(arrs, L, mid); //左排 30 | process(arrs, mid+1, R); //右排 31 | merge(arrs, L, mid, R); //合起来牌 32 | } 33 | 34 | public static void merge(int[] arrs,int L,int mid,int R){ 35 | int[] temp = new int[R-L+1];//创建一个临时和原数组大小一样的临时数组 36 | int i= 0; 37 | int p1 = L; 38 | int p2 = mid+1; 39 | while (p1<=mid && p2<=R){ 40 | temp[i++] = arrs[p1]<= arrs[p2] ? arrs[p1++] :arrs[p2++]; //赋值完再移动指针 41 | } 42 | while (p1<=mid){ 43 | temp[i++] = arrs[p1++];//p2越界,触发这个while,把左半部分都放到临时 44 | } 45 | while (p2<=R){ 46 | temp[i++] = arrs[p2++];//p1越界,触发这个while,把右半部分都放到临时 47 | } 48 | for (int j = 0; j < temp.length; j++) { 49 | arrs[L + j] = temp[j];//排序完再把临时的还给arrs 50 | } 51 | } 52 | 53 | public static int[] mergesort(int[] arrs){ 54 | if (arrs == null || arrs.length <2){ 55 | return arrs; 56 | } 57 | process(arrs,0, arrs.length-1); 58 | return arrs; 59 | } 60 | } 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/facing/chiTang.java: -------------------------------------------------------------------------------- 1 | package facing; 2 | 3 | import java.util.Scanner; 4 | 5 | /** 6 | * @author renyujie518 7 | * @version 1.0.0 8 | * @ClassName chiTang.java 9 | * @Description 10 | * 小Q的父母要出差N天,走之前给小Q留下了M块巧克力。 11 | * 小Q决定每天吃的巧克力数量不少于前一天吃的一半,但是他又不想在父母回来之前的某一天没有巧克力吃,请问他第一天最多能吃多少块巧克力。 12 | * 13 | * 例:input:出差 3 天,7块奶糖 14 | *   output:4 15 | * 即:第一天吃4块糖,第二天吃2块糖,第三天吃1块糖。 16 | * 这里用的是折半查找(二分查找)的思想,先找最中间的。 17 | * 二分查找: 18 | * 首先赋低位min为0,高位max为所有巧克力数n, 19 | * while(min> result = new ArrayList<>(); 31 | LinkedList path = new LinkedList<>(); 32 | 33 | //一个范围内取数,不重复,结果也不重复 34 | public List> combine(int n, int k) { 35 | backtrace(n, k, 1); 36 | return result; 37 | } 38 | // startIndex 用来记录本层递归的中,集合从哪里开始遍历 39 | private void backtrace(int n, int k, int startIndex){ 40 | if (path.size() == k) { 41 | result.add(new ArrayList<>(path)); 42 | return; 43 | } 44 | /** 45 | * 剪枝优化过程如下: 46 | * 已经选择的元素个数:path.size(); 47 | * 还需要的元素个数为: k - path.size(); 48 | * 在集合n中至少要从该起始位置 : n - (k - path.size()) + 1,开始遍历 49 | * 为什么有个+1呢,因为包括起始位置,我们要是一个左闭的集合。 50 | * 51 | * 举个例子,n = 4,k = 3, 目前已经选取的元素为0(path.size为0),n - (k - 0) + 1 即 4 - ( 3 - 0) + 1 = 2。 52 | * 从2开始搜索都是合理的,可以是组合[2, 3, 4]。 53 | */ 54 | for (int i = startIndex; i <= n - (k - path.size()) + 1; i++) { 55 | path.add(i); 56 | backtrace(n, k, i + 1); 57 | path.removeLast(); 58 | } 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/facing/ReverseNum.java: -------------------------------------------------------------------------------- 1 | package facing; 2 | 3 | /** 4 | * @author admin 5 | * @version 1.0.0 6 | * @ClassName ReverseArry.java 7 | * @Description 反转一个数 8 | * 示例 1: 9 | * 10 | * 输入:x = 123 11 | * 输出:321 12 | * 示例 2: 13 | * 14 | * 输入:x = -123 15 | * 输出:-321 16 | * 17 | * 要在没有辅助堆栈 / 数组的帮助下 “弹出” 和 “推入” 数字,我们可以使用数学方法。 18 | * 19 | * 20 | * //pop operation: 21 | * pop = x % 10; 22 | * x /= 10; 23 | * 24 | * //push operation: 25 | * temp = rev * 10 + pop; 26 | * rev = temp; 27 | * 28 | * 但是,这种方法很危险,因为当 \text{temp} = \text{rev} \cdot 10 + \text{pop}temp=rev⋅10+pop 时会导致溢出。 29 | 30 | * @createTime 2021年03月07日 19:33:00 31 | */ 32 | public class ReverseNum { 33 | public int reverse1(int x) { 34 | long temp = 0; 35 | 36 | while(x != 0){ 37 | int pop = x % 10; 38 | temp = temp * 10 + pop; 39 | 40 | if(temp > Integer.MAX_VALUE || temp < Integer.MIN_VALUE){ 41 | return 0; 42 | } 43 | x /= 10; 44 | } 45 | return (int)temp; 46 | } 47 | 48 | public int reverse2(int x) { 49 | if(x == Integer.MIN_VALUE) return 0;//因为该数的绝对值越界了,而且其翻转的结果超过了int范围,这里直接处理 50 | boolean mark = true;//该标记位用来记录x是正数还是负数 51 | if(x<0) {//如果x是负数,则改变标记位,同时将负数变成正数 52 | mark = false; 53 | x = Math.abs(x); 54 | } 55 | String str = Integer.toString(x);//正数变成字符串String 56 | StringBuffer stringBuffer = new StringBuffer(str);//字符串String变成StringBuffer 57 | str = stringBuffer.reverse().toString();//StringBuffer翻转在变回字符串String 58 | long result = Long.parseLong(str);//字符串变成长整型,这里为什么用长整型来接收,是因为有很多数已经越界了 59 | if(mark == false) {//如果标记位为负数,则转回负数 60 | result = 0 - result; 61 | } 62 | if(result>Integer.MAX_VALUE || result0i>0 且 j=0j=0 时,dp[i][0]=dp[i-1][0]+grid[i][0]dp[i][0] 25 | * 26 | * 当 i=0i=0 且 j>0j>0 时,dp[0][j]=dp[0][j-1]+grid[0][j]dp[0][j] 27 | * 28 | * 当 i>0i>0 且 j>0j>0 时,dp[i][j]=min(dp[i-1][j],dp[i][j-1])+grid[i][j] 29 | * 30 | * 最后得到dp[m-1][n-1]dp[m−1][n−1] 的值即为从网格左上角到网格右下角的最小路径和。 31 | 32 | * @createTime 2021年03月07日 20:46:00 33 | */ 34 | public class wanggeMinPath { 35 | public int minGridPathSum(int[][] grid){ 36 | if (grid == null || grid.length == 0 || grid[0].length == 0){ 37 | return 0; 38 | } 39 | int rows = grid.length; 40 | int column = grid[0].length; 41 | int[][] dp = new int[rows][column]; 42 | dp[0][0] = grid[0][0];//左上角开始 43 | for (int i = 1; i < rows; i++) { 44 | dp[i][0] = dp[i-1][0] + grid[i][0]; 45 | } 46 | for (int j = 1; j countMap = new HashMap<>(); 39 | // 记录重复子树的根节点 40 | static LinkedList res = new LinkedList<>(); 41 | 42 | 43 | //使用深度优先搜索,其中递归函数返回当前子树的序列化结果。 44 | //把每个节点开始的子树序列化结果保存在 map 中,然后判断是否存在重复的子树 45 | public static List findDuplicateSubtrees(TreeNode root) { 46 | collect(root); 47 | return res; 48 | } 49 | 50 | public static String collect(TreeNode node) { 51 | if (node == null) { 52 | return "#"; 53 | } 54 | //序列化格式的拼接(里面包含着中序的递归) 55 | String serial = node.val + "," + collect(node.left) + "," + collect(node.right); 56 | //getOrDefault() 方法获取指定 key 对应对 value,如果找不到 key ,则返回设置的默认值。这里默认返回0 57 | //这里相当于累加 每次遇到先+1,再把之前的次数加上去 58 | countMap.put(serial, 1 + countMap.getOrDefault(serial, 0)); 59 | if (countMap.get(serial) == 2){ 60 | //重复 即次数为2 61 | res.add(node); 62 | } 63 | return serial; 64 | } 65 | 66 | 67 | } 68 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/recursion/num2String.java: -------------------------------------------------------------------------------- 1 | package recursion; 2 | 3 | /** 4 | * @author admin 5 | * @version 1.0.0 6 | * @ClassName num2String.java 7 | * @Description 8 | * 规定1和A对应、2和B对应、3和C对应...那么一个数字字符串比如"111",就可以转化为"AAA"、"KA"和"AK"。 9 | * 给定一个只有数字字符组成的字符串str,返回有多少种转化结果。 10 | 思路: 11 | 首先 1和A对应 那字母最多不超过26 【3,9】一定对应的【C,I】 12 | 若当前位置是1 有可能是A 也有可能是十几 13 | 若当前位置是2.要看超过26没 14 | 若当前位置是0,之前做的决定不对,因为没有0开头的,只可能是10,20 15 | * @createTime 2021年03月18日 23:37:00 16 | */ 17 | public class num2String { 18 | //在字符串的i位置做决定 i之前的位置,如何转化已经做过决定了。考虑的是i..有多少种可能 19 | public static int process(char[] str,int i){ 20 | if (i == str.length){ 21 | //如果i到了最后的位置,返回的就是1种决定 就是之前做好的决定 22 | return 1; 23 | } 24 | if (str[i]== 0){//若当前位置是0,之前做的决定虽然有效,但到了i让我没法继续,在之前做的决定下再看整体有效0种 25 | return 0; 26 | } 27 | if (str[i]=='1'){ 28 | //为1是非常通用的情况,这时候我一定可以做一个决定是 i自己作为单独的部分,再考虑后续有多少种方法 29 | int res = process(str, i + 1); 30 | if (i+1='0')){ 40 | res += process(str, i + 2); 41 | } 42 | return res; 43 | } 44 | //字符范围是3-9的时候,只能做对应的【C,I】的决定 45 | return process(str, i + 1); 46 | } 47 | 48 | public static int num2String(String str) { 49 | if (str == null || str.length() == 0) { 50 | return 0; 51 | } 52 | return process(str.toCharArray(), 0); 53 | } 54 | 55 | public static void main(String[] args) { 56 | System.out.println(num2String("111")); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/recursion/printAllSubsquence.java: -------------------------------------------------------------------------------- 1 | package recursion; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** 7 | * @author admin 8 | * @version 1.0.0 9 | * @ClassName printAllSubsquence.java 10 | * @Description 打印最长子序列 11 | * 比如给定abc 打印 abc ab,ac,a,bc ,b,c,null 12 | * 思路 每个字符都选择要还是不要,和二叉树类似,再接着,每个子树又要选择要还是不要 13 | * @createTime 2021年03月18日 14:07:00 14 | */ 15 | public class printAllSubsquence { 16 | public static void printAllSubsquence(String str){ 17 | char[] chs = str.toCharArray(); 18 | process(chs, 0,new ArrayList()); 19 | 20 | } 21 | //来到i的位置,要还是不要两条路 ,res储存的是之前的选择所形成的的列表 22 | private static void process(char[] chs, int i, List res) { 23 | //basecase 来到终止的位置,即遍历完所有的字符串的时候,打印出来结果即可 24 | if (i ==chs.length){ 25 | printList(res); 26 | return; 27 | } 28 | List resKeep = copyList(res);//把之前的结果拷贝一份 29 | resKeep.add(chs[i]); //加入当前字符 30 | process(chs,i+1,resKeep); //要当前字符的路 31 | List resNoinclude = copyList(res);////把之前的结果拷贝一份(注意,这里包括了要当前字符的路的结果) 32 | process(chs,i+1,resNoinclude);//不要当前的路 33 | 34 | } 35 | //工具函数,将之前存放的结果的list再复制一份放到一个新的list里 36 | private static List copyList(List oldList) { 37 | if (oldList == null){ 38 | return new ArrayList(); 39 | } 40 | ArrayList newList = new ArrayList<>(); 41 | for (Character ch : oldList) { 42 | newList.add(ch); 43 | } 44 | return newList; 45 | } 46 | //工具函数,打印list里所有的字符串 47 | private static void printList(List res) { 48 | for (Character ch : res) { 49 | System.out.print(ch); 50 | } 51 | System.out.println(); 52 | } 53 | 54 | public static void main(String[] args) { 55 | String test = "abc"; 56 | printAllSubsquence(test); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/facing/combineAndpermute/combinaSumAsTarget2.java: -------------------------------------------------------------------------------- 1 | package facing.combineAndpermute; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.LinkedList; 6 | import java.util.List; 7 | 8 | /** 9 | * @author renyujie518 10 | * @version 1.0.0 11 | * @ClassName combinaSumAsTarget2.java 12 | * @Description 13 | * 给定一个无重复元素的正整数数组candidates和一个正整数target, 14 | * 找出candidates中所有可以使数字和为目标数target的唯一组合。 15 | * candidates中的数字可以无限制重复被选取。如果至少一个所选数字数量不同,则两种组合是唯一的。 16 | * 题目中给出了数组中元素的范围是 [1,200](数组中的数字可以无限制重复被取,这里注意如果有 0 会导致死循环) 17 | * 18 | * 输入: candidates = [2,3,5], target = 8 19 | * 输出: [[2,2,2,2],[2,3,3],[3,5]] 20 | * 21 | * 组合总和也是组合类题目中的常见题目,经典的回溯算法 22 | * @createTime 2021年09月01日 16:41:00 23 | */ 24 | public class combinaSumAsTarget2 { 25 | List> res = new ArrayList<>(); // 用于存储结果 26 | LinkedList track = new LinkedList<>(); // 存储每次遍历的正确结果 27 | 28 | //无重复元素的数组中组合出不重复的的解,但是元素可以重复使用 29 | public List> combinaSumAsTarget2(int[] candidates, int target) { 30 | Arrays.sort(candidates); // 先进行排序 31 | backtrack(0, target, 0, candidates); 32 | return res; 33 | } 34 | public void backtrack(int curSum, int target, int start, int[] candidates) { 35 | //base case 36 | if (curSum == target) { 37 | res.add(new ArrayList<>(track)); 38 | return; 39 | } 40 | // 如果 curSum + candidates[i] > target 就终止遍历(剪枝) 41 | for (int i = start; i < candidates.length && curSum + candidates[i] <= target; i++) { 42 | //做选择 43 | curSum += candidates[i]; 44 | track.add(candidates[i]); 45 | // 递归 注意 这里是与combinaSumAsTarget1最大的不同: 可以重复,所以从 i 开始遍历子树,而不是必须r+1, 46 | //也不用去重了,本身题目就是无重复的candidates,排序后直接往后用就行 47 | backtrack(curSum, target, i, candidates); 48 | // 回溯,撤销选择 49 | curSum -= candidates[i]; 50 | track.removeLast(); 51 | } 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/facing/xiaYiGePaiLie.java: -------------------------------------------------------------------------------- 1 | package facing; 2 | 3 | /** 4 | * @author renyujie518 5 | * @version 1.0.0 6 | * @ClassName xiaYiGePaiLie.java 7 | * @Description 8 | * “下一个排列”的定义是:给定数字序列的字典序中下一个更大的排列。 9 | * 如果不存在下一个更大的排列,则将数字重新排列成最小的排列(即升序排列)。 10 | * 11 | * 我们可以将该问题形式化地描述为:给定若干个数字,将其组合为一个整数。 12 | * 何将这些数字重新排列,以得到下一个更大的整数。如 123 下一个更大的数为 132。如果没有更大的整数,则输出最小的整数。 13 | * 14 | * 以 1,2,3,4,5,6 为例,其排列依次为: 15 | * 123456 16 | * 123465 17 | * 123546 18 | * ... 19 | * 654321 20 | * 可以看到有这样的关系:123456 < 123465 < 123546 < ... < 654321。 21 | * 22 | * 以数字序列 [1,2,3][1,2,3] 为例,其排列按照字典序依次为: 23 | * [1,2,3] 24 | * [1,3,2] 25 | * [2,1,3] 26 | * [2,3,1] 27 | * [3,1,2] 28 | * [3,2,1] 29 | *特别的,最大的排列 [3,2,1] 的下一个排列为最小的排列 [1,2,3] 30 | * 31 | * 思路: 32 | * 1、[1,2,3,6,5,4]下一个排列是[1,2,4,3,5,6]完全题解题意后就好写了 33 | * 2、从后往前找到不是递增的那个数上述是3,然后再从后往前找到第一个比3大的数,交换这两个数的位置后[1,2,4,6,5,3] 34 | * 3、已近知道6,5,3从后往前是递增的,所以要逆序一下 35 | * 4、注意边界判断 36 | * @createTime 2021年08月04日 19:24:00 37 | */ 38 | public class xiaYiGePaiLie { 39 | public static void nextPermutation(int[] nums) { 40 | if (nums == null || nums.length == 0) { 41 | return; 42 | } 43 | int i = nums.length - 2;//以倒数第二个开始 44 | while (i >= 0 && nums[i + 1] <= nums[i]) {//直到找到"从左往右较大的那个数" 45 | i--; 46 | } 47 | //这时会i指向从前往后的那个较大的数 48 | if (i > 0) { 49 | int j = nums.length - 1; 50 | while (j >= 0 && nums[j] <= nums[i]) {//从后往前找到第一个i位置还大的数 51 | j--; 52 | } 53 | swap(nums, i, j); 54 | } 55 | //此时这两个数交换完之间的数是递减的(大数被交换到前面去了) 56 | reverse(nums, i + 1, nums.length - 1); 57 | } 58 | 59 | private static void swap(int[] nums, int i, int j) { 60 | int temp = nums[i]; 61 | nums[i] = nums[j]; 62 | nums[j] = temp; 63 | } 64 | 65 | public static void reverse(int[] nums, int i, int j) { 66 | while (i < j) { 67 | swap(nums, i++, j--); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/MiddleClass/optionWithRule.java: -------------------------------------------------------------------------------- 1 | package MiddleClass; 2 | 3 | /** 4 | * @author admin 5 | * @version 1.0.0 6 | * @ClassName optionWithRule.java 7 | * @Description 假设s和m初始化, s = " a" ; m = s; 8 | * 再定义两种操作 9 | * 第一种操作: m = s; s = s + s; 10 | * 第二种操作: s = s + m; 11 | * 求最小的操作步骤数, 可以将s拼接到长度等于n(比如给个4 结果应该是aaaa) 12 | * 初始的时候 s = a ;m =a 13 | * 注意: 目标是s 14 | * 这是一道纯业务的题 就是考临时分析的能力 15 | *

16 | * 结论: 17 | * 当n为质数的时候 单独完成操作2所需要的步数就是最小步数(只需要调n-1次 因为开始有个a了) 18 | * 这其实也很好理解 操作1有个2倍,一旦有操作1的参入(第一步除外),后续再有操作1会导致永远不会形成质数 19 | *

20 | * n不为质数,假设n由若干个质数因子构成 ,a*b*c.... 而且这个顺序是最优的 21 | * 举个例子 21 = 7*3 = 3*7 到底是先完成7个a在搞出3份 还是反之 不知道哪个最优 22 | * 但最后一个一定是质数 所以最后一步需要的步数应该是c-1 23 | * 同理 层层递推 24 | *

25 | * 所以问题转化为 寻找n的质数因子 最终结论=sum(寻找到的质数)-n 每找到一个就要-1.所以-n 26 | * @createTime 2021年07月25日 16:13:00 27 | */ 28 | public class optionWithRule { 29 | 30 | //判断一个数是不是质数 31 | public static Boolean isZhiShu(int n) { 32 | if (n < 2) { 33 | return false; 34 | } 35 | //为了节省空间 实际上不用全部遍历 36 | int max = (int) Math.sqrt((double) n); 37 | for (int i = 2; i <= max; i++) { 38 | if (n % i == 0) { 39 | return false; 40 | } 41 | } 42 | return true; 43 | } 44 | 45 | //首先n不是质数 返回0号位:所有因子的和但不包括1 返回2号位:所有因子的个数但不包括1 46 | public static int[] sumAndCount(int n) { 47 | int sum = 0; 48 | int count = 0; 49 | for (int i = 2; i <= n; i++) { 50 | while (n % i == 0) { 51 | sum += i; 52 | count++; 53 | n = n / i; 54 | } 55 | } 56 | return new int[]{sum, count}; 57 | } 58 | 59 | public static int optionWithRule(int n) { 60 | if (n < 2) { 61 | return 0; 62 | } 63 | if (isZhiShu(n)) { 64 | return n - 1; 65 | } 66 | int[] sumAndCount = sumAndCount(n); 67 | return sumAndCount[0] - sumAndCount[1]; 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/jainShenZi_14.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | /** 4 | * @author renyujie518 5 | * @version 1.0.0 6 | * @ClassName jainShenZi.java 7 | * @Description 剪绳子问题 8 | * 把一根绳子剪成多段,并且使得每段的长度乘积最大。 9 | * n = 2 10 | * return 1 (2 = 1 + 1) 11 | * 12 | * n = 10 13 | * return 36 (10 = 3 + 3 + 4) 14 | * 15 | * 思路: 16 | * 动态规划 用一个dp数组记录从0到n长度的绳子剪掉后的最大乘积 17 | * dp[i]表示长度为i的绳子剪成m段后的最大乘积,初始化dp[2] = 1 18 | * 我们想要求长度为n的绳子剪掉后的最大乘积,可以从前面比n小的绳子转移而来 19 | * 我们先把绳子剪掉第一段(长度为j),如果只剪掉长度为1,对最后的乘积无任何增益,所以从长度为2开始剪 20 | * 剪了第一段后,剩下(i - j)长度可以剪也可以不剪。如果不剪的话长度乘积即为j * (i - j);如果剪的话长度乘积即为j * dp[i - j] 21 | * 取两者最大值max(j * (i - j), j * dp[i - j]) 22 | * 23 | * 对所有j不同的情况取最大值,因此最终dp[i]的转移方程为 24 | * dp[i] = max(dp[i], max(j * (i - j), j * dp[i - j])) 25 | 26 | * 最后返回dp[n]即可 27 | 28 | * @createTime 2021年08月18日 15:48:00 29 | */ 30 | public class jainShenZi_14 { 31 | public static int cuttingRope1(int n) { 32 | int[] dp = new int[n + 1]; 33 | //dp[i]表示长度为i的绳子剪成若干段后的最大乘积 34 | dp[2] = 1; 35 | for (int i = 3; i <= n; i++) { 36 | //我们想要求长度为n的绳子剪掉后的最大乘积,可以从前面比n小的绳子转移而来 37 | //第一段长度j可以取的区间为[2,i) 38 | for (int j = 2; j < i; j++) { 39 | dp[i] = Math.max(dp[i], Math.max((j * (i - j)), (j * dp[i - j]))); 40 | } 41 | } 42 | return dp[n]; 43 | } 44 | 45 | /** 46 | 剑指 Offer 14- II. 剪绳子 II 47 | 这里加个限定:答案需要取模 1e9+7(1000000007),如计算初始结果为:1000000008,请返回 1。 48 | 这题再用动态规划的话惨不忍睹,还是要看贪心 49 | */ 50 | //尽可能把绳子分成长度为3的小段,这样乘积最大(贪心) 51 | public int cuttingRope2(int n) { 52 | if(n < 4){ 53 | //如果 n == 2,返回1,如果 n == 3,返回2,两个可以合并成n小于4的时候返回n - 1 54 | return n - 1; 55 | } 56 | long res = 1; 57 | while(n > 4){ 58 | //分成尽可能多的长度为3的小段,每次循环长度n减去3,乘积res乘以3; 59 | 60 | res = res * 3 % 1000000007; 61 | n -= 3; 62 | } 63 | //最后返回时乘以小于等于4的最后一小段;每次乘法操作后记得取余就行 64 | return (int) (res * n % 1000000007); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /StructandAlgorithm/src/SwordOffer/onceInArray_56.java: -------------------------------------------------------------------------------- 1 | package SwordOffer; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * @author renyujie518 8 | * @version 1.0.0 9 | * @ClassName onceInArray_56.java 10 | * @Description 数组中只出现一次的数字 11 | * 一个整型数组里除了两个数字之外,其他的数字都出现了两次,找出这两个数。 12 | * 题目要求时间复杂度 O(N),空间复杂度 O(1) 13 | 考虑异或操作的性质:对于两个操作数的每一位,相同结果为 0,不同结果为 1,0异或任何数还是自己 14 | 15 | * 那么在计算过程中,成对出现的数字的所有位会两两抵消为 0,最终得到的结果就是那个出现了一次的数字的异或值。 16 | * 设两个只出现一次的数字为 x , y,由于 x!=y ,则 x 和 y 二进制至少有一位不同(即分别为 0 和 1 ),根据此位可以将 nums 17 | * 拆分为分别包含 x 和 y 的两个子数组。 18 | 19 | 在异或结果中找到任意为 1的位。 20 | 根据这一位对所有的数字进行分组。 21 | 在每个组内进行异或操作,得到两个数字 22 | */ 23 | public class onceInArray_56 { 24 | public int[] singleNumbers1(int[] nums) { 25 | int x = 0, y = 0; 26 | int yihuoResult = 0; 27 | int shouwei1 = 1; //这里看做000...1 28 | for(int num : nums) // 1. 遍历异或 29 | yihuoResult ^= num; 30 | while((yihuoResult & shouwei1) == 0) // 2. 利用按位与的性质,找到从右往左yihuoResult首位出现1的位置 31 | shouwei1 = shouwei1 << 1; 32 | for(int num: nums) { // 3. 遍历 nums 分组 33 | if((num & shouwei1) != 0) { //同样是利用按位与的性质,依照&后到底是0还是1分组 34 | x ^= num; //分别遍历两个子数组执行异或(相同抵消) 35 | } else{ 36 | y ^= num; 37 | } 38 | } 39 | return new int[] {x, y}; 40 | } 41 | 42 | //在一个数组 nums 中除一个数字只出现一次之外,其他数字都出现了三次。请找出那个只出现一次的数字。 43 | public int singleNumbers2(int[] nums) { 44 | Map map = new HashMap<>(); 45 | //先把数字存储到map中,其中key存储的是当前数字,value是 46 | //数字的出现的次数 47 | for (int num : nums) { 48 | map.put(num, map.getOrDefault(num, 0) + 1); 49 | } 50 | //最后在遍历map中的所有元素,返回value值等于1的 51 | for (Map.Entry entry : map.entrySet()) { 52 | if (entry.getValue() == 1) 53 | return entry.getKey(); 54 | } 55 | return -1; 56 | } 57 | } 58 | --------------------------------------------------------------------------------