3. 数组中重复的数字

题目描述:

在一个长度为 \(n​\) 的数组里的所有数字都在 \(0​\) 到 \(n-1​\) 的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的,也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为 \(7​\) 的数组 \(\{2,3,1,0,2,5,3\}​\),那么对应的输出是第一个重复的数字 \(2​\)。

  1. Input:
  2. {2, 3, 1, 0, 2, 5}
  3. Output:
  4. 2

思路:

最直接的策略是使用哈希表,时间复杂度为 \(O(n)\),但是它提高时间效率是以一个大小为 \(O(n)\) 的哈希表为代价的,可以采用更好的方式使空间复杂度降为 \(O(1)​\)。

本题数组里的元素都在 \(0\) 到 \(n-1\) 的范围内,我们从头到尾扫描数组的每个元素,将元素 \(j = numbers[i]\) 与下标为 \(j\) 的元素进行位置交换,如果 \(numbers[j]\) 已经等于 \(j\),则不用进行交换,数字 \(j\) 就是重复数字(\(i \ne j\))。

  1. public class Solution {
  2. /**
  3. * numbers: 数组;length: 数组的长度;duplication[0]: 存放任意一个重复的数字
  4. * 返回为 true 代表为数组中存在重复数字,false 代表没有
  5. */
  6. public boolean duplicate(int[] numbers,int length,int[] duplication) {
  7. for(int i = 0; i < length; i++) {
  8. while(i != numbers[i]) {
  9. int j = numbers[i];
  10. if(numbers[j] == j) {
  11. //要替换的位置下标 j 上的元素已经为 j,说明已经存在重复元素
  12. duplication[0] = j;
  13. return true;
  14. }
  15. //进行位置上的元素替换
  16. numbers[i] = numbers[j];
  17. numbers[j] = j;
  18. }
  19. }
  20. return false;
  21. }
  22. }

4. 二维数组中的查找

题目描述:

在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。

  1. Consider the following matrix:
  2. [
  3. [1, 4, 7, 11, 15],
  4. [2, 5, 8, 12, 19],
  5. [3, 6, 9, 16, 22],
  6. [10, 13, 14, 17, 24],
  7. [18, 21, 23, 26, 30]
  8. ]
  9. Given target = 5, return true.
  10. Given target = 20, return false.

思路:

该二维数组中的一个数,小于它的数一定在其左边,大于它的数一定在其下边。因此,从右上角开始查找,就可以根据 \(target\) 和当前元素的大小关系来缩小查找区间,如果当前元素等于 \(target\),返回 \(true\);如果当前元素小于 \(target\),则下个位置左移;如果当前元素大于 \(target\),则下个位置下移;如果下个位置超出了数组边界范围,返回 \(false\)。

  1. public class Solution {
  2. public boolean Find(int target, int [][] array) {
  3. if(array == null || array.length == 0 || array[0].length == 0) {
  4. return false;
  5. }
  6. int m = array.length, n = array[0].length;
  7. int i = 0, j = n - 1;
  8. while(i < m && j >= 0) {
  9. if(array[i][j] == target) {
  10. return true;
  11. }else if(array[i][j] < target) {
  12. i++;
  13. }else{
  14. j--;
  15. }
  16. }
  17. return false;
  18. }
  19. }

5. 替换空格

题目描述:

请实现一个函数,将一个字符串中的每个空格替换成 “%20”。例如,当字符串为 We Are Happy,则经过替换之后的字符串为 We%20Are%20Happy。

思路:

本题可以直接使用 \(String\) 的 \(replace()\) 或者 \(replaceAll()\) 方法,但是该题的本意是希望在一个字符数组上实现该功能。

  1. public class Solution {
  2. public String replaceSpace(StringBuffer str) {
  3. return str.toString().replace(" ", "%20");
  4. }
  5. }

将字符数组扩充到能容纳替换后的字符串大小,使用指针 \(p1\) 指向原来的字符数组末端,指针 \(p2\) 指向扩充后的字符数组末端。指针 \(p1\) 向前移动,$p1 $ 遇到非空格字符,将该字符拷贝到 \(p2\) 位置,\(p2\) 向前移动一位;\(p1\) 遇到空格字符,\(p2\) 向前移动三位,\(p2\) 后续的三位放置 "%20";直达两指针相遇结束。

  1. public class Solution {
  2. public String replaceSpace(StringBuffer str) {
  3. int p1 = str.length() - 1;
  4. //增大数组空间
  5. for(int i = 0; i <= p1; i++) {
  6. if(str.charAt(i) == ' ') {
  7. str.append(" ");
  8. }
  9. }
  10. int p2 = str.length() - 1;
  11. while(p1 < p2) {
  12. if(str.charAt(p1) != ' ') {
  13. str.setCharAt(p2--, str.charAt(p1--));
  14. }else {
  15. str.setCharAt(p2--, '0');
  16. str.setCharAt(p2--, '2');
  17. str.setCharAt(p2--, '%');
  18. p1--;
  19. }
  20. }
  21. return str.toString();
  22. }
  23. }

6. 从尾到头打印链表

题目描述:

输入一个链表,按链表从尾到头的顺序返回一个 \(ArrayList\)。

思路:

思路一:

使用递归的方式,逆序打印该链表。

  1. /**
  2. * public class ListNode {
  3. * int val;
  4. * ListNode next = null;
  5. *
  6. * ListNode(int val) {
  7. * this.val = val;
  8. * }
  9. * }
  10. */
  11. import java.util.ArrayList;
  12. public class Solution {
  13. public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
  14. ArrayList<Integer> ret = new ArrayList<>();
  15. if(listNode == null) {
  16. return ret;
  17. }
  18. dfs(listNode, ret);
  19. return ret;
  20. }
  21. public void dfs(ListNode listNode, ArrayList<Integer> ret) {
  22. if(listNode.next != null) {
  23. dfs(listNode.next, ret);
  24. }
  25. ret.add(listNode.val);
  26. }
  27. }

思路二:

使用头插法的方式得到一个逆序的链表。

头插法的头结点 \(head\) 是一个额外节点,这个节点不存储值,\(head.next\) 才是链表的第一个节点。当需要插入一个新的节点 \(node\),该节点的插入位置在 \(head\) 和 \(head.next\) 之间,所以插入时 \(node.next = head.next\),\(head.next =node\)。

  1. /**
  2. * public class ListNode {
  3. * int val;
  4. * ListNode next = null;
  5. *
  6. * ListNode(int val) {
  7. * this.val = val;
  8. * }
  9. * }
  10. */
  11. import java.util.ArrayList;
  12. public class Solution {
  13. public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
  14. ListNode head = new ListNode(-1);
  15. while(listNode != null) {
  16. ListNode node = listNode.next;
  17. listNode.next = head.next;
  18. head.next = listNode;
  19. listNode = node;
  20. }
  21. ArrayList<Integer> ret = new ArrayList<>();
  22. while(head.next != null) {
  23. ret.add(head.next.val);
  24. head = head.next;
  25. }
  26. return ret;
  27. }
  28. }

思路三:

使用栈结构。

  1. /**
  2. * public class ListNode {
  3. * int val;
  4. * ListNode next = null;
  5. *
  6. * ListNode(int val) {
  7. * this.val = val;
  8. * }
  9. * }
  10. */
  11. import java.util.*;
  12. public class Solution {
  13. public ArrayList<Integer> printListFromTailToHead(ListNode listNode) {
  14. Stack<Integer> stack = new Stack<>();
  15. while(listNode != null) {
  16. stack.push(listNode.val);
  17. listNode = listNode.next;
  18. }
  19. ArrayList<Integer> ret = new ArrayList<>();
  20. while(!stack.isEmpty()) {
  21. ret.add(stack.pop());
  22. }
  23. return ret;
  24. }
  25. }

7. 重建二叉树

题目描述:

输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列 \(\{1,2,4,7,3,5,6,8\}\) 和中序遍历序列 \(\{4,7,2,1,5,3,8,6\}\),则重建下图所示二叉树并返回二叉树的根节点。

思路:

可以从二叉树的前序遍历和中序遍历序列中确定根节点的值、左子树和右子树的范围。

  1. /**
  2. * Definition for binary tree
  3. * public class TreeNode {
  4. * int val;
  5. * TreeNode left;
  6. * TreeNode right;
  7. * TreeNode(int x) { val = x; }
  8. * }
  9. */
  10. public class Solution {
  11. public TreeNode reConstructBinaryTree(int[] pre,int [] in) {
  12. return buildSubTree(pre, in, 0, 0, pre.length);
  13. }
  14. //pStart 是子树在前序遍历数组的起始索引,iStart 是子树在中序遍历数组的起始索引,len 是子树节点数目(大小)
  15. public TreeNode buildSubTree(int[] pre, int[] in, int pStart, int iStart, int len) {
  16. if(len == 0) {
  17. return null;
  18. }
  19. //子树根节点
  20. int rootVal = pre[pStart];
  21. TreeNode root = new TreeNode(rootVal);
  22. //寻找中序遍历数组中子树的根节点
  23. int index = iStart;
  24. while(in[index] != rootVal) {
  25. index++;
  26. }
  27. int lLen = index - iStart; //根节点左子树的大小
  28. int rLen = len - lLen - 1; //根节点右子树的大小
  29. //左右孩子节点
  30. TreeNode lNode = buildSubTree(pre, in, pStart + 1, iStart, lLen);
  31. TreeNode rNode = buildSubTree(pre, in, pStart + lLen + 1, iStart + lLen + 1, rLen);
  32. root.left = lNode;
  33. root.right = rNode;
  34. return root;
  35. }
  36. }

8. 二叉树的下一个结点

题目描述:

给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回。注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针。下图中,树中从父节点指向子节点的指针用实线表示,从子节点指向父节点的指针用虚线表示。

思路:

中序遍历顺序的下一个节点可以分两种情况讨论:

  • 如果一个节点的右子树不为空,那么该节点的下一个节点是其右子树的最左节点。例如:上图中的节点 \(b\) 的下一个节点为 \(h\), 节点 \(a\) 的下一个节点是 \(f\),节点 \(e\) 的下一个节点是 \(i\) 。
  • 如果一个节点的右子树为空,我们沿着父节点的指针往上遍历,如果遍历的某个节点是其父节点的左孩子节点,则该节点的父节点便是我们要寻找的下一个节点。例如:上图中的节点 \(i\) 的下一个节点为 \(a\),节点 \(d\) 的下一个节点为 \(b\)。
  1. /*
  2. public class TreeLinkNode {
  3. int val;
  4. TreeLinkNode left = null;
  5. TreeLinkNode right = null;
  6. TreeLinkNode next = null;
  7. TreeLinkNode(int val) {
  8. this.val = val;
  9. }
  10. }
  11. */
  12. public class Solution {
  13. public TreeLinkNode GetNext(TreeLinkNode pNode){
  14. if(pNode == null) {
  15. return null;
  16. }
  17. //右子树不为空,寻找右子树最左端
  18. if(pNode.right != null) {
  19. TreeLinkNode node = pNode.right;
  20. while(node.left != null) {
  21. node = node.left;
  22. }
  23. return node;
  24. }
  25. //右子树为空,往父节点方向向上寻找,直到找到某个节点是其父节点的左孩子
  26. if(pNode.right == null) {
  27. while(pNode.next != null) {
  28. TreeLinkNode parent = pNode.next;
  29. if(parent.left == pNode) {
  30. return parent;
  31. }
  32. pNode = parent;
  33. }
  34. }
  35. return null;
  36. }
  37. }

9. 用两个栈实现队列

题目描述:

用两个栈来实现一个队列,完成队列的 \(Push\) 和 \(Pop\) 操作。 队列中的元素为 \(int​\) 类型。

思路:

使用两个栈 \(in\) 和 \(out\),\(in\) 栈负责数据的入栈操作,\(out\) 栈负责数据的出栈操作。需要注意的是,\(in\) 栈的数据倾倒到 \(out\) 栈过程要一次性将 \(in\) 栈全部数据倒入。

  1. import java.util.Stack;
  2. public class Solution {
  3. Stack<Integer> stack1 = new Stack<Integer>(); //in 栈
  4. Stack<Integer> stack2 = new Stack<Integer>(); //out 栈
  5. public void push(int node) {
  6. stack1.push(node); //入栈数据全部进入 in 栈
  7. }
  8. public int pop() {
  9. if(stack2.isEmpty()) {
  10. //out 栈为空,要将 in 栈全部数据一次性倾倒到 out 栈
  11. while(!stack1.isEmpty()) {
  12. stack2.push(stack1.pop());
  13. }
  14. }
  15. return stack2.pop();
  16. }
  17. }

参考

  1. https://cyc2018.github.io/CS-Notes/#/notes/%E5%89%91%E6%8C%87%20Offer%20%E9%A2%98%E8%A7%A3%20-%203~9
  2. 《剑指OFFER 名企面试官精讲典型编程题 第2版》

剑指Offer-3~9题的更多相关文章

  1. 浅谈《剑指offer》原题:不使用条件、循环语句求1+2+……+n

    转载自:浅谈<剑指offer>原题:求1+2+--+n 如侵犯您的版权,请联系:windeal12@qq.com <剑指offer>上的一道原题,求1+2+--+n,要求不能使 ...

  2. 《剑指offer》算法题第十二天

    今天是<剑指offer>算法题系列的最后一天了,但是这个系列并没有包括书上的所有题目,因为正如第一天所说,这些代码是在牛客网上写并且测试的,但是牛客网上并没有涵盖书上所有的题目. 今日题目 ...

  3. 《剑指offer》刷题目录

    <剑指offer>刷题目录 面试题03. 数组中重复的数字 面试题04. 二维数组中的查找 面试题05. 替换空格 面试题06. 从尾到头打印链表 面试题07. 重建二叉树 面试题09. ...

  4. 《剑指Offer》附加题_用两个队列实现一个栈_C++版

    在<剑指Offer>中,在栈和队列习题中,作者留下来一道题目供读者自己实现,即"用两个队列实现一个栈". 在计算机数据结构中,栈的特点是后进先出,即最后被压入(push ...

  5. 剑指offer 面试5题

    面试5题: 题目:请实现一个函数,将一个字符串中的空格替换成“%20”.例如,当字符串为We Are Happy.则经过替换之后的字符串为We%20Are%20Happy. 方法一: # -*- co ...

  6. 剑指offer 面试8题

    面试8题: 题目:二叉树的下一个节点 题目描述:给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回.注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针. 解题思路:详见剑 ...

  7. 剑指offer 面试10题

    面试10题: 题目:大家都知道斐波那契数列,现在要求输入一个整数n,请你输出斐波那契数列的第n项.n<=39 n=0时,f(n)=0 n=1时,f(n)=1 n>1时,f(n)=f(n-1 ...

  8. 剑指offer 面试11题

    面试11题: 题目: 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转. 输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素. 例如数组{3,4,5,1,2}为{1,2,3,4 ...

  9. 剑指offer 面试14题

    面试14题: 题目:剪绳子 题:给你一根长度为n的绳子,请把绳子剪成m段(m,n都是整数,且n>1,m>1),每段绳子的长度记为k[0],k[1],k[2],...,k[m].请问k[0] ...

  10. 剑指offer 面试17题

    面试17题: 题目:打印从1到最大的n位数 题:输入数字n,按顺序打印出从1到最大的n位十进制数,比如输入3,则打印出1.2.3一直到最大的3位数999. 解题思路:需要考虑大数问题,这是题目设置的陷 ...

随机推荐

  1. 手写call,apply方法实现

    call Function.prototype.myCall = function(){ var object = arguments[0]; var arr = []; for(var i = 1; ...

  2. hdu 3339 In Action(迪杰斯特拉+01背包)

    In Action Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total S ...

  3. Pytest - 使用介绍

    1. 概述 pytest是一个非常成熟的全功能的Python测试框架,主要特点有以下几点: 1.简单灵活,容易上手,文档丰富: 2.支持参数化,可以细粒度地控制要测试的测试用例: 3.能够支持简单的单 ...

  4. springmvc web.xml和application.xml配置详情(附:完整版pom.xml)

    web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="htt ...

  5. Scheduler

    先看看文档对于Scheduler的作用介绍 https://code4craft.gitbooks.io/webmagic-in-action/content/zh/posts/ch1-overvie ...

  6. 聚类——DBSCAN

    转载自: https://www.cnblogs.com/pinard/p/6208966.html http://www.cnblogs.com/pinard/p/6217852.html http ...

  7. 【30.43%】【codeforces 746C】Tram

    time limit per test1 second memory limit per test256 megabytes inputstandard input outputstandard ou ...

  8. 【31.93%】【codeforces 670E】Correct Bracket Sequence Editor

    time limit per test2 seconds memory limit per test256 megabytes inputstandard input outputstandard o ...

  9. VSCode提示没有权限,无法保存文件问题

    重装了系统之后,重新打开了VSCode发现无法保存修改的文件,激活系统后发现还是无法保存文件,都是提示权限问题,原因在于文件夹权限继承并不是我所登录的这个用户,接着我试着按照网上的方法,在文件夹后,右 ...

  10. [板子]用线段树解决ST表问题

    ST表可以参考:http://blog.csdn.net/whistlena/article/details/52191463 简单说就是区间RMQ最值问题. 对解决这种问题,线段树不用用啥啊. 扔一 ...