这道题的核心是: 翻转就是将所有节点的左右子树都交换
就是要遍历到每个节点, 然后交换
遍历二叉树:
前序遍历
中序遍历
后序遍历
层序遍历
遍历二叉树的四种遍历方法都可以
public class _226_翻转二叉树 {
// public TreeNode invertTree(TreeNode root) {
// if (root == null) return null;
//
// TreeNode tmp = root.left;
// root.left = root.right;
// root.right = tmp;
//
// invertTree(root.left);
// invertTree(root.right);
//
// return root;
// }
// public TreeNode invertTree(TreeNode root) {
// if (root == null) return null;
//
// invertTree(root.left);
// invertTree(root.right);
//
// TreeNode tmp = root.left;
// root.left = root.right;
// root.right = tmp;
//
// return root;
// }
// public TreeNode invertTree(TreeNode root) {
// if (root == null) return null;
//
// invertTree(root.left);
//
// TreeNode tmp = root.left;
// root.left = root.right;
// root.right = tmp;
//
// invertTree(root.left); // 注意
//
// return root;
// }
public TreeNode invertTree(TreeNode root) {
if (root == null) return null;
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
TreeNode node = queue.poll();
TreeNode tmp = node.left;
node.left = node.right;
node.right = tmp;
if (node.left != null) {
queue.offer(node.left);
}
if (node.right != null) {
queue.offer(node.right);
}
}
return root;
}
}
搞两个栈
public class _232_用栈实现队列 {
private Stack<Integer> inStack;
private Stack<Integer> outStack;
/** Initialize your data structure here. */
public _232_用栈实现队列() {
inStack = new Stack<>();
outStack = new Stack<>();
}
/** Push element x to the back of queue. */
public void push(int x) {
inStack.push(x);
}
/** Removes the element from in front of queue and returns that element. */
public int pop() {
checkOutStack();
return outStack.pop();
}
/** Get the front element. */
public int peek() {
checkOutStack();
return outStack.peek();
}
/** Returns whether the queue is empty. */
public boolean empty() {
return inStack.isEmpty() && outStack.isEmpty();
}
private void checkOutStack() {
if (outStack.isEmpty()) {
while (!inStack.isEmpty()) {
outStack.push(inStack.pop());
}
}
}
}
([{}]) [](){}
要拿 { 和 } 匹配很容易想到用栈这种数据结构, 因为栈的后进先出特点, 导致后进的 { 会先弹出栈来和 } 匹配, 这正好符合题目的要求.
所以做法是: 遍历到左括号入栈, 遍历到右括号, 弹出栈顶元素匹配, 如果最后栈是空的说明有效. 因为有可能有这种特殊情况 ([])[ ([{
另外每次遍历到右括号都要检查栈是否为空, 如果栈是空的说明没有左括号与右括号匹配了. 对应这种情况: [{}]]
public class _20_有效的括号 {
private HashMap<Character, Character> map = new HashMap<>();
public _20_有效的括号() {
map.put('(', ')');
map.put('{', '}');
map.put('[', ']');
}
public boolean isValid(String s) {
Stack<Character> stack = new Stack<>();
int len = s.length();
for (int i = 0; i < len; i++) {
char c = s.charAt(i);
if (map.containsKey(c)) {
stack.push(c);
} else {
if (stack.isEmpty()) return false;
if (c != map.get(stack.pop())) return false;
}
}
return stack.isEmpty();
}
public boolean isValid1(String s) {
Stack<Character> stack = new Stack<>();
int len = s.length();
for (int i = 0; i < len; i++) {
char c = s.charAt(i);
if (c == '(' || c == '{' || c == '[') {
stack.push(c);
} else {
if (stack.isEmpty()) return false; // 注意特殊情况
char left = stack.pop();
if (left == '(' && c != ')') return false;
if (left == '{' && c != '}') return false;
if (left == '[' && c != ']') return false;
}
}
return stack.isEmpty();
}
public boolean isValid2(String s) {
while (s.contains("{}")
|| s.contains("[]")
|| s.contains("()")) {
s = s.replace("{}", "");
s = s.replace("()", "");
s = s.replace("[]", "");
}
return s.isEmpty();
}
}
因为本题中的链表是排序链表, 所以重复元素只可能在这个元素的后继位置, 如果重复多次的话, 只能是 1 -> 1 -> 1 -> 2 -> 3 -> 5 这种, 不可能重复元素1在5的后面出现, 因为是排好序的链表.
public class _83_删除排序链表中的重复元素 {
public ListNode deleteDuplicates(ListNode head) {
ListNode headNode = head;
if (headNode == null) return null;
while (headNode.next != null) {
if (headNode.val == headNode.next.val) {
headNode.next = headNode.next.next;
} else {
headNode = headNode.next;
}
}
return head;
}
}
判断一个链表是否有环, 用快慢指针的思想.
一个慢指针指向第一个节点, 一个快指针指向第二个节点, 慢指针一次next一步, 快指针一次next2步, 这样可以保证如果有环的话一定可以追到.
为什么快指针不一次next3步呢?
因为next3步有可能会错过慢指针
public class _141_环形链表 {
public boolean hasCycle(ListNode head) {
if (head == null || head.next == null) return false;
ListNode slow = head;
ListNode fast = head.next;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) return true;
}
return false;
}
}
public class _203_移除链表元素 {
public ListNode removeElements(ListNode head, int val) {
if (head == null) return null;
while (head.val == val) {
head = head.next;
if (head == null) {
return null;
}
}
ListNode headNode = head;
while (headNode.next != null) {
if (headNode.next.val == val) {
headNode.next = headNode.next.next;
} else {
headNode = headNode.next;
}
}
return head;
}
}
示例:
输入: 5->4->3->2->1->NULL
输出: 1->2->3->4->5->NULL
递归解法的关键是搞清楚递归的方法的作用是什么, 然后充分利用他的作用去做事情.
比如给定一个节点4, 这个函数会返回什么? 返回 1->2->3->4->NULL
然后在head.next.next = head;
4->5
head.next = null;
5->null
最后做一些边界条件作为递归的出口.
if (head == null || head.next == null) return head;
public ListNode reverseList(ListNode head) {
if (head == null || head.next == null) return head;
ListNode newHead = reverseList(head.next);
head.next.next = head;
head.next = null;
return newHead;
}
因为只能拿到head, 所以只能从head开始一个一个串起来.
public ListNode reverseList(ListNode head) {
if (head == null || head.next == null) return head;
ListNode newHead = null;
while (head != null) {
ListNode tmp = head.next;
head.next = newHead;
newHead = head;
head = tmp;
}
return newHead;
}
题目特别说明了要删除的节点是非末尾的节点, 并且我们只能拿到要求被删除的节点.
平常删除链表的节点的做法都是利用要被删除的节点的上一个节点然后node.next = node.next.next
现在拿不到他的上一个节点, 所以用他下一个节点的值覆盖当前节点的值, 然后让他的next指向他的next的next 来巧妙的达到目的.
node.val = node.next.val;
node.next = node.next.next;
class Solution {
public void deleteNode(ListNode node) {
node.val = node.next.val;
node.next = node.next.next;
}
}
快慢指针思想
class Solution {
public ListNode middleNode(ListNode head) {
if (head.next == null) return head;
ListNode slow = head;
ListNode fast = head.next;
while (fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (fast == null) {
return slow;
}
if (fast.next == null) {
slow = slow.next;
return slow;
}
}
return slow.next;
}
}