JZ19 顺时针打印矩阵

JZ19 顺时针打印矩阵

代码分析

public class Solution {public ArrayList<Integer> printMatrix(int[][] matrix) {ArrayList<Integer> ret = new ArrayList<>();int r1 = 0, r2 = matrix.length - 1, c1 = 0, c2 = matrix[0].length - 1;while (r1 <= r2 && c1 <= c2) {// 上for (int i = c1; i <= c2; i++)ret.add(matrix[r1][i]);// 右for (int i = r1 + 1; i <= r2; i++)ret.add(matrix[i][c2]);if (r1 != r2){// 下for (int i = c2 - 1; i >= c1; i--)ret.add(matrix[r2][i]);}if (c1 != c2){// 左for (int i = r2 - 1; i > r1; i--)ret.add(matrix[i][c1]);}r1++; r2--; c1++; c2--;}return ret;
}}

分析

特殊情况:只要一行的时候所以需要后面两个if判断。

Z25 复杂链表的复制

题目地址

方法一(map)

public class Solution {public RandomListNode Clone(RandomListNode pHead) {if(pHead == null) return null;RandomListNode cur = pHead;Map<RandomListNode, RandomListNode> map = new HashMap<>();// 3. 复制各节点,并建立 “原节点 -> 新节点” 的 Map 映射while(cur != null) {map.put(cur, new RandomListNode(cur.label));cur = cur.next;}cur = pHead;// 4. 构建新链表的 next 和 random 指向while(cur != null) {map.get(cur).next = map.get(cur.next);map.get(cur).random = map.get(cur.random);cur = cur.next;}// 5. 返回新链表的头节点return map.get(pHead);}
}

算法流程:

  1. 若头节点 head 为空节点,直接返回 nullnull ;
  2. 初始化: 哈希表 map, 节点 cur 指向头节点;
  3. 复制链表
    1. 建立新节点,并向 dic 添加键值对 (原 cur 节点, 新 cur 节点) ;
    2. cur 遍历至原链表下一节点;
  4. 构建新链表的引用指向
    1. 构建新节点的 next 和 random 引用指向;
    2. cur 遍历至原链表下一节点;
  5. 返回值: 新链表的头节点 map[cur] ;

来源:力扣(LeetCode)

弄个字典对应新老节点,需要节点,从字典的值拿

方法二:拼接 + 拆分

public class Solution {public RandomListNode Clone(RandomListNode pHead) {if(pHead == null) return null;RandomListNode cur = pHead;// 1. 复制各节点,并构建拼接链表while(cur != null) {RandomListNode tmp = new RandomListNode(cur.label);tmp.next = cur.next;cur.next = tmp;cur = tmp.next;}// 2. 构建各新节点的 random 指向cur = pHead;while(cur != null) {if(cur.random != null)cur.next.random = cur.random.next;cur = cur.next.next;}// 3. 拆分两链表cur = pHead.next;RandomListNode pre = pHead;//老链表指针RandomListNode res = pHead.next;//新链表指针while(cur.next != null) {pre.next = pre.next.next;cur.next = cur.next.next;pre = pre.next;cur = cur.next;}pre.next = null; // 单独处理原链表尾节点return res;      // 返回新链表头节点}
}

分析

wrong,cannot used original node of list(错误,不能使用列表的原始节点)
所以单独处理原链表尾节点

JZ64 滑动窗口的最大值

题目地址

暴力解法

import java.util.*;
public class Solution {public ArrayList<Integer> maxInWindows(int [] num, int size) {ArrayList<Integer> list=new ArrayList<Integer>();int max=0;if(num.length == 0 || size > num.length || size==0){return list;}for(int i=0;i <= num.length - size;i++){max=num[i];for(int j=i;j<size + i;j++){if(max < num[j]){max=num[j];}}list.add(max);}return list;}
}

单调队列

import java.util.*;
public class Solution {public ArrayList<Integer> maxInWindows(int [] nums, int k) {//单调队列//下面是要注意的点://队列按从大到小放入//如果首位值(即最大值)不在窗口区间,删除首位//如果新增的值小于队列尾部值,加到队列尾部//如果新增值大于队列尾部值,删除队列中比新增值小的值,如果在把新增值加入到队列中//如果新增值大于队列中所有值,删除所有,然后把新增值放到队列首位,保证队列一直是从大到小ArrayList<Integer> list = new ArrayList<>();if(k <= 0|| k > nums.length)return list;Deque<Integer> deque = new LinkedList<>();//未形成窗口区间for (int i = 0; i < k; i++) {//队列不为空时,当前值与队列尾部值比较,如果大于,删除队列尾部值//一直循环删除到队列中的值都大于当前值,或者删到队列为空while (!deque.isEmpty() && nums[i] > deque.peekLast())  deque.removeLast();//执行完上面的循环后,队列中要么为空,要么值都比当前值大,然后就把当前值添加到队列中deque.addLast(nums[i]);}//窗口区间刚形成后,把队列首位值添加到队列中//因为窗口形成后,就需要把队列首位添加到数组中,而下面的循环是直接跳过这一步的,所以需要我们直接添加list.add(deque.peekFirst());//窗口区间形成for (int i = k; i < nums.length; i++) {//i-k是已经在区间外了,如果首位等于nums[i-k],那么说明此时首位值已经不再区间内了,需要删除if (deque.peekFirst() == nums[i - k])   deque.removeFirst();//删除队列中比当前值大的值while (!deque.isEmpty() && nums[i] > deque.peekLast())  deque.removeLast();//把当前值添加到队列中deque.addLast(nums[i]);//把队列的首位值添加到arr数组中list.add(deque.peekFirst());}return list;}
}

分析
最大值放在第一位

JZ56 删除链表中重复的结点

题目地址

递归

public class Solution {public ListNode deleteDuplication(ListNode pHead) {// 递归出口:当「输入节点为空」或者「不存在下一节点」,直接返回if (pHead == null || pHead.next == null) return pHead;if (pHead.val != pHead.next.val) {// 若「当前节点」与「下一节点」值不同,则当前节点可以被保留pHead.next = deleteDuplication(pHead.next);return pHead;} else {// 若「当前节点」与「下一节点」相同,需要跳过「值相同的连续一段」ListNode tmp = pHead;//用来对比while (tmp != null && tmp.val == pHead.val) tmp = tmp.next;return deleteDuplication(tmp);}}
}

非递归

public ListNode deleteDuplication(ListNode pHead) {ListNode h = new ListNode(0), p = h;h.next = pHead;while (pHead != null) {if (pHead.next != null && pHead.val == pHead.next.val) {p.next = null;} else if (pHead.next != null && p.next == null) {p.next = pHead.next;} else {p = p.next;}pHead = pHead.next;}return h.next;
}

说明:
pHead为指针
p为指针
如果当前结点和下一个重复,则p.next没空,否则,如果p.next为空,则跳过当前结点(代表当前结点为同类重复结点的最后一个).

非递归二

class Solution {public ListNode deleteDuplication(ListNode pHead) {ListNode dummy = new ListNode(-1);ListNode tail = dummy;while (pHead != null) {// 进入循环时,确保了 pHead 不会与上一节点相同if (pHead.next == null || pHead.next.val != pHead.val) {tail.next = pHead;tail = pHead;}// 如果 pHead 与下一节点相同,跳过相同节点(到达「连续相同一段」的最后一位)while (pHead.next != null && pHead.val == pHead.next.val) pHead = pHead.next;pHead = pHead.next;}tail.next = null;return dummy.next;}
}

说明:

dummy :哑节点,保存正确头节点
tail :连接正确节点
pHead:试错的

什么时候用哑节点

可以起到避免处理头节点为空的边界问题的作用,减少代码执行异常的可能性。

通过这样完美解决一旦头结点被删除无法返回元素的问题。

哑节点一般用来保存头结点的,所以遇到一些问题可能导致头结点不在的非递归问题,用到他。

JZ27 字符串的排列

题目地址

递归

public ArrayList<String> Permutation(String s) {c = s.toCharArray();//从第一层开始递归dfs(0);//将字符串数组ArrayList转化为String类型数组return list1;}public void dfs(int x) {//递归出口,最后只有一个字符,不需要交换if (x == c.length - 1) {//将字符数组转换为字符串list1.add(String.valueOf(c));return;}//为了防止同一层递归出现重复元素HashSet<Character> set = new HashSet<>();//这里就很巧妙了,第一层可以是a,b,c那么就有三种情况,这里i = x,正巧dfs(0),正好i = 0开始// 当第二层只有两种情况,dfs(1)i = 1开始for (int i = x; i < c.length; i++) {//发生剪枝,当包含这个元素的时候,直接跳过if (set.contains(c[i])) {continue;}set.add(c[i]);//交换元素,这里很是巧妙,当在第二层dfs(1),x = 1,那么i = 1或者 2, 不是交换1和1,要就是交换1和2swap(i, x);//进入下一层递归dfs(x + 1);swap(i, x);}}public void swap(int i, int x) {char temp = c[i];c[i] = c[x];c[x] = temp;}

分析

跟图一样,

第一层是 a b c,第一个for循环。

(x == c.length - 1)递归结束条件

每一层会有个set的存储,所以每一层的第一个元素都不可能重复,

遍历递归

第一个swap(i, x):负责把后面的分别和每一层第一个元素替换

dfs(x + 1);会进入下一层:x就是层数

第二个swap:用以使得字符数组的顺序回到进入递归前的状态,这样才不会影响外部的遍历顺序。因为在第一次交换后进入递归运算的时候,字符数组的顺序改变了。

下一个排列

 public ArrayList<String> Permutation2(String str) {ArrayList<String> list = new ArrayList<String>();if (str == null || str.length() == 0) {return list;}char[] chars = str.toCharArray();Arrays.sort(chars);list.add(String.valueOf(chars));int len = chars.length;while (true) {int l = len - 1;int r;while (l >= 1 && chars[l - 1] >= chars[l]) {l--;}if (l == 0)break;r = l;while (r < len && chars[r] > chars[l - 1]) {r++;}swap(chars, l - 1, r - 1);reverse(chars, l);list.add(String.valueOf(chars));}return list;}private void swap(char[] cs, int i, int j) {char temp = cs[i];cs[i] = cs[j];cs[j] = temp;}private void reverse(char[] chars, int k) {if (chars == null || chars.length <= k)return;int len = chars.length;for (int i = 0; i < (len - k) / 2; i++) {int m = k + i;int n = len - 1 - i;if (m <= n) {swap(chars, m, n);}}}

分析
一个全排列可看做一个字符串,字符串可有前缀、后缀。
生成给定全排列的下一个排列.所谓一个的下一个就是这一个与下一个之间没有其他的。
这就要求这一个与下一个有尽可能长的共同前缀,也即变化限制在尽可能短的后缀上。

[例]839647521是1–9的排列。1—9的排列最前面的是123456789,最后面的987654321,
从右向左扫描若都是增的,就到了987654321,也就没有下一个了。否则找出第一次出现下降的位置。

【例】 如何得到346987521的下一个

  1. 从尾部往前找第一个P(i-1) < P(i)的位置
    3 4 6 <- 9 <- 8 <- 7 <- 5 <- 2 <- 1
    最终找到6是第一个变小的数字,记录下6的位置i-1

  2. 从i位置往后找到最后一个大于6的数: chars[r] > chars[l - 1] ,因为是while,r-1才满足条件
    3 4 6 -> 9 -> 8 -> 7 5 2 1
    最终找到7的位置,记录位置为m

  3. 交换位置i-1和m的值
    3 4 7 9 8 6 5 2 1

  4. 倒序i位置后的所有数据
    3 4 7 1 2 5 6 8 9

则347125689为346987521的下一个排列

while (true) {int i = chars.length - 1;int j = chars.length - 1;while (i > 0 && chars[i-1] >= chars[i ]) {//为了保证不越界,而且最大也能判断得到,只能i--;}if (i==0){break;}while (j > i && chars[i-1] >= chars[j]) {j--;}swap(chars, i-1, j);reverse(chars, i );list.add(String.valueOf(chars));
}

第二步也可以从后往前查找第一个大于i的数
while (i > 0 && chars[i-1] >= chars[i ]) {//为了保证不越界,而且最大也能判断得到,只能chars[i-1] >= chars[i]

下一个排列

public char[] nextPermutation(String str) {char[] nums = str.toCharArray();int i = nums.length - 1;while (i > 0 && nums[i-1] >= nums[i ]) {i--;}if (i > 0) {int j = nums.length - 1;while (j >= 0 && nums[i] >= nums[j]) {j--;}swap(nums, i, j);}reverse(nums, i );return nums;}

为了321能变成123,所以i有个边界值0,。

矩阵中的路径

题目地址

代码分析

public boolean hasPath (char[][] board, String word) {// write code herechar[] words = word.toCharArray();for(int i = 0; i < board.length; i++) {// 遍历board,将所有的元素都看作起始节点开始搜寻for(int j = 0; j < board[0].length; j++) {if(dfs(board, words, i, j, 0)) return true;// 如果找到一次则直接返回true}}return false;// 找不到返回 false}boolean dfs(char[][] board, char[] word, int i, int j, int k) {//如果数组下标越界或者 board[i][j] != word[depth] ,则不匹配,返回false;if(i >= board.length || i < 0 || j >= board[0].length || j < 0 || board[i][j] != word[k]) return false;if(k == word.length-1 ) return true;//说明已经全部匹配完board[i][j] = '\0'; // 即表示空字符,单词中永远不会走到//递归boolean res = dfs(board, word, i + 1, j, k + 1) || dfs(board, word, i - 1, j, k + 1) || dfs(board, word, i, j + 1, k + 1) || dfs(board, word, i , j - 1, k + 1);board[i][j] = word[k];// 回溯: 将标记位复原return res;}

JZ33 丑数
题目地址

代码

public class Solution {public int GetUglyNumber_Solution(int n) {if(n==0) return 0;int a = 0, b = 0, c = 0;//初始化三个指向三个潜在成为最小丑数的位置int[] dp = new int[n];dp[0] = 1;// 第一个丑数为 1for(int i = 1; i < n; i++) {int n2 = dp[a] * 2, n3 = dp[b] * 3, n5 = dp[c] * 5;// 得到下一个丑数,三者中最小的dp[i] = Math.min(Math.min(n2, n3), n5);if(dp[i] == n2) a++;//下一个丑数可以由dp[i]*2得到,则a指针后移if(dp[i] == n3) b++;//下一个丑数可以由dp[i]*3得到,则b指针后移if(dp[i] == n5) c++;//下一个丑数可以由dp[i]*5得到,则c指针后移//此处不能写if -else ,因为可能存在dp[a]*2==dp[b]*3这种情况//那么在下一次循环中,dp[b]*3就会被再次选中,这样就会造成dp中有重复元素出现}return dp[n - 1];}
}

剑指Offer(较难)相关推荐

  1. 【剑指offer】顺时针打印矩阵

    转载请注明出处:http://blog.csdn.net/ns_code/article/details/26053049 剑指offer上的第20题,九度OJ上測试通过. 题目描写叙述: 输入一个矩 ...

  2. 剑指offer解题思路锦集11-20题

    又来更新剑指offer上的题目思路啦. 11.[二进制中1的个数] 题目:输入一个整数,输出该数二进制表示中1的个数.其中负数用补码表示. eg:NumberOf1(1)=1 NumberOf1(2) ...

  3. 剑指offer试题(PHP篇一)

    剑指offer试题(PHP篇一) 1.二维数组中的查找 题目描述 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个函数,输入这样的一个二维数组和一个 ...

  4. [字符串] --- 字符串的排列(剑指 Offer 38)

    [试题描述] 输入一个字符串,打印出该字符串中字符的所有排列.你可以以任意顺序返回这个字符串数组,但里面不能有重复元素. 例如输入字符串abc,则打印出a,b,c所能排列出来的所有字符串abc,acb ...

  5. 对分查找的最多次数_「剑指offer题解」数组中出现次数超过一半的数字

    关注我--个人公众号:后端技术漫谈 我目前是一名后端开发工程师.主要关注后端开发,数据安全,网络爬虫,物联网,边缘计算等方向. 原创博客主要内容 Java知识点复习全手册 Leetcode算法题解析 ...

  6. 剑指Offer(四):重建二叉树

    说明: 1.本系列是根据<剑指Offer>这个系列做的一个小笔记. 2.直接动力是因为师兄师姐找工作很难,而且机械出生的我面试算法更难. 3.刚开始准备刷LeetCode.LintCode ...

  7. 两个数组中对应的下标的值合成一个新的数组_剑指 offer 面试题精选图解 03 . 数组中重复的数字

    今天分享的题目来源于 LeetCode 上的剑指 Offer 系列 面试题03. 数组中重复的数字. 题目链接:https://leetcode-cn.com/problems/shu-zu-zhon ...

  8. 【LeetCode】剑指 Offer 62. 圆圈中最后剩下的数字

    [LeetCode]剑指 Offer 62. 圆圈中最后剩下的数字 文章目录 [LeetCode]剑指 Offer 62. 圆圈中最后剩下的数字 一.动态规划 总结 一.动态规划 构建一个长度为 n ...

  9. background背景图片自适应_一侧定宽、一侧自适应,尽量多的方案实现?「前端剑指offer」...

    # 问题 一侧定宽.一侧自适应,尽量多的方案实现. # 回答 有下面几种方案能实现一侧定宽一侧自适应. 使用flex布局,这也是目前主流方式.父容器设置display:flex,定宽字元素设置flex ...

最新文章

  1. linux CentOS7 下 Docker安装
  2. php 经纬度 摩卡 转换,WGS84经纬度坐标与WEB摩卡托坐标转换
  3. xp系统如何开启索引服务器,Windows XP系统关闭磁盘索引的两个方法图文教程
  4. 蓝桥杯JAVA省赛2013-----B------3(振兴中华)
  5. SQL join中on与where区别
  6. 华为P40系列起售价曝光!有点意想不到
  7. Java网络编程之通过代码实现Socket通信
  8. android theme错误,关于android:您需要在此活动中使用Theme.AppCompat主题(或后代)。 更改为Theme.AppCompat会导致其他错误...
  9. Redis 实战之多节点分布式锁【SpringBoot + RedLock】
  10. Edge 浏览器的收藏夹文档位置——最新版windows10和edge
  11. 云模型及发生器matlab代码
  12. mac获取ios应用包名
  13. python超声成像仿真_超声合成孔径成像
  14. 面试好处多,有机会还是多出去面面吧(技术岗)
  15. 把树莓派变身无线AP
  16. 上班能做什么副业?上班太闲做个什么副业?
  17. 掌上实验室V8系列教程(三)数码管动态显示
  18. 最常用的酒店IPTV系统实施方案
  19. 解套方案——1.6黄金独家操作建议,纸白银价格趋势精准解析
  20. 360全景开发(二) 鱼眼摄像头校正

热门文章

  1. 超级全的免费杂志索取汇总~ 希望加精 不收集怎知不容易啊 奉献出来
  2. 傲梅迁移系统后 休眠=关机问题解决(更换硬盘)
  3. 爬虫基础(案例:爬取17K小说个人书架信息)
  4. 无人机——遥控器篇(三)
  5. 基于STM32的esp8266WIFI自动校时+语音报时可调时钟(附源代码)
  6. 关于如何获得虚拟机还是真机信息
  7. 微信三方登录相关(Swift)
  8. actuator微服务信息完善
  9. 获取键盘鼠标操作的函数(GetAsyncKeyState ())
  10. ionic-打包成iOS系统Camera插件获取视频路径之后访问无权限