diff --git a/notes/算法.md b/notes/算法.md index 0669b910..4d818bad 100644 --- a/notes/算法.md +++ b/notes/算法.md @@ -83,6 +83,8 @@ public class ThreeSum { 通过将数组先排序,对两个元素求和,并用二分查找方法查找是否存在该和的相反数,如果存在,就说明存在三元组的和为 0。 +应该注意的是,只有数组不含有相同元素才能使用这种解法,否则二分查找的结果会出错。 + 该方法可以将 ThreeSum 算法增长数量级降低为 O(N2logN)。 ```java @@ -94,18 +96,19 @@ public class ThreeSumFast { for (int i = 0; i < N; i++) for (int j = i + 1; j < N; j++) { int target = -nums[i] - nums[j]; - int index = binarySearch(nums, target); + int index = BinarySearch.search(nums, target); // 应该注意这里的下标必须大于 j,否则会重复统计。 - if (index <= j) - continue; - while (index < N && nums[index++] == target) + if (index > j) cnt++; } - return cnt; } +} +``` - private static int binarySearch(int[] nums, int target) { +```java +public class BinarySearch { + public static int search(int[] nums, int target) { int l = 0, h = nums.length - 1; while (l <= h) { int m = l + (h - l) / 2; @@ -142,23 +145,38 @@ public class ThreeSumFast { public class RatioTest { public static void main(String[] args) { int N = 500; - int K = 7; - long preTime = -1; - while (K-- > 0) { + int loopTimes = 7; + double preTime = -1; + while (loopTimes-- > 0) { int[] nums = new int[N]; - long startTime = System.currentTimeMillis(); + StopWatch.start(); int cnt = ThreeSum.count(nums); - long endTime = System.currentTimeMillis(); - long time = endTime - startTime; - double ratio = preTime == -1 ? 0 : (double) time / preTime; - System.out.println(N + " " + time + " " + ratio); - preTime = time; + System.out.println(cnt); + double elapsedTime = StopWatch.elapsedTime(); + double ratio = preTime == -1 ? 0 : elapsedTime / preTime; + System.out.println(N + " " + elapsedTime + " " + ratio); + preTime = elapsedTime; N *= 2; } } } ``` +```java +public class StopWatch { + private static long start; + + public static void start(){ + start = System.currentTimeMillis(); + } + + public static double elapsedTime() { + long now = System.currentTimeMillis(); + return (now - start) / 1000.0; + } +} +``` + ## 注意事项 ### 1. 大常数 @@ -187,25 +205,39 @@ public class RatioTest { First-In-Last-Out +```java +public interface MyStack extends Iterable { + MyStack push(Item item); + + Item pop() throws Exception; + + boolean isEmpty(); + + int size(); +} +``` + ### 1. 数组实现 ```java -import java.util.Iterator; - -public class ResizingArrayStack implements Iterable { +public class ResizingArrayStack implements MyStack { // 栈元素数组 private Item[] a = (Item[]) new Object[1]; // 只能通过转型来创建泛型数组 // 元素数量 private int N = 0; - public void push(Item item) { + @Override + public MyStack push(Item item) { check(); a[N++] = item; + return this; } + @Override public Item pop() throws Exception { if (isEmpty()) throw new Exception("stack is empty"); + Item item = a[--N]; check(); a[N] = null; // 避免对象游离 @@ -229,10 +261,12 @@ public class ResizingArrayStack implements Iterable { a = tmp; } + @Override public boolean isEmpty() { return N == 0; } + @Override public int size() { return N; } @@ -240,57 +274,29 @@ public class ResizingArrayStack implements Iterable { @Override public Iterator iterator() { // 返回逆序遍历的迭代器 - return new ReverseArrayIterator(); - } + return new Iterator() { + private int i = N; - private class ReverseArrayIterator implements Iterator { - private int i = N; + @Override + public boolean hasNext() { + return i > 0; + } - @Override - public boolean hasNext() { - return i > 0; - } - - @Override - public Item next() { - return a[--i]; - } + @Override + public Item next() { + return a[--i]; + } + }; } } ``` -```java -public static void main(String[] args) throws Exception { - ResizingArrayStack stack = new ResizingArrayStack(); - stack.push(1); - stack.push(2); - stack.push(3); - stack.push(4); - System.out.println(stack.isEmpty()); - System.out.println(stack.size()); - System.out.println(stack.pop()); - System.out.println(stack.pop()); - for (Integer item : stack) - System.out.println(item); -} -``` - -```html -false -4 -4 -3 -2 -1 -``` - ### 2. 链表实现 需要使用链表的头插法来实现,因为头插法中最后压入栈的元素在链表的开头,它的 next 指针指向前一个压入栈的元素,在弹出元素使就可以通过 next 指针遍历到前一个压入栈的元素从而让这个元素称为新的栈顶元素。 ```java -public class ListStack { - +public class ListStack implements MyStack { private Node top = null; private int N = 0; @@ -299,22 +305,17 @@ public class ListStack { Node next; } - public boolean isEmpty() { - return N == 0; - } - - public int size() { - return N; - } - - public void push(Item item) { + @Override + public MyStack push(Item item) { Node newTop = new Node(); newTop.item = item; newTop.next = top; top = newTop; N++; + return this; } + @Override public Item pop() throws Exception { if (isEmpty()) throw new Exception("stack is empty"); @@ -323,28 +324,36 @@ public class ListStack { N--; return item; } -} -``` -```java -public static void main(String[] args) throws Exception { - ListStack stack = new ListStack(); - stack.push(1); - stack.push(2); - stack.push(3); - stack.push(4); - System.out.println(stack.isEmpty()); - System.out.println(stack.size()); - System.out.println(stack.pop()); - System.out.println(stack.pop()); -} -``` + @Override + public boolean isEmpty() { + return N == 0; + } -```html -false -4 -4 -3 + @Override + public int size() { + return N; + } + + @Override + public Iterator iterator() { + return new Iterator() { + private Node cur = top; + + @Override + public boolean hasNext() { + return cur != null; + } + + @Override + public Item next() { + Item item = cur.item; + cur = cur.next; + return item; + } + }; + } +} ``` ## 队列 @@ -355,6 +364,88 @@ First-In-First-Out 这里需要考虑 first 和 last 指针哪个作为链表的开头。因为出队列操作需要让队首元素的下一个元素成为队首,所以需要容易获取下一个元素,而链表的头部节点的 next 指针指向下一个元素,因此可以让 first 指针链表的开头。 +```java +public interface MyQueue extends Iterable { + int size(); + + boolean isEmpty(); + + MyQueue add(Item item); + + Item remove() throws Exception; +} +``` + +```java +public class ListQueue implements MyQueue { + private Node first; + private Node last; + int N = 0; + + private class Node { + Item item; + Node next; + } + + @Override + public boolean isEmpty() { + return N == 0; + } + + @Override + public int size() { + return N; + } + + @Override + public MyQueue add(Item item) { + Node newNode = new Node(); + newNode.item = item; + newNode.next = null; + if (isEmpty()) { + last = newNode; + first = newNode; + } else { + last.next = newNode; + last = newNode; + } + N++; + return this; + } + + @Override + public Item remove() throws Exception { + if (isEmpty()) + throw new Exception("queue is empty"); + Node node = first; + first = first.next; + N--; + if (isEmpty()) + last = null; + return node.item; + } + + @Override + public Iterator iterator() { + return new Iterator() { + Node cur = first; + + @Override + public boolean hasNext() { + return cur != null; + } + + @Override + public Item next() { + Item item = cur.item; + cur = cur.next; + return item; + } + }; + } +} +``` + ```java public class Queue { private Node first; @@ -401,25 +492,6 @@ public class Queue { } ``` -```java -public static void main(String[] args) throws Exception { - Queue queue = new Queue(); - queue.add(1); - queue.add(2); - System.out.println(queue.remove()); - System.out.println(queue.remove()); - queue.add(3); - queue.add(4); - System.out.println(queue.size()); -} -``` - -```html -1 -2 -2 -``` - # 三、排序 待排序的元素需要实现 Java 的 Comparable 接口,该接口有 compareTo() 方法,可以用它来判断两个元素的大小关系。