7474#### [ 题065机器人的运动范围] ( #题065 )
7575#### [ 题066剪绳子] ( #题066 )
7676
77-
7877### 题003 二维数组中的查找
7978
8079##### 题目内容:
8180
81+ ![ image-20201202111714727] ( ../static/image-20201202111714727.png )
82+
8283在一个二维[ 数组] ( https://cuijiahua.com/blog/tag/数组/ ) 中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维[ 数组] ( https://cuijiahua.com/blog/tag/数组/ ) 和一个整数,判断数组中是否含有该整数。
8384
8485如果在一个二维数组中找到数字7,则返回true,如果没有找到,则返回false。
@@ -830,7 +831,7 @@ public ArrayList<Integer> PrintFromTopToBottom(TreeNode root) {
830831
831832也可以使用递归实现,深度遍历递归实现
832833
833- ```
834+ ``` java
834835ArrayList<TreeNode > list = new ArrayList<TreeNode > ();
835836void deepTranverse(TreeNode node) {
836837 if (node!= null ) {
@@ -1974,7 +1975,7 @@ public int Add(int num1,int num2) {
19741975
19751976-2的31次方 2的31次方-1
19761977
1977- ```
1978+ ``` java
19781979public static int StrToInt(String str) {
19791980 if (str== null || str. length()== 0 ) {
19801981 return 0 ;
@@ -2041,7 +2042,7 @@ public boolean duplicate(int numbers[],int length,int [] duplication) {
20412042
20422043## 题050 构建乘积数组
20432044
2044- ```
2045+ ``` java
20452046就是B [i] = A [0 ]A [1 ].. . A [i- 1 ] A [i+ 1 ]... * A [n- 1 ],通过拆分成两部分,
20462047C [i] = A [0 ]A [1 ].. . A [i- 1 ]
20472048D [i] = A [i+ 1 ]... * A [n- 1 ]
@@ -2074,7 +2075,7 @@ public int[] multiply(int[] A) {
20742075
20752076就是使用一个数组来记录字符出现的次数。
20762077
2077- ```
2078+ ``` java
20782079StringBuffer str = new StringBuffer ();
20792080 int [] table = new int [256 ];// 记录出现次数,0代表0次,1代表1次,2代表2次及2次以上
20802081 public void Insert(char ch)
@@ -2260,7 +2261,7 @@ public ListNode deleteDuplication(ListNode pHead)
22602261
22612262 父节点是右子树,一直向上遍历,直到找到一个父节点,他是祖先节点是左节点的,找到就返回祖先节点,找不到就返回空。
22622263
2263- ```
2264+ ``` java
22642265public TreeLinkNode GetNext(TreeLinkNode pNode)
22652266{
22662267 // 这个节点有右子树
@@ -2307,7 +2308,7 @@ public TreeLinkNode GetNext(TreeLinkNode pNode)
23072308
23082309假设有另外一种遍历是根节点,右子树,左子树,如果二叉树是对称,那么这两种遍历的结果是一样的,所以使用递归来进行两种遍历,然后在过程中判断两种遍历结果是否一样。
23092310
2310- ```
2311+ ``` java
23112312boolean isSymmetrical(TreeNode pRoot)
23122313{
23132314 return isSymmetrical(pRoot,pRoot);
@@ -2426,7 +2427,7 @@ public ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
24262427
24272428就是使用一个队列queue,一开始将根节点加入queue,并且加入一个null元素到队列中作为标志元素,用来分割每一层,标志这一层的节点都在标志元素的前面。然后对queue中元素出列,每个进行打印,直到出列的元素是null,表示这一层已经结束了,如果queue中还有元素,那么在后面加入null标志元素分割,并且进行换行,打印下一行,如果queue中没有元素就结束循环
24282429
2429- ```
2430+ ``` java
24302431ArrayList<ArrayList<Integer > > Print(TreeNode pRoot) {
24312432 ArrayList<ArrayList<Integer > > arrayLists= new ArrayList<ArrayList<Integer > > ();
24322433 if (pRoot == null ) return arrayLists;
@@ -2457,7 +2458,7 @@ ArrayList<ArrayList<Integer>> Print(TreeNode pRoot) {
24572458
24582459这种方法也可以用来进行二叉树深度遍历,遍历完之后将嵌套数组拆分成单层的数组。
24592460
2460- ```
2461+ ``` java
24612462ArrayList<ArrayList<Integer > > Print2(TreeNode pRoot) {
24622463 ArrayList<ArrayList<Integer > > arrayLists = new ArrayList<ArrayList<Integer > > ();
24632464 if (pRoot== null )return arrayLists;
@@ -2494,7 +2495,7 @@ void find(TreeNode pRoot, int depth, ArrayList<ArrayList<Integer>> arrayLists) {
24942495
249524964.重复步骤3,直到队列元素个数为空。
24962497
2497- ```
2498+ ``` java
24982499String Serialize(TreeNode root) {
24992500 StringBuffer stringBuffer = new StringBuffer ();
25002501 if (root == null ) {return stringBuffer. toString();}
@@ -2567,7 +2568,7 @@ Integer convert(String str) {
25672568
25682569由于前序遍历是先左子树,根节点,右子树的顺序,所以前序遍历的结果,就是二叉搜索树中元素按递增顺序排列的结果,所以按照前序遍历到第K个元素就是第K小的节点。
25692570
2570- ```
2571+ ``` java
25712572Integer index = 0 ;
25722573TreeNode kNode = null ;
25732574
@@ -2599,7 +2600,7 @@ void find(TreeNode node, Integer k) {
25992600
26002601
26012602
2602- ```
2603+ ``` java
26032604 ArrayList<Integer > arrayList = new ArrayList<Integer > ();
26042605
26052606 public void Insert(Integer num) {
@@ -2633,7 +2634,7 @@ void find(TreeNode node, Integer k) {
26332634
26342635解法一就是维护一个排序好的数组,数组就是当前滑动窗口排序好的结果,每次滑动时将值插入排序好的队列,并且将过期的值删除,
26352636
2636- ```
2637+ ``` java
26372638public ArrayList<Integer > maxInWindows1(int [] num, int size) {
26382639 ArrayList<Integer > arrayList = new ArrayList<Integer > ();
26392640 ArrayList<Integer > sortList = new ArrayList<Integer > (size);
@@ -2688,7 +2689,7 @@ ArrayList<Integer> removeValueIntoSorted(ArrayList<Integer> sortList, int curren
26882689因为这些元素的index比A都小,而且值也比A小,不可能再成为最大值了,
26892690然后判断队列头结点的最大值是否过期,过期的话也删除
26902691
2691- ```
2692+ ``` java
26922693
26932694public ArrayList<Integer > maxInWindows(int [] num, int size) {
26942695 ArrayList<Integer > arrayList = new ArrayList<Integer > ();
@@ -2727,7 +2728,7 @@ public ArrayList<Integer> maxInWindows(int[] num, int size) {
27272728
27282729就是递归去判断就行了。
27292730
2730- ```
2731+ ``` java
27312732public boolean hasPath(char [] matrix, int rows, int cols, char [] str)
27322733{
27332734 boolean [] flag = new boolean [matrix. length];
@@ -2782,7 +2783,7 @@ boolean judge(char[] matrix, int rows, int cols, int i, int j, char[] str, int c
27822783
27832784就是递归去求解,判断每个节点的上,下,左,右节点是否满足需求。
27842785
2785- ```
2786+ ``` java
27862787public int movingCount(int threshold, int rows, int cols)
27872788{
27882789 if (rows< 0 || cols< 0 || threshold< 0 ) {return 0 ;}
@@ -2872,7 +2873,7 @@ public int cutRope(int target) {
28722873
28732874普通的二分查找
28742875
2875- ```
2876+ ``` java
28762877public int findByHalf(int [] array, int target) {
28772878 if (array== null || array. length== 0 ) {
28782879 return - 1 ;
@@ -2895,7 +2896,7 @@ public int findByHalf(int[] array, int target) {
28952896
28962897二分查找-左边界版本,只要改等于时的判断以及后面的越界判断
28972898
2898- ```
2899+ ``` java
28992900 public static void main(String [] args) {
29002901 Test002 test002 = new Test002 ();
29012902 int [] array = new int []{1 ,2 ,2 ,2 ,2 ,4 ,5 ,6 ,7 };
@@ -2923,195 +2924,3 @@ public int findByHalf(int[] array, int target) {
29232924 // 如果有左边界就返回左边界,没有时,此时的left应该是最接近左边界,并且大于左边界的数的位置
29242925```
29252926
2926-
2927-
2928- ## LRU算法
2929-
2930- LRU其实就是Last Recent Used,就是最近使用淘汰策略,所以当空间满了时,就根据最近使用时间来删除。一般是使用一个双向链表来实现,同时为了快速访问节点,会使用一个HashMap来存储键值映射关系。(需要注意的是,为了在内存满时删除最后一个节点时,可以以O(1)时间复杂度从HashMap中删掉这个键值对,每个节点除了存储value以外,还需要存储key)。
2931-
2932- #### 添加新元素的过程:
2933-
2934- 首先我们增加两个方法,remove()方法用于删除一个节点,addNewNodeToHead()代表添加一个节点到头部。
2935-
2936- 1.判断节点是否已经存在于链表中,是的话,找出节点,
2937-
2938- 1.1更新value,
2939-
2940- 1.2调用remove()方法删除节点,
2941-
2942- 1.3调用addNewNodeToHead()将节点添加到链表头部。
2943-
2944- 2.节点不存在于链表中,那么判断链表长度是否超出限制,
2945-
2946- 2.1是的话remove(lastNode.key)
2947-
2948- 2.2创建一个新节点,调用addNewNodeToHead()将节点添加到链表头部。
2949-
2950-
2951-
2952- remove()方法的细节,主要是更新node的前后节点的next或pre指针,以及更新后需要判断删除的节点是否是headNode或者lastNode,是的话同时需要更新headNode或者lastNode。
2953-
2954- addNewNodeToHead()方法细节,主要是要先判断head是否为null,是的话说明链表为空,需要将headNode和lastNode都设置为node,不为null就执行添加操作,将headNode.pre设置为node,node的next设置为headNode,headNode=node;
2955-
2956-
2957-
2958- ``` java
2959- // 双向链表
2960- public static class ListNode {
2961- String key;
2962- Integer value;
2963- ListNode pre = null ;
2964- ListNode next = null ;
2965- ListNode (String key , Integer value ) {
2966- this . key = key;
2967- this . value = value;
2968- }
2969- }
2970- ListNode headNode;
2971- ListNode lastNode;
2972- int limit= 4 ;
2973- HashMap<String , ListNode > hashMap = new HashMap<String , ListNode > ();
2974- public void put(String key, Integer val) {
2975- ListNode existNode = hashMap. get(key);
2976- if (existNode!= null ) {// 有老的节点,只是更新值,先从链表移除,然后从头部添加
2977- existNode. value= val;
2978- remove(key);
2979- addNewNodeToHead(existNode);
2980- } else {
2981- // 达到限制,先删除尾节点
2982- if (hashMap. size() == limit) { remove(lastNode. key); }
2983- ListNode newNode = new ListNode (key,val);
2984- addNewNodeToHead(newNode);
2985- }
2986- }
2987- public ListNode get(String key) {
2988-
2989- ListNode node = hashMap. get(key);
2990- if (node == null ) {
2991- return null ;
2992- }
2993- remove(node. key);
2994- addNewNodeToHead(node);
2995- return node;
2996- }
2997- public void remove(String key) {
2998- ListNode deleteNode = hashMap. get(key);
2999- hashMap. remove(key);
3000- ListNode preNode = deleteNode. pre;
3001- ListNode nextNode = deleteNode. next;
3002- // 删除操作需要更新pre节点的next指针和next节点的pre指针,以及更新head和last
3003- if (preNode!= null ) { preNode. next = nextNode; }
3004- if (nextNode!= null ) { nextNode. pre = preNode; }
3005- if (headNode == deleteNode) { headNode = nextNode; }
3006- if (lastNode == deleteNode) { lastNode = preNode; }
3007- }
3008- private void addNewNodeToHead(ListNode node) {
3009- hashMap. put(node. key,node);
3010- if (headNode== null || lastNode== null ) {
3011- headNode = node;
3012- lastNode = node;
3013- return ;
3014- }
3015- headNode. pre = node;
3016- node. next = headNode;
3017- headNode = node;
3018- }
3019- ```
3020-
3021- ##### 使用LinkedHashMap实现的算法
3022-
3023- 使用LinkedHashMap实现LRU算法,
3024-
3025- * LinkedHashMap默认的accessOrder为false,也就是会按照插入顺序排序,
3026- 所以在插入新的键值对时,总是添加在队列尾部,
3027- 如果是访问已存在的键值对,或者是put操作的键值对已存在,那么需要将键值对先移除再添加。
3028- * 如果是将accessOrder设置为true,get已有键值对时就不需要删除key了,会自动调整顺序,put方法需要在添加或者更新键值对后调用LinkedHashMap#get()访问key,调整顺序。
3029-
3030- ``` java
3031- // accessOrder为false,按照插入顺序排序的写法
3032- public static class LRUCache {
3033- int capacity;
3034- Map<Integer , Integer > map;
3035- public LRUCache (int capacity ) {
3036- this . capacity = capacity;
3037- map = new LinkedHashMap<> ();
3038- }
3039- public int get (int key ) {
3040- if (! map. containsKey(key)) {
3041- return - 1 ;
3042- }
3043- // 先删除旧的位置,再放入新位置
3044- Integer value = map. remove(key);
3045- map. put(key, value);
3046- return value;
3047- }
3048-
3049- public void put (int key , int value ) {
3050- if (map. containsKey(key)) {
3051- map. remove(key);
3052- map. put(key, value);
3053- return ;
3054- }
3055- map. put(key, value);
3056- // 超出capacity,删除最久没用的,利用迭代器,删除第一个
3057- if (map. size() > capacity) {
3058- map. remove(map. keySet(). iterator(). next());
3059- }
3060- }
3061- }
3062- ```
3063- accessOrder为true,按照访问顺序排序的实现方法
3064- ``` java
3065-
3066- public static class LRUCache2 {
3067- int capacity;
3068- LinkedHashMap<Integer , Integer > linkedHashMap;
3069- LRUCache2 (int capacity ) {
3070- this . capacity = capacity;
3071- // 如果要修改accessOrder只能使用这种构造器方法来创建LinkedHashMap
3072- linkedHashMap = new LinkedHashMap<Integer , Integer > (16 ,0.75f ,true );
3073- }
3074- public int get (int key ) {
3075- Integer value = linkedHashMap. get(key);
3076- return value == null ? - 1 : value;
3077- }
3078- public void put (int key , int val ) {
3079- linkedHashMap. put(key, val);
3080- if (linkedHashMap. size() > capacity) {
3081- linkedHashMap. remove(linkedHashMap. keySet(). iterator(). next());
3082- }
3083- }
3084- // 通过调用get()方法访问key来调整顺序
3085- linkedHashMap.get (key );
3086- }
3087- ```
3088-
3089- ### 【面试算法题】阿拉伯数字转化为中文读法
3090-
3091- 例如我们要将10100转换为中文,总体流程就是先拿10100/1个亿,发现结果为0,说明不会包含亿这个数量级,然后10100/1万,得到结果result为1,余数remain为100,说明包含万这个数量级,我们的结果肯定是等于 "result的中文表示"+单位"万"+"余数的中文表示",所以就对问题进行了分解,f(n) = f(n/数量级)+数量级单位+f(n%数量级)
3092-
3093- ``` java
3094- static String [] nameArray1 = {" " ," 一" ," 二" ," 三" ," 四" ," 五" ," 六" ," 七" ," 八" ," 九" };
3095- static String [] nameArray2 = {" " ," 十" ," 百" ," 千" ," 万" ," 亿" };
3096- static int [] intArray = {1 ,10 ,100 ,1000 ,10000 ,100000000 };
3097-
3098- public static String numToChinese(int num) {
3099- for (int i = intArray. length- 1 ; i >= 0 ; i-- ) {
3100- int part1 = num/ intArray[i];
3101- int part2 = num% intArray[i];
3102- if (i== 0 ) {// 到个位了
3103- return nameArray1[part1];
3104- }
3105- if (part1> 0 ) {
3106- // 整除部分,例如10100,整除部分就是十
3107- String left = numToChinese(part1);
3108- // 整除部分的单位,例如10100,整除部分的单位就是万
3109- String unitString = nameArray2[i];
3110- // 余数部分,例如10100,余数部分就是一百
3111- String right = numToChinese(part2);
3112- return left + unitString + right;
3113- }
3114- }
3115- return " " ;
3116- }
3117- ```
0 commit comments