diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..fbf98296 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/out +/.idea +/*.iml diff --git a/README.md b/README.md index 243e1474..61f293fa 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ## 仓库目录结构说明 -1. `week01/` 代表第一周作业提交目录,以此类推。 +1. `week01/` 代表第一周作业提交目录,以此类推。 2. 请在对应周的目录下新建或修改自己的代码作业。 2. 每周均有一个 `REDAME.md` 文档,你可以将自己当周的学习心得以及做题过程中的思考记录在该文档中。 diff --git a/Week_00/README.md b/Week_00/README.md new file mode 100644 index 00000000..44deee85 --- /dev/null +++ b/Week_00/README.md @@ -0,0 +1,3 @@ +**学习笔记 + +恩。就很棒!** \ No newline at end of file diff --git a/Week_00/a_wait/AtomicCounter.java b/Week_00/a_wait/AtomicCounter.java new file mode 100644 index 00000000..0efc3a85 --- /dev/null +++ b/Week_00/a_wait/AtomicCounter.java @@ -0,0 +1,55 @@ +package a_wait; + +import sun.misc.Unsafe; + +import java.lang.reflect.Field; + +public class AtomicCounter { + private volatile long counter = 0; + private Unsafe unsafe; + private long offset; + + + public AtomicCounter() throws NoSuchFieldException { + unsafe = getUnsafe(); + //获得counter字段在内存的起始地址 + offset = unsafe.objectFieldOffset(AtomicCounter.class.getDeclaredField("counter")); + } + + public void increment() { + long expect = counter; + long update = counter + 1; + while (!unsafe.compareAndSwapLong(this, offset, expect, update)) { + expect = counter; + } + } + + public long getCounter() { + return this.counter; + } + + //反射获得 unsafe + public static Unsafe getUnsafe() { + try { + Field singletonInstanceField = Unsafe.class.getDeclaredField("theUnsafe"); + singletonInstanceField.setAccessible(true); + return (Unsafe) singletonInstanceField.get(null); + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + return null; + } + + public static void main(String[] args) throws NoSuchFieldException { + int posIndex = 50; + AtomicCounter ac = new AtomicCounter(); + while (posIndex-- > 0) { + ac.increment(); + System.out.println(ac.getCounter()); + } + } + + +} \ No newline at end of file diff --git a/Week_00/a_wait/AtomicIntegerTest.java b/Week_00/a_wait/AtomicIntegerTest.java new file mode 100644 index 00000000..c1673c0d --- /dev/null +++ b/Week_00/a_wait/AtomicIntegerTest.java @@ -0,0 +1,16 @@ +package a_wait; + +import sun.misc.Unsafe; + +import java.util.concurrent.atomic.AtomicInteger; + +public class AtomicIntegerTest { + public static void main(String[] args) { + int posIndex = 10; + AtomicInteger atomicIntegerTest = new AtomicInteger(); + while (posIndex-- > 0) + atomicIntegerTest.getAndIncrement(); + System.out.println(atomicIntegerTest.get()); + + } +} diff --git a/Week_00/a_wait/MaxSlidingWindow.java b/Week_00/a_wait/MaxSlidingWindow.java new file mode 100644 index 00000000..c1ab9eba --- /dev/null +++ b/Week_00/a_wait/MaxSlidingWindow.java @@ -0,0 +1,6 @@ +package a_wait; + +//239 滑动最大窗口 https://leetcode-cn.com/problems/sliding-window-maximum/ +public class MaxSlidingWindow { + +} diff --git a/Week_00/a_wait/VolatileTest.java b/Week_00/a_wait/VolatileTest.java new file mode 100644 index 00000000..c0c57681 --- /dev/null +++ b/Week_00/a_wait/VolatileTest.java @@ -0,0 +1,56 @@ +package a_wait; + +import java.nio.charset.StandardCharsets; + +public class VolatileTest { + private static int COUNTER = 0; + + + public static void main(String[] args) { + + +// new ChangeListener().start(); +// new ChangeMaker().start(); + } + + static class ChangeListener extends Thread { + + @Override + public void run() { + int value = 0; + while (value < 5) { + if (value != COUNTER) { + System.out.println("changed! " + COUNTER); + value = COUNTER; + + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + } + } + + + static class ChangeMaker extends Thread { + @Override + public void run() { + while (COUNTER < 10) { + + synchronized (ChangeMaker.class) { + COUNTER++; + System.out.println("increment! " + (COUNTER)); + } + try { + Thread.sleep(400); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + } + } + + } +} diff --git a/Week_00/practice/array/Again.java b/Week_00/practice/array/Again.java new file mode 100644 index 00000000..25c00305 --- /dev/null +++ b/Week_00/practice/array/Again.java @@ -0,0 +1,31 @@ +package practice.array; + +import utils.Utils; + +public class Again { + public static void main(String[] args) { + int[] arr = new int[]{1, 2, 3, 4, 5, 6, 7}; + new Again().rotate(arr, 3); + Utils.displayArray(arr); + } + + public void rotate(int[] nums, int k) { + k = k % nums.length; + int count = 0; + + for (int i = 0; count < nums.length; i++) { + int preValue = nums[i]; + int nextIndex = (i + k) % nums.length; + + while (i != nextIndex) { + int tmp = nums[nextIndex]; + nums[nextIndex] = preValue; + preValue = tmp; + nextIndex = (nextIndex + k) % nums.length; + count++; + } + nums[i] = preValue; + count++; + } + } +} diff --git a/Week_00/practice/array/ClimbStairs_4.java b/Week_00/practice/array/ClimbStairs_4.java new file mode 100644 index 00000000..3a943e11 --- /dev/null +++ b/Week_00/practice/array/ClimbStairs_4.java @@ -0,0 +1,34 @@ +package practice.array; +//70 爬楼梯 https://leetcode-cn.com/problems/climbing-stairs/ + +public class ClimbStairs_4 { + + public int climbStairs(int n) { + //双指针 + if (n <= 2) return n; + int fir = 0, sec = 1; + for (int i = 0; i < n; i++) { + sec = fir + sec; + fir = sec - fir; + } + return sec; + } + + + /** + * 递归加记忆算法 + * + * 转化为斐波那契数列的递归式的时间复杂度为2^n, 引入记忆缓存为使得时间复杂度为O(n) + */ + public int climbStairsByMemory(int n) { + int[] mem = new int[n]; + int res = recursion(mem, n); + return res; + } + + private int recursion(int[] mem, int n) { + if (n <= 2) return n; + if (mem[n] == 0) mem[n] = recursion(mem, n - 2) + recursion(mem, n - 1); //记忆数组中没有值,就计算赋值 + return mem[n]; //直接取记忆数组中的值 + } +} diff --git a/Week_00/practice/array/FourSumCount_2.java b/Week_00/practice/array/FourSumCount_2.java new file mode 100644 index 00000000..6398e196 --- /dev/null +++ b/Week_00/practice/array/FourSumCount_2.java @@ -0,0 +1,40 @@ +package practice.array; + +import java.util.HashMap; +import java.util.Map; + +/** + * 454 四数相加 https://leetcode-cn.com/problems/4sum-ii/ + * + */ +public class FourSumCount_2 { + public static void main(String[] args) { + } + + /** + * 由于是满足的组合数, 所以通过hashMap对2个数组的和进行计数, 再用map中的数值与剩下的2个数组和的绝对值进行比对。 + */ + public int fourSumCount(int[] A, int[] B, int[] C, int[] D) { + Map map = new HashMap<>(); + int res = 0; + + for (int i = 0; i < A.length; i++) { + for (int j = 0; j < B.length; j++) { + map.put(A[i] + B[j], map.getOrDefault(A[i] + B[j], 0) + 1); + } + } + + for (int i = 0; i < C.length; i++) { + for (int j = 0; j < D.length; j++) { + if (map.containsKey(-(C[i] + D[j]))) res += map.get(-(C[i] + D[j])); + } + } + + return res; + } + + + + + +} diff --git a/Week_00/practice/array/FourSum_2.java b/Week_00/practice/array/FourSum_2.java new file mode 100644 index 00000000..36d5e978 --- /dev/null +++ b/Week_00/practice/array/FourSum_2.java @@ -0,0 +1,41 @@ +package practice.array; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +// 18 四数之和 https://leetcode-cn.com/problems/4sum/ + +public class FourSum_2 { + /** + * 三数之和 的套路 - 双指针法 + */ + public List> fourSum(int[] nums, int target) { + if(nums.length < 4) return new ArrayList<>(); + List> resList = new ArrayList<>(); + Arrays.sort(nums); + + for(int i = 0; i < nums.length - 3; i++){ // nums.length - 3(-1 必减, 第二层循环 -1, tail指针 -1) + if(i > 0 && nums[i] == nums[i - 1]) continue; //去除重复 + for(int j = i + 1; j < nums.length -2; j++){ + if(j > i + 1 && nums[j] == nums[j - 1]) continue; //去除重复 + + int head = j + 1; + int tail = nums.length - 1; + + while(head < tail) { + int fourSum = nums[i] + nums[j] + nums[head] + nums[tail]; + if (fourSum == target) { + resList.add(Arrays.asList(nums[i], nums[j], nums[head++], nums[tail--])); + + while(head < tail && nums[head] == nums[head - 1]) head++; //去除重复 + while(head < tail && nums[tail] == nums[tail + 1]) tail--; //去除重复 + + }else if(fourSum > target) tail--; //大于目标数 tail中间靠 + else head++; //小于目标数 head中间靠 + } + } + } + return resList; + } +} diff --git a/Week_00/practice/array/MaxArea_2.java b/Week_00/practice/array/MaxArea_2.java new file mode 100644 index 00000000..807d5394 --- /dev/null +++ b/Week_00/practice/array/MaxArea_2.java @@ -0,0 +1,25 @@ +package practice.array; + +// 11 盛最多水的容器 https://leetcode-cn.com/problems/container-with-most-water/ +public class MaxArea_2 { + + + /** + * 这套题用双指针夹逼法来做, 前后指针向中间靠近,指针移动的条件是 left < right && + * @param height + * @return + */ + public int maxArea(int[] height) { + int left = 0; + int right = height.length - 1; + int res = 0; + while (left < right) { + int leftHeight = height[left]; + int rightHeight = height[right]; + res = leftHeight > rightHeight ? + Math.max(res, (right-- - left) * rightHeight) : + Math.max(res, (right - left++) * leftHeight); + } + return res; + } +} diff --git a/Week_00/practice/array/MergeTwoArray_2.java b/Week_00/practice/array/MergeTwoArray_2.java new file mode 100644 index 00000000..417a79df --- /dev/null +++ b/Week_00/practice/array/MergeTwoArray_2.java @@ -0,0 +1,30 @@ +package practice.array; + + +//88 合并2个有序数组 https://leetcode-cn.com/problems/merge-sorted-array/ +public class MergeTwoArray_2 { + public static void main(String[] args) { + new MergeTwoArray_2().merge(new int[]{1, 2, 3, 0, 0, 0}, 3, new int[]{2, 5, 6}, 3); + } + + public void merge(int[] nums1, int m, int[] nums2, int n) { + // 1. nums2 复制到 nums1 再排序 + + /*System.arraycopy(nums2, 0, nums1, m, n); + Arrays.sort(nums1); +*/ + // 2. 从前往后,双指针, 需要开辟额外空间 + + // 3. 从后往前, 双指针, 原地操作 + /* int p1 = m - 1; + int p2 = n - 1; + int cur = m + n -1; + while (p1 >= 0 && p2 >= 0) nums1[cur--] = nums1[p1] > nums2[p2] ? nums1[p1--] : nums2[p2--]; + while (p2 >=0 ) nums1[cur--] = nums2[p2--];*/ + + + int p1 = nums1.length - 1; + while (m != 0 && n != 0) nums1[p1--] = nums1[m - 1] < nums2[n - 1] ? nums2[n-- - 1] : nums1[m-- - 1]; + while (n != 0) nums1[p1--] = nums2[n-- - 1]; + } +} diff --git a/Week_00/practice/array/MoveZeroes_2.java b/Week_00/practice/array/MoveZeroes_2.java new file mode 100644 index 00000000..20b1c436 --- /dev/null +++ b/Week_00/practice/array/MoveZeroes_2.java @@ -0,0 +1,66 @@ +package practice.array; + +// 283. 移动零 https://leetcode-cn.com/problems/move-zeroes/ + +public class MoveZeroes_2 { + + + public static void main(String[] args) { + int[] nums = {0, 0, 1, 0, 2}; + +// moveZeroes_loop2(nums); + moveZeroes_twoPoints(nums); + printNums(nums); + + } + + + + /** + * 双指针. 把不知零的数前挪动,最后补零。 + * + * @param nums + */ + public static void moveZeroes_twoPoints(int[] nums) { + + int p1 = 0, p2 = 0; + while (p2 < nums.length) { + if (nums[p2] != 0) nums[p1++] = nums[p2++]; + else p2++; + } + while (p1 < nums.length) nums[p1++] = 0; + + /*int i = 0; + for (int j = 0; j < nums.length; j++) { + if (nums[j] != 0) { + int swap = nums[i]; + nums[i++] = nums[j]; + nums[j] = swap; + } + }*/ + } + + /** + * O(n^2) + * + * @param nums + */ + public static void moveZeroes_loop2(int[] nums) { + for (int i = 0; i < nums.length - 1; i++) { + for (int j = 0; j < nums.length - 1; j++) { + if (nums[j] == 0) { + nums[j] = nums[j + 1]; + nums[j + 1] = 0; + } + } + } + + } + + + private static void printNums(int[] nums) { + for (int i = 0; i < nums.length; i++) { + System.out.print(nums[i] + " "); + } + } +} diff --git a/Week_00/practice/array/MyCircularDeque_2.java b/Week_00/practice/array/MyCircularDeque_2.java new file mode 100644 index 00000000..186f7e06 --- /dev/null +++ b/Week_00/practice/array/MyCircularDeque_2.java @@ -0,0 +1,69 @@ +package practice.array; + +//641 设计循环双端队列 https://leetcode-cn.com/problems/design-circular-deque/ +public class MyCircularDeque_2 { + + int capacity = 0; + int front = 0; + int rear = 0; + int arr[]; + + + /** Initialize your data structure here. Set the size of the deque to be k. */ + public MyCircularDeque_2(int k) { + capacity = k + 1; //多一个位置用于rear指针放着。 + arr = new int[capacity]; + } + + /** Adds an item at the front of Deque. Return true if the operation is successful. */ + public boolean insertFront(int value) { + if (isFull()) return false; + front = (front - 1 + capacity) % capacity; //先移动再赋值。 + arr[front] = value; + return true; + } + + /** Adds an item at the rear of Deque. Return true if the operation is successful. */ + public boolean insertLast(int value) { //先赋值 再移动。 每次都指向下一个被放入的位置。 + if (isFull()) return false; + arr[rear] = value; + rear = (rear + 1) % capacity; + return true; + } + + /** Deletes an item from the front of Deque. Return true if the operation is successful. */ + public boolean deleteFront() { + if (isEmpty()) return false; + front = (front + 1) % capacity; + return true; + } + + /** Deletes an item from the rear of Deque. Return true if the operation is successful. */ + public boolean deleteLast() { + if (isEmpty()) return false; + rear = (rear - 1 + capacity) % capacity; + return true; + } + + /** Get the front item from the deque. */ + public int getFront() { + if (isEmpty()) return -1; + return arr[front]; + } + + /** Get the last item from the deque. */ + public int getRear() { + if (isEmpty()) return -1; + return arr[(rear - 1 + capacity) % capacity]; + } + + /** Checks whether the circular deque is empty or not. */ + public boolean isEmpty() { + return front == rear; + }//为空表示在同一个位置。 + + /** Checks whether the circular deque is full or not. */ + public boolean isFull() { + return front == (rear + 1) % capacity; + } +} diff --git a/Week_00/practice/array/PlusOne_2.java b/Week_00/practice/array/PlusOne_2.java new file mode 100644 index 00000000..75c24436 --- /dev/null +++ b/Week_00/practice/array/PlusOne_2.java @@ -0,0 +1,55 @@ +package practice.array; + +import utils.Utils; + +// 66 加1 https://leetcode-cn.com/problems/plus-one/ +public class PlusOne_2 { + public static void main(String[] args) { + Utils.displayArray(new PlusOne_2().plusOne(new int[]{1, 2, 3})); + + } + + + + + + public int[] plusOne(int[] digits) { + // 换算出整数 + 1, 再写回一个新的数组 + // 个位 + 1,判断是否进位。 + /* int index = digits.length - 1; + digits[index] = digits[index] + 1; + while (digits[index] > 9) { + digits[index--] = 0; + if (index != -1) digits[index] += 1; + else { + int[] res = new int[digits.length + 1]; + res[0] = 1; + return res; + } + } + return digits;*/ + + for (int i = digits.length - 1; i >= 0; i--) { + digits[i]++; + if (digits[i] > 9) digits[i] = 0; + else return digits; + } + digits = new int[digits.length + 1]; + digits[0] = 1; + return digits; + + + //解法2 + /** + * for (int i = digits.length - 1; i >= 0; i--) { + * digits[i]++; + * digits[i] = digits[i] % 10; + * if (digits[i] != 0) return digits; + * } + * digits = new int[digits.length + 1]; + * digits[0] = 1; + * return digits; + */ + + } +} diff --git a/Week_00/practice/array/RemoveDuplicates_2.java b/Week_00/practice/array/RemoveDuplicates_2.java new file mode 100644 index 00000000..f68321ad --- /dev/null +++ b/Week_00/practice/array/RemoveDuplicates_2.java @@ -0,0 +1,17 @@ +package practice.array; + + +// 26 删除排序数组中的重复项 https://leetcode-cn.com/problems/remove-duplicates-from-sorted-array/ +public class RemoveDuplicates_2 { + + + public int removeDuplicates(int[] nums) { + // 双指针 cur , pos. + int cur = 0, pos = 1; + while(pos < nums.length) { + if (nums[cur] != nums[pos]) nums[++cur] = nums[pos++]; + else pos++; + } + return cur + 1; + } +} diff --git a/Week_00/practice/array/Rotate_3_need.java b/Week_00/practice/array/Rotate_3_need.java new file mode 100644 index 00000000..007e25a3 --- /dev/null +++ b/Week_00/practice/array/Rotate_3_need.java @@ -0,0 +1,79 @@ +package practice.array; + +//189 旋转数组 https://leetcode-cn.com/problems/rotate-array/ +public class Rotate_3_need { + + public static void main(String[] args) { + int[] a = new int[]{1, 2, 3, 4, 5, 6, 7}; + new Rotate_3_need().rotate(a, 3); + + + } + + public void rotate(int[] nums, int k) { + + + // 3 反转数组 3次 + + k = k % nums.length; // 优化k,除去重复的循环 + + // 1 暴力解法 从第一个数开始逐渐循环替换后面的数 O(kn) + /* + for (int i = 0; i < k; i++) { + int pre = nums[nums.length - 1]; + for (int j = 0; j < nums.length; j++) { + int tmp = nums[j]; + nums[j] = pre; + pre = tmp; + } + Utils.displayArray(nums); + System.out.println(""); + } + */ + + // 2 循环替换,让座位 + int count = 0; //换座位次数,每个人换一次就算是完成了。即 count = nums.length + for(int i = 0; count < nums.length; i++) { + int preValue = nums[i]; + int nextIndex = i; + do { + nextIndex = (nextIndex + k) % nums.length; + int tmp = nums[nextIndex]; + nums[nextIndex] = preValue; + preValue = tmp; + count++; + }while (nextIndex != i); + } + + + /* + + //用的 while, 而不是do while。 当nextIndex = i时,需要在跳出循环时给nums[i]赋值,count++ + k = k % nums.length; + int count = 0; + + for (int i = 0; count < nums.length; i++) { + int preValue = nums[i]; + int nextIndex = (i + k) % nums.length; + + while (i != nextIndex) { + int tmp = nums[nextIndex]; + nums[nextIndex] = preValue; + preValue = tmp; + nextIndex = (nextIndex + k) % nums.length; + count++; + } + nums[i] = preValue; + count++; + }*/ + + + // 4 非原地算法: 引入额外的数据 + /* + int[] res = new int[nums.length]; + System.arraycopy(nums, 0, res, k, nums.length - k); + System.arraycopy(nums, nums.length - k, res, 0, k); + System.arraycopy(res, 0, nums, 0, nums.length); + */ + } +} diff --git a/Week_00/practice/array/SearchRange_2.java b/Week_00/practice/array/SearchRange_2.java new file mode 100644 index 00000000..aee450b7 --- /dev/null +++ b/Week_00/practice/array/SearchRange_2.java @@ -0,0 +1,33 @@ +package practice.array; +// 34 在排序数组中查找元素的第一个和最后一个位置 https://leetcode-cn.com/problems/find-first-and-last-position-of-element-in-sorted-array/ +public class SearchRange_2 { + + + public static void main(String[] args) { + new SearchRange_2().searchRange(new int[]{5,7,7,8,8,10},8); + } + + // 34 + public int[] searchRange(int[] nums, int target) { + int tar_cnt = 0; // 记录目标数的个数 + int end_index = 0; + + for (int i = 0; i < nums.length; i++) { + if (nums[i] == target) { + tar_cnt++; + end_index = i; + } + } + + if (tar_cnt > 0) { + return new int[]{end_index - tar_cnt + 1, end_index}; + } + + return new int[]{-1, -1}; + } + + + + + +} diff --git a/Week_00/practice/array/ThreeSum_2.java b/Week_00/practice/array/ThreeSum_2.java new file mode 100644 index 00000000..3d572fa6 --- /dev/null +++ b/Week_00/practice/array/ThreeSum_2.java @@ -0,0 +1,40 @@ +package practice.array; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + + +// 15 三数之和 https://leetcode-cn.com/problems/3sum/ +public class ThreeSum_2 { + public List> threeSum(int[] nums) { + + if (nums.length < 3) { + return new ArrayList>(); + } + + Arrays.sort(nums); + List> resList = new ArrayList<>(); + + for (int i = 0; i < nums.length - 2; i++) { + if (i > 0 && nums[i] == nums[i - 1]) continue; + + int head = i + 1; + int tail = nums.length - 1; + + while (head < tail) { + int threeSum = nums[i] + nums[head] + nums[tail]; + if (threeSum == 0) { + resList.add(Arrays.asList(nums[i], nums[head++], nums[tail--])); + while (head < tail && nums[head] == nums[head - 1]) {head++;} + while (head < tail && nums[tail] == nums[tail + 1]) {tail--;} + } else if (threeSum > 0) + tail--; + else + head++; + } + } + return resList; + } + +} diff --git a/Week_00/practice/array/TwoSum_2.java b/Week_00/practice/array/TwoSum_2.java new file mode 100644 index 00000000..0d2741e8 --- /dev/null +++ b/Week_00/practice/array/TwoSum_2.java @@ -0,0 +1,37 @@ +package practice.array; + +import java.util.HashMap; +import java.util.Map; + + +// 1 两数之和 https://leetcode-cn.com/problems/two-sum/ +public class TwoSum_2 { + + //哈希 取相反数 + public int[] twoSum(int[] nums, int target) { + HashMap hashMap = new HashMap<>(); + for (int i = 0; i < nums.length; i++) { + hashMap.put(nums[i], i); + } + + for (int i = 0; i < nums.length; i++) { + int otherNum = target - nums[i]; + if (hashMap.containsKey(otherNum) && hashMap.get(otherNum) != i) { + return new int[]{i, hashMap.get(otherNum)}; + } + } + return null; + } + + + public int[] twoSum_new(int[] nums, int target) { + Map map = new HashMap<>(); + for (int i = 0;i < nums.length; i++) + if (map.containsKey(target - nums[i])) + return new int[]{i, map.get(target - nums[i])}; + else + map.put(nums[i],i); + return nums; + } + +} diff --git a/Week_00/practice/bit/HammingWeight.java b/Week_00/practice/bit/HammingWeight.java new file mode 100644 index 00000000..08643a35 --- /dev/null +++ b/Week_00/practice/bit/HammingWeight.java @@ -0,0 +1,38 @@ +package practice.bit; + +//131 位的个数 https://leetcode-cn.com/problems/number-of-1-bits/ +public class HammingWeight { + + + public static void main(String[] args) { + + int a = 4; + int b = a >> 1; + System.out.println(b); + } + + + // 通过消除最低位的1进行计数,直到最后为0 + public int hammingWeight(int n) { + int count = 0; + while (n != 0) { + count++; + n = (n - 1) & n; //消除最低位的 1 + } + return count; + } + + // 通过 1 左移,与原数进行“与”操作, 不为零则计数1个 + public int hammingWeight_2(int n) { + int count = 0; + int mark = 1; + int bit = 32; + + while (bit-- != 0) { + if((mark & n) != 0) count++; + mark <<= 1; + } + return count; + } + +} diff --git a/Week_00/practice/bit/IsPowerOfTwo.java b/Week_00/practice/bit/IsPowerOfTwo.java new file mode 100644 index 00000000..8bccb75b --- /dev/null +++ b/Week_00/practice/bit/IsPowerOfTwo.java @@ -0,0 +1,18 @@ +package practice.bit; + +//231 2的幂 https://leetcode-cn.com/problems/power-of-two/ +public class IsPowerOfTwo { + + + public static void main(String[] args) { + int a = 1 & 1; + int b = 1 & 4; + System.out.println(a); + } + + // 二进制位中 只有一位是1 即是2的幂 + public boolean isPowerOfTwo(int n) { + while (n > 0 && (n & 1) == 0) n >>= 1; + return n == 1; + } +} diff --git a/Week_00/practice/bit/ReverseBits.java b/Week_00/practice/bit/ReverseBits.java new file mode 100644 index 00000000..93728e67 --- /dev/null +++ b/Week_00/practice/bit/ReverseBits.java @@ -0,0 +1,34 @@ +package practice.bit; + +//190 颠倒二进制位 https://leetcode-cn.com/problems/reverse-bits/ +public class ReverseBits { + public static void main(String[] args) { + System.out.println(new ReverseBits().reverseBits(1)); + System.out.println(Integer.MAX_VALUE); + } + + /** + * 位置替换 , 比如 高32位换低1位, 高31位换低2位 + */ + public int reverseBits(int n) { + int res = 0; + for (int i = 0; i < 32 ; i++) { + res = res | ((n >> i) & 1) << (31 - i); + } + return res; + } + + + /** + * res 向左移空出一个位 + 加上n的最右边的位, n右移,最右的一位下一次res相加。 + */ + public int reverseBits_1(int n) { + int res = 0; + for (int i = 0; i < 32; i++) { + res = (res <<= 1) + (n & 1); + n >>= 1; + } + return res; + } + +} diff --git a/Week_00/practice/bit/TotalNQueens.java b/Week_00/practice/bit/TotalNQueens.java new file mode 100644 index 00000000..1d5b076a --- /dev/null +++ b/Week_00/practice/bit/TotalNQueens.java @@ -0,0 +1,55 @@ +package practice.bit; + +import com.sun.deploy.util.StringUtils; + +public class TotalNQueens { + private int size; + private int count; + + + public static void main(String[] args) { +// System.out.println(new practice.bit.TotalNQueens().totalNQueens(4)); + } + + public int totalNQueens(int n) { + count = 0; + size = (1 << n) - 1; + displayBit("size", size); + solve(0, 0, 0); + return count; + } + + private void solve(int row, int ld, int rd) { + if (row == size) { + count++; + return; + } + displayBit("row",row); + displayBit("ld",ld); + displayBit("rd",rd); + + int pos = size & (~(row | ld | rd)); // 位上的1表示没有被占 + displayBit("pos",pos); + while (pos != 0) { + displayBit("-pos",(-pos)); + int p = pos & (-pos); + displayBit("p",p); + pos -= p; // pos &= pos - 1; + displayBit("pos",(pos)); + solve(row | p, (ld | p) << 1, (rd | p) >> 1); + } + } + + public static void displayBit(String info, int num) { + StringBuilder sb = new StringBuilder(); + while (num > 0) { + int bit = num % 2; + sb.append(String.valueOf(bit)); + num = num / 2; + } + while (sb.length() < 4) sb.append("0"); + System.out.println(info + " : " + sb.reverse().toString()); + } + + +} diff --git a/Week_00/practice/disjointset/FindCircleNum.java b/Week_00/practice/disjointset/FindCircleNum.java new file mode 100644 index 00000000..a45da861 --- /dev/null +++ b/Week_00/practice/disjointset/FindCircleNum.java @@ -0,0 +1,52 @@ +package practice.disjointset; + +//547 省份的数量 https://leetcode-cn.com/problems/number-of-provinces/ +public class FindCircleNum { + + + public int findCircleNum(int[][] isConnected) { + + int rows = isConnected.length; + UnionFind unionFind = new UnionFind(rows); + for (int i = 0; i < rows - 1; i++) { + for (int j = i + 1; j < rows; j++) { + if (isConnected[i][j] == 1) unionFind.union(i , j); + } + } + return unionFind.count; + } + + + /** + * 并查集模板代码 + */ + class UnionFind { + private int count = 0; + private int[] parent; + + public UnionFind(int size) { + count = size; + parent = new int[size]; + for (int i = 0; i < size; i++) parent[i] = i; + } + + public int find(int p) { + while (p != parent[p]) { + parent[p] = parent[parent[p]]; //压缩 : 指向上一个节点 + p = parent[p]; + } + return p; + } + + public void union(int p, int q){ + int rootP = find(p); + int rootQ = find(q); + if (rootP == rootQ) return; + parent[rootQ] = parent[rootP]; + count--; + } + + + } + +} diff --git a/Week_00/practice/dynamicprogram/CoinChange.java b/Week_00/practice/dynamicprogram/CoinChange.java new file mode 100644 index 00000000..56d09314 --- /dev/null +++ b/Week_00/practice/dynamicprogram/CoinChange.java @@ -0,0 +1,37 @@ +package practice.dynamicprogram; + +import java.util.Arrays; + +/** + * 322 零钱兑换 https://leetcode-cn.com/problems/coin-change/ + */ +public class CoinChange { + public static void main(String[] args) { + System.out.println(new CoinChange().coinChange(new int[]{1, 2, 5}, 11)); + } + + /** + * 解法: + * 1 递归 + * 2 动态规划 + * + * 动态规划: + * 类似爬楼梯的斐波那契数列。 + * 分治:dp[amount] = Math.min(dp[amount - k]) + 1; k为 coints数组中的硬币数。 即: 取amount - k对应的钱的最小硬币数加上 1个(k硬币) + * 即为当前最小硬币数 + */ + public int coinChange(int[] coins, int amount) { + int size = amount + 1; + int[] dp = new int[size]; + Arrays.fill(dp, size); //填充数组,用于最后判断,如果只数组下标对应的为amount + 1 (size),说明没有被改动,就是没有对应的硬币分配方案。 + dp[0] = 0; + + for (int i = 1; i < size; i++) { + for(int k = 0; k < coins.length; k++){ + if(i - coins[k] >= 0) + dp[i] = Math.min(dp[i],dp[i - coins[k]] + 1); + } + } + return dp[amount] > amount ? -1 : dp[amount]; + } +} diff --git a/Week_00/practice/dynamicprogram/LengthOfLIS.java b/Week_00/practice/dynamicprogram/LengthOfLIS.java new file mode 100644 index 00000000..2d020f34 --- /dev/null +++ b/Week_00/practice/dynamicprogram/LengthOfLIS.java @@ -0,0 +1,34 @@ +package practice.dynamicprogram; + +import java.util.Arrays; + +// 最长递增子序列 https://leetcode-cn.com/problems/longest-increasing-subsequence/ +public class LengthOfLIS { + public static void main(String[] args) { + System.out.println(new LengthOfLIS().lengthOfLIS(new int[]{0, 1, 0, 3, 2, 3})); + } + + + /** + * dp[i] 表示nums数组从0到i中最大递增序列长度 + *

+ * int j = 0; + * while (j++ < i) + * if (nums[i] > nums[j - 1]) dp[i] = Math.max(dp[i], dp[j - 1] + 1); + */ + public int lengthOfLIS(int[] nums) { + if (nums.length <= 1) return nums.length; + int dp[] = new int[nums.length]; + int maxLen = 0; + Arrays.fill(dp, 1); + + for (int i = 1; i < nums.length; i++) { + int j = 0; + while (j++ < i) + if (nums[i] > nums[j - 1]) dp[i] = Math.max(dp[i], dp[j - 1] + 1); + maxLen = Math.max(maxLen, dp[i]); + } + return maxLen; + } + +} diff --git a/Week_00/practice/dynamicprogram/LongestCommonSubsequence.java b/Week_00/practice/dynamicprogram/LongestCommonSubsequence.java new file mode 100644 index 00000000..b642c93e --- /dev/null +++ b/Week_00/practice/dynamicprogram/LongestCommonSubsequence.java @@ -0,0 +1,35 @@ +package practice.dynamicprogram; + + +/** + * 1143 最长公共子序列 https://leetcode-cn.com/problems/longest-common-subsequence/ + */ +public class LongestCommonSubsequence { + public static void main(String[] args) { + System.out.println(new LongestCommonSubsequence().longestCommonSubsequence("abced","acd")); + + } + + /** + * 解法: + * 分治: 如果2个比较的子串的最后一个相同时,那么就是除最后一个字符串以外前面的最大子序列数 + 1. + * 当2个比较的子串的最后一个不相同时,那么就是取 “每个子串分别删除最后一个字符时”取最大的列数。 + */ + public int longestCommonSubsequence(String text1, String text2){ + int len1 = text1.length() + 1;// + 1 是因为index = 0 作为dp方程式的累加启动行,实际操作的数值从index = 1开始 + int len2 = text2.length() + 1;// + int[][] dp = new int[len1][len2]; + + //构造 dp table + for (int i = 1; i < len1; i++) { + for (int j = 1; j < len2; j++) { + if (text1.charAt(i - 1) == text2.charAt(j - 1)) { //比较2个子串的是否相同 + dp[i][j] = 1 + dp[i - 1][j - 1]; // 相同就是上一个子串的最长公共子序列数 + 1 + }else { + dp[i][j] = Math.max(dp[i - 1][j],dp[i][j - 1]); // 否则就是 删除每个子串的最后一个字符后,取最大公共数 + } + } + } + return dp[len1 - 1][len2 - 1]; + } +} diff --git a/Week_00/practice/dynamicprogram/LongestPalindrome.java b/Week_00/practice/dynamicprogram/LongestPalindrome.java new file mode 100644 index 00000000..5e8489e0 --- /dev/null +++ b/Week_00/practice/dynamicprogram/LongestPalindrome.java @@ -0,0 +1,45 @@ +package practice.dynamicprogram; + +//5 最长回文子串 https://leetcode-cn.com/problems/longest-palindromic-substring/ +public class LongestPalindrome { + public static void main(String[] args) { + System.out.println(new LongestPalindrome().longestPalindrome("")); + } + + + /** + * 动态规划 + * + * + */ + public String longestPalindromeByDp(String s) { + + + return null; + } + + + + /** + * 遍历每个字符 , 向两边扩散 + */ + public String longestPalindrome(String s) { + String maxLenStr = s.substring(0,1); + for (int i = 0; i < s.length(); i++) { + String len1Str = ""; + if (i > 0) len1Str = getPalindrome(s, i - 1, i + 1); + String len2Str = getPalindrome(s, i, i + 1); + String resStr = len1Str.length() > len2Str.length() ? len1Str : len2Str; + maxLenStr = maxLenStr.length() > resStr.length() ? maxLenStr : resStr; + } + return maxLenStr; + } + + private String getPalindrome(String s, int l, int r) { + while (l >= 0 && r < s.length() && s.charAt(l) == s.charAt(r)) { + l--; r++; + } + return s.substring(l + 1, r); + } + +} diff --git a/Week_00/practice/dynamicprogram/MaxProfit.java b/Week_00/practice/dynamicprogram/MaxProfit.java new file mode 100644 index 00000000..bfc19f24 --- /dev/null +++ b/Week_00/practice/dynamicprogram/MaxProfit.java @@ -0,0 +1,29 @@ +package practice.dynamicprogram; + +public class MaxProfit { + + + public int maxProfit(int[] prices) { + + /** + * + * i - 天 + * k - 是否卖出 1:卖出 0:没有卖出 + * + * dp[0][0] = prices[i]; //持有股票的钱 + * + * i > 0 + * dp[i][1] = Math.max(dp[i - 1][1], prices[i] - dp[i - 1][0]); //卖了的利润 + * dp[i][0] = Math.min(dp[i - 1][0], prices[i]); //持有成本最低的股票 + * + */ + + int[][] dp = new int[prices.length][2]; + dp[0][0] = prices[0]; + for (int i = 1; i < prices.length; i++) { + dp[i][1] = Math.max(dp[i - 1][1], prices[i] - dp[i - 1][0]); //卖了的利润 + dp[i][0] = Math.min(dp[i - 1][0], prices[i]); //持有成本最低的股票 + } + return dp[prices.length - 1][1]; + } +} diff --git a/Week_00/practice/dynamicprogram/MaxSubArray.java b/Week_00/practice/dynamicprogram/MaxSubArray.java new file mode 100644 index 00000000..bc0212a4 --- /dev/null +++ b/Week_00/practice/dynamicprogram/MaxSubArray.java @@ -0,0 +1,25 @@ +package practice.dynamicprogram; + +/** + * 53 最大序和 https://leetcode-cn.com/problems/maximum-subarray/ + */ +public class MaxSubArray { + + public static void main(String[] args) { + System.out.println(new MaxSubArray().maxSubArray(new int[]{-2,1,-3,4,-1,2,1,-5,4})); +// System.out.println(new MaxSubArray().maxSubArray(new int[]{-2,-1})); + } + + /** + * 动态规划:dp[i] = Math.max(dp[i - 1], 0) + nums[i]; 当前数的上一个序列和是否大于0, 大于0就加上当前数作为当前最大序列和, + * 否则取当前数作为最大序列和。 + */ + public int maxSubArray(int[] nums) { + int cur = 0, res = nums[0]; + for(int i = 0; i < nums.length; i++){ + cur = Math.max(cur, 0) + nums[i]; + res = Math.max(cur, res); + } + return res; + } +} diff --git a/Week_00/practice/dynamicprogram/MinPathSum.java b/Week_00/practice/dynamicprogram/MinPathSum.java new file mode 100644 index 00000000..24904aa2 --- /dev/null +++ b/Week_00/practice/dynamicprogram/MinPathSum.java @@ -0,0 +1,44 @@ +package practice.dynamicprogram; + +/** + * 64 最小路径和 https://leetcode-cn.com/problems/minimum-path-sum/ + */ +public class MinPathSum { + + public static void main(String[] args) { + System.out.println(new MinPathSum().minPathSum(new int[][]{{1, 3, 1}, {1, 5, 1}, {4, 2, 1}})); + } + + + /** + * 动态规划: + * 构造一个二维数组, 每个格子代表从(0,0)位置到当前位置的最小和。 结果取 (m, n) + * + * dp方程: + * 初始化(除 (0, 0)外 ): + * dp[m][n] = dp[m][n - 1] + grid[m][n]; //m = 0 + * dp[m][n] = dp[m - 1][n] + grid[m][n]; //n = 0 + * + * dp[m][n] = Math.min(dp[m][n - 1], dp[m - 1][n]) + grid[m][n]; + */ + public int minPathSum(int[][] grid) { + int row = grid.length; + int col = grid[0].length; + int[][] dp = new int[row][col]; + dp[0][0] = grid[0][0]; + + for (int m = 0; m < row; m++) { + for (int n = 0; n < col; n++) { + if (m == 0 && n > 0) dp[m][n] = dp[m][n - 1] + grid[m][n]; //初始化 m = 0 && n > 0 + else if (m > 0 && n == 0) dp[m][n] = dp[m - 1][n] + grid[m][n]; //初始化 n = 0 && m > 0 + else if (m > 0) { + dp[m][n] = Math.min(dp[m][n - 1], dp[m - 1][n]) + grid[m][n]; + } + } + } + + return dp[row - 1][col - 1]; + } + + +} diff --git a/Week_00/practice/dynamicprogram/NumDecodings.java b/Week_00/practice/dynamicprogram/NumDecodings.java new file mode 100644 index 00000000..767988f2 --- /dev/null +++ b/Week_00/practice/dynamicprogram/NumDecodings.java @@ -0,0 +1,60 @@ +package practice.dynamicprogram; + +/** + * 91 解码方法 https://leetcode-cn.com/problems/decode-ways/ + */ + +public class NumDecodings { + public static void main(String[] args) { + System.out.println(new NumDecodings().numDecodings("203")); + } + /** + * 动态规划: + * 解法类似爬楼梯:因为编码数值最大到26,所以切分的子字符串最大长度为2,因此只需要关注字符串的最后2个字符即可. + * dp[i] 就等于i - 2到i要走的步数加上 i - 1到i要走的步数。但是有多种边界条件致使dp方程不一定是dp[i] = dp[i -1] + dp[i - 2]. + * + * dp方程: dp[i] 表示从第0个字符到第i个字符形成的字符串的解码数。 + * + * + * 当s[i] = '0' 时 + * ① s[i - 1] = '1' || s[i - 1] = '2' 方程式为 dp[i] = dp[i - 2]. + * 解释: + * 因为最后一个字符为0,不能单独被解码,它只有和它前面的一个字符合为一个整体才有可能被解码,当字符是 1 或者 2,就可以当10或者20被解码了, + * 那也就意味着和dp[i - 2]的解码数一样。 + * 例如: 2220 . dp[3] = dp[3 -2]. + * dp[1]对应的字符串(22)组合有 2 2 和 22,共2种。 + * dp[3]对应的字符串(2220)组合有 2 2 20 和 22 20 共2种。 + * 所以可以观察到dp[3] 就是在dp[1]的每种组合后面加了20这个数。 即dp[i] = dp[i - 2]. + * + * ② 否则直接返回 0 。 因为 00, 30 ,40 这种串使得整个串无法被解码 + * + * + * 当 0 < s[i - 1][i] <27 时 + * ① dp[i] = dp[i - 1] + dp[i - 2]; + * 解释: + * 字符串最后是0的情况分析完,就分析不是0的。 最后2个字符组成的数字范围是0到27,意味着这2个字符有2种解码组合。比如 26 可以是 2 6 和 26 两种。 + * 所以dp[i] = dp[i - 1] + dp[i - 2] 就是爬楼梯的思维。 i - 1 就是从 i - 1到 i的爬楼方法数加 i - 2到 i 的爬楼方法数。 + * + * ② 否则 dp[i] = dp[i - 1]. 如果不满足 0 到 27的范围, 说明最后2个字符无法合在一起被解码。只能拆开解码,而拆开的解码数就等于dp[i -1]. + * 例如 23字符串的组合有 2 3 和 23 两种。 237字符串有2 3 7 和 23 7两种。 所以就是在i - 1组合后面加了 s[i]这个字符。 + */ + public int numDecodings(String s) { + if (s.charAt(0) == '0') return 0; + int[] dp = new int[s.length()]; + dp[0] = 1; // 一个非0的数默认有1种解码 + + for (int i = 1; i < s.length(); i++) { + char si = s.charAt(i); //代数 + char si_1 = s.charAt(i - 1); //代数 + int dpi_2 = (i - 2) >= 0 ? dp[i - 2] : 1; //代数 表示 dp[i - 2] + + if(si == '0'){ + if(si_1 == '1' || si_1 == '2') dp[i] = dpi_2; + else return 0; + } else if(si_1 == '1' || si_1 == '2' && si > '0' && si < '7'){ + dp[i] = dp[i - 1] + dpi_2; + } else dp[i] = dp[i - 1]; + } + return dp[s.length() - 1]; + } +} diff --git a/Week_00/practice/dynamicprogram/Rob.java b/Week_00/practice/dynamicprogram/Rob.java new file mode 100644 index 00000000..5d5c3cfc --- /dev/null +++ b/Week_00/practice/dynamicprogram/Rob.java @@ -0,0 +1,33 @@ +package practice.dynamicprogram; + + +/** + * 198 打家劫舍 https://leetcode-cn.com/problems/house-robber/ + */ +public class Rob { + public static void main(String[] args) { + System.out.println(new Rob().rob(new int[]{2, 1, 1, 2})); + } + + /** + * 动态规划: + * dp方程: 二维数组第二维 : 1 - 偷, 0 - 不偷 + * + * // 这一次不偷 = max(上一次不偷, 上一次偷) 注意的是“上一次偷” 不一定大于 “上一次不偷” + * dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1]); + * + * // 这一次偷 = 上一次不偷 + 偷这一次的钱 + * dp[i][1] = dp[i - 1][0] + nums[i]; + * + */ + public int rob(int[] nums) { + int size = nums.length + 1; + int[][] dp = new int[size][2]; + for (int i = 1; i < size; i++) { + dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1]); + dp[i][1] = dp[i - 1][0] + nums[i - 1]; + } + return Math.max(dp[size - 1][0], dp[size - 1][1]); + } + +} diff --git a/Week_00/practice/dynamicprogram/UniquePaths.java b/Week_00/practice/dynamicprogram/UniquePaths.java new file mode 100644 index 00000000..f28b0e44 --- /dev/null +++ b/Week_00/practice/dynamicprogram/UniquePaths.java @@ -0,0 +1,32 @@ +package practice.dynamicprogram; + + +/** + * 62 不同路径 https://leetcode-cn.com/problems/unique-paths/ + */ +public class UniquePaths { + + + public static void main(String[] args) { + System.out.println(new UniquePaths().uniquePaths(3, 7)); + } + public int uniquePaths(int m, int n) { + /** + * 3种解法: + * 1 每个方块表示到finish点的路径数。 就可以构造一个二维数组, 那么start方块处对应的数字就是总的路径数。 + * 2 每个方块表示从Start到该处的路径数, 那么finish方块处对应的数字就是总的路径数 + */ + + //解法1 + //初始化数组 + int[][] arr = new int[m][n]; + for (int i = 0; i < m; i++) arr[i][n - 1] = 1; + for (int j = 0; j < n ;j++) arr[m - 1][j] = 1; + + for (int i = m - 2; i >= 0 ; i--) + for (int j = n - 2; j >= 0 ; j--) + arr[i][j] = arr[i][j + 1] + arr[i + 1][j]; + return arr[0][0]; + + } +} diff --git a/Week_00/practice/dynamicprogram/UniquePathsWithObstacles.java b/Week_00/practice/dynamicprogram/UniquePathsWithObstacles.java new file mode 100644 index 00000000..7da4af8c --- /dev/null +++ b/Week_00/practice/dynamicprogram/UniquePathsWithObstacles.java @@ -0,0 +1,35 @@ +package practice.dynamicprogram; + +// 63 不同路径2 https://leetcode-cn.com/problems/unique-paths-ii/ +public class UniquePathsWithObstacles { + + + /** + * + * dp[0][0] = 1; + * dp[0][j] = 1; //j > 0, 需要判断左侧没有障碍物 + * dp[i][0] = 1; //i > 0, 需要判断上方没有障碍物 + * dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; + */ + public int uniquePathsWithObstacles(int[][] obstacleGrid) { + if(obstacleGrid[0][0] == 1) return 0; + int rows = obstacleGrid.length; + int cols = obstacleGrid[0].length; + int[][] dp = new int[rows][cols]; + dp[0][0] = 1; + + for (int i = 0; i < rows; i++) { + for (int j = 0; j < cols; j++) { + if (obstacleGrid[i][j] == 0) { + if ((i == 0 || j == 0) && (i > 0 && dp[i - 1][j] > 0 || j > 0 && dp[i][j - 1] > 0)) + dp[i][j] = 1; //初始化 第0行 第0列都为1 + else if (i > 0 && j > 0) + dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; + } + } + } + return dp[rows - 1][cols - 1]; + } + + +} diff --git a/Week_00/practice/greedy/FindContentChildren.java b/Week_00/practice/greedy/FindContentChildren.java new file mode 100644 index 00000000..3054f86d --- /dev/null +++ b/Week_00/practice/greedy/FindContentChildren.java @@ -0,0 +1,31 @@ +package practice.greedy; + +import java.util.Arrays; + +/** + * 455 分发饼干 https://leetcode-cn.com/problems/assign-cookies/description/ + */ + +public class FindContentChildren { + public static void main(String[] args) { + System.out.println(new FindContentChildren().findContentChildren(new int[]{1, 2, 3}, new int[]{1, 1})); + } + + + /** + * 贪心算法: 只要保证每次分配的饼干能满足孩子的需求,并且是所有饼干中尺寸最小的即可 + */ + public int findContentChildren(int[] g, int[] s) { + int gindex = 0, sindex = 0, count = 0; + Arrays.sort(g); + Arrays.sort(s); + + while (gindex < g.length && sindex < s.length) { + if (s[sindex++] >= g[gindex]) { + count++; + gindex++; + } + } + return count; + } +} diff --git a/Week_00/practice/hash/IsValidSudoku.java b/Week_00/practice/hash/IsValidSudoku.java new file mode 100644 index 00000000..bce8e96b --- /dev/null +++ b/Week_00/practice/hash/IsValidSudoku.java @@ -0,0 +1,27 @@ +package practice.hash; + + +//36 有效的数独 https://leetcode-cn.com/problems/valid-sudoku/ +public class IsValidSudoku { + + public boolean isValidSudoku(char[][] board) { + int[][] rows = new int[9][9]; + int[][] cols = new int[9][9]; + int[][] boxes = new int[9][9]; + + for (int i = 0; i < 9; i++) { + for (int j = 0; j < 9; j++) { + if (board[i][j] == '.') continue; + int index = board[i][j] - '1'; + int[] box = boxes[i / 3 * 3 + j / 3]; + if (rows[i][index] == 1 || cols[j][index] == 1 || box[index] == 1) { + return false; + } + rows[i][index] = 1; + cols[j][index] = 1; + box[index] = 1; + } + } + return true; + } +} diff --git a/Week_00/practice/heap/GetLeastNumber.java b/Week_00/practice/heap/GetLeastNumber.java new file mode 100644 index 00000000..4182cf92 --- /dev/null +++ b/Week_00/practice/heap/GetLeastNumber.java @@ -0,0 +1,31 @@ +package practice.heap; + + +import java.util.Arrays; +import java.util.PriorityQueue; + +/** + * 剑指offer 40 最小的k个数 https://leetcode-cn.com/problems/zui-xiao-de-kge-shu-lcof/ + */ +public class GetLeastNumber { + + /** + * 解法1 : 数组排序, 取前k个 + * 解法2 : 构造最小堆, 弹出k次 + */ + + public int[] getLeastNumbers(int[] arr, int k) { + int[] res = new int[k]; + + //解法1 + /*Arrays.sort(arr); + for (int i = 0; i < k; i++) res[i] = arr[i];*/ + + //解法2 + PriorityQueue pq = new PriorityQueue<>(); + for (Integer num : arr) pq.add(num); + for (int i = 0; i < k; i++) res[i] = pq.poll(); + + return res; + } +} diff --git a/Week_00/practice/heap/NthUglyNumber.java b/Week_00/practice/heap/NthUglyNumber.java new file mode 100644 index 00000000..b7c9d60e --- /dev/null +++ b/Week_00/practice/heap/NthUglyNumber.java @@ -0,0 +1,30 @@ +package practice.heap; + +import java.util.PriorityQueue; + +public class NthUglyNumber { + + public static void main(String[] args) { + System.out.println(new NthUglyNumber().nthUglyNumber(1407)); + } + + // 264 丑数 II + public int nthUglyNumber(int n) { + /** + * 1. 根据入参 构造最小堆 + * 2. 弹出最小堆的堆顶元素 n 次, 注意去重。 + */ + PriorityQueue pq = new PriorityQueue<>(); + long res = 1; + for (int i = 1; i < n; i++) { + pq.add(res * 2); + pq.add(res * 3); + pq.add(res * 5); + res = pq.poll(); + while (res == pq.peek()) pq.poll(); + } + return (int) res; + } + + +} diff --git a/Week_00/practice/heap/TopKFrequent.java b/Week_00/practice/heap/TopKFrequent.java new file mode 100644 index 00000000..cbe82cae --- /dev/null +++ b/Week_00/practice/heap/TopKFrequent.java @@ -0,0 +1,39 @@ +package practice.heap; + +import utils.Utils; + +import java.util.*; + +public class TopKFrequent { + // 347. 前 K 个高频元素 + + public static void main(String[] args) { + int[] nums = new int[]{2, 3, 4, 4, 4, 3, 1, 2, 5, 6, 7}; + new TopKFrequent().topKFrequent(nums, 3); + } + + public int[] topKFrequent(int[] nums, int k) { + + /** + * 1. 放入map中计数 + * 2. 构造最大堆 + * 3. 弹出K个堆顶元素 + */ + int[] res = new int[k]; + HashMap map = new HashMap<>(); + for (int num : nums) { + map.put(num, map.getOrDefault(num, 0) + 1); + } + + PriorityQueue> pq = new PriorityQueue<>(((o1, o2) -> o2.getValue() - o1.getValue())); + + for (Map.Entry entry : map.entrySet()) { + pq.add(entry); + } + + int i = 0; + while (i < k) res[i++] = pq.poll().getKey(); + Utils.displayArray(res); + return res; + } +} diff --git a/Week_00/practice/linkedlist/DetectCycle.java b/Week_00/practice/linkedlist/DetectCycle.java new file mode 100644 index 00000000..307034d7 --- /dev/null +++ b/Week_00/practice/linkedlist/DetectCycle.java @@ -0,0 +1,29 @@ +package practice.linkedlist; + +import java.util.HashMap; +import java.util.HashSet; + +// 142 环形链表 II https://leetcode-cn.com/problems/linked-list-cycle-ii/ +public class DetectCycle { + class ListNode { + int val; + ListNode next; + + ListNode(int x) { + val = x; + next = null; + } + } + + public ListNode detectCycle(ListNode head) { + HashSet set = new HashSet<>(); + while (head != null) { + if(set.contains(head)) { + return head; + } + set.add(head); + head = head.next; + } + return null; + } +} diff --git a/Week_00/practice/linkedlist/HasCycle.java b/Week_00/practice/linkedlist/HasCycle.java new file mode 100644 index 00000000..a04769a4 --- /dev/null +++ b/Week_00/practice/linkedlist/HasCycle.java @@ -0,0 +1,31 @@ +package practice.linkedlist; + +import java.util.HashSet; + +//141 环形指针 +public class HasCycle { + + class ListNode { + int val; + ListNode next; + + ListNode(int x) { + val = x; + next = null; + } + } + + public boolean hasCycle(ListNode head) { + HashSet hashSet = new HashSet<>(); + while (head != null) { + if (hashSet.contains(head)) { + return true; + }else { + hashSet.add(head); + } + head = head.next; + } + return false; + + } +} diff --git a/Week_00/practice/linkedlist/MergeTwoLists.java b/Week_00/practice/linkedlist/MergeTwoLists.java new file mode 100644 index 00000000..ff3d725f --- /dev/null +++ b/Week_00/practice/linkedlist/MergeTwoLists.java @@ -0,0 +1,42 @@ +package practice.linkedlist; + +// 21 合并2个有序链表 https://leetcode-cn.com/problems/merge-two-sorted-lists/ +public class MergeTwoLists { + public ListNode mergeTwoLists(ListNode l1, ListNode l2) { + + ListNode res = new ListNode(-1); + ListNode head = res; + + while (l1 != null && l2 != null) { + if (l1.val <= l2.val) { + head.next = l1; + l1 = l1.next; + } else { + head.next = l2; + l2 = l2.next; + } + head = head.next; + } + + head.next = l1 == null ? l2 : l1; + return res.next; + } +} + +class ListNode { + int val; + ListNode next; + + ListNode() { + } + + ListNode(int val) { + this.val = val; + } + + ListNode(int val, ListNode next) { + this.val = val; + this.next = next; + } + +} diff --git a/Week_00/practice/linkedlist/ReverseList.java b/Week_00/practice/linkedlist/ReverseList.java new file mode 100644 index 00000000..30bc7da7 --- /dev/null +++ b/Week_00/practice/linkedlist/ReverseList.java @@ -0,0 +1,31 @@ +package practice.linkedlist; + + +// 206 反转链表 +public class ReverseList { + + public class ListNode { + int val; + ListNode next; + + ListNode(int x) { + val = x; + } + } + + public ListNode reverseList(ListNode head) { + System.out.println(head); + ListNode pre = null; + ListNode cur = head; + ListNode tmp = null; + + while (cur != null) { + tmp = cur.next; + cur.next = pre; + pre = cur; + cur = tmp; + } + return pre; + + } +} diff --git a/Week_00/practice/lrucache/LRUCache.java b/Week_00/practice/lrucache/LRUCache.java new file mode 100644 index 00000000..eae0c97c --- /dev/null +++ b/Week_00/practice/lrucache/LRUCache.java @@ -0,0 +1,29 @@ +package practice.lrucache; + +import java.util.LinkedHashMap; +import java.util.Map; + +//146 LRUCache 缓存机制 https://leetcode-cn.com/problems/lru-cache/#/ +public class LRUCache { + private final int capacity; + private LinkedHashMap map; + + + public LRUCache(int capacity) { + this.capacity = capacity; + map = new LinkedHashMap(capacity, 0.75f, true) { + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > capacity; + } + }; + } + + public int get(int key) { + return map.getOrDefault(key, -1); + } + + public void put(int key, int value) { + map.put(key, value); + } +} diff --git a/Week_00/practice/recusion/Combine.java b/Week_00/practice/recusion/Combine.java new file mode 100644 index 00000000..02a2e6f9 --- /dev/null +++ b/Week_00/practice/recusion/Combine.java @@ -0,0 +1,41 @@ +package practice.recusion; + +import java.util.ArrayList; +import java.util.List; + +// 77 组合 https://leetcode-cn.com/problems/combinations/ +public class Combine { + + public static void main(String[] args) { + List> combine = new Combine().combine(3, 2); + combine.forEach(System.out::println); + } + + + /** + * 像列举出所有组合的题,是经典的回溯算法题,可以根据dfs的思想来解题。 + */ + public List> combine(int n, int k) { + List> resList = new ArrayList<>(); + dfs(1, n, k, new ArrayList(), resList); + return resList; + } + + private void dfs(int begin, int n, int k, ArrayList curList, List> resList) { + //recursion terminator + if (curList.size() == k) { + resList.add(new ArrayList<>(curList)); + return; + } + int loopTimes = n - (k - curList.size()) + 1; //剪枝优化: 本层最多可以从第几个数开始遍历(主要用于删去不必要的遍历操作,比如数不够就没必要再遍历了。) + for (int i = begin; i <= loopTimes; i++) { + // process + curList.add(i); + // drill down + dfs(i + 1, n, k, curList, resList); + // revert state + curList.remove(curList.size() - 1); + } + } + +} diff --git a/Week_00/practice/recusion/GenerateParenthesis.java b/Week_00/practice/recusion/GenerateParenthesis.java new file mode 100644 index 00000000..588992ce --- /dev/null +++ b/Week_00/practice/recusion/GenerateParenthesis.java @@ -0,0 +1,79 @@ +package practice.recusion; + +import java.util.*; + +//22 括号生成 https://leetcode-cn.com/problems/generate-parentheses/ + +/** + * 数字 n 代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。 + */ + + +public class GenerateParenthesis { + + public static void main(String[] args) { + +// System.out.println(new GenerateParenthesis().generateParenthesis(3)); + System.out.println(new GenerateParenthesis().generateParenthesis_dp(3)); + } + + public List generateParenthesis(int n) { + List resList = new ArrayList<>(); + dfs(resList, "", 0, 0, n); + return resList; + } + + /** + * dfs + */ + private void dfs(List resList, String s, int leftCnt, int rightCnt, int target) { + // recursion terminator + if (rightCnt == target && leftCnt == target) { + resList.add(s); + return; + } + + // process 剪枝 + if (leftCnt < rightCnt) return; + + // drill down + if (leftCnt < target) dfs(resList, s + "(", leftCnt + 1, rightCnt, target); + if (rightCnt < target) dfs(resList, s + ")", leftCnt, rightCnt + 1, target); + } + + + /** + * 动态规划 + */ + + public List generateParenthesis_dp(int n) { + LinkedList> result = new LinkedList>(); + if (n == 0) + return result.get(0); + LinkedList list0 = new LinkedList(); + list0.add(""); + result.add(list0); + LinkedList list1 = new LinkedList(); + list1.add("()"); + result.add(list1); + for (int i = 2; i <= n; i++) { + LinkedList temp = new LinkedList(); + for (int j = 0; j < i; j++) { + List str1 = result.get(j); + List str2 = result.get(i - 1 - j); + for (String s1 : str1) { + for (String s2 : str2) { + String el = "(" + s1 + ")" + s2; + System.out.println("s1 :"+ s1 + "\n s2: " + s2 + "\n el: " + el); + System.out.println(""); + temp.add(el); + } + } + } + result.add(temp); + } + return result.get(n); + } + +} + diff --git a/Week_00/practice/recusion/MajorityElement.java b/Week_00/practice/recusion/MajorityElement.java new file mode 100644 index 00000000..148fe57d --- /dev/null +++ b/Week_00/practice/recusion/MajorityElement.java @@ -0,0 +1,24 @@ +package practice.recusion; + + +// 169 多数元素 https://leetcode-cn.com/problems/majority-element/description/ + +import java.util.Arrays; + +/** + * 给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数大于n/2的元素。 + * + * 你可以假设数组是非空的,并且给定的数组总是存在多数元素。 + */ +public class MajorityElement { + /** + * 分治算法 + * + * @param nums + * @return + */ + public int majorityElement(int[] nums) { + Arrays.sort(nums); + return nums[nums.length/2]; + } +} diff --git a/Week_00/practice/recusion/MyPow.java b/Week_00/practice/recusion/MyPow.java new file mode 100644 index 00000000..08cdf701 --- /dev/null +++ b/Week_00/practice/recusion/MyPow.java @@ -0,0 +1,40 @@ +package practice.recusion; + +// 50 x的n次幂 https://leetcode-cn.com/problems/powx-n/ +public class MyPow { + + public static void main(String[] args) { + System.out.println(Integer.MAX_VALUE); + System.out.println(Integer.MIN_VALUE); + } + + public double myPow(double x, int n) { +// return recursion(1, x, n); // 暴力递归 + + return n > 0 ? _divide(x, n) : 1/_divide(x, -n); //分治算法 + } + + /** + * 分治 + */ + private double _divide(double x, int n) { + //terminator + if (n == 0) return 1.0; + + double res = _divide(x, n / 2); // x的n/2次幂的结果 + + if (n % 2 == 0) return res * res; // n为偶数 即 res * res + else return res * res * x; // n为奇数,需要再多乘一个 + } + + + /** + * 递归 n变大会超时 + */ + private double recursion(double res, double x, int n) { + if (n-- == 0) return res; + return recursion(res * x, x, n); + } + + +} diff --git a/Week_00/practice/recusion/Permute.java b/Week_00/practice/recusion/Permute.java new file mode 100644 index 00000000..9fbaaea1 --- /dev/null +++ b/Week_00/practice/recusion/Permute.java @@ -0,0 +1,48 @@ +package practice.recusion; + +import java.util.ArrayList; +import java.util.List; + +// 46 全排列 https://leetcode-cn.com/problems/permutations/ +public class Permute { + + public static void main(String[] args) { + new Permute().permute(new int[]{1, 2, 3}).forEach(System.out::println); + } + + public List> permute(int[] nums) { + List> resList = new ArrayList<>(); + int[] visited = new int[nums.length]; //记录当前list中访问nums元素的情况,1表示curList中已经存在nums[0]元素 + dfs(nums, new ArrayList(), resList, visited); + return resList; + } + + /** + * 回溯 dfs + * @param nums + * @param curList + * @param resList + * @param visited + */ + private void dfs(int[] nums, ArrayList curList, List> resList, int[] visited) { + //recursion terminator + if (curList.size() == nums.length) { + resList.add(new ArrayList<>(curList)); + return; + } + + for (int i = 0; i < nums.length; i++) { + //process + if (visited[i] != 0) continue; + curList.add(nums[i]); + visited[i] = 1; + + //drill down + dfs(nums, curList, resList, visited); + + // revert state + curList.remove(curList.size() - 1); + visited[i] = 0; + } + } +} diff --git a/Week_00/practice/recusion/SolveNQueens.java b/Week_00/practice/recusion/SolveNQueens.java new file mode 100644 index 00000000..0e428321 --- /dev/null +++ b/Week_00/practice/recusion/SolveNQueens.java @@ -0,0 +1,77 @@ +package practice.recusion; +import java.util.ArrayList; +import java.util.List; + +// 51 N皇后 https://leetcode-cn.com/problems/n-queens/ +public class SolveNQueens { + + public static void main(String[] args) { + System.out.println(new SolveNQueens().solveNQueens(4)); + } + + List> resList = new ArrayList<>(); + int[] cols; + int[] leftX; + int[] rightX; + + /** + * 回溯法 + * 1 每列, 对角线不能同时存在2个皇后。 + * 通过数组对每个节点hash到每列,对角线, + * 这里只需要一维数组,不同于数独要映射9个数,这里只需要映射一整列,对角线 + * 2 针对每个格子进行回溯 + * 3 满足条件加入到结果集,再回溯清除当前状态,因为可能存在多个解 + */ + public List> solveNQueens(int n) { + if (n == 0) return resList; + cols = new int[n]; + leftX = new int[2 * n - 1]; + rightX = new int[2 * n - 1]; + + List curFlag = new ArrayList<>(); + dfs(n, 0, curFlag); + return resList; + } + + private void dfs(int n, int row, List curFlag) { + if (row == n) {// 填完,记录结果并返回 + resList.add(arrayToString(curFlag, n)); + return; + } + //对每个格子进行回溯 + for (int i = 0; i < n; i++) { + if (cols[i] == 0 && leftX[row + i] == 0 && rightX[row - i + n - 1] == 0) { //[row - i]下标可能为负数,加上n - 1凑0 + curFlag.add(i); + cols[i] = 1; + leftX[row + i] = 1; + rightX[row - i + n - 1] = 1; + + dfs(n, row + 1, curFlag); + + curFlag.remove(curFlag.size() - 1); + cols[i] = 0; + leftX[row + i] = 0; + rightX[row - i + n - 1] = 0; + } + } + } + + /** + * 数组到列表的格式化 + * + * @param curFlag + * @param n + * @return + */ + private List arrayToString(List curFlag, int n) { + List lst = new ArrayList<>(); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < n; i++) sb.append("."); + for (int numFlag : curFlag) { + StringBuilder cpSb = new StringBuilder(sb); + String str = cpSb.replace(numFlag, numFlag + 1, "Q").toString(); + lst.add(str); + } + return lst; + } +} diff --git a/Week_00/practice/recusion/SolveSudoku.java b/Week_00/practice/recusion/SolveSudoku.java new file mode 100644 index 00000000..15d4bb70 --- /dev/null +++ b/Week_00/practice/recusion/SolveSudoku.java @@ -0,0 +1,77 @@ +package practice.recusion; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +//37 解数独 https://leetcode-cn.com/problems/sudoku-solver/#/description +public class SolveSudoku { + + public static void main(String[] args) { + int num = 1; + char a = (char) num; + char b = '2'; + System.out.println(a); + } + + public void solveSudoku(char[][] board) { + //记录每行,列,块中被填的数据 + int[][] rows = new int[9][9]; + int[][] cols = new int[9][9]; + int[][] boxes = new int[9][9]; + + //初始化,在数独的每行,列,块中有哪些数已经被填 + for (int i = 0; i < 9; i++) { + for (int j = 0; j < 9; j++) { + if (board[i][j] == '.') { + continue; + } else { + int index = board[i][j] - '1'; + int[] box = boxes[i / 3 * 3 + j / 3]; + rows[i][index] = 1; + cols[j][index] = 1; + box[index] = 1; + } + } + } + //fill it + fillit(board, rows, cols, boxes, 0, 0); + } + + private boolean fillit(char[][] board, int[][] rows, int[][] cols, int[][] boxes, int row, int col) { + //列到头了就换行 + if (col > 8) { + col = 0; + row++; + } + //填满了 + if (row > 8) return true; + + //不为空,下一个 + if (board[row][col] != '.') { + return fillit(board, rows, cols, boxes, row, col + 1); + } + + //从 1 到 9 逐个回溯 + for (char i = '1'; i <= '9'; i++) { + int[] box = boxes[row / 3 * 3 + col / 3]; //计算块的索引 + int index = i - '1'; // hash + if (rows[row][index] == 1 || cols[col][index] == 1 || box[index] == 1) { //判断每 行,列,块中是否已经被填 + continue; + } else { + board[row][col] = i; + rows[row][index] = 1; + cols[col][index] = 1; + box[index] = 1; + if(fillit(board, rows, cols, boxes, row, col + 1)) return true; + else {//回溯当前层操作 + board[row][col] = '.'; + rows[row][index] = 0; + cols[col][index] = 0; + box[index] = 0; + } + } + } + return false; + } +} diff --git a/Week_00/practice/search/BinarySearch.java b/Week_00/practice/search/BinarySearch.java new file mode 100644 index 00000000..82b28758 --- /dev/null +++ b/Week_00/practice/search/BinarySearch.java @@ -0,0 +1,31 @@ +package practice.search; + +/** + * 33 搜索旋转排序数组 https://leetcode-cn.com/problems/search-in-rotated-sorted-array/ + */ +public class BinarySearch { + public static void main(String[] args) { + System.out.println(new BinarySearch().search(new int[]{4, 5, 6, 7, 0, 1, 2}, 0)); + } + + /** + * 中心思想: 判断target在 mid左边还是右边,就可以直接使用二分查找进行搜索。 + * 本体在if中列举了 所有可能在mid左边的情况 + */ + + public int search(int[] nums, int target) { + int left = 0; + int right = nums.length - 1; + + while (left <= right) { + int mid = left + (right - left) / 2; + if (nums[mid] == target) return mid; + + //枚举出 target在 mid 左边的条件 + else if ((nums[left] > nums[mid] && (target >= nums[left] || target < nums[mid])) || (target >= nums[left] && target < nums[mid])) { + right = mid - 1; + } else left = mid + 1; + } + return -1; + } +} diff --git a/Week_00/practice/sort/BubbleSort_2.java b/Week_00/practice/sort/BubbleSort_2.java new file mode 100644 index 00000000..c9b2a030 --- /dev/null +++ b/Week_00/practice/sort/BubbleSort_2.java @@ -0,0 +1,41 @@ +package practice.sort; + +import utils.Utils; + +public class BubbleSort_2 { + + public static void main(String[] args) { + int[] arr = new int[]{30, 20, 10, 5, 4, 3, -1, 2, 1, 0}; + bubbleSort(arr); + Utils.displayArray(arr); + } + + /** + * 冒泡排序 + */ + public static void bubbleSort(int[] arr) { + /*for (int i = 0; i < arr.length - 1; i++) { + for (int j = 0; j < arr.length - i - 1; j++) { + if (arr[j] > arr[j + 1]) { + int tmp = arr[j]; + arr[j] = arr[j + 1]; + arr[j + 1] = tmp; + } + } + }*/ + + + for (int i = 0; i < arr.length - 1; i++) { + for (int j = 0; j < arr.length - i - 1; j++) { + if (arr[j] > arr[j + 1]) { + arr[j] += arr[j + 1]; + arr[j + 1] = arr[j] - arr[j + 1]; + arr[j] = arr[j] - arr[j + 1]; + } + } + + } + + } + +} diff --git a/Week_00/practice/sort/HeapSort.java b/Week_00/practice/sort/HeapSort.java new file mode 100644 index 00000000..e7f9b53d --- /dev/null +++ b/Week_00/practice/sort/HeapSort.java @@ -0,0 +1,42 @@ +package practice.sort; + +import utils.Utils; + +public class HeapSort { + public static void main(String[] args) { + int[] arr = new int[]{30, 20, 10, 5, 4, 3, -1, 2, 1, 0}; + heapSort(arr); + Utils.displayArray(arr); + } + + + static void heapify(int[] array, int length, int i) { + int left = 2 * i + 1, right = 2 * i + 2;int largest = i; + if (left < length && array[left] > array[largest]) { + largest = left; + } + if (right < length && array[right] > array[largest]) { + largest = right; + } + if (largest != i) { + int temp = array[i]; + array[i] = array[largest]; + array[largest] = temp; + heapify(array, length, largest); + } + } + + public static void heapSort(int[] array) { + if (array.length == 0) return; + int length = array.length; + for (int i = length / 2 - 1; i >= 0; i --) + heapify(array, length, i); + + for (int i = length - 1; i >= 0; i--) { + int temp = array[0]; + array[0] = array[i]; + array[i] = temp; + heapify(array, i, 0); + } + } +} diff --git a/Week_00/practice/sort/InsertionSort_2.java b/Week_00/practice/sort/InsertionSort_2.java new file mode 100644 index 00000000..252dab49 --- /dev/null +++ b/Week_00/practice/sort/InsertionSort_2.java @@ -0,0 +1,41 @@ +package practice.sort; + +import utils.Utils; + +public class InsertionSort_2 { + + public static void main(String[] args) { + int[] arr = new int[]{30, 20, 10, 5, 4, 3, -1, 2, 1, 0}; + insertionSort(arr); + Utils.displayArray(arr); + } + + /** + * 插入排序 + * 遍历每个数,与它之前的数进行比较,直到最后一个比它大的数,交换位置。 + */ + private static void insertionSort(int[] arr) { + + /*for (int i = 0; i < arr.length - 1; i++) { + int preIndex = i; + int curNum = arr[preIndex + 1]; + while (preIndex >= 0 && curNum < arr[preIndex]) { + arr[preIndex + 1] = arr[preIndex]; + preIndex--; + } + arr[preIndex + 1] = curNum; + }*/ + + + for (int i = 0; i < arr.length - 1; i++) { + int preIndex = i, curNum = arr[preIndex + 1]; + while (preIndex >= 0 && curNum < arr[preIndex]) { + arr[preIndex + 1] = arr[preIndex]; + preIndex--; + } + arr[preIndex + 1] = curNum; + } + + } + +} diff --git a/Week_00/practice/sort/MergeRange_2.java b/Week_00/practice/sort/MergeRange_2.java new file mode 100644 index 00000000..8ecf12ea --- /dev/null +++ b/Week_00/practice/sort/MergeRange_2.java @@ -0,0 +1,33 @@ +package practice.sort; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; + +//56 合并区间 https://leetcode-cn.com/problems/merge-intervals/ +public class MergeRange_2 { + public static void main(String[] args) { + } + + /** + * intervals是 n行2列 ex: [(1, 2)], [(2, 4)] + * 把intervals按照 元组的第一个元素升序排序 + * 把第一个元组放入list, 遍历所有元组,与list的最后一个元组比较边界 + */ + public int[][] merge(int[][] intervals) { + if (intervals.length < 2) return intervals; + + ArrayList resList = new ArrayList<>(); + Arrays.sort(intervals, (v1, v2) -> v1[0] - v2[0]); + resList.add(intervals[0]); + + for(int i = 1; i < intervals.length; i++) { + int[] curRange = intervals[i]; + int[] lastRange = resList.get(resList.size() - 1); + + if(lastRange[1] < curRange[0]) resList.add(curRange); + else lastRange[1] = Math.max(lastRange[1], curRange[1]); + } + return resList.toArray(new int[resList.size()][1]); + } +} diff --git a/Week_00/practice/sort/MergeSort_2.java b/Week_00/practice/sort/MergeSort_2.java new file mode 100644 index 00000000..6ab3684c --- /dev/null +++ b/Week_00/practice/sort/MergeSort_2.java @@ -0,0 +1,40 @@ +package practice.sort; + +import utils.Utils; + +public class MergeSort_2 { + public static void main(String[] args) { + int[] arr = new int[]{30, 20, 10, 5, 4, 3, -1, 40, 1, 0}; + mergeSort(arr, 0, arr.length - 1); + Utils.displayArray(arr); + } + + /** + * 只操作一个数组,通过下标限制排序的范围 + */ + private static void mergeSort(int[] arr, int begin, int end) { + // terminator + if (end <= begin) return; + + //process and drill down + int mid = (begin + end) >> 1; + mergeSort(arr, begin, mid); + mergeSort(arr, mid + 1, end); + merge(arr, begin, mid, end); + } + + private static void merge(int[] arr, int begin, int mid, int end) { + int[] tmp = new int[end - begin + 1]; + int left = begin, right = mid + 1, tmpIndex = 0; + + while (left <= mid && right <= end) { + tmp[tmpIndex++] = arr[left] < arr[right] ? arr[left++] : arr[right++]; + } + while (left <= mid) tmp[tmpIndex++] = arr[left++]; + while (right <= end) tmp[tmpIndex++] = arr[right++]; + + for (int i = 0; i < tmp.length; i++) arr[begin + i] = tmp[i]; + } + + +} diff --git a/Week_00/practice/sort/QuickSort_2.java b/Week_00/practice/sort/QuickSort_2.java new file mode 100644 index 00000000..c32a9bf7 --- /dev/null +++ b/Week_00/practice/sort/QuickSort_2.java @@ -0,0 +1,48 @@ +package practice.sort; + +import utils.Utils; + +public class QuickSort_2 { + + + public static void main(String[] args) { + int[] arr = new int[]{30, 20, 10, 5, 4, 3, -1, 40, 1, 0}; + quickSort(arr, 0, arr.length - 1); + Utils.displayArray(arr); + } + + /** + * 根据pivot进行分治排序 + */ + private static void quickSort(int[] arr,int begin, int end) { + //terminator + if (end <= begin) return; + //process + int pivot = partition(arr, begin, end); + //drill down + merge + quickSort(arr, begin, pivot - 1); + quickSort(arr, pivot + 1, end); + } + + /** + * 目的是返回pivot, 首次默认以end作为pivot + */ + private static int partition(int[] arr, int begin, int end) { + int pivot = end; + int curIndex = begin; + for (int i = begin + 1; i < end; i++) { + if(arr[i] < arr[pivot]) + swap(arr, curIndex++, i); + } + swap(arr, pivot, curIndex); + return curIndex; + } + + private static void swap(int[] arr, int i, int j) { + int tmp = arr[i]; + arr[i] = arr[j]; + arr[j] = tmp; + } + + +} diff --git a/Week_00/practice/sort/RelativeSortArray_2.java b/Week_00/practice/sort/RelativeSortArray_2.java new file mode 100644 index 00000000..91023809 --- /dev/null +++ b/Week_00/practice/sort/RelativeSortArray_2.java @@ -0,0 +1,52 @@ +package practice.sort; + +import java.util.Map; +import java.util.TreeMap; + +//1122 数组的相对排序 https://leetcode-cn.com/problems/relative-sort-array/ +public class RelativeSortArray_2 { + public static void main(String[] args) { + + + } + + public int[] relativeSortArray_1(int[] arr1, int[] arr2) { + int[] arrCnt = new int[1001]; + int[] res = new int[arr1.length]; + int index = 0; + + for (int i : arr1) arrCnt[i]++; //计数 + for (int i : arr2) + while (arrCnt[i]-- > 0) res[index++] = i; + + for (int i = 0; i < arrCnt.length; i++) + while (arrCnt[i]-- > 0) res[index++] = i; + + return res; + } + + public int[] relativeSortArray(int[] arr1, int[] arr2) { + int[] res = new int[arr1.length]; + int index = 0; + TreeMap map = new TreeMap<>(); + + for (int i = 0; i < arr1.length; i++) { + map.put(arr1[i], map.getOrDefault(arr1[i], 0) + 1); + } + + for (int i = 0; i < arr2.length; i++) { + int key = arr2[i]; + int value = map.get(key); + while (value-- > 0) res[index++] = arr2[i]; + map.remove(key); + } + + for (Map.Entry entry : map.entrySet()) { + int key = entry.getKey(); + int value = entry.getValue(); + while (value-- > 0) + res[index++] = key; + } + return res; + } +} diff --git a/Week_00/practice/sort/SelectionSort_2.java b/Week_00/practice/sort/SelectionSort_2.java new file mode 100644 index 00000000..f4457d7c --- /dev/null +++ b/Week_00/practice/sort/SelectionSort_2.java @@ -0,0 +1,29 @@ +package practice.sort; + +import utils.Utils; + +public class SelectionSort_2 { + public static void main(String[] args) { + int[] arr = new int[]{30, 20, 10, 5, 4, 3, -1, 2, 1, 0}; + selectSort(arr); + Utils.displayArray(arr); + } + + /** + * 选择排序 : 选最小的数替换数组 + */ + private static void selectSort(int[] arr) { + for (int i = 0; i < arr.length - 1; i++) { + int minIndex = i; + + for (int j = i + 1; j < arr.length; j++) { + if (arr[minIndex] > arr[j]) + minIndex = j; + } + int tmp = arr[i]; + arr[i] = arr[minIndex]; + arr[minIndex] = tmp; + } + } + +} diff --git a/Week_00/practice/stack/Again.java b/Week_00/practice/stack/Again.java new file mode 100644 index 00000000..df245083 --- /dev/null +++ b/Week_00/practice/stack/Again.java @@ -0,0 +1,33 @@ +package practice.stack; + +import utils.Utils; + +import java.util.LinkedList; + +public class Again { + public static void main(String[] args) { + int a = 10; + int b = 10; + new LinkedList(); + System.out.println(a ^ b); + + } + + public int[] relativeSortArray(int[] arr1, int[] arr2) { + int[] arr = new int[1000]; + int index = 0; + for (int i = 0; i < arr1.length; i++) arr[arr1[i]]++; + for (int i = 0; i < arr2.length; i++) { + while (arr[arr2[i]]-- > 0) { + arr1[index++] = arr2[i]; + } + } + for (int i = 0; i < arr.length; i++) { + while (arr[i]-- > 0) arr1[index++] = i; + } + + return arr1; + } + + +} diff --git a/Week_00/practice/stack/IsValid.java b/Week_00/practice/stack/IsValid.java new file mode 100644 index 00000000..81945430 --- /dev/null +++ b/Week_00/practice/stack/IsValid.java @@ -0,0 +1,48 @@ +package practice.stack; + + +import java.util.Stack; + +// 20 有效的括号 https://leetcode-cn.com/problems/valid-parentheses/ +public class IsValid { + + public static void main(String[] args) { + Stack stack = new Stack<>(); + stack.pop(); + + } + + public static boolean isValidByReplace(String s) { + while (s.contains("()") || s.contains("{}") || s.contains("[]")) { + if (s.contains("()")) s = s.replaceAll("\\(\\)",""); + if (s.contains("{}")) s = s.replaceAll("\\{\\}",""); + if (s.contains("[]")) s = s.replaceAll("\\[\\]",""); + } + return s.length() == 0; + } + + + + + + /** + * 入栈 出栈法 + * @param s + * @return + */ + public static boolean isValid(String s) { + Stack stack = new Stack<>(); + char[] chars = s.toCharArray(); + if (chars.length % 2 != 0) {return false;} + for (char c : chars) { + if (c == '(') stack.push('('); + else if (c == '{') stack.push('{'); + else if (c == '[') stack.push('['); + else if (stack.size() > 0 && c == ')' && stack.pop() == '(') continue; + else if (stack.size() > 0 && c == '}' && stack.pop() == '{') continue; + else if (stack.size() > 0 && c == ']' && stack.pop() == '[') continue; + else return false; + } + return stack.empty(); + } +} diff --git a/Week_00/practice/stack/MinStack.java b/Week_00/practice/stack/MinStack.java new file mode 100644 index 00000000..db055e1e --- /dev/null +++ b/Week_00/practice/stack/MinStack.java @@ -0,0 +1,35 @@ +package practice.stack; + +import java.util.Stack; +//155 最小栈 https://leetcode-cn.com/problems/min-stack/ +public class MinStack { + Stack stack; + int minValue; + + public MinStack() { + stack = new Stack<>(); + } + + public void push(int x) { + if (stack.size() > 0 && x <= minValue) { + stack.push(minValue); + minValue = x; + } else if (stack.size() == 0) { + minValue = x; + } + stack.push(x); + } + + public void pop() { + if (stack.pop() == minValue && stack.size() > 0) minValue = stack.pop(); + } + + + public int top() { + return stack.peek(); + } + + public int getMin() { + return minValue; + } +} diff --git a/Week_00/practice/stack/SwapPairs.java b/Week_00/practice/stack/SwapPairs.java new file mode 100644 index 00000000..e717c083 --- /dev/null +++ b/Week_00/practice/stack/SwapPairs.java @@ -0,0 +1,78 @@ +package practice.stack; + +import java.util.Stack; + +// 24 两两交换链表中的节点 https://leetcode-cn.com/problems/swap-nodes-in-pairs/ +public class SwapPairs { + + public static void main(String[] args) { + ListNode head = new ListNode(10); + ListNode head1 = new ListNode(9); + ListNode head2 = new ListNode(8); + ListNode head3 = new ListNode(7); + head.next = head1; + head1.next = head2; + head2.next = head3; + + swapPairs(head); + } + + + public static ListNode swapPairs(ListNode head) { + if (head == null || head.next == null) return head; + + // 初始化栈 , 虚拟头节点 ,指向虚拟头节点的引用(用于结果返回), 当前指针 + Stack stack = new Stack(); + ListNode vhead = new ListNode(); + ListNode res = vhead; + ListNode cur = head; + // 循环 入栈, 出栈 + while (cur != null && cur.next != null) { + stack.push(cur); + stack.push(cur.next); + System.out.println(" stack size : " + stack.size()); + cur = cur.next.next; + + vhead.next = stack.pop(); + vhead = vhead.next; + vhead.next = stack.pop(); + vhead = vhead.next; + + displayNode(vhead); + } + + if(cur != null) vhead.next = cur; + else vhead.next = null; + + // 返回虚拟头节点的next节点 + return res.next; + } + + public static void displayNode(ListNode vhead) { + while (vhead != null){ + System.out.print(vhead.val + " , "); + vhead =vhead.next; + } + System.out.println(" "); + } + + +} + + +class ListNode { + int val; + ListNode next; + + ListNode() { + } + + ListNode(int val) { + this.val = val; + } + + ListNode(int val, ListNode next) { + this.val = val; + this.next = next; + } +} \ No newline at end of file diff --git a/Week_00/practice/string/IsAnagram_2.java b/Week_00/practice/string/IsAnagram_2.java new file mode 100644 index 00000000..85d6a913 --- /dev/null +++ b/Week_00/practice/string/IsAnagram_2.java @@ -0,0 +1,25 @@ +package practice.string; + +//242 有效的字母异位词 https://leetcode-cn.com/problems/valid-anagram/ +public class IsAnagram_2 { + + public static void main(String[] args) { + System.out.println('b' - 'a'); + } + + public boolean isAnagram(String s, String t) { + /*int[] arrCnt = new int[26]; + for (char ch : s.toCharArray()) arrCnt[ch - 'a']++; + for (char ch : t.toCharArray()) arrCnt[ch - 'a']--; + for (int num : arrCnt) if (num != 0) return false; + return true;*/ + + + int[] arr = new int[26]; + int index = 0; + for (int i = 0; i < s.length(); i++) arr[s.charAt(i) - 'a']++; + for (int i = 0; i < t.length(); i++) arr[t.charAt(i) - 'a']--; + while (index < arr.length) if(arr[index++] != 0) return false; + return true; + } +} diff --git a/Week_00/practice/string/IsIsomorphic.java b/Week_00/practice/string/IsIsomorphic.java new file mode 100644 index 00000000..166330a7 --- /dev/null +++ b/Week_00/practice/string/IsIsomorphic.java @@ -0,0 +1,31 @@ +package practice.string; + +import java.util.HashMap; + +// 205. 同构字符串 https://leetcode-cn.com/problems/isomorphic-strings/ +public class IsIsomorphic { + + public static void main(String[] args) { + System.out.println(new IsIsomorphic().isIsomorphic("egg", "add")); + } + + /** + * 通过map映射 : 每次put操作之前判断之前是否已经映射, 若是,比较已经映射的值 和 即将映射的值 知否相等。 + * 如果之前没有被映射, 还要判断即将映射的值是否已经被映射。 + */ + public boolean isIsomorphic(String s, String t) { + HashMap map = new HashMap<>(); + int index = 0; + while (index < s.length()) { + if (map.containsKey(s.charAt(index))) { + if (map.get(s.charAt(index)) != t.charAt(index)) return false; + } else if (!map.containsValue(t.charAt(index))){ + map.put(s.charAt(index), t.charAt(index)); + }else return false; + index++; + } + return true; + } + + +} diff --git a/Week_00/practice/string/LongestPalindrome.java b/Week_00/practice/string/LongestPalindrome.java new file mode 100644 index 00000000..21150517 --- /dev/null +++ b/Week_00/practice/string/LongestPalindrome.java @@ -0,0 +1,43 @@ +package practice.string; + +//5 最长回文子串 https://leetcode-cn.com/problems/longest-palindromic-substring/ +public class LongestPalindrome { + public static void main(String[] args) { + System.out.println(new LongestPalindrome().longestPalindrome("")); + } + + + /** + * 动态规划 + * + * + */ + public String longestPalindromeByDp(String s) { + return null; + } + + + + /** + * 遍历每个字符 , 向两边扩散 + */ + public String longestPalindrome(String s) { + String maxLenStr = s.substring(0,1); + for (int i = 0; i < s.length(); i++) { + String len1Str = ""; + if (i > 0) len1Str = getPalindrome(s, i - 1, i + 1); + String len2Str = getPalindrome(s, i, i + 1); + String resStr = len1Str.length() > len2Str.length() ? len1Str : len2Str; + maxLenStr = maxLenStr.length() > resStr.length() ? maxLenStr : resStr; + } + return maxLenStr; + } + + private String getPalindrome(String s, int l, int r) { + while (l >= 0 && r < s.length() && s.charAt(l) == s.charAt(r)) { + l--; r++; + } + return s.substring(l + 1, r); + } + +} diff --git a/Week_00/practice/string/ReverseOnlyLetters.java b/Week_00/practice/string/ReverseOnlyLetters.java new file mode 100644 index 00000000..8e877f53 --- /dev/null +++ b/Week_00/practice/string/ReverseOnlyLetters.java @@ -0,0 +1,27 @@ +package practice.string; + +//917 仅仅翻转字母 https://leetcode-cn.com/problems/reverse-only-letters/ +public class ReverseOnlyLetters { + + public static void main(String[] args) { + System.out.println(new ReverseOnlyLetters().reverseOnlyLetters("7_28]")); + + } + + /** + * 双指针夹逼 + */ + public String reverseOnlyLetters(String S) { + int begin = 0, end = S.length() - 1; + char[] charArray = S.toCharArray(); + + while (begin < end) { + while (!Character.isLetter(charArray[begin]) && begin < end) begin++; + while (!Character.isLetter(charArray[end]) && begin < end) end--; + char tmp = charArray[begin]; + charArray[begin++] = charArray[end]; + charArray[end--] = tmp; + } + return new String(charArray); + } +} diff --git a/Week_00/practice/string/ReverseStr.java b/Week_00/practice/string/ReverseStr.java new file mode 100644 index 00000000..14fbcde3 --- /dev/null +++ b/Week_00/practice/string/ReverseStr.java @@ -0,0 +1,24 @@ +package practice.string; + +//541. 反转字符串 II https://leetcode-cn.com/problems/reverse-string-ii/ +public class ReverseStr { + + public static void main(String[] args) { + System.out.println(new ReverseStr().reverseStr("abcd1234jkmn12", 2)); + } + + public String reverseStr(String s, int k) { + char[] strArr = s.toCharArray(); + for (int i = 0; i < s.length(); i += 2 * k) { //每次只对 (2k * i, k)范围内的前k个字符操作 + int start = i; //双指针 + int end = Math.min(i + k - 1, s.length() - 1); //如果 s.length < k ,只对k个字符反转 + while (start < end) { + char tmp = strArr[start]; + strArr[start++] = strArr[end]; + strArr[end--] = tmp; + } + } + + return new String(strArr); + } +} diff --git a/Week_00/practice/string/ReverseWords.java b/Week_00/practice/string/ReverseWords.java new file mode 100644 index 00000000..d0fee99c --- /dev/null +++ b/Week_00/practice/string/ReverseWords.java @@ -0,0 +1,21 @@ +package practice.string; + + +// 151. 翻转字符串里的单词 https://leetcode-cn.com/problems/reverse-words-in-a-string/ +public class ReverseWords { + + public static void main(String[] args) { + System.out.println(new ReverseWords().reverseWords("a good example")); + } + public String reverseWords(String s) { + String[] str = s.trim().split(" "); + StringBuilder sb = new StringBuilder(); + for (int i = str.length - 1; i >= 0; i--) { + if (!str[i].equals("")) { + sb.append(str[i]); + if(i > 0) sb.append(" "); + } + } + return sb.toString(); + } +} diff --git a/Week_00/practice/string/ReverseWords_3.java b/Week_00/practice/string/ReverseWords_3.java new file mode 100644 index 00000000..d942f991 --- /dev/null +++ b/Week_00/practice/string/ReverseWords_3.java @@ -0,0 +1,46 @@ +package practice.string; + +// 557. 反转字符串中的单词 III https://leetcode-cn.com/problems/reverse-words-in-a-string-iii/ +public class ReverseWords_3 { + + public static void main(String[] args) { + System.out.println(new ReverseWords_3().reverseWords("Let's take LeetCode contest")); + System.out.println(new ReverseWords_3().reverseWords_2("Let's take LeetCode contest")); + } + + /** + * 系统api解法 + */ + public String reverseWords(String s) { + String[] strArr = s.split(" "); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < strArr.length; i++) { + if (!strArr[i].equals("")) sb.append(new StringBuilder(strArr[i]).reverse().toString()); + if (i < strArr.length - 1) sb.append(" "); + } + return sb.toString(); + } + + + /** + * 双指针解法 + */ + public String reverseWords_2(String s) { + char[] chArr = s.toCharArray(); + int start = 0, end = 0, length = s.length(); + while (end < length) { + while (end < length && chArr[end] != ' ') end++; //找到空格,然后翻转空格前的单词 + swapChar(chArr, start, end - 1); + start = end++ + 1; + } + return new String(chArr); + } + + private void swapChar(char[] chArr, int start, int end) { + while (start < end) { + char tmp = chArr[start]; + chArr[start++] = chArr[end]; + chArr[end--] = tmp; + } + } +} diff --git a/Week_00/practice/string/ToLowerCase.java b/Week_00/practice/string/ToLowerCase.java new file mode 100644 index 00000000..343551f3 --- /dev/null +++ b/Week_00/practice/string/ToLowerCase.java @@ -0,0 +1,17 @@ +package practice.string; + +public class ToLowerCase { + public static void main(String[] args) { + System.out.println(" aa bb cc ".trim()); + } + + public String toLowerCase(String str) { + char[] chars = str.toCharArray(); + for(int i = 0; i < str.length(); i++) { + if (chars[i] >= 'A' && chars[i] <= 'Z') chars[i] += 32; + } + + return new String(chars); + + } +} diff --git a/Week_00/practice/string/ValidPalindrome.java b/Week_00/practice/string/ValidPalindrome.java new file mode 100644 index 00000000..9203cf73 --- /dev/null +++ b/Week_00/practice/string/ValidPalindrome.java @@ -0,0 +1,30 @@ +package practice.string; + + +// 680 验证回文字符串 https://leetcode-cn.com/problems/valid-palindrome-ii/ +public class ValidPalindrome { + + public static void main(String[] args) { + System.out.println(new ValidPalindrome().validPalindrome + ("lcupuufxoohdfpgjdmysgvhmvffcnqxjjxqncffvmhvgsymdjgpfdhooxfuupucul")); + } + + /** + * 双指针 + 递归 + * @param s + * @return + */ + public boolean validPalindrome(String s) { + int begin = 0, end = s.length() - 1; + int cnt = 0; + return isvalidPalindrome(s, begin, end, cnt); + } + + private boolean isvalidPalindrome(String s, int begin, int end, int cnt) { + if(cnt > 1) return false; + if(begin >= end) return true; + if (s.charAt(begin) == s.charAt(end)) return isvalidPalindrome(s, begin + 1, end - 1, cnt); + return isvalidPalindrome(s, begin + 1, end, cnt + 1) || isvalidPalindrome(s, begin, end - 1, cnt + 1); + } + +} diff --git a/Week_00/practice/tree/BreadthFirstSearchNTree.java b/Week_00/practice/tree/BreadthFirstSearchNTree.java new file mode 100644 index 00000000..bb708c3d --- /dev/null +++ b/Week_00/practice/tree/BreadthFirstSearchNTree.java @@ -0,0 +1,105 @@ +package practice.tree; + +import java.util.*; + +/** + * 429 N叉树的中层遍历 https://leetcode-cn.com/problems/n-ary-tree-level-order-traversal/ + * + * + * 总结: 这题用递归比迭代麻烦的地方在于,递归需要维护一个和层数映射的map用于收集多个系统栈的数据。 + * 而迭代方便的地方在于,每次只需要把下一次要访问的节点放到队列中,然后进行循环,就可以保证最外层 + * 的循环可以把一层的所有节点都访问到,作为一层写入一个list中。 + * + */ +public class BreadthFirstSearchNTree { + + /** + * 迭代 bfs + * + * 思路: 每次都把下一次需要访问的节点都放入队列中 + * @param root + * @return + */ + public List> levelOrderByIteration(Node root) { + if (root == null) return new ArrayList<>(); + List> resList = new ArrayList<>(); + Queue queue = new LinkedList<>(); + queue.add(root); + + while (!queue.isEmpty()) { + List curLevel = new ArrayList<>(); + int size = queue.size(); + for (int i = 0; i < size; i++) { + Node node = queue.poll(); + curLevel.add(node.val); + queue.addAll(node.children); + } + resList.add(curLevel); + } + return resList; + } + + /** + * 429 递归 bfs + * + * @param root + * @return + */ + public List> levelOrderByRecursion(Node root) { + if (root == null) + return new ArrayList<>(); + + //initialize + List> resList = new ArrayList<>(); + LinkedHashMap> linkedHashMap = new LinkedHashMap<>(); + resList.add(Arrays.asList(root.val)); + + bfsByRecursion(root, 1, linkedHashMap); + + //move to list from hashMap + for (Map.Entry> entry : linkedHashMap.entrySet()) resList.add(entry.getValue()); + + return resList; + } + + /** + * @param root + * @param level 层数 + * @param linkedHashMap 层数, 对应的当前层数的list + */ + private void bfsByRecursion(Node root, int level, LinkedHashMap> linkedHashMap) { + //recurtion terminator + if (root == null) return; + + for (Node childNode : root.children) { + if (childNode == null) continue; + + // process + List list = linkedHashMap.getOrDefault(level, new ArrayList()); + list.add(childNode.val); + linkedHashMap.put(level, list); + + //drill down + bfsByRecursion(childNode, level + 1, linkedHashMap); + } + } + + + + class Node { + public int val; + public List children; + + public Node() { + } + + public Node(int _val) { + val = _val; + } + + public Node(int _val, List _children) { + val = _val; + children = _children; + } + }; +} diff --git a/Week_00/practice/tree/FindWords.java b/Week_00/practice/tree/FindWords.java new file mode 100644 index 00000000..9f07d530 --- /dev/null +++ b/Week_00/practice/tree/FindWords.java @@ -0,0 +1,83 @@ +package practice.tree; + + +import java.util.*; + +// 212 单词搜索II https://leetcode-cn.com/problems/word-search-ii/ +public class FindWords { + public List findWords(char[][] board, String[] words) { + + /** + * 1 先把words生成字典树 + * 2 遍历board每个字符去字典树中查找。 + * 3 需要对board中的每个字符进行dfs搜索 + */ + + HashSet resSet = new HashSet<>(); + Trie trie = new Trie(); + for (String word : words) trie.insert(word); + + int rows = board.length; + int cols = board[0].length; + boolean[][] visited = new boolean[rows][cols]; + + for (int i = 0; i < rows; i++) { + for (int j = 0; j < cols; j++) { + char ch = board[i][j]; + dfs(trie, board, i, j, resSet, visited); + } + } + return new ArrayList<>(resSet); + } + + private void dfs(Trie curNode, char[][] board, int i, int j, HashSet hashSet, boolean[][] visited) { + if (i < 0 || i > board.length - 1 || j < 0 || j > board[0].length - 1 || visited[i][j]) return; + + visited[i][j] = true; + curNode = curNode.next[board[i][j] - 'a']; //拿到字符在字典中对应位置的值 + if (curNode == null) {//当前字母不在字典中,清除访问状态并返回 + visited[i][j] = false; //清除状态 + return; + } + if (curNode.isEnd) hashSet.add(curNode.val);//当前字母不为空,且是叶子节点,把对应的值加入到list + + dfs(curNode, board, i + 1, j, hashSet, visited); + dfs(curNode, board, i - 1, j, hashSet, visited); + dfs(curNode, board, i, j + 1, hashSet, visited); + dfs(curNode, board, i, j - 1, hashSet, visited); + + visited[i][j] = false; //清除状态 + } + + + class Trie { + /** + * Initialize your data structure here. + */ + private boolean isEnd; + private Trie[] next; + private String val; + + public Trie() { + isEnd = false; + next = new Trie[26]; + } + + /** + * Inserts a word into the trie. + */ + public void insert(String word) { + if (word == null || word.length() == 0) return; + char[] chars = word.toCharArray(); + + Trie cur = this; + for (Character ch : chars) { + int index = ch - 'a'; + if (cur.next[index] == null) cur.next[index] = new Trie(); + cur = cur.next[index]; + } + cur.isEnd = true; + cur.val = word; + } + } +} diff --git a/Week_00/practice/tree/LevelOrder.java b/Week_00/practice/tree/LevelOrder.java new file mode 100644 index 00000000..12ddea2b --- /dev/null +++ b/Week_00/practice/tree/LevelOrder.java @@ -0,0 +1,43 @@ +package practice.tree; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + + +// 102 二叉树的层序遍历 https://leetcode-cn.com/problems/binary-tree-level-order-traversal/submissions/ + +public class LevelOrder { + + + public List> levelOrder(TreeNode root) { + List resList = new ArrayList>(); + if (root == null) return resList; + Queue queue = new LinkedList<>(); + queue.add(root); + while (!queue.isEmpty()) { + int size = queue.size(); + ArrayList curList = new ArrayList<>(); + for (int i = 0; i < size; i++) { + TreeNode treeNode = queue.poll(); + curList.add(treeNode.val); + if (treeNode.left != null) queue.add(treeNode.left); + if (treeNode.right != null) queue.add(treeNode.right); + } + resList.add(curList); + } + return resList; + } + + + public class TreeNode { + TreeNode left; + TreeNode right; + int val; + + TreeNode(int x) { + this.val = x; + } + } +} \ No newline at end of file diff --git a/Week_00/practice/tree/NumIslands.java b/Week_00/practice/tree/NumIslands.java new file mode 100644 index 00000000..8517ce10 --- /dev/null +++ b/Week_00/practice/tree/NumIslands.java @@ -0,0 +1,53 @@ +package practice.tree; + +/** + * 200 岛屿数量 https://leetcode-cn.com/problems/number-of-islands/ + */ +public class NumIslands { + + public static void main(String[] args) { +// char[][] grid = new char[3][3]{'1','1','0','1','0','1','0','1','1'}; + char[][] grid = { + {'1','0','1'}, + {'1','0','1'}, + {'1','1','1'}}; + System.out.println(new NumIslands().numIslands(grid)); + } + + /** + * 解题思路: 根据dfs找到水平/垂直方向的1,直到遇到0,岛屿数加1. + */ + + public int numIslands(char[][] grid) { + int count = 0; + int rows = grid.length; + int cols = grid[0].length; + boolean[][] visited = new boolean[rows][cols]; + + for (int i = 0; i < rows; i++) { + for (int j = 0; j < cols; j++) { + if (!visited[i][j] && grid[i][j] == '1') { //没有被访问,并且为陆地(值等于 '1') + count++; + dfs(grid, i, j, visited); + } + } + } + return count; + } + + + private void dfs(char[][] grid, int row, int col, boolean[][] visited) { + //recursion terminator + if (row == grid.length || col == grid[0].length || row < 0 || col < 0) return; + + //process + if (grid[row][col] == '1' && !visited[row][col]) { + visited[row][col] = true; + //drill down + dfs(grid, row - 1 , col, visited); + dfs(grid, row + 1, col, visited); + dfs(grid, row, col - 1, visited); + dfs(grid, row, col + 1, visited); + } + } +} diff --git a/Week_00/practice/tree/TraversalBinaryTree.java b/Week_00/practice/tree/TraversalBinaryTree.java new file mode 100644 index 00000000..371f7ae7 --- /dev/null +++ b/Week_00/practice/tree/TraversalBinaryTree.java @@ -0,0 +1,121 @@ +package practice.tree; + +import java.util.ArrayList; +import java.util.List; +import java.util.Stack; + + +/** + * 144 前序遍历树 https://leetcode-cn.com/problems/binary-tree-preorder-traversal/submissions/ + * 94 中序遍历树 https://leetcode-cn.com/problems/binary-tree-inorder-traversal/ + */ +public class TraversalBinaryTree { + + + /** + * 前后后遍历 二叉树 - 递归法 + * @param root + * @return + */ + public List traversalTreeByRecursion(TreeNode root) { + + List resList = new ArrayList<>(); + predfs(root, resList); + return resList; + } + + + /** + * 前序遍历 二叉树 - 迭代法(通过手动维护栈来实现) + * + * @param root + * @return + */ + public List preorderTraversalByIteration(TreeNode root) { + if (root == null) return new ArrayList(); + + List resList = new ArrayList<>(); + Stack stack = new Stack<>(); + + stack.push(root); + + while (!stack.isEmpty()) { + TreeNode head = stack.pop(); + resList.add(head.val); + if (head.right != null) stack.push(head.right); + if (head.left != null) stack.push(head.left); + } + + return resList; + } + + /** + * 前序遍历 二叉树 - 递归法(利用系统栈来实现) + * + * @param root + * @param resList + */ + private void predfs(TreeNode root, List resList) { + // recursion terminator + if (root == null) return; + + //process + resList.add(root.val); + + //drill down + predfs(root.left, resList); + predfs(root.right, resList); + } + + + /** + * 中序遍历 二叉树 + * + * @param root + * @param resList + */ + private void indfs(TreeNode root, List resList) { + // recursion terminator + if (root == null) return; + + indfs(root.left, resList); + resList.add(root.val); + indfs(root.right, resList); + } + + /** + * 后序遍历 二叉树 + * + * @param root + * @param resList + */ + private void postdfs(TreeNode root, List resList) { + // recursion terminator + if (root == null) return; + + postdfs(root.left, resList); + postdfs(root.right, resList); + resList.add(root.val); + } + + public class TreeNode { + int val; + TreeNode left; + TreeNode right; + + TreeNode() { + } + + TreeNode(int val) { + this.val = val; + } + + TreeNode(int val, TreeNode left, TreeNode right) { + this.val = val; + this.left = left; + this.right = right; + } + } +} + + diff --git a/Week_00/practice/tree/TraversalNTree.java b/Week_00/practice/tree/TraversalNTree.java new file mode 100644 index 00000000..f553d84d --- /dev/null +++ b/Week_00/practice/tree/TraversalNTree.java @@ -0,0 +1,84 @@ +package practice.tree; + +import java.util.*; + +/** + * 589 N叉树的前序遍历 https://leetcode-cn.com/problems/n-ary-tree-preorder-traversal/ + */ + +public class TraversalNTree { + + /** + * 589 前序遍历 N叉树 - 递归法 + * + * @param root + * @return + */ + public List preorder(Node root) { + List resList = new ArrayList<>(); + preDfsByRecursion(root, resList); + return resList; + } + + + /** + * 589 前序遍历 N叉树 - 迭代法 + * 递归遍历树用的是系统栈, 而迭代遍历树需要自己维护一个栈 + */ + public List preorderByIteration(Node root) { + if (root == null) return new ArrayList(); + + List resList = new ArrayList<>(); + Stack stack = new Stack<>(); + stack.push(root); + + while (!stack.isEmpty()) { + Node head = stack.pop(); + resList.add(head.val); + + List childrenList = head.children; + for (int i = childrenList.size() - 1; i >= 0; i--) { + Node node = childrenList.get(i); + if (node != null) stack.push(node); + } + } + + return resList; + } + + /** + * 递归 前序遍历N叉树 + * + * @param root + * @param resList + */ + private void preDfsByRecursion(Node root, List resList) { + // recursion terminator + if (root == null) return; + // process + resList.add(root.val); + for (Node node : root.children) + preDfsByRecursion(node, resList);//drill down + } + + class Node { + public int val; + public List children; + + public Node() { + } + + public Node(int _val) { + val = _val; + } + + public Node(int _val, List _children) { + val = _val; + children = _children; + } + }; + + +} + + diff --git a/Week_00/practice/tree/TreeDepth.java b/Week_00/practice/tree/TreeDepth.java new file mode 100644 index 00000000..8146b867 --- /dev/null +++ b/Week_00/practice/tree/TreeDepth.java @@ -0,0 +1,68 @@ +package practice.tree; + + +// 104 树的深度最大 https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/ +// 111 二叉树的最小深度 https://leetcode-cn.com/problems/minimum-depth-of-binary-tree/ +public class TreeDepth { + + /** + * 104 最大深度 + * @param root + * @return + */ + public int maxDepth(TreeNode root) { + int depth = dfs(0, root); + return depth; + } + + private int dfs(int depth, TreeNode root) { + //termin + if (root == null) return depth; + + //process and drill down + depth++; + int leftDepth = dfs(depth, root.left); + int rightDepth = dfs(depth, root.right); + return Math.max(leftDepth, rightDepth); + } + + + /** + * 111 树的子节点最小深度 + * @param root + * @return + */ + public int minDepth(TreeNode root) { + // recursion treminator + if(root == null) return 0; + + //process drill down + int leftDepth = minDepth(root.left); + int rightDepth = minDepth(root.right); + //只要有个孩子节点为null,那么对应深度就为0,相加不会产生影响,简化了代码逻辑,不需要判断具体哪一个孩子节点为null + if(root.left == null || root.right == null) return leftDepth + rightDepth + 1; + else return Math.min(leftDepth,rightDepth) + 1; + } + + /** + * node + */ + public class TreeNode { + int val; + TreeNode left; + TreeNode right; + + TreeNode() { + } + + TreeNode(int val) { + this.val = val; + } + + TreeNode(int val, TreeNode left, TreeNode right) { + this.val = val; + this.left = left; + this.right = right; + } + } +} diff --git a/Week_00/practice/tree/Trie.java b/Week_00/practice/tree/Trie.java new file mode 100644 index 00000000..6e993244 --- /dev/null +++ b/Week_00/practice/tree/Trie.java @@ -0,0 +1,72 @@ +package practice.tree; + + +// 208 实现前缀树 https://leetcode-cn.com/problems/implement-trie-prefix-tree/ +public class Trie { + public static void main(String[] args) { + Trie trie = new Trie(); + trie.insert("apple"); + trie.search("apple"); + trie.startsWith("app"); + } + + /** + * Initialize your data structure here. + */ + private boolean isEnd; + private Trie[] next; + + public Trie() { + isEnd = false; + next = new Trie[26]; + } + + /** + * Inserts a word into the trie. + */ + public void insert(String word) { + if (word == null || word.length() == 0) return; + char[] chars = word.toCharArray(); + + Trie cur = this; + for(Character ch : chars){ + int index = ch - 'a'; + if (cur.next[index] == null) cur.next[index] = new Trie(); + cur = cur.next[index]; + } + cur.isEnd = true; + } + + /** + * Returns if the word is in the trie. + */ + public boolean search(String word) { + Trie node = searchPrefix(word); + return node != null && node.isEnd; + } + + + + /** + * Returns if there is any word in the trie that starts with the given prefix. + */ + public boolean startsWith(String prefix) { + Trie node = searchPrefix(prefix); + return node != null; + } + + private Trie searchPrefix(String word) { + if (word == null || word.length() == 0) return null; + + Trie cur = this; + char[] chars = word.toCharArray(); + + for(Character ch : chars){ + int index = ch - 'a'; + if(cur.next[index] == null) return null; + else cur = cur.next[index]; + } + return cur; + } + +} diff --git a/Week_00/utils/Utils.java b/Week_00/utils/Utils.java new file mode 100644 index 00000000..2a8967a8 --- /dev/null +++ b/Week_00/utils/Utils.java @@ -0,0 +1,12 @@ +package utils; + +public class Utils { + public static void displayArray(int[] nums) { + + int i = 0; + while (i < nums.length) { + System.out.print(nums[i++] + (i < nums.length ? ", " : "")); + } + } + +} diff --git a/Week_01/MaxArea.java b/Week_01/MaxArea.java new file mode 100644 index 00000000..073a2b4d --- /dev/null +++ b/Week_01/MaxArea.java @@ -0,0 +1,23 @@ + +// 11 盛最多水的容器 +public class MaxArea { + + //关键在于指针的移动 + + public int maxArea(int[] height) { + int startIndex = 0; //首坐标 + int endIndex = height.length - 1; //尾坐标 + int maxArea = 0; //最大面积 + while (startIndex != endIndex) { + maxArea = height[startIndex] > height[endIndex] ? + Math.max(maxArea, getArea(height, startIndex, endIndex--)) : + Math.max(maxArea, getArea(height, startIndex++, endIndex)); + } + + return maxArea; + } + + public int getArea(int[] height,int startIndex, int endIndex) { + return (endIndex - startIndex) * Math.min(height[startIndex], height[endIndex]); + } +} diff --git a/Week_01/README.md b/Week_01/README.md index 50de3041..853d1c1a 100644 --- a/Week_01/README.md +++ b/Week_01/README.md @@ -1 +1,56 @@ -学习笔记 \ No newline at end of file +## 学习总结: + +- 要时刻记录题干意思,有时候想着想着就忘了要干嘛。 +- 五毒神掌要坚持,做一遍很快就忘,就前功尽弃了。 +- 发现排序,哈希去重,双指针这些都是数组的常用解题套路。 +- 课程中给的练习题要尽量抽时间多刷些题,套路攒多了,遇到题就思路了。 还有一些延伸的链接很棒,比如list的源码,LRU Cache解析,Redis的跳表使用. + +### linkedList - 链表 + + linkedList是通过双向链表实现的,而双向链表就是通过Node类来体现的,类中通过item变量保存了当前节点的值,通过next变量指向下一个节点,通过prev变量指向上一个节点。 + +### Stack - 栈 + +```java +public class Stack extends Vector { + + public synchronized T pop() { + if (elementCount == 0) + throw new EmptyStackException(); + + modCount++; + T obj = elementData[--elementCount]; + + // Set topmost element to null to assist the gc in cleanup. + elementData[elementCount] = null; + return obj; + } + + public E push(E item) { + addElement(item); + return item; + } + + // Vector父类的方法 + public synchronized void addElement(E obj) { + modCount++; + ensureCapacityHelper(elementCount + 1); + elementData[elementCount++] = obj; + } + +} +``` + + 从以上源码可以看出,栈本质上是在维护一个数组,pop方法即把当前栈顶对应数组中的值设置为null, push方法则是在数组末尾添加新元素。 + +### queue - 队列 + +```java + +public interface Queue extends Collection { +} +``` + + 队列是一种特殊的线性表,它只允许在表的前端进行删除操作,而在表的后端进行插入操作。从源码中可以看出queue是一个接口,它有多种实现,比如LinkedList。 + 那么就可以认为队列本质上就是一种先进先出,只允许操作 "前端元素"的链表。 + \ No newline at end of file diff --git a/Week_01/TwoSum.java b/Week_01/TwoSum.java new file mode 100644 index 00000000..1113ba2a --- /dev/null +++ b/Week_01/TwoSum.java @@ -0,0 +1,37 @@ +import java.util.HashMap; + + +// 1 两数之和 +public class TwoSum { + + //哈希 取相反数 + public int[] twoSum(int[] nums, int target) { + HashMap hashMap = new HashMap<>(); + for (int i = 0; i < nums.length; i++) { + hashMap.put(nums[i], i); + } + + for (int i = 0; i < nums.length; i++) { + int otherNum = target - nums[i]; + if (hashMap.containsKey(otherNum) && hashMap.get(otherNum) != i) { + return new int[]{i, hashMap.get(otherNum)}; + } + } + return null; + } + + //夹逼 + public int[] twoSum_2(int[] nums, int target) { + HashMap hashMap = new HashMap<>(); + + for (int i = 0; i < nums.length; i++) { + int otherNum = target - nums[i]; + if (hashMap.containsKey(otherNum)) { + return new int[]{hashMap.get(otherNum), i}; + }else { + hashMap.put(nums[i], i); + } + } + return null; + } +} diff --git a/Week_02/NthUglyNumber.java b/Week_02/NthUglyNumber.java new file mode 100644 index 00000000..21d048fa --- /dev/null +++ b/Week_02/NthUglyNumber.java @@ -0,0 +1,28 @@ +import java.util.PriorityQueue; + +public class NthUglyNumber { + + public static void main(String[] args) { + System.out.println(new NthUglyNumber().nthUglyNumber(1407)); + } + + // 264 丑数 II + public int nthUglyNumber(int n) { + /** + * 1. 根据入参 构造最小堆 + * 2. 弹出最小堆的堆顶元素 n 次, 注意去重。 + */ + PriorityQueue pq = new PriorityQueue<>(); + long res = 1; + for (int i = 1; i < n; i++) { + pq.add(res * 2); + pq.add(res * 3); + pq.add(res * 5); + res = pq.poll(); + while (res == pq.peek()) pq.poll(); + } + return (int) res; + } + + +} diff --git a/Week_02/README.md b/Week_02/README.md index 50de3041..2be1a805 100644 --- a/Week_02/README.md +++ b/Week_02/README.md @@ -1 +1,15 @@ -学习笔记 \ No newline at end of file +## 学习笔记 +### 哈希表 + +- 哈希表本质上是一个一维数组,存放数据的逻辑是把待插入的值,通过哈希函数映射到目标数组的下标,如果出现多个值得下标相同, +那么这些相同的数值会通过链表的方式连接以起来。 +- 哈希表对单个key的增删改查的时间复杂度都是O(1). +- 源码: `http://developer.classpath.org/doc/java/util/HashMap-source.html` + +### 树 +- 树,本质上是一个链条,与一般所说的单链表不同的是,它会存在多个分支,只有2个分支的树,成为二叉树。 +- 如果二叉树的非叶子节点都存在子节点,那么就是完全二叉树。 +- 二叉搜索树具备以下性质: +> 左子树上所有结点的值均小于它的根结点的值,右子树上所有结点的值均大于它的根结点的值; +> 它的增删改查的时间复杂度都是O(logN) + diff --git a/Week_02/TopKFrequent.java b/Week_02/TopKFrequent.java new file mode 100644 index 00000000..bd55cf4b --- /dev/null +++ b/Week_02/TopKFrequent.java @@ -0,0 +1,38 @@ +import java.util.*; + +public class TopKFrequent { + // 347. 前 K 个高频元素 + + public static void main(String[] args) { + int[] nums = new int[]{2,3,4,4,4,3,1,2,5,6,7}; + new TopKFrequent().topKFrequent(nums,3); + } + + public int[] topKFrequent(int[] nums, int k) { + + /** + * 1. 放入map中计数 + * 2. 构造最大堆 + * 3. 弹出K个堆顶元素 + */ + int[] res = new int[k]; + HashMap map = new HashMap<>(); + for (int num : nums) { + map.put(num, map.getOrDefault(num,0) + 1); + } + + PriorityQueue> pq = new PriorityQueue<>(new Comparator>() { + @Override + public int compare(Map.Entry o1, Map.Entry o2) { + return o2.getValue() - o1.getValue(); + } + }); + for (Map.Entry entry : map.entrySet()) { + pq.add(entry); + } + + int i = 0; + while (i < k) res[i++] = pq.poll().getKey(); + return res; + } +} diff --git a/Week_03/Combine.java b/Week_03/Combine.java new file mode 100644 index 00000000..cfebab4d --- /dev/null +++ b/Week_03/Combine.java @@ -0,0 +1,40 @@ +// 77 组合 https://leetcode-cn.com/problems/combinations/ + +import java.util.ArrayList; +import java.util.List; + +public class Combine { + + public static void main(String[] args) { + List> combine = new Combine().combine(3, 2); + combine.forEach(System.out::println); + } + + public List> combine(int n, int k) { + /** + * 像列举出所有组合的题,是经典的回溯算法题,可以根据dfs的思想来解题。 + */ + + List> resList = new ArrayList<>(); + dfs(1, n, k, new ArrayList(), resList); + return resList; + } + + private void dfs(int begin, int n, int k, ArrayList curList, List> resList) { + //recursion terminator + if (curList.size() == k) { + resList.add(new ArrayList<>(curList)); + return; + } + int loopTimes = n - (k - curList.size()) + 1; //剪枝优化: 本层最多可以从第几个数开始遍历(主要用于删去不必要的遍历操作,比如数不够就没必要再遍历了。) + for (int i = begin; i <= loopTimes; i++) { + // process + curList.add(i); + // drill down + dfs(i + 1, n, k, curList, resList); + // revert state + curList.remove(curList.size() - 1); + } + } + +} diff --git a/Week_03/Permute.java b/Week_03/Permute.java new file mode 100644 index 00000000..b63b782a --- /dev/null +++ b/Week_03/Permute.java @@ -0,0 +1,39 @@ +import java.util.ArrayList; +import java.util.List; + +// 46 全排列 https://leetcode-cn.com/problems/permutations/ +public class Permute { + + public static void main(String[] args) { + new Permute().permute(new int[]{1, 2, 3}).forEach(System.out::println); + } + + public List> permute(int[] nums) { + List> resList = new ArrayList<>(); + int[] visited = new int[nums.length]; //记录当前list中访问nums元素的情况,1表示curList中已经存在nums[0]元素 + dfs(nums, new ArrayList(), resList, visited); + return resList; + } + + private void dfs(int[] nums, ArrayList curList, List> resList, int[] visited) { + //recursion terminator + if (curList.size() == nums.length) { + resList.add(new ArrayList<>(curList)); + return; + } + + for (int i = 0; i < nums.length; i++) { + //process + if (visited[i] != 0) continue; + curList.add(nums[i]); + visited[i] = 1; + + //drill down + dfs(nums, curList, resList, visited); + + // revert state + curList.remove(curList.size() - 1); + visited[i] = 0; + } + } +} diff --git a/Week_03/README.md b/Week_03/README.md index 50de3041..6ba32c29 100644 --- a/Week_03/README.md +++ b/Week_03/README.md @@ -1 +1,29 @@ -学习笔记 \ No newline at end of file +#### 学习笔记 + +### 递归 + +- 代码模板 + - 跳出递归的终止条件 + - 处理当前层的逻辑 + - 跳入至下一层 + - 清理当前层状态 + +- 注意事项 + - 避免人肉递归 + - 找最小重复子问题 + - 数学归纳法: 即证明n , n+1满足某种规律。 + + +### 分治 和 回溯 + + 分治简单说就是将一个问题分成多个相同逻辑子问题,最后对子问题的返回结果聚合处理,得到最终结果。 +> 比如求n的k次幂,就可以把问题转化为求n的k/2次幂,这样相对于暴力循环来说就可以减少重复的计算步骤。 + + 回溯从代码的逻辑上讲就是通过一步步尝试来判断是否满足预期结果(列出所有可能性)的求解方式。在编码时需要注意的是: +一般来说在代码末尾需要回溯,即清除当前层操作留下的痕迹(删除在当前层插入到列表中的元素)。 +> 常用于求组合的所有可能性。 比如求一个数组中的所有数组合的子集。 + + 分治和回溯本质上也是一种递归,只是在计算逻辑细节上有一些差别,分别用来解决特定场景的问题。 + + + diff --git a/Week_04/FindContentChildren.java b/Week_04/FindContentChildren.java new file mode 100644 index 00000000..6f62a341 --- /dev/null +++ b/Week_04/FindContentChildren.java @@ -0,0 +1,29 @@ +import java.util.Arrays; + +/** + * 455 分发饼干 https://leetcode-cn.com/problems/assign-cookies/description/ + */ + +public class FindContentChildren { + public static void main(String[] args) { + System.out.println(new FindContentChildren().findContentChildren(new int[]{1, 2, 3}, new int[]{1, 1})); + } + + + /** + * 贪心算法: 只要保证每次分配的饼干能满足孩子的需求,并且是所有饼干中尺寸最小的即可 + */ + public int findContentChildren(int[] g, int[] s) { + int gindex = 0, sindex = 0, count = 0; + Arrays.sort(g); + Arrays.sort(s); + + while (gindex < g.length && sindex < s.length) { + if (s[sindex++] >= g[gindex]) { + count++; + gindex++; + } + } + return count; + } +} diff --git a/Week_04/NumIslands.java b/Week_04/NumIslands.java new file mode 100644 index 00000000..fe5a305f --- /dev/null +++ b/Week_04/NumIslands.java @@ -0,0 +1,51 @@ +/** + * 200 岛屿数量 https://leetcode-cn.com/problems/number-of-islands/ + */ +public class NumIslands { + + public static void main(String[] args) { +// char[][] grid = new char[3][3]{'1','1','0','1','0','1','0','1','1'}; + char[][] grid = { + {'1','0','1'}, + {'1','0','1'}, + {'1','1','1'}}; + System.out.println(new NumIslands().numIslands(grid)); + } + + /** + * 解题思路: 根据dfs找到水平/垂直方向的1,直到遇到0,岛屿数加1. + */ + + public int numIslands(char[][] grid) { + int count = 0; + int rows = grid.length; + int cols = grid[0].length; + boolean[][] visited = new boolean[rows][cols]; + + for (int i = 0; i < rows; i++) { + for (int j = 0; j < cols; j++) { + if (!visited[i][j] && grid[i][j] == '1') { + count++; + dfs(grid, i, j, visited); + } + } + } + + return count; + } + + private void dfs(char[][] grid, int row, int col, boolean[][] visited) { + //recursion terminator + if (row == grid.length || col == grid[0].length || row < 0 || col < 0) return; + + //process + if (grid[row][col] == '1' && !visited[row][col]) { + visited[row][col] = true; + //drill down + dfs(grid, row - 1 , col, visited); + dfs(grid, row + 1, col, visited); + dfs(grid, row, col - 1, visited); + dfs(grid, row, col + 1, visited); + } + } +} diff --git a/Week_04/README.md b/Week_04/README.md index 50de3041..203c8e18 100644 --- a/Week_04/README.md +++ b/Week_04/README.md @@ -1 +1,17 @@ -学习笔记 \ No newline at end of file +## 学习笔记 + +### 深度、广度优先搜索 + 代码模板有递归式和非递归式,非递归式需要把下一次遍历的节点放在一个专用的存储对象中(栈,队列); + 递归式就和递归代码模板相同。 + +### 贪心 + 贪心算法是一种在每一步选择中都采取在当前状态下最好或最优(即最有 利)的选择,从而希望导致结果是全局最好或最优的算法。 + 贪心算法与动态规划的不同在于它对每个子问题的解决方案都做出选择,不能回退。动态规划则会保存以前的运算结果,并根据以前的结果对当前进行选择,有回退功能。 +#### 适用场景 + 问题能够分解成子问题来解决,子问题的最优解能递推到最终问题的最优解。这种子问题最优解称为最优子结构。 + +### 二分查找 +####前提 +• 目标函数单调性(单调递增或者递减) +• 存在上下界(bounded) +• 能够通过索引访问(index accessible) diff --git a/Week_04/Search.java b/Week_04/Search.java new file mode 100644 index 00000000..50de5110 --- /dev/null +++ b/Week_04/Search.java @@ -0,0 +1,31 @@ +/** + * 33 搜索旋转排序数组 https://leetcode-cn.com/problems/search-in-rotated-sorted-array/ + */ +public class Search { + public static void main(String[] args) { + System.out.println(new Search().search(new int[]{4, 5, 6, 7, 0, 1, 2}, 0)); + } + + + + + /** + * 中心思想: 判断target在 mid左边还是右边,就可以直接使用二分查找进行搜索。 + * 本体在if中列举了 所有可能在mid左边的情况 + */ + + public int search(int[] nums, int target) { + int left = 0; + int right = nums.length - 1; + + while (left <= right) { + int mid = left + (right - left) / 2; + if (nums[mid] == target) return mid; + + else if ((nums[left] > nums[mid] && (target >= nums[left] || target < nums[mid])) || (target >= nums[left] && target < nums[mid])) { + right = mid - 1; + } else left = mid + 1; + } + return -1; + } +} diff --git a/Week_05/README.md b/Week_05/README.md index 50de3041..deb996af 100644 --- a/Week_05/README.md +++ b/Week_05/README.md @@ -1 +1,11 @@ -学习笔记 \ No newline at end of file + +## 动态规划 +Simplifying a complicated problem by breaking it down into simpler sub-problems +通过把一个问题简化成多个简单的子问题,即分治+最优子结构,所以本质上是递归的思想,只是在实现上大多通过DP方程构造1,2维数组来解题。 +最优子结构意味着中途可以淘汰最优解。 + + +感受: + 刷了7道题,动态规划这类题,最难的就是找最优子结构,特别是通过dp方程构造数组的时候还要考虑边界的问题,特别容易出错,且不容易发现。 +找最优子结构来构造数组需要有比较多的经验,很多时候无从下手只能看题解。 + 总结出的套路基本都是针对题目给的对象,比如字符串,数组,对每个字符 或是数组中的每个数找到其和其他数的运算关系。 \ No newline at end of file diff --git a/Week_06/MinPathSum.java b/Week_06/MinPathSum.java new file mode 100644 index 00000000..7861a94c --- /dev/null +++ b/Week_06/MinPathSum.java @@ -0,0 +1,42 @@ +/** + * 64 最小路径和 https://leetcode-cn.com/problems/minimum-path-sum/ + */ +public class MinPathSum { + + public static void main(String[] args) { + System.out.println(new MinPathSum().minPathSum(new int[][]{{1, 3, 1}, {1, 5, 1}, {4, 2, 1}})); + } + + + /** + * 动态规划: + * 构造一个二维数组, 每个格子代表从(0,0)位置到当前位置的最小和。 结果取 (m, n) + * + * dp方程: + * 初始化(除 (0, 0)外 ): + * dp[m][n] = dp[m][n - 1] + grid[m][n]; //m = 0 + * dp[m][n] = dp[m - 1][n] + grid[m][n]; //n = 0 + * + * dp[m][n] = Math.min(dp[m][n - 1], dp[m - 1][n]) + grid[m][n]; + */ + public int minPathSum(int[][] grid) { + int row = grid.length; + int col = grid[0].length; + int[][] dp = new int[row][col]; + dp[0][0] = grid[0][0]; + + for (int m = 0; m < row; m++) { + for (int n = 0; n < col; n++) { + if (m == 0 && n > 0) dp[m][n] = dp[m][n - 1] + grid[m][n]; //初始化 m = 0 && n > 0 + else if (m > 0 && n == 0) dp[m][n] = dp[m - 1][n] + grid[m][n]; //初始化 n = 0 && m > 0 + else if (m > 0) { + dp[m][n] = Math.min(dp[m][n - 1], dp[m - 1][n]) + grid[m][n]; + } + } + } + + return dp[row - 1][col - 1]; + } + + +} diff --git a/Week_06/NumDecodings.java b/Week_06/NumDecodings.java new file mode 100644 index 00000000..2cebddbe --- /dev/null +++ b/Week_06/NumDecodings.java @@ -0,0 +1,58 @@ +/** + * 91 解码方法 https://leetcode-cn.com/problems/decode-ways/ + */ + +public class NumDecodings { + public static void main(String[] args) { + System.out.println(new NumDecodings().numDecodings("203")); + } + /** + * 动态规划: + * 解法类似爬楼梯:因为编码数值最大到26,所以切分的子字符串最大长度为2,因此只需要关注字符串的最后2个字符即可. + * dp[i] 就等于i - 2到i要走的步数加上 i - 1到i要走的步数。但是有多种边界条件致使dp方程不一定是dp[i] = dp[i -1] + dp[i - 2]. + * + * dp方程: dp[i] 表示从第0个字符到第i个字符形成的字符串的解码数。 + * + * + * 当s[i] = '0' 时 + * ① s[i - 1] = '1' || s[i - 1] = '2' 方程式为 dp[i] = dp[i - 2]. + * 解释: + * 因为最后一个字符为0,不能单独被解码,它只有和它前面的一个字符合为一个整体才有可能被解码,当字符是 1 或者 2,就可以当10或者20被解码了, + * 那也就意味着和dp[i - 2]的解码数一样。 + * 例如: 2220 . dp[3] = dp[3 -2]. + * dp[1]对应的字符串(22)组合有 2 2 和 22,共2种。 + * dp[3]对应的字符串(2220)组合有 2 2 20 和 22 20 共2种。 + * 所以可以观察到dp[3] 就是在dp[1]的每种组合后面加了20这个数。 即dp[i] = dp[i - 2]. + * + * ② 否则直接返回 0 。 因为 00, 30 ,40 这种串使得整个串无法被解码 + * + * + * 当 0 < s[i - 1][i] <27 时 + * ① dp[i] = dp[i - 1] + dp[i - 2]; + * 解释: + * 字符串最后是0的情况分析完,就分析不是0的。 最后2个字符组成的数字范围是0到27,意味着这2个字符有2种解码组合。比如 26 可以是 2 6 和 26 两种。 + * 所以dp[i] = dp[i - 1] + dp[i - 2] 就是爬楼梯的思维。 i - 1 就是从 i - 1到 i的爬楼方法数加 i - 2到 i 的爬楼方法数。 + * + * ② 否则 dp[i] = dp[i - 1]. 如果不满足 0 到 27的范围, 说明最后2个字符无法合在一起被解码。只能拆开解码,而拆开的解码数就等于dp[i -1]. + * 例如 23字符串的组合有 2 3 和 23 两种。 237字符串有2 3 7 和 23 7两种。 所以就是在i - 1组合后面加了 s[i]这个字符。 + */ + public int numDecodings(String s) { + if (s.charAt(0) == '0') return 0; + int[] dp = new int[s.length()]; + dp[0] = 1; // 一个非0的数默认有1种解码 + + for (int i = 1; i < s.length(); i++) { + char si = s.charAt(i); //代数 + char si_1 = s.charAt(i - 1); //代数 + int dpi_2 = (i - 2) >= 0 ? dp[i - 2] : 1; //代数 表示 dp[i - 2] + + if(si == '0'){ + if(si_1 == '1' || si_1 == '2') dp[i] = dpi_2; + else return 0; + } else if(si_1 == '1' || si_1 == '2' && si > '0' && si < '7'){ + dp[i] = dp[i - 1] + dpi_2; + } else dp[i] = dp[i - 1]; + } + return dp[s.length() - 1]; + } +} diff --git a/Week_06/README.md b/Week_06/README.md index 50de3041..deb996af 100644 --- a/Week_06/README.md +++ b/Week_06/README.md @@ -1 +1,11 @@ -学习笔记 \ No newline at end of file + +## 动态规划 +Simplifying a complicated problem by breaking it down into simpler sub-problems +通过把一个问题简化成多个简单的子问题,即分治+最优子结构,所以本质上是递归的思想,只是在实现上大多通过DP方程构造1,2维数组来解题。 +最优子结构意味着中途可以淘汰最优解。 + + +感受: + 刷了7道题,动态规划这类题,最难的就是找最优子结构,特别是通过dp方程构造数组的时候还要考虑边界的问题,特别容易出错,且不容易发现。 +找最优子结构来构造数组需要有比较多的经验,很多时候无从下手只能看题解。 + 总结出的套路基本都是针对题目给的对象,比如字符串,数组,对每个字符 或是数组中的每个数找到其和其他数的运算关系。 \ No newline at end of file diff --git a/Week_07/FindCircleNum.java b/Week_07/FindCircleNum.java new file mode 100644 index 00000000..8ff887f6 --- /dev/null +++ b/Week_07/FindCircleNum.java @@ -0,0 +1,50 @@ +//547 省份的数量 https://leetcode-cn.com/problems/number-of-provinces/ +public class FindCircleNum { + + + public int findCircleNum(int[][] isConnected) { + + int rows = isConnected.length; + UnionFind unionFind = new UnionFind(rows); + for (int i = 0; i < rows - 1; i++) { + for (int j = i + 1; j < rows; j++) { + if (isConnected[i][j] == 1) unionFind.union(i , j); + } + } + return unionFind.count; + } + + + /** + * 并查集模板代码 + */ + class UnionFind { + private int count = 0; + private int[] parent; + + public UnionFind(int size) { + count = size; + parent = new int[size]; + for (int i = 0; i < size; i++) parent[i] = i; + } + + public int find(int p) { + while (p != parent[p]) { + parent[p] = parent[parent[p]]; //压缩 : 指向上一个节点 + p = parent[p]; + } + return p; + } + + public void union(int p, int q){ + int rootP = find(p); + int rootQ = find(q); + if (rootP == rootQ) return; + parent[rootQ] = parent[rootP]; + count--; + } + + + } + +} diff --git a/Week_07/FindWords.java b/Week_07/FindWords.java new file mode 100644 index 00000000..8fba0bab --- /dev/null +++ b/Week_07/FindWords.java @@ -0,0 +1,82 @@ +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + +// 212 单词搜索II https://leetcode-cn.com/problems/word-search-ii/ +public class FindWords { + public List findWords(char[][] board, String[] words) { + + /** + * 1 先把words生成字典树 + * 2 遍历board每个字符去字典树中查找。 + * 3 需要对board中的每个字符进行dfs搜索 + */ + + HashSet resSet = new HashSet<>(); + Trie trie = new Trie(); + for (String word : words) trie.insert(word); + + int rows = board.length; + int cols = board[0].length; + boolean[][] visited = new boolean[rows][cols]; + + for (int i = 0; i < rows; i++) { + for (int j = 0; j < cols; j++) { + char ch = board[i][j]; + dfs(trie, board, i, j, resSet, visited); + } + } + return new ArrayList<>(resSet); + } + + private void dfs(Trie curNode, char[][] board, int i, int j, HashSet hashSet, boolean[][] visited) { + if (i < 0 || i > board.length - 1 || j < 0 || j > board[0].length - 1 || visited[i][j]) return; + + visited[i][j] = true; + curNode = curNode.next[board[i][j] - 'a']; //拿到字符在字典中对应位置的值 + if (curNode == null) {//当前字母不在字典中,清除访问状态并返回 + visited[i][j] = false; //清除状态 + return; + } + if (curNode.isEnd) hashSet.add(curNode.val);//当前字母不为空,且是叶子节点,把对应的值加入到list + + dfs(curNode, board, i + 1, j, hashSet, visited); + dfs(curNode, board, i - 1, j, hashSet, visited); + dfs(curNode, board, i, j + 1, hashSet, visited); + dfs(curNode, board, i, j - 1, hashSet, visited); + + visited[i][j] = false; //清除状态 + } + + + class Trie { + /** + * Initialize your data structure here. + */ + private boolean isEnd; + private Trie[] next; + private String val; + + public Trie() { + isEnd = false; + next = new Trie[26]; + } + + /** + * Inserts a word into the trie. + */ + public void insert(String word) { + if (word == null || word.length() == 0) return; + char[] chars = word.toCharArray(); + + Trie cur = this; + for (Character ch : chars) { + int index = ch - 'a'; + if (cur.next[index] == null) cur.next[index] = new Trie(); + cur = cur.next[index]; + } + cur.isEnd = true; + cur.val = word; + } + } +} diff --git a/Week_07/README.md b/Week_07/README.md index 50de3041..2b7dd2b5 100644 --- a/Week_07/README.md +++ b/Week_07/README.md @@ -1 +1,88 @@ -学习笔记 \ No newline at end of file +## 字典树 + +### 概述 + +字典树,即 Trie 树,称单词查找树或键树,前缀树 + + + +### 特征 + +- 结点本身不存完整单词 +- 从根结点到某一结点,路径上经过的字符连接起来,为该结点对应的 字符串; +- 每个结点的所有子结点路径代表的字符都不相同 + + + +### 优点: + +最大限度地减少 无谓的字符串比较,查询效率 比哈希表高 + + + +## 并查集 + +### 适用场景: + +组团、配对问题 + +## 高级搜索 + +### 种类 + +#### 剪枝 + +- 去重复 +- 搜索过程中移除决策树中分辨能力较弱的部分而减少树大小的方法,降低复杂度 + +#### 双向 Breadth First Search (BFS) + +- 双向搜索算法是一种图的遍历算法,用于在有向图中搜索从一个顶点到另一个顶点的最短路径问题 +- 算法同时运行两个搜索:一个从初始状态正向搜索,另一个从目标状态反向搜索,当两者在中间汇合时搜索停止 + +#### 启发式搜索(A*) + +- 启发函数:h(n),用来评价哪些节点最有希望的是一个我们要找的节点,h(n)会返回一个非负实数,也可以认为是从结点n的目标结点路径的估计成本 +- 启发式函数是一种告知搜索方向的方法 + +### AVL树和红黑树 + +#### AVL树 + +- AVL树是一个平衡二叉搜索树,每个节点左右两子树高度差不超多1 +- Balance Factor(平衡因子):节点的左子树的高度减去右子树的高度(有时相反),balance factor = {-1, 0, 1} +- 插入和删除操作可能需要一次或多次旋转实现树的重新平衡 +- 四种旋转操作: + +- - 右右子树 —> 左旋 +- 左左子树 —> 右旋 +- 左右子树 —> 左右旋 +- 右左子树 —> 右左旋 + +- 不足:节点需要存储额外信息、且调整次数频繁 + + + +#### 红黑树 + +- 红黑树是一种近似平衡二叉搜索树,它能确保任何一个节点的左右子树高度差小于二倍 +- 满足的条件: + +- - 每个节点要么是红色,要么是黑色 +- 根节点是黑色 +- 每个叶子节点(NIL节点)是黑色 +- 不能有两个相邻的红色节点 +- 从任一个节点到其每个叶子节点所有路径都包含相同数目的黑色节点 + +- 关键性质:从根到叶子的最长的可能路径不多于最短的可能路径的两倍长 +- 查找、插入、删除的时间复杂度:O(logn),n是树中元素的数目 + + + +#### AVL树对比红黑树 + +- AVL树提供更快的查找比红黑树,因为AVL提供更严格平衡,没增删一次都要维护平衡。 +- 红黑树提供了更快的插入和删除操作比AVL树,由于相对宽松的平衡,旋转次数较少 +- AVL树每个节点需要存储平衡因子和高度,需要更多存储空间,而红黑树每个节点只需要用一个bit位来存储0和1表示红或者黑 +- 大多数语言库都是使用的红黑树(Java1.8 Map),AVL树则用于需要更快检索的数据库 +- 读多写少的场景用AVL树,除此之外多用红黑树。 \ No newline at end of file diff --git a/Week_07/SolveNQueens.java b/Week_07/SolveNQueens.java new file mode 100644 index 00000000..8cce4aaf --- /dev/null +++ b/Week_07/SolveNQueens.java @@ -0,0 +1,79 @@ +import java.util.ArrayList; +import java.util.List; + +// 51 N皇后 https://leetcode-cn.com/problems/n-queens/ +public class SolveNQueens { + + public static void main(String[] args) { + System.out.println(new SolveNQueens().solveNQueens(4)); + } + + List> resList = new ArrayList<>(); + int[] cols; + int[] leftX; + int[] rightX; + + /** + * 回溯法 + *

+ * 1 每列, 对角线不能同时存在2个皇后。 + * 通过数组对每个节点hash到每列,对角线, + * 这里只需要一维数组,不同于数独要映射9个数,这里只需要映射一整列,对角线 + *

+ * 2 针对每个格子进行回溯 + * 3 满足条件加入到结果集,再回溯清除当前状态,因为可能存在多个解 + */ + public List> solveNQueens(int n) { + if (n == 0) return resList; + cols = new int[n]; + leftX = new int[2 * n - 1]; + rightX = new int[2 * n - 1]; + + List curFlag = new ArrayList<>(); + dfs(n, 0, curFlag); + return resList; + } + + private void dfs(int n, int row, List curFlag) { + if (row == n) {// 填完,记录结果并返回 + resList.add(arrayToString(curFlag, n)); + return; + } + //对每个格子进行回溯 + for (int i = 0; i < n; i++) { + if (cols[i] == 0 && leftX[row + i] == 0 && rightX[row - i + n - 1] == 0) { //[row - i]下标可能为负数,加上n - 1凑0 + curFlag.add(i); + cols[i] = 1; + leftX[row + i] = 1; + rightX[row - i + n - 1] = 1; + + dfs(n, row + 1, curFlag); + + curFlag.remove(curFlag.size() - 1); + cols[i] = 0; + leftX[row + i] = 0; + rightX[row - i + n - 1] = 0; + }else continue; + + } + } + + /** + * 数组到列表的格式化 + * + * @param curFlag + * @param n + * @return + */ + private List arrayToString(List curFlag, int n) { + List lst = new ArrayList<>(); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < n; i++) sb.append("."); + for (int numFlag : curFlag) { + StringBuilder cpSb = new StringBuilder(sb); + String str = cpSb.replace(numFlag, numFlag + 1, "Q").toString(); + lst.add(str); + } + return lst; + } +} diff --git a/Week_07/SolveSudoku.java b/Week_07/SolveSudoku.java new file mode 100644 index 00000000..b18f994d --- /dev/null +++ b/Week_07/SolveSudoku.java @@ -0,0 +1,69 @@ +//37 解数独 https://leetcode-cn.com/problems/sudoku-solver/#/description +public class SolveSudoku { + + public static void main(String[] args) { + int num = 1; + char a = (char) num; + char b = '2'; + System.out.println(a); + } + + public void solveSudoku(char[][] board) { + int[][] rows = new int[9][9]; + int[][] cols = new int[9][9]; + int[][] boxes = new int[9][9]; + + //初始化,在数独的每行,列,块中有哪些数已经被填 + for (int i = 0; i < 9; i++) { + for (int j = 0; j < 9; j++) { + if (board[i][j] == '.') { + continue; + } else { + int index = board[i][j] - '1'; + int[] box = boxes[i / 3 * 3 + j / 3]; + rows[i][index] = 1; + cols[j][index] = 1; + box[index] = 1; + } + } + } + //fill it + fillit(board, rows, cols, boxes, 0, 0); + } + + private boolean fillit(char[][] board, int[][] rows, int[][] cols, int[][] boxes, int row, int col) { + //列到头了就换行 + if (col > 8) { + col = 0; + row++; + } + + if (row > 8) return true; + + if (board[row][col] != '.') { + return fillit(board, rows, cols, boxes, row, col + 1); + } + + //从 1 到 9 逐个回溯 + for (char i = '1'; i <= '9'; i++) { + int[] box = boxes[row / 3 * 3 + col / 3]; + int index = i - '1'; + if (rows[row][index] == 1 || cols[col][index] == 1 || box[index] == 1) { + continue; + } else { + board[row][col] = i; + rows[row][index] = 1; + cols[col][index] = 1; + box[index] = 1; + if(fillit(board, rows, cols, boxes, row, col + 1)) return true; + else { + board[row][col] = '.'; + rows[row][index] = 0; + cols[col][index] = 0; + box[index] = 0; + } + } + } + return false; + } +} diff --git a/Week_08/HammingWeight.java b/Week_08/HammingWeight.java new file mode 100644 index 00000000..efc5b953 --- /dev/null +++ b/Week_08/HammingWeight.java @@ -0,0 +1,36 @@ +//131 位的个数 https://leetcode-cn.com/problems/number-of-1-bits/ +public class HammingWeight { + + + public static void main(String[] args) { + + int a = 4; + int b = a >> 1; + System.out.println(b); + } + + + // 通过消除最低位的1进行计数,直到最后为0 + public int hammingWeight(int n) { + int count = 0; + while (n != 0) { + count++; + n = (n - 1) & n; //消除最低位的 1 + } + return count; + } + + // 通过 1 左移,与原数进行“与”操作, 不为零则计数1个 + public int hammingWeight_2(int n) { + int count = 0; + int mark = 1; + int bit = 32; + + while (bit-- != 0) { + if((mark & n) != 0) count++; + mark <<= 1; + } + return count; + } + +} diff --git a/Week_08/IsAnagram.java b/Week_08/IsAnagram.java new file mode 100644 index 00000000..410c7aa4 --- /dev/null +++ b/Week_08/IsAnagram.java @@ -0,0 +1,15 @@ +//242 有效的字母异位词 https://leetcode-cn.com/problems/valid-anagram/ +public class IsAnagram { + + public static void main(String[] args) { + System.out.println('b' - 'a'); + } + + public boolean isAnagram(String s, String t) { + int[] arrCnt = new int[26]; + for (char ch : s.toCharArray()) arrCnt[ch - 'a']++; + for (char ch : t.toCharArray()) arrCnt[ch - 'a']--; + for (int num : arrCnt) if (num != 0) return false; + return true; + } +} diff --git a/Week_08/IsPowerOfTwo.java b/Week_08/IsPowerOfTwo.java new file mode 100644 index 00000000..d2273818 --- /dev/null +++ b/Week_08/IsPowerOfTwo.java @@ -0,0 +1,16 @@ +//231 2的幂 https://leetcode-cn.com/problems/power-of-two/ +public class IsPowerOfTwo { + + + public static void main(String[] args) { + int a = 1 & 1; + int b = 1 & 4; + System.out.println(a); + } + + // 二进制位中 只有一位是1 即是2的幂 + public boolean isPowerOfTwo(int n) { + while (n > 0 && (n & 1) == 0) n >>= 1; + return n == 1; + } +} diff --git a/Week_08/LRUCache.java b/Week_08/LRUCache.java new file mode 100644 index 00000000..f7bdef43 --- /dev/null +++ b/Week_08/LRUCache.java @@ -0,0 +1,27 @@ +import java.util.LinkedHashMap; +import java.util.Map; + +//146 LRUCache 缓存机制 https://leetcode-cn.com/problems/lru-cache/#/ +public class LRUCache { + private final int capacity; + private LinkedHashMap map; + + + public LRUCache(int capacity) { + this.capacity = capacity; + map = new LinkedHashMap(capacity, 0.75f, true) { + @Override + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > capacity; + } + }; + } + + public int get(int key) { + return map.getOrDefault(key, -1); + } + + public void put(int key, int value) { + map.put(key, value); + } +} diff --git a/Week_08/Merge.java b/Week_08/Merge.java new file mode 100644 index 00000000..11f1fae8 --- /dev/null +++ b/Week_08/Merge.java @@ -0,0 +1,30 @@ +import java.util.ArrayList; +import java.util.Arrays; + +//56 合并区间 https://leetcode-cn.com/problems/merge-intervals/ +public class Merge { + public static void main(String[] args) { + } + + /** + * intervals是 n行2列 ex: [(1, 2)], [(2, 4)] + * 把intervals按照 元组的第一个元素升序排序 + * 把第一个元组放入list, 遍历所有元组,与list的最后一个元组比较边界 + */ + public int[][] merge(int[][] intervals) { + if (intervals.length < 2) return intervals; + + ArrayList resList = new ArrayList<>(); + Arrays.sort(intervals, (v1, v2) -> v1[0] - v2[0]); + resList.add(intervals[0]); + + for(int i = 1; i < intervals.length; i++) { + int[] curRange = intervals[i]; + int[] lastRange = resList.get(resList.size() - 1); + + if(lastRange[1] < curRange[0]) resList.add(curRange); + else lastRange[1] = Math.max(lastRange[1], curRange[1]); + } + return resList.toArray(new int[resList.size()][1]); + } +} diff --git a/Week_08/README.md b/Week_08/README.md index 50de3041..fd8e4db0 100644 --- a/Week_08/README.md +++ b/Week_08/README.md @@ -1 +1,32 @@ -学习笔记 \ No newline at end of file +### 位运算 + +#### 记住一些运算技巧 + +- 获取 x 的第 n 位值(0 或者 1): (x >> n) & 1 +- 获取 x 的第 n 位的幂值: x & (1 << n) +- 仅将第 n 位置为 1: x | (1 << n) +- 仅将第 n 位置为 0: x & (~ (1 << n)) +- 清零最低位的 1: x = x & (x - 1) +- 得到最低位的 1: x & -x + + + +### 布隆过滤器, LRU Cache + +#### 布隆过滤器 + +- 布隆过滤器使用bitmap + 多组Hash function构建 +- 应用在不需存放整个Key与Value,而只要快速判断key是否在talbe中的场景 +- 因为hash碰撞的原因,可能会造成误报情况,就是判断在Table内但却不在,所以需要搭配后端存放完整Key的容器再次判断 + +#### LRU Cache + +- 一种缓存替换策略,当缓存空间不足以存放新近元素时,淘汰最久没有使用到的元素 + + + +### 排序算法 + +**详情参见:** ` **https://www.cnblogs.com/onepixel/p/7674659.html**` + +![image.png](https://cdn.nlark.com/yuque/0/2021/png/324862/1611555205953-83434aa5-bfbf-4647-a56b-de9862650e61.png) \ No newline at end of file diff --git a/Week_08/RelativeSortArray.java b/Week_08/RelativeSortArray.java new file mode 100644 index 00000000..3ca77850 --- /dev/null +++ b/Week_08/RelativeSortArray.java @@ -0,0 +1,58 @@ +import java.util.Map; +import java.util.TreeMap; + +//1122 数组的相对排序 https://leetcode-cn.com/problems/relative-sort-array/ +public class RelativeSortArray { + public static void main(String[] args) { + + + } + + /** + * arrCnt数组用于 hash计数,排序 + */ + public int[] relativeSortArray_1(int[] arr1, int[] arr2) { + int[] arrCnt = new int[1001]; + int[] res = new int[arr1.length]; + int index = 0; + + for (int i : arr1) arrCnt[i]++; //计数 + for (int i : arr2) + while (arrCnt[i]-- > 0) res[index++] = i; + + for (int i = 0; i < arrCnt.length; i++) + while (arrCnt[i]-- > 0) res[index++] = i; + + return res; + } + + + /** + * treemap 用于 计数,排序 + */ + public int[] relativeSortArray(int[] arr1, int[] arr2) { + int[] res = new int[arr1.length]; + int index = 0; + TreeMap map = new TreeMap<>(); + + for (int i = 0; i < arr1.length; i++) { + map.put(arr1[i], map.getOrDefault(arr1[i], 0) + 1); + } + + for (int i = 0; i < arr2.length; i++) { + int key = arr2[i]; + int value = map.get(key); + while (value-- > 0) res[index++] = arr2[i]; + map.remove(key); + } + + for (Map.Entry entry : map.entrySet()) { + int key = entry.getKey(); + int value = entry.getValue(); + while (value-- > 0) + res[index++] = key; + } + return res; + + } +} diff --git a/Week_08/ReverseBits.java b/Week_08/ReverseBits.java new file mode 100644 index 00000000..7e0e2b2c --- /dev/null +++ b/Week_08/ReverseBits.java @@ -0,0 +1,32 @@ +//190 颠倒二进制位 https://leetcode-cn.com/problems/reverse-bits/ +public class ReverseBits { + public static void main(String[] args) { + System.out.println(new ReverseBits().reverseBits(1)); + System.out.println(Integer.MAX_VALUE); + } + + /** + * 位置替换 , 比如 高32位换低1位, 高31位换低2位 + */ + public int reverseBits(int n) { + int res = 0; + for (int i = 0; i < 32 ; i++) { + res = res | ((n >> i) & 1) << (31 - i); + } + return res; + } + + + /** + * res 向左移空出一个位 + 加上n的最右边的位, n右移,最右的一位下一次res相加。 + */ + public int reverseBits_1(int n) { + int res = 0; + for (int i = 0; i < 32; i++) { + res = (res <<= 1) + (n & 1); + n >>= 1; + } + return res; + } + +} diff --git a/Week_08/TotalNQueens.java b/Week_08/TotalNQueens.java new file mode 100644 index 00000000..f8528b18 --- /dev/null +++ b/Week_08/TotalNQueens.java @@ -0,0 +1,51 @@ +public class TotalNQueens { + private int size; + private int count; + + + public static void main(String[] args) { +// System.out.println(new practice.bit.TotalNQueens().totalNQueens(4)); + } + + public int totalNQueens(int n) { + count = 0; + size = (1 << n) - 1; + displayBit("size", size); + solve(0, 0, 0); + return count; + } + + private void solve(int row, int ld, int rd) { + if (row == size) { + count++; + return; + } + displayBit("row",row); + displayBit("ld",ld); + displayBit("rd",rd); + + int pos = size & (~(row | ld | rd)); // 位上的1表示没有被占 + displayBit("pos",pos); + while (pos != 0) { + displayBit("-pos",(-pos)); + int p = pos & (-pos); + displayBit("p",p); + pos -= p; // pos &= pos - 1; + displayBit("pos",(pos)); + solve(row | p, (ld | p) << 1, (rd | p) >> 1); + } + } + + public static void displayBit(String info, int num) { + StringBuilder sb = new StringBuilder(); + while (num > 0) { + int bit = num % 2; + sb.append(String.valueOf(bit)); + num = num / 2; + } + while (sb.length() < 4) sb.append("0"); + System.out.println(info + " : " + sb.reverse().toString()); + } + + +} diff --git a/Week_08/sort/BubbleSort.java b/Week_08/sort/BubbleSort.java new file mode 100644 index 00000000..5d14d4c1 --- /dev/null +++ b/Week_08/sort/BubbleSort.java @@ -0,0 +1,27 @@ +package sort; + +import utils.Utils; + +public class BubbleSort { + + public static void main(String[] args) { + int[] arr = new int[]{30, 20, 10, 5, 4, 3, -1, 2, 1, 0}; + bubbleSort(arr); + Utils.displayArray(arr); + } + + /** + * 冒泡排序 + */ + public static void bubbleSort(int[] arr){ + for (int i = 0; i < arr.length - 1; i++) { + for (int j = 0; j < arr.length - i - 1; j++) { + if (arr[j] > arr[j + 1]) { + int tmp = arr[j]; + arr[j] = arr[j + 1]; + arr[j + 1] = tmp; + } + } + } + } +} diff --git a/Week_08/sort/HeapSort.java b/Week_08/sort/HeapSort.java new file mode 100644 index 00000000..80730589 --- /dev/null +++ b/Week_08/sort/HeapSort.java @@ -0,0 +1,42 @@ +package sort; + +import utils.Utils; + +public class HeapSort { + public static void main(String[] args) { + int[] arr = new int[]{30, 20, 10, 5, 4, 3, -1, 2, 1, 0}; + heapSort(arr); + Utils.displayArray(arr); + } + + + static void heapify(int[] array, int length, int i) { + int left = 2 * i + 1, right = 2 * i + 2;int largest = i; + if (left < length && array[left] > array[largest]) { + largest = left; + } + if (right < length && array[right] > array[largest]) { + largest = right; + } + if (largest != i) { + int temp = array[i]; + array[i] = array[largest]; + array[largest] = temp; + heapify(array, length, largest); + } + } + + public static void heapSort(int[] array) { + if (array.length == 0) return; + int length = array.length; + for (int i = length / 2 - 1; i >= 0; i --) + heapify(array, length, i); + + for (int i = length - 1; i >= 0; i--) { + int temp = array[0]; + array[0] = array[i]; + array[i] = temp; + heapify(array, i, 0); + } + } +} diff --git a/Week_08/sort/InsertionSort.java b/Week_08/sort/InsertionSort.java new file mode 100644 index 00000000..225a3259 --- /dev/null +++ b/Week_08/sort/InsertionSort.java @@ -0,0 +1,30 @@ +package sort; + +import utils.Utils; + +public class InsertionSort { + + public static void main(String[] args) { + int[] arr = new int[]{30, 20, 10, 5, 4, 3, -1, 2, 1, 0}; + insertionSort(arr); + Utils.displayArray(arr); + } + + /** + * 插入排序 + */ + private static void insertionSort(int[] arr) { + + for (int i = 0; i < arr.length - 1; i++) { + int preIndex = i; + int curNum = arr[i + 1]; + while (preIndex >= 0 && curNum < arr[preIndex]) { + arr[preIndex + 1] = arr[preIndex]; + preIndex--; + } + arr[preIndex + 1] = curNum; + } + + } + +} diff --git a/Week_08/sort/MergeSort.java b/Week_08/sort/MergeSort.java new file mode 100644 index 00000000..c9e16f66 --- /dev/null +++ b/Week_08/sort/MergeSort.java @@ -0,0 +1,38 @@ +package sort; + +import utils.Utils; + +public class MergeSort { + public static void main(String[] args) { + int[] arr = new int[]{30, 20, 10, 5, 4, 3, -1, 40, 1, 0}; + mergeSort(arr, 0, arr.length - 1); + Utils.displayArray(arr); + } + + /** + * 只操作一个数组,通过下标限制排序的范围 + */ + private static void mergeSort(int[] arr, int begin, int end) { + if (end <= begin) return; + + int mid = (begin + end) >> 1; + mergeSort(arr, begin, mid); + mergeSort(arr, mid + 1, end); + merge(arr, begin, mid, end); + } + + private static void merge(int[] arr, int begin, int mid, int end) { + int[] tmp = new int[end - begin + 1]; + int a = begin, b = mid + 1, k = 0; + + while (a <= mid && b <= end) + tmp[k++] = arr[a] < arr[b] ? arr[a++] : arr[b++]; + + while (a <= mid) tmp[k++] = arr[a++]; + while (b <= end) tmp[k++] = arr[b++]; + + for (int i = 0; i < tmp.length; i++) arr[begin + i] = tmp[i]; + } + + +} diff --git a/Week_08/sort/QuickSort.java b/Week_08/sort/QuickSort.java new file mode 100644 index 00000000..9239d0d3 --- /dev/null +++ b/Week_08/sort/QuickSort.java @@ -0,0 +1,50 @@ +package sort; + +import utils.Utils; + +public class QuickSort { + + + public static void main(String[] args) { + int[] arr = new int[]{30, 20, 10, 5, 4, 3, -1, 40, 1, 0}; + quickSort(arr, 0, arr.length - 1); + Utils.displayArray(arr); + } + + /** + * 根据pivot进行分治排序 + */ + private static void quickSort(int[] arr,int begin, int end) { + //terminator + if (end <= begin) return; + //process + int pivot = partition(arr, begin, end); + //drill down + merge + quickSort(arr, begin, pivot - 1); + quickSort(arr, pivot + 1, end); + } + + /** + * 目的是返回pivot, 首次默认以end作为pivot + */ + private static int partition(int[] arr, int begin, int end) { + int pivot = end, curIndex = begin; + for (int i = begin; i < end; i++) { + if (arr[i] < arr[pivot]) + swap(arr, curIndex++, i); + } + + swap(arr, curIndex, pivot); + + return curIndex; + } + + + private static void swap(int[] arr, int a, int b) { + int tmp = arr[a]; + arr[a] = arr[b]; + arr[b] = tmp; + } + + +} diff --git a/Week_08/sort/SelectionSort.java b/Week_08/sort/SelectionSort.java new file mode 100644 index 00000000..1500e79b --- /dev/null +++ b/Week_08/sort/SelectionSort.java @@ -0,0 +1,29 @@ +package sort; + +import utils.Utils; + +public class SelectionSort { + public static void main(String[] args) { + int[] arr = new int[]{30, 20, 10, 5, 4, 3, -1, 2, 1, 0}; + selectSort(arr); + Utils.displayArray(arr); + } + + /** + * 选择排序 : 选最小的数替换数组 + */ + private static void selectSort(int[] arr) { + for (int i = 0; i < arr.length; i++) { + int minIndex = i; + + for (int j = i + 1; j < arr.length; j++) { + if (arr[minIndex] > arr[j]) + minIndex = j; + } + int tmp = arr[i]; + arr[i] = arr[minIndex]; + arr[minIndex] = tmp; + } + } + +} diff --git a/Week_09/IsIsomorphic.java b/Week_09/IsIsomorphic.java new file mode 100644 index 00000000..4c3f9dac --- /dev/null +++ b/Week_09/IsIsomorphic.java @@ -0,0 +1,29 @@ +import java.util.HashMap; + +// 205. 同构字符串 https://leetcode-cn.com/problems/isomorphic-strings/ +public class IsIsomorphic { + + public static void main(String[] args) { + System.out.println(new IsIsomorphic().isIsomorphic("egg", "add")); + } + + /** + * 通过map映射 : 每次put操作之前判断之前是否已经映射, 若是,比较已经映射的值 和 即将映射的值 知否相等。 + * 如果之前没有被映射, 还要判断即将映射的值是否已经被映射。 + */ + public boolean isIsomorphic(String s, String t) { + HashMap map = new HashMap<>(); + int index = 0; + while (index < s.length()) { + if (map.containsKey(s.charAt(index))) { + if (map.get(s.charAt(index)) != t.charAt(index)) return false; + } else if (!map.containsValue(t.charAt(index))){ + map.put(s.charAt(index), t.charAt(index)); + }else return false; + index++; + } + return true; + } + + +} diff --git a/Week_09/LengthOfLIS.java b/Week_09/LengthOfLIS.java new file mode 100644 index 00000000..b5581553 --- /dev/null +++ b/Week_09/LengthOfLIS.java @@ -0,0 +1,32 @@ +import java.util.Arrays; + +// 最长递增子序列 https://leetcode-cn.com/problems/longest-increasing-subsequence/ +public class LengthOfLIS { + public static void main(String[] args) { + System.out.println(new LengthOfLIS().lengthOfLIS(new int[]{0, 1, 0, 3, 2, 3})); + } + + + /** + * dp[i] 表示nums数组从0到i中最大递增序列长度 + *

+ * int j = 0; + * while (j++ < i) + * if (nums[i] > nums[j - 1]) dp[i] = Math.max(dp[i], dp[j - 1] + 1); + */ + public int lengthOfLIS(int[] nums) { + if (nums.length <= 1) return nums.length; + int dp[] = new int[nums.length]; + int maxLen = 0; + Arrays.fill(dp, 1); + + for (int i = 1; i < nums.length; i++) { + int j = 0; + while (j++ < i) + if (nums[i] > nums[j - 1]) dp[i] = Math.max(dp[i], dp[j - 1] + 1); + maxLen = Math.max(maxLen, dp[i]); + } + return maxLen; + } + +} diff --git a/Week_09/LongestPalindrome.java b/Week_09/LongestPalindrome.java new file mode 100644 index 00000000..9c80b9cd --- /dev/null +++ b/Week_09/LongestPalindrome.java @@ -0,0 +1,41 @@ +//5 最长回文子串 https://leetcode-cn.com/problems/longest-palindromic-substring/ +public class LongestPalindrome { + public static void main(String[] args) { + System.out.println(new LongestPalindrome().longestPalindrome("")); + } + + + /** + * 动态规划 + * + * + */ + public String longestPalindromeByDp(String s) { + return null; + } + + + + /** + * 遍历每个字符 , 向两边扩散 + */ + public String longestPalindrome(String s) { + String maxLenStr = s.substring(0,1); + for (int i = 0; i < s.length(); i++) { + String len1Str = ""; + if (i > 0) len1Str = getPalindrome(s, i - 1, i + 1); + String len2Str = getPalindrome(s, i, i + 1); + String resStr = len1Str.length() > len2Str.length() ? len1Str : len2Str; + maxLenStr = maxLenStr.length() > resStr.length() ? maxLenStr : resStr; + } + return maxLenStr; + } + + private String getPalindrome(String s, int l, int r) { + while (l >= 0 && r < s.length() && s.charAt(l) == s.charAt(r)) { + l--; r++; + } + return s.substring(l + 1, r); + } + +} diff --git a/Week_09/MaxProfit.java b/Week_09/MaxProfit.java new file mode 100644 index 00000000..71ea334a --- /dev/null +++ b/Week_09/MaxProfit.java @@ -0,0 +1,27 @@ +public class MaxProfit { + + + public int maxProfit(int[] prices) { + + /** + * + * i - 天 + * k - 是否卖出 1:卖出 0:没有卖出 + * + * dp[0][0] = prices[i]; //持有股票的钱 + * + * i > 0 + * dp[i][1] = Math.max(dp[i - 1][1], prices[i] - dp[i - 1][0]); //卖了的利润 + * dp[i][0] = Math.min(dp[i - 1][0], prices[i]); //持有成本最低的股票 + * + */ + + int[][] dp = new int[prices.length][2]; + dp[0][0] = prices[0]; + for (int i = 1; i < prices.length; i++) { + dp[i][1] = Math.max(dp[i - 1][1], prices[i] - dp[i - 1][0]); //卖了的利润 + dp[i][0] = Math.min(dp[i - 1][0], prices[i]); //持有成本最低的股票 + } + return dp[prices.length - 1][1]; + } +} diff --git a/Week_09/README.md b/Week_09/README.md index 50de3041..7c8a336f 100644 --- a/Week_09/README.md +++ b/Week_09/README.md @@ -1 +1,3 @@ -学习笔记 \ No newline at end of file +## 动态规划 + + 动态规划实际上是在填表,并且高行/列的数值通常可以通过低行/列得到,通过状态转移实现。 \ No newline at end of file diff --git a/Week_09/ReverseOnlyLetters.java b/Week_09/ReverseOnlyLetters.java new file mode 100644 index 00000000..c77902c2 --- /dev/null +++ b/Week_09/ReverseOnlyLetters.java @@ -0,0 +1,25 @@ +//917 仅仅翻转字母 https://leetcode-cn.com/problems/reverse-only-letters/ +public class ReverseOnlyLetters { + + public static void main(String[] args) { + System.out.println(new ReverseOnlyLetters().reverseOnlyLetters("7_28]")); + + } + + /** + * 双指针夹逼 + */ + public String reverseOnlyLetters(String S) { + int begin = 0, end = S.length() - 1; + char[] charArray = S.toCharArray(); + + while (begin < end) { + while (!Character.isLetter(charArray[begin]) && begin < end) begin++; + while (!Character.isLetter(charArray[end]) && begin < end) end--; + char tmp = charArray[begin]; + charArray[begin++] = charArray[end]; + charArray[end--] = tmp; + } + return new String(charArray); + } +} diff --git a/Week_09/ReverseStr.java b/Week_09/ReverseStr.java new file mode 100644 index 00000000..3c46dfa7 --- /dev/null +++ b/Week_09/ReverseStr.java @@ -0,0 +1,22 @@ +//541. 反转字符串 II https://leetcode-cn.com/problems/reverse-string-ii/ +public class ReverseStr { + + public static void main(String[] args) { + System.out.println(new ReverseStr().reverseStr("abcd1234jkmn12", 2)); + } + + public String reverseStr(String s, int k) { + char[] strArr = s.toCharArray(); + for (int i = 0; i < s.length(); i += 2 * k) { //每次只对 (2k * i, k)范围内的前k个字符操作 + int start = i; //双指针 + int end = Math.min(i + k - 1, s.length() - 1); //如果 s.length < k ,只对k个字符反转 + while (start < end) { + char tmp = strArr[start]; + strArr[start++] = strArr[end]; + strArr[end--] = tmp; + } + } + + return new String(strArr); + } +} diff --git a/Week_09/ReverseWords.java b/Week_09/ReverseWords.java new file mode 100644 index 00000000..b5baa3a0 --- /dev/null +++ b/Week_09/ReverseWords.java @@ -0,0 +1,18 @@ +// 151. 翻转字符串里的单词 https://leetcode-cn.com/problems/reverse-words-in-a-string/ +public class ReverseWords { + + public static void main(String[] args) { + System.out.println(new ReverseWords().reverseWords("a good example")); + } + public String reverseWords(String s) { + String[] str = s.trim().split(" "); + StringBuilder sb = new StringBuilder(); + for (int i = str.length - 1; i >= 0; i--) { + if (!str[i].equals("")) { + sb.append(str[i]); + if(i > 0) sb.append(" "); + } + } + return sb.toString(); + } +} diff --git a/Week_09/ReverseWords_3.java b/Week_09/ReverseWords_3.java new file mode 100644 index 00000000..7e319caa --- /dev/null +++ b/Week_09/ReverseWords_3.java @@ -0,0 +1,44 @@ +// 557. 反转字符串中的单词 III https://leetcode-cn.com/problems/reverse-words-in-a-string-iii/ +public class ReverseWords_3 { + + public static void main(String[] args) { + System.out.println(new ReverseWords_3().reverseWords("Let's take LeetCode contest")); + System.out.println(new ReverseWords_3().reverseWords_2("Let's take LeetCode contest")); + } + + /** + * 系统api解法 + */ + public String reverseWords(String s) { + String[] strArr = s.split(" "); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < strArr.length; i++) { + if (!strArr[i].equals("")) sb.append(new StringBuilder(strArr[i]).reverse().toString()); + if (i < strArr.length - 1) sb.append(" "); + } + return sb.toString(); + } + + + /** + * 双指针解法 + */ + public String reverseWords_2(String s) { + char[] chArr = s.toCharArray(); + int start = 0, end = 0, length = s.length(); + while (end < length) { + while (end < length && chArr[end] != ' ') end++; //找到空格,然后翻转空格前的单词 + swapChar(chArr, start, end - 1); + start = end++ + 1; + } + return new String(chArr); + } + + private void swapChar(char[] chArr, int start, int end) { + while (start < end) { + char tmp = chArr[start]; + chArr[start++] = chArr[end]; + chArr[end--] = tmp; + } + } +} diff --git a/Week_09/ToLowerCase.java b/Week_09/ToLowerCase.java new file mode 100644 index 00000000..e0220848 --- /dev/null +++ b/Week_09/ToLowerCase.java @@ -0,0 +1,15 @@ +public class ToLowerCase { + public static void main(String[] args) { + System.out.println(" aa bb cc ".trim()); + } + + public String toLowerCase(String str) { + char[] chars = str.toCharArray(); + for(int i = 0; i < str.length(); i++) { + if (chars[i] >= 'A' && chars[i] <= 'Z') chars[i] += 32; + } + + return new String(chars); + + } +} diff --git a/Week_09/UniquePathsWithObstacles.java b/Week_09/UniquePathsWithObstacles.java new file mode 100644 index 00000000..cecdb34d --- /dev/null +++ b/Week_09/UniquePathsWithObstacles.java @@ -0,0 +1,33 @@ +// 63 不同路径2 https://leetcode-cn.com/problems/unique-paths-ii/ +public class UniquePathsWithObstacles { + + + /** + * + * dp[0][0] = 1; + * dp[0][j] = 1; //j > 0, 需要判断左侧没有障碍物 + * dp[i][0] = 1; //i > 0, 需要判断上方没有障碍物 + * dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; + */ + public int uniquePathsWithObstacles(int[][] obstacleGrid) { + if(obstacleGrid[0][0] == 1) return 0; + int rows = obstacleGrid.length; + int cols = obstacleGrid[0].length; + int[][] dp = new int[rows][cols]; + dp[0][0] = 1; + + for (int i = 0; i < rows; i++) { + for (int j = 0; j < cols; j++) { + if (obstacleGrid[i][j] == 0) { + if ((i == 0 || j == 0) && (i > 0 && dp[i - 1][j] > 0 || j > 0 && dp[i][j - 1] > 0)) + dp[i][j] = 1; //初始化 第0行 第0列都为1 + else if (i > 0 && j > 0) + dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; + } + } + } + return dp[rows - 1][cols - 1]; + } + + +} diff --git a/Week_09/ValidPalindrome.java b/Week_09/ValidPalindrome.java new file mode 100644 index 00000000..fa6c0945 --- /dev/null +++ b/Week_09/ValidPalindrome.java @@ -0,0 +1,27 @@ +// 680 验证回文字符串 https://leetcode-cn.com/problems/valid-palindrome-ii/ +public class ValidPalindrome { + + public static void main(String[] args) { + System.out.println(new ValidPalindrome().validPalindrome + ("lcupuufxoohdfpgjdmysgvhmvffcnqxjjxqncffvmhvgsymdjgpfdhooxfuupucul")); + } + + /** + * 双指针 + 递归 + * @param s + * @return + */ + public boolean validPalindrome(String s) { + int begin = 0, end = s.length() - 1; + int cnt = 0; + return isvalidPalindrome(s, begin, end, cnt); + } + + private boolean isvalidPalindrome(String s, int begin, int end, int cnt) { + if(cnt > 1) return false; + if(begin >= end) return true; + if (s.charAt(begin) == s.charAt(end)) return isvalidPalindrome(s, begin + 1, end - 1, cnt); + return isvalidPalindrome(s, begin + 1, end, cnt + 1) || isvalidPalindrome(s, begin, end - 1, cnt + 1); + } + +} diff --git "a/Week_\346\257\225\344\270\232\346\200\273\347\273\223/README.md" "b/Week_\346\257\225\344\270\232\346\200\273\347\273\223/README.md" index 50de3041..8597b327 100644 --- "a/Week_\346\257\225\344\270\232\346\200\273\347\273\223/README.md" +++ "b/Week_\346\257\225\344\270\232\346\200\273\347\273\223/README.md" @@ -1 +1,9 @@ -学习笔记 \ No newline at end of file +# 毕业总结 + +经过2个月的学习,学习算法最大的感受是先理解理论知识,然后多练,重复,简而言之就是熟能生巧。 + +整个学习过程中收获很多,比如不要死磕题,意识到优先级,时间成本的重要性,也深刻感受到重复带来的反馈,使得自己面对算法题更有信心。 + +最开心的莫过于按照老师的方法论不看题解就能把动态规划的题解出来。 + +如果说学好算法最重要的还是想学的欲望以及坚持。