剑指offer-java
面试题67 机器人的运动范围
题意:
地上有一个m行和n列的方格。一个机器人从坐标0,0的格子开始移动,每一次只能向左,右,上,下四个方向移动一格,但是不能进入行坐标和列坐标的数位之和大于k的格子。 例如,当k为18时,机器人能够进入方格(35,37),因为3+5+3+7 = 18。但是,它不能进入方格(35,38),因为3+5+3+8 = 19。请问该机器人能够达到多少个格子?
解法:回溯法。注意申请的内存要释放掉。delete[] visited;
1 class Solution { 2 public: 3 int movingCount(int threshold, int rows, int cols) 4 { 5 bool *visited = new bool[rows * cols]; 6 for (int i = 0; i < rows * cols; i++) { 7 visited[i] = false; 8 } 9 int count = helper(visited, threshold, 0, 0, rows, cols); 10 delete[] visited; 11 return count; 12 } 13 int helper(bool* visited, int threshold, int i, int j, int rows, int cols) { 14 if (i <0 || j < 0 || i >= rows || j >= cols || visited[i * cols + j]) { 15 return 0; 16 } 17 if (digitSum(i, j) <= threshold) { 18 visited[i * cols + j] = true; 19 int count = 1; 20 count += helper(visited, threshold, i - 1, j, rows, cols); 21 count += helper(visited, threshold, i, j + 1, rows, cols); 22 count += helper(visited, threshold, i + 1, j, rows, cols); 23 count += helper(visited, threshold, i, j - 1, rows, cols); 24 return count; 25 } 26 return 0; 27 } 28 int digitSum(int i, int j) { 29 int sum = 0; 30 while (i / 10 != 0) { 31 sum += i % 10; 32 i = i / 10; 33 } 34 sum += i; 35 while (j / 10 != 0) { 36 sum += j % 10; 37 j = j / 10; 38 } 39 sum += j; 40 return sum; 41 } 42 };
面试题66 判断二维矩阵是否存在一条包含所有字符串的路径。leetcode 79
题意:
Given a 2D board and a word, find if the word exists in the grid. The word can be constructed from letters of sequentially adjacent cell, where "adjacent" cells are those horizontally or vertically neighboring. The same letter cell may not be used more than once. For example, Given board = [ ['A','B','C','E'], ['S','F','C','S'], ['A','D','E','E'] ] word = "ABCCED", -> returns true, word = "SEE", -> returns true, word = "ABCB", -> returns false.
解法:回溯法也是dfs。
1 class Solution { 2 public: 3 bool exist(vector<vector<char>>& board, string word) { 4 int m = board.size(); 5 int n = board[0].size(); 6 for (int i = 0; i < m; i++) { 7 for (int j = 0; j < n; j++) { 8 if (helper(board, i, j, word, 0)) { 9 return true; 10 } 11 } 12 } 13 return false; 14 } 15 bool helper(vector<vector<char>>& board, int i, int j, string word, int index) { 16 if (i >= board.size() || j >= board[0].size() || i < 0 || j < 0 || board[i][j] == '#') { 17 return false; 18 } 19 if (board[i][j] != word.at(index)) { 20 return false; 21 } 22 if (index == word.length() - 1) { 23 return true; 24 } 25 board[i][j] = '#'; //去重 26 bool next = helper(board, i - 1, j, word, index + 1) || 27 helper(board, i, j + 1, word, index + 1) || 28 helper(board, i + 1, j, word, index + 1) || 29 helper(board, i, j - 1, word, index + 1); 30 board[i][j] = word.at(index); 31 return next; 32 } 33 };
面试题65 滑动窗口的最大值 leetcode239
题意:给出一个数组和一个滑动窗口大小,返回所有滑动窗口的最大值。
思路:利用双端队列。队列头部始终保存当前滑动窗口的最大值。遍历数组,如果当前数值大于队列尾部数值,则删除所有小于当前数字的队列尾部数字,并在尾部加入当前数值,如果当前数值小于队列尾部,则加入尾部。如果当前索引号与队列头部索引号之差大于等于滑动窗口大小k,则删除头部,因为头部已经不属于当前滑动窗口。注意队列中存的是数值的索引号。
1 class Solution { 2 public: 3 vector<int> maxSlidingWindow(vector<int>& nums, int k) { 4 vector<int> max; 5 if (nums.size() < k || k < 1) { 6 return max; 7 } 8 deque<int> dq; 9 for (int i = 0; i < k; i++) { 10 while (!dq.empty() && nums[i] > nums[dq.back()]) { 11 dq.pop_back(); 12 } 13 dq.push_back(i); 14 } 15 max.push_back(nums[dq.front()]); 16 for (int i = k; i < nums.size(); i++) { 17 while (!dq.empty() && nums[i] >= nums[dq.back()]) { 18 dq.pop_back(); 19 } 20 if (!dq.empty() && dq.front() <= i - k) { 21 dq.pop_front(); 22 } 23 dq.push_back(i); 24 max.push_back(nums[dq.front()]); 25 } 26 return max; 27 } 28 };
面试题64 数据流的实时中位数
思路:用一个最大堆和一个最小堆。将数据流平均分成两个部分,最大堆中的数都小于最小堆中的数,当前数据流个数是偶数的时候,中位数则是最大堆与最小堆堆顶元素之和/2;当前数据流个数是奇数的时候,中位数是最大堆的堆顶。所以当数据流个数是偶数时,最大堆与最小堆数据个数相等,反之,最大堆的数据个数比最小堆多一个。
1.当最大堆与最小堆长度相等时,新来一个数据,本应该加入最大堆,但是如果这个数据比最小堆的最小值要大,就要加入最小堆,再将最小堆的堆顶元素pop出来加入最大堆中。
2.当最大堆长度大于最小堆时,新来一个数据,本应该加入最小堆,但是如果这个数据比最大堆最大值要小,则应该加入最大堆,并且将最大堆的最大值pop加入最小堆。
通过上面两步保证最大堆的所有元素小于最小堆。
插入时间复杂度为O(logn),获取中位数的时间复杂度为O(1);
代码使用c++写的,注意priority_queue的pop函数返回void,并不会返回数值。最小堆的实现没有自定义比较器,而是将数值取负号放入最大堆,相当于形成了一个最小堆。这时候用long类型就比较安全,因为最小负整数加负号之后会越界int的。
1 class MedianFinder { 2 priority_queue<long> small, large; 3 public: 4 /** initialize your data structure here. */ 5 MedianFinder() { 6 7 } 8 9 void addNum(int num) { 10 if (small.size() == large.size()) { 11 if (large.size() != 0 && num > -large.top()) { 12 large.push(-num); 13 small.push(-large.top()); 14 large.pop(); 15 } else { 16 small.push(num); 17 } 18 } 19 if (small.size() > large.size()) { 20 if (num < small.top()) { 21 small.push(num); 22 large.push(- small.top()); 23 small.pop(); 24 } else { 25 large.push(- num); 26 } 27 } 28 } 29 30 double findMedian() { 31 return small.size() > large.size() ? small.top() : (small.top() - large.top()) / 2.0; 32 } 33 }; 34 35 /** 36 * Your MedianFinder object will be instantiated and called as such: 37 * MedianFinder obj = new MedianFinder(); 38 * obj.addNum(num); 39 * double param_2 = obj.findMedian(); 40 */
面试题63 找二叉搜索树的第k大的节点
思路:中序遍历二叉搜索树,非递归的。
1 import java.util.*; 2 /* 3 public class TreeNode { 4 int val = 0; 5 TreeNode left = null; 6 TreeNode right = null; 7 8 public TreeNode(int val) { 9 this.val = val; 10 11 } 12 13 } 14 */ 15 public class Solution { 16 TreeNode KthNode(TreeNode pRoot, int k) 17 { 18 if (pRoot == null || k <= 0) { 19 return null; 20 } 21 Stack<TreeNode> s = new Stack<TreeNode>(); 22 s.push(pRoot); 23 while (!s.isEmpty()) { 24 while (pRoot.left != null) { 25 s.push(pRoot.left); 26 pRoot = pRoot.left; 27 } 28 TreeNode cur = s.pop(); 29 if (--k == 0) { 30 return cur; 31 } 32 if (cur.right != null) { 33 pRoot = cur.right; 34 s.push(pRoot); 35 } 36 } 37 return null; 38 } 39 }
面试题62序列化与反序列化二叉树
思路:采用前序遍历来序列化,相对于之前的层序遍历要简单一点。
1 /* 2 public class TreeNode { 3 int val = 0; 4 TreeNode left = null; 5 TreeNode right = null; 6 7 public TreeNode(int val) { 8 this.val = val; 9 10 } 11 12 } 13 */ 14 public class Solution { 15 String Serialize(TreeNode root) { 16 if (root == null) { 17 return "$"; 18 } 19 StringBuilder s = new StringBuilder(""); 20 SerializeHelper(root, s); 21 s.deleteCharAt(s.length() - 1); 22 return s.toString(); 23 } 24 public void SerializeHelper(TreeNode root, StringBuilder s) { 25 if (root == null) { 26 s.append("$").append(","); 27 return; 28 } 29 s.append(String.valueOf(root.val)).append(","); 30 SerializeHelper(root.left, s); 31 SerializeHelper(root.right, s); 32 } 33 int index = 0; 34 TreeNode Deserialize(String str) { 35 if (str == null || str.length() == 0 || str.charAt(0) == '$') { 36 return null; 37 } 38 String[] strs = str.split(","); 39 return DeserializeHelper(strs); 40 } 41 public TreeNode DeserializeHelper(String[] strs) { 42 if (strs[index].equals("$")) { 43 index++; 44 return null; 45 } else { 46 TreeNode node = new TreeNode(Integer.valueOf(strs[index])); 47 index++; 48 node.left = DeserializeHelper(strs); 49 node.right = DeserializeHelper(strs); 50 return node; 51 } 52 } 53 }
面试题61 之字形打印树
思路:双栈,两个栈分别保存偶数层和奇数层的(0开始)。偶数层的节点的下一层从左到右保存到奇数层,奇数层的下一层的节点从右到左保存到偶数层。
注意:之前的做法是用ArrayList.add(0,element)来实现,这个方法源代码是需要内存复制的,效率应该会比较低
1.java
1 import java.util.ArrayList; 2 import java.util.*; 3 /* 4 public class TreeNode { 5 int val = 0; 6 TreeNode left = null; 7 TreeNode right = null; 8 9 public TreeNode(int val) { 10 this.val = val; 11 12 } 13 14 } 15 */ 16 public class Solution { 17 public ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) { 18 if (pRoot == null) { 19 return new ArrayList<>(); 20 } 21 ArrayList<ArrayList<Integer> > ans = new ArrayList<>(); 22 Stack<TreeNode> s1 = new Stack<>(); 23 Stack<TreeNode> s2 = new Stack<>(); 24 boolean curLevel = true; 25 s1.push(pRoot); 26 while (!s1.isEmpty() || !s2.isEmpty()) { 27 ArrayList<Integer> level = new ArrayList<>(); 28 if (curLevel) { 29 while (!s1.isEmpty()) { 30 TreeNode cur = s1.pop(); 31 level.add(cur.val); 32 if (cur.left != null) { 33 s2.push(cur.left); 34 } 35 if (cur.right != null) { 36 s2.push(cur.right); 37 } 38 } 39 } else { 40 while (!s2.isEmpty()) { 41 TreeNode cur = s2.pop(); 42 level.add(cur.val); 43 if (cur.right != null) { 44 s1.push(cur.right); 45 } 46 if (cur.left != null) { 47 s1.push(cur.left); 48 } 49 } 50 } 51 ans.add(level); 52 curLevel = !curLevel; 53 } 54 return ans; 55 } 56 57 }
2.c++
1 /* 2 struct TreeNode { 3 int val; 4 struct TreeNode *left; 5 struct TreeNode *right; 6 TreeNode(int x) : 7 val(x), left(NULL), right(NULL) { 8 } 9 }; 10 */ 11 class Solution { 12 public: 13 vector<vector<int>> Print(TreeNode* pRoot) { 14 vector<vector<int>> result; 15 if (pRoot == NULL) { 16 return result; 17 } 18 stack<TreeNode*> s[2]; 19 int cur = 0; 20 s[cur].push(pRoot); 21 struct TreeNode* node; 22 while (!s[0].empty() || !s[1].empty()) { 23 vector<int> level; 24 while (!s[cur].empty()) { 25 node = s[cur].top(); 26 s[cur].pop(); 27 level.push_back(node->val); 28 if (cur == 0) { 29 if (node->left != NULL) { 30 s[1].push(node->left); 31 } 32 if (node->right != NULL) { 33 s[1].push(node->right); 34 } 35 } 36 if (cur == 1) { 37 if (node->right != NULL) { 38 s[0].push(node->right); 39 } 40 if (node->left != NULL) { 41 s[0].push(node->left); 42 } 43 } 44 } 45 result.push_back(level); 46 cur = (cur + 1) % 2; 47 } 48 return result; 49 } 50 51 };
面试题60 按行打印二叉树
思路:bfs
1 import java.util.ArrayList; 2 3 import java.util.*; 4 /* 5 public class TreeNode { 6 int val = 0; 7 TreeNode left = null; 8 TreeNode right = null; 9 10 public TreeNode(int val) { 11 this.val = val; 12 13 } 14 15 } 16 */ 17 public class Solution { 18 ArrayList<ArrayList<Integer> > Print(TreeNode pRoot) { 19 if (pRoot == null) { 20 return new ArrayList<>(); 21 } 22 ArrayList<ArrayList<Integer> > ans = new ArrayList<>(); 23 Queue<TreeNode> qu = new LinkedList<>(); 24 qu.add(pRoot); 25 while (!qu.isEmpty()) { 26 ArrayList<Integer> level = new ArrayList<>(); 27 int size = qu.size(); 28 for (int i = 0; i < size; i++) { 29 TreeNode cur = qu.poll(); 30 level.add(cur.val); 31 if (cur.left != null) { 32 qu.add(cur.left); 33 } 34 if (cur.right != null) { 35 qu.add(cur.right); 36 } 37 } 38 ans.add(level); 39 } 40 return ans; 41 } 42 43 }
面试题59 判断对称二叉树
思路:判断当前左右子树是否对称,也就是要判断当前左子树的左子树与当前右子树的右子树是否对称,并且当前左子树的右子树与当前右子树的左子树是否对称。
1 /* 2 public class TreeNode { 3 int val = 0; 4 TreeNode left = null; 5 TreeNode right = null; 6 7 public TreeNode(int val) { 8 this.val = val; 9 10 } 11 12 } 13 */ 14 public class Solution { 15 boolean isSymmetrical(TreeNode pRoot) 16 { 17 if (pRoot == null) { 18 return true; 19 } 20 return helper(pRoot.left, pRoot.right); 21 } 22 public boolean helper(TreeNode a, TreeNode b) { 23 if (a == null && b == null) { 24 return true; 25 } 26 if (a == null || b == null) { 27 return false; 28 } 29 if (a.val != b.val) { 30 return false; 31 } 32 return helper(a.left, b.right) && helper(a.right, b.left); 33 } 34 }
面试题58 二叉树中序遍历的下一个节点
思路:
1.当前节点有右子树,则找到右子树的最左子节点
2.当前节点没有右子树,而且当前节点是父节点的左子树,则返回父节点
3.当前节点没有右子树,而且当前节点不是父节点的左子树,则往上遍历直到找到一个节点,他是父节点的左子树,返回该父节点。
1 /* 2 public class TreeLinkNode { 3 int val; 4 TreeLinkNode left = null; 5 TreeLinkNode right = null; 6 TreeLinkNode next = null; 7 8 TreeLinkNode(int val) { 9 this.val = val; 10 } 11 } 12 */ 13 public class Solution { 14 public TreeLinkNode GetNext(TreeLinkNode pNode) 15 { 16 if (pNode == null) { 17 return pNode; 18 } 19 if (pNode.right == null) { 20 if (pNode.next != null) { 21 while (pNode.next != null && pNode.next.left != pNode) { 22 pNode = pNode.next; 23 } 24 return pNode.next; 25 } else { 26 return null; 27 } 28 } 29 TreeLinkNode rightNode = pNode.right; 30 while (rightNode.left != null) { 31 rightNode = rightNode.left; 32 } 33 return rightNode; 34 } 35 }
面试题57 删除链表中的重复节点
题意:在一个排序的链表中,存在重复的结点,请删除该链表中重复的结点,重复的结点不保留,返回链表头指针。 例如,链表1->2->3->3->4->4->5 处理后为 1->2->5
思路:新建dummy 节点,防止头结点被删掉会出现的bug。保存当前节点的值,看下一个节点是否相同值,如果相同则进入循环删除,在循环结束的时候别忘了删除最后一个相同的节点;反之则,当前节点和前一节点都往前移一位。
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 public class Solution { 12 public ListNode deleteDuplication(ListNode pHead) 13 { 14 if(pHead == null || pHead.next == null) { 15 return pHead; 16 } 17 ListNode dummy = new ListNode(0); 18 dummy.next = pHead; 19 ListNode cur = pHead; 20 ListNode pre = dummy; 21 while (cur != null) { 22 int val = cur.val; 23 if (cur.next != null && val == cur.next.val) { 24 while (cur.next != null && val == cur.next.val) { 25 pre.next = cur.next; 26 cur = cur.next; 27 } 28 pre.next = cur.next; 29 cur = cur.next; 30 } else { 31 pre = cur; 32 cur = cur.next; 33 } 34 } 35 return dummy.next; 36 } 37 }
面试题56 找循环链表的环起点
思路:第一步使用快慢指针,直到两指针相遇。要注意的是初始值的选择,与非环链表的判断。第二步让头结点与慢指针同时出发,相遇节点则是环的起点。
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 public class Solution { 12 13 public ListNode EntryNodeOfLoop(ListNode pHead) 14 { 15 if(pHead == null || pHead.next == null) { 16 return null; 17 } 18 ListNode fast = pHead.next.next; 19 ListNode slow = pHead.next; 20 while (fast != slow && fast != null && fast.next != null) { 21 fast = fast.next.next; 22 slow = slow.next; 23 } 24 if (fast != slow) { 25 return null; 26 } 27 while(slow != pHead && slow != null && pHead != null) { 28 slow = slow.next; 29 pHead = pHead.next; 30 } 31 return pHead; 32 } 33 }
面试题55找出字符流中第一个不重复的字符
题意:当从字符流中只读出前两个字符"go"时,第一个只出现一次的字符是"g"。当从该字符流中读出前六个字符“google"时,第一个只出现一次的字符是"l"。
思路:这题与面试题35相似,但是这题是字符流,说明会有大量的字符,但是字符的种类最多是256种,所以依然可以用hash表的方式。不同的是,这里hash表记录的不能是该字符出现的次数了(这样的话就得保存字符流,然后再去遍历像题35一样,很明显是保存不了的),而是记录该字符出现的位置,若没有出现记录为-1,出现多次记录为-2.在查找的时候,只需遍历hash表就可以,由于hash表长度是256所以是O(1)的时间复杂度。空间复杂度也是O(1)。
1 public class Solution { 2 3 int[] occ = null; 4 int pos = 0; 5 public Solution() { 6 occ = new int[256]; 7 for (int i = 0; i < 256; i++) { 8 occ[i] = -1; 9 } 10 } 11 //Insert one char from stringstream 12 public void Insert(char ch) 13 { 14 if (occ[ch] == -1) { 15 occ[ch] = pos; 16 } else if(occ[ch] >= 0) { 17 occ[ch] = -2; 18 } 19 pos++; 20 } 21 //return the first appearence once char in current stringstream 22 public char FirstAppearingOnce() 23 { 24 int min = pos + 1; 25 char ans = '#'; 26 for (int i = 0; i < 256; i++) { 27 if(occ[i] >= 0 && occ[i] < min) { 28 ans = (char)i; 29 min = occ[i]; 30 } 31 } 32 return ans; 33 } 34 }
面试题54 判断字符串是否表示数字
请实现一个函数用来判断字符串是否表示数值(包括整数和小数)。例如,字符串"+100","5e2","-123","3.1416"和"-1E-16"都表示数值。 但是"12e","1a3.14","1.2.3","+-5"和"12e+4.3"都不是。
代码:
1 public class Solution { 2 public boolean isNumeric(char[] str) { 3 if (str == null || str.length == 0) { 4 return false; 5 } 6 int pos = 0; 7 if (str[pos] == '+' || str[pos] == '-') { 8 pos++; 9 } 10 if (pos == str.length) { 11 return false; 12 } 13 pos = scanDigits(str, pos); 14 if (pos == str.length) { 15 return true; 16 } 17 if (str[pos] == '.') { 18 pos++; 19 int temp = pos; 20 pos = scanDigits(str, pos); 21 if (pos == temp) { 22 return false; 23 } 24 if (pos == str.length) { 25 return true; 26 } 27 if (str[pos] == 'e' || str[pos] == 'E') { 28 return isExpo(str, pos + 1); 29 } else { 30 return false; 31 } 32 } else if (str[pos] == 'e' || str[pos] == 'E') { 33 return isExpo(str, pos + 1); 34 } else { 35 return false; 36 } 37 } 38 public int scanDigits(char[] str, int pos) { 39 while (pos != str.length && str[pos] >= '0' && str[pos] <= '9') { 40 pos++; 41 } 42 return pos; 43 } 44 public boolean isExpo(char[] str, int pos) { 45 if (pos == str.length) { 46 return false; 47 } 48 if (str[pos] == '+' || str[pos] == '-') { 49 pos++; 50 } 51 int temp = pos; 52 pos = scanDigits(str, pos); 53 if (pos == temp) { 54 return false; 55 } 56 if (pos != str.length) { 57 return false; 58 } 59 return true; 60 } 61 }
面试题53正则表达式匹配
题意:请实现一个函数用来匹配包括'.'和'*'的正则表达式。模式中的字符'.'表示任意一个字符,而'*'表示它前面的字符可以出现任意次(包含0次)。 在本题中,匹配是指字符串的所有字符匹配整个模式。例如,字符串"aaa"与模式"a.a"和"ab*ac*a"匹配,但是与"aa.a"和"ab*a"均不匹配
回溯法代码:
1 public class Solution { 2 public boolean match(char[] str, char[] pattern) 3 { 4 if (str == null && pattern == null) { 5 return true; 6 } 7 if (str == null || pattern == null) { 8 return false; 9 } 10 return helper(str, 0, pattern, 0); 11 } 12 public boolean helper(char[] str, int strPos, char[] pattern, int pPos) { 13 if (strPos == str.length && pPos == pattern.length) { 14 return true; 15 } 16 if (strPos < str.length && pPos >= pattern.length) { 17 return false; 18 } 19 if (pPos < pattern.length - 1 && pattern[pPos + 1] == '*') { 20 if (strPos < str.length && (str[strPos] == pattern[pPos] || pattern[pPos] == '.')) { 21 return helper(str, strPos, pattern, pPos + 2) || 22 helper(str, strPos + 1, pattern, pPos) || 23 helper(str, strPos + 1, pattern, pPos + 2); 24 } else { 25 return helper(str, strPos, pattern, pPos + 2); 26 } 27 } else if(strPos < str.length && (str[strPos] == pattern[pPos] || pattern[pPos] == '.')) { 28 return helper(str, strPos + 1, pattern, pPos + 1); 29 } else { 30 return false; 31 } 32 } 33 }
dp思路:
dp[i][j]表示s的前i-1个字符与p的前j-1个字符是否匹配,最后返回dp[s.length()][p.length()]即可。 1, If p.charAt(j) == s.charAt(i) : dp[i][j] = dp[i-1][j-1]; 2, If p.charAt(j) == '.' : dp[i][j] = dp[i-1][j-1]; 3, If p.charAt(j) == '*': here are two sub conditions: if p.charAt(j-1) != s.charAt(i) : dp[i][j] = dp[i][j-2] //in this case, a* only counts as empty if p.charAt(i-1) == s.charAt(i) or p.charAt(i-1) == '.': dp[i][j] = dp[i-1][j] //in this case, a* counts as multiple a or dp[i][j] = dp[i][j-1] // in this case, a* counts as single a or dp[i][j] = dp[i][j-2] // in this case, a* counts as empty
dp代码:
1 public class Solution { 2 public boolean isMatch(String s, String p) { 3 if (s == null && p == null) { 4 return true; 5 } 6 if (s == null || p == null) { 7 return false; 8 } 9 int m = s.length(); 10 int n = p.length(); 11 boolean[][] dp = new boolean[m + 1][n + 1]; 12 dp[0][0] = true; 13 //形如“a*b*c*”的形式dp[0][i] = true 14 for (int i = 0; i < n; i++) { 15 if (p.charAt(i) == '*' && dp[0][i - 1]) { 16 dp[0][i + 1] = true; 17 } 18 } 19 for (int i = 0; i < m; i++) { 20 for (int j = 0; j < n; j++) { 21 if (p.charAt(j) == '.' || p.charAt(j) == s.charAt(i)) { 22 dp[i + 1][j + 1] = dp[i][j]; 23 } 24 if (p.charAt(j) == '*') { 25 if (p.charAt(j - 1) != s.charAt(i) && p.charAt(j - 1) != '.') { 26 dp[i + 1][j + 1] = dp[i + 1][j - 1]; 27 } else { 28 dp[i + 1][j + 1] = dp[i + 1][j - 1] || dp[i + 1][j] || dp[i][j + 1]; 29 } 30 } 31 } 32 } 33 return dp[m][n]; 34 } 35 }
面试题52 构建乘积数组,不可使用除法leetcode238
题意:乘积数组中B[i]是原数组A中所有数的乘积,除了A[i]自身.
思路:将B[i]看成是A[i]左边的乘积乘以右边的乘积,先从右往左求出右边每一项的乘积B[i] =B[i + 1] * A[i + 1],再从左往右计算。O(n)时间复杂度
1 public class Solution { 2 public int[] productExceptSelf(int[] nums) { 3 if (nums == null || nums.length == 0) { 4 return null; 5 } 6 int[] answer = new int[nums.length]; 7 answer[nums.length - 1] = 1; 8 for (int i = nums.length - 2; i >= 0; i--) { 9 answer[i] = answer[i + 1] * nums[i + 1]; 10 if (nums[i] == 0) { 11 break; 12 } 13 } 14 int left = 1; 15 for (int i = 0; i < nums.length; i++) { 16 answer[i] = left * answer[i]; 17 left *= nums[i]; 18 } 19 return answer; 20 } 21 }
面试题 51 找出数组中任意一个重复数字,长度为n的数组,数字范围也是0~n-1
思考:不使用额外空间的做法。扫描整个数组,看下标为i的数字m是不是等于i,如果是扫描下一个,如果不是,将它与第m个数字比较,如果想等则有重复,如果不等则交换。
1 public class Solution { 2 // Parameters: 3 // numbers: an array of integers 4 // length: the length of array numbers 5 // duplication: (Output) the duplicated number in the array number,length of duplication array is 1,so using duplication[0] = ? in implementation; 6 // Here duplication like pointor in C/C++, duplication[0] equal *duplication in C/C++ 7 // 这里要特别注意~返回任意重复的一个,赋值duplication[0] 8 // Return value: true if the input is valid, and there are some duplications in the array number 9 // otherwise false 10 public boolean duplicate(int numbers[],int length,int [] duplication) { 11 if (numbers == null || length < 2) { 12 return false; 13 } 14 for (int i = 0; i < length; i++) { 15 while(numbers[i] != i) { 16 int m = numbers[numbers[i]]; 17 if (m == numbers[i]) { 18 duplication[0] = m; 19 return true; 20 } 21 numbers[numbers[i]] = numbers[i]; 22 numbers[i] = m; 23 } 24 } 25 return false; 26 } 27 }
面试题50 找树中两个节点的最低公共祖先
以二叉树为例
1.有父节点的情况,可以转换成求两个交叉链表的第一个公共节点。先从两个给出的节点开始向父节点递归遍历,一直到根节点,分别记录到根节点的长度。然后让长度较大的先移动k步,k是两个长度之差,然后共同移动,相遇时就是最低公共祖先。这个没有考虑节点不在树中的情况
1 public ParentTreeNode lowestCommonAncestorII(ParentTreeNode root, 2 ParentTreeNode A, 3 ParentTreeNode B) { 4 int lenA = getLen(A); 5 int lenB = getLen(B); 6 if (lenA > lenB) { 7 int diff = lenA - lenB; 8 while (diff > 0) { 9 A = A.parent; 10 diff--; 11 } 12 } 13 if (lenB > lenA) { 14 int diff = lenB - lenA; 15 while (diff > 0) { 16 B = B.parent; 17 diff--; 18 } 19 } 20 while (A != B) { 21 A = A.parent; 22 B = B.parent; 23 } 24 return A; 25 } 26 public int getLen(ParentTreeNode A) { 27 int len = 0; 28 while (A != null) { 29 len++; 30 A = A.parent; 31 } 32 return len; 33 }
2.没有父节点的情况,参考lintcode的代码。找到公共祖先则返回,只找到A,则返回A,只找到B,则返回B,找不到返回null。当节点不是全在这棵树中的时候,会返回在的那个节点,这个情况可以与面试官沟通。
1 public TreeNode lowestCommonAncestor(TreeNode root, TreeNode A, TreeNode B) { 2 // write your code here 3 if (root == null) { 4 return null; 5 } 6 if (root == A || root == B) { 7 return root; 8 } 9 TreeNode left = lowestCommonAncestor(root.left, A, B); 10 TreeNode right = lowestCommonAncestor(root.right, A, B); 11 if (left != null && right != null) { 12 return root; 13 } 14 if (left != null) { 15 return left; 16 } 17 if (right != null) { 18 return right; 19 } 20 return null; 21 }
面试题47 不用加减乘除做加法
写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。
思路:转换到位运算,第一步,只做加法不进位,相当于按位异或运算;第二步,计算进位,按位与运算的结果左移一位。直到进位为零。
1 public class Solution { 2 public int Add(int num1,int num2) { 3 while (num2 != 0) { 4 int a = num1 ^ num2; 5 int carry = (num1 & num2) << 1; 6 num1 = a; 7 num2 = carry; 8 } 9 return num1; 10 } 11 }
面试题45 圆圈中最后剩下的数字
题意:
0,1,。。。n -1这n个数字排成一圈,从数字0开始每次从圆圈中删除第m个数字,下一次的开始位置从删除数字的下一位开始,求圆圈中最后剩下的数字。
解析:来自《剑指offer》
代码:
1 public class Solution { 2 public int LastRemaining_Solution(int n, int m) { 3 if (n <1 || m < 1) { 4 return -1; 5 } 6 int last = 0; 7 for (int i = 2; i <= n; i++) { 8 last = (last + m) % i; 9 } 10 return last; 11 } 12 }
面试题44 判断数组是否连续,0可看成任意数字(原题为判断扑克牌是否为顺子,大小王可看成任意数字,用零表示)
思路:先将数组排序,再计算零的个数,如果零的个数大于等于数组中间断的数字的个数,则是连续的。
1 import java.util.Arrays; 2 public class Solution { 3 public boolean isContinuous(int [] numbers) { 4 if (numbers == null || numbers.length == 0) { 5 return false; 6 } 7 Arrays.sort(numbers); 8 int zeroCount = 0; 9 int gapCount = 0; 10 int last = 0; 11 for (int i = 0; i < numbers.length - 1; i++) { 12 if (numbers[i] == 0) { 13 zeroCount++; 14 } else if (numbers[i + 1] == numbers[i]) { 15 return false; 16 } else { 17 gapCount += numbers[i + 1] - numbers[i] - 1; 18 } 19 } 20 return zeroCount >= gapCount ? true : false; 21 } 22 }
面试题42 反转字符串中的单词顺序与旋转字符串前n位
1.反转字符串中的单词顺序
思路:先反转整个字符串,再反转每一个单词。用c++写的时候可以做到O(1) space。reverse()函数用于反转字符串。
1 class Solution { 2 public: 3 string ReverseSentence(string str) { 4 reverse(str.begin(), str.end()); 5 int curPos = 0; 6 int wordLen = 0; 7 int blankCount = 0; 8 for (int i = 0; i < str.size(); i++) { 9 if (str[i] != ' ') { 10 wordLen++; 11 blankCount = 0; 12 if (i == str.size() - 1) { 13 reverse(str.begin() + curPos, str.begin() + curPos + wordLen); 14 } 15 } else if (blankCount == 0){ 16 blankCount++; 17 reverse(str.begin() + curPos, str.begin() + curPos + wordLen); 18 curPos = curPos + wordLen + 1; 19 wordLen = 0; 20 } else { 21 blankCount++; 22 curPos++; 23 } 24 } 25 return str; 26 } 27 };
2.旋转字符串前n位
思路:先分别反转前n位字符和n位之后的字符,再反转整个字符串,调用三次reverse函数即可。
1 class Solution { 2 public: 3 string LeftRotateString(string str, int n) { 4 reverse(str.begin(), str.begin() + n); 5 reverse(str.begin() + n, str.end()); 6 reverse(str.begin(), str.end()); 7 return str; 8 } 9 };
附上自定义的反转字符串函数
1 void myReverse(char *begin, char* end) { 2 if (begin == NULL || end == NULL) { 3 return; 4 } 5 while (begin < end) { 6 char temp = *end; 7 *end = *begin; 8 *begin = temp; 9 begin++; 10 end--; 11 } 12 }
面试题41 对于正数s,求所有和为s的连续正数序列
思路:先确定这个序列最大的可能的数字是中点的值mid = (1 + s) / 2,从1,2两个数字的序列开始,若当前序列和小于s,则向右扩大一位即1,2,3,若当前序列和大于s,则去掉头一位,则变成了2,3;如果当前序列和等于s,则也向右扩大一位,变成了2,3,4.直到右端点到达mid,并且头部和尾部只差一位。
1 import java.util.ArrayList; 2 public class Solution { 3 public ArrayList<ArrayList<Integer> > FindContinuousSequence(int sum) { 4 ArrayList<ArrayList<Integer>> ans = new ArrayList<>(); 5 int mid = (1 + sum) / 2; 6 int left = 1; 7 int right = 2; 8 ArrayList<Integer> seq = new ArrayList<>(); 9 seq.add(left); 10 seq.add(right); 11 int curSum = left + right; 12 while (right <= mid && left < right) { 13 if (curSum == sum) { 14 ans.add(new ArrayList<>(seq)); 15 seq.add(++right); 16 curSum += right; 17 } else if (curSum < sum) { 18 seq.add(++right); 19 curSum += right; 20 } else { 21 seq.remove(0); 22 curSum -= left; 23 left++; 24 } 25 } 26 return ans; 27 } 28 }
面试题40 数组中除了两个数字只出现了一次之外,其他数字都出现了两次
思考:这个跟数组中只有一个数字出现了一次是不一样的,但是要想办法转换为两个子数组,每个子数组满足只有一个数字出现了一次。
先异或原数组的所有数字,得到一个不为零的结果,必然至少有一个二进制位不为零,找出这个结果中二进制位第一个不为零的位,从最低位开始找。
然后根据这个不为零的位将原数组划分为两个数组,第一个子数组这位是1,第二个子数组这位是0。那么两个只出现了一次的数字肯定分开在两个子数组中,并且保证了相同的数字分在同一个数组中。
1 //num1,num2分别为长度为1的数组。传出参数 2 //将num1[0],num2[0]设置为返回结果 3 public class Solution { 4 public void FindNumsAppearOnce(int [] array,int num1[] , int num2[]) { 5 if (array == null || array.length == 0) { 6 return; 7 } 8 int len = array.length; 9 int res = 0; 10 for (int num : array) { 11 res ^= num; 12 } 13 if (res == 0) { 14 return; 15 } 16 int pos = 0; 17 while (((res >> pos) & 1) != 1) { 18 pos++; 19 } 20 int[] nums1 = new int[len]; 21 int[] nums0 = new int[len]; 22 int i = 0; 23 int j = 0; 24 for (int num : array) { 25 if (((num >> pos) & 1) == 1) { 26 nums1[i++] = num; 27 } else { 28 nums0[j++] = num; 29 } 30 } 31 num1[0] = 0; 32 for (int p = 0; p < i; p++) { 33 num1[0] ^= nums1[p]; 34 } 35 num2[0] = 0; 36 for (int p = 0; p < j; p++) { 37 num2[0] ^= nums0[p]; 38 } 39 } 40 }
面试题 39
1.二叉树的深度
思路:递归取左右子树深度大的加一
1 public int getDepth(TreeNode root){ 2 if(root == null) 3 return 0; 4 int rDepth = getDepth(root.right); 5 int lDepth = getDepth(root.left); 6 return rDepth > lDepth ? (rDepth + 1) : (lDepth + 1); 7 }
2.判断是否为平衡二叉树 leetcode 110
思路:如果只是简单的递归,会遍历多次树取得深度。目标是遍历一次树,所以采用后序遍历。
1 /** 2 * Definition for a binary tree node. 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 boolean isBalanced(TreeNode root) { 12 if(root == null) 13 return true; 14 if (helper(root) == -1) { 15 return false; 16 } 17 return true; 18 } 19 public int helper(TreeNode root) { 20 if (root == null) { 21 return 0; 22 } 23 int left = helper(root.left); 24 int right = helper(root.right); 25 if (left == -1 || right == -1) { 26 return -1; 27 } 28 if (left - right <= 1 && left - right >= -1) { 29 return left > right ? (left + 1) : (right + 1); 30 } 31 return -1; 32 } 33 }
面试题38 目标数字在排序数组中出现的次数
给一个升序的数组,以及一个target,找到它在数组中出现的次数。
思路:用两次二分法,先找到第一个位置,再找到最后一个位置
1 public class Solution { 2 /** 3 * @param A an integer array sorted in ascending order 4 * @param target an integer 5 * @return an integer 6 */ 7 public int totalOccurrence(int[] A, int target) { 8 // Write your code here 9 if (A == null || A.length == 0) { 10 return 0; 11 } 12 int start = 0; 13 int end = A.length - 1; 14 int first = -1; 15 while (start <= end) { 16 int mid = start + (end - start) / 2; 17 if (A[mid] == target) { 18 first = mid; 19 end = mid - 1; 20 } else if (A[mid] > target) { 21 end = mid - 1; 22 } else { 23 start = mid + 1; 24 } 25 } 26 if (first == -1) { 27 return 0; 28 } 29 start = first + 1; 30 end = A.length - 1; 31 int last = first; 32 while (start <= end) { 33 int mid = start + (end - start) / 2; 34 if (A[mid] == target) { 35 last = mid; 36 start = mid + 1; 37 } else { 38 end = mid - 1; 39 } 40 } 41 return last - first + 1; 42 } 43 }
面试题37 找出两个交叉链表的第一个公共节点
思路:先求出两个链表的长度,让更长的链表先移动k步(k = len1 - len2或len2 - len1),使得两个链表从同一出发点开始同时移动,直到遇到相同的节点
1 /* 2 public class ListNode { 3 int val; 4 ListNode next = null; 5 6 ListNode(int val) { 7 this.val = val; 8 } 9 }*/ 10 public class Solution { 11 public ListNode FindFirstCommonNode(ListNode pHead1, ListNode pHead2) { 12 if (pHead1 == null || pHead2 == null) { 13 return null; 14 } 15 int len1 = 0; 16 int len2 = 0; 17 ListNode cur = pHead1; 18 while (cur != null) { 19 cur = cur.next; 20 len1++; 21 } 22 cur = pHead2; 23 while (cur != null) { 24 cur = cur.next; 25 len2++; 26 } 27 while (len1 > len2) { 28 pHead1 = pHead1.next; 29 len1--; 30 } 31 while (len2 > len1) { 32 pHead2 = pHead2.next; 33 len2--; 34 } 35 while (pHead1 != pHead2 && pHead1 != null && pHead2 != null) { 36 pHead1 = pHead1.next; 37 pHead2 = pHead2.next; 38 } 39 return pHead1; 40 } 41 }
面试题36 计算数组中逆序对的个数
题意:
链接:https://www.nowcoder.com/practice/96bd6684e04a44eb80e6a68efc0ec6c5?tpId=13&tqId=11188&tPage=2&rp=2&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking 来源:牛客网 在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007 输入描述: 题目保证输入的数组中没有的相同的数字 数据范围: 对于%50的数据,size<=10^4 对于%75的数据,size<=10^5 对于%100的数据,size<=2*10^5 输入例子: 1,2,3,4,5,6,7,0 输出例子: 7
代码:用到了归并排序的思想,在排序的时候计数逆序对的个数
1 public class Solution { 2 int[] temp = null; 3 public int InversePairs(int [] array) { 4 if (array == null || array.length == 0) { 5 return 0; 6 } 7 temp = new int[array.length]; 8 return count(array, 0, array.length - 1); 9 } 10 public int count(int[] arr, int left, int right) { 11 if (left >= right) { 12 return 0; 13 } 14 int mid = left + (right - left) / 2; 15 int leftCount = count(arr, left, mid); 16 int rightCount = count(arr, mid + 1, right); 17 return (((mergeAndCount(arr, left, right) + leftCount) % 1000000007) + rightCount) % 1000000007; 18 } 19 public int mergeAndCount(int[] arr, int left, int right) { 20 for (int i = left; i <= right; i++) { 21 temp[i] = arr[i]; 22 } 23 int count = 0; 24 int mid = left + (right - left) / 2; 25 int leftP = mid; 26 int rightP = right; 27 while (leftP >= left && rightP > mid) { 28 if (temp[leftP] > temp[rightP]) { 29 count += ((rightP - mid) % 1000000007); 30 count %= 1000000007; 31 arr[right--] = temp[leftP--]; 32 } else { 33 arr[right--] = temp[rightP--]; 34 } 35 } 36 while (rightP > mid) { 37 arr[right--] = temp[rightP--]; 38 } 39 return count; 40 } 41 }
面试题35 第一个出现一次的字符 leetcode 387 -- 百度面试题
找到一个字符串中第一个只出现了一次的字符,所有字符都是小写字母的情况
1 public class Solution { 2 public int firstUniqChar(String s) { 3 if (s == null || s.length() == 0) { 4 return -1; 5 } 6 int[] count = new int[26]; 7 for (int i = 0; i < s.length(); i++) { 8 count[s.charAt(i) - 'a']++; 9 } 10 for (int i = 0; i < s.length(); i++) { 11 if (count[s.charAt(i) - 'a'] == 1) { 12 return i; 13 } 14 } 15 return -1; 16 } 17 }
面试题 34 ugly number
质因数只有2,3,5的正数是ugly number
1.判断是否是ugly number的代码
1 public class Solution { 2 public boolean isUgly(int num) { 3 while(num >= 2 && 0 == num % 2) num /= 2; 4 while(num >= 3 && 0 == num % 3) num /= 3; 5 while(num >= 5 && 0 == num % 5) num /= 5; 6 return 1 == num; 7 } 8 }
2.找第n个uglynumber,通用化的条件,质因数是所给数组primes的时候
1 public class Solution { 2 public int nthSuperUglyNumber(int n, int[] primes) { 3 if (primes == null || primes.length == 0) { 4 return 0; 5 } 6 int[] index = new int[primes.length]; 7 int[] ugly = new int[n]; 8 ugly[0] = 1; 9 for (int i = 1; i < n; i++) { 10 ugly[i] = Integer.MAX_VALUE; 11 for (int j = 0; j < primes.length; j++) { 12 ugly[i] = Math.min(ugly[i], primes[j] * ugly[index[j]]); 13 } 14 for (int j = 0; j < primes.length; j++) { 15 if (ugly[i] == primes[j] * ugly[index[j]]) { 16 index[j] ++; 17 } 18 } 19 } 20 return ugly[n - 1]; 21 } 22 }
面试题33 把整数数组排成最小的数
题意:
1 输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。
解法:自定义排序函数,把整数转换成字符串,方便排序,也可以避免大数越界。时间复杂度O(nlogn)
1 import java.util.ArrayList; 2 import java.util.Comparator; 3 import java.lang.StringBuilder; 4 import java.util.Arrays; 5 public class Solution { 6 public String PrintMinNumber(int [] numbers) { 7 if (numbers == null || numbers.length == 0) { 8 return new String(""); 9 } 10 String[] nums = new String[numbers.length]; 11 for (int i = 0; i < numbers.length; i++) { 12 nums[i] = String.valueOf(numbers[i]); 13 } 14 Arrays.sort(nums, new MyComparator()); 15 StringBuilder ans = new StringBuilder(); 16 for (String num : nums) { 17 ans.append(num); 18 } 19 return ans.toString(); 20 } 21 public class MyComparator implements Comparator<String> { 22 public int compare(String s1, String s2) { 23 return (s1 + s2).compareTo(s2 + s1); 24 } 25 } 26 }
面试题30 找出数组中最小的k个数,利用priorityqueue,时间复杂度为O(nlogk)
注意数组长度小于k,或k = 0的情况
1 import java.util.ArrayList; 2 import java.util.Comparator; 3 import java.util.PriorityQueue; 4 public class Solution { 5 public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) { 6 if(input == null || input.length < k || k <= 0) { 7 return new ArrayList<Integer>(); 8 } 9 ArrayList<Integer> ans = new ArrayList<Integer>(); 10 PriorityQueue<Integer> pq = new PriorityQueue<>(k, new MyComparator()); 11 for (int num : input) { 12 if (pq.size() < k) { 13 pq.offer(num); 14 } else { 15 if (pq.peek() <= num) { 16 continue; 17 } else { 18 pq.poll(); 19 pq.offer(num); 20 } 21 } 22 } 23 while (pq.size() != 0) { 24 ans.add(pq.poll()); 25 } 26 return ans; 27 } 28 29 } 30 class MyComparator implements Comparator<Integer> { 31 public int compare(Integer t1, Integer t2) { 32 return t2 - t1; 33 } 34 }
面试题 29 找出数组中出现次数超过一半的元素 leetcode 169,区别在于不一定存在,所以要验证
1 public class Solution { 2 public int MoreThanHalfNum_Solution(int [] array) { 3 if (array == null || array.length == 0) { 4 return 0; 5 } 6 int count = 0; 7 int major = array[0]; 8 for (int num : array) { 9 if (count == 0) { 10 count ++; 11 major = num; 12 } else if (major == num){ 13 count ++; 14 } else { 15 count --; 16 } 17 } 18 count = 0; 19 for (int num : array) { 20 if (num == major) { 21 count ++; 22 } 23 } 24 return count > array.length / 2 ? major : 0; 25 } 26 }
面试题 28 求出字符串的所有排列
题意:
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。 可能有重复字符。
思路:将每个位置的字符串与其后面位置的字符串进行交换,再把后面位置的看成一个整体,求后面位置所有排列。注意字符串有重复,需要去重,然后就是要按照字母排序输出。
1 class Solution { 2 public: 3 vector<string> Permutation(string str) { 4 vector<string> ans; 5 helper(ans, str, 0); 6 return ans; 7 } 8 void helper(vector<string>& ans, string str, int k) { 9 if (k == str.size() - 1) { 10 ans.push_back(str); 11 return; 12 } 13 set<char> set; 14 sort(str.begin() + k, str.end()); 15 for (int i = k; i < str.size(); i++) { 16 //去重 17 if (set.find(str[i]) == set.end()) { 18 set.insert(str[i]); 19 char temp = str[i]; 20 str[i] = str[k]; 21 str[k] = temp; 22 //swap(str[i], str[k]); 23 helper(ans, str, k + 1); 24 temp = str[i]; 25 str[i] = str[k]; 26 str[k] = temp; 27 //swap(str[i], str[k]); 28 } 29 } 30 } 31 };
面试题 27 二叉搜索树转换为双向链表
1 /** 2 public class TreeNode { 3 int val = 0; 4 TreeNode left = null; 5 TreeNode right = null; 6 7 public TreeNode(int val) { 8 this.val = val; 9 10 } 11 12 } 13 */ 14 public class Solution { 15 TreeNode lastNode = null; 16 public TreeNode Convert(TreeNode pRootOfTree) { 17 if (pRootOfTree == null) { 18 return pRootOfTree; 19 } 20 convertNode(pRootOfTree); 21 while (lastNode != null && lastNode.left != null) { 22 lastNode = lastNode.left; 23 } 24 return lastNode; 25 } 26 public void convertNode(TreeNode pRootOfTree) { 27 if (pRootOfTree.left != null) { 28 convertNode(pRootOfTree.left); 29 } 30 pRootOfTree.left = lastNode; 31 if (lastNode != null) { 32 lastNode.right = pRootOfTree; 33 } 34 lastNode = pRootOfTree; 35 if (pRootOfTree.right != null) { 36 convertNode(pRootOfTree.right); 37 } 38 } 39 }
面试题22 判断栈的压入弹出序列是否匹配
如果下一个弹出数字刚好是栈顶,则直接弹出。否则将未压入的入栈序列继续压入栈,直到栈顶元素为下一个弹出元素。如果入栈序列全部压入了,仍没有找到匹配的栈顶元素,则两个序列不匹配。
最后辅助栈是空的,因为都弹出了。
1 import java.util.ArrayList; 2 import java.util.Stack; 3 public class Solution { 4 public boolean IsPopOrder(int [] pushA,int [] popA) { 5 if ((pushA == null && popA == null) || (pushA.length == 0 && popA.length == 0)) { 6 return true; 7 } 8 if ((pushA == null || popA == null) || (pushA.length == 0 || popA.length == 0)) { 9 return false; 10 } 11 int pushPos = 0; 12 int popPos = 0; 13 int len = pushA.length; 14 Stack<Integer> s = new Stack<>(); 15 while (pushPos <= len && popPos < len) { 16 if (s.isEmpty()) { 17 s.push(pushA[pushPos++]); 18 } 19 if (s.peek() != popA[popPos]) { 20 if (pushPos == len) { 21 return false; 22 } 23 s.push(pushA[pushPos++]); 24 } else { 25 popPos++; 26 s.pop(); 27 } 28 } 29 if (s.size() == 0) { 30 return true; 31 } 32 return false; 33 } 34 }
面试题 21 leetcode 155
题意:
1 Design a stack that supports push, pop, top, and retrieving the minimum element in constant time. 2 3 push(x) -- Push element x onto stack. 4 pop() -- Removes the element on top of the stack. 5 top() -- Get the top element. 6 getMin() -- Retrieve the minimum element in the stack. 7 Example: 8 MinStack minStack = new MinStack(); 9 minStack.push(-2); 10 minStack.push(0); 11 minStack.push(-3); 12 minStack.getMin(); --> Returns -3. 13 minStack.pop(); 14 minStack.top(); --> Returns 0. 15 minStack.getMin(); --> Returns -2.
新建一个辅助栈,来存放当前最小值。当有pop操作时,判断当前pop是否等于当前最小值,等于的话就将辅助栈的栈顶元素pop。注意在判断等于的时候不能用==,而要用equals。
1 public class MinStack { 2 Stack<Integer> s; 3 Stack<Integer> min; 4 /** initialize your data structure here. */ 5 public MinStack() { 6 s = new Stack<Integer>(); 7 min = new Stack<Integer>(); 8 } 9 10 public void push(int x) { 11 s.push(x); 12 if (min.isEmpty() || x <= min.peek()) { 13 min.push(x); 14 } 15 } 16 17 public void pop() { 18 if (!s.isEmpty()) { 19 if(s.pop().equals(min.peek())) { 20 min.pop(); 21 } 22 } 23 } 24 25 public int top() { 26 if (s.isEmpty()) { 27 return 0; 28 } 29 return s.peek(); 30 } 31 32 public int getMin() { 33 if (min.isEmpty()) { 34 return 0; 35 } 36 return min.peek(); 37 } 38 } 39 40 /** 41 * Your MinStack object will be instantiated and called as such: 42 * MinStack obj = new MinStack(); 43 * obj.push(x); 44 * obj.pop(); 45 * int param_3 = obj.top(); 46 * int param_4 = obj.getMin(); 47 */
面试题 20 螺旋打印数组 leetcode 54
看起来很简单,注意边界条件
1 public class Solution { 2 public List<Integer> spiralOrder(int[][] matrix) { 3 List<Integer> spiralList = new ArrayList<>(); 4 if (matrix == null || matrix.length == 0 || matrix[0].length == 0) { 5 return spiralList; 6 } 7 8 int m = matrix.length; 9 int n = matrix[0].length; 10 int min = Math.min(m, n); 11 12 for (int i = 0; i < (min + 1) / 2; i++) { 13 //top 14 for (int top = i; top < n - i; top++) { 15 spiralList.add(matrix[i][top]); 16 } 17 //right 18 for (int right = i + 1; right < m - i; right++) { 19 spiralList.add(matrix[right][n - i - 1]); 20 } 21 if (i == m / 2 || i == n / 2) { 22 break; 23 } 24 //bottom 25 for (int bottom = n - i - 2; bottom >= i; bottom--) { 26 spiralList.add(matrix[m - i - 1][bottom]); 27 } 28 //left 29 for (int left = m - i - 2; left > i; left--) { 30 spiralList.add(matrix[left][i]); 31 } 32 } 33 34 return spiralList; 35 } 36 }
面试题 18 判断B是否为A的子树
遍历树A,以A的每一个节点为根节点与B比较
1 /** 2 public class TreeNode { 3 int val = 0; 4 TreeNode left = null; 5 TreeNode right = null; 6 7 public TreeNode(int val) { 8 this.val = val; 9 10 } 11 12 } 13 */ 14 public class Solution { 15 public boolean HasSubtree(TreeNode root1,TreeNode root2) { 16 if (root1 == null || root2 == null) { 17 return false; 18 } 19 if (helper(root1, root2)) { 20 return true; 21 } 22 if (root1.left != null) { 23 if (HasSubtree(root1.left, root2)) { 24 return true; 25 } 26 } 27 if (root1.right != null) { 28 if (HasSubtree(root1.right, root2)) { 29 return true; 30 } 31 } 32 return false; 33 } 34 public boolean helper(TreeNode root1,TreeNode root2) { 35 if (root2 == null) { 36 return true; 37 } 38 if (root1 == null) { 39 return false; 40 } 41 if (root1.val != root2.val) { 42 return false; 43 } 44 return helper(root1.left, root2.left) && helper(root1.right, root2.right); 45 } 46 }
面试题 17 合并两个排序链表 LeetCode 21
代码:
1 /** 2 * Definition for singly-linked list. 3 * public class ListNode { 4 * int val; 5 * ListNode next; 6 * ListNode(int x) { val = x; } 7 * } 8 */ 9 public class Solution { 10 public ListNode mergeTwoLists(ListNode l1, ListNode l2) { 11 if(l1 == null) 12 return l2; 13 if(l2 == null) 14 return l1; 15 ListNode ln; 16 if(l1.val<=l2.val){ 17 ln = l1; 18 l1 = l1.next; 19 }else{ 20 ln = l2; 21 l2 = l2.next; 22 } 23 ListNode mergeHead = ln; 24 while(l1!=null&l2!=null){ 25 if(l1.val<=l2.val){ 26 mergeHead.next = l1; 27 l1 = l1.next; 28 }else{ 29 mergeHead.next = l2; 30 l2 = l2.next; 31 } 32 mergeHead = mergeHead.next; 33 } 34 if(l1!=null) 35 mergeHead.next = l1; 36 if(l2!=null) 37 mergeHead.next = l2; 38 return ln; 39 } 40 }
面试题15 输入一个链表,输出该链表中倒数第k个结点
代码:用两指针的方法,第一个指针先走k-1步,然后第二个指针从头开始与第一个指针同步前进,第一个指针到达链表尾部的时候第二个指针就是倒数第k个节点。
1 /* 2 public class ListNode { 3 int val; 4 ListNode next = null; 5 6 ListNode(int val) { 7 this.val = val; 8 } 9 }*/ 10 public class Solution { 11 public ListNode FindKthToTail(ListNode head,int k) { 12 if (head == null || k == 0) { 13 return null; 14 } 15 ListNode cur = head; 16 for (int i = 0; i < k - 1; i++) { 17 if (cur.next != null) { 18 cur = cur.next; 19 } else { 20 return null; 21 } 22 } 23 ListNode result = head; 24 while (cur != null && cur.next != null) { 25 cur = cur.next; 26 result = result.next; 27 } 28 return result; 29 } 30 }
注意边界条件,k == 0或k大于链表长度的时候。
面试题14 调整数组顺序使奇数位于偶数前面
1.不需要保持原来的相对顺序,使用两指针做交换即可(书上是这样的)
1 public class Solution { 2 public void reOrderArray(int [] array) { 3 if (array == null || array.length == 0) { 4 return; 5 } 6 int left = 0; 7 int right = array.length - 1; 8 while (left < right) { 9 while (left < right && (array[left] & 1) == 1) { 10 left ++; 11 } 12 while (left < right && (array[right] & 1) != 1) { 13 right --; 14 } 15 int temp = array[left]; 16 array[left] = array[right]; 17 array[right] = temp; 18 left ++; 19 right --; 20 } 21 } 22 }
2.需要保持原来的顺序,新建一个数组,将原数组复制给它,遍历新数组,将奇数从前往后填入原数组,再倒序遍历新数组,将偶数从后往前填入新数组。
1 public class Solution { 2 public void reOrderArray(int [] array) { 3 if (array == null || array.length == 0) { 4 return; 5 } 6 int len = array.length; 7 int[] arr = new int[len]; 8 System.arraycopy(array, 0, arr, 0, len); 9 int left = 0; 10 int right = len - 1; 11 for (int i = 0; i < len; i++) { 12 if ((arr[i] & 1) != 0) { 13 array[left++] = arr[i]; 14 } 15 } 16 for (int i = len - 1; i >= 0; i--) { 17 if ((arr[i] & 1) == 0) { 18 array[right--] = arr[i]; 19 } 20 } 21 } 22 }
面试题13:leetcode 237. Delete Node in a Linked List (O(1))
题意:
Write a function to delete a node (except the tail) in a singly linked list, given only access to that node. Supposed the linked list is 1 -> 2 -> 3 -> 4 and you are given the third node with value 3, the linked list should become 1 -> 2 -> 4 after calling your function.
解法:
O(1)时间复杂度的解法,将当前要删除节点的的下一个节点的值赋值给当前节点,再删除下一个节点。
1 /** 2 * Definition for singly-linked list. 3 * public class ListNode { 4 * int val; 5 * ListNode next; 6 * ListNode(int x) { val = x; } 7 * } 8 */ 9 public class Solution { 10 public void deleteNode(ListNode node) { 11 node.val = node.next.val; 12 node.next = node.next.next; 13 } 14 }
剑指offer中考虑了待删除节点是尾节点的情况,我觉得如果是尾节点的话,直接将当前节点置为null就可以了。
面试题11:leetcode 50. Pow(x, n)
1.当指数是正数的时候,可以有如下算法
利用乘方的性质:
a^n = a ^ (n / 2) * a ^ (n / 2), n 为偶数;
a^n = a * a ^ (n - 1 / 2) * a ^ (n - 1 / 2), n 为奇数;
代码如下:
1 public double powWithUnsigned(double x, int n) { 2 if (n == 0) { 3 return 1; 4 } 5 double result = powWithUnsigned(x, n >> 1); 6 result *= result; 7 if ((n & 1) == 1) { 8 result *= x; 9 } 10 return result; 11 }
2.再考虑如果指数不一定为正数的情况,可以取指数的绝对值,然后对结果取倒数。有一个例外当指数是最小的负数Integer.MIN_VALUE,不能对其取绝对值,因为最小的负数和最大的正数绝对值差1,所以先把指数当做Integer.MAX_VALUE,最后再乘一次。
1 //位运算符加括号 2 //n = -2147483648 3 public class Solution { 4 public double myPow(double x, int n) { 5 boolean min = false; 6 boolean negative = false; 7 if (n == Integer.MIN_VALUE) { 8 min = true; 9 negative = true; 10 n = Integer.MAX_VALUE; 11 } else if (n < 0) { 12 n = - n; 13 negative = true; 14 } 15 double ret = powWithUnsigned(x, n); 16 if (min) { 17 ret *= x; 18 } 19 return negative ? 1 / ret : ret; 20 } 21 public double powWithUnsigned(double x, int n) { 22 if (n == 0) { 23 return 1; 24 } 25 double result = powWithUnsigned(x, n >> 1); 26 result *= result; 27 if ((n & 1) == 1) { 28 result *= x; 29 } 30 return result; 31 } 32 }
面试题10:leetcode 191. Number of 1 Bits
题意:
Write a function that takes an unsigned integer and returns the number of ’1' bits it has (also known as the Hamming weight).
For example, the 32-bit integer ’11' has binary representation 00000000000000000000000000001011
, so the function should return 3.
解法:
1.普通解法:利用1的移位相与
1 public class Solution { 2 // you need to treat n as an unsigned value 3 public int hammingWeight(int n) { 4 int flag = 1; 5 int count = 0; 6 while (flag != 0) { 7 if ((n & flag) != 0) { 8 count++; 9 } 10 flag = flag << 1; 11 } 12 return count; 13 } 14 }
2.能给面试官打来惊喜的解法,p80 利用减1的性质:减1其实是把最右边的一个1之后的位取反,再与原数字相与就只剩下了前面的位。
1 public class Solution { 2 // you need to treat n as an unsigned value 3 public int hammingWeight(int n) { 4 int count = 0; 5 while (n != 0) { 6 count++; 7 n = n & (n - 1); 8 } 9 return count; 10 } 11 }