diff --git a/README.md b/README.md
index 501762a..a008d61 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# LeetCode-Java
## 说明
-- leetcode练习,坚持每天一道,目前已完成252道
+- leetcode练习,坚持每天一道,目前已完成275道
- 解题语言是Java
- 每道题都是可编译运行的
- 每道题有自己的方法和他人优秀解法
@@ -10,21 +10,19 @@
- 网址:https://leetcode-cn.com/
## 待解题目列表
-2020春节放假停更,祝大家越码越溜~
+剑指offer系列-持续多周,每周7题
-- [x] [118. 杨辉三角](https://github.com/pphdsny/Leetcode-Java/blob/master/src/pp/arithmetic/leetcode/_118_generate.java)
+- [x] [剑指 Offer 16. 数值的整数次方](https://github.com/pphdsny/Leetcode-Java/blob/master/src/pp/arithmetic/offer/_16_myPow.java)
+- [x] [剑指 Offer 17. 打印从1到最大的n位数](https://github.com/pphdsny/Leetcode-Java/blob/master/src/pp/arithmetic/offer/_17_printNumbers.java)
+- [x] [剑指 Offer 18. 删除链表的节点](https://leetcode-cn.com/problems/shan-chu-lian-biao-de-jie-dian-lcof/)
+- [ ] [剑指 Offer 19. 正则表达式匹配](https://leetcode-cn.com/problems/zheng-ze-biao-da-shi-pi-pei-lcof/)
+- [ ] [剑指 Offer 20. 表示数值的字符串](https://leetcode-cn.com/problems/biao-shi-shu-zhi-de-zi-fu-chuan-lcof/)
+- [ ] [剑指 Offer 21. 调整数组顺序使奇数位于偶数前面](https://leetcode-cn.com/problems/diao-zheng-shu-zu-shun-xu-shi-qi-shu-wei-yu-ou-shu-qian-mian-lcof/)
+- [ ] [剑指 Offer 22. 链表中倒数第k个节点](https://leetcode-cn.com/problems/lian-biao-zhong-dao-shu-di-kge-jie-dian-lcof/)
-- [x] [119. 杨辉三角 II](https://github.com/pphdsny/Leetcode-Java/blob/master/src/pp/arithmetic/leetcode/_119_getRow.java)
-
-- [x] [129. 求根到叶子节点数字之和](https://github.com/pphdsny/Leetcode-Java/blob/master/src/pp/arithmetic/leetcode/_129_sumNumbers.java)
-
-- [x] [130. 被围绕的区域](https://github.com/pphdsny/Leetcode-Java/blob/master/src/pp/arithmetic/leetcode/_130_solve.java)
-
-- [x] [131. 分割回文串](https://github.com/pphdsny/Leetcode-Java/blob/master/src/pp/arithmetic/leetcode/_131_partition.java)
-
-- [x] [132. 分割回文串 II](https://github.com/pphdsny/Leetcode-Java/blob/master/src/pp/arithmetic/leetcode/_132_minCut.java)
+LCP
-- [x] [133. 克隆图](https://github.com/pphdsny/Leetcode-Java/blob/master/src/pp/arithmetic/leetcode/_133_cloneGraph.java)
+- [x] [LCP 06. 拿硬币](https://github.com/pphdsny/Leetcode-Java/blob/master/src/pp/arithmetic/LCP/_6_minCount.java)
## 已解题目
@@ -59,9 +57,32 @@
- [线段树](https://leetcode-cn.com/tag/segment-tree/)(9)
- [二叉搜索树](https://leetcode-cn.com/tag/binary-search-tree/)(15)
-### 题目列表(更新中—已完成251)
+### 题目列表(更新中—已完成275)
-[Leetcode-Java(250+题解,持续更新、欢迎star&留言&交流)](https://github.com/pphdsny/Leetcode-Java/blob/master/src/pp/arithmetic/leetcode/_133_cloneGraph.java)
+[Leetcode-Java(270+题解,持续更新、欢迎star&留言&交流)](https://github.com/pphdsny/Leetcode-Java/blob/master/src/pp/arithmetic/offer/_18_deleteNode.java)
+
+#### 剑指offer系列
+
+| 题目 | 解决方案 | 相关话题 | 难度 | 备注 |
+| ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ | ------ | ---- |
+| [剑指 Offer 03. 数组中重复的数字](https://leetcode-cn.com/problems/shu-zu-zhong-zhong-fu-de-shu-zi-lcof/) | [FindRepeatNumber](https://github.com/pphdsny/Leetcode-Java/blob/master/src/pp/arithmetic/offer/_03_findRepeatNumber.java) | [数组](
+ * 桌上有 n 堆力扣币,每堆的数量保存在数组 coins 中。我们每次可以选择任意一堆,拿走其中的一枚或者两枚,求拿完所有力扣币的最少次数。 + *
+ * 示例 1: + *
+ * 输入:[4,2,1] + *
+ * 输出:4 + *
+ * 解释:第一堆力扣币最少需要拿 2 次,第二堆最少需要拿 1 次,第三堆最少需要拿 1 次,总共 4 次即可拿完。 + *
+ * 示例 2: + *
+ * 输入:[2,3,10] + *
+ * 输出:8 + *
+ * 限制: + *
+ * 1 <= n <= 4 + * 1 <= coins[i] <= 10 + *
+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/na-ying-bi + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +public class _6_minCount { + + public static void main(String[] args) { + _6_minCount minCount = new _6_minCount(); + System.out.println(minCount.minCount(new int[]{4, 2, 1})); + System.out.println(minCount.minCount(new int[]{2, 3, 10})); + + } + + /** + * 解题思路: + * 需要最少次数,利用贪心的思路,每次尽可能的多拿(也就是2个) + * + * @param coins + * @return + */ + public int minCount(int[] coins) { + if (coins == null) return 0; + int retVal = 0; + for (int i = 0; i < coins.length; i++) { + int coin = coins[i]; + if (coin % 2 == 0) { + retVal += coin / 2; + } else { + retVal += coin / 2 + 1; + } + } + + return retVal; + } +} diff --git a/src/pp/arithmetic/leetcode/_1114_Foo.java b/src/pp/arithmetic/leetcode/_1114_Foo.java new file mode 100644 index 0000000..c78916a --- /dev/null +++ b/src/pp/arithmetic/leetcode/_1114_Foo.java @@ -0,0 +1,117 @@ +package pp.arithmetic.leetcode; + +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Created by wangpeng on 2020-03-09. + * 1114. 按序打印 + *
+ * 我们提供了一个类: + *
+ * public class Foo { + * public void one() { print("one"); } + * public void two() { print("two"); } + * public void three() { print("three"); } + * } + * 三个不同的线程将会共用一个 Foo 实例。 + *
+ * 线程 A 将会调用 one() 方法 + * 线程 B 将会调用 two() 方法 + * 线程 C 将会调用 three() 方法 + * 请设计修改程序,以确保 two() 方法在 one() 方法之后被执行,three() 方法在 two() 方法之后被执行。 + *
+ * + *
+ * 示例 1: + *
+ * 输入: [1,2,3] + * 输出: "onetwothree" + * 解释: + * 有三个线程会被异步启动。 + * 输入 [1,2,3] 表示线程 A 将会调用 one() 方法,线程 B 将会调用 two() 方法,线程 C 将会调用 three() 方法。 + * 正确的输出是 "onetwothree"。 + * 示例 2: + *
+ * 输入: [1,3,2] + * 输出: "onetwothree" + * 解释: + * 输入 [1,3,2] 表示线程 A 将会调用 one() 方法,线程 B 将会调用 three() 方法,线程 C 将会调用 two() 方法。 + * 正确的输出是 "onetwothree"。 + * + *
+ * 注意: + *
+ * 尽管输入中的数字似乎暗示了顺序,但是我们并不保证线程在操作系统中的调度顺序。 + *
+ * 你看到的输入格式主要是为了确保测试的全面性。 + *
+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/print-in-order + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +public class _1114_Foo { + + public static void main(String[] args) { + Foo foo = new Foo(); + Runnable runnable1 = new Runnable() { + @Override + public void run() { + System.out.println("printFirst"); + } + }; + Runnable runnable2 = new Runnable() { + @Override + public void run() { + System.out.println("printSecond"); + } + }; + Runnable runnable3 = new Runnable() { + @Override + public void run() { + System.out.println("printThird"); + } + }; + try { + foo.first(runnable1); + foo.third(runnable3); + foo.second(runnable2); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + } + + //一道题自己没有跑成功,不知道测试用例如何输出的 + static class Foo { + + private AtomicInteger firstJobDone = new AtomicInteger(0); + private AtomicInteger secondJobDone = new AtomicInteger(0); + + public Foo() {} + + public void first(Runnable printFirst) throws InterruptedException { + // printFirst.run() outputs "first". + printFirst.run(); + // mark the first job as done, by increasing its count. + firstJobDone.incrementAndGet(); + } + + public void second(Runnable printSecond) throws InterruptedException { + while (firstJobDone.get() != 1) { + // waiting for the first job to be done. + } + // printSecond.run() outputs "second". + printSecond.run(); + // mark the second as done, by increasing its count. + secondJobDone.incrementAndGet(); + } + + public void third(Runnable printThird) throws InterruptedException { + while (secondJobDone.get() != 1) { + // waiting for the second job to be done. + } + // printThird.run() outputs "third". + printThird.run(); + } + } +} diff --git a/src/pp/arithmetic/leetcode/_1115_FooBar.java b/src/pp/arithmetic/leetcode/_1115_FooBar.java new file mode 100644 index 0000000..b981d43 --- /dev/null +++ b/src/pp/arithmetic/leetcode/_1115_FooBar.java @@ -0,0 +1,111 @@ +package pp.arithmetic.leetcode; + +import java.util.concurrent.Semaphore; + +/** + * Created by wangpeng on 2020-07-08. + * 1115. 交替打印FooBar + *
+ * 我们提供一个类: + *
+ * class FooBar { + * public void foo() { + * for (int i = 0; i < n; i++) { + * print("foo"); + * } + * } + *
+ * public void bar() { + * for (int i = 0; i < n; i++) { + * print("bar"); + * } + * } + * } + * 两个不同的线程将会共用一个 FooBar 实例。其中一个线程将会调用 foo() 方法,另一个线程将会调用 bar() 方法。 + *
+ * 请设计修改程序,以确保 "foobar" 被输出 n 次。 + *
+ * + *
+ * 示例 1: + *
+ * 输入: n = 1 + * 输出: "foobar" + * 解释: 这里有两个线程被异步启动。其中一个调用 foo() 方法, 另一个调用 bar() 方法,"foobar" 将被输出一次。 + * 示例 2: + *
+ * 输入: n = 2 + * 输出: "foobarfoobar" + * 解释: "foobar" 将被输出两次。 + *
+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/print-foobar-alternately + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +public class _1115_FooBar { + + public static void main(String[] args) { + + FooBar fooBar = new FooBar(5); + new Thread() { + @Override + public void run() { + try { + fooBar.foo(new Runnable() { + @Override + public void run() { + System.out.println("foo"); + } + }); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }.start(); + new Thread() { + @Override + public void run() { + try { + fooBar.bar(new Runnable() { + @Override + public void run() { + System.out.println("bar"); + } + }); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }.start(); + + } + + static class FooBar { + private Semaphore fooSe = new Semaphore(0); + private Semaphore barSe = new Semaphore(1); + + private int n; + + public FooBar(int n) { + this.n = n; + } + + public void foo(Runnable printFoo) throws InterruptedException { + + for (int i = 0; i < n; i++) { + barSe.acquire(); + printFoo.run(); + fooSe.release(); + } + } + + public void bar(Runnable printBar) throws InterruptedException { + + for (int i = 0; i < n; i++) { + fooSe.acquire(); + printBar.run(); + barSe.release(); + } + } + } +} diff --git a/src/pp/arithmetic/leetcode/_1116_ZeroEvenOdd.java b/src/pp/arithmetic/leetcode/_1116_ZeroEvenOdd.java new file mode 100644 index 0000000..5aeac21 --- /dev/null +++ b/src/pp/arithmetic/leetcode/_1116_ZeroEvenOdd.java @@ -0,0 +1,125 @@ +package pp.arithmetic.leetcode; + +import java.util.concurrent.Semaphore; +import java.util.function.IntConsumer; + +/** + * Created by wangpeng on 2020-07-09. + * 1116. 打印零与奇偶数 + *
+ * 假设有这么一个类: + *
+ * class ZeroEvenOdd { + * public ZeroEvenOdd(int n) { ... } // 构造函数 + * public void zero(printNumber) { ... } // 仅打印出 0 + * public void even(printNumber) { ... } // 仅打印出 偶数 + * public void odd(printNumber) { ... } // 仅打印出 奇数 + * } + * 相同的一个 ZeroEvenOdd 类实例将会传递给三个不同的线程: + *
+ * 线程 A 将调用 zero(),它只输出 0 。 + * 线程 B 将调用 even(),它只输出偶数。 + * 线程 C 将调用 odd(),它只输出奇数。 + * 每个线程都有一个 printNumber 方法来输出一个整数。请修改给出的代码以输出整数序列 010203040506... ,其中序列的长度必须为 2n。 + *
+ * + *
+ * 示例 1: + *
+ * 输入:n = 2 + * 输出:"0102" + * 说明:三条线程异步执行,其中一个调用 zero(),另一个线程调用 even(),最后一个线程调用odd()。正确的输出为 "0102"。 + * 示例 2: + *
+ * 输入:n = 5 + * 输出:"0102030405" + *
+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/print-zero-even-odd + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +public class _1116_ZeroEvenOdd { + + public static void main(String[] args) { + ZeroEvenOdd zeroEvenOdd = new ZeroEvenOdd(5); + IntConsumer printNumber = new IntConsumer() { + @Override + public void accept(int value) { + System.out.println(value); + } + }; + new Thread(){ + @Override + public void run() { + super.run(); + try { + zeroEvenOdd.zero(printNumber); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }.start(); + new Thread(){ + @Override + public void run() { + super.run(); + try { + zeroEvenOdd.even(printNumber); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }.start(); + new Thread(){ + @Override + public void run() { + super.run(); + try { + zeroEvenOdd.odd(printNumber); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }.start(); + } + + static class ZeroEvenOdd { + private int n; + private Semaphore zeroSe = new Semaphore(1); + private Semaphore evenSe = new Semaphore(0); + private Semaphore oddSe = new Semaphore(0); + + public ZeroEvenOdd(int n) { + this.n = n; + } + + // printNumber.accept(x) outputs "x", where x is an integer. + public void zero(IntConsumer printNumber) throws InterruptedException { + for (int i = 0; i < n; i++) { + zeroSe.acquire(); + printNumber.accept(0); + if (i % 2 == 0) { + evenSe.release(); + } else { + oddSe.release(); + } + } + } + + public void even(IntConsumer printNumber) throws InterruptedException { + for (int i = 1; i <= n; i+=2) { + evenSe.acquire(); + printNumber.accept(i); + zeroSe.release(); + } + } + + public void odd(IntConsumer printNumber) throws InterruptedException { + for (int i = 2; i <= n; i+=2) { + oddSe.acquire(); + printNumber.accept(i); + zeroSe.release(); + } + } + } +} diff --git a/src/pp/arithmetic/leetcode/_1117_H2O.java b/src/pp/arithmetic/leetcode/_1117_H2O.java new file mode 100644 index 0000000..79a195e --- /dev/null +++ b/src/pp/arithmetic/leetcode/_1117_H2O.java @@ -0,0 +1,197 @@ +package pp.arithmetic.leetcode; + +import java.util.concurrent.BrokenBarrierException; +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.Semaphore; + +/** + * Created by wangpeng on 2020-07-15. + * 1117. H2O 生成 + *
+ * 现在有两种线程,氧 oxygen 和氢 hydrogen,你的目标是组织这两种线程来产生水分子。 + *
+ * 存在一个屏障(barrier)使得每个线程必须等候直到一个完整水分子能够被产生出来。 + *
+ * 氢和氧线程会被分别给予 releaseHydrogen 和 releaseOxygen 方法来允许它们突破屏障。 + *
+ * 这些线程应该三三成组突破屏障并能立即组合产生一个水分子。 + *
+ * 你必须保证产生一个水分子所需线程的结合必须发生在下一个水分子产生之前。 + *
+ * 换句话说: + *
+ * 如果一个氧线程到达屏障时没有氢线程到达,它必须等候直到两个氢线程到达。 + * 如果一个氢线程到达屏障时没有其它线程到达,它必须等候直到一个氧线程和另一个氢线程到达。 + * 书写满足这些限制条件的氢、氧线程同步代码。 + *
+ * + *
+ * 示例 1: + *
+ * 输入: "HOH" + * 输出: "HHO" + * 解释: "HOH" 和 "OHH" 依然都是有效解。 + * 示例 2: + *
+ * 输入: "OOHHHH" + * 输出: "HHOHHO" + * 解释: "HOHHHO", "OHHHHO", "HHOHOH", "HOHHOH", "OHHHOH", "HHOOHH", "HOHOHH" 和 "OHHOHH" 依然都是有效解。 + * + *
+ * 提示: + *
+ * 输入字符串的总长将会是 3n, 1 ≤ n ≤ 50; + * 输入字符串中的 “H” 总数将会是 2n 。 + * 输入字符串中的 “O” 总数将会是 n 。 + *
+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/building-h2o + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +public class _1117_H2O { + + + public static void main(String[] args) { + + int n = 6; + H2O h2O = new H2O(); + for (int i = 0; i < n * 2; i++) { + //H + new Thread() { + @Override + public void run() { + super.run(); + try { + h2O.hydrogen(new Runnable() { + @Override + public void run() { + System.out.println("H"); + } + }); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }.start(); + } + for (int i = 0; i < n; i++) { + //O + new Thread() { + @Override + public void run() { + super.run(); + try { + h2O.oxygen(new Runnable() { + @Override + public void run() { + System.out.println("O"); + } + }); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }.start(); + } + } + + //使用系统类进行优化 + class H2O2 { + + private Semaphore hs; + private Semaphore os; + private CyclicBarrier totalBarrier; + + public H2O2() { + hs = new Semaphore(2); + os = new Semaphore(1); + //await用于标识等待所有的线程都达到barrier才继续执行 + totalBarrier = new CyclicBarrier(3); + } + + public void hydrogen(Runnable releaseHydrogen) throws InterruptedException { + hs.acquire(); + // releaseHydrogen.run() outputs "H". Do not change or remove this line. + releaseHydrogen.run(); + try { + totalBarrier.await(); + } catch (BrokenBarrierException e) { + e.printStackTrace(); + } + hs.release(); + } + + public void oxygen(Runnable releaseOxygen) throws InterruptedException { + os.acquire(); + // releaseOxygen.run() outputs "O". Do not change or remove this line. + releaseOxygen.run(); + try { + totalBarrier.await(); + } catch (BrokenBarrierException e) { + e.printStackTrace(); + } + os.release(); + } + } + + + static class H2O { + + private final Object lock = new Object(); + private int hc = 2; + private int oc = 1; + + public H2O() { + + } + + public void hydrogen(Runnable releaseHydrogen) throws InterruptedException { + + boolean flag = false; + synchronized (lock) { + while (hc == 0) { + lock.wait(); + synchronized (lock) { + if (hc > 0) { + hc--; + flag = true; + break; + } + } + } + if (!flag) hc--; + // releaseHydrogen.run() outputs "H". Do not change or remove this line. + releaseHydrogen.run(); + reset(); + } + } + + public void oxygen(Runnable releaseOxygen) throws InterruptedException { + boolean flag = false; + synchronized (lock) { + while (oc == 0) { + lock.wait(); + synchronized (lock) { + if (oc > 0) { + oc--; + flag = true; + break; + } + } + } + if (!flag) oc--; + // releaseOxygen.run() outputs "O". Do not change or remove this line. + releaseOxygen.run(); + reset(); + } + } + + private void reset() { + if (hc == 0 && oc == 0) { + hc = 2; + oc = 1; + lock.notifyAll(); + } + } + } +} diff --git a/src/pp/arithmetic/leetcode/_1195_FizzBuzz.java b/src/pp/arithmetic/leetcode/_1195_FizzBuzz.java new file mode 100644 index 0000000..1c000d7 --- /dev/null +++ b/src/pp/arithmetic/leetcode/_1195_FizzBuzz.java @@ -0,0 +1,187 @@ +package pp.arithmetic.leetcode; + +import java.util.concurrent.Semaphore; +import java.util.function.IntConsumer; + +/** + * Created by wangpeng on 2020-07-16. + * 1195. 交替打印字符串 + *
+ * 编写一个可以从 1 到 n 输出代表这个数字的字符串的程序,但是: + *
+ * 如果这个数字可以被 3 整除,输出 "fizz"。 + * 如果这个数字可以被 5 整除,输出 "buzz"。 + * 如果这个数字可以同时被 3 和 5 整除,输出 "fizzbuzz"。 + * 例如,当 n = 15,输出: 1, 2, fizz, 4, buzz, fizz, 7, 8, fizz, buzz, 11, fizz, 13, 14, fizzbuzz。 + *
+ * 假设有这么一个类: + *
+ * class FizzBuzz { + * public FizzBuzz(int n) { ... } // constructor + * public void fizz(printFizz) { ... } // only output "fizz" + * public void buzz(printBuzz) { ... } // only output "buzz" + * public void fizzbuzz(printFizzBuzz) { ... } // only output "fizzbuzz" + * public void number(printNumber) { ... } // only output the numbers + * } + * 请你实现一个有四个线程的多线程版 FizzBuzz, 同一个 FizzBuzz 实例会被如下四个线程使用: + *
+ * 线程A将调用 fizz() 来判断是否能被 3 整除,如果可以,则输出 fizz。 + * 线程B将调用 buzz() 来判断是否能被 5 整除,如果可以,则输出 buzz。 + * 线程C将调用 fizzbuzz() 来判断是否同时能被 3 和 5 整除,如果可以,则输出 fizzbuzz。 + * 线程D将调用 number() 来实现输出既不能被 3 整除也不能被 5 整除的数字。 + *
+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/fizz-buzz-multithreaded + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +public class _1195_FizzBuzz { + + public static void main(String[] args) { + FizzBuzz fizzBuzz = new FizzBuzz(16); + new Thread() { + @Override + public void run() { + super.run(); + try { + fizzBuzz.fizz(new Runnable() { + @Override + public void run() { + System.out.println("fizz"); + } + }); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }.start(); + new Thread() { + @Override + public void run() { + super.run(); + try { + fizzBuzz.buzz(new Runnable() { + @Override + public void run() { + System.out.println("buzz"); + } + }); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }.start(); + new Thread() { + @Override + public void run() { + super.run(); + try { + fizzBuzz.fizzbuzz(new Runnable() { + @Override + public void run() { + System.out.println("fizzbuzz"); + } + }); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }.start(); + new Thread() { + @Override + public void run() { + super.run(); + try { + fizzBuzz.number(new IntConsumer() { + @Override + public void accept(int value) { + System.out.println(value); + } + }); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }.start(); + } + + /** + * 解题思路:对于多线程的问题,无非是加锁、等待、解锁、通知, + */ + static class FizzBuzz { + private int n; + private int pn = 1; + private Semaphore fs = new Semaphore(0); + private Semaphore bs = new Semaphore(0); + private Semaphore fbs = new Semaphore(0); + private Semaphore ns = new Semaphore(1); + + public FizzBuzz(int n) { + this.n = n; + } + + // printFizz.run() outputs "fizz". + public void fizz(Runnable printFizz) throws InterruptedException { + while (pn <= n) { + fs.acquire(); + if (pn > n) break; + printFizz.run(); + pn++; + notifyPrint(); + } + } + + // printBuzz.run() outputs "buzz". + public void buzz(Runnable printBuzz) throws InterruptedException { + while (pn <= n) { + bs.acquire(); + if (pn > n) break; + printBuzz.run(); + pn++; + notifyPrint(); + } + } + + // printFizzBuzz.run() outputs "fizzbuzz". + public void fizzbuzz(Runnable printFizzBuzz) throws InterruptedException { + while (pn <= n) { + fbs.acquire(); + if (pn > n) break; + printFizzBuzz.run(); + pn++; + notifyPrint(); + } + } + + // printNumber.accept(x) outputs "x", where x is an integer. + public void number(IntConsumer printNumber) throws InterruptedException { + while (pn <= n) { + ns.acquire(); + if (pn > n) break; + printNumber.accept(pn); + pn++; + notifyPrint(); + } + } + + private void notifyPrint() { + if (pn > n) { + fs.release(); + bs.release(); + fbs.release(); + ns.release(); + return; + } + boolean m3 = pn % 3 == 0; + boolean m5 = pn % 5 == 0; + if (m3 && m5) { + fbs.release(); + } else if (m3) { + fs.release(); + } else if (m5) { + bs.release(); + } else { + ns.release(); + } + } + } +} diff --git a/src/pp/arithmetic/leetcode/_1226_DiningPhilosophers.java b/src/pp/arithmetic/leetcode/_1226_DiningPhilosophers.java new file mode 100644 index 0000000..291cf68 --- /dev/null +++ b/src/pp/arithmetic/leetcode/_1226_DiningPhilosophers.java @@ -0,0 +1,155 @@ +package pp.arithmetic.leetcode; + +import java.util.concurrent.Semaphore; + +/** + * Created by wangpeng on 2020-07-17. + * 1226. 哲学家进餐 + * + * 5 个沉默寡言的哲学家围坐在圆桌前,每人面前一盘意面。叉子放在哲学家之间的桌面上。(5 个哲学家,5 根叉子) + * + * 所有的哲学家都只会在思考和进餐两种行为间交替。哲学家只有同时拿到左边和右边的叉子才能吃到面,而同一根叉子在同一时间只能被一个哲学家使用。每个哲学家吃完面后都需要把叉子放回桌面以供其他哲学家吃面。只要条件允许,哲学家可以拿起左边或者右边的叉子,但在没有同时拿到左右叉子时不能进食。 + * + * 假设面的数量没有限制,哲学家也能随便吃,不需要考虑吃不吃得下。 + * + * 设计一个进餐规则(并行算法)使得每个哲学家都不会挨饿;也就是说,在没有人知道别人什么时候想吃东西或思考的情况下,每个哲学家都可以在吃饭和思考之间一直交替下去。 + * + * + * 问题描述和图片来自维基百科 wikipedia.org + * 图片地址:https://assets.leetcode-cn.com/aliyun-lc-upload/uploads/2019/10/23/an_illustration_of_the_dining_philosophers_problem.png + * + * + * 哲学家从 0 到 4 按 顺时针 编号。请实现函数 void wantsToEat(philosopher, pickLeftFork, pickRightFork, eat, putLeftFork, putRightFork): + * + * philosopher 哲学家的编号。 + * pickLeftFork 和 pickRightFork 表示拿起左边或右边的叉子。 + * eat 表示吃面。 + * putLeftFork 和 putRightFork 表示放下左边或右边的叉子。 + * 由于哲学家不是在吃面就是在想着啥时候吃面,所以思考这个方法没有对应的回调。 + * 给你 5 个线程,每个都代表一个哲学家,请你使用类的同一个对象来模拟这个过程。在最后一次调用结束之前,可能会为同一个哲学家多次调用该函数。 + * + * + * + * 示例: + * + * 输入:n = 1 + * 输出:[[4,2,1],[4,1,1],[0,1,1],[2,2,1],[2,1,1],[2,0,3],[2,1,2],[2,2,2],[4,0,3],[4,1,2],[0,2,1],[4,2,2],[3,2,1],[3,1,1],[0,0,3],[0,1,2],[0,2,2],[1,2,1],[1,1,1],[3,0,3],[3,1,2],[3,2,2],[1,0,3],[1,1,2],[1,2,2]] + * 解释: + * n 表示每个哲学家需要进餐的次数。 + * 输出数组描述了叉子的控制和进餐的调用,它的格式如下: + * output[i] = [a, b, c] (3个整数) + * - a 哲学家编号。 + * - b 指定叉子:{1 : 左边, 2 : 右边}. + * - c 指定行为:{1 : 拿起, 2 : 放下, 3 : 吃面}。 + * 如 [4,2,1] 表示 4 号哲学家拿起了右边的叉子。 + * + * + * 提示: + * + * 1 <= n <= 60 + * + * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/the-dining-philosophers + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +public class _1226_DiningPhilosophers { + + public static void main(String[] args) { + DiningPhilosophers diningPhilosophers = new DiningPhilosophers(); + for (int i = 0; i < 5; i++) { + int finalI = i; + new Thread(){ + @Override + public void run() { + super.run(); + try { + diningPhilosophers.wantsToEat(finalI, new Runnable() { + @Override + public void run() { + System.out.println("["+finalI+",1,1]"); + } + }, new Runnable() { + @Override + public void run() { + System.out.println("["+finalI+",2,1]"); + } + }, new Runnable() { + @Override + public void run() { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + System.out.println("["+finalI+",0,3]"); + } + }, new Runnable() { + @Override + public void run() { + System.out.println("["+finalI+",1,2]"); + } + }, new Runnable() { + @Override + public void run() { + System.out.println("["+finalI+",2,2]"); + } + }); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }.start(); + } + } + + /** + * 解题思路: + * 资源:5个叉子(5个信号量),同时拿到两个叉子(1个互斥信号量,防止死锁),此题解并不是最优解,最优解应该能满足多个同时进餐 + */ + static class DiningPhilosophers { + //一个互斥信号量用于临界资源的互斥访问 + private Semaphore mutex; + //5个同步信号量用于哲学家之间的同步访问 + private Semaphore[] sema; + public DiningPhilosophers() { + mutex = new Semaphore(1); + sema = new Semaphore[] { + new Semaphore(1), + new Semaphore(1), + new Semaphore(1), + new Semaphore(1), + new Semaphore(1) + }; + } + + // call the run() method of any runnable to execute its code + public void wantsToEat(int philosopher, + Runnable pickLeftFork, + Runnable pickRightFork, + Runnable eat, + Runnable putLeftFork, + Runnable putRightFork) throws InterruptedException { + //一个哲学家如果要拿起叉子就同时拿两个,因此这里是一个原子操作,需要用mutex信号量包起来,表示互斥 + mutex.acquire(); + //尝试获取左手边的叉子 + sema[philosopher].acquire(); + //尝试获取右手边的叉子 + sema[(philosopher+1) % 5].acquire(); + + pickLeftFork.run(); + pickRightFork.run(); + //我认为这句话应该放在这里。 + // mutex.release(); + + //拿到叉子开始吃饭 + eat.run(); + + //吃完饭放下叉子 + putLeftFork.run(); + sema[philosopher].release(); + putRightFork.run(); + sema[(philosopher+1) % 5].release(); + mutex.release(); + } + } +} diff --git a/src/pp/arithmetic/offer/_03_findRepeatNumber.java b/src/pp/arithmetic/offer/_03_findRepeatNumber.java new file mode 100644 index 0000000..8fbedfa --- /dev/null +++ b/src/pp/arithmetic/offer/_03_findRepeatNumber.java @@ -0,0 +1,124 @@ +package pp.arithmetic.offer; + +import java.util.HashMap; + +/** + * Created by wangpeng on 2020-07-28. + * 剑指 Offer 03. 数组中重复的数字 + *
+ * 找出数组中重复的数字。 + *
+ *
+ * 在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。 + *
+ * 示例 1: + *
+ * 输入: + * [2, 3, 1, 0, 2, 5, 3] + * 输出:2 或 3 + * + *
+ * 限制: + *
+ * 2 <= n <= 100000 + *
+ * 来源:力扣(LeetCode) + * 链接:https://leetcode-cn.com/problems/shu-zu-zhong-zhong-fu-de-shu-zi-lcof + * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +public class _03_findRepeatNumber { + + public static void main(String[] args) { + _03_findRepeatNumber findRepeatNumber = new _03_findRepeatNumber(); + int repeatNumber = findRepeatNumber.findRepeatNumber(new int[]{2, 3, 0, 1, 2, 3, 4, 5, 3}); + System.out.println(repeatNumber); + int repeatNumber2 = findRepeatNumber.findRepeatNumber2(new int[]{2, 3, 0, 1, 2, 3, 4, 5, 3}); + System.out.println(repeatNumber2); + int repeatNumber3 = findRepeatNumber.findRepeatNumber3(new int[]{2, 3, 0, 1, 2, 3, 4, 5, 3}); + System.out.println(repeatNumber3); + } + + /** + * 解题思路: + * 方案一:最直接的方式,使用一个HashMap进行存储遍历过的数字,性能肯定不会差,内存占用会高 + *
+ * 执行用时:13 ms, 在所有 Java 提交中击败了8.98%的用户 + * 内存消耗:48.7 MB, 在所有 Java 提交中击败了100.00%的用户 + *
+ * 执行结果有点出人意料,用时偏高 + *
+ * 优化方案二:{@link _03_findRepeatNumber#findRepeatNumber2(int[])}
+ *
+ * @param nums
+ * @return
+ */
+ public int findRepeatNumber(int[] nums) {
+ HashMap
+ * 剑指 Offer 12. 矩阵中的路径
+ *
+ * 请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一格开始,每一步可以在矩阵中向左、右、上、下移动一格。
+ * 如果一条路径经过了矩阵的某一格,那么该路径不能再次进入该格子。例如,在下面的3×4的矩阵中包含一条字符串“bfce”的路径(路径中的字母用加粗标出)。
+ *
+ * [["a","b","c","e"],
+ * ["s","f","c","s"],
+ * ["a","d","e","e"]]
+ *
+ * 但矩阵中不包含字符串“abfb”的路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入这个格子。
+ *
+ *
+ *
+ * 示例 1:
+ *
+ * 输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
+ * 输出:true
+ * 示例 2:
+ *
+ * 输入:board = [["a","b"],["c","d"]], word = "abcd"
+ * 输出:false
+ * 提示:
+ *
+ * 1 <= board.length <= 200
+ * 1 <= board[i].length <= 200
+ *
+ * 来源:力扣(LeetCode)
+ * 链接:https://leetcode-cn.com/problems/ju-zhen-zhong-de-lu-jing-lcof
+ * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
+ */
+public class _12_exist {
+
+ public static void main(String[] args) {
+ _12_exist exist = new _12_exist();
+// char[][] board = new char[][]{
+// {'A', 'B', 'C', 'E'},
+// {'S', 'F', 'C', 'S'},
+// {'A', 'D', 'E', 'E'}
+// };
+// System.out.println(exist.exist(board,"ABFACED"));
+// System.out.println(exist.exist(new char[][]{
+// {'a','b'},
+// {'c','d'}
+// },"abcd"));
+ System.out.println(exist.exist(new char[][]{
+ {'C','A','A'},
+ {'A','A','A'},
+ {'B','C','D'}
+ },"AAB"));
+ }
+
+ /**
+ * 解题思路:
+ * 从board的[0,0]开始向上、左、下、右进行深度遍历,逐步去匹配word中的字符,新建个history保存遍历路径,防止死循环
+ *
+ * @param board
+ * @param word
+ * @return
+ */
+ public boolean exist(char[][] board, String word) {
+ if (board == null || board.length == 0) return false;
+ int[][] history = new int[board.length][board[0].length];
+ return dfs(board, word, history, 0, 0, 0);
+ }
+
+ private boolean dfs(char[][] board, String word, int[][] history, int wi, int nx, int ny) {
+ if (wi >= word.length()) {
+ //word遍历结束才返回true
+ return true;
+ }
+ //遍历越界
+ if (nx < 0 || nx >= board.length || ny < 0 || ny >= board[nx].length) return false;
+ //之前走过这个位置
+ if (history[nx][ny] == 1) return false;
+ if (board[nx][ny] == word.charAt(wi)) {
+ history[nx][ny] = 1;
+ if (dfs(board, word, history, wi + 1, nx, ny + 1)
+ || dfs(board, word, history, wi + 1, nx + 1, ny)
+ || dfs(board, word, history, wi + 1, nx, ny - 1)
+ || dfs(board, word, history, wi + 1, nx - 1, ny)) {
+ return true;
+ }
+ history[nx][ny] = 0;
+ }
+ if (wi == 0) {
+ //定位首个字符的标识位
+ if (nx < board.length - 1) {
+ if (dfs(board, word, history, wi, nx + 1, ny)) {
+ return true;
+ }
+ } else if (ny < board[0].length - 1) {
+ if (dfs(board, word, history, wi, 0, ny + 1)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+}
diff --git a/src/pp/arithmetic/offer/_13_movingCount.java b/src/pp/arithmetic/offer/_13_movingCount.java
new file mode 100644
index 0000000..e89db86
--- /dev/null
+++ b/src/pp/arithmetic/offer/_13_movingCount.java
@@ -0,0 +1,83 @@
+package pp.arithmetic.offer;
+
+/**
+ * Created by wangpeng on 2020-08-06.
+ *
+ * 剑指 Offer 13. 机器人的运动范围
+ *
+ * 地上有一个m行n列的方格,从坐标 [0,0] 到坐标 [m-1,n-1] 。一个机器人从坐标 [0, 0] 的格子开始移动,它每次可以向左、右、上、下移动一格(不能移动到方格外),
+ * 也不能进入行坐标和列坐标的数位之和大于k的格子。例如,当k为18时,机器人能够进入方格 [35, 37] ,因为3+5+3+7=18。但它不能进入方格 [35, 38],因为3+5+3+8=19。请问该机器人能够到达多少个格子?
+ *
+ *
+ *
+ * 示例 1:
+ *
+ * 输入:m = 2, n = 3, k = 1
+ * 输出:3
+ * 示例 2:
+ *
+ * 输入:m = 3, n = 1, k = 0
+ * 输出:1
+ * 提示:
+ *
+ * 1 <= n,m <= 100
+ * 0 <= k <= 20
+ *
+ * 来源:力扣(LeetCode)
+ * 链接:https://leetcode-cn.com/problems/ji-qi-ren-de-yun-dong-fan-wei-lcof
+ * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
+ */
+public class _13_movingCount {
+
+ public static void main(String[] args) {
+ _13_movingCount movingCount = new _13_movingCount();
+ System.out.println(movingCount.movingCount(2,3,1));
+ System.out.println(movingCount.movingCount(3,1,0));
+ System.out.println(movingCount.movingCount(1,2,1));
+ System.out.println(movingCount.movingCount(10,10,2));
+ System.out.println(movingCount.movingCount(16,8,4));
+ }
+
+ private int retVal = 0;
+
+ /**
+ * 解题思路:
+ * 使用int[m][n]大小的数组保存行进记录,上下左右进行DFS,不满足条件的跳过
+ * 需要注意的可能中间某些行和列相加也满足条件,所以需要整个行和列都需要遍历完(不需要考虑,题中是从0,0开始的)
+ *
+ * @param m
+ * @param n
+ * @param k
+ * @return
+ */
+ public int movingCount(int m, int n, int k) {
+ if (k < 0 || m <=0 || n<=0) return 0;
+ retVal = 0;
+ int[][] history = new int[m][n];
+ dfs(0,0,m,n,k,history);
+ return retVal;
+ }
+
+ private void dfs(int cx, int cy, int m, int n, int k, int[][] history) {
+ if (cx < 0 || cx >= m || cy < 0 || cy >= n) return;
+ if (add(cx, cy) > k) return;
+ if (history[cx][cy] == 1) return;
+ history[cx][cy] = 1;
+ retVal++;
+ dfs(cx, cy + 1, m, n, k, history);
+ dfs(cx + 1, cy, m, n, k, history);
+ dfs(cx, cy - 1, m, n, k, history);
+ dfs(cx - 1, cy, m, n, k, history);
+ }
+
+ private int add(int x, int y) {
+ int retVal = 0;
+ retVal += x / 100;
+ retVal += (x - x / 100 * 100) / 10;
+ retVal += x - x / 100 * 100 - (x - x / 100 * 100) / 10 * 10;
+ retVal += y / 100;
+ retVal += (y - y / 100 * 100) / 10;
+ retVal += y - y / 100 * 100 - (y - y / 100 * 100) / 10 * 10;
+ return retVal;
+ }
+}
diff --git a/src/pp/arithmetic/offer/_14_1_cuttingRope.java b/src/pp/arithmetic/offer/_14_1_cuttingRope.java
new file mode 100644
index 0000000..939abcc
--- /dev/null
+++ b/src/pp/arithmetic/offer/_14_1_cuttingRope.java
@@ -0,0 +1,68 @@
+package pp.arithmetic.offer;
+
+/**
+ * Created by wangpeng on 2020-08-07.
+ *
+ * 剑指 Offer 14- I. 剪绳子
+ *
+ * 给你一根长度为 n 的绳子,请把绳子剪成整数长度的 m 段(m、n都是整数,n>1并且m>1),每段绳子的长度记为 k[0],k[1]...k[m-1] 。请问 k[0]*k[1]*...*k[m-1] 可能的最大乘积是多少?例如,当绳子的长度是8时,我们把它剪成长度分别为2、3、3的三段,此时得到的最大乘积是18。
+ *
+ * 示例 1:
+ *
+ * 输入: 2
+ * 输出: 1
+ * 解释: 2 = 1 + 1, 1 × 1 = 1
+ * 示例 2:
+ *
+ * 输入: 10
+ * 输出: 36
+ * 解释: 10 = 3 + 3 + 4, 3 × 3 × 4 = 36
+ * 提示:
+ *
+ * 2 <= n <= 58
+ *
+ * 来源:力扣(LeetCode)
+ * 链接:https://leetcode-cn.com/problems/jian-sheng-zi-lcof
+ * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
+ */
+public class _14_1_cuttingRope {
+
+ public static void main(String[] args) {
+ _14_1_cuttingRope cuttingRope = new _14_1_cuttingRope();
+ System.out.println(cuttingRope.cuttingRope(8));
+ System.out.println(cuttingRope.cuttingRope(10));
+ System.out.println(cuttingRope.cuttingRope(14));
+ System.out.println(cuttingRope.cuttingRope(58));
+ }
+
+ /**
+ * 解题思路:
+ * 手动模拟了从2-10的最大乘积数字拆解,发现了一个现象:
+ * 对于数字n,n一直除以2到1为止,得到的数字就是最大的乘积,举例如下:
+ * 数字n 2 3 4 5 6 7 8 9 10
+ * 乘积 1,1 1,2 2,2 2,3 3,3 3,4(2,2) 4(2,2),4(2,2) 4,5(2,3) 5(2,3),5(2,3)
+ * 发现到了后面的最大乘积可以利用之前的计算好的结果,从而得出动态规划转移方程
+ * dp[i]=dp[i/2]*dp[i-i/2](i>3)
+ * 上面有问题,例如8的最大值不是除以2得到4*4=16,而是3*2*3=18,所以得双重循环取所有情况的最大值
+ * for (int j = 1; j <= i / 2; j++) {
+ * dp[i] = Math.max(dp[i], dp[j] * dp[i - j]);
+ * }
+ *
+ * @param n
+ * @return
+ */
+ public int cuttingRope(int n) {
+ if (n <= 3) return n - 1;
+ int[] dp = new int[n + 1];
+ //初始化,1,2,3特殊处理
+ dp[1] = 1;
+ dp[2] = 2;
+ dp[3] = 3;
+ for (int i = 4; i <= n; i++) {
+ for (int j = 1; j <= i / 2; j++) {
+ dp[i] = Math.max(dp[i], dp[j] * dp[i - j]);
+ }
+ }
+ return dp[n];
+ }
+}
diff --git a/src/pp/arithmetic/offer/_15_hammingWeight.java b/src/pp/arithmetic/offer/_15_hammingWeight.java
new file mode 100644
index 0000000..bfae0b4
--- /dev/null
+++ b/src/pp/arithmetic/offer/_15_hammingWeight.java
@@ -0,0 +1,57 @@
+package pp.arithmetic.offer;
+
+/**
+ * Created by wangpeng on 2020-08-10.
+ * 剑指 Offer 15. 二进制中1的个数
+ *
+ * 请实现一个函数,输入一个整数,输出该数二进制表示中 1 的个数。例如,把 9 表示成二进制是 1001,有 2 位是 1。因此,如果输入 9,则该函数输出 2。
+ *
+ * 示例 1:
+ *
+ * 输入:00000000000000000000000000001011
+ * 输出:3
+ * 解释:输入的二进制串 00000000000000000000000000001011 中,共有三位为 '1'。
+ * 示例 2:
+ *
+ * 输入:00000000000000000000000010000000
+ * 输出:1
+ * 解释:输入的二进制串 00000000000000000000000010000000 中,共有一位为 '1'。
+ * 示例 3:
+ *
+ * 输入:11111111111111111111111111111101
+ * 输出:31
+ * 解释:输入的二进制串 11111111111111111111111111111101 中,共有 31 位为 '1'。
+ *
+ * 来源:力扣(LeetCode)
+ * 链接:https://leetcode-cn.com/problems/er-jin-zhi-zhong-1de-ge-shu-lcof
+ * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
+ */
+public class _15_hammingWeight {
+
+ public static void main(String[] args) {
+ _15_hammingWeight hammingWeight = new _15_hammingWeight();
+ System.out.println(hammingWeight.hammingWeight(11));
+ System.out.println(hammingWeight.hammingWeight(128));
+// System.out.println(hammingWeight.hammingWeight(4294967293));
+ }
+
+
+ /**
+ * 解题思路:
+ * 如果n%2!=0,则1的个数+1,直到n=1
+ *
+ * 注意无符号的,对应int会超 右移动使用>>>(无符号右移)
+ * @param n
+ * @return
+ */
+ // you need to treat n as an unsigned value
+ public int hammingWeight(int n) {
+ int retVal = 0;
+ while (n != 0) {
+ retVal += n & 1;
+ n = n >>> 1;
+ }
+
+ return retVal;
+ }
+}
diff --git a/src/pp/arithmetic/offer/_16_myPow.java b/src/pp/arithmetic/offer/_16_myPow.java
new file mode 100644
index 0000000..0a49745
--- /dev/null
+++ b/src/pp/arithmetic/offer/_16_myPow.java
@@ -0,0 +1,71 @@
+package pp.arithmetic.offer;
+
+/**
+ * Created by wangpeng on 2020-08-11.
+ *
+ * 剑指 Offer 16. 数值的整数次方
+ *
+ * 实现函数double Power(double base, int exponent),求base的exponent次方。不得使用库函数,同时不需要考虑大数问题。
+ *
+ *
+ *
+ * 示例 1:
+ *
+ * 输入: 2.00000, 10
+ * 输出: 1024.00000
+ * 示例 2:
+ *
+ * 输入: 2.10000, 3
+ * 输出: 9.26100
+ * 示例 3:
+ *
+ * 输入: 2.00000, -2
+ * 输出: 0.25000
+ * 解释: 2-2 = 1/22 = 1/4 = 0.25
+ *
+ *
+ * 说明:
+ *
+ * -100.0 < x < 100.0
+ * n 是 32 位有符号整数,其数值范围是 [−2^31, 2^31 − 1] 。
+ *
+ * 来源:力扣(LeetCode)
+ * 链接:https://leetcode-cn.com/problems/shu-zhi-de-zheng-shu-ci-fang-lcof
+ * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
+ */
+public class _16_myPow {
+
+ public static void main(String[] args) {
+ _16_myPow myPow = new _16_myPow();
+ System.out.println(myPow.myPow(2.0,10));
+ System.out.println(myPow.myPow(2.1,3));
+ System.out.println(myPow.myPow(2.0,-2));
+ System.out.println(myPow.myPow(0.00001, 2147483647));
+ System.out.println(myPow.myPow(2, -2147483648));
+ }
+
+ /**
+ * 解题思路:
+ * 最简单的方式就是直接循环0-n,将x相乘得出结果,题目中的n范围比较大,这样子效率太低
+ * 优化:类似2分拆分,一半一半的计算结果,最终相乘
+ * @param x
+ * @param n
+ * @return
+ */
+ public double myPow(double x, int n) {
+ if (n == 0) return 1;
+ if (n<0){
+ //指数是否负数,负数需要取倒数
+ x = 1/x;
+ }
+ //是否取一半还有剩余一个
+ boolean isOdd = n % 2 != 0;
+ double v = myPow(x, Math.abs(n / 2));
+ if (isOdd) {
+ return v * v * x;
+ } else {
+ return v * v;
+ }
+ }
+
+}
diff --git a/src/pp/arithmetic/offer/_17_printNumbers.java b/src/pp/arithmetic/offer/_17_printNumbers.java
new file mode 100644
index 0000000..6eb52a1
--- /dev/null
+++ b/src/pp/arithmetic/offer/_17_printNumbers.java
@@ -0,0 +1,56 @@
+package pp.arithmetic.offer;
+
+import pp.arithmetic.Util;
+
+/**
+ * Created by wangpeng on 2020-08-11.
+ *
+ * 剑指 Offer 17. 打印从1到最大的n位数
+ *
+ * 输入数字 n,按顺序打印出从 1 到最大的 n 位十进制数。比如输入 3,则打印出 1、2、3 一直到最大的 3 位数 999。
+ *
+ * 示例 1:
+ *
+ * 输入: n = 1
+ * 输出: [1,2,3,4,5,6,7,8,9]
+ *
+ *
+ * 说明:
+ *
+ * 用返回一个整数列表来代替打印
+ * n 为正整数
+ *
+ * 来源:力扣(LeetCode)
+ * 链接:https://leetcode-cn.com/problems/da-yin-cong-1dao-zui-da-de-nwei-shu-lcof
+ * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
+ */
+public class _17_printNumbers {
+
+ public static void main(String[] args) {
+ _17_printNumbers printNumbers = new _17_printNumbers();
+ Util.printArray(printNumbers.printNumbers(1));
+ Util.printArray(printNumbers.printNumbers(2));
+ Util.printArray(printNumbers.printNumbers(3));
+ }
+
+ /**
+ * 解题思路:
+ * 本题没有什么难度,唯一难的就是咋根据n构建出相应size的数组
+ *
+ * @param n
+ * @return
+ */
+ public int[] printNumbers(int n) {
+ char[] len = new char[n];
+ for (int i = 0; i < n; i++) {
+ len[i] = '9';
+ }
+ int size = Integer.parseInt(new String(len));
+ int[] retVal = new int[size];
+ for (int i = 0; i < size; i++) {
+ retVal[i] = i+1;
+ }
+
+ return retVal;
+ }
+}
diff --git a/src/pp/arithmetic/offer/_18_deleteNode.java b/src/pp/arithmetic/offer/_18_deleteNode.java
new file mode 100644
index 0000000..0f4dd2e
--- /dev/null
+++ b/src/pp/arithmetic/offer/_18_deleteNode.java
@@ -0,0 +1,74 @@
+package pp.arithmetic.offer;
+
+import pp.arithmetic.Util;
+import pp.arithmetic.model.ListNode;
+
+/**
+ * Created by wangpeng on 2020-09-09.
+ * 剑指 Offer 18. 删除链表的节点
+ *
+ * 给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。
+ *
+ * 返回删除后的链表的头节点。
+ *
+ * 注意:此题对比原题有改动
+ *
+ * 示例 1:
+ *
+ * 输入: head = [4,5,1,9], val = 5
+ * 输出: [4,1,9]
+ * 解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9.
+ * 示例 2:
+ *
+ * 输入: head = [4,5,1,9], val = 1
+ * 输出: [4,5,9]
+ * 解释: 给定你链表中值为 1 的第三个节点,那么在调用了你的函数之后,该链表应变为 4 -> 5 -> 9.
+ *
+ *
+ * 说明:
+ *
+ * 题目保证链表中节点的值互不相同
+ * 若使用 C 或 C++ 语言,你不需要 free 或 delete 被删除的节点
+ *
+ * 来源:力扣(LeetCode)
+ * 链接:https://leetcode-cn.com/problems/shan-chu-lian-biao-de-jie-dian-lcof
+ * 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
+ */
+public class _18_deleteNode {
+
+ public static void main(String[] args) {
+ _18_deleteNode deleteNode = new _18_deleteNode();
+ ListNode head = new ListNode(4);
+ head.next = new ListNode(5);
+ head.next.next = new ListNode(1);
+ head.next.next.next = new ListNode(9);
+ ListNode listNode = deleteNode.deleteNode(head, 4);
+ Util.printListNode(listNode);
+ }
+
+ /**
+ * 解题思路:
+ * 对于链表的问题,最核心的思想就是遍历,使用一个虚拟节点指向头结点,用来缓存返回结果
+ *
+ * @param head
+ * @param val
+ * @return
+ */
+ public ListNode deleteNode(ListNode head, int val) {
+ ListNode dummp = new ListNode(0);
+ dummp.next = head;
+ ListNode pre = dummp;
+ ListNode next = head;
+ while (next != null) {
+ if (next.val == val) {
+ pre.next = next.next;
+ next.next = null;
+ return dummp.next;
+ }
+ pre = next;
+ next = next.next;
+ }
+
+ return null;
+ }
+}