第一章 、常见运算
//取模运算:余数,可以用作循环
5%2 = 1
5/2 = 2
i++ //输出后再加
++i //加完再输出
-
class LRUCache { private HashMap<Integer, Node> map; private DoubleList cache; // 最大容量 private int cap; public LRUCache(int capacity) { this.cap = capacity; map = new HashMap<>(); cache = new DoubleList(); } public int get(int key) { if (!map.containsKey(key)) return -1; int val = map.get(key).val; // 利用 put 方法把该数据提前 put(key, val); return val; } public void put(int key, int val) { // 先把新节点 x 做出来 Node x = new Node(key, val); if (map.containsKey(key)) { // 删除旧的节点,新的插到头部 cache.remove(map.get(key)); cache.addFirst(x); // 更新 map 中对应的数据 map.put(key, x); } else { //满了则删除链表最后一个数据 if (cap == cache.size()) { Node last = cache.removeLast(); map.remove(last.key); } // 直接添加到头部 cache.addFirst(x); map.put(key, x); } } static class DoubleList { //确保链表不为空,头结点为first.next private Node first = new Node(0, 0); private Node end = new Node(0, 0); private int size; public DoubleList() { first.next = end; end.prev = first; size = 0; } // 在链表头部添加节点 x,时间 O(1) public void addFirst(Node x) { Node temp = first.next; first.next = x;//第二个才是插入的头结点 x.next = temp; temp.prev = x; x.prev = first; size++; } // 删除链表中的 x 节点(x 一定存在) // 由于是双链表且给的是目标 Node 节点,时间 O(1) public void remove(Node x) { x.next.prev = x.prev; x.prev.next = x.next; size--; } // 删除链表中最后一个节点,并返回该节点,时间 O(1) public Node removeLast() { Node last = end.prev; remove(last); return last; } // 返回链表长度,时间 O(1) public int size() { return size; } } static class Node { public int key, val; public Node next, prev; public Node(int k, int v) { this.key = k; this.val = v; } } }
public class Solution {
public ListNode detectCycle(ListNode head) {
//快慢指针
//每次移动两步,有环则一定会在环的 某一个位置 超越慢指针
ListNode fast = head, slow = head;
while(true){
if(fast == null || fast.next == null) return null;
fast = fast.next.next;
slow = slow.next;
if(fast == slow) break;
}
// 假如fast == head --> 有环
fast = head;
while(fast != slow){
fast = fast.next;
slow = slow.next;
}
return fast;
}
}
-
-
public ListNode getIntersectionNode(ListNode headA, ListNode headB) { //双指针:a和b以相同的速度走过相同的路程,如果最后一段路程相同则他们必然在共同路程的入口相遇; ListNode a = headA; ListNode b = headB; while(a != b){ a = a == null ? headB : a.next; b = b == null ? headA : b.next; } return a; }
class Solution { public ListNode reverseList(ListNode head) { //双指针实现 ListNode per = null; ListNode cur = head; ListNode temp = null; while(cur != null){ temp = cur.next; //改变指向,局部反转 cur.next = per; //指针向前移动 per = cur; cur = temp; } return per; } }
public boolean hasCycle(ListNode head) { //快慢指针 ListNode f = head, s = head; while(true){ if(f == null || f.next == null) return false; f = f.next.next; s = s.next; if(f == s) return true; } }
public ListNode mergeTwoLists(ListNode l1, ListNode l2) { //迭代法 ListNode head = new ListNode(-1); //维护一个指针,一直指向链表的最后一个位置 ListNode per = head; while(l1 != null && l2 != null){ if(l1.val >= l2.val){ per.next = l2; l2 = l2.next; }else{ per.next = l1; l1 = l1.next; } //指针后移一位 per = per.next; } //链表末尾指向不为空的链表 per.next = l1 == null ? l2 : l1; return head.next; }
public ListNode mergeTwoLists(ListNode l1, ListNode l2) { //递归解法,较小的节点指向其余待合并元素 if(l1 == null) return l2; if(l2 == null) return l1; if(l1.val > l2.val){ l2.next = mergeTwoLists(l1, l2.next); return l2; }else{ l1.next = mergeTwoLists(l1.next, l2); return l1; } }
二、树、二叉树、二叉搜索树
1、 解题模板
/* 基本的二叉树节点 */ class TreeNode { int val; TreeNode left, right; } void traverse(TreeNode root) { traverse(root.left); traverse(root.right); }
/* 基本的 N 叉树节点 */ class TreeNode { int val; TreeNode[] children; } void traverse(TreeNode root) { for (TreeNode child : root.children) traverse(child); }
2、力扣刷题
-
class Solution { public List<Integer> inorderTraversal(TreeNode root) { List<Integer> res = new ArrayList(); order(root, res); return res; } private void order(TreeNode root, List<Integer> res){ //中序遍历 做根右 if(null == root) return; order(root.left, res); res.add(root.val); order(root.right, res); } }
-
class Solution { public List<Integer> postorder(Node root) { List<Integer> res = new ArrayList(); inOrder(root, res); return res; } private void inOrder(Node node, List<Integer> res){ if(null == node) return; //遍历孩子节点 for(Node n : node.children){ inOrder(n, res); } res.add(node.val); } }
-
class Solution { public List<List<Integer>> levelOrder(Node root) { List<List<Integer>> res = new ArrayList(); if(root == null) return res; inOrder(root, res, 0); return res; } private void inOrder(Node root,List<List<Integer>> res, int level){ if(root == null) return; //遍历当前层 if(res.size() < level + 1){ res.add(new ArrayList()); //防止下标越界 } res.get(level).add(root.val); for(Node n : root.children){ inOrder(n, res, level + 1);//注意不能是level++ } } }
-
class Solution { public TreeNode invertTree(TreeNode root) { //递归终止条件 if(null == root) return root; //当前层逻辑:位置交换 TreeNode temp = root.left; root.left = root.right; root.right = temp; //下探一层 invertTree(root.left); invertTree(root.right); return root; } }
class Solution { long per = Long.MIN_VALUE;//注意 public boolean isValidBST(TreeNode root) { //根据中序遍历的特点(左,根,右)--》前一个节点小于后一个节点 if(root == null) return true; //左节点 if(!isValidBST(root.left)){ return false; } //根节点处理,左要小于根 if(per >= root.val){ return false; } //记录前节点 per = root.val; //右节点 return isValidBST(root.right); } }
class Solution { //深度优先搜索 public int maxDepth(TreeNode root) { if(root == null) return 0; return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1; } }
三、栈、队列
1、解题模板
2、力扣刷题
四、哈希表、映射、集合
1、解题模板
2、力扣刷题
五、堆、二叉堆、图
1、解题模板
2、力扣刷题
第三章 常用算法
一、递归
二、分治、回溯
三、深度优先和广度优先搜索
1、解题模板
-
void dfs(TreeNode root) { if (root == null) { return; } dfs(root.left); dfs(root.right); }
-
void bfs(TreeNode root) { Queue<TreeNode> queue = new ArrayDeque<>(); queue.add(root); while (!queue.isEmpty()) { TreeNode node = queue.poll(); // Java 的 pop 写作 poll() if (node.left != null) { queue.add(node.left); } if (node.right != null) { queue.add(node.right); } } } // 二叉树的层序遍历 void bfs(TreeNode root) { Queue<TreeNode> queue = new ArrayDeque<>(); queue.add(root); while (!queue.isEmpty()) { //在每一层遍历开始前,先记录队列中的结点数量 nn(这一层的结点数量),然后一口气处理完这一层的结点 int n = queue.size(); for (int i = 0; i < n; i++) { // 变量 i 无实际意义,只是为了循环 n 次 TreeNode node = queue.poll(); if (node.left != null) { queue.add(node.left); } if (node.right != null) { queue.add(node.right); } } } }
2、力扣刷题
-
class Solution { public List<List<Integer>> levelOrder(TreeNode root) { List<List<Integer>> res = new ArrayList(); return inOrder(root, 0, res); } private List<List<Integer>> inOrder(TreeNode root, int level, List<List<Integer>> res){ if (null == root) return res; if(res.size() <= level){ res.add(new ArrayList()); } res.get(level).add(root.val); inOrder(root.left, level + 1, res); inOrder(root.right, level + 1, res); return res; } }
-
public List<List<Integer>> levelOrder(TreeNode root) { List<List<Integer>> res = new ArrayList<>(); Queue<TreeNode> queue = new ArrayDeque<>(); if (root != null) { queue.add(root); } while (!queue.isEmpty()) { int n = queue.size();//记录一层的节点数量,然后一次性遍历完成 List<Integer> level = new ArrayList<>(); for (int i = 0; i < n; i++) { TreeNode node = queue.poll(); level.add(node.val); if (node.left != null) { queue.add(node.left); } if (node.right != null) { queue.add(node.right); } } res.add(level); } return res; }
四、贪心算法
五、二分查找
六、动态规划
七、排序算法
1、解题模板
private static void mpsort(int[] a) { for (int i = 0; i < a.length; i++) { for (int j = 0; j < a.length - i - 1; j++) { //内层循环,升序(如果前一个值比后一个值大,则交换) if (a[j] > a[j + 1]) { swap(a, j, j + 1); } } } } private static void swap(int[] a, int j, int i) { int temp = a[i]; a[i] = a[j]; a[j] = temp; }
-
public static void main(String[] args) { int arr[] = {7, 5, 3, 2, 4}; //插入排序 for (int i = 1; i < arr.length; i++) { //外层循环,从第二个开始比较 for (int j = i; j > 0; j--) { //内存循环,与前面排好序的数据比较,如果后面的数据小于前面的则交换 if (arr[j] < arr[j - 1]) { int temp = arr[j - 1]; arr[j - 1] = arr[j]; arr[j] = temp; } else { //如果不小于,说明插入完毕,退出内层循环 break; } } } }
/** * 归并排序 * 分治思想 */ private static void mergeSort(int[] arr) { int[] temp = new int[arr.length]; subSort(arr, temp, 0,arr.length -1); } private static void subSort(int[] arr, int[] temp, int left, int right) { if (left < right){ int mid = (left + right) >> 1; //左边排序 subSort(arr, temp, left, mid); //右边排序 subSort(arr, temp, mid + 1, right); //合并 merge(arr, temp, left, mid, right); } } /** * 合并两个有序数组 */ private static void merge(int[] arr, int[] temp, int left, int mid, int right) { //左序列指针 int i = left; //右序列指针 int j = mid + 1; int k = 0; while (i <= mid && j <= right){ temp[k++] = arr[i] <= arr[j] ? arr[i++] : arr[j++]; } //把左边填入,只会有一个越界 while (i <= mid){ temp[k++] = arr[i++]; } //把右边填入 while (j <= right){ temp[k++] = arr[j++]; } k = 0; //将temp中的元素全部拷贝到原数组中 while(left <= right){ arr[left++] = temp[k++]; } }
2、力扣刷题
八、字符串算法
第五章、其他相关知识
一、多线程
1、常用思路
2、力扣刷题
class Foo { private boolean firstFinish = false; private boolean secondFinish = false; Object lock = new Object(); public Foo() { } public void first(Runnable printFirst) throws InterruptedException { synchronized(lock){ printFirst.run(); firstFinish = true; //通知所有在等待lock的线程 lock.notifyAll(); } } public void second(Runnable printSecond) throws InterruptedException { synchronized(lock){ //当1还未执行结束,则自旋等待,防止出现中途跳出 while(!firstFinish){ lock.wait(); } while() printSecond.run(); secondFinish = true; lock.notifyAll(); } } public void third(Runnable printThird) throws InterruptedException { synchronized(lock){ //当2还未执行结束则自旋等待 while(!secondFinish){ lock.wait(); } printThird.run(); } } }
class Foo { //保证可见性及禁止指令重排序 volatile int count=1; public Foo() { } public void first(Runnable printFirst) throws InterruptedException { printFirst.run(); count++; } public void second(Runnable printSecond) throws InterruptedException { //自旋,volatile更新数据后会通知到其他线程获取最新的值 while (count!=2); printSecond.run(); count++; } public void third(Runnable printThird) throws InterruptedException { //自旋,volatile更新数据后会通知到其他线程获取最新的值 while (count!=3); printThird.run(); } }
class Foo { int num; Lock lock; //精确的通知和唤醒线程 Condition condition1, condition2, condition3; public Foo() { num = 1; lock = new ReentrantLock(); condition1 = lock.newCondition(); condition2 = lock.newCondition(); condition3 = lock.newCondition(); } public void first(Runnable printFirst) throws InterruptedException { lock.lock(); try { //自旋等待 while (num != 1) { condition1.await(); } printFirst.run(); num = 2; condition2.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void second(Runnable printSecond) throws InterruptedException { lock.lock(); try { while (num != 2) { condition2.await(); } printSecond.run(); num = 3; condition3.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } public void third(Runnable printThird) throws InterruptedException { lock.lock(); try { while (num != 3) { condition3.await(); } printThird.run(); num = 1; condition1.signal(); } catch (Exception e) { e.printStackTrace(); } finally { lock.unlock(); } } }
lass Foo { CountDownLatch a; CountDownLatch b; public Foo() { //等待一个线程执行完 a = new CountDownLatch(1); b = new CountDownLatch(1); } public void first(Runnable printFirst) throws InterruptedException { printFirst.run(); a.countDown();//执行完-1 } public void second(Runnable printSecond) throws InterruptedException { a.await(); printSecond.run(); b.countDown(); } public void third(Runnable printThird) throws InterruptedException { b.await(); printThird.run(); } }
class Foo { private Semaphore sa; private Semaphore sb; public Foo() { sa = new Semaphore(0);//等待first执行完后再+许可 sb = new Semaphore(0); } public void first(Runnable printFirst) throws InterruptedException { printFirst.run(); sa.release();//给second加许可,释放一个sa的信号量 } public void second(Runnable printSecond) throws InterruptedException { sa.acquire(); printSecond.run(); sb.release();//给third加许可 } public void third(Runnable printThird) throws InterruptedException { sb.acquire(); printThird.run(); } }
-
class Foo { BlockingQueue<String> blockingQueue12, blockingQueue23; public Foo() { //同步队列,没有容量,进去一个元素,必须等待取出来以后,才能再往里面放一个元素 blockingQueue12 = new SynchronousQueue<>(); blockingQueue23 = new SynchronousQueue<>(); } public void first(Runnable printFirst) throws InterruptedException { printFirst.run(); blockingQueue12.put("stop"); } public void second(Runnable printSecond) throws InterruptedException { blockingQueue12.take(); printSecond.run(); blockingQueue23.put("stop"); } public void third(Runnable printThird) throws InterruptedException { blockingQueue23.take(); printThird.run(); } }
class FooBar { private int n; public FooBar(int n) { this.n = n; } Semaphore foo = new Semaphore(1); Semaphore bar = new Semaphore(0); public void foo(Runnable printFoo) throws InterruptedException { for (int i = 0; i < n; i++) { foo.acquire(); printFoo.run(); bar.release(); } } public void bar(Runnable printBar) throws InterruptedException { for (int i = 0; i < n; i++) { bar.acquire(); printBar.run(); foo.release(); } } }
class FooBar { private int n; public FooBar(int n) { this.n = n; } Lock lock = new ReentrantLock(true); volatile boolean permitFoo = true; public void foo(Runnable printFoo) throws InterruptedException { for (int i = 0; i < n; ) { lock.lock(); try { if(permitFoo) { printFoo.run(); i++; permitFoo = false; } }finally { lock.unlock(); } } } public void bar(Runnable printBar) throws InterruptedException { for (int i = 0; i < n; ) { lock.lock(); try { if(!permitFoo) { printBar.run(); i++; permitFoo = true; } }finally { lock.unlock(); } } } }
class FooBar { private int n; public FooBar(int n) { this.n = n; } volatile boolean permitFoo = true; public void foo(Runnable printFoo) throws InterruptedException { for (int i = 0; i < n; ) { if (permitFoo) { printFoo.run(); i++; permitFoo = false;//下一次一定是要等待其他线程完成修改 } } } public void bar(Runnable printBar) throws InterruptedException { for (int i = 0; i < n; ) { if (!permitFoo) { printBar.run(); i++; permitFoo = true; } } } }
class FooBar { private int n; public FooBar(int n) { this.n = n; } CyclicBarrier cb = new CyclicBarrier(2); volatile boolean fin = true; public void foo(Runnable printFoo) throws InterruptedException { for (int i = 0; i < n; i++) { while (!fin) ;//自旋等待,必须要加锁否则下一次任然可能是自己抢占了时间片 printFoo.run(); fin = false; try { cb.await();//阻塞自己,等待其他线程到达屏障点-->到达后进入下一次循环; } catch (BrokenBarrierException e) { } } } public void bar(Runnable printBar) throws InterruptedException { for (int i = 0; i < n; i++) { try { cb.await();//阻塞自己,等待其他线程到达屏障点-->到达后执行打印逻辑 } catch (BrokenBarrierException e) { } printBar.run(); fin = true;//类似于唤醒在自旋的线程 } } }
public class FooBar { private int n; private BlockingQueue<Integer> bar = new LinkedBlockingQueue<>(1); private BlockingQueue<Integer> foo = new LinkedBlockingQueue<>(1); public FooBar(int n) { this.n = n; } public void foo(Runnable printFoo) throws InterruptedException { for (int i = 0; i < n; i++) { foo.put(i);//在take前都只能阻塞 printFoo.run(); bar.put(i); } } public void bar(Runnable printBar) throws InterruptedException { for (int i = 0; i < n; i++) { bar.take(); printBar.run(); foo.take();//执行完释放,类似于通知 } } }
class BoundedBlockingQueue { //通过链表实现,可以从头结点添加,尾结点删除 private LinkedList<Integer> list; private int capacity; private volatile int size; Object lock = new Object(); public BoundedBlockingQueue(int capacity) { this.list = new LinkedList(); this.capacity = capacity; this.size = 0; } public void enqueue(int element) throws InterruptedException { synchronized(lock){ while(size + 1 > capacity) lock.wait();//自旋等待 size++; list.addFirst(element); lock.notify(); } } public int dequeue() throws InterruptedException { synchronized(lock){ while(size <= 0) lock.wait();//自旋等待 int res = list.removeLast(); size--; lock.notify(); return res; } } public int size() { return size; } }
二、布隆过滤器
三、LRU缓存
-
2、力扣刷题
-
遍历
/* 基本的单链表节点 */ class ListNode { int val; ListNode next; } void traverse(ListNode head) { for (ListNode p = head; p != null; p = p.next) { // 迭代访问 p.val } } void traverse(ListNode head) { // 递归访问 head.val traverse(head.next); }
1、解题模板
-
原始数组
//1、 数据类型 [] 数组名=new 数据类型[ length]; int[] ary = new int[4];//初始化值为0 //2、初始化 int[] ary2 = {0, 1, 2}; //数组扩容,拷贝 int[] newInts = Arrays.copyOf(ary2, 5);//表面上对数组长度进行扩容,实际新开辟一个空间 System.out.println(Arrays.toString(newInts));//[0, 1, 0, 0, 0] //数组复制 System.arraycopy(ary2, 1, ary, 1, 2);//把ary2中的内容从下标1开始复制到ary中,复制长度2 System.out.println(Arrays.toString(ary));//[0, 1, 2, 0]
-
ArrayList
public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e;//学习写法:添加到size位置后,size++ return true; }
-
跳表
-
升维:增加多级索引Olog(n);随着增加删除索引索引可能需要重建,空间复杂度O(n)
-
-
链表
//双向链表 class Node { public int key, val; public Node next, prev; public Node(int k, int v) { this.key = k; this.val = v; } }
一、数组、链表、跳表
第二章、常用数据结构与算法