咸鱼了好久,leetcode还是要坚持认真做的啊。
文章目录
- 7.4
- [32. 最长有效括号](https://leetcode-cn.com/problems/longest-valid-parentheses/)
- [74. 搜索二维矩阵](https://leetcode-cn.com/problems/search-a-2d-matrix/)
- [240. 搜索二维矩阵 II](https://leetcode-cn.com/problems/search-a-2d-matrix-ii/)
- [8. 字符串转换整数 (atoi)](https://leetcode-cn.com/problems/string-to-integer-atoi/)
- [718. 最长重复子数组](https://leetcode-cn.com/problems/maximum-length-of-repeated-subarray/)
- [24. 两两交换链表中的节点](https://leetcode-cn.com/problems/swap-nodes-in-pairs/)
- 7.13
- [349. 两个数组的交集](https://leetcode-cn.com/problems/intersection-of-two-arrays/)
- [350. 两个数组的交集 II](https://leetcode-cn.com/problems/intersection-of-two-arrays-ii/)
- [面试题 01.01. 判定字符是否唯一](https://leetcode-cn.com/problems/is-unique-lcci/)
- [面试题 01.02. 判定是否互为字符重排](https://leetcode-cn.com/problems/check-permutation-lcci/)
- [面试题 01.03. URL化](https://leetcode-cn.com/problems/string-to-url-lcci/)
- [面试题 01.04. 回文排列](https://leetcode-cn.com/problems/palindrome-permutation-lcci/)
- [面试题 01.06. 字符串压缩](https://leetcode-cn.com/problems/compress-string-lcci/)
- [面试题 01.07. 旋转矩阵](https://leetcode-cn.com/problems/rotate-matrix-lcci/)
- [面试题 01.08. 零矩阵](https://leetcode-cn.com/problems/zero-matrix-lcci/)
- [面试题 01.09. 字符串轮转](https://leetcode-cn.com/problems/string-rotation-lcci/)
- [面试题 02.02. 返回倒数第 k 个节点](https://leetcode-cn.com/problems/kth-node-from-end-of-list-lcci/)
- [面试题 02.03. 删除中间节点](https://leetcode-cn.com/problems/delete-middle-node-lcci/)
- [面试题 02.04. 分割链表](https://leetcode-cn.com/problems/partition-list-lcci/)
- [面试题 02.06. 回文链表](https://leetcode-cn.com/problems/palindrome-linked-list-lcci/)
- [面试题 03.02. 栈的最小值](https://leetcode-cn.com/problems/min-stack-lcci/)
- 7.14
- [面试题 03.04. 化栈为队](https://leetcode-cn.com/problems/implement-queue-using-stacks-lcci/)
- [面试题 03.05. 栈排序](https://leetcode-cn.com/problems/sort-of-stacks-lcci/)
- [面试题 04.02. 最小高度树](https://leetcode-cn.com/problems/minimum-height-tree-lcci/)
- [面试题 04.03. 特定深度节点链表](https://leetcode-cn.com/problems/list-of-depth-lcci/)
- [面试题 04.04. 检查平衡性](https://leetcode-cn.com/problems/check-balance-lcci/)
- [面试题 04.05. 合法二叉搜索树](https://leetcode-cn.com/problems/legal-binary-search-tree-lcci/)
- [面试题 04.06. 后继者](https://leetcode-cn.com/problems/successor-lcci/)
- [面试题 04.08. 首个共同祖先](https://leetcode-cn.com/problems/first-common-ancestor-lcci/)
- [面试题 04.10. 检查子树](https://leetcode-cn.com/problems/check-subtree-lcci/)
- [面试题 04.12. 求和路径](https://leetcode-cn.com/problems/paths-with-sum-lcci/)
- [面试题 08.01. 三步问题](https://leetcode-cn.com/problems/three-steps-problem-lcci/)
- 7.15
- [面试题68 - I. 二叉搜索树的最近公共祖先](https://leetcode-cn.com/problems/er-cha-sou-suo-shu-de-zui-jin-gong-gong-zu-xian-lcof/)
- [面试题 08.03. 魔术索引](https://leetcode-cn.com/problems/magic-index-lcci/)
- [面试题 08.04. 幂集](https://leetcode-cn.com/problems/power-set-lcci/)
- [面试题 08.07. 无重复字符串的排列组合](https://leetcode-cn.com/problems/permutation-i-lcci/)
- [面试题 08.08. 有重复字符串的排列组合](https://leetcode-cn.com/problems/permutation-ii-lcci/)
- [面试题 08.09. 括号](https://leetcode-cn.com/problems/bracket-lcci/)
- [面试题 08.10. 颜色填充](https://leetcode-cn.com/problems/color-fill-lcci/)
- [面试题 08.12. 八皇后](https://leetcode-cn.com/problems/eight-queens-lcci/)
- [面试题 10.01. 合并排序的数组](https://leetcode-cn.com/problems/sorted-merge-lcci/)
7.4
32. 最长有效括号
难度困难
利用动态规划的想法来做。dp,i表示以第i个字符为最后一个字符所能构成的字符串的长度。
class Solution {
public int longestValidParentheses(String s) {
if(s.length() == 0){
return 0;
}
int[] dp = new int[s.length()];
int len = 0;
for(int i = 1;i < s.length();i++){
if(s.charAt(i) == ')'){
if(s.charAt(i - 1) == '('){
dp[i] = 2 + ((i >= 2) ? dp[i - 2] : 0);
}else if(i - dp[i - 1] > 0 && s.charAt(i - dp[i - 1] - 1) == '('){
//需要注意的是这里。在连续的((()))类型之前,还可能有有效的括号(),所以还要加上之前的长度
dp[i] = dp[i - 1] + ((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2;
}
}
len = Math.max(len, dp[i]);
}
return len;
}
}
74. 搜索二维矩阵
难度中等
因为矩阵是有序的,并且上下行之间的大小也是有序的。所以可以把整个数组看成一个有序的一维数组,只需要做一个一维数组到二维数组的转换就可以用二分查找的方法来解决问题了。
class Solution {
public boolean searchMatrix(int[][] matrix, int target) {
if(matrix.length == 0 || matrix[0].length == 0){
return false;
}
int m = matrix.length;
int n = matrix[0].length;
int begin = 0;
int end = m * n - 1;
int mid;
while(begin <= end){
mid = begin + (end - begin) / 2;
int num = matrix[mid / n][mid % n];
if(num == target){
return true;
}else if(num > target){
end = mid - 1;
}else{
begin = mid + 1;
}
}
return false;
}
}
240. 搜索二维矩阵 II
难度中等
通过观察矩阵,我们发现从矩阵的右上角出发,往下走是比它大的元素,往左走是比它小的元素。所以利用这个特性我们可以找到解决的方法。终止条件就是X或Y指针越界。
class Solution {
public boolean searchMatrix(int[][] matrix, int target) {
if(matrix.length == 0 || matrix[0].length == 0){
return false;
}
int indexY = matrix[0].length - 1;
int indexX = 0;
while(indexY >= 0 && indexX < matrix.length){
if(matrix[indexX][indexY] == target){
return true;
}else if(matrix[indexX][indexY] > target){
indexY--;
}else{
indexX++;
}
}
return false;
}
}
8. 字符串转换整数 (atoi)
难度中等
这个题比较难的地方算是对于溢出情况判断及处理了吧。参照了题解,对溢出的处理方法是将最大值减去现在遍历到的字符,再除以10.如果我们现在的数值比它大的话说明会有溢出,就需要返回int的最大值或最小值(具体是哪一个由符号位flag决定)。
class Solution {
public int myAtoi(String str) {
String s = str.trim();
if(s.length() == 0){
return 0;
}
int ans = 0;
boolean flag = false;
int index = 0;
if(s.charAt(index) == '-'){
flag = true;
index++;
}
if(index == 0 && s.charAt(index) == '+'){
index++;
}
while(index < s.length() && Character.isDigit(s.charAt(index))){
if(ans > (Integer.MAX_VALUE - Integer.parseInt(s.charAt(index) + "")) / 10){
return flag ? Integer.MIN_VALUE : Integer.MAX_VALUE;
}
ans = ans * 10 + Integer.parseInt(s.charAt(index) + "");
index++;
}
return flag ? -ans : ans;
}
}
718. 最长重复子数组
难度中等
主要是利用了动态规划的思想,利用二维数组来进行标记。为了方便之后的处理,将二维数组的长度设置为对应数组长度加1。
class Solution {
public int findLength(int[] A, int[] B) {
int m = A.length;
int n = B.length;
int[][] dp = new int[m + 1][n + 1];
int max = -1;
for(int i = m - 1;i >= 0;i--){
for(int j = n - 1;j >= 0;j--){
dp[i][j] = A[i] == B[j] ? dp[i + 1][j + 1] + 1 : 0;
max = Math.max(dp[i][j], max);
}
}
return max;
}
}
24. 两两交换链表中的节点
难度中等
利用递归的思想可以快速解决问题。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode swapPairs(ListNode head) {
if(head == null || head.next == null){
return head;
}
ListNode firstNode = head;
ListNode secondNode = head.next;
firstNode.next = swapPairs(secondNode.next);
secondNode.next = firstNode;
return secondNode;
}
}
当然,利用迭代也能解决问题。我之前做的时候就是因为没有利用好哨兵节点,所以返回值一直不对。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode swapPairs(ListNode head) {
//主要是要利用哨兵节点
ListNode dummy = new ListNode(-1);
dummy.next = head;
ListNode prevNode = dummy;
while (head != null && head.next != null) {
//赋值
ListNode firstNode = head;
ListNode secondNode = head.next;
//交换前后节点位置
prevNode.next = secondNode;
firstNode.next = secondNode.next;
secondNode.next = firstNode;
//更新前继节点
prevNode = firstNode;
//更新先操作节点,一次是操作两个,所以选择firstNode的next(此时firstNode已经在secondNode后面了)
head = firstNode.next;
}
return dummy.next;
}
}
7.13
好久没做了,又开始吧
349. 两个数组的交集
难度简单
class Solution {
public int[] intersection(int[] nums1, int[] nums2) {
Set<Integer> set = new HashSet<>();
for(int num : nums1){
set.add(num);
}
List<Integer> ans = new LinkedList<>();
for(int num : nums2){
if(set.contains(num) && !ans.contains(num)){
ans.add(num);
}
}
int[] re = new int[ans.size()];
for(int i = 0;i < ans.size();i++){
re[i] = ans.get(i);
}
return re;
}
}
350. 两个数组的交集 II
难度简单
class Solution {
public int[] intersect(int[] nums1, int[] nums2) {
Map<Integer, Integer> map1 = getMap(nums1);
Map<Integer, Integer> map2 = getMap(nums2);
List<Integer> list = new ArrayList<>();
for(Map.Entry<Integer, Integer> entry : map1.entrySet()){
if(map2.containsKey(entry.getKey())){
int count = Math.min(entry.getValue(), map2.get(entry.getKey()));
for(int i = 0;i < count;i++){
list.add(entry.getKey());
}
}
}
int[] ans = new int[list.size()];
for(int i = 0;i < list.size();i++){
ans[i] = list.get(i);
}
return ans;
}
public Map<Integer, Integer> getMap(int[] nums){
Map<Integer, Integer> map = new HashMap<>();
for(int num : nums){
if(map.containsKey(num)){
map.put(num, map.get(num) + 1);
}else{
map.put(num, 1);
}
}
return map;
}
}
面试题 01.01. 判定字符是否唯一
难度简单
利用set的性质可以实现
class Solution {
public boolean isUnique(String astr) {
Set<Character> set = new HashSet<>();
for(char c : astr.toCharArray()){
if(set.contains(c)){
return false;
}else{
set.add(c);
}
}
return true;
}
}
不使用额外的数据结构的话可以用indexOf实现
class Solution {
public boolean isUnique(String astr) {
for(char c : astr.toCharArray()){
if(astr.indexOf(c) != astr.lastIndexOf(c)){
return false;
}
}
return true;
}
}
面试题 01.02. 判定是否互为字符重排
难度简单
在确定出现的字符只可能是小写字母的情况下可以这样做。如果还会出现其他的字符,则需要遍历两个字符串记录下对应的字符以及出现次数。在比较两个Map是否相等
class Solution {
public boolean CheckPermutation(String s1, String s2) {
int[] count = new int[26];
for(char c : s1.toCharArray()){
count[c - 97]++;
}
for(char c : s2.toCharArray()){
count[c - 97]--;
}
for(int num : count){
if(num != 0){
return false;
}
}
return true;
}
}
面试题 01.03. URL化
难度简单
第一次遍历找出空格数量确定最终数组的长度,第二步遍历进行替换
class Solution {
public String replaceSpaces(String S, int length) {
int count = 0;
for(int i = 0;i < length;i++){
char c = S.charAt(i);
if(c == ' '){
count++;
}
}
char[] arr = new char[length + count * 2];
int index = arr.length - 1;
for(int i = length - 1;i >= 0;i--){
char c = S.charAt(i);
if(c == ' '){
arr[index--] = '0';
arr[index--] = '2';
arr[index--] = '%';
}else{
arr[index--] = c;
}
}
return String.valueOf(arr);
}
}
面试题 01.04. 回文排列
难度简单
判断字符出现的次数是不是偶数,如果字符串长度为奇数,则允许出现一次奇数个的字符。
class Solution {
public boolean canPermutePalindrome(String s) {
Map<Character, Integer> map = new HashMap<>();
for(char c : s.toCharArray()){
if(map.containsKey(c)){
map.put(c, map.get(c) + 1);
}else{
map.put(c, 1);
}
}
boolean flag = false;//标识是否已经遍历过奇数个的字符了
boolean isEven = s.length() % 2 == 0;//标识是否可以包含一个奇数个的字符
for(Map.Entry<Character, Integer> entry : map.entrySet()){
if(entry.getValue() % 2 != 0){
if(flag || isEven){
return false;
}else{
flag = true;
}
}
}
return true;
}
}
面试题 01.06. 字符串压缩
难度简单
需要注意的是在最后要加上对应的次数
class Solution {
public String compressString(String S) {
if(S.length() == 0){
return S;
}
char c = S.charAt(0);
int count = 1;
StringBuilder sb = new StringBuilder();
sb.append(c);
for(int i = 1;i < S.length();i++){
if(c == S.charAt(i)){
count++;
}else{
sb.append(count + "");
c = S.charAt(i);
sb.append(c);
count = 1;
}
}
sb.append(count);
if(S.length() > sb.length()){
return sb.toString();
}else{
return S;
}
}
}
面试题 01.07. 旋转矩阵
难度中等
除了之前提到的先沿对角线对折交换,再沿中间列交换的做法,还能直接进行交换。
先记录下起始旋转位置,再进行四个角的交换。
class Solution {
public void rotate(int[][] matrix) {
if(matrix.length == 0){
return ;
}
int a;
int n=matrix.length;
for (int i = 0; i < n / 2 ; i++) {
for (int j = i; j < n - i - 1; j++) {
a = matrix[i][j];
matrix[i][j] = matrix[n - j - 1][i];
matrix[n - j - 1][i] = matrix[n - i - 1][n - j - 1];
matrix[n - i - 1][n - j - 1] = matrix[j][n - i - 1];
matrix[j][n - i - 1] = a;
}
}
}
}
面试题 01.08. 零矩阵
难度中等
用一个布尔数组来辅助标识改变的0值
class Solution {
public void setZeroes(int[][] matrix) {
if(matrix.length == 0){
return ;
}
boolean[][] isZero = new boolean[matrix.length][matrix[0].length];
for(int i = 0;i < matrix.length;i++){
for(int j = 0;j < matrix[0].length;j++){
if(matrix[i][j] == 0 && !isZero[i][j]){
changeZero(matrix, i, j, isZero);
}
}
}
}
public void changeZero(int[][] matrix, int i, int j, boolean[][] isZero){
for(int m = 0;m < matrix[0].length;m++){
if(matrix[i][m] != 0){
isZero[i][m] = true;
matrix[i][m] = 0;
}
}
for(int m = 0;m < matrix.length;m++){
if(matrix[m][j] != 0){
isZero[m][j] = true;
matrix[m][j] = 0;
}
}
}
}
面试题 01.09. 字符串轮转
难度简单
最开始想复杂了。这个题仔细想想,如果说s1可以翻转成s2,那么s2一定包含在s1+s1之中。因为轮转最多也就轮转到与最开始一样的状态。
class Solution {
public boolean isFlipedString(String s1, String s2) {
if(s1.length() != s2.length()){
return false;
}
String str = s1 + s1;
int index = str.indexOf(s2);
return index != -1;
}
}
面试题 02.02. 返回倒数第 k 个节点
难度简单
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public int kthToLast(ListNode head, int k) {
if(head == null){
return -1;
}
ListNode node = head;
int i = 0;
while(i < k){
node = node.next;
i++;
}
while(node != null){
node = node.next;
head = head.next;
}
return head.val;
}
}
面试题 02.03. 删除中间节点
难度简单
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public void deleteNode(ListNode node) {
node.val = node.next.val;
node.next = node.next.next;
}
}
面试题 02.04. 分割链表
难度中等
多用几个指针。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode partition(ListNode head, int x) {
if(head == null){
return head;
}
ListNode headL = new ListNode();
ListNode headR = new ListNode();
ListNode hL = headL;
ListNode hR = headR;
ListNode node = head;
while(node != null){
if(node.val >= x){
headR.next = node;
headR = node;
}else{
headL.next = node;
headL = node;
}
node = node.next;
}
headR.next = null;
headL.next = hR.next;
return hL.next;
}
}
面试题 02.06. 回文链表
难度简单
为了不适用额外的存储空间,可以将后半部分的链表进行反转,再和开头链表进行比较就可以了
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public boolean isPalindrome(ListNode head) {
if(head == null){
return true;
}
int length = 0;
ListNode node = head;
while(node != null){
length++;
node = node.next;
}
node = head;
int k = 0;
while(k < length / 2){
node = node.next;
k++;
}
if(length % 2 != 0){
node = node.next;
}
ListNode pre = null;
while(node != null){
ListNode next = node.next;
node.next = pre;
pre = node;
node = next;
}
node = head;
while(pre != null){
if(pre.val == node.val){
pre = pre.next;
node = node.next;
}else{
return false;
}
}
return true;
}
}
面试题 03.02. 栈的最小值
难度简单
用两个栈来实现,一个放置元素,另一个放置对应位置的最小值。
class MinStack {
Stack<Integer> stack;
Stack<Integer> myStask;
/** initialize your data structure here. */
public MinStack() {
stack = new Stack<>();
myStask = new Stack<>();
}
public void push(int x) {
stack.push(x);
if(myStask.isEmpty() || x <= myStask.peek()){
myStask.push(x);
}else{
myStask.push(myStask.peek());
}
}
public void pop() {
stack.pop();
myStask.pop();
}
public int top() {
return stack.peek();
}
public int getMin() {
return myStask.peek();
}
}
/**
* Your MinStack object will be instantiated and called as such:
* MinStack obj = new MinStack();
* obj.push(x);
* obj.pop();
* int param_3 = obj.top();
* int param_4 = obj.getMin();
*/
7.14
面试题 03.04. 化栈为队
难度简单
用两个栈就能模拟出队列的效果
class MyQueue {
Stack<Integer> pushStack;
Stack<Integer> popStack;
/** Initialize your data structure here. */
public MyQueue() {
pushStack = new Stack<>();
popStack = new Stack<>();
}
/** Push element x to the back of queue. */
public void push(int x) {
while(!popStack.isEmpty()){
pushStack.push(popStack.pop());
}
pushStack.push(x);
}
/** Removes the element from in front of queue and returns that element. */
public int pop() {
while(!pushStack.isEmpty()){
popStack.push(pushStack.pop());
}
return popStack.pop();
}
/** Get the front element. */
public int peek() {
while(!pushStack.isEmpty()){
popStack.push(pushStack.pop());
}
return popStack.peek();
}
/** Returns whether the queue is empty. */
public boolean empty() {
return popStack.isEmpty() && pushStack.isEmpty();
}
}
/**
* Your MyQueue object will be instantiated and called as such:
* MyQueue obj = new MyQueue();
* obj.push(x);
* int param_2 = obj.pop();
* int param_3 = obj.peek();
* boolean param_4 = obj.empty();
*/
面试题 03.05. 栈排序
难度中等
用两个栈,一个栈用来辅助排序。
class SortedStack {
Stack<Integer> minStack;
Stack<Integer> tempStack;
public SortedStack() {
minStack = new Stack<>();
tempStack = new Stack<>();
}
public void push(int val) {
if(minStack.isEmpty()){
minStack.push(val);
}else{
while(!minStack.isEmpty() && minStack.peek() < val){
tempStack.push(minStack.pop());
}
minStack.push(val);
while(!tempStack.isEmpty()){
minStack.push(tempStack.pop());
}
}
}
public void pop() {
if(!minStack.isEmpty()){
minStack.pop();
}
}
public int peek() {
if(!minStack.isEmpty()){
return minStack.peek();
}
return -1;
}
public boolean isEmpty() {
return minStack.isEmpty();
}
}
/**
* Your SortedStack object will be instantiated and called as such:
* SortedStack obj = new SortedStack();
* obj.push(val);
* obj.pop();
* int param_3 = obj.peek();
* boolean param_4 = obj.isEmpty();
*/
面试题 04.02. 最小高度树
难度简单
为了能够满足二叉搜索树的定义,所以每次取根节点时都应该从中间位置取出。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode sortedArrayToBST(int[] nums) {
return sortedArrayToBST(nums, 0, nums.length - 1);
}
public TreeNode sortedArrayToBST(int[] nums, int begin, int end){
if(begin > end){
return null;
}
int mid = begin + (end - begin) / 2;
TreeNode root = new TreeNode(nums[mid]);
root.left = sortedArrayToBST(nums, begin, mid - 1);
root.right = sortedArrayToBST(nums, mid + 1, end);
return root;
}
}
面试题 04.03. 特定深度节点链表
难度中等
一个树的广度优先遍历算法
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode[] listOfDepth(TreeNode tree) {
if(tree == null){
return new ListNode[0];
}
List<ListNode> list = new LinkedList<>();
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(tree);
int cur = 1;
int next = 0;
while(!queue.isEmpty()){
ListNode temp = new ListNode();
ListNode head = temp;
while(cur > 0){
TreeNode node = queue.poll();
temp.next = new ListNode(node.val);
temp = temp.next;
if(node.left != null){
next++;
queue.offer(node.left);
}
if(node.right != null){
next++;
queue.offer(node.right);
}
cur--;
}
list.add(head.next);
cur = next;
next = 0;
}
return list.toArray(new ListNode[list.size()]);
}
}
面试题 04.04. 检查平衡性
难度简单
需要一个辅助函数来确定树的高度
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public boolean isBalanced(TreeNode root) {
if(root == null){
return true;
}
int left = getHeight(root.left);
int right = getHeight(root.right);
if(Math.abs(left - right) > 1){
return false;
}else{
return isBalanced(root.left) && isBalanced(root.right);
}
}
public int getHeight(TreeNode root){
if(root == null){
return 0;
}
int left = getHeight(root.left);
int right = getHeight(root.right);
return Math.max(left, right) + 1;
}
}
面试题 04.05. 合法二叉搜索树
难度中等
中序遍历二叉搜索树得到的是一个从小到大排列的序列。利用这个性质就能实现。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public boolean isValidBST(TreeNode root) {
return inOrder(root, new Stack<Integer>());
}
public boolean inOrder(TreeNode root, Stack<Integer> stack){
if(root == null){
return true;
}
boolean left = inOrder(root.left, stack);
if(!stack.isEmpty() && root.val <= stack.peek()){
return false;
}else{
stack.push(root.val);
}
boolean right = inOrder(root.right, stack);
return left && right;
}
}
面试题 04.06. 后继者
难度中等
用一个栈来构建中序遍历。之后再在这个栈中找到对应的节点。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode inorderSuccessor(TreeNode root, TreeNode p) {
if(root == null){
return null;
}
Stack<TreeNode> stack = new Stack<>();
inorderSuccessor(root, p, stack);
TreeNode ans = null;
TreeNode node;
while(!stack.isEmpty()){
node = stack.pop();
if(node == p){
break;
}
ans = node;
}
return ans;
}
public void inorderSuccessor(TreeNode root, TreeNode p, Stack<TreeNode> stack){
if(root == null){
return ;
}
inorderSuccessor(root.left, p, stack);
stack.push(root);
inorderSuccessor(root.right, p, stack);
}
}
面试题 04.08. 首个共同祖先
难度中等
递归解决
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == null || root == p || root == q){
return root;
}
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
if(left != null && right != null){
return root;
}else if(right == null){
return left;
}else{
return right;
}
}
}
面试题 04.10. 检查子树
难度中等
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public boolean checkSubTree(TreeNode t1, TreeNode t2) {
if(t1 == null && t2 != null){
return false;
}
if(t1.val != t2.val){
return checkSubTree(t1.left, t2) || checkSubTree(t1.right, t2);
}else
return isSameTree(t1, t2) || (checkSubTree(t1.left, t2) || checkSubTree(t1.right, t2));
}
public boolean isSameTree(TreeNode t1, TreeNode t2){
if(t1 == null && t2 == null){
return true;
}else if(t1 == null || t2 == null || t1.val != t2.val){
return false;
}
return isSameTree(t1.left, t2.left) && isSameTree(t1.right, t2.right);
}
}
面试题 04.12. 求和路径
难度中等
因为它可以从任何节点开始,这个部分做了好久都没做出来。最好参照了题解才做出来。
主要是增加一个考虑从各个节点为起点的情况。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public int pathSum(TreeNode root, int sum) {
if(root == null) return 0;
return dfs(root, sum);
}
//考虑每个节点为起点的可能性,统计该节点下的所有的结果
private int dfs(TreeNode root, int sum){
int l = 0, m = 0,r = 0;
m = dfs_count(root, sum);
if(root.left != null) l = dfs(root.left, sum);
if(root.right != null) r = dfs(root.right, sum);
return l + m + r;
}
//以一个节点为起点的结果数
private int dfs_count(TreeNode root, int sum){
int l = 0, m = 0, r = 0;
if(sum - root.val == 0){
m = 1;
}
if(root.left != null){
l = dfs_count(root.left, sum - root.val);
}
if(root.right != null){
r = dfs_count(root.right, sum - root.val);
}
return l + m + r;
}
}
面试题 08.01. 三步问题
难度简单
动态规划问题。注意三个相加时可能会溢出,所以先两个相加取模后再加第三个数值。
class Solution {
public int waysToStep(int n) {
if(n <= 2){
return n;
}else if(n == 3){
return 4;
}
int[] dp = new int[n + 1];
dp[1] = 1;
dp[2] = 2;
dp[3] = 4;
for(int i = 4;i <= n;i++){
dp[i] = ((dp[i - 1] + dp[i - 2]) % 1000000007 + dp[i - 3]) % 1000000007;
}
return dp[n];
}
}
7.15
面试题68 - I. 二叉搜索树的最近公共祖先
难度简单
和之前做过的求树的最近公共祖先一个类型
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode(int x) { val = x; }
* }
*/
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if(root == null || root == p || root == q){
return root;
}
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
if(left == null){
return right;
}else if(right == null){
return left;
}else{
return root;
}
}
}
面试题 08.03. 魔术索引
难度简单
一次循环即可。
class Solution {
public int findMagicIndex(int[] nums) {
for(int i = 0;i < nums.length;i++){
if(nums[i] == i){
return i;
}
}
return -1;
}
}
面试题 08.04. 幂集
难度中等
因为不包含重复的元素,所以还是比较好解决的。就是一个求子集的问题。
class Solution {
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> ans = new LinkedList<>();
ans.add(new LinkedList<Integer>());
getSubsets(nums, 0, new LinkedList<Integer>(), ans);
return ans;
}
public void getSubsets(int[] nums, int index, List<Integer> list, List<List<Integer>> ans){
for(int i = index;i < nums.length;i++){
list.add(nums[i]);
ans.add(new LinkedList<Integer>(list));
getSubsets(nums, i + 1, list, ans);
list.remove(list.size() - 1);
}
}
}
面试题 08.07. 无重复字符串的排列组合
难度中等
固定第一位,交换后面的元素位置;之后固定前两个位置,交换后面的元素位置。依此递推下去就行
class Solution {
public String[] permutation(String S) {
List<String> list = new LinkedList<>();
getPermutation(S.toCharArray(), 0, list);
return list.toArray(new String[list.size()]);
}
public void getPermutation(char[] arr, int begin, List<String> list){
if(begin == arr.length){
list.add(String.valueOf(arr));
}
for(int i = begin;i < arr.length;i++){
swap(arr, i, begin);
getPermutation(arr, begin + 1, list);
swap(arr, i, begin);
}
}
public void swap(char[] arr, int i, int j){
char c = arr[i];
arr[i] = arr[j];
arr[j] = c;
}
}
面试题 08.08. 有重复字符串的排列组合
难度中等
因为有重复的字符,所以改用set来存储String,这样就能避免添加重复的字符串。
class Solution {
public String[] permutation(String S) {
Set<String> set = new HashSet<>();
getPermutation(S.toCharArray(), 0, set);
return set.toArray(new String[set.size()]);
}
public void getPermutation(char[] arr, int begin, Set<String> set){
if(begin == arr.length){
set.add(String.valueOf(arr));
}
for(int i = begin;i < arr.length;i++){
swap(arr, i, begin);
getPermutation(arr, begin + 1, set);
swap(arr, i, begin);
}
}
public void swap(char[] arr, int i, int j){
char c = arr[i];
arr[i] = arr[j];
arr[j] = c;
}
}
面试题 08.09. 括号
难度中等
生成合格的括号,用递归的方法先判断能不能加左括号,之后再判断能不能加上右括号。
class Solution {
public List<String> generateParenthesis(int n) {
List<String> ans = new LinkedList<>();
generateParenthesis(ans, n, 0, 0, new StringBuilder());
return ans;
}
public void generateParenthesis(List<String> ans, int n, int left, int right, StringBuilder sb)
{
if(left == n && right == n){
ans.add(sb.toString());
return ;
}
if(left < n){
sb.append("(");
generateParenthesis(ans, n, left + 1, right, sb);
sb.deleteCharAt(sb.length() - 1);
}
if(right < left){
sb.append(")");
generateParenthesis(ans, n, left, right + 1, sb);
sb.deleteCharAt(sb.length() - 1);
}
}
}
面试题 08.10. 颜色填充
难度简单
这就是一个简单的深度遍历问题。其中需要注意的是当新旧颜色相同时的情况,可以直接返回。
class Solution {
int oldColor = -1;
public int[][] floodFill(int[][] image, int sr, int sc, int newColor) {
oldColor = image[sr][sc];
if(oldColor == newColor){
return image;
}
dfs(image, sr, sc, newColor);
return image;
}
public void dfs(int[][] image, int i, int j, int newColor){
if(i >= 0 && i < image.length && j >= 0 && j < image[0].length && image[i][j] == oldColor){
image[i][j] = newColor;
dfs(image, i + 1, j, newColor);
dfs(image, i - 1, j, newColor);
dfs(image, i, j + 1, newColor);
dfs(image, i, j - 1, newColor);
}
}
}
面试题 08.12. 八皇后
难度困难
这个题没什么头绪,只能去参看题解。
尝试在每个位置都加上Q,如果矛盾的话就继续找下一个位置,如果满足条件就继续取尝试找答案。
class Solution {
public List<List<String>> solveNQueens(int n) {
List<List<String>> ans = new ArrayList<>();
char[][] nums = new char[n][n];
for (int i = 0; i < n; i++) {
Arrays.fill(nums[i], '.');
}
backtrack(nums,0, ans);
return ans;
}
private void backtrack(char[][] nums, int currRow, List<List<String>> ans) {
int len = nums.length;
if (len == currRow) {//遍历结束
List<String> path = new ArrayList<>();
for (int i = 0; i < len; i++) {
path.add(String.valueOf(nums[i]));
}
ans.add(path’);
return;
}
for (int col = 0; col < len; col++) {
//判断这个位置是否合适
boolean isok = true;
for (int row = 0; row < currRow; row++) {
//竖的有Q
if (nums[row][col] == 'Q') {
isok = false;
break;
}
//判断对角线,只用判断相邻的对角线节点是否为Q就行了,因为是一步一步添加Q的
if (col + (currRow - row) < len && nums[row][col + (currRow - row)] == 'Q') {
isok = false;
break;
}
if (col - (currRow - row) >= 0 && nums[row][col - (currRow - row)] == 'Q') {
isok = false;
break;
}
}
//不满足条件
if (!isok) {
continue;
}
//满足条件
nums[currRow][col] = 'Q';
backtrack(nums, currRow + 1, ans);
nums[currRow][col] = '.';
}
}
}
面试题 10.01. 合并排序的数组
难度简单
从数组的末端进行合并
class Solution {
public void merge(int[] A, int m, int[] B, int n) {
int indexA = m - 1;
int indexB = n - 1;
int index = m + n - 1;
while(indexA >= 0 || indexB >= 0){
int numA = indexA >= 0 ? A[indexA] : Integer.MIN_VALUE;
int numB = indexB >= 0 ? B[indexB] : Integer.MIN_VALUE;
if(numA > numB){
A[index] = numA;
indexA--;
}else{
A[index] = numB;
indexB--;
}
index--;
}
}
}