Repository: bighuang624/Algorithms-notes Branch: master Commit: e598780a9ecf Files: 169 Total size: 543.9 KB Directory structure: gitextract_vx5c0m3w/ ├── .gitignore ├── README.md ├── code/ │ ├── README.md │ ├── algs4/ │ │ ├── Alphabet.java │ │ ├── Bag.java │ │ ├── BinarySearch.java │ │ ├── BinaryStdIn.java │ │ ├── BinaryStdOut.java │ │ ├── Counter.java │ │ ├── DepthFirstOrder.java │ │ ├── Draw.java │ │ ├── DrawListener.java │ │ ├── Heap.java │ │ ├── HexDump.java │ │ ├── In.java │ │ ├── IndexMinPQ.java │ │ ├── Interval1D.java │ │ ├── Interval2D.java │ │ ├── MinPQ.java │ │ ├── Out.java │ │ ├── Particle.java │ │ ├── Picture.java │ │ ├── PictureDump.java │ │ ├── Point2D.java │ │ ├── Queue.java │ │ ├── SET.java │ │ ├── ST.java │ │ ├── Stack.java │ │ ├── StdDraw.java │ │ ├── StdIn.java │ │ ├── StdOut.java │ │ └── StdRandom.java │ ├── chapter1_1_Programming_Model/ │ │ ├── BinarySearch.java │ │ ├── BouncingBall.java │ │ ├── Ex1.java │ │ ├── Ex10.java │ │ ├── Ex11.java │ │ ├── Ex13.java │ │ ├── Ex14.java │ │ ├── Ex15.java │ │ ├── Ex19.java │ │ ├── Ex2.java │ │ ├── Ex20.java │ │ ├── Ex3.java │ │ ├── Ex30.java │ │ ├── Ex31.java │ │ ├── Ex32.java │ │ ├── Ex6.java │ │ ├── Ex7a.java │ │ ├── Ex7b.java │ │ ├── Ex7c.java │ │ ├── Ex8.java │ │ ├── Ex9.java │ │ ├── RightTriangle.java │ │ ├── Sattolo.java │ │ └── StdDrawTest.java │ ├── chapter1_2_Data_Abstraction/ │ │ ├── Accumulator.java │ │ ├── AccumulatorTest.java │ │ ├── Cat.java │ │ ├── Date.java │ │ ├── Flips.java │ │ ├── FlipsMax.java │ │ ├── Interval2DTest.java │ │ ├── Rolls.java │ │ ├── StaticSETofInts.java │ │ ├── VisualAccumulator.java │ │ ├── VisualAccumulatorTest.java │ │ ├── WhiteList.java │ │ ├── in1.txt │ │ └── in2.txt │ ├── chapter1_3_Bags_Queues_Stacks/ │ │ └── ResizingArrayStack.java │ ├── chapter1_4_Analysis_of_Algorithms/ │ │ ├── DoublingRatio.java │ │ ├── DoublingTest.java │ │ ├── Stopwatch.java │ │ ├── StopwatchTest.java │ │ └── ThreeSum.java │ ├── chapter1_5_Case_Study_Union_Find/ │ │ ├── UF.java │ │ └── WeightedQuickUnionUF.java │ ├── chapter2_1_Elementary_Sorts/ │ │ ├── Insertion.java │ │ ├── Selection.java │ │ ├── Shell.java │ │ └── SortCompare.java │ ├── chapter2_2_Mergesort/ │ │ ├── Merge.java │ │ └── MergeBU.java │ ├── chapter2_3_Quicksort/ │ │ ├── Ex25.java │ │ ├── Quick.java │ │ └── Quick3way.java │ ├── chapter2_4_Priority_Queues/ │ │ ├── MaxPQ.java │ │ └── TopM.java │ ├── chapter3_1_Symbol_Tables/ │ │ ├── BinarySearchST.java │ │ ├── FrequencyCounter.java │ │ └── SequentialSearchST.java │ ├── chapter3_2_Binary_Search_Trees/ │ │ └── BST.java │ ├── chapter3_4_Hash_Tables/ │ │ ├── LinearProbingHashST.java │ │ └── SeparateChainingHashST.java │ ├── chapter3_5_Searching_Applications/ │ │ ├── SparseVector.java │ │ └── WhiteFilter.java │ ├── chapter4_1_Undirected_Graphs/ │ │ ├── BreadthFirstPaths.java │ │ ├── CC.java │ │ ├── Cycle.java │ │ ├── DegreesOfSeparation.java │ │ ├── DepthFirstPaths.java │ │ ├── DepthFirstSearch.java │ │ ├── Graph.java │ │ ├── SymbolGraph.java │ │ ├── TestCC.java │ │ ├── TestPaths.java │ │ ├── TestSearch.java │ │ ├── TestSymbolGraph.java │ │ └── TwoColor.java │ ├── chapter4_2_Directed_Graphs/ │ │ ├── DepthFirstOrder.java │ │ ├── Digraph.java │ │ ├── DirectedCycle.java │ │ ├── DirectedDFS.java │ │ ├── KosarajuSCC.java │ │ ├── SymbolDigraph.java │ │ ├── Topological.java │ │ └── TransitiveClosure.java │ ├── chapter4_3_Minimum_Spanning_Tree/ │ │ ├── Edge.java │ │ ├── EdgeWeightedGraph.java │ │ ├── KruskalMST.java │ │ ├── LazyPrimMST.java │ │ ├── PrimMST.java │ │ ├── TestMST.java │ │ └── UF.java │ ├── chapter4_4_Shortest_Paths/ │ │ ├── DijkstraSP.java │ │ ├── DirectedEdge.java │ │ ├── EdgeWeightedDigraph.java │ │ ├── SP.java │ │ └── TestSP.java │ ├── chapter5_3_Substring_Search/ │ │ ├── BoyerMoore.java │ │ ├── KMP.java │ │ └── RabinKarp.java │ ├── chapter6_3_Suffix_Arrays/ │ │ ├── KWIC.java │ │ ├── LRS.java │ │ └── SuffixArray.java │ └── yuki.config.json ├── docs/ │ ├── .nojekyll │ ├── Context/ │ │ ├── 6.1_事件驱动模拟.md │ │ ├── 6.2_B-树.md │ │ ├── 6.3_后缀数组.md │ │ ├── 6.4_网络流算法.md │ │ ├── 6.5_问题规约.md │ │ └── 6.6_不可解性.md │ ├── Fundamentals/ │ │ ├── 1.3_背包、队列和栈.md │ │ ├── 1.4_算法分析.md │ │ └── 1.5_案例研究:union-find算法.md │ ├── Graphs/ │ │ ├── 4.1_无向图.md │ │ ├── 4.2_有向图.md │ │ ├── 4.3_最小生成树.md │ │ └── 4.4_最短路径.md │ ├── README.md │ ├── Searching/ │ │ ├── 3.1_符号表.md │ │ ├── 3.2_二叉查找树.md │ │ ├── 3.3_平衡查找树.md │ │ ├── 3.4_散列表.md │ │ └── 3.5_应用.md │ ├── Sorting/ │ │ ├── 2.1_初级排序算法.md │ │ ├── 2.2_归并排序.md │ │ ├── 2.3_快速排序.md │ │ ├── 2.4_优先队列.md │ │ └── 2.5_应用.md │ ├── Strings/ │ │ ├── 5.2_单词查找树.md │ │ └── 5.3_子字符串查找.md │ ├── _sidebar.md │ └── index.html ├── package.json ├── 每一节可以再看一遍的题.md └── 相关问题解决方法.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ # Compiled class file *.class # Log file *.log # BlueJ files *.ctxt # Mobile Tools for Java (J2ME) .mtj.tmp/ # Package Files # *.jar *.war *.ear *.zip *.tar.gz *.rar # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* ================================================ FILE: README.md ================================================ # 《算法(第4版)》笔记及代码 [![作者](https://img.shields.io/badge/%E4%BD%9C%E8%80%85-KyonHuang-7AD6FD.svg)](http://kyonhuang.top) ## 笔记 推荐阅览 html 版本的笔记,由 [docsify](https://docsify.js.org/#/zh-cn/) 动态生成文档网站。 [在线阅览地址](http://kyonhuang.top/Algorithms-notes/) ## 目录 * [《每一节可以再看一遍的题》](https://github.com/bighuang624/Algorithms-notes/blob/master/每一节可以再看一遍的题.md) * [《相关问题解决方法》](https://github.com/bighuang624/Algorithms-notes/blob/master/相关问题解决方法.md) ### Fundamentals * [《1.3_背包、队列和栈》](https://github.com/bighuang624/Algorithms-notes/blob/master/docs/Fundamentals/1.3_背包、队列和栈.md) * [《1.4_算法分析》](https://github.com/bighuang624/Algorithms-notes/blob/master/docs/Fundamentals/1.4_算法分析.md) * [《1.5_案例研究:union-find算法》](https://github.com/bighuang624/Algorithms-notes/blob/master/docs/Fundamentals/1.5_案例研究:union-find算法.md) ### Sorting * [《2.1_初级排序算法》](https://github.com/bighuang624/Algorithms-notes/blob/master/docs/Sorting/2.1_初级排序算法.md) * [《2.2_归并排序》](https://github.com/bighuang624/Algorithms-notes/blob/master/docs/Sorting/2.2_归并排序.md) * [《2.3_快速排序》](https://github.com/bighuang624/Algorithms-notes/blob/master/docs/Sorting/2.3_快速排序.md) * [《2.4_优先队列》](https://github.com/bighuang624/Algorithms-notes/blob/master/docs/Sorting/2.4_优先队列.md) * [《2.5_应用》](https://github.com/bighuang624/Algorithms-notes/blob/master/docs/Sorting/2.5_应用.md) ### Searching * [《3.1_符号表》](https://github.com/bighuang624/Algorithms-notes/blob/master/docs/Searching/3.1_符号表.md) * [《3.2_二叉查找树》](https://github.com/bighuang624/Algorithms-notes/blob/master/docs/Searching/3.2_二叉查找树.md) * [《3.3_平衡查找树》](https://github.com/bighuang624/Algorithms-notes/blob/master/docs/Searching/3.3_平衡查找树.md) * [《3.4_散列表》](https://github.com/bighuang624/Algorithms-notes/blob/master/docs/Searching/3.4_散列表.md) * [《3.5_应用》](https://github.com/bighuang624/Algorithms-notes/blob/master/docs/Searching/3.5_应用.md) ### Graphs * [《4.1_无向图》](https://github.com/bighuang624/Algorithms-notes/blob/master/docs/Graphs/4.1_无向图.md) * [《4.2_有向图》](https://github.com/bighuang624/Algorithms-notes/blob/master/docs/Graphs/4.2_有向图.md) * [《4.3_最小生成树》](https://github.com/bighuang624/Algorithms-notes/blob/master/docs/Graphs/4.3_最小生成树.md) * [《4.4_最短路径》](https://github.com/bighuang624/Algorithms-notes/blob/master/docs/Graphs/4.4_最短路径.md) ### Strings * [《5.2_单词查找树》](https://github.com/bighuang624/Algorithms-notes/blob/master/docs/Strings/5.2_单词查找树.md) * [《5.3_子字符串查找》](https://github.com/bighuang624/Algorithms-notes/blob/master/docs/Strings/5.3_子字符串查找.md) ### Context * [《6.3_后缀数组》](https://github.com/bighuang624/Algorithms-notes/blob/master/docs/Context/6.3_后缀数组.md) * [《6.4_网络流算法》](https://github.com/bighuang624/Algorithms-notes/blob/master/docs/Context/6.4_网络流算法.md) * [《6.5_问题规约》](https://github.com/bighuang624/Algorithms-notes/blob/master/docs/Context/6.5_问题规约.md) * [《6.6_不可解性》](https://github.com/bighuang624/Algorithms-notes/blob/master/docs/Context/6.6_不可解性.md) ## 代码 相关代码详见[代码目录](https://github.com/bighuang624/Algorithms-notes/blob/master/code) ## Cheatsheet [Algorithms and Data Structures Cheatsheet](https://algs4.cs.princeton.edu/cheatsheet/) ![Cheatsheet](https://raw.githubusercontent.com/bighuang624/Algorithms-notes/master/Cheatsheet.png) ## 参考资料 [算法(第4版)课后练习答案及相关问题解决方案 - 孙强Jimmy的技术博客 - CSDN博客](http://blog.csdn.net/u013541140/article/details/53222770) ================================================ FILE: code/README.md ================================================ # 代码目录 ### algs4 * [Alphabet.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/algs4/Alphabet.java) * [Bag.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/algs4/Bag.java) * [BinarySearch.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/algs4/BinarySearch.java) * [BinaryStdIn.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/algs4/BinaryStdIn.java) * [BinaryStdOut.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/algs4/BinaryStdOut.java) * [Counter.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/algs4/Counter.java) * [DepthFirstOrder.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/algs4/DepthFirstOrder.java) * [Draw.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/algs4/Draw.java) * [DrawListener.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/algs4/DrawListener.java) * [Heap.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/algs4/Heap.java) * [HexDump.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/algs4/HexDump.java) * [In.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/algs4/In.java) * [IndexMinPQ.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/algs4/IndexMinPQ.java) * [Interval1D.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/algs4/Interval1D.java) * [Interval2D.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/algs4/Interval2D.java) * [MinPQ.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/algs4/MinPQ.java) * [Out.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/algs4/Out.java) * [Particle.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/algs4/Particle.java) * [Picture.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/algs4/Picture.java) * [PictureDump.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/algs4/PictureDump.java) * [Point2D.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/algs4/Point2D.java) * [Queue.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/algs4/Queue.java) * [SET.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/algs4/SET.java) * [ST.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/algs4/ST.java) * [Stack.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/algs4/Stack.java) * [StdDraw.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/algs4/StdDraw.java) * [StdIn.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/algs4/StdIn.java) * [StdOut.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/algs4/StdOut.java) * [StdRandom.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/algs4/StdRandom.java) ### chapter1_1_Programming_Model * [BinarySearch.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter1_1_Programming_Model/BinarySearch.java) * [BouncingBall.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter1_1_Programming_Model/BouncingBall.java) * [Ex1.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter1_1_Programming_Model/Ex1.java) * [Ex10.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter1_1_Programming_Model/Ex10.java) * [Ex11.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter1_1_Programming_Model/Ex11.java) * [Ex13.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter1_1_Programming_Model/Ex13.java) * [Ex14.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter1_1_Programming_Model/Ex14.java) * [Ex15.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter1_1_Programming_Model/Ex15.java) * [Ex19.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter1_1_Programming_Model/Ex19.java) * [Ex2.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter1_1_Programming_Model/Ex2.java) * [Ex20.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter1_1_Programming_Model/Ex20.java) * [Ex3.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter1_1_Programming_Model/Ex3.java) * [Ex30.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter1_1_Programming_Model/Ex30.java) * [Ex31.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter1_1_Programming_Model/Ex31.java) * [Ex32.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter1_1_Programming_Model/Ex32.java) * [Ex6.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter1_1_Programming_Model/Ex6.java) * [Ex7a.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter1_1_Programming_Model/Ex7a.java) * [Ex7b.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter1_1_Programming_Model/Ex7b.java) * [Ex7c.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter1_1_Programming_Model/Ex7c.java) * [Ex8.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter1_1_Programming_Model/Ex8.java) * [Ex9.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter1_1_Programming_Model/Ex9.java) * [RightTriangle.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter1_1_Programming_Model/RightTriangle.java) * [Sattolo.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter1_1_Programming_Model/Sattolo.java) * [StdDrawTest.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter1_1_Programming_Model/StdDrawTest.java) ### chapter1_2_Data_Abstraction * [Accumulator.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter1_2_Data_Abstraction/Accumulator.java) * [AccumulatorTest.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter1_2_Data_Abstraction/AccumulatorTest.java) * [Cat.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter1_2_Data_Abstraction/Cat.java) * [Date.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter1_2_Data_Abstraction/Date.java) * [Flips.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter1_2_Data_Abstraction/Flips.java) * [FlipsMax.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter1_2_Data_Abstraction/FlipsMax.java) * [Interval2DTest.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter1_2_Data_Abstraction/Interval2DTest.java) * [Rolls.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter1_2_Data_Abstraction/Rolls.java) * [StaticSETofInts.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter1_2_Data_Abstraction/StaticSETofInts.java) * [VisualAccumulator.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter1_2_Data_Abstraction/VisualAccumulator.java) * [VisualAccumulatorTest.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter1_2_Data_Abstraction/VisualAccumulatorTest.java) * [WhiteList.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter1_2_Data_Abstraction/WhiteList.java) * [in1.txt](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter1_2_Data_Abstraction/in1.txt) * [in2.txt](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter1_2_Data_Abstraction/in2.txt) ### chapter1_3_Bags_Queues_Stacks * [ResizingArrayStack.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter1_3_Bags_Queues_Stacks/ResizingArrayStack.java) ### chapter1_4_Analysis_of_Algorithms * [DoublingRatio.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter1_4_Analysis_of_Algorithms/DoublingRatio.java) * [DoublingTest.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter1_4_Analysis_of_Algorithms/DoublingTest.java) * [Stopwatch.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter1_4_Analysis_of_Algorithms/Stopwatch.java) * [StopwatchTest.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter1_4_Analysis_of_Algorithms/StopwatchTest.java) * [ThreeSum.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter1_4_Analysis_of_Algorithms/ThreeSum.java) ### chapter1_5_Case_Study_Union_Find * [UF.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter1_5_Case_Study_Union_Find/UF.java) * [WeightedQuickUnionUF.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter1_5_Case_Study_Union_Find/WeightedQuickUnionUF.java) ### chapter2_1_Elementary_Sorts * [Insertion.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter2_1_Elementary_Sorts/Insertion.java) * [Selection.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter2_1_Elementary_Sorts/Selection.java) * [Shell.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter2_1_Elementary_Sorts/Shell.java) * [SortCompare.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter2_1_Elementary_Sorts/SortCompare.java) ### chapter2_2_Mergesort * [Merge.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter2_2_Mergesort/Merge.java) * [MergeBU.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter2_2_Mergesort/MergeBU.java) ### chapter2_3_Quicksort * [Ex25.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter2_3_Quicksort/Ex25.java) * [Quick.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter2_3_Quicksort/Quick.java) * [Quick3way.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter2_3_Quicksort/Quick3way.java) ### chapter2_4_Priority_Queues * [MaxPQ.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter2_4_Priority_Queues/MaxPQ.java) * [TopM.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter2_4_Priority_Queues/TopM.java) ### chapter2_5_Sorting_Applications ### chapter3_1_Symbol_Tables * [BinarySearchST.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter3_1_Symbol_Tables/BinarySearchST.java) * [FrequencyCounter.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter3_1_Symbol_Tables/FrequencyCounter.java) * [SequentialSearchST.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter3_1_Symbol_Tables/SequentialSearchST.java) ### chapter3_2_Binary_Search_Trees * [BST.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter3_2_Binary_Search_Trees/BST.java) ### chapter3_4_Hash_Tables * [LinearProbingHashST.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter3_4_Hash_Tables/LinearProbingHashST.java) * [SeparateChainingHashST.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter3_4_Hash_Tables/SeparateChainingHashST.java) ### chapter3_5_Searching_Applications * [SparseVector.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter3_5_Searching_Applications/SparseVector.java) * [WhiteFilter.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter3_5_Searching_Applications/WhiteFilter.java) ### chapter4_1_Undirected_Graphs * [BreadthFirstPaths.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_1_Undirected_Graphs/BreadthFirstPaths.java) * [CC.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_1_Undirected_Graphs/CC.java) * [Cycle.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_1_Undirected_Graphs/Cycle.java) * [DegreesOfSeparation.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_1_Undirected_Graphs/DegreesOfSeparation.java) * [DepthFirstPaths.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_1_Undirected_Graphs/DepthFirstPaths.java) * [DepthFirstSearch.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_1_Undirected_Graphs/DepthFirstSearch.java) * [Graph.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_1_Undirected_Graphs/Graph.java) * [SymbolGraph.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_1_Undirected_Graphs/SymbolGraph.java) * [TestCC.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_1_Undirected_Graphs/TestCC.java) * [TestPaths.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_1_Undirected_Graphs/TestPaths.java) * [TestSearch.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_1_Undirected_Graphs/TestSearch.java) * [TestSymbolGraph.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_1_Undirected_Graphs/TestSymbolGraph.java) * [TwoColor.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_1_Undirected_Graphs/TwoColor.java) ### chapter4_2_Directed_Graphs * [DepthFirstOrder.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_2_Directed_Graphs/DepthFirstOrder.java) * [Digraph.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_2_Directed_Graphs/Digraph.java) * [DirectedCycle.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_2_Directed_Graphs/DirectedCycle.java) * [DirectedDFS.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_2_Directed_Graphs/DirectedDFS.java) * [KosarajuSCC.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_2_Directed_Graphs/KosarajuSCC.java) * [SymbolDigraph.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_2_Directed_Graphs/SymbolDigraph.java) * [Topological.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_2_Directed_Graphs/Topological.java) * [TransitiveClosure.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_2_Directed_Graphs/TransitiveClosure.java) ### chapter4_3_Minimum_Spanning_Tree * [Edge.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_3_Minimum_Spanning_Tree/Edge.java) * [EdgeWeightedGraph.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_3_Minimum_Spanning_Tree/EdgeWeightedGraph.java) * [KruskalMST.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_3_Minimum_Spanning_Tree/KruskalMST.java) * [LazyPrimMST.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_3_Minimum_Spanning_Tree/LazyPrimMST.java) * [PrimMST.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_3_Minimum_Spanning_Tree/PrimMST.java) * [TestMST.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_3_Minimum_Spanning_Tree/TestMST.java) * [UF.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_3_Minimum_Spanning_Tree/UF.java) ### chapter4_4_Shortest_Paths * [DijkstraSP.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_4_Shortest_Paths/DijkstraSP.java) * [DirectedEdge.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_4_Shortest_Paths/DirectedEdge.java) * [EdgeWeightedDigraph.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_4_Shortest_Paths/EdgeWeightedDigraph.java) * [SP.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_4_Shortest_Paths/SP.java) * [TestSP.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_4_Shortest_Paths/TestSP.java) ### chapter5_3_Substring_Search * [BoyerMoore.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter5_3_Substring_Search/BoyerMoore.java) * [KMP.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter5_3_Substring_Search/KMP.java) * [RabinKarp.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter5_3_Substring_Search/RabinKarp.java) ### chapter6_3_Suffix_Arrays * [KWIC.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter6_3_Suffix_Arrays/KWIC.java) * [LRS.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter6_3_Suffix_Arrays/LRS.java) * [SuffixArray.java](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter6_3_Suffix_Arrays/SuffixArray.java) ================================================ FILE: code/algs4/Alphabet.java ================================================ /****************************************************************************** * Compilation: javac Alphabet.java * Execution: java Alphabet * Dependencies: StdOut.java * * A data type for alphabets, for use with string-processing code * that must convert between an alphabet of size R and the integers * 0 through R-1. * * Warning: supports only the basic multilingual plane (BMP), i.e, * Unicode characters between U+0000 and U+FFFF. * ******************************************************************************/ package algs4; public class Alphabet { /** * The binary alphabet { 0, 1 }. */ public static final Alphabet BINARY = new Alphabet("01"); /** * The octal alphabet { 0, 1, 2, 3, 4, 5, 6, 7 }. */ public static final Alphabet OCTAL = new Alphabet("01234567"); /** * The decimal alphabet { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }. */ public static final Alphabet DECIMAL = new Alphabet("0123456789"); /** * The hexadecimal alphabet { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F }. */ public static final Alphabet HEXADECIMAL = new Alphabet("0123456789ABCDEF"); /** * The DNA alphabet { A, C, T, G }. */ public static final Alphabet DNA = new Alphabet("ACGT"); /** * The lowercase alphabet { a, b, c, ..., z }. */ public static final Alphabet LOWERCASE = new Alphabet("abcdefghijklmnopqrstuvwxyz"); /** * The uppercase alphabet { A, B, C, ..., Z }. */ public static final Alphabet UPPERCASE = new Alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZ"); /** * The protein alphabet { A, C, D, E, F, G, H, I, K, L, M, N, P, Q, R, S, T, V, W, Y }. */ public static final Alphabet PROTEIN = new Alphabet("ACDEFGHIKLMNPQRSTVWY"); /** * The base-64 alphabet (64 characters). */ public static final Alphabet BASE64 = new Alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"); /** * The ASCII alphabet (0-127). */ public static final Alphabet ASCII = new Alphabet(128); /** * The extended ASCII alphabet (0-255). */ public static final Alphabet EXTENDED_ASCII = new Alphabet(256); /** * The Unicode 16 alphabet (0-65,535). */ public static final Alphabet UNICODE16 = new Alphabet(65536); private char[] alphabet; // the characters in the alphabet private int[] inverse; // indices private final int R; // the radix of the alphabet /** * Initializes a new alphabet from the given set of characters. * * @param alpha the set of characters */ public Alphabet(String alpha) { // check that alphabet contains no duplicate chars boolean[] unicode = new boolean[Character.MAX_VALUE]; for (int i = 0; i < alpha.length(); i++) { char c = alpha.charAt(i); if (unicode[c]) throw new IllegalArgumentException("Illegal alphabet: repeated character = '" + c + "'"); unicode[c] = true; } alphabet = alpha.toCharArray(); R = alpha.length(); inverse = new int[Character.MAX_VALUE]; for (int i = 0; i < inverse.length; i++) inverse[i] = -1; // can't use char since R can be as big as 65,536 for (int c = 0; c < R; c++) inverse[alphabet[c]] = c; } /** * Initializes a new alphabet using characters 0 through R-1. * * @param radix the number of characters in the alphabet (the radix R) */ private Alphabet(int radix) { this.R = radix; alphabet = new char[R]; inverse = new int[R]; // can't use char since R can be as big as 65,536 for (int i = 0; i < R; i++) alphabet[i] = (char) i; for (int i = 0; i < R; i++) inverse[i] = i; } /** * Initializes a new alphabet using characters 0 through 255. */ public Alphabet() { this(256); } /** * Returns true if the argument is a character in this alphabet. * * @param c the character * @return {@code true} if {@code c} is a character in this alphabet; * {@code false} otherwise */ public boolean contains(char c) { return inverse[c] != -1; } /** * Returns the number of characters in this alphabet (the radix). * * @return the number of characters in this alphabet * @deprecated Replaced by {@link #radix()}. */ @Deprecated public int R() { return R; } /** * Returns the number of characters in this alphabet (the radix). * * @return the number of characters in this alphabet */ public int radix() { return R; } /** * Returns the binary logarithm of the number of characters in this alphabet. * * @return the binary logarithm (rounded up) of the number of characters in this alphabet */ public int lgR() { int lgR = 0; for (int t = R-1; t >= 1; t /= 2) lgR++; return lgR; } /** * Returns the index corresponding to the argument character. * * @param c the character * @return the index corresponding to the character {@code c} * @throws IllegalArgumentException unless {@code c} is a character in this alphabet */ public int toIndex(char c) { if (c >= inverse.length || inverse[c] == -1) { throw new IllegalArgumentException("Character " + c + " not in alphabet"); } return inverse[c]; } /** * Returns the indices corresponding to the argument characters. * * @param s the characters * @return the indices corresponding to the characters {@code s} * @throws IllegalArgumentException unless every character in {@code s} * is a character in this alphabet */ public int[] toIndices(String s) { char[] source = s.toCharArray(); int[] target = new int[s.length()]; for (int i = 0; i < source.length; i++) target[i] = toIndex(source[i]); return target; } /** * Returns the character corresponding to the argument index. * * @param index the index * @return the character corresponding to the index {@code index} * @throws IllegalArgumentException unless {@code 0 <= index < R} */ public char toChar(int index) { if (index < 0 || index >= R) { throw new IndexOutOfBoundsException("Alphabet index out of bounds"); } return alphabet[index]; } /** * Returns the characters corresponding to the argument indices. * * @param indices the indices * @return the characters corresponding to the indices {@code indices} * @throws IllegalArgumentException unless {@code 0 < indices[i] < R} * for every {@code i} */ public String toChars(int[] indices) { StringBuilder s = new StringBuilder(indices.length); for (int i = 0; i < indices.length; i++) s.append(toChar(indices[i])); return s.toString(); } /** * Unit tests the {@code Alphabet} data type. * * @param args the command-line arguments */ public static void main(String[] args) { int[] encoded1 = Alphabet.BASE64.toIndices("NowIsTheTimeForAllGoodMen"); String decoded1 = Alphabet.BASE64.toChars(encoded1); StdOut.println(decoded1); int[] encoded2 = Alphabet.DNA.toIndices("AACGAACGGTTTACCCCG"); String decoded2 = Alphabet.DNA.toChars(encoded2); StdOut.println(decoded2); int[] encoded3 = Alphabet.DECIMAL.toIndices("01234567890123456789"); String decoded3 = Alphabet.DECIMAL.toChars(encoded3); StdOut.println(decoded3); } } /****************************************************************************** * Copyright 2002-2016, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * * Algorithms, 4th edition by Robert Sedgewick and Kevin Wayne, * Addison-Wesley Professional, 2011, ISBN 0-321-57351-X. * http://algs4.cs.princeton.edu * * * algs4.jar is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * algs4.jar is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with algs4.jar. If not, see http://www.gnu.org/licenses. ******************************************************************************/ ================================================ FILE: code/algs4/Bag.java ================================================ /****************************************************************************** * Compilation: javac Bag.java * Execution: java Bag < input.txt * Dependencies: StdIn.java StdOut.java * * A generic bag or multiset, implemented using a singly-linked list. * * % more tobe.txt * to be or not to - be - - that - - - is * * % java Bag < tobe.txt * size of bag = 14 * is * - * - * - * that * - * - * be * - * to * not * or * be * to * ******************************************************************************/ package algs4; import java.util.Iterator; import java.util.NoSuchElementException; /** * The {@code Bag} class represents a bag (or multiset) of * generic items. It supports insertion and iterating over the * items in arbitrary order. *

* This implementation uses a singly-linked list with a static nested class Node. * See {@link LinkedBag} for the version from the * textbook that uses a non-static nested class. * The add, isEmpty, and size operations * take constant time. Iteration takes time proportional to the number of items. *

* For additional documentation, see Section 1.3 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne * * @param the generic type of an item in this bag */ public class Bag implements Iterable { private Node first; // beginning of bag private int n; // number of elements in bag // helper linked list class private static class Node { private Item item; private Node next; } /** * Initializes an empty bag. */ public Bag() { first = null; n = 0; } /** * Returns true if this bag is empty. * * @return {@code true} if this bag is empty; * {@code false} otherwise */ public boolean isEmpty() { return first == null; } /** * Returns the number of items in this bag. * * @return the number of items in this bag */ public int size() { return n; } /** * Adds the item to this bag. * * @param item the item to add to this bag */ public void add(Item item) { Node oldfirst = first; first = new Node(); first.item = item; first.next = oldfirst; n++; } /** * Returns an iterator that iterates over the items in this bag in arbitrary order. * * @return an iterator that iterates over the items in this bag in arbitrary order */ public Iterator iterator() { return new ListIterator(first); } // an iterator, doesn't implement remove() since it's optional private class ListIterator implements Iterator { private Node current; public ListIterator(Node first) { current = first; } public boolean hasNext() { return current != null; } public void remove() { throw new UnsupportedOperationException(); } public Item next() { if (!hasNext()) throw new NoSuchElementException(); Item item = current.item; current = current.next; return item; } } /** * Unit tests the {@code Bag} data type. * * @param args the command-line arguments */ public static void main(String[] args) { Bag bag = new Bag(); while (!StdIn.isEmpty()) { String item = StdIn.readString(); bag.add(item); } StdOut.println("size of bag = " + bag.size()); for (String s : bag) { StdOut.println(s); } } } /****************************************************************************** * Copyright 2002-2016, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * * Algorithms, 4th edition by Robert Sedgewick and Kevin Wayne, * Addison-Wesley Professional, 2011, ISBN 0-321-57351-X. * http://algs4.cs.princeton.edu * * * algs4.jar is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * algs4.jar is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with algs4.jar. If not, see http://www.gnu.org/licenses. ******************************************************************************/ ================================================ FILE: code/algs4/BinarySearch.java ================================================ /****************************************************************************** * Compilation: javac BinarySearch.java * Execution: java BinarySearch whitelist.txt < input.txt * Dependencies: In.java StdIn.java StdOut.java * Data files: http://algs4.cs.princeton.edu/11model/tinyW.txt * http://algs4.cs.princeton.edu/11model/tinyT.txt * http://algs4.cs.princeton.edu/11model/largeW.txt * http://algs4.cs.princeton.edu/11model/largeT.txt * * % java BinarySearch tinyW.txt < tinyT.txt * 50 * 99 * 13 * * % java BinarySearch largeW.txt < largeT.txt | more * 499569 * 984875 * 295754 * 207807 * 140925 * 161828 * [367,966 total values] * ******************************************************************************/ package algs4; import java.util.Arrays; /** * The {@code BinarySearch} class provides a static method for binary * searching for an integer in a sorted array of integers. *

* The indexOf operations takes logarithmic time in the worst case. *

* For additional documentation, see Section 1.1 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne */ public class BinarySearch { /** * This class should not be instantiated. */ private BinarySearch() { } /** * Returns the index of the specified key in the specified array. * * @param a the array of integers, must be sorted in ascending order * @param key the search key * @return index of key in array {@code a} if present; {@code -1} otherwise */ public static int indexOf(int[] a, int key) { int lo = 0; int hi = a.length - 1; while (lo <= hi) { // Key is in a[lo..hi] or not present. int mid = lo + (hi - lo) / 2; if (key < a[mid]) hi = mid - 1; else if (key > a[mid]) lo = mid + 1; else return mid; } return -1; } /** * Returns the index of the specified key in the specified array. * This function is poorly named because it does not give the rank * if the array has duplicate keys or if the key is not in the array. * * @param key the search key * @param a the array of integers, must be sorted in ascending order * @return index of key in array {@code a} if present; {@code -1} otherwise * @deprecated Replaced by {@link #indexOf(int[], int)}. */ @Deprecated public static int rank(int key, int[] a) { return indexOf(a, key); } /** * Reads in a sequence of integers from the whitelist file, specified as * a command-line argument; reads in integers from standard input; * prints to standard output those integers that do not appear in the file. * * @param args the command-line arguments */ public static void main(String[] args) { // read the integers from a file In in = new In(args[0]); int[] whitelist = in.readAllInts(); // sort the array Arrays.sort(whitelist); // read integer key from standard input; print if not in whitelist while (!StdIn.isEmpty()) { int key = StdIn.readInt(); if (BinarySearch.indexOf(whitelist, key) == -1) StdOut.println(key); } } } /****************************************************************************** * Copyright 2002-2016, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * * Algorithms, 4th edition by Robert Sedgewick and Kevin Wayne, * Addison-Wesley Professional, 2011, ISBN 0-321-57351-X. * http://algs4.cs.princeton.edu * * * algs4.jar is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * algs4.jar is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with algs4.jar. If not, see http://www.gnu.org/licenses. ******************************************************************************/ ================================================ FILE: code/algs4/BinaryStdIn.java ================================================ /****************************************************************************** * Compilation: javac BinaryStdIn.java * Execution: java BinaryStdIn < input > output * Dependencies: none * * Supports reading binary data from standard input. * * % java BinaryStdIn < input.jpg > output.jpg * % diff input.jpg output.jpg * ******************************************************************************/ package algs4; import java.io.BufferedInputStream; import java.io.IOException; import java.util.NoSuchElementException; /** * Binary standard input. This class provides methods for reading * in bits from standard input, either one bit at a time (as a {@code boolean}), * 8 bits at a time (as a {@code byte} or {@code char}), * 16 bits at a time (as a {@code short}), 32 bits at a time * (as an {@code int} or {@code float}), or 64 bits at a time (as a * {@code double} or {@code long}). *

* All primitive types are assumed to be represented using their * standard Java representations, in big-endian (most significant * byte first) order. *

* The client should not intermix calls to {@code BinaryStdIn} with calls * to {@code StdIn} or {@code System.in}; * otherwise unexpected behavior will result. * * @author Robert Sedgewick * @author Kevin Wayne */ public final class BinaryStdIn { private static BufferedInputStream in = new BufferedInputStream(System.in); private static final int EOF = -1; // end of file private static int buffer; // one character buffer private static int n; // number of bits left in buffer // static initializer static { fillBuffer(); } // don't instantiate private BinaryStdIn() { } private static void fillBuffer() { try { buffer = in.read(); n = 8; } catch (IOException e) { System.out.println("EOF"); buffer = EOF; n = -1; } } /** * Close this input stream and release any associated system resources. */ public static void close() { try { in.close(); } catch (IOException ioe) { throw new IllegalStateException("Could not close BinaryStdIn", ioe); } } /** * Returns true if standard input is empty. * @return true if and only if standard input is empty */ public static boolean isEmpty() { return buffer == EOF; } /** * Reads the next bit of data from standard input and return as a boolean. * * @return the next bit of data from standard input as a {@code boolean} * @throws NoSuchElementException if standard input is empty */ public static boolean readBoolean() { if (isEmpty()) throw new NoSuchElementException("Reading from empty input stream"); n--; boolean bit = ((buffer >> n) & 1) == 1; if (n == 0) fillBuffer(); return bit; } /** * Reads the next 8 bits from standard input and return as an 8-bit char. * Note that {@code char} is a 16-bit type; * to read the next 16 bits as a char, use {@code readChar(16)}. * * @return the next 8 bits of data from standard input as a {@code char} * @throws NoSuchElementException if there are fewer than 8 bits available on standard input */ public static char readChar() { if (isEmpty()) throw new NoSuchElementException("Reading from empty input stream"); // special case when aligned byte if (n == 8) { int x = buffer; fillBuffer(); return (char) (x & 0xff); } // combine last n bits of current buffer with first 8-n bits of new buffer int x = buffer; x <<= (8 - n); int oldN = n; fillBuffer(); if (isEmpty()) throw new NoSuchElementException("Reading from empty input stream"); n = oldN; x |= (buffer >>> n); return (char) (x & 0xff); // the above code doesn't quite work for the last character if n = 8 // because buffer will be -1, so there is a special case for aligned byte } /** * Reads the next r bits from standard input and return as an r-bit character. * * @param r number of bits to read. * @return the next r bits of data from standard input as a {@code char} * @throws NoSuchElementException if there are fewer than {@code r} bits available on standard input * @throws IllegalArgumentException unless {@code 1 <= r <= 16} */ public static char readChar(int r) { if (r < 1 || r > 16) throw new IllegalArgumentException("Illegal value of r = " + r); // optimize r = 8 case if (r == 8) return readChar(); char x = 0; for (int i = 0; i < r; i++) { x <<= 1; boolean bit = readBoolean(); if (bit) x |= 1; } return x; } /** * Reads the remaining bytes of data from standard input and return as a string. * * @return the remaining bytes of data from standard input as a {@code String} * @throws NoSuchElementException if standard input is empty or if the number of bits * available on standard input is not a multiple of 8 (byte-aligned) */ public static String readString() { if (isEmpty()) throw new NoSuchElementException("Reading from empty input stream"); StringBuilder sb = new StringBuilder(); while (!isEmpty()) { char c = readChar(); sb.append(c); } return sb.toString(); } /** * Reads the next 16 bits from standard input and return as a 16-bit short. * * @return the next 16 bits of data from standard input as a {@code short} * @throws NoSuchElementException if there are fewer than 16 bits available on standard input */ public static short readShort() { short x = 0; for (int i = 0; i < 2; i++) { char c = readChar(); x <<= 8; x |= c; } return x; } /** * Reads the next 32 bits from standard input and return as a 32-bit int. * * @return the next 32 bits of data from standard input as a {@code int} * @throws NoSuchElementException if there are fewer than 32 bits available on standard input */ public static int readInt() { int x = 0; for (int i = 0; i < 4; i++) { char c = readChar(); x <<= 8; x |= c; } return x; } /** * Reads the next r bits from standard input and return as an r-bit int. * * @param r number of bits to read. * @return the next r bits of data from standard input as a {@code int} * @throws NoSuchElementException if there are fewer than {@code r} bits available on standard input * @throws IllegalArgumentException unless {@code 1 <= r <= 32} */ public static int readInt(int r) { if (r < 1 || r > 32) throw new IllegalArgumentException("Illegal value of r = " + r); // optimize r = 32 case if (r == 32) return readInt(); int x = 0; for (int i = 0; i < r; i++) { x <<= 1; boolean bit = readBoolean(); if (bit) x |= 1; } return x; } /** * Reads the next 64 bits from standard input and return as a 64-bit long. * * @return the next 64 bits of data from standard input as a {@code long} * @throws NoSuchElementException if there are fewer than 64 bits available on standard input */ public static long readLong() { long x = 0; for (int i = 0; i < 8; i++) { char c = readChar(); x <<= 8; x |= c; } return x; } /** * Reads the next 64 bits from standard input and return as a 64-bit double. * * @return the next 64 bits of data from standard input as a {@code double} * @throws NoSuchElementException if there are fewer than 64 bits available on standard input */ public static double readDouble() { return Double.longBitsToDouble(readLong()); } /** * Reads the next 32 bits from standard input and return as a 32-bit float. * * @return the next 32 bits of data from standard input as a {@code float} * @throws NoSuchElementException if there are fewer than 32 bits available on standard input */ public static float readFloat() { return Float.intBitsToFloat(readInt()); } /** * Reads the next 8 bits from standard input and return as an 8-bit byte. * * @return the next 8 bits of data from standard input as a {@code byte} * @throws NoSuchElementException if there are fewer than 8 bits available on standard input */ public static byte readByte() { char c = readChar(); return (byte) (c & 0xff); } /** * Test client. Reads in a binary input file from standard input and writes * it to standard output. * * @param args the command-line arguments */ public static void main(String[] args) { // read one 8-bit char at a time while (!BinaryStdIn.isEmpty()) { char c = BinaryStdIn.readChar(); BinaryStdOut.write(c); } BinaryStdOut.flush(); } } /****************************************************************************** * Copyright 2002-2016, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * * Algorithms, 4th edition by Robert Sedgewick and Kevin Wayne, * Addison-Wesley Professional, 2011, ISBN 0-321-57351-X. * http://algs4.cs.princeton.edu * * * algs4.jar is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * algs4.jar is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with algs4.jar. If not, see http://www.gnu.org/licenses. ******************************************************************************/ ================================================ FILE: code/algs4/BinaryStdOut.java ================================================ /****************************************************************************** * Compilation: javac BinaryStdOut.java * Execution: java BinaryStdOut * Dependencies: none * * Write binary data to standard output, either one 1-bit boolean, * one 8-bit char, one 32-bit int, one 64-bit double, one 32-bit float, * or one 64-bit long at a time. * * The bytes written are not aligned. * ******************************************************************************/ package algs4; import java.io.BufferedOutputStream; import java.io.IOException; /** * Binary standard output. This class provides methods for converting * primtive type variables ({@code boolean}, {@code byte}, {@code char}, * {@code int}, {@code long}, {@code float}, and {@code double}) * to sequences of bits and writing them to standard output. * Uses big-endian (most-significant byte first). *

* The client must {@code flush()} the output stream when finished writing bits. *

* The client should not intermixing calls to {@code BinaryStdOut} with calls * to {@code StdOut} or {@code System.out}; otherwise unexpected behavior * will result. * * @author Robert Sedgewick * @author Kevin Wayne */ public final class BinaryStdOut { private static BufferedOutputStream out = new BufferedOutputStream(System.out); private static int buffer; // 8-bit buffer of bits to write out private static int n; // number of bits remaining in buffer // don't instantiate private BinaryStdOut() { } /** * Write the specified bit to standard output. */ private static void writeBit(boolean bit) { // add bit to buffer buffer <<= 1; if (bit) buffer |= 1; // if buffer is full (8 bits), write out as a single byte n++; if (n == 8) clearBuffer(); } /** * Write the 8-bit byte to standard output. */ private static void writeByte(int x) { assert x >= 0 && x < 256; // optimized if byte-aligned if (n == 0) { try { out.write(x); } catch (IOException e) { e.printStackTrace(); } return; } // otherwise write one bit at a time for (int i = 0; i < 8; i++) { boolean bit = ((x >>> (8 - i - 1)) & 1) == 1; writeBit(bit); } } // write out any remaining bits in buffer to standard output, padding with 0s private static void clearBuffer() { if (n == 0) return; if (n > 0) buffer <<= (8 - n); try { out.write(buffer); } catch (IOException e) { e.printStackTrace(); } n = 0; buffer = 0; } /** * Flush standard output, padding 0s if number of bits written so far * is not a multiple of 8. */ public static void flush() { clearBuffer(); try { out.flush(); } catch (IOException e) { e.printStackTrace(); } } /** * Flush and close standard output. Once standard output is closed, you can no * longer write bits to it. */ public static void close() { flush(); try { out.close(); } catch (IOException e) { e.printStackTrace(); } } /** * Write the specified bit to standard output. * @param x the {@code boolean} to write. */ public static void write(boolean x) { writeBit(x); } /** * Write the 8-bit byte to standard output. * @param x the {@code byte} to write. */ public static void write(byte x) { writeByte(x & 0xff); } /** * Write the 32-bit int to standard output. * @param x the {@code int} to write. */ public static void write(int x) { writeByte((x >>> 24) & 0xff); writeByte((x >>> 16) & 0xff); writeByte((x >>> 8) & 0xff); writeByte((x >>> 0) & 0xff); } /** * Write the r-bit int to standard output. * @param x the {@code int} to write. * @param r the number of relevant bits in the char. * @throws IllegalArgumentException if {@code r} is not between 1 and 32. * @throws IllegalArgumentException if {@code x} is not between 0 and 2r - 1. */ public static void write(int x, int r) { if (r == 32) { write(x); return; } if (r < 1 || r > 32) throw new IllegalArgumentException("Illegal value for r = " + r); if (x < 0 || x >= (1 << r)) throw new IllegalArgumentException("Illegal " + r + "-bit char = " + x); for (int i = 0; i < r; i++) { boolean bit = ((x >>> (r - i - 1)) & 1) == 1; writeBit(bit); } } /** * Write the 64-bit double to standard output. * @param x the {@code double} to write. */ public static void write(double x) { write(Double.doubleToRawLongBits(x)); } /** * Write the 64-bit long to standard output. * @param x the {@code long} to write. */ public static void write(long x) { writeByte((int) ((x >>> 56) & 0xff)); writeByte((int) ((x >>> 48) & 0xff)); writeByte((int) ((x >>> 40) & 0xff)); writeByte((int) ((x >>> 32) & 0xff)); writeByte((int) ((x >>> 24) & 0xff)); writeByte((int) ((x >>> 16) & 0xff)); writeByte((int) ((x >>> 8) & 0xff)); writeByte((int) ((x >>> 0) & 0xff)); } /** * Write the 32-bit float to standard output. * @param x the {@code float} to write. */ public static void write(float x) { write(Float.floatToRawIntBits(x)); } /** * Write the 16-bit int to standard output. * @param x the {@code short} to write. */ public static void write(short x) { writeByte((x >>> 8) & 0xff); writeByte((x >>> 0) & 0xff); } /** * Write the 8-bit char to standard output. * @param x the {@code char} to write. * @throws IllegalArgumentException if {@code x} is not betwen 0 and 255. */ public static void write(char x) { if (x < 0 || x >= 256) throw new IllegalArgumentException("Illegal 8-bit char = " + x); writeByte(x); } /** * Write the r-bit char to standard output. * @param x the {@code char} to write. * @param r the number of relevant bits in the char. * @throws IllegalArgumentException if {@code r} is not between 1 and 16. * @throws IllegalArgumentException if {@code x} is not between 0 and 2r - 1. */ public static void write(char x, int r) { if (r == 8) { write(x); return; } if (r < 1 || r > 16) throw new IllegalArgumentException("Illegal value for r = " + r); if (x >= (1 << r)) throw new IllegalArgumentException("Illegal " + r + "-bit char = " + x); for (int i = 0; i < r; i++) { boolean bit = ((x >>> (r - i - 1)) & 1) == 1; writeBit(bit); } } /** * Write the string of 8-bit characters to standard output. * @param s the {@code String} to write. * @throws IllegalArgumentException if any character in the string is not * between 0 and 255. */ public static void write(String s) { for (int i = 0; i < s.length(); i++) write(s.charAt(i)); } /** * Write the String of r-bit characters to standard output. * @param s the {@code String} to write. * @param r the number of relevants bits in each character. * @throws IllegalArgumentException if r is not between 1 and 16. * @throws IllegalArgumentException if any character in the string is not * between 0 and 2r - 1. */ public static void write(String s, int r) { for (int i = 0; i < s.length(); i++) write(s.charAt(i), r); } /** * Test client. * * @param args the command-line arguments */ public static void main(String[] args) { int m = Integer.parseInt(args[0]); // write n integers to binary standard output for (int i = 0; i < m; i++) { BinaryStdOut.write(i); } BinaryStdOut.flush(); } } /****************************************************************************** * Copyright 2002-2016, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * * Algorithms, 4th edition by Robert Sedgewick and Kevin Wayne, * Addison-Wesley Professional, 2011, ISBN 0-321-57351-X. * http://algs4.cs.princeton.edu * * * algs4.jar is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * algs4.jar is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with algs4.jar. If not, see http://www.gnu.org/licenses. ******************************************************************************/ ================================================ FILE: code/algs4/Counter.java ================================================ /****************************************************************************** * Compilation: javac Counter.java * Execution: java Counter n trials * Dependencies: StdRandom.java StdOut.java * * A mutable data type for an integer counter. * * The test clients create n counters and performs trials increment * operations on random counters. * * java Counter 6 600000 * 100140 counter0 * 100273 counter1 * 99848 counter2 * 100129 counter3 * 99973 counter4 * 99637 counter5 * ******************************************************************************/ package algs4; /** * The {@code Counter} class is a mutable data type to encapsulate a counter. *

* For additional documentation, * see Section 1.2 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne */ public class Counter implements Comparable { private final String name; // counter name private int count = 0; // current value /** * Initializes a new counter starting at 0, with the given id. * * @param id the name of the counter */ public Counter(String id) { name = id; } /** * Increments the counter by 1. */ public void increment() { count++; } /** * Returns the current value of this counter. * * @return the current value of this counter */ public int tally() { return count; } /** * Returns a string representation of this counter. * * @return a string representation of this counter */ public String toString() { return count + " " + name; } /** * Compares this counter to the specified counter. * * @param that the other counter * @return {@code 0} if the value of this counter equals * the value of that counter; a negative integer if * the value of this counter is less than the value of * that counter; and a positive integer if the value * of this counter is greater than the value of that * counter */ @Override public int compareTo(Counter that) { if (this.count < that.count) return -1; else if (this.count > that.count) return +1; else return 0; } /** * Reads two command-line integers n and trials; creates n counters; * increments trials counters at random; and prints results. * * @param args the command-line arguments */ public static void main(String[] args) { int n = Integer.parseInt(args[0]); int trials = Integer.parseInt(args[1]); // create n counters Counter[] hits = new Counter[n]; for (int i = 0; i < n; i++) { hits[i] = new Counter("counter" + i); } // increment trials counters at random for (int t = 0; t < trials; t++) { hits[StdRandom.uniform(n)].increment(); } // print results for (int i = 0; i < n; i++) { StdOut.println(hits[i]); } } } /****************************************************************************** * Copyright 2002-2016, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * * Algorithms, 4th edition by Robert Sedgewick and Kevin Wayne, * Addison-Wesley Professional, 2011, ISBN 0-321-57351-X. * http://algs4.cs.princeton.edu * * * algs4.jar is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * algs4.jar is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with algs4.jar. If not, see http://www.gnu.org/licenses. ******************************************************************************/ ================================================ FILE: code/algs4/DepthFirstOrder.java ================================================ ///****************************************************************************** // * Compilation: javac DepthFirstOrder.java // * Execution: java DepthFirstOrder digraph.txt // * Dependencies: Digraph.java Queue.java Stack.java StdOut.java // * EdgeWeightedDigraph.java DirectedEdge.java // * Data files: http://algs4.cs.princeton.edu/42digraph/tinyDAG.txt // * http://algs4.cs.princeton.edu/42digraph/tinyDG.txt // * // * Compute preorder and postorder for a digraph or edge-weighted digraph. // * Runs in O(E + V) time. // * // * % java DepthFirstOrder tinyDAG.txt // * v pre post // * -------------- // * 0 0 8 // * 1 3 2 // * 2 9 10 // * 3 10 9 // * 4 2 0 // * 5 1 1 // * 6 4 7 // * 7 11 11 // * 8 12 12 // * 9 5 6 // * 10 8 5 // * 11 6 4 // * 12 7 3 // * Preorder: 0 5 4 1 6 9 11 12 10 2 3 7 8 // * Postorder: 4 5 1 12 11 10 9 6 0 3 2 7 8 // * Reverse postorder: 8 7 2 3 0 6 9 10 11 12 1 5 4 // * // ******************************************************************************/ // //package algs4; // //import com.jimmysun.algorithms.chapter4_2.Digraph; //import com.jimmysun.algorithms.chapter4_4.DirectedEdge; //import com.jimmysun.algorithms.chapter4_4.EdgeWeightedDigraph; // ///** // * The {@code DepthFirstOrder} class represents a data type for // * determining depth-first search ordering of the vertices in a digraph // * or edge-weighted digraph, including preorder, postorder, and reverse postorder. // *

// * This implementation uses depth-first search. // * The constructor takes time proportional to V + E // * (in the worst case), // * where V is the number of vertices and E is the number of edges. // * Afterwards, the preorder, postorder, and reverse postorder // * operation takes take time proportional to V. // *

// * For additional documentation, // * see Section 4.2 of // * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. // * // * @author Robert Sedgewick // * @author Kevin Wayne // */ //public class DepthFirstOrder { // private boolean[] marked; // marked[v] = has v been marked in dfs? // private int[] pre; // pre[v] = preorder number of v // private int[] post; // post[v] = postorder number of v // private Queue preorder; // vertices in preorder // private Queue postorder; // vertices in postorder // private int preCounter; // counter or preorder numbering // private int postCounter; // counter for postorder numbering // // /** // * Determines a depth-first order for the digraph {@code G}. // * @param G the digraph // */ // public DepthFirstOrder(Digraph G) { // pre = new int[G.V()]; // post = new int[G.V()]; // postorder = new Queue(); // preorder = new Queue(); // marked = new boolean[G.V()]; // for (int v = 0; v < G.V(); v++) // if (!marked[v]) dfs(G, v); // // assert check(); // } // // /** // * Determines a depth-first order for the edge-weighted digraph {@code G}. // * @param G the edge-weighted digraph // */ // public DepthFirstOrder(EdgeWeightedDigraph G) { // pre = new int[G.V()]; // post = new int[G.V()]; // postorder = new Queue(); // preorder = new Queue(); // marked = new boolean[G.V()]; // for (int v = 0; v < G.V(); v++) // if (!marked[v]) dfs(G, v); // } // // // run DFS in digraph G from vertex v and compute preorder/postorder // private void dfs(Digraph G, int v) { // marked[v] = true; // pre[v] = preCounter++; // preorder.enqueue(v); // for (int w : G.adj(v)) { // if (!marked[w]) { // dfs(G, w); // } // } // postorder.enqueue(v); // post[v] = postCounter++; // } // // // run DFS in edge-weighted digraph G from vertex v and compute preorder/postorder // private void dfs(EdgeWeightedDigraph G, int v) { // marked[v] = true; // pre[v] = preCounter++; // preorder.enqueue(v); // for (DirectedEdge e : G.adj(v)) { // int w = e.to(); // if (!marked[w]) { // dfs(G, w); // } // } // postorder.enqueue(v); // post[v] = postCounter++; // } // // /** // * Returns the preorder number of vertex {@code v}. // * @param v the vertex // * @return the preorder number of vertex {@code v} // * @throws IllegalArgumentException unless {@code 0 <= v < V} // */ // public int pre(int v) { // validateVertex(v); // return pre[v]; // } // // /** // * Returns the postorder number of vertex {@code v}. // * @param v the vertex // * @return the postorder number of vertex {@code v} // * @throws IllegalArgumentException unless {@code 0 <= v < V} // */ // public int post(int v) { // validateVertex(v); // return post[v]; // } // // /** // * Returns the vertices in postorder. // * @return the vertices in postorder, as an iterable of vertices // */ // public Iterable post() { // return postorder; // } // // /** // * Returns the vertices in preorder. // * @return the vertices in preorder, as an iterable of vertices // */ // public Iterable pre() { // return preorder; // } // // /** // * Returns the vertices in reverse postorder. // * @return the vertices in reverse postorder, as an iterable of vertices // */ // public Iterable reversePost() { // Stack reverse = new Stack(); // for (int v : postorder) // reverse.push(v); // return reverse; // } // // // // check that pre() and post() are consistent with pre(v) and post(v) // private boolean check() { // // // check that post(v) is consistent with post() // int r = 0; // for (int v : post()) { // if (post(v) != r) { // StdOut.println("post(v) and post() inconsistent"); // return false; // } // r++; // } // // // check that pre(v) is consistent with pre() // r = 0; // for (int v : pre()) { // if (pre(v) != r) { // StdOut.println("pre(v) and pre() inconsistent"); // return false; // } // r++; // } // // return true; // } // // // throw an IllegalArgumentException unless {@code 0 <= v < V} // private void validateVertex(int v) { // int V = marked.length; // if (v < 0 || v >= V) // throw new IllegalArgumentException("vertex " + v + " is not between 0 and " + (V-1)); // } // // /** // * Unit tests the {@code DepthFirstOrder} data type. // * // * @param args the command-line arguments // */ // public static void main(String[] args) { // In in = new In(args[0]); // Digraph G = new Digraph(in); // // DepthFirstOrder dfs = new DepthFirstOrder(G); // StdOut.println(" v pre post"); // StdOut.println("--------------"); // for (int v = 0; v < G.V(); v++) { // StdOut.printf("%4d %4d %4d\n", v, dfs.pre(v), dfs.post(v)); // } // // StdOut.print("Preorder: "); // for (int v : dfs.pre()) { // StdOut.print(v + " "); // } // StdOut.println(); // // StdOut.print("Postorder: "); // for (int v : dfs.post()) { // StdOut.print(v + " "); // } // StdOut.println(); // // StdOut.print("Reverse postorder: "); // for (int v : dfs.reversePost()) { // StdOut.print(v + " "); // } // StdOut.println(); // // // } // //} // ///****************************************************************************** // * Copyright 2002-2016, Robert Sedgewick and Kevin Wayne. // * // * This file is part of algs4.jar, which accompanies the textbook // * // * Algorithms, 4th edition by Robert Sedgewick and Kevin Wayne, // * Addison-Wesley Professional, 2011, ISBN 0-321-57351-X. // * http://algs4.cs.princeton.edu // * // * // * algs4.jar is free software: you can redistribute it and/or modify // * it under the terms of the GNU General Public License as published by // * the Free Software Foundation, either version 3 of the License, or // * (at your option) any later version. // * // * algs4.jar is distributed in the hope that it will be useful, // * but WITHOUT ANY WARRANTY; without even the implied warranty of // * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // * GNU General Public License for more details. // * // * You should have received a copy of the GNU General Public License // * along with algs4.jar. If not, see http://www.gnu.org/licenses. // ******************************************************************************/ ================================================ FILE: code/algs4/Draw.java ================================================ /****************************************************************************** * Compilation: javac Draw.java * Execution: java Draw * Dependencies: none * * Drawing library. This class provides a basic capability for creating * drawings with your programs. It uses a simple graphics model that * allows you to create drawings consisting of points, lines, and curves * in a window on your computer and to save the drawings to a file. * This is the object-oriented version of standard draw; it supports * multiple indepedent drawing windows. * * Todo * ---- * - Add support for gradient fill, etc. * * Remarks * ------- * - don't use AffineTransform for rescaling since it inverts * images and strings * - careful using setFont in inner loop within an animation - * it can cause flicker * ******************************************************************************/ package algs4; import java.awt.BasicStroke; import java.awt.Color; import java.awt.FileDialog; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.RenderingHints; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.geom.Arc2D; import java.awt.geom.Ellipse2D; import java.awt.geom.GeneralPath; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.awt.image.DirectColorModel; import java.awt.image.WritableRaster; import java.io.File; import java.io.IOException; import java.net.URL; import java.util.ArrayList; import java.util.LinkedList; import java.util.TreeSet; import javax.imageio.ImageIO; import javax.swing.ImageIcon; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.KeyStroke; /** * Draw. This class provides a basic capability for * creating drawings with your programs. It uses a simple graphics model that * allows you to create drawings consisting of points, lines, and curves * in a window on your computer and to save the drawings to a file. * This is the object-oriented version of standard draw; it supports * multiple indepedent drawing windows. *

* For additional documentation, see Section 3.1 of * Computer Science: An Interdisciplinary Approach by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne */ public final class Draw implements ActionListener, MouseListener, MouseMotionListener, KeyListener { /** * The color black. */ public static final Color BLACK = Color.BLACK; /** * The color blue. */ public static final Color BLUE = Color.BLUE; /** * The color cyan. */ public static final Color CYAN = Color.CYAN; /** * The color dark gray. */ public static final Color DARK_GRAY = Color.DARK_GRAY; /** * The color gray. */ public static final Color GRAY = Color.GRAY; /** * The color green. */ public static final Color GREEN = Color.GREEN; /** * The color light gray. */ public static final Color LIGHT_GRAY = Color.LIGHT_GRAY; /** * The color magenta. */ public static final Color MAGENTA = Color.MAGENTA; /** * The color orange. */ public static final Color ORANGE = Color.ORANGE; /** * The color pink. */ public static final Color PINK = Color.PINK; /** * The color red. */ public static final Color RED = Color.RED; /** * The color white. */ public static final Color WHITE = Color.WHITE; /** * The color yellow. */ public static final Color YELLOW = Color.YELLOW; /** * Shade of blue used in Introduction to Programming in Java. * It is Pantone 300U. The RGB values are approximately (9, 90, 166). */ public static final Color BOOK_BLUE = new Color(9, 90, 166); /** * Shade of light blue used in Introduction to Programming in Java. * The RGB values are approximately (103, 198, 243). */ public static final Color BOOK_LIGHT_BLUE = new Color(103, 198, 243); /** * Shade of red used in Algorithms, 4th edition. * It is Pantone 1805U. The RGB values are approximately (150, 35, 31). */ public static final Color BOOK_RED = new Color(150, 35, 31); // default colors private static final Color DEFAULT_PEN_COLOR = BLACK; private static final Color DEFAULT_CLEAR_COLOR = WHITE; // boundary of drawing canvas, 0% border private static final double BORDER = 0.0; private static final double DEFAULT_XMIN = 0.0; private static final double DEFAULT_XMAX = 1.0; private static final double DEFAULT_YMIN = 0.0; private static final double DEFAULT_YMAX = 1.0; // default canvas size is SIZE-by-SIZE private static final int DEFAULT_SIZE = 512; // default pen radius private static final double DEFAULT_PEN_RADIUS = 0.002; // default font private static final Font DEFAULT_FONT = new Font("SansSerif", Font.PLAIN, 16); // current pen color private Color penColor; // canvas size private int width = DEFAULT_SIZE; private int height = DEFAULT_SIZE; // current pen radius private double penRadius; // show we draw immediately or wait until next show? private boolean defer = false; private double xmin, ymin, xmax, ymax; // name of window private String name = "Draw"; // for synchronization private final Object mouseLock = new Object(); private final Object keyLock = new Object(); // current font private Font font; // the JLabel for drawing private JLabel draw; // double buffered graphics private BufferedImage offscreenImage, onscreenImage; private Graphics2D offscreen, onscreen; // the frame for drawing to the screen private JFrame frame = new JFrame(); // mouse state private boolean mousePressed = false; private double mouseX = 0; private double mouseY = 0; // keyboard state private final LinkedList keysTyped = new LinkedList(); private final TreeSet keysDown = new TreeSet(); // event-based listeners private final ArrayList listeners = new ArrayList(); /** * Initializes an empty drawing object with the given name. * * @param name the title of the drawing window. */ public Draw(String name) { this.name = name; init(); } /** * Initializes an empty drawing object. */ public Draw() { init(); } private void init() { if (frame != null) frame.setVisible(false); frame = new JFrame(); offscreenImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); onscreenImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); offscreen = offscreenImage.createGraphics(); onscreen = onscreenImage.createGraphics(); setXscale(); setYscale(); offscreen.setColor(DEFAULT_CLEAR_COLOR); offscreen.fillRect(0, 0, width, height); setPenColor(); setPenRadius(); setFont(); clear(); // add antialiasing RenderingHints hints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); offscreen.addRenderingHints(hints); // frame stuff ImageIcon icon = new ImageIcon(onscreenImage); draw = new JLabel(icon); draw.addMouseListener(this); draw.addMouseMotionListener(this); frame.setContentPane(draw); frame.addKeyListener(this); // JLabel cannot get keyboard focus frame.setResizable(false); // frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // closes all windows frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); // closes only current window frame.setTitle(name); frame.setJMenuBar(createMenuBar()); frame.pack(); frame.requestFocusInWindow(); frame.setVisible(true); } /** * Sets the upper-left hand corner of the drawing window to be (x, y), where (0, 0) is upper left. * * @param x the number of pixels from the left * @param y the number of pixels from the top * @throws IllegalArgumentException if the width or height is 0 or negative */ public void setLocationOnScreen(int x, int y) { if (x <= 0 || y <= 0) throw new IllegalArgumentException(); frame.setLocation(x, y); } /** * Sets the default close operation. * * @param value the value, typically {@code JFrame.EXIT_ON_CLOSE} * (close all windows) or {@code JFrame.DISPOSE_ON_CLOSE} * (close current window) */ public void setDefaultCloseOperation(int value) { frame.setDefaultCloseOperation(value); } /** * Sets the canvas (drawing area) to be width-by-height pixels. * This also erases the current drawing and resets the coordinate system, pen radius, * pen color, and font back to their default values. * Ordinarly, this method is called once, at the very beginning of a program. * * @param canvasWidth the width as a number of pixels * @param canvasHeight the height as a number of pixels * @throws IllegalArgumentException unless both {@code canvasWidth} * and {@code canvasHeight} are positive */ public void setCanvasSize(int canvasWidth, int canvasHeight) { if (canvasWidth < 1 || canvasHeight < 1) { throw new IllegalArgumentException("width and height must be positive"); } width = canvasWidth; height = canvasHeight; init(); } // create the menu bar (changed to private) private JMenuBar createMenuBar() { JMenuBar menuBar = new JMenuBar(); JMenu menu = new JMenu("File"); menuBar.add(menu); JMenuItem menuItem1 = new JMenuItem(" Save... "); menuItem1.addActionListener(this); menuItem1.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); menu.add(menuItem1); return menuBar; } /*************************************************************************** * User and screen coordinate systems. ***************************************************************************/ /** * Sets the x-scale to be the default (between 0.0 and 1.0). */ public void setXscale() { setXscale(DEFAULT_XMIN, DEFAULT_XMAX); } /** * Sets the y-scale to be the default (between 0.0 and 1.0). */ public void setYscale() { setYscale(DEFAULT_YMIN, DEFAULT_YMAX); } /** * Sets the x-scale. * * @param min the minimum value of the x-scale * @param max the maximum value of the x-scale */ public void setXscale(double min, double max) { double size = max - min; xmin = min - BORDER * size; xmax = max + BORDER * size; } /** * Sets the y-scale. * * @param min the minimum value of the y-scale * @param max the maximum value of the y-scale */ public void setYscale(double min, double max) { double size = max - min; ymin = min - BORDER * size; ymax = max + BORDER * size; } // helper functions that scale from user coordinates to screen coordinates and back private double scaleX(double x) { return width * (x - xmin) / (xmax - xmin); } private double scaleY(double y) { return height * (ymax - y) / (ymax - ymin); } private double factorX(double w) { return w * width / Math.abs(xmax - xmin); } private double factorY(double h) { return h * height / Math.abs(ymax - ymin); } private double userX(double x) { return xmin + x * (xmax - xmin) / width; } private double userY(double y) { return ymax - y * (ymax - ymin) / height; } /** * Clears the screen to the default color (white). */ public void clear() { clear(DEFAULT_CLEAR_COLOR); } /** * Clears the screen to the given color. * * @param color the color to make the background */ public void clear(Color color) { offscreen.setColor(color); offscreen.fillRect(0, 0, width, height); offscreen.setColor(penColor); draw(); } /** * Gets the current pen radius. * * @return the current pen radius */ public double getPenRadius() { return penRadius; } /** * Sets the pen size to the default (.002). */ public void setPenRadius() { setPenRadius(DEFAULT_PEN_RADIUS); } /** * Sets the radius of the pen to the given size. * * @param r the radius of the pen * @throws IllegalArgumentException if r is negative */ public void setPenRadius(double r) { if (r < 0) throw new IllegalArgumentException("pen radius must be positive"); penRadius = r * DEFAULT_SIZE; BasicStroke stroke = new BasicStroke((float) penRadius, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND); // BasicStroke stroke = new BasicStroke((float) penRadius); offscreen.setStroke(stroke); } /** * Gets the current pen color. * * @return the current pen color */ public Color getPenColor() { return penColor; } /** * Sets the pen color to the default color (black). */ public void setPenColor() { setPenColor(DEFAULT_PEN_COLOR); } /** * Sets the pen color to the given color. * * @param color the color to make the pen */ public void setPenColor(Color color) { penColor = color; offscreen.setColor(penColor); } /** * Sets the pen color to the given RGB color. * * @param red the amount of red (between 0 and 255) * @param green the amount of green (between 0 and 255) * @param blue the amount of blue (between 0 and 255) * @throws IllegalArgumentException if the amount of red, green, or blue are outside prescribed range */ public void setPenColor(int red, int green, int blue) { if (red < 0 || red >= 256) throw new IllegalArgumentException("amount of red must be between 0 and 255"); if (green < 0 || green >= 256) throw new IllegalArgumentException("amount of red must be between 0 and 255"); if (blue < 0 || blue >= 256) throw new IllegalArgumentException("amount of red must be between 0 and 255"); setPenColor(new Color(red, green, blue)); } /** * Turns on xor mode. */ public void xorOn() { offscreen.setXORMode(DEFAULT_CLEAR_COLOR); } /** * Turns off xor mode. */ public void xorOff() { offscreen.setPaintMode(); } /** * Gets the current {@code JLabel} for use in some other GUI. * * @return the current {@code JLabel} */ public JLabel getJLabel() { return draw; } /** * Gets the current font. * * @return the current font */ public Font getFont() { return font; } /** * Sets the font to the default font (sans serif, 16 point). */ public void setFont() { setFont(DEFAULT_FONT); } /** * Sets the font to the given value. * * @param font the font */ public void setFont(Font font) { this.font = font; } /*************************************************************************** * Drawing geometric shapes. ***************************************************************************/ /** * Draws a line from (x0, y0) to (x1, y1). * * @param x0 the x-coordinate of the starting point * @param y0 the y-coordinate of the starting point * @param x1 the x-coordinate of the destination point * @param y1 the y-coordinate of the destination point */ public void line(double x0, double y0, double x1, double y1) { offscreen.draw(new Line2D.Double(scaleX(x0), scaleY(y0), scaleX(x1), scaleY(y1))); draw(); } /** * Draws one pixel at (x, y). * * @param x the x-coordinate of the pixel * @param y the y-coordinate of the pixel */ private void pixel(double x, double y) { offscreen.fillRect((int) Math.round(scaleX(x)), (int) Math.round(scaleY(y)), 1, 1); } /** * Draws a point at (x, y). * * @param x the x-coordinate of the point * @param y the y-coordinate of the point */ public void point(double x, double y) { double xs = scaleX(x); double ys = scaleY(y); double r = penRadius; // double ws = factorX(2*r); // double hs = factorY(2*r); // if (ws <= 1 && hs <= 1) pixel(x, y); if (r <= 1) pixel(x, y); else offscreen.fill(new Ellipse2D.Double(xs - r/2, ys - r/2, r, r)); draw(); } /** * Draws a circle of radius r, centered on (x, y). * * @param x the x-coordinate of the center of the circle * @param y the y-coordinate of the center of the circle * @param r the radius of the circle * @throws IllegalArgumentException if the radius of the circle is negative */ public void circle(double x, double y, double r) { if (r < 0) throw new IllegalArgumentException("circle radius can't be negative"); double xs = scaleX(x); double ys = scaleY(y); double ws = factorX(2*r); double hs = factorY(2*r); if (ws <= 1 && hs <= 1) pixel(x, y); else offscreen.draw(new Ellipse2D.Double(xs - ws/2, ys - hs/2, ws, hs)); draw(); } /** * Draws a filled circle of radius r, centered on (x, y). * * @param x the x-coordinate of the center of the circle * @param y the y-coordinate of the center of the circle * @param r the radius of the circle * @throws IllegalArgumentException if the radius of the circle is negative */ public void filledCircle(double x, double y, double r) { if (r < 0) throw new IllegalArgumentException("circle radius can't be negative"); double xs = scaleX(x); double ys = scaleY(y); double ws = factorX(2*r); double hs = factorY(2*r); if (ws <= 1 && hs <= 1) pixel(x, y); else offscreen.fill(new Ellipse2D.Double(xs - ws/2, ys - hs/2, ws, hs)); draw(); } /** * Draws an ellipse with given semimajor and semiminor axes, centered on (x, y). * * @param x the x-coordinate of the center of the ellipse * @param y the y-coordinate of the center of the ellipse * @param semiMajorAxis is the semimajor axis of the ellipse * @param semiMinorAxis is the semiminor axis of the ellipse * @throws IllegalArgumentException if either of the axes are negative */ public void ellipse(double x, double y, double semiMajorAxis, double semiMinorAxis) { if (semiMajorAxis < 0) throw new IllegalArgumentException("ellipse semimajor axis can't be negative"); if (semiMinorAxis < 0) throw new IllegalArgumentException("ellipse semiminor axis can't be negative"); double xs = scaleX(x); double ys = scaleY(y); double ws = factorX(2*semiMajorAxis); double hs = factorY(2*semiMinorAxis); if (ws <= 1 && hs <= 1) pixel(x, y); else offscreen.draw(new Ellipse2D.Double(xs - ws/2, ys - hs/2, ws, hs)); draw(); } /** * Draws an ellipse with given semimajor and semiminor axes, centered on (x, y). * @param x the x-coordinate of the center of the ellipse * @param y the y-coordinate of the center of the ellipse * @param semiMajorAxis is the semimajor axis of the ellipse * @param semiMinorAxis is the semiminor axis of the ellipse * @throws IllegalArgumentException if either of the axes are negative */ public void filledEllipse(double x, double y, double semiMajorAxis, double semiMinorAxis) { if (semiMajorAxis < 0) throw new IllegalArgumentException("ellipse semimajor axis can't be negative"); if (semiMinorAxis < 0) throw new IllegalArgumentException("ellipse semiminor axis can't be negative"); double xs = scaleX(x); double ys = scaleY(y); double ws = factorX(2*semiMajorAxis); double hs = factorY(2*semiMinorAxis); if (ws <= 1 && hs <= 1) pixel(x, y); else offscreen.fill(new Ellipse2D.Double(xs - ws/2, ys - hs/2, ws, hs)); draw(); } /** * Draws an arc of radius r, centered on (x, y), from angle1 to angle2 (in degrees). * * @param x the x-coordinate of the center of the circle * @param y the y-coordinate of the center of the circle * @param r the radius of the circle * @param angle1 the starting angle. 0 would mean an arc beginning at 3 o'clock. * @param angle2 the angle at the end of the arc. For example, if * you want a 90 degree arc, then angle2 should be angle1 + 90. * @throws IllegalArgumentException if the radius of the circle is negative */ public void arc(double x, double y, double r, double angle1, double angle2) { if (r < 0) throw new IllegalArgumentException("arc radius can't be negative"); while (angle2 < angle1) angle2 += 360; double xs = scaleX(x); double ys = scaleY(y); double ws = factorX(2*r); double hs = factorY(2*r); if (ws <= 1 && hs <= 1) pixel(x, y); else offscreen.draw(new Arc2D.Double(xs - ws/2, ys - hs/2, ws, hs, angle1, angle2 - angle1, Arc2D.OPEN)); draw(); } /** * Draws a square of side length 2r, centered on (x, y). * * @param x the x-coordinate of the center of the square * @param y the y-coordinate of the center of the square * @param r radius is half the length of any side of the square * @throws IllegalArgumentException if r is negative */ public void square(double x, double y, double r) { if (r < 0) throw new IllegalArgumentException("square side length can't be negative"); double xs = scaleX(x); double ys = scaleY(y); double ws = factorX(2*r); double hs = factorY(2*r); if (ws <= 1 && hs <= 1) pixel(x, y); else offscreen.draw(new Rectangle2D.Double(xs - ws/2, ys - hs/2, ws, hs)); draw(); } /** * Draws a filled square of side length 2r, centered on (x, y). * * @param x the x-coordinate of the center of the square * @param y the y-coordinate of the center of the square * @param r radius is half the length of any side of the square * @throws IllegalArgumentException if r is negative */ public void filledSquare(double x, double y, double r) { if (r < 0) throw new IllegalArgumentException("square side length can't be negative"); double xs = scaleX(x); double ys = scaleY(y); double ws = factorX(2*r); double hs = factorY(2*r); if (ws <= 1 && hs <= 1) pixel(x, y); else offscreen.fill(new Rectangle2D.Double(xs - ws/2, ys - hs/2, ws, hs)); draw(); } /** * Draws a rectangle of given half width and half height, centered on (x, y). * * @param x the x-coordinate of the center of the rectangle * @param y the y-coordinate of the center of the rectangle * @param halfWidth is half the width of the rectangle * @param halfHeight is half the height of the rectangle * @throws IllegalArgumentException if halfWidth or halfHeight is negative */ public void rectangle(double x, double y, double halfWidth, double halfHeight) { if (halfWidth < 0) throw new IllegalArgumentException("half width can't be negative"); if (halfHeight < 0) throw new IllegalArgumentException("half height can't be negative"); double xs = scaleX(x); double ys = scaleY(y); double ws = factorX(2*halfWidth); double hs = factorY(2*halfHeight); if (ws <= 1 && hs <= 1) pixel(x, y); else offscreen.draw(new Rectangle2D.Double(xs - ws/2, ys - hs/2, ws, hs)); draw(); } /** * Draws a filled rectangle of given half width and half height, centered on (x, y). * * @param x the x-coordinate of the center of the rectangle * @param y the y-coordinate of the center of the rectangle * @param halfWidth is half the width of the rectangle * @param halfHeight is half the height of the rectangle * @throws IllegalArgumentException if halfWidth or halfHeight is negative */ public void filledRectangle(double x, double y, double halfWidth, double halfHeight) { if (halfWidth < 0) throw new IllegalArgumentException("half width can't be negative"); if (halfHeight < 0) throw new IllegalArgumentException("half height can't be negative"); double xs = scaleX(x); double ys = scaleY(y); double ws = factorX(2*halfWidth); double hs = factorY(2*halfHeight); if (ws <= 1 && hs <= 1) pixel(x, y); else offscreen.fill(new Rectangle2D.Double(xs - ws/2, ys - hs/2, ws, hs)); draw(); } /** * Draws a polygon with the given (x[i], y[i]) coordinates. * * @param x an array of all the x-coordindates of the polygon * @param y an array of all the y-coordindates of the polygon */ public void polygon(double[] x, double[] y) { int n = x.length; GeneralPath path = new GeneralPath(); path.moveTo((float) scaleX(x[0]), (float) scaleY(y[0])); for (int i = 0; i < n; i++) path.lineTo((float) scaleX(x[i]), (float) scaleY(y[i])); path.closePath(); offscreen.draw(path); draw(); } /** * Draws a filled polygon with the given (x[i], y[i]) coordinates. * * @param x an array of all the x-coordindates of the polygon * @param y an array of all the y-coordindates of the polygon */ public void filledPolygon(double[] x, double[] y) { int n = x.length; GeneralPath path = new GeneralPath(); path.moveTo((float) scaleX(x[0]), (float) scaleY(y[0])); for (int i = 0; i < n; i++) path.lineTo((float) scaleX(x[i]), (float) scaleY(y[i])); path.closePath(); offscreen.fill(path); draw(); } /*************************************************************************** * Drawing images. ***************************************************************************/ private static BufferedImage getImage(String filename) { // from a file or URL try { URL url = new URL(filename); return ImageIO.read(url); } catch (IOException e) { // ignore } // in case file is inside a .jar (classpath relative to StdDraw) try { URL url = StdDraw.class.getResource(filename); return ImageIO.read(url); } catch (IOException e) { // ignore } // in case file is inside a .jar (classpath relative to root of jar) try { URL url = StdDraw.class.getResource("/" + filename); return ImageIO.read(url); } catch (IOException e) { // ignore } throw new IllegalArgumentException("image " + filename + " not found"); } /** * Draws picture (gif, jpg, or png) centered on (x, y). * * @param x the center x-coordinate of the image * @param y the center y-coordinate of the image * @param filename the name of the image/picture, e.g., "ball.gif" * @throws IllegalArgumentException if the image is corrupt * @throws IllegalArgumentException if {@code filename} is {@code null} */ public void picture(double x, double y, String filename) { if (filename == null) throw new IllegalArgumentException("filename argument is null"); BufferedImage image = getImage(filename); double xs = scaleX(x); double ys = scaleY(y); int ws = image.getWidth(); int hs = image.getHeight(); if (ws < 0 || hs < 0) throw new IllegalArgumentException("image " + filename + " is corrupt"); offscreen.drawImage(image, (int) Math.round(xs - ws/2.0), (int) Math.round(ys - hs/2.0), null); draw(); } /** * Draws picture (gif, jpg, or png) centered on (x, y), * rotated given number of degrees. * * @param x the center x-coordinate of the image * @param y the center y-coordinate of the image * @param filename the name of the image/picture, e.g., "ball.gif" * @param degrees is the number of degrees to rotate counterclockwise * @throws IllegalArgumentException if the image is corrupt * @throws IllegalArgumentException if {@code filename} is {@code null} */ public void picture(double x, double y, String filename, double degrees) { if (filename == null) throw new IllegalArgumentException("filename argument is null"); BufferedImage image = getImage(filename); double xs = scaleX(x); double ys = scaleY(y); int ws = image.getWidth(); int hs = image.getHeight(); if (ws < 0 || hs < 0) throw new IllegalArgumentException("image " + filename + " is corrupt"); offscreen.rotate(Math.toRadians(-degrees), xs, ys); offscreen.drawImage(image, (int) Math.round(xs - ws/2.0), (int) Math.round(ys - hs/2.0), null); offscreen.rotate(Math.toRadians(+degrees), xs, ys); draw(); } /** * Draws picture (gif, jpg, or png) centered on (x, y), rescaled to w-by-h. * * @param x the center x coordinate of the image * @param y the center y coordinate of the image * @param filename the name of the image/picture, e.g., "ball.gif" * @param w the width of the image * @param h the height of the image * @throws IllegalArgumentException if the image is corrupt * @throws IllegalArgumentException if {@code filename} is {@code null} */ public void picture(double x, double y, String filename, double w, double h) { if (filename == null) throw new IllegalArgumentException("filename argument is null"); Image image = getImage(filename); double xs = scaleX(x); double ys = scaleY(y); double ws = factorX(w); double hs = factorY(h); if (ws < 0 || hs < 0) throw new IllegalArgumentException("image " + filename + " is corrupt"); if (ws <= 1 && hs <= 1) pixel(x, y); else { offscreen.drawImage(image, (int) Math.round(xs - ws/2.0), (int) Math.round(ys - hs/2.0), (int) Math.round(ws), (int) Math.round(hs), null); } draw(); } /** * Draws picture (gif, jpg, or png) centered on (x, y), rotated * given number of degrees, rescaled to w-by-h. * * @param x the center x-coordinate of the image * @param y the center y-coordinate of the image * @param filename the name of the image/picture, e.g., "ball.gif" * @param w the width of the image * @param h the height of the image * @param degrees is the number of degrees to rotate counterclockwise * @throws IllegalArgumentException if the image is corrupt * @throws IllegalArgumentException if {@code filename} is {@code null} */ public void picture(double x, double y, String filename, double w, double h, double degrees) { if (filename == null) throw new IllegalArgumentException("filename argument is null"); Image image = getImage(filename); double xs = scaleX(x); double ys = scaleY(y); double ws = factorX(w); double hs = factorY(h); if (ws < 0 || hs < 0) throw new IllegalArgumentException("image " + filename + " is corrupt"); if (ws <= 1 && hs <= 1) pixel(x, y); offscreen.rotate(Math.toRadians(-degrees), xs, ys); offscreen.drawImage(image, (int) Math.round(xs - ws/2.0), (int) Math.round(ys - hs/2.0), (int) Math.round(ws), (int) Math.round(hs), null); offscreen.rotate(Math.toRadians(+degrees), xs, ys); draw(); } /*************************************************************************** * Drawing text. ***************************************************************************/ /** * Writes the given text string in the current font, centered on (x, y). * * @param x the center x-coordinate of the text * @param y the center y-coordinate of the text * @param s the text */ public void text(double x, double y, String s) { offscreen.setFont(font); FontMetrics metrics = offscreen.getFontMetrics(); double xs = scaleX(x); double ys = scaleY(y); int ws = metrics.stringWidth(s); int hs = metrics.getDescent(); offscreen.drawString(s, (float) (xs - ws/2.0), (float) (ys + hs)); draw(); } /** * Writes the given text string in the current font, centered on (x, y) and * rotated by the specified number of degrees. * * @param x the center x-coordinate of the text * @param y the center y-coordinate of the text * @param s the text * @param degrees is the number of degrees to rotate counterclockwise */ public void text(double x, double y, String s, double degrees) { double xs = scaleX(x); double ys = scaleY(y); offscreen.rotate(Math.toRadians(-degrees), xs, ys); text(x, y, s); offscreen.rotate(Math.toRadians(+degrees), xs, ys); } /** * Writes the given text string in the current font, left-aligned at (x, y). * * @param x the x-coordinate of the text * @param y the y-coordinate of the text * @param s the text */ public void textLeft(double x, double y, String s) { offscreen.setFont(font); FontMetrics metrics = offscreen.getFontMetrics(); double xs = scaleX(x); double ys = scaleY(y); // int ws = metrics.stringWidth(s); int hs = metrics.getDescent(); offscreen.drawString(s, (float) xs, (float) (ys + hs)); draw(); } /** * Displays on screen, pause for {@code t} milliseconds, and turn on * animation mode. * Subsequent calls to drawing methods such as {@code line()}, {@code circle()}, * and {@code square()} will not be displayed on screen until the next call to {@code show()}. * This is useful for producing animations (clear the screen, draw a bunch of shapes, * display on screen for a fixed amount of time, and repeat). It also speeds up * drawing a huge number of shapes (call {@code show(0)} to defer drawing * on screen, draw the shapes, and call {@code show(0)} to display them all * on screen at once). * * @param t number of milliseconds */ public void show(int t) { defer = false; draw(); try { Thread.sleep(t); } catch (InterruptedException e) { System.out.println("Error sleeping"); } defer = true; } /** * Displays on-screen and turn off animation mode. * Subsequent calls to drawing methods such as {@code line()}, {@code circle()}, * and {@code square()} will be displayed on screen when called. This is the default. */ public void show() { defer = false; draw(); } // draw onscreen if defer is false private void draw() { if (defer) return; onscreen.drawImage(offscreenImage, 0, 0, null); frame.repaint(); } /** * Saves this drawing to a file. * * @param filename the name of the file (with suffix png, jpg, or gif) */ public void save(String filename) { File file = new File(filename); String suffix = filename.substring(filename.lastIndexOf('.') + 1); // png files if ("png".equalsIgnoreCase(suffix)) { try { ImageIO.write(offscreenImage, suffix, file); } catch (IOException e) { e.printStackTrace(); } } // need to change from ARGB to RGB for jpeg // reference: http://archives.java.sun.com/cgi-bin/wa?A2=ind0404&L=java2d-interest&D=0&P=2727 else if ("jpg".equalsIgnoreCase(suffix)) { WritableRaster raster = offscreenImage.getRaster(); WritableRaster newRaster; newRaster = raster.createWritableChild(0, 0, width, height, 0, 0, new int[] {0, 1, 2}); DirectColorModel cm = (DirectColorModel) offscreenImage.getColorModel(); DirectColorModel newCM = new DirectColorModel(cm.getPixelSize(), cm.getRedMask(), cm.getGreenMask(), cm.getBlueMask()); BufferedImage rgbBuffer = new BufferedImage(newCM, newRaster, false, null); try { ImageIO.write(rgbBuffer, suffix, file); } catch (IOException e) { e.printStackTrace(); } } else { System.out.println("Invalid image file type: " + suffix); } } /** * This method cannot be called directly. */ @Override public void actionPerformed(ActionEvent e) { FileDialog chooser = new FileDialog(frame, "Use a .png or .jpg extension", FileDialog.SAVE); chooser.setVisible(true); String filename = chooser.getFile(); if (filename != null) { save(chooser.getDirectory() + File.separator + chooser.getFile()); } } /*************************************************************************** * Event-based interactions. ***************************************************************************/ /** * Adds a {@link DrawListener} to listen to keyboard and mouse events. * * @param listener the {\tt DrawListener} argument */ public void addListener(DrawListener listener) { // ensure there is a window for listenting to events show(); listeners.add(listener); frame.addKeyListener(this); frame.addMouseListener(this); frame.addMouseMotionListener(this); frame.setFocusable(true); } /*************************************************************************** * Mouse interactions. ***************************************************************************/ /** * Returns true if the mouse is being pressed. * * @return {@code true} if the mouse is being pressed; * {@code false} otherwise */ public boolean mousePressed() { synchronized (mouseLock) { return mousePressed; } } /** * Returns the x-coordinate of the mouse. * @return the x-coordinate of the mouse */ public double mouseX() { synchronized (mouseLock) { return mouseX; } } /** * Returns the y-coordinate of the mouse. * * @return the y-coordinate of the mouse */ public double mouseY() { synchronized (mouseLock) { return mouseY; } } /** * This method cannot be called directly. */ @Override public void mouseClicked(MouseEvent e) { // this body is intentionally left empty } /** * This method cannot be called directly. */ @Override public void mouseEntered(MouseEvent e) { // this body is intentionally left empty } /** * This method cannot be called directly. */ @Override public void mouseExited(MouseEvent e) { // this body is intentionally left empty } /** * This method cannot be called directly. */ @Override public void mousePressed(MouseEvent e) { synchronized (mouseLock) { mouseX = userX(e.getX()); mouseY = userY(e.getY()); mousePressed = true; } if (e.getButton() == MouseEvent.BUTTON1) { for (DrawListener listener : listeners) listener.mousePressed(userX(e.getX()), userY(e.getY())); } } /** * This method cannot be called directly. */ @Override public void mouseReleased(MouseEvent e) { synchronized (mouseLock) { mousePressed = false; } if (e.getButton() == MouseEvent.BUTTON1) { for (DrawListener listener : listeners) listener.mouseReleased(userX(e.getX()), userY(e.getY())); } } /** * This method cannot be called directly. */ @Override public void mouseDragged(MouseEvent e) { synchronized (mouseLock) { mouseX = userX(e.getX()); mouseY = userY(e.getY()); } // doesn't seem to work if a button is specified for (DrawListener listener : listeners) listener.mouseDragged(userX(e.getX()), userY(e.getY())); } /** * This method cannot be called directly. */ @Override public void mouseMoved(MouseEvent e) { synchronized (mouseLock) { mouseX = userX(e.getX()); mouseY = userY(e.getY()); } } /*************************************************************************** * Keyboard interactions. ***************************************************************************/ /** * Returns true if the user has typed a key. * * @return {@code true} if the user has typed a key; {@code false} otherwise */ public boolean hasNextKeyTyped() { synchronized (keyLock) { return !keysTyped.isEmpty(); } } /** * The next key typed by the user. * * @return the next key typed by the user */ public char nextKeyTyped() { synchronized (keyLock) { return keysTyped.removeLast(); } } /** * Returns true if the keycode is being pressed. *

* This method takes as an argument the keycode (corresponding to a physical key). * It can handle action keys (such as F1 and arrow keys) and modifier keys * (such as shift and control). * See {@link KeyEvent} for a description of key codes. * * @param keycode the keycode to check * @return {@code true} if {@code keycode} is currently being pressed; * {@code false} otherwise */ public boolean isKeyPressed(int keycode) { synchronized (keyLock) { return keysDown.contains(keycode); } } /** * This method cannot be called directly. */ @Override public void keyTyped(KeyEvent e) { synchronized (keyLock) { keysTyped.addFirst(e.getKeyChar()); } // notify all listeners for (DrawListener listener : listeners) listener.keyTyped(e.getKeyChar()); } /** * This method cannot be called directly. */ @Override public void keyPressed(KeyEvent e) { synchronized (keyLock) { keysDown.add(e.getKeyCode()); } // notify all listeners for (DrawListener listener : listeners) listener.keyPressed(e.getKeyCode()); } /** * This method cannot be called directly. */ @Override public void keyReleased(KeyEvent e) { synchronized (keyLock) { keysDown.remove(e.getKeyCode()); } // notify all listeners for (DrawListener listener : listeners) listener.keyPressed(e.getKeyCode()); } /** * Test client. * * @param args the command-line arguments */ public static void main(String[] args) { // create one drawing window Draw draw1 = new Draw("Test client 1"); draw1.square(.2, .8, .1); draw1.filledSquare(.8, .8, .2); draw1.circle(.8, .2, .2); draw1.setPenColor(Draw.MAGENTA); draw1.setPenRadius(.02); draw1.arc(.8, .2, .1, 200, 45); // create another one Draw draw2 = new Draw("Test client 2"); draw2.setCanvasSize(900, 200); // draw a blue diamond draw2.setPenRadius(); draw2.setPenColor(Draw.BLUE); double[] x = { .1, .2, .3, .2 }; double[] y = { .2, .3, .2, .1 }; draw2.filledPolygon(x, y); // text draw2.setPenColor(Draw.BLACK); draw2.text(0.2, 0.5, "bdfdfdfdlack text"); draw2.setPenColor(Draw.WHITE); draw2.text(0.8, 0.8, "white text"); } } /****************************************************************************** * Copyright 2002-2016, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * * Algorithms, 4th edition by Robert Sedgewick and Kevin Wayne, * Addison-Wesley Professional, 2011, ISBN 0-321-57351-X. * http://algs4.cs.princeton.edu * * * algs4.jar is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * algs4.jar is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with algs4.jar. If not, see http://www.gnu.org/licenses. ******************************************************************************/ ================================================ FILE: code/algs4/DrawListener.java ================================================ /****************************************************************************** * Compilation: javac DrawListener.java * Execution: none * Dependencies: none * * Interface that accompanies Draw.java. ******************************************************************************/ package algs4; public interface DrawListener { /** * Invoked when the mouse has been pressed. * * @param x the x-coordinate of the mouse * @param y the y-coordinate of the mouse */ void mousePressed(double x, double y); /** * Invoked when the mouse has been dragged. * * @param x the x-coordinate of the mouse * @param y the y-coordinate of the mouse */ void mouseDragged(double x, double y); /** * Invoked when the mouse has been released. * * @param x the x-coordinate of the mouse * @param y the y-coordinate of the mouse */ void mouseReleased(double x, double y); /** * Invoked when a key has been typed. * * @param c the character typed */ void keyTyped(char c); /** * Invoked when a key has been pressed. * * @param keycode the key combination pressed */ void keyPressed(int keycode); /** * Invoked when a key has been released. * * @param keycode the key combination released */ void keyReleased(int keycode); } /****************************************************************************** * Copyright 2002-2016, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * * Algorithms, 4th edition by Robert Sedgewick and Kevin Wayne, * Addison-Wesley Professional, 2011, ISBN 0-321-57351-X. * http://algs4.cs.princeton.edu * * * algs4.jar is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * algs4.jar is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with algs4.jar. If not, see http://www.gnu.org/licenses. ******************************************************************************/ ================================================ FILE: code/algs4/Heap.java ================================================ /****************************************************************************** * Compilation: javac Heap.java * Execution: java Heap < input.txt * Dependencies: StdOut.java StdIn.java * Data files: http://algs4.cs.princeton.edu/24pq/tiny.txt * http://algs4.cs.princeton.edu/24pq/words3.txt * * Sorts a sequence of strings from standard input using heapsort. * * % more tiny.txt * S O R T E X A M P L E * * % java Heap < tiny.txt * A E E L M O P R S T X [ one string per line ] * * % more words3.txt * bed bug dad yes zoo ... all bad yet * * % java Heap < words3.txt * all bad bed bug dad ... yes yet zoo [ one string per line ] * ******************************************************************************/ package algs4; /** * The {@code Heap} class provides a static methods for heapsorting * an array. *

* For additional documentation, see Section 2.4 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne */ public class Heap { // This class should not be instantiated. private Heap() { } /** * Rearranges the array in ascending order, using the natural order. * @param pq the array to be sorted */ public static void sort(Comparable[] pq) { int n = pq.length; for (int k = n/2; k >= 1; k--) sink(pq, k, n); while (n > 1) { exch(pq, 1, n--); sink(pq, 1, n); } } /*************************************************************************** * Helper functions to restore the heap invariant. ***************************************************************************/ private static void sink(Comparable[] pq, int k, int n) { while (2*k <= n) { int j = 2*k; if (j < n && less(pq, j, j+1)) j++; if (!less(pq, k, j)) break; exch(pq, k, j); k = j; } } /*************************************************************************** * Helper functions for comparisons and swaps. * Indices are "off-by-one" to support 1-based indexing. ***************************************************************************/ private static boolean less(Comparable[] pq, int i, int j) { return pq[i-1].compareTo(pq[j-1]) < 0; } private static void exch(Object[] pq, int i, int j) { Object swap = pq[i-1]; pq[i-1] = pq[j-1]; pq[j-1] = swap; } // is v < w ? private static boolean less(Comparable v, Comparable w) { return v.compareTo(w) < 0; } /*************************************************************************** * Check if array is sorted - useful for debugging. ***************************************************************************/ private static boolean isSorted(Comparable[] a) { for (int i = 1; i < a.length; i++) if (less(a[i], a[i-1])) return false; return true; } // print array to standard output private static void show(Comparable[] a) { for (int i = 0; i < a.length; i++) { StdOut.println(a[i]); } } /** * Reads in a sequence of strings from standard input; heapsorts them; * and prints them to standard output in ascending order. * * @param args the command-line arguments */ public static void main(String[] args) { String[] a = StdIn.readAllStrings(); Heap.sort(a); show(a); assert isSorted(a); } } /****************************************************************************** * Copyright 2002-2016, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * * Algorithms, 4th edition by Robert Sedgewick and Kevin Wayne, * Addison-Wesley Professional, 2011, ISBN 0-321-57351-X. * http://algs4.cs.princeton.edu * * * algs4.jar is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * algs4.jar is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with algs4.jar. If not, see http://www.gnu.org/licenses. ******************************************************************************/ ================================================ FILE: code/algs4/HexDump.java ================================================ /****************************************************************************** * Compilation: javac HexDump.java * Execution: java HexDump < file * Dependencies: BinaryStdIn.java StdOut.java * Data file: http://algs4.cs.princeton.edu/55compression/abra.txt * * Reads in a binary file and writes out the bytes in hex, 16 per line. * * % more abra.txt * ABRACADABRA! * * % java HexDump 16 < abra.txt * 41 42 52 41 43 41 44 41 42 52 41 21 * 96 bits * * * Remark * -------------------------- * - Similar to the Unix utilities od (octal dump) or hexdump (hexadecimal dump). * * % od -t x1 < abra.txt * 0000000 41 42 52 41 43 41 44 41 42 52 41 21 * 0000014 * ******************************************************************************/ package algs4; /** * The {@code HexDump} class provides a client for displaying the contents * of a binary file in hexadecimal. *

* For additional documentation, * see Section 5.5 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. *

* See also {@link BinaryDump} and {@link PictureDump}. * For more full-featured versions, see the Unix utilities * {@code od} (octal dump) and {@code hexdump} (hexadecimal dump). *

* * @author Robert Sedgewick * @author Kevin Wayne */ public class HexDump { // Do not instantiate. private HexDump() { } /** * Reads in a sequence of bytes from standard input and writes * them to standard output using hexademical notation, k hex digits * per line, where k is given as a command-line integer (defaults * to 16 if no integer is specified); also writes the number * of bits. * * @param args the command-line arguments */ public static void main(String[] args) { int bytesPerLine = 16; if (args.length == 1) { bytesPerLine = Integer.parseInt(args[0]); } int i; for (i = 0; !BinaryStdIn.isEmpty(); i++) { if (bytesPerLine == 0) { BinaryStdIn.readChar(); continue; } if (i == 0) StdOut.printf(""); else if (i % bytesPerLine == 0) StdOut.printf("\n", i); else StdOut.print(" "); char c = BinaryStdIn.readChar(); StdOut.printf("%02x", c & 0xff); } if (bytesPerLine != 0) StdOut.println(); StdOut.println((i*8) + " bits"); } } /****************************************************************************** * Copyright 2002-2016, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * * Algorithms, 4th edition by Robert Sedgewick and Kevin Wayne, * Addison-Wesley Professional, 2011, ISBN 0-321-57351-X. * http://algs4.cs.princeton.edu * * * algs4.jar is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * algs4.jar is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with algs4.jar. If not, see http://www.gnu.org/licenses. ******************************************************************************/ ================================================ FILE: code/algs4/In.java ================================================ /****************************************************************************** * Compilation: javac In.java * Execution: java In (basic test --- see source for required files) * Dependencies: none * * Reads in data of various types from standard input, files, and URLs. * ******************************************************************************/ package algs4; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.net.Socket; // import java.net.HttpURLConnection; import java.net.URLConnection; import java.util.ArrayList; import java.util.InputMismatchException; import java.util.Locale; import java.util.NoSuchElementException; import java.util.Scanner; import java.util.regex.Pattern; /** * Input. This class provides methods for reading strings * and numbers from standard input, file input, URLs, and sockets. *

* The Locale used is: language = English, country = US. This is consistent * with the formatting conventions with Java floating-point literals, * command-line arguments (via {@link Double#parseDouble(String)}) * and standard output. *

* For additional documentation, see * Section 3.1 of * Computer Science: An Interdisciplinary Approach * by Robert Sedgewick and Kevin Wayne. *

* Like {@link Scanner}, reading a token also consumes preceding Java * whitespace, reading a full line consumes * the following end-of-line delimeter, while reading a character consumes * nothing extra. *

* Whitespace is defined in {@link Character#isWhitespace(char)}. Newlines * consist of \n, \r, \r\n, and Unicode hex code points 0x2028, 0x2029, 0x0085; * see * Scanner.java (NB: Java 6u23 and earlier uses only \r, \r, \r\n). * * @author David Pritchard * @author Robert Sedgewick * @author Kevin Wayne */ public final class In { ///// begin: section (1 of 2) of code duplicated from In to StdIn. // assume Unicode UTF-8 encoding private static final String CHARSET_NAME = "UTF-8"; // assume language = English, country = US for consistency with System.out. private static final Locale LOCALE = Locale.US; // the default token separator; we maintain the invariant that this value // is held by the scanner's delimiter between calls private static final Pattern WHITESPACE_PATTERN = Pattern.compile("\\p{javaWhitespace}+"); // makes whitespace characters significant private static final Pattern EMPTY_PATTERN = Pattern.compile(""); // used to read the entire input. source: // http://weblogs.java.net/blog/pat/archive/2004/10/stupid_scanner_1.html private static final Pattern EVERYTHING_PATTERN = Pattern.compile("\\A"); //// end: section (1 of 2) of code duplicated from In to StdIn. private Scanner scanner; /** * Initializes an input stream from standard input. */ public In() { scanner = new Scanner(new BufferedInputStream(System.in), CHARSET_NAME); scanner.useLocale(LOCALE); } /** * Initializes an input stream from a socket. * * @param socket the socket * @throws IllegalArgumentException if cannot open {@code socket} * @throws IllegalArgumentException if {@code socket} is {@code null} */ public In(Socket socket) { if (socket == null) throw new IllegalArgumentException("socket argument is null"); try { InputStream is = socket.getInputStream(); scanner = new Scanner(new BufferedInputStream(is), CHARSET_NAME); scanner.useLocale(LOCALE); } catch (IOException ioe) { throw new IllegalArgumentException("Could not open " + socket, ioe); } } /** * Initializes an input stream from a URL. * * @param url the URL * @throws IllegalArgumentException if cannot open {@code url} * @throws IllegalArgumentException if {@code url} is {@code null} */ public In(URL url) { if (url == null) throw new IllegalArgumentException("url argument is null"); try { URLConnection site = url.openConnection(); InputStream is = site.getInputStream(); scanner = new Scanner(new BufferedInputStream(is), CHARSET_NAME); scanner.useLocale(LOCALE); } catch (IOException ioe) { throw new IllegalArgumentException("Could not open " + url, ioe); } } /** * Initializes an input stream from a file. * * @param file the file * @throws IllegalArgumentException if cannot open {@code file} * @throws IllegalArgumentException if {@code file} is {@code null} */ public In(File file) { if (file == null) throw new IllegalArgumentException("file argument is null"); try { // for consistency with StdIn, wrap with BufferedInputStream instead of use // file as argument to Scanner FileInputStream fis = new FileInputStream(file); scanner = new Scanner(new BufferedInputStream(fis), CHARSET_NAME); scanner.useLocale(LOCALE); } catch (IOException ioe) { throw new IllegalArgumentException("Could not open " + file, ioe); } } /** * Initializes an input stream from a filename or web page name. * * @param name the filename or web page name * @throws IllegalArgumentException if cannot open {@code name} as * a file or URL * @throws IllegalArgumentException if {@code name} is {@code null} */ public In(String name) { if (name == null) throw new IllegalArgumentException("argument is null"); try { // first try to read file from local file system File file = new File(name); if (file.exists()) { // for consistency with StdIn, wrap with BufferedInputStream instead of use // file as argument to Scanner FileInputStream fis = new FileInputStream(file); scanner = new Scanner(new BufferedInputStream(fis), CHARSET_NAME); scanner.useLocale(LOCALE); return; } // next try for files included in jar URL url = getClass().getResource(name); // or URL from web if (url == null) { url = new URL(name); } URLConnection site = url.openConnection(); // in order to set User-Agent, replace above line with these two // HttpURLConnection site = (HttpURLConnection) url.openConnection(); // site.addRequestProperty("User-Agent", "Mozilla/4.76"); InputStream is = site.getInputStream(); scanner = new Scanner(new BufferedInputStream(is), CHARSET_NAME); scanner.useLocale(LOCALE); } catch (IOException ioe) { throw new IllegalArgumentException("Could not open " + name, ioe); } } /** * Initializes an input stream from a given {@link Scanner} source; use with * {@code new Scanner(String)} to read from a string. *

* Note that this does not create a defensive copy, so the * scanner will be mutated as you read on. * * @param scanner the scanner * @throws IllegalArgumentException if {@code scanner} is {@code null} */ public In(Scanner scanner) { if (scanner == null) throw new IllegalArgumentException("scanner argument is null"); this.scanner = scanner; } /** * Returns true if this input stream exists. * * @return {@code true} if this input stream exists; {@code false} otherwise */ public boolean exists() { return scanner != null; } //// begin: section (2 of 2) of code duplicated from In to StdIn, //// with all methods changed from "public" to "public static". /** * Returns true if input stream is empty (except possibly whitespace). * Use this to know whether the next call to {@link #readString()}, * {@link #readDouble()}, etc will succeed. * * @return {@code true} if this input stream is empty (except possibly whitespace); * {@code false} otherwise */ public boolean isEmpty() { return !scanner.hasNext(); } /** * Returns true if this input stream has a next line. * Use this method to know whether the * next call to {@link #readLine()} will succeed. * This method is functionally equivalent to {@link #hasNextChar()}. * * @return {@code true} if this input stream has more input (including whitespace); * {@code false} otherwise */ public boolean hasNextLine() { return scanner.hasNextLine(); } /** * Returns true if this input stream has more inputy (including whitespace). * Use this method to know whether the next call to {@link #readChar()} will succeed. * This method is functionally equivalent to {@link #hasNextLine()}. * * @return {@code true} if this input stream has more input (including whitespace); * {@code false} otherwise */ public boolean hasNextChar() { scanner.useDelimiter(EMPTY_PATTERN); boolean result = scanner.hasNext(); scanner.useDelimiter(WHITESPACE_PATTERN); return result; } /** * Reads and returns the next line in this input stream. * * @return the next line in this input stream; {@code null} if no such line */ public String readLine() { String line; try { line = scanner.nextLine(); } catch (NoSuchElementException e) { line = null; } return line; } /** * Reads and returns the next character in this input stream. * * @return the next character in this input stream */ public char readChar() { scanner.useDelimiter(EMPTY_PATTERN); String ch = scanner.next(); assert ch.length() == 1 : "Internal (Std)In.readChar() error!" + " Please contact the authors."; scanner.useDelimiter(WHITESPACE_PATTERN); return ch.charAt(0); } /** * Reads and returns the remainder of this input stream, as a string. * * @return the remainder of this input stream, as a string */ public String readAll() { if (!scanner.hasNextLine()) return ""; String result = scanner.useDelimiter(EVERYTHING_PATTERN).next(); // not that important to reset delimeter, since now scanner is empty scanner.useDelimiter(WHITESPACE_PATTERN); // but let's do it anyway return result; } /** * Reads the next token from this input stream and returns it as a {@code String}. * * @return the next {@code String} in this input stream */ public String readString() { return scanner.next(); } /** * Reads the next token from this input stream, parses it as a {@code int}, * and returns the {@code int}. * * @return the next {@code int} in this input stream */ public int readInt() { return scanner.nextInt(); } /** * Reads the next token from this input stream, parses it as a {@code double}, * and returns the {@code double}. * * @return the next {@code double} in this input stream */ public double readDouble() { return scanner.nextDouble(); } /** * Reads the next token from this input stream, parses it as a {@code float}, * and returns the {@code float}. * * @return the next {@code float} in this input stream */ public float readFloat() { return scanner.nextFloat(); } /** * Reads the next token from this input stream, parses it as a {@code long}, * and returns the {@code long}. * * @return the next {@code long} in this input stream */ public long readLong() { return scanner.nextLong(); } /** * Reads the next token from this input stream, parses it as a {@code short}, * and returns the {@code short}. * * @return the next {@code short} in this input stream */ public short readShort() { return scanner.nextShort(); } /** * Reads the next token from this input stream, parses it as a {@code byte}, * and returns the {@code byte}. *

* To read binary data, use {@link BinaryIn}. * * @return the next {@code byte} in this input stream */ public byte readByte() { return scanner.nextByte(); } /** * Reads the next token from this input stream, parses it as a {@code boolean} * (interpreting either {@code "true"} or {@code "1"} as {@code true}, * and either {@code "false"} or {@code "0"} as {@code false}). * * @return the next {@code boolean} in this input stream */ public boolean readBoolean() { String s = readString(); if ("true".equalsIgnoreCase(s)) return true; if ("false".equalsIgnoreCase(s)) return false; if ("1".equals(s)) return true; if ("0".equals(s)) return false; throw new InputMismatchException(); } /** * Reads all remaining tokens from this input stream and returns them as * an array of strings. * * @return all remaining tokens in this input stream, as an array of strings */ public String[] readAllStrings() { // we could use readAll.trim().split(), but that's not consistent // since trim() uses characters 0x00..0x20 as whitespace String[] tokens = WHITESPACE_PATTERN.split(readAll()); if (tokens.length == 0 || tokens[0].length() > 0) return tokens; String[] decapitokens = new String[tokens.length-1]; for (int i = 0; i < tokens.length-1; i++) decapitokens[i] = tokens[i+1]; return decapitokens; } /** * Reads all remaining lines from this input stream and returns them as * an array of strings. * * @return all remaining lines in this input stream, as an array of strings */ public String[] readAllLines() { ArrayList lines = new ArrayList(); while (hasNextLine()) { lines.add(readLine()); } return lines.toArray(new String[lines.size()]); } /** * Reads all remaining tokens from this input stream, parses them as integers, * and returns them as an array of integers. * * @return all remaining lines in this input stream, as an array of integers */ public int[] readAllInts() { String[] fields = readAllStrings(); int[] vals = new int[fields.length]; for (int i = 0; i < fields.length; i++) vals[i] = Integer.parseInt(fields[i]); return vals; } /** * Reads all remaining tokens from this input stream, parses them as longs, * and returns them as an array of longs. * * @return all remaining lines in this input stream, as an array of longs */ public long[] readAllLongs() { String[] fields = readAllStrings(); long[] vals = new long[fields.length]; for (int i = 0; i < fields.length; i++) vals[i] = Long.parseLong(fields[i]); return vals; } /** * Reads all remaining tokens from this input stream, parses them as doubles, * and returns them as an array of doubles. * * @return all remaining lines in this input stream, as an array of doubles */ public double[] readAllDoubles() { String[] fields = readAllStrings(); double[] vals = new double[fields.length]; for (int i = 0; i < fields.length; i++) vals[i] = Double.parseDouble(fields[i]); return vals; } ///// end: section (2 of 2) of code duplicated from In to StdIn */ /** * Closes this input stream. */ public void close() { scanner.close(); } /** * Reads all integers from a file and returns them as * an array of integers. * * @param filename the name of the file * @return the integers in the file * @deprecated Replaced by {@code new In(filename)}.{@link #readAllInts()}. */ @Deprecated public static int[] readInts(String filename) { return new In(filename).readAllInts(); } /** * Reads all doubles from a file and returns them as * an array of doubles. * * @param filename the name of the file * @return the doubles in the file * @deprecated Replaced by {@code new In(filename)}.{@link #readAllDoubles()}. */ @Deprecated public static double[] readDoubles(String filename) { return new In(filename).readAllDoubles(); } /** * Reads all strings from a file and returns them as * an array of strings. * * @param filename the name of the file * @return the strings in the file * @deprecated Replaced by {@code new In(filename)}.{@link #readAllStrings()}. */ @Deprecated public static String[] readStrings(String filename) { return new In(filename).readAllStrings(); } /** * Reads all integers from standard input and returns them * an array of integers. * * @return the integers on standard input * @deprecated Replaced by {@link StdIn#readAllInts()}. */ @Deprecated public static int[] readInts() { return new In().readAllInts(); } /** * Reads all doubles from standard input and returns them as * an array of doubles. * * @return the doubles on standard input * @deprecated Replaced by {@link StdIn#readAllDoubles()}. */ @Deprecated public static double[] readDoubles() { return new In().readAllDoubles(); } /** * Reads all strings from standard input and returns them as * an array of strings. * * @return the strings on standard input * @deprecated Replaced by {@link StdIn#readAllStrings()}. */ @Deprecated public static String[] readStrings() { return new In().readAllStrings(); } /** * Unit tests the {@code In} data type. * * @param args the command-line arguments */ public static void main(String[] args) { In in; String urlName = "http://introcs.cs.princeton.edu/stdlib/InTest.txt"; // read from a URL System.out.println("readAll() from URL " + urlName); System.out.println("---------------------------------------------------------------------------"); try { in = new In(urlName); System.out.println(in.readAll()); } catch (IllegalArgumentException e) { System.out.println(e); } System.out.println(); // read one line at a time from URL System.out.println("readLine() from URL " + urlName); System.out.println("---------------------------------------------------------------------------"); try { in = new In(urlName); while (!in.isEmpty()) { String s = in.readLine(); System.out.println(s); } } catch (IllegalArgumentException e) { System.out.println(e); } System.out.println(); // read one string at a time from URL System.out.println("readString() from URL " + urlName); System.out.println("---------------------------------------------------------------------------"); try { in = new In(urlName); while (!in.isEmpty()) { String s = in.readString(); System.out.println(s); } } catch (IllegalArgumentException e) { System.out.println(e); } System.out.println(); // read one line at a time from file in current directory System.out.println("readLine() from current directory"); System.out.println("---------------------------------------------------------------------------"); try { in = new In("./InTest.txt"); while (!in.isEmpty()) { String s = in.readLine(); System.out.println(s); } } catch (IllegalArgumentException e) { System.out.println(e); } System.out.println(); // read one line at a time from file using relative path System.out.println("readLine() from relative path"); System.out.println("---------------------------------------------------------------------------"); try { in = new In("../stdlib/InTest.txt"); while (!in.isEmpty()) { String s = in.readLine(); System.out.println(s); } } catch (IllegalArgumentException e) { System.out.println(e); } System.out.println(); // read one char at a time System.out.println("readChar() from file"); System.out.println("---------------------------------------------------------------------------"); try { in = new In("InTest.txt"); while (!in.isEmpty()) { char c = in.readChar(); System.out.print(c); } } catch (IllegalArgumentException e) { System.out.println(e); } System.out.println(); System.out.println(); // read one line at a time from absolute OS X / Linux path System.out.println("readLine() from absolute OS X / Linux path"); System.out.println("---------------------------------------------------------------------------"); in = new In("/n/fs/introcs/www/java/stdlib/InTest.txt"); try { while (!in.isEmpty()) { String s = in.readLine(); System.out.println(s); } } catch (IllegalArgumentException e) { System.out.println(e); } System.out.println(); // read one line at a time from absolute Windows path System.out.println("readLine() from absolute Windows path"); System.out.println("---------------------------------------------------------------------------"); try { in = new In("G:\\www\\introcs\\stdlib\\InTest.txt"); while (!in.isEmpty()) { String s = in.readLine(); System.out.println(s); } System.out.println(); } catch (IllegalArgumentException e) { System.out.println(e); } System.out.println(); } } /****************************************************************************** * Copyright 2002-2016, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * * Algorithms, 4th edition by Robert Sedgewick and Kevin Wayne, * Addison-Wesley Professional, 2011, ISBN 0-321-57351-X. * http://algs4.cs.princeton.edu * * * algs4.jar is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * algs4.jar is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with algs4.jar. If not, see http://www.gnu.org/licenses. ******************************************************************************/ ================================================ FILE: code/algs4/IndexMinPQ.java ================================================ /****************************************************************************** * Compilation: javac IndexMinPQ.java * Execution: java IndexMinPQ * Dependencies: StdOut.java * * Minimum-oriented indexed PQ implementation using a binary heap. * ******************************************************************************/ package algs4; import java.util.Iterator; import java.util.NoSuchElementException; /** * The {@code IndexMinPQ} class represents an indexed priority queue of generic keys. * It supports the usual insert and delete-the-minimum * operations, along with delete and change-the-key * methods. In order to let the client refer to keys on the priority queue, * an integer between {@code 0} and {@code maxN - 1} * is associated with each key—the client uses this integer to specify * which key to delete or change. * It also supports methods for peeking at the minimum key, * testing if the priority queue is empty, and iterating through * the keys. *

* This implementation uses a binary heap along with an array to associate * keys with integers in the given range. * The insert, delete-the-minimum, delete, * change-key, decrease-key, and increase-key * operations take logarithmic time. * The is-empty, size, min-index, min-key, * and key-of operations take constant time. * Construction takes time proportional to the specified capacity. *

* For additional documentation, see Section 2.4 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne * * @param the generic type of key on this priority queue */ public class IndexMinPQ> implements Iterable { private int maxN; // maximum number of elements on PQ private int n; // number of elements on PQ private int[] pq; // binary heap using 1-based indexing private int[] qp; // inverse of pq - qp[pq[i]] = pq[qp[i]] = i private Key[] keys; // keys[i] = priority of i /** * Initializes an empty indexed priority queue with indices between {@code 0} * and {@code maxN - 1}. * @param maxN the keys on this priority queue are index from {@code 0} * {@code maxN - 1} * @throws IllegalArgumentException if {@code maxN < 0} */ public IndexMinPQ(int maxN) { if (maxN < 0) throw new IllegalArgumentException(); this.maxN = maxN; n = 0; keys = (Key[]) new Comparable[maxN + 1]; // make this of length maxN?? pq = new int[maxN + 1]; qp = new int[maxN + 1]; // make this of length maxN?? for (int i = 0; i <= maxN; i++) qp[i] = -1; } /** * Returns true if this priority queue is empty. * * @return {@code true} if this priority queue is empty; * {@code false} otherwise */ public boolean isEmpty() { return n == 0; } /** * Is {@code i} an index on this priority queue? * * @param i an index * @return {@code true} if {@code i} is an index on this priority queue; * {@code false} otherwise * @throws IndexOutOfBoundsException unless {@code 0 <= i < maxN} */ public boolean contains(int i) { if (i < 0 || i >= maxN) throw new IndexOutOfBoundsException(); return qp[i] != -1; } /** * Returns the number of keys on this priority queue. * * @return the number of keys on this priority queue */ public int size() { return n; } /** * Associates key with index {@code i}. * * @param i an index * @param key the key to associate with index {@code i} * @throws IndexOutOfBoundsException unless {@code 0 <= i < maxN} * @throws IllegalArgumentException if there already is an item associated * with index {@code i} */ public void insert(int i, Key key) { if (i < 0 || i >= maxN) throw new IndexOutOfBoundsException(); if (contains(i)) throw new IllegalArgumentException("index is already in the priority queue"); n++; qp[i] = n; pq[n] = i; keys[i] = key; swim(n); } /** * Returns an index associated with a minimum key. * * @return an index associated with a minimum key * @throws NoSuchElementException if this priority queue is empty */ public int minIndex() { if (n == 0) throw new NoSuchElementException("Priority queue underflow"); return pq[1]; } /** * Returns a minimum key. * * @return a minimum key * @throws NoSuchElementException if this priority queue is empty */ public Key minKey() { if (n == 0) throw new NoSuchElementException("Priority queue underflow"); return keys[pq[1]]; } /** * Removes a minimum key and returns its associated index. * @return an index associated with a minimum key * @throws NoSuchElementException if this priority queue is empty */ public int delMin() { if (n == 0) throw new NoSuchElementException("Priority queue underflow"); int min = pq[1]; exch(1, n--); sink(1); assert min == pq[n+1]; qp[min] = -1; // delete keys[min] = null; // to help with garbage collection pq[n+1] = -1; // not needed return min; } /** * Returns the key associated with index {@code i}. * * @param i the index of the key to return * @return the key associated with index {@code i} * @throws IndexOutOfBoundsException unless {@code 0 <= i < maxN} * @throws NoSuchElementException no key is associated with index {@code i} */ public Key keyOf(int i) { if (i < 0 || i >= maxN) throw new IndexOutOfBoundsException(); if (!contains(i)) throw new NoSuchElementException("index is not in the priority queue"); else return keys[i]; } /** * Change the key associated with index {@code i} to the specified value. * * @param i the index of the key to change * @param key change the key associated with index {@code i} to this key * @throws IndexOutOfBoundsException unless {@code 0 <= i < maxN} * @throws NoSuchElementException no key is associated with index {@code i} */ public void changeKey(int i, Key key) { if (i < 0 || i >= maxN) throw new IndexOutOfBoundsException(); if (!contains(i)) throw new NoSuchElementException("index is not in the priority queue"); keys[i] = key; swim(qp[i]); sink(qp[i]); } /** * Change the key associated with index {@code i} to the specified value. * * @param i the index of the key to change * @param key change the key associated with index {@code i} to this key * @throws IndexOutOfBoundsException unless {@code 0 <= i < maxN} * @deprecated Replaced by {@code changeKey(int, Key)}. */ @Deprecated public void change(int i, Key key) { changeKey(i, key); } /** * Decrease the key associated with index {@code i} to the specified value. * * @param i the index of the key to decrease * @param key decrease the key associated with index {@code i} to this key * @throws IndexOutOfBoundsException unless {@code 0 <= i < maxN} * @throws IllegalArgumentException if {@code key >= keyOf(i)} * @throws NoSuchElementException no key is associated with index {@code i} */ public void decreaseKey(int i, Key key) { if (i < 0 || i >= maxN) throw new IndexOutOfBoundsException(); if (!contains(i)) throw new NoSuchElementException("index is not in the priority queue"); if (keys[i].compareTo(key) <= 0) throw new IllegalArgumentException("Calling decreaseKey() with given argument would not strictly decrease the key"); keys[i] = key; swim(qp[i]); } /** * Increase the key associated with index {@code i} to the specified value. * * @param i the index of the key to increase * @param key increase the key associated with index {@code i} to this key * @throws IndexOutOfBoundsException unless {@code 0 <= i < maxN} * @throws IllegalArgumentException if {@code key <= keyOf(i)} * @throws NoSuchElementException no key is associated with index {@code i} */ public void increaseKey(int i, Key key) { if (i < 0 || i >= maxN) throw new IndexOutOfBoundsException(); if (!contains(i)) throw new NoSuchElementException("index is not in the priority queue"); if (keys[i].compareTo(key) >= 0) throw new IllegalArgumentException("Calling increaseKey() with given argument would not strictly increase the key"); keys[i] = key; sink(qp[i]); } /** * Remove the key associated with index {@code i}. * * @param i the index of the key to remove * @throws IndexOutOfBoundsException unless {@code 0 <= i < maxN} * @throws NoSuchElementException no key is associated with index {@code i} */ public void delete(int i) { if (i < 0 || i >= maxN) throw new IndexOutOfBoundsException(); if (!contains(i)) throw new NoSuchElementException("index is not in the priority queue"); int index = qp[i]; exch(index, n--); swim(index); sink(index); keys[i] = null; qp[i] = -1; } /*************************************************************************** * General helper functions. ***************************************************************************/ private boolean greater(int i, int j) { return keys[pq[i]].compareTo(keys[pq[j]]) > 0; } private void exch(int i, int j) { int swap = pq[i]; pq[i] = pq[j]; pq[j] = swap; qp[pq[i]] = i; qp[pq[j]] = j; } /*************************************************************************** * Heap helper functions. ***************************************************************************/ private void swim(int k) { while (k > 1 && greater(k/2, k)) { exch(k, k/2); k = k/2; } } private void sink(int k) { while (2*k <= n) { int j = 2*k; if (j < n && greater(j, j+1)) j++; if (!greater(k, j)) break; exch(k, j); k = j; } } /*************************************************************************** * Iterators. ***************************************************************************/ /** * Returns an iterator that iterates over the keys on the * priority queue in ascending order. * The iterator doesn't implement {@code remove()} since it's optional. * * @return an iterator that iterates over the keys in ascending order */ public Iterator iterator() { return new HeapIterator(); } private class HeapIterator implements Iterator { // create a new pq private IndexMinPQ copy; // add all elements to copy of heap // takes linear time since already in heap order so no keys move public HeapIterator() { copy = new IndexMinPQ(pq.length - 1); for (int i = 1; i <= n; i++) copy.insert(pq[i], keys[pq[i]]); } public boolean hasNext() { return !copy.isEmpty(); } public void remove() { throw new UnsupportedOperationException(); } public Integer next() { if (!hasNext()) throw new NoSuchElementException(); return copy.delMin(); } } /** * Unit tests the {@code IndexMinPQ} data type. * * @param args the command-line arguments */ public static void main(String[] args) { // insert a bunch of strings String[] strings = { "it", "was", "the", "best", "of", "times", "it", "was", "the", "worst" }; IndexMinPQ pq = new IndexMinPQ(strings.length); for (int i = 0; i < strings.length; i++) { pq.insert(i, strings[i]); } // delete and print each key while (!pq.isEmpty()) { int i = pq.delMin(); StdOut.println(i + " " + strings[i]); } StdOut.println(); // reinsert the same strings for (int i = 0; i < strings.length; i++) { pq.insert(i, strings[i]); } // print each key using the iterator for (int i : pq) { StdOut.println(i + " " + strings[i]); } while (!pq.isEmpty()) { pq.delMin(); } } } /****************************************************************************** * Copyright 2002-2016, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * * Algorithms, 4th edition by Robert Sedgewick and Kevin Wayne, * Addison-Wesley Professional, 2011, ISBN 0-321-57351-X. * http://algs4.cs.princeton.edu * * * algs4.jar is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * algs4.jar is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with algs4.jar. If not, see http://www.gnu.org/licenses. ******************************************************************************/ ================================================ FILE: code/algs4/Interval1D.java ================================================ /****************************************************************************** * Compilation: javac Interval1D.java * Execution: java Interval1D * Dependencies: StdOut.java * * 1-dimensional interval data type. * ******************************************************************************/ package algs4; import java.util.Arrays; import java.util.Comparator; /** * The {@code Interval1D} class represents a one-dimensional interval. * The interval is closed—it contains both endpoints. * Intervals are immutable: their values cannot be changed after they are created. * The class {@code Interval1D} includes methods for checking whether * an interval contains a point and determining whether two intervals intersect. *

* For additional documentation, * see Section 1.2 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne */ public class Interval1D { /** * Compares two intervals by min endpoint. */ public static final Comparator MIN_ENDPOINT_ORDER = new MinEndpointComparator(); /** * Compares two intervals by max endpoint. */ public static final Comparator MAX_ENDPOINT_ORDER = new MaxEndpointComparator(); /** * Compares two intervals by length. */ public static final Comparator LENGTH_ORDER = new LengthComparator(); private final double min; private final double max; /** * Initializes a closed interval [min, max]. * * @param min the smaller endpoint * @param max the larger endpoint * @throws IllegalArgumentException if the min endpoint is greater than the max endpoint * @throws IllegalArgumentException if either {@code min} or {@code max} * is {@code Double.NaN}, {@code Double.POSITIVE_INFINITY} or * {@code Double.NEGATIVE_INFINITY} */ public Interval1D(double min, double max) { if (Double.isInfinite(min) || Double.isInfinite(max)) throw new IllegalArgumentException("Endpoints must be finite"); if (Double.isNaN(min) || Double.isNaN(max)) throw new IllegalArgumentException("Endpoints cannot be NaN"); // convert -0.0 to +0.0 if (min == 0.0) min = 0.0; if (max == 0.0) max = 0.0; if (min <= max) { this.min = min; this.max = max; } else throw new IllegalArgumentException("Illegal interval"); } /** * Returns the left endpoint of this interval. * * @return the left endpoint of this interval * @deprecated Replaced by {@link #min()}. */ @Deprecated public double left() { return min; } /** * Returns the right endpoint of this interval. * @return the right endpoint of this interval * @deprecated Replaced by {@link #max()}. */ @Deprecated public double right() { return max; } /** * Returns the min endpoint of this interval. * * @return the min endpoint of this interval */ public double min() { return min; } /** * Returns the max endpoint of this interval. * * @return the max endpoint of this interval */ public double max() { return max; } /** * Returns true if this interval intersects the specified interval. * * @param that the other interval * @return {@code true} if this interval intersects the argument interval; * {@code false} otherwise */ public boolean intersects(Interval1D that) { if (this.max < that.min) return false; if (that.max < this.min) return false; return true; } /** * Returns true if this interval contains the specified value. * * @param x the value * @return {@code true} if this interval contains the value {@code x}; * {@code false} otherwise */ public boolean contains(double x) { return (min <= x) && (x <= max); } /** * Returns the length of this interval. * * @return the length of this interval (max - min) */ public double length() { return max - min; } /** * Returns a string representation of this interval. * * @return a string representation of this interval in the form [min, max] */ public String toString() { return "[" + min + ", " + max + "]"; } /** * Compares this transaction to the specified object. * * @param other the other interval * @return {@code true} if this interval equals the other interval; * {@code false} otherwise */ public boolean equals(Object other) { if (other == this) return true; if (other == null) return false; if (other.getClass() != this.getClass()) return false; Interval1D that = (Interval1D) other; return this.min == that.min && this.max == that.max; } /** * Returns an integer hash code for this interval. * * @return an integer hash code for this interval */ public int hashCode() { int hash1 = ((Double) min).hashCode(); int hash2 = ((Double) max).hashCode(); return 31*hash1 + hash2; } // ascending order of min endpoint, breaking ties by max endpoint private static class MinEndpointComparator implements Comparator { public int compare(Interval1D a, Interval1D b) { if (a.min < b.min) return -1; else if (a.min > b.min) return +1; else if (a.max < b.max) return -1; else if (a.max > b.max) return +1; else return 0; } } // ascending order of max endpoint, breaking ties by min endpoint private static class MaxEndpointComparator implements Comparator { public int compare(Interval1D a, Interval1D b) { if (a.min < b.max) return -1; else if (a.min > b.max) return +1; else if (a.min < b.min) return -1; else if (a.min > b.min) return +1; else return 0; } } // ascending order of length private static class LengthComparator implements Comparator { public int compare(Interval1D a, Interval1D b) { double alen = a.length(); double blen = b.length(); if (alen < blen) return -1; else if (alen > blen) return +1; else return 0; } } /** * Unit tests the {@code Interval1D} data type. * * @param args the command-line arguments */ public static void main(String[] args) { Interval1D[] intervals = new Interval1D[4]; intervals[0] = new Interval1D(15.0, 33.0); intervals[1] = new Interval1D(45.0, 60.0); intervals[2] = new Interval1D(20.0, 70.0); intervals[3] = new Interval1D(46.0, 55.0); StdOut.println("Unsorted"); for (int i = 0; i < intervals.length; i++) StdOut.println(intervals[i]); StdOut.println(); StdOut.println("Sort by min endpoint"); Arrays.sort(intervals, Interval1D.MIN_ENDPOINT_ORDER); for (int i = 0; i < intervals.length; i++) StdOut.println(intervals[i]); StdOut.println(); StdOut.println("Sort by max endpoint"); Arrays.sort(intervals, Interval1D.MAX_ENDPOINT_ORDER); for (int i = 0; i < intervals.length; i++) StdOut.println(intervals[i]); StdOut.println(); StdOut.println("Sort by length"); Arrays.sort(intervals, Interval1D.LENGTH_ORDER); for (int i = 0; i < intervals.length; i++) StdOut.println(intervals[i]); StdOut.println(); } } /****************************************************************************** * Copyright 2002-2016, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * * Algorithms, 4th edition by Robert Sedgewick and Kevin Wayne, * Addison-Wesley Professional, 2011, ISBN 0-321-57351-X. * http://algs4.cs.princeton.edu * * * algs4.jar is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * algs4.jar is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with algs4.jar. If not, see http://www.gnu.org/licenses. ******************************************************************************/ ================================================ FILE: code/algs4/Interval2D.java ================================================ /****************************************************************************** * Compilation: javac Interval2D.java * Execution: java Interval2D * Dependencies: StdOut.java Interval1D.java StdDraw.java * * 2-dimensional interval data type. * ******************************************************************************/ package algs4; /** * The {@code Interval2D} class represents a closed two-dimensional interval, * which represents all points (x, y) with both {@code xmin <= x <= xmax} and * {@code ymin <= y <= ymax}. * Two-dimensional intervals are immutable: their values cannot be changed * after they are created. * The class {@code Interval2D} includes methods for checking whether * a two-dimensional interval contains a point and determining whether * two two-dimensional intervals intersect. *

* For additional documentation, * see Section 1.2 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne */ public class Interval2D { private final Interval1D x; private final Interval1D y; /** * Initializes a two-dimensional interval. * @param x the one-dimensional interval of x-coordinates * @param y the one-dimensional interval of y-coordinates */ public Interval2D(Interval1D x, Interval1D y) { this.x = x; this.y = y; } /** * Does this two-dimensional interval intersect that two-dimensional interval? * @param that the other two-dimensional interval * @return true if this two-dimensional interval intersects * that two-dimensional interval; false otherwise */ public boolean intersects(Interval2D that) { if (!this.x.intersects(that.x)) return false; if (!this.y.intersects(that.y)) return false; return true; } /** * Does this two-dimensional interval contain the point p? * @param p the two-dimensional point * @return true if this two-dimensional interval contains the point p; false otherwise */ public boolean contains(Point2D p) { return x.contains(p.x()) && y.contains(p.y()); } /** * Returns the area of this two-dimensional interval. * @return the area of this two-dimensional interval */ public double area() { return x.length() * y.length(); } /** * Returns a string representation of this two-dimensional interval. * @return a string representation of this two-dimensional interval * in the form [xmin, xmax] x [ymin, ymax] */ public String toString() { return x + " x " + y; } /** * Does this interval equal the other interval? * @param other the other interval * @return true if this interval equals the other interval; false otherwise */ public boolean equals(Object other) { if (other == this) return true; if (other == null) return false; if (other.getClass() != this.getClass()) return false; Interval2D that = (Interval2D) other; return this.x.equals(that.x) && this.y.equals(that.y); } /** * Returns an integer hash code for this interval. * @return an integer hash code for this interval */ public int hashCode() { int hash1 = x.hashCode(); int hash2 = y.hashCode(); return 31*hash1 + hash2; } /** * Draws this two-dimensional interval to standard draw. */ public void draw() { double xc = (x.min() + x.max()) / 2.0; double yc = (y.min() + y.max()) / 2.0; StdDraw.rectangle(xc, yc, x.length() / 2.0, y.length() / 2.0); } /** * Unit tests the {@code Interval2D} data type. * * @param args the command-line arguments */ public static void main(String[] args) { double xmin = Double.parseDouble(args[0]); double xmax = Double.parseDouble(args[1]); double ymin = Double.parseDouble(args[2]); double ymax = Double.parseDouble(args[3]); int trials = Integer.parseInt(args[4]); Interval1D xInterval = new Interval1D(xmin, xmax); Interval1D yInterval = new Interval1D(ymin, ymax); Interval2D box = new Interval2D(xInterval, yInterval); box.draw(); Counter counter = new Counter("hits"); for (int t = 0; t < trials; t++) { double x = StdRandom.uniform(0.0, 1.0); double y = StdRandom.uniform(0.0, 1.0); Point2D point = new Point2D(x, y); if (box.contains(point)) counter.increment(); else point.draw(); } StdOut.println(counter); StdOut.printf("box area = %.2f\n", box.area()); } } /****************************************************************************** * Copyright 2002-2016, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * * Algorithms, 4th edition by Robert Sedgewick and Kevin Wayne, * Addison-Wesley Professional, 2011, ISBN 0-321-57351-X. * http://algs4.cs.princeton.edu * * * algs4.jar is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * algs4.jar is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with algs4.jar. If not, see http://www.gnu.org/licenses. ******************************************************************************/ ================================================ FILE: code/algs4/MinPQ.java ================================================ /****************************************************************************** * Compilation: javac MinPQ.java * Execution: java MinPQ < input.txt * Dependencies: StdIn.java StdOut.java * Data files: http://algs4.cs.princeton.edu/24pq/tinyPQ.txt * * Generic min priority queue implementation with a binary heap. * Can be used with a comparator instead of the natural order. * * % java MinPQ < tinyPQ.txt * E A E (6 left on pq) * * We use a one-based array to simplify parent and child calculations. * * Can be optimized by replacing full exchanges with half exchanges * (ala insertion sort). * ******************************************************************************/ package algs4; import java.util.Comparator; import java.util.Iterator; import java.util.NoSuchElementException; /** * The {@code MinPQ} class represents a priority queue of generic keys. * It supports the usual insert and delete-the-minimum * operations, along with methods for peeking at the minimum key, * testing if the priority queue is empty, and iterating through * the keys. *

* This implementation uses a binary heap. * The insert and delete-the-minimum operations take * logarithmic amortized time. * The min, size, and is-empty operations take constant time. * Construction takes time proportional to the specified capacity or the number of * items used to initialize the data structure. *

* For additional documentation, see Section 2.4 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne * * @param the generic type of key on this priority queue */ public class MinPQ implements Iterable { private Key[] pq; // store items at indices 1 to n private int n; // number of items on priority queue private Comparator comparator; // optional comparator /** * Initializes an empty priority queue with the given initial capacity. * * @param initCapacity the initial capacity of this priority queue */ public MinPQ(int initCapacity) { pq = (Key[]) new Object[initCapacity + 1]; n = 0; } /** * Initializes an empty priority queue. */ public MinPQ() { this(1); } /** * Initializes an empty priority queue with the given initial capacity, * using the given comparator. * * @param initCapacity the initial capacity of this priority queue * @param comparator the order to use when comparing keys */ public MinPQ(int initCapacity, Comparator comparator) { this.comparator = comparator; pq = (Key[]) new Object[initCapacity + 1]; n = 0; } /** * Initializes an empty priority queue using the given comparator. * * @param comparator the order to use when comparing keys */ public MinPQ(Comparator comparator) { this(1, comparator); } /** * Initializes a priority queue from the array of keys. *

* Takes time proportional to the number of keys, using sink-based heap construction. * * @param keys the array of keys */ public MinPQ(Key[] keys) { n = keys.length; pq = (Key[]) new Object[keys.length + 1]; for (int i = 0; i < n; i++) pq[i+1] = keys[i]; for (int k = n/2; k >= 1; k--) sink(k); assert isMinHeap(); } /** * Returns true if this priority queue is empty. * * @return {@code true} if this priority queue is empty; * {@code false} otherwise */ public boolean isEmpty() { return n == 0; } /** * Returns the number of keys on this priority queue. * * @return the number of keys on this priority queue */ public int size() { return n; } /** * Returns a smallest key on this priority queue. * * @return a smallest key on this priority queue * @throws NoSuchElementException if this priority queue is empty */ public Key min() { if (isEmpty()) throw new NoSuchElementException("Priority queue underflow"); return pq[1]; } // helper function to double the size of the heap array private void resize(int capacity) { assert capacity > n; Key[] temp = (Key[]) new Object[capacity]; for (int i = 1; i <= n; i++) { temp[i] = pq[i]; } pq = temp; } /** * Adds a new key to this priority queue. * * @param x the key to add to this priority queue */ public void insert(Key x) { // double size of array if necessary if (n == pq.length - 1) resize(2 * pq.length); // add x, and percolate it up to maintain heap invariant pq[++n] = x; swim(n); assert isMinHeap(); } /** * Removes and returns a smallest key on this priority queue. * * @return a smallest key on this priority queue * @throws NoSuchElementException if this priority queue is empty */ public Key delMin() { if (isEmpty()) throw new NoSuchElementException("Priority queue underflow"); exch(1, n); Key min = pq[n--]; sink(1); pq[n+1] = null; // avoid loitering and help with garbage collection if ((n > 0) && (n == (pq.length - 1) / 4)) resize(pq.length / 2); assert isMinHeap(); return min; } /*************************************************************************** * Helper functions to restore the heap invariant. ***************************************************************************/ private void swim(int k) { while (k > 1 && greater(k/2, k)) { exch(k, k/2); k = k/2; } } private void sink(int k) { while (2*k <= n) { int j = 2*k; if (j < n && greater(j, j+1)) j++; if (!greater(k, j)) break; exch(k, j); k = j; } } /*************************************************************************** * Helper functions for compares and swaps. ***************************************************************************/ private boolean greater(int i, int j) { if (comparator == null) { return ((Comparable) pq[i]).compareTo(pq[j]) > 0; } else { return comparator.compare(pq[i], pq[j]) > 0; } } private void exch(int i, int j) { Key swap = pq[i]; pq[i] = pq[j]; pq[j] = swap; } // is pq[1..N] a min heap? private boolean isMinHeap() { return isMinHeap(1); } // is subtree of pq[1..n] rooted at k a min heap? private boolean isMinHeap(int k) { if (k > n) return true; int left = 2*k; int right = 2*k + 1; if (left <= n && greater(k, left)) return false; if (right <= n && greater(k, right)) return false; return isMinHeap(left) && isMinHeap(right); } /** * Returns an iterator that iterates over the keys on this priority queue * in ascending order. *

* The iterator doesn't implement {@code remove()} since it's optional. * * @return an iterator that iterates over the keys in ascending order */ public Iterator iterator() { return new HeapIterator(); } private class HeapIterator implements Iterator { // create a new pq private MinPQ copy; // add all items to copy of heap // takes linear time since already in heap order so no keys move public HeapIterator() { if (comparator == null) copy = new MinPQ(size()); else copy = new MinPQ(size(), comparator); for (int i = 1; i <= n; i++) copy.insert(pq[i]); } public boolean hasNext() { return !copy.isEmpty(); } public void remove() { throw new UnsupportedOperationException(); } public Key next() { if (!hasNext()) throw new NoSuchElementException(); return copy.delMin(); } } /** * Unit tests the {@code MinPQ} data type. * * @param args the command-line arguments */ public static void main(String[] args) { MinPQ pq = new MinPQ(); while (!StdIn.isEmpty()) { String item = StdIn.readString(); if (!item.equals("-")) pq.insert(item); else if (!pq.isEmpty()) StdOut.print(pq.delMin() + " "); } StdOut.println("(" + pq.size() + " left on pq)"); } } /****************************************************************************** * Copyright 2002-2016, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * * Algorithms, 4th edition by Robert Sedgewick and Kevin Wayne, * Addison-Wesley Professional, 2011, ISBN 0-321-57351-X. * http://algs4.cs.princeton.edu * * * algs4.jar is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * algs4.jar is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with algs4.jar. If not, see http://www.gnu.org/licenses. ******************************************************************************/ ================================================ FILE: code/algs4/Out.java ================================================ /****************************************************************************** * Compilation: javac Out.java * Execution: java Out * Dependencies: none * * Writes data of various types to: stdout, file, or socket. * ******************************************************************************/ package algs4; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.Socket; import java.util.Locale; /** * This class provides methods for writing strings and numbers to * various output streams, including standard output, file, and sockets. *

* For additional documentation, see * Section 3.1 of * Computer Science: An Interdisciplinary Approach * by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne */ public class Out { // force Unicode UTF-8 encoding; otherwise it's system dependent private static final String CHARSET_NAME = "UTF-8"; // assume language = English, country = US for consistency with In private static final Locale LOCALE = Locale.US; private PrintWriter out; /** * Initializes an output stream from a {@link OutputStream}. * * @param os the {@code OutputStream} */ public Out(OutputStream os) { try { OutputStreamWriter osw = new OutputStreamWriter(os, CHARSET_NAME); out = new PrintWriter(osw, true); } catch (IOException e) { e.printStackTrace(); } } /** * Initializes an output stream from standard output. */ public Out() { this(System.out); } /** * Initializes an output stream from a socket. * * @param socket the socket */ public Out(Socket socket) { try { OutputStream os = socket.getOutputStream(); OutputStreamWriter osw = new OutputStreamWriter(os, CHARSET_NAME); out = new PrintWriter(osw, true); } catch (IOException e) { e.printStackTrace(); } } /** * Initializes an output stream from a file. * * @param filename the name of the file */ public Out(String filename) { try { OutputStream os = new FileOutputStream(filename); OutputStreamWriter osw = new OutputStreamWriter(os, CHARSET_NAME); out = new PrintWriter(osw, true); } catch (IOException e) { e.printStackTrace(); } } /** * Closes the output stream. */ public void close() { out.close(); } /** * Terminates the current line by printing the line-separator string. */ public void println() { out.println(); } /** * Prints an object to this output stream and then terminates the line. * * @param x the object to print */ public void println(Object x) { out.println(x); } /** * Prints a boolean to this output stream and then terminates the line. * * @param x the boolean to print */ public void println(boolean x) { out.println(x); } /** * Prints a character to this output stream and then terminates the line. * * @param x the character to print */ public void println(char x) { out.println(x); } /** * Prints a double to this output stream and then terminates the line. * * @param x the double to print */ public void println(double x) { out.println(x); } /** * Prints a float to this output stream and then terminates the line. * * @param x the float to print */ public void println(float x) { out.println(x); } /** * Prints an integer to this output stream and then terminates the line. * * @param x the integer to print */ public void println(int x) { out.println(x); } /** * Prints a long to this output stream and then terminates the line. * * @param x the long to print */ public void println(long x) { out.println(x); } /** * Prints a byte to this output stream and then terminates the line. *

* To write binary data, see {@link BinaryOut}. * * @param x the byte to print */ public void println(byte x) { out.println(x); } /** * Flushes this output stream. */ public void print() { out.flush(); } /** * Prints an object to this output stream and flushes this output stream. * * @param x the object to print */ public void print(Object x) { out.print(x); out.flush(); } /** * Prints a boolean to this output stream and flushes this output stream. * * @param x the boolean to print */ public void print(boolean x) { out.print(x); out.flush(); } /** * Prints a character to this output stream and flushes this output stream. * * @param x the character to print */ public void print(char x) { out.print(x); out.flush(); } /** * Prints a double to this output stream and flushes this output stream. * * @param x the double to print */ public void print(double x) { out.print(x); out.flush(); } /** * Prints a float to this output stream and flushes this output stream. * * @param x the float to print */ public void print(float x) { out.print(x); out.flush(); } /** * Prints an integer to this output stream and flushes this output stream. * * @param x the integer to print */ public void print(int x) { out.print(x); out.flush(); } /** * Prints a long integer to this output stream and flushes this output stream. * * @param x the long integer to print */ public void print(long x) { out.print(x); out.flush(); } /** * Prints a byte to this output stream and flushes this output stream. * * @param x the byte to print */ public void print(byte x) { out.print(x); out.flush(); } /** * Prints a formatted string to this output stream, using the specified format * string and arguments, and then flushes this output stream. * * @param format the format string * @param args the arguments accompanying the format string */ public void printf(String format, Object... args) { out.printf(LOCALE, format, args); out.flush(); } /** * Prints a formatted string to this output stream, using the specified * locale, format string, and arguments, and then flushes this output stream. * * @param locale the locale * @param format the format string * @param args the arguments accompanying the format string */ public void printf(Locale locale, String format, Object... args) { out.printf(locale, format, args); out.flush(); } /** * A test client. * * @param args the command-line arguments */ public static void main(String[] args) { Out out; // write to stdout out = new Out(); out.println("Test 1"); out.close(); // write to a file out = new Out("test.txt"); out.println("Test 2"); out.close(); } } /****************************************************************************** * Copyright 2002-2016, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * * Algorithms, 4th edition by Robert Sedgewick and Kevin Wayne, * Addison-Wesley Professional, 2011, ISBN 0-321-57351-X. * http://algs4.cs.princeton.edu * * * algs4.jar is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * algs4.jar is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with algs4.jar. If not, see http://www.gnu.org/licenses. ******************************************************************************/ ================================================ FILE: code/algs4/Particle.java ================================================ /****************************************************************************** * Compilation: javac Particle.java * Execution: none * Dependencies: StdDraw.java * * A particle moving in the unit box with a given position, velocity, * radius, and mass. * ******************************************************************************/ package algs4; import java.awt.Color; /** * The {@code Particle} class represents a particle moving in the unit box, * with a given position, velocity, radius, and mass. Methods are provided * for moving the particle and for predicting and resolvling elastic * collisions with vertical walls, horizontal walls, and other particles. * This data type is mutable because the position and velocity change. *

* For additional documentation, * see Section 6.1 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne */ public class Particle { private static final double INFINITY = Double.POSITIVE_INFINITY; private double rx, ry; // position private double vx, vy; // velocity private int count; // number of collisions so far private final double radius; // radius private final double mass; // mass private final Color color; // color /** * Initializes a particle with the specified position, velocity, radius, mass, and color. * * @param rx x-coordinate of position * @param ry y-coordinate of position * @param vx x-coordinate of velocity * @param vy y-coordinate of velocity * @param radius the radius * @param mass the mass * @param color the color */ public Particle(double rx, double ry, double vx, double vy, double radius, double mass, Color color) { this.vx = vx; this.vy = vy; this.rx = rx; this.ry = ry; this.radius = radius; this.mass = mass; this.color = color; } /** * Initializes a particle with a random position and velocity. * The position is uniform in the unit box; the velocity in * either direciton is chosen uniformly at random. */ public Particle() { rx = StdRandom.uniform(0.0, 1.0); ry = StdRandom.uniform(0.0, 1.0); vx = StdRandom.uniform(-0.005, 0.005); vy = StdRandom.uniform(-0.005, 0.005); radius = 0.01; mass = 0.5; color = Color.BLACK; } /** * Moves this particle in a straight line (based on its velocity) * for the specified amount of time. * * @param dt the amount of time */ public void move(double dt) { rx += vx * dt; ry += vy * dt; } /** * Draws this particle to standard draw. */ public void draw() { StdDraw.setPenColor(color); StdDraw.filledCircle(rx, ry, radius); } /** * Returns the number of collisions involving this particle with * vertical walls, horizontal walls, or other particles. * This is equal to the number of calls to {@link #bounceOff}, * {@link #bounceOffVerticalWall}, and * {@link #bounceOffHorizontalWall}. * * @return the number of collisions involving this particle with * vertical walls, horizontal walls, or other particles */ public int count() { return count; } /** * Returns the amount of time for this particle to collide with the specified * particle, assuming no interening collisions. * * @param that the other particle * @return the amount of time for this particle to collide with the specified * particle, assuming no interening collisions; * {@code Double.POSITIVE_INFINITY} if the particles will not collide */ public double timeToHit(Particle that) { if (this == that) return INFINITY; double dx = that.rx - this.rx; double dy = that.ry - this.ry; double dvx = that.vx - this.vx; double dvy = that.vy - this.vy; double dvdr = dx*dvx + dy*dvy; if (dvdr > 0) return INFINITY; double dvdv = dvx*dvx + dvy*dvy; double drdr = dx*dx + dy*dy; double sigma = this.radius + that.radius; double d = (dvdr*dvdr) - dvdv * (drdr - sigma*sigma); // if (drdr < sigma*sigma) StdOut.println("overlapping particles"); if (d < 0) return INFINITY; return -(dvdr + Math.sqrt(d)) / dvdv; } /** * Returns the amount of time for this particle to collide with a vertical * wall, assuming no interening collisions. * * @return the amount of time for this particle to collide with a vertical wall, * assuming no interening collisions; * {@code Double.POSITIVE_INFINITY} if the particle will not collide * with a vertical wall */ public double timeToHitVerticalWall() { if (vx > 0) return (1.0 - rx - radius) / vx; else if (vx < 0) return (radius - rx) / vx; else return INFINITY; } /** * Returns the amount of time for this particle to collide with a horizontal * wall, assuming no interening collisions. * * @return the amount of time for this particle to collide with a horizontal wall, * assuming no interening collisions; * {@code Double.POSITIVE_INFINITY} if the particle will not collide * with a horizontal wall */ public double timeToHitHorizontalWall() { if (vy > 0) return (1.0 - ry - radius) / vy; else if (vy < 0) return (radius - ry) / vy; else return INFINITY; } /** * Updates the velocities of this particle and the specified particle according * to the laws of elastic collision. Assumes that the particles are colliding * at this instant. * * @param that the other particle */ public void bounceOff(Particle that) { double dx = that.rx - this.rx; double dy = that.ry - this.ry; double dvx = that.vx - this.vx; double dvy = that.vy - this.vy; double dvdr = dx*dvx + dy*dvy; // dv dot dr double dist = this.radius + that.radius; // distance between particle centers at collison // magnitude of normal force double magnitude = 2 * this.mass * that.mass * dvdr / ((this.mass + that.mass) * dist); // normal force, and in x and y directions double fx = magnitude * dx / dist; double fy = magnitude * dy / dist; // update velocities according to normal force this.vx += fx / this.mass; this.vy += fy / this.mass; that.vx -= fx / that.mass; that.vy -= fy / that.mass; // update collision counts this.count++; that.count++; } /** * Updates the velocity of this particle upon collision with a vertical * wall (by reflecting the velocity in the x-direction). * Assumes that the particle is colliding with a vertical wall at this instant. */ public void bounceOffVerticalWall() { vx = -vx; count++; } /** * Updates the velocity of this particle upon collision with a horizontal * wall (by reflecting the velocity in the y-direction). * Assumes that the particle is colliding with a horizontal wall at this instant. */ public void bounceOffHorizontalWall() { vy = -vy; count++; } /** * Returns the kinetic energy of this particle. * The kinetic energy is given by the formula 1/2 m v2, * where m is the mass of this particle and v is its velocity. * * @return the kinetic energy of this particle */ public double kineticEnergy() { return 0.5 * mass * (vx*vx + vy*vy); } } /****************************************************************************** * Copyright 2002-2016, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * * Algorithms, 4th edition by Robert Sedgewick and Kevin Wayne, * Addison-Wesley Professional, 2011, ISBN 0-321-57351-X. * http://algs4.cs.princeton.edu * * * algs4.jar is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * algs4.jar is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with algs4.jar. If not, see http://www.gnu.org/licenses. ******************************************************************************/ ================================================ FILE: code/algs4/Picture.java ================================================ /****************************************************************************** * Compilation: javac Picture.java * Execution: java Picture imagename * Dependencies: none * * Data type for manipulating individual pixels of an image. The original * image can be read from a file in jpg, gif, or png format, or the * user can create a blank image of a given size. Includes methods for * displaying the image in a window on the screen or saving to a file. * * % java Picture mandrill.jpg * * Remarks * ------- * - pixel (x, y) is column x and row y, where (0, 0) is upper left * * - see also GrayPicture.java for a grayscale version * ******************************************************************************/ package algs4; import java.awt.Color; import java.awt.FileDialog; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import java.net.URL; import javax.imageio.ImageIO; import javax.swing.ImageIcon; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.KeyStroke; /** * This class provides methods for manipulating individual pixels of * an image. The original image can be read from a {@code .jpg}, {@code .gif}, * or {@code .png} file or the user can create a blank image of a given size. * This class includes methods for displaying the image in a window on * the screen or saving it to a file. *

* Pixel (col, row) is column col and row row. * By default, the origin (0, 0) is the pixel in the top-left corner, * which is a common convention in image processing. * The method {@code setOriginLowerLeft()} change the origin to the lower left. *

* For additional documentation, see * Section 3.1 of * Computer Science: An Interdisciplinary Approach * by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne */ public final class Picture implements ActionListener { private BufferedImage image; // the rasterized image private JFrame frame; // on-screen view private String filename; // name of file private boolean isOriginUpperLeft = true; // location of origin private final int width, height; // width and height /** * Initializes a blank {@code width}-by-{@code height} picture, with {@code width} columns * and {@code height} rows, where each pixel is black. * * @param width the width of the picture * @param height the height of the picture * @throws IllegalArgumentException if {@code width} is negative * @throws IllegalArgumentException if {@code height} is negative */ public Picture(int width, int height) { if (width < 0) throw new IllegalArgumentException("width must be nonnegative"); if (height < 0) throw new IllegalArgumentException("height must be nonnegative"); this.width = width; this.height = height; image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); // set to TYPE_INT_ARGB to support transparency filename = width + "-by-" + height; } /** * Initializes a new picture that is a deep copy of the argument picture. * * @param picture the picture to copy */ public Picture(Picture picture) { width = picture.width(); height = picture.height(); image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); filename = picture.filename; for (int col = 0; col < width(); col++) for (int row = 0; row < height(); row++) image.setRGB(col, row, picture.get(col, row).getRGB()); } /** * Initializes a picture by reading from a file or URL. * * @param filename the name of the file (.png, .gif, or .jpg) or URL. * @throws IllegalArgumentException if cannot read image */ public Picture(String filename) { this.filename = filename; try { // try to read from file in working directory File file = new File(filename); if (file.isFile()) { image = ImageIO.read(file); } // now try to read from file in same directory as this .class file else { URL url = getClass().getResource(filename); if (url == null) { url = new URL(filename); } image = ImageIO.read(url); } if (image == null) { throw new IllegalArgumentException("could not read image file: " + filename); } width = image.getWidth(null); height = image.getHeight(null); } catch (IOException ioe) { throw new IllegalArgumentException("could not open image file: " + filename, ioe); } } /** * Initializes a picture by reading in a .png, .gif, or .jpg from a file. * * @param file the file * @throws IllegalArgumentException if cannot read image */ public Picture(File file) { try { image = ImageIO.read(file); } catch (IOException ioe) { throw new IllegalArgumentException("could not open file: " + file, ioe); } if (image == null) { throw new IllegalArgumentException("could not read file: " + file); } width = image.getWidth(null); height = image.getHeight(null); filename = file.getName(); } /** * Returns a JLabel containing this picture, for embedding in a JPanel, * JFrame or other GUI widget. * * @return the {@code JLabel} */ public JLabel getJLabel() { if (image == null) return null; // no image available ImageIcon icon = new ImageIcon(image); return new JLabel(icon); } /** * Sets the origin to be the upper left pixel. This is the default. */ public void setOriginUpperLeft() { isOriginUpperLeft = true; } /** * Sets the origin to be the lower left pixel. */ public void setOriginLowerLeft() { isOriginUpperLeft = false; } /** * Displays the picture in a window on the screen. */ public void show() { // create the GUI for viewing the image if needed if (frame == null) { frame = new JFrame(); JMenuBar menuBar = new JMenuBar(); JMenu menu = new JMenu("File"); menuBar.add(menu); JMenuItem menuItem1 = new JMenuItem(" Save... "); menuItem1.addActionListener(this); menuItem1.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); menu.add(menuItem1); frame.setJMenuBar(menuBar); frame.setContentPane(getJLabel()); // f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); frame.setTitle(filename); frame.setResizable(false); frame.pack(); frame.setVisible(true); } // draw frame.repaint(); } /** * Returns the height of the picture. * * @return the height of the picture (in pixels) */ public int height() { return height; } /** * Returns the width of the picture. * * @return the width of the picture (in pixels) */ public int width() { return width; } private void validateRow(int row) { if (row < 0 || row >= height()) throw new IndexOutOfBoundsException("row must be between 0 and " + (height() - 1) + ": " + row); } private void validateCol(int col) { if (col < 0 || col >= width()) throw new IndexOutOfBoundsException("col must be between 0 and " + (width() - 1) + ": " + col); } /** * Returns the color of pixel ({@code col}, {@code row}). * * @param col the column index * @param row the row index * @return the color of pixel ({@code col}, {@code row}) * @throws IndexOutOfBoundsException unless both {@code 0 <= col < width} and {@code 0 <= row < height} */ public Color get(int col, int row) { validateCol(col); validateRow(row); if (isOriginUpperLeft) return new Color(image.getRGB(col, row)); else return new Color(image.getRGB(col, height - row - 1)); } /** * Sets the color of pixel ({@code col}, {@code row}) to given color. * * @param col the column index * @param row the row index * @param color the color * @throws IndexOutOfBoundsException unless both {@code 0 <= col < width} and {@code 0 <= row < height} * @throws IllegalArgumentException if {@code color} is {@code null} */ public void set(int col, int row, Color color) { validateCol(col); validateRow(row); if (color == null) throw new IllegalArgumentException("color argument is null"); if (isOriginUpperLeft) image.setRGB(col, row, color.getRGB()); else image.setRGB(col, height - row - 1, color.getRGB()); } /** * Returns true if this picture is equal to the argument picture. * * @param other the other picture * @return {@code true} if this picture is the same dimension as {@code other} * and if all pixels have the same color; {@code false} otherwise */ public boolean equals(Object other) { if (other == this) return true; if (other == null) return false; if (other.getClass() != this.getClass()) return false; Picture that = (Picture) other; if (this.width() != that.width()) return false; if (this.height() != that.height()) return false; for (int col = 0; col < width(); col++) for (int row = 0; row < height(); row++) if (!this.get(col, row).equals(that.get(col, row))) return false; return true; } /** * This operation is not supported because pictures are mutable. * * @return does not return a value * @throws UnsupportedOperationException if called */ public int hashCode() { throw new UnsupportedOperationException("hashCode() is not supported because pictures are mutable"); } /** * Saves the picture to a file in a standard image format. * The filetype must be .png or .jpg. * * @param name the name of the file */ public void save(String name) { save(new File(name)); } /** * Saves the picture to a file in a PNG or JPEG image format. * * @param file the file */ public void save(File file) { filename = file.getName(); if (frame != null) frame.setTitle(filename); String suffix = filename.substring(filename.lastIndexOf('.') + 1); if ("jpg".equalsIgnoreCase(suffix) || "png".equalsIgnoreCase(suffix)) { try { ImageIO.write(image, suffix, file); } catch (IOException e) { e.printStackTrace(); } } else { System.out.println("Error: filename must end in .jpg or .png"); } } /** * Opens a save dialog box when the user selects "Save As" from the menu. */ @Override public void actionPerformed(ActionEvent e) { FileDialog chooser = new FileDialog(frame, "Use a .png or .jpg extension", FileDialog.SAVE); chooser.setVisible(true); if (chooser.getFile() != null) { save(chooser.getDirectory() + File.separator + chooser.getFile()); } } /** * Unit tests this {@code Picture} data type. * Reads a picture specified by the command-line argument, * and shows it in a window on the screen. * * @param args the command-line arguments */ public static void main(String[] args) { Picture picture = new Picture(args[0]); System.out.printf("%d-by-%d\n", picture.width(), picture.height()); picture.show(); } } /****************************************************************************** * Copyright 2002-2016, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * * Algorithms, 4th edition by Robert Sedgewick and Kevin Wayne, * Addison-Wesley Professional, 2011, ISBN 0-321-57351-X. * http://algs4.cs.princeton.edu * * * algs4.jar is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * algs4.jar is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with algs4.jar. If not, see http://www.gnu.org/licenses. ******************************************************************************/ ================================================ FILE: code/algs4/PictureDump.java ================================================ /****************************************************************************** * Compilation: javac PictureDump.java * Execution: java PictureDump width height < file * Dependencies: BinaryStdIn.java Picture.java * Data file: http://introcs.cs.princeton.edu/stdlib/abra.txt * * Reads in a binary file and writes out the bits as w-by-h picture, * with the 1 bits in black and the 0 bits in white. * * % more abra.txt * ABRACADABRA! * * % java PictureDump 16 6 < abra.txt * ******************************************************************************/ package algs4; import java.awt.Color; /** * The {@code PictureDump} class provides a client for displaying the contents * of a binary file as a black-and-white picture. *

* For additional documentation, * see Section 5.5 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. *

* See also {@link BinaryDump} and {@link HexDump}. * * @author Robert Sedgewick * @author Kevin Wayne */ public class PictureDump { // Do not instantiate. private PictureDump() { } /** * Reads in a sequence of bytes from standard input and draws * them to standard drawing output as a width-by-height picture, * using black for 1 and white for 0 (and red for any leftover * pixels). * * @param args the command-line arguments */ public static void main(String[] args) { int width = Integer.parseInt(args[0]); int height = Integer.parseInt(args[1]); Picture picture = new Picture(width, height); for (int row = 0; row < height; row++) { for (int col = 0; col < width; col++) { if (!BinaryStdIn.isEmpty()) { boolean bit = BinaryStdIn.readBoolean(); if (bit) picture.set(col, row, Color.BLACK); else picture.set(col, row, Color.WHITE); } else { picture.set(col, row, Color.RED); } } } picture.show(); } } /****************************************************************************** * Copyright 2002-2016, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * * Algorithms, 4th edition by Robert Sedgewick and Kevin Wayne, * Addison-Wesley Professional, 2011, ISBN 0-321-57351-X. * http://algs4.cs.princeton.edu * * * algs4.jar is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * algs4.jar is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with algs4.jar. If not, see http://www.gnu.org/licenses. ******************************************************************************/ ================================================ FILE: code/algs4/Point2D.java ================================================ /****************************************************************************** * Compilation: javac Point2D.java * Execution: java Point2D x0 y0 N * Dependencies: StdDraw.java StdRandom.java * * Immutable point data type for points in the plane. * ******************************************************************************/ package algs4; import java.util.Arrays; import java.util.Comparator; /** * The {@code Point} class is an immutable data type to encapsulate a * two-dimensional point with real-value coordinates. *

* Note: in order to deal with the difference behavior of double and * Double with respect to -0.0 and +0.0, the Point2D constructor converts * any coordinates that are -0.0 to +0.0. *

* For additional documentation, * see Section 1.2 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne */ public final class Point2D implements Comparable { /** * Compares two points by x-coordinate. */ public static final Comparator X_ORDER = new XOrder(); /** * Compares two points by y-coordinate. */ public static final Comparator Y_ORDER = new YOrder(); /** * Compares two points by polar radius. */ public static final Comparator R_ORDER = new ROrder(); private final double x; // x coordinate private final double y; // y coordinate /** * Initializes a new point (x, y). * @param x the x-coordinate * @param y the y-coordinate * @throws IllegalArgumentException if either {@code x} or {@code y} * is {@code Double.NaN}, {@code Double.POSITIVE_INFINITY} or * {@code Double.NEGATIVE_INFINITY} */ public Point2D(double x, double y) { if (Double.isInfinite(x) || Double.isInfinite(y)) throw new IllegalArgumentException("Coordinates must be finite"); if (Double.isNaN(x) || Double.isNaN(y)) throw new IllegalArgumentException("Coordinates cannot be NaN"); if (x == 0.0) this.x = 0.0; // convert -0.0 to +0.0 else this.x = x; if (y == 0.0) this.y = 0.0; // convert -0.0 to +0.0 else this.y = y; } /** * Returns the x-coordinate. * @return the x-coordinate */ public double x() { return x; } /** * Returns the y-coordinate. * @return the y-coordinate */ public double y() { return y; } /** * Returns the polar radius of this point. * @return the polar radius of this point in polar coordiantes: sqrt(x*x + y*y) */ public double r() { return Math.sqrt(x*x + y*y); } /** * Returns the angle of this point in polar coordinates. * @return the angle (in radians) of this point in polar coordiantes (between -pi/2 and pi/2) */ public double theta() { return Math.atan2(y, x); } /** * Returns the angle between this point and that point. * @return the angle in radians (between -pi and pi) between this point and that point (0 if equal) */ private double angleTo(Point2D that) { double dx = that.x - this.x; double dy = that.y - this.y; return Math.atan2(dy, dx); } /** * Returns true if a→b→c is a counterclockwise turn. * @param a first point * @param b second point * @param c third point * @return { -1, 0, +1 } if a→b→c is a { clockwise, collinear; counterclocwise } turn. */ public static int ccw(Point2D a, Point2D b, Point2D c) { double area2 = (b.x-a.x)*(c.y-a.y) - (b.y-a.y)*(c.x-a.x); if (area2 < 0) return -1; else if (area2 > 0) return +1; else return 0; } /** * Returns twice the signed area of the triangle a-b-c. * @param a first point * @param b second point * @param c third point * @return twice the signed area of the triangle a-b-c */ public static double area2(Point2D a, Point2D b, Point2D c) { return (b.x-a.x)*(c.y-a.y) - (b.y-a.y)*(c.x-a.x); } /** * Returns the Euclidean distance between this point and that point. * @param that the other point * @return the Euclidean distance between this point and that point */ public double distanceTo(Point2D that) { double dx = this.x - that.x; double dy = this.y - that.y; return Math.sqrt(dx*dx + dy*dy); } /** * Returns the square of the Euclidean distance between this point and that point. * @param that the other point * @return the square of the Euclidean distance between this point and that point */ public double distanceSquaredTo(Point2D that) { double dx = this.x - that.x; double dy = this.y - that.y; return dx*dx + dy*dy; } /** * Compares two points by y-coordinate, breaking ties by x-coordinate. * Formally, the invoking point (x0, y0) is less than the argument point (x1, y1) * if and only if either {@code y0 < y1} or if {@code y0 == y1} and {@code x0 < x1}. * * @param that the other point * @return the value {@code 0} if this string is equal to the argument * string (precisely when {@code equals()} returns {@code true}); * a negative integer if this point is less than the argument * point; and a positive integer if this point is greater than the * argument point */ public int compareTo(Point2D that) { if (this.y < that.y) return -1; if (this.y > that.y) return +1; if (this.x < that.x) return -1; if (this.x > that.x) return +1; return 0; } /** * Compares two points by polar angle (between 0 and 2pi) with respect to this point. * * @return the comparator */ public Comparator polarOrder() { return new PolarOrder(); } /** * Compares two points by atan2() angle (between -pi and pi) with respect to this point. * * @return the comparator */ public Comparator atan2Order() { return new Atan2Order(); } /** * Compares two points by distance to this point. * * @return the comparator */ public Comparator distanceToOrder() { return new DistanceToOrder(); } // compare points according to their x-coordinate private static class XOrder implements Comparator { public int compare(Point2D p, Point2D q) { if (p.x < q.x) return -1; if (p.x > q.x) return +1; return 0; } } // compare points according to their y-coordinate private static class YOrder implements Comparator { public int compare(Point2D p, Point2D q) { if (p.y < q.y) return -1; if (p.y > q.y) return +1; return 0; } } // compare points according to their polar radius private static class ROrder implements Comparator { public int compare(Point2D p, Point2D q) { double delta = (p.x*p.x + p.y*p.y) - (q.x*q.x + q.y*q.y); if (delta < 0) return -1; if (delta > 0) return +1; return 0; } } // compare other points relative to atan2 angle (bewteen -pi/2 and pi/2) they make with this Point private class Atan2Order implements Comparator { public int compare(Point2D q1, Point2D q2) { double angle1 = angleTo(q1); double angle2 = angleTo(q2); if (angle1 < angle2) return -1; else if (angle1 > angle2) return +1; else return 0; } } // compare other points relative to polar angle (between 0 and 2pi) they make with this Point private class PolarOrder implements Comparator { public int compare(Point2D q1, Point2D q2) { double dx1 = q1.x - x; double dy1 = q1.y - y; double dx2 = q2.x - x; double dy2 = q2.y - y; if (dy1 >= 0 && dy2 < 0) return -1; // q1 above; q2 below else if (dy2 >= 0 && dy1 < 0) return +1; // q1 below; q2 above else if (dy1 == 0 && dy2 == 0) { // 3-collinear and horizontal if (dx1 >= 0 && dx2 < 0) return -1; else if (dx2 >= 0 && dx1 < 0) return +1; else return 0; } else return -ccw(Point2D.this, q1, q2); // both above or below // Note: ccw() recomputes dx1, dy1, dx2, and dy2 } } // compare points according to their distance to this point private class DistanceToOrder implements Comparator { public int compare(Point2D p, Point2D q) { double dist1 = distanceSquaredTo(p); double dist2 = distanceSquaredTo(q); if (dist1 < dist2) return -1; else if (dist1 > dist2) return +1; else return 0; } } /** * Compares this point to the specified point. * * @param other the other point * @return {@code true} if this point equals {@code other}; * {@code false} otherwise */ @Override public boolean equals(Object other) { if (other == this) return true; if (other == null) return false; if (other.getClass() != this.getClass()) return false; Point2D that = (Point2D) other; return this.x == that.x && this.y == that.y; } /** * Return a string representation of this point. * @return a string representation of this point in the format (x, y) */ @Override public String toString() { return "(" + x + ", " + y + ")"; } /** * Returns an integer hash code for this point. * @return an integer hash code for this point */ @Override public int hashCode() { int hashX = ((Double) x).hashCode(); int hashY = ((Double) y).hashCode(); return 31*hashX + hashY; } /** * Plot this point using standard draw. */ public void draw() { StdDraw.point(x, y); } /** * Plot a line from this point to that point using standard draw. * @param that the other point */ public void drawTo(Point2D that) { StdDraw.line(this.x, this.y, that.x, that.y); } /** * Unit tests the point data type. * * @param args the command-line arguments */ public static void main(String[] args) { int x0 = Integer.parseInt(args[0]); int y0 = Integer.parseInt(args[1]); int n = Integer.parseInt(args[2]); StdDraw.setCanvasSize(800, 800); StdDraw.setXscale(0, 100); StdDraw.setYscale(0, 100); StdDraw.setPenRadius(0.005); StdDraw.enableDoubleBuffering(); Point2D[] points = new Point2D[n]; for (int i = 0; i < n; i++) { int x = StdRandom.uniform(100); int y = StdRandom.uniform(100); points[i] = new Point2D(x, y); points[i].draw(); } // draw p = (x0, x1) in red Point2D p = new Point2D(x0, y0); StdDraw.setPenColor(StdDraw.RED); StdDraw.setPenRadius(0.02); p.draw(); // draw line segments from p to each point, one at a time, in polar order StdDraw.setPenRadius(); StdDraw.setPenColor(StdDraw.BLUE); Arrays.sort(points, p.polarOrder()); for (int i = 0; i < n; i++) { p.drawTo(points[i]); StdDraw.show(); StdDraw.pause(100); } } } /****************************************************************************** * Copyright 2002-2016, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * * Algorithms, 4th edition by Robert Sedgewick and Kevin Wayne, * Addison-Wesley Professional, 2011, ISBN 0-321-57351-X. * http://algs4.cs.princeton.edu * * * algs4.jar is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * algs4.jar is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with algs4.jar. If not, see http://www.gnu.org/licenses. ******************************************************************************/ ================================================ FILE: code/algs4/Queue.java ================================================ /****************************************************************************** * Compilation: javac Queue.java * Execution: java Queue < input.txt * Dependencies: StdIn.java StdOut.java * Data files: http://algs4.cs.princeton.edu/13stacks/tobe.txt * * A generic queue, implemented using a linked list. * * % java Queue < tobe.txt * to be or not to be (2 left on queue) * ******************************************************************************/ package algs4; import java.util.Iterator; import java.util.NoSuchElementException; /** * The {@code Queue} class represents a first-in-first-out (FIFO) * queue of generic items. * It supports the usual enqueue and dequeue * operations, along with methods for peeking at the first item, * testing if the queue is empty, and iterating through * the items in FIFO order. *

* This implementation uses a singly-linked list with a static nested class for * linked-list nodes. See {@link LinkedQueue} for the version from the * textbook that uses a non-static nested class. * The enqueue, dequeue, peek, size, and is-empty * operations all take constant time in the worst case. *

* For additional documentation, see Section 1.3 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne * * @param the generic type of an item in this queue */ public class Queue implements Iterable { private Node first; // beginning of queue private Node last; // end of queue private int n; // number of elements on queue // helper linked list class private static class Node { private Item item; private Node next; } /** * Initializes an empty queue. */ public Queue() { first = null; last = null; n = 0; } /** * Returns true if this queue is empty. * * @return {@code true} if this queue is empty; {@code false} otherwise */ public boolean isEmpty() { return first == null; } /** * Returns the number of items in this queue. * * @return the number of items in this queue */ public int size() { return n; } /** * Returns the item least recently added to this queue. * * @return the item least recently added to this queue * @throws NoSuchElementException if this queue is empty */ public Item peek() { if (isEmpty()) throw new NoSuchElementException("Queue underflow"); return first.item; } /** * Adds the item to this queue. * * @param item the item to add */ public void enqueue(Item item) { Node oldlast = last; last = new Node(); last.item = item; last.next = null; if (isEmpty()) first = last; else oldlast.next = last; n++; } /** * Removes and returns the item on this queue that was least recently added. * * @return the item on this queue that was least recently added * @throws NoSuchElementException if this queue is empty */ public Item dequeue() { if (isEmpty()) throw new NoSuchElementException("Queue underflow"); Item item = first.item; first = first.next; n--; if (isEmpty()) last = null; // to avoid loitering return item; } /** * Returns a string representation of this queue. * * @return the sequence of items in FIFO order, separated by spaces */ public String toString() { StringBuilder s = new StringBuilder(); for (Item item : this) { s.append(item); s.append(' '); } return s.toString(); } /** * Returns an iterator that iterates over the items in this queue in FIFO order. * * @return an iterator that iterates over the items in this queue in FIFO order */ public Iterator iterator() { return new ListIterator(first); } // an iterator, doesn't implement remove() since it's optional private class ListIterator implements Iterator { private Node current; public ListIterator(Node first) { current = first; } public boolean hasNext() { return current != null; } public void remove() { throw new UnsupportedOperationException(); } public Item next() { if (!hasNext()) throw new NoSuchElementException(); Item item = current.item; current = current.next; return item; } } /** * Unit tests the {@code Queue} data type. * * @param args the command-line arguments */ public static void main(String[] args) { Queue queue = new Queue(); while (!StdIn.isEmpty()) { String item = StdIn.readString(); if (!item.equals("-")) queue.enqueue(item); else if (!queue.isEmpty()) StdOut.print(queue.dequeue() + " "); } StdOut.println("(" + queue.size() + " left on queue)"); } } /****************************************************************************** * Copyright 2002-2016, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * * Algorithms, 4th edition by Robert Sedgewick and Kevin Wayne, * Addison-Wesley Professional, 2011, ISBN 0-321-57351-X. * http://algs4.cs.princeton.edu * * * algs4.jar is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * algs4.jar is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with algs4.jar. If not, see http://www.gnu.org/licenses. ******************************************************************************/ ================================================ FILE: code/algs4/SET.java ================================================ /****************************************************************************** * Compilation: javac SET.java * Execution: java SET * Dependencies: StdOut.java * * Set implementation using Java's TreeSet library. * Does not allow duplicates. * * % java SET * 128.112.136.11 * 208.216.181.15 * null * ******************************************************************************/ package algs4; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.TreeSet; /** * The {@code SET} class represents an ordered set of comparable keys. * It supports the usual add, contains, and delete * methods. It also provides ordered methods for finding the minimum, * maximum, floor, and ceiling and set methods * for union, intersection, and equality. *

* Even though this implementation include the method {@code equals()}, it * does not support the method {@code hashCode()} because sets are mutable. *

* This implementation uses a balanced binary search tree. It requires that * the key type implements the {@code Comparable} interface and calls the * {@code compareTo()} and method to compare two keys. It does not call either * {@code equals()} or {@code hashCode()}. * The add, contains, delete, minimum, * maximum, ceiling, and floor methods take * logarithmic time in the worst case. * The size, and is-empty operations take constant time. * Construction takes constant time. *

* This implementation uses a balanced binary search tree. It requires that * For additional documentation, see * Section 3.5 of * Algorithms in Java, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne * * @param the generic type of a key in this set */ public class SET> implements Iterable { private TreeSet set; /** * Initializes an empty set. */ public SET() { set = new TreeSet(); } /** * Initializes a new set that is an independent copy of the specified set. * * @param x the set to copy */ public SET(SET x) { set = new TreeSet(x.set); } /** * Adds the key to this set (if it is not already present). * * @param key the key to add * @throws IllegalArgumentException if {@code key} is {@code null} */ public void add(Key key) { if (key == null) throw new IllegalArgumentException("called add() with a null key"); set.add(key); } /** * Returns true if this set contains the given key. * * @param key the key * @return {@code true} if this set contains {@code key}; * {@code false} otherwise * @throws IllegalArgumentException if {@code key} is {@code null} */ public boolean contains(Key key) { if (key == null) throw new IllegalArgumentException("called contains() with a null key"); return set.contains(key); } /** * Removes the specified key from this set (if the set contains the specified key). * * @param key the key * @throws IllegalArgumentException if {@code key} is {@code null} */ public void delete(Key key) { if (key == null) throw new IllegalArgumentException("called delete() with a null key"); set.remove(key); } /** * Returns the number of keys in this set. * * @return the number of keys in this set */ public int size() { return set.size(); } /** * Returns true if this set is empty. * * @return {@code true} if this set is empty; * {@code false} otherwise */ public boolean isEmpty() { return size() == 0; } /** * Returns all of the keys in this set, as an iterator. * To iterate over all of the keys in a set named {@code set}, use the * foreach notation: {@code for (Key key : set)}. * * @return an iterator to all of the keys in this set */ public Iterator iterator() { return set.iterator(); } /** * Returns the largest key in this set. * * @return the largest key in this set * @throws NoSuchElementException if this set is empty */ public Key max() { if (isEmpty()) throw new NoSuchElementException("called max() with empty set"); return set.last(); } /** * Returns the smallest key in this set. * * @return the smallest key in this set * @throws NoSuchElementException if this set is empty */ public Key min() { if (isEmpty()) throw new NoSuchElementException("called min() with empty set"); return set.first(); } /** * Returns the smallest key in this set greater than or equal to {@code key}. * * @param key the key * @return the smallest key in this set greater than or equal to {@code key} * @throws IllegalArgumentException if {@code key} is {@code null} * @throws NoSuchElementException if there is no such key */ public Key ceiling(Key key) { if (key == null) throw new IllegalArgumentException("called ceiling() with a null key"); Key k = set.ceiling(key); if (k == null) throw new NoSuchElementException("all keys are less than " + key); return k; } /** * Returns the largest key in this set less than or equal to {@code key}. * * @param key the key * @return the largest key in this set table less than or equal to {@code key} * @throws IllegalArgumentException if {@code key} is {@code null} * @throws NoSuchElementException if there is no such key */ public Key floor(Key key) { if (key == null) throw new IllegalArgumentException("called floor() with a null key"); Key k = set.floor(key); if (k == null) throw new NoSuchElementException("all keys are greater than " + key); return k; } /** * Returns the union of this set and that set. * * @param that the other set * @return the union of this set and that set * @throws IllegalArgumentException if {@code that} is {@code null} */ public SET union(SET that) { if (that == null) throw new IllegalArgumentException("called union() with a null argument"); SET c = new SET(); for (Key x : this) { c.add(x); } for (Key x : that) { c.add(x); } return c; } /** * Returns the intersection of this set and that set. * * @param that the other set * @return the intersection of this set and that set * @throws IllegalArgumentException if {@code that} is {@code null} */ public SET intersects(SET that) { if (that == null) throw new IllegalArgumentException("called intersects() with a null argument"); SET c = new SET(); if (this.size() < that.size()) { for (Key x : this) { if (that.contains(x)) c.add(x); } } else { for (Key x : that) { if (this.contains(x)) c.add(x); } } return c; } /** * Compares this set to the specified set. *

* Note that this method declares two empty sets to be equal * even if they are parameterized by different generic types. * This is consistent with the behavior of {@code equals()} * within Java's Collections framework. * * @param other the other set * @return {@code true} if this set equals {@code other}; * {@code false} otherwise */ @Override public boolean equals(Object other) { if (other == this) return true; if (other == null) return false; if (other.getClass() != this.getClass()) return false; SET that = (SET) other; return this.set.equals(that.set); } /** * This operation is not supported because sets are mutable. * * @return does not return a value * @throws UnsupportedOperationException if called */ @Override public int hashCode() { throw new UnsupportedOperationException("hashCode() is not supported because sets are mutable"); } /** * Returns a string representation of this set. * * @return a string representation of this set, enclosed in curly braces, * with adjacent keys separated by a comma and a space */ @Override public String toString() { String s = set.toString(); return "{ " + s.substring(1, s.length() - 1) + " }"; } /** * Unit tests the {@code SET} data type. * * @param args the command-line arguments */ public static void main(String[] args) { SET set = new SET(); StdOut.println("set = " + set); // insert some keys set.add("www.cs.princeton.edu"); set.add("www.cs.princeton.edu"); // overwrite old value set.add("www.princeton.edu"); set.add("www.math.princeton.edu"); set.add("www.yale.edu"); set.add("www.amazon.com"); set.add("www.simpsons.com"); set.add("www.stanford.edu"); set.add("www.google.com"); set.add("www.ibm.com"); set.add("www.apple.com"); set.add("www.slashdot.com"); set.add("www.whitehouse.gov"); set.add("www.espn.com"); set.add("www.snopes.com"); set.add("www.movies.com"); set.add("www.cnn.com"); set.add("www.iitb.ac.in"); StdOut.println(set.contains("www.cs.princeton.edu")); StdOut.println(!set.contains("www.harvardsucks.com")); StdOut.println(set.contains("www.simpsons.com")); StdOut.println(); StdOut.println("ceiling(www.simpsonr.com) = " + set.ceiling("www.simpsonr.com")); StdOut.println("ceiling(www.simpsons.com) = " + set.ceiling("www.simpsons.com")); StdOut.println("ceiling(www.simpsont.com) = " + set.ceiling("www.simpsont.com")); StdOut.println("floor(www.simpsonr.com) = " + set.floor("www.simpsonr.com")); StdOut.println("floor(www.simpsons.com) = " + set.floor("www.simpsons.com")); StdOut.println("floor(www.simpsont.com) = " + set.floor("www.simpsont.com")); StdOut.println(); StdOut.println("set = " + set); StdOut.println(); // print out all keys in this set in lexicographic order for (String s : set) { StdOut.println(s); } StdOut.println(); SET set2 = new SET(set); StdOut.println(set.equals(set2)); } } /****************************************************************************** * Copyright 2002-2016, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * * Algorithms, 4th edition by Robert Sedgewick and Kevin Wayne, * Addison-Wesley Professional, 2011, ISBN 0-321-57351-X. * http://algs4.cs.princeton.edu * * * algs4.jar is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * algs4.jar is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with algs4.jar. If not, see http://www.gnu.org/licenses. ******************************************************************************/ ================================================ FILE: code/algs4/ST.java ================================================ /****************************************************************************** * Compilation: javac ST.java * Execution: java ST < input.txt * Dependencies: StdIn.java StdOut.java * Data files: http://algs4.cs.princeton.edu/35applications/tinyST.txt * * Sorted symbol table implementation using a java.util.TreeMap. * Does not allow duplicates. * ******************************************************************************/ package algs4; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.TreeMap; /** * The {@code ST} class represents an ordered symbol table of generic * key-value pairs. * It supports the usual put, get, contains, * delete, size, and is-empty methods. * It also provides ordered methods for finding the minimum, * maximum, floor, and ceiling. * It also provides a keys method for iterating over all of the keys. * A symbol table implements the associative array abstraction: * when associating a value with a key that is already in the symbol table, * the convention is to replace the old value with the new value. * Unlike {@link java.util.Map}, this class uses the convention that * values cannot be {@code null}—setting the * value associated with a key to {@code null} is equivalent to deleting the key * from the symbol table. *

* This implementation uses a balanced binary search tree. It requires that * the key type implements the {@code Comparable} interface and calls the * {@code compareTo()} and method to compare two keys. It does not call either * {@code equals()} or {@code hashCode()}. * The put, contains, remove, minimum, * maximum, ceiling, and floor operations each take * logarithmic time in the worst case. * The size, and is-empty operations take constant time. * Construction takes constant time. *

* For additional documentation, see Section 3.5 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne * * @param the generic type of keys in this symbol table * @param the generic type of values in this symbol table */ public class ST, Value> implements Iterable { private TreeMap st; /** * Initializes an empty symbol table. */ public ST() { st = new TreeMap(); } /** * Returns the value associated with the given key in this symbol table. * * @param key the key * @return the value associated with the given key if the key is in this symbol table; * {@code null} if the key is not in this symbol table * @throws IllegalArgumentException if {@code key} is {@code null} */ public Value get(Key key) { if (key == null) throw new IllegalArgumentException("called get() with null key"); return st.get(key); } /** * Inserts the specified key-value pair into the symbol table, overwriting the old * value with the new value if the symbol table already contains the specified key. * Deletes the specified key (and its associated value) from this symbol table * if the specified value is {@code null}. * * @param key the key * @param val the value * @throws IllegalArgumentException if {@code key} is {@code null} */ public void put(Key key, Value val) { if (key == null) throw new IllegalArgumentException("called put() with null key"); if (val == null) st.remove(key); else st.put(key, val); } /** * Removes the specified key and its associated value from this symbol table * (if the key is in this symbol table). * * @param key the key * @throws IllegalArgumentException if {@code key} is {@code null} */ public void delete(Key key) { if (key == null) throw new IllegalArgumentException("called delete() with null key"); st.remove(key); } /** * Returns true if this symbol table contain the given key. * * @param key the key * @return {@code true} if this symbol table contains {@code key} and * {@code false} otherwise * @throws IllegalArgumentException if {@code key} is {@code null} */ public boolean contains(Key key) { if (key == null) throw new IllegalArgumentException("called contains() with null key"); return st.containsKey(key); } /** * Returns the number of key-value pairs in this symbol table. * * @return the number of key-value pairs in this symbol table */ public int size() { return st.size(); } /** * Returns true if this symbol table is empty. * * @return {@code true} if this symbol table is empty and {@code false} otherwise */ public boolean isEmpty() { return size() == 0; } /** * Returns all keys in this symbol table. *

* To iterate over all of the keys in the symbol table named {@code st}, * use the foreach notation: {@code for (Key key : st.keys())}. * * @return all keys in this symbol table */ public Iterable keys() { return st.keySet(); } /** * Returns all of the keys in this symbol table. * To iterate over all of the keys in a symbol table named {@code st}, use the * foreach notation: {@code for (Key key : st)}. *

* This method is provided for backward compatibility with the version from * Introduction to Programming in Java: An Interdisciplinary Approach. * * @return an iterator to all of the keys in this symbol table * @deprecated Replaced by {@link #keys()}. */ @Deprecated public Iterator iterator() { return st.keySet().iterator(); } /** * Returns the smallest key in this symbol table. * * @return the smallest key in this symbol table * @throws NoSuchElementException if this symbol table is empty */ public Key min() { if (isEmpty()) throw new NoSuchElementException("called min() with empty symbol table"); return st.firstKey(); } /** * Returns the largest key in this symbol table. * * @return the largest key in this symbol table * @throws NoSuchElementException if this symbol table is empty */ public Key max() { if (isEmpty()) throw new NoSuchElementException("called max() with empty symbol table"); return st.lastKey(); } /** * Returns the smallest key in this symbol table greater than or equal to {@code key}. * * @param key the key * @return the smallest key in this symbol table greater than or equal to {@code key} * @throws NoSuchElementException if there is no such key * @throws IllegalArgumentException if {@code key} is {@code null} */ public Key ceiling(Key key) { if (key == null) throw new IllegalArgumentException("called ceiling() with null key"); Key k = st.ceilingKey(key); if (k == null) throw new NoSuchElementException("all keys are less than " + key); return k; } /** * Returns the largest key in this symbol table less than or equal to {@code key}. * * @param key the key * @return the largest key in this symbol table less than or equal to {@code key} * @throws NoSuchElementException if there is no such key * @throws IllegalArgumentException if {@code key} is {@code null} */ public Key floor(Key key) { if (key == null) throw new IllegalArgumentException("called floor() with null key"); Key k = st.floorKey(key); if (k == null) throw new NoSuchElementException("all keys are greater than " + key); return k; } /** * Unit tests the {@code ST} data type. * * @param args the command-line arguments */ public static void main(String[] args) { ST st = new ST(); for (int i = 0; !StdIn.isEmpty(); i++) { String key = StdIn.readString(); st.put(key, i); } for (String s : st.keys()) StdOut.println(s + " " + st.get(s)); } } /****************************************************************************** * Copyright 2002-2016, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * * Algorithms, 4th edition by Robert Sedgewick and Kevin Wayne, * Addison-Wesley Professional, 2011, ISBN 0-321-57351-X. * http://algs4.cs.princeton.edu * * * algs4.jar is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * algs4.jar is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with algs4.jar. If not, see http://www.gnu.org/licenses. ******************************************************************************/ ================================================ FILE: code/algs4/Stack.java ================================================ /****************************************************************************** * Compilation: javac Stack.java * Execution: java Stack < input.txt * Dependencies: StdIn.java StdOut.java * Data files: http://algs4.cs.princeton.edu/13stacks/tobe.txt * * A generic stack, implemented using a singly-linked list. * Each stack element is of type Item. * * This version uses a static nested class Node (to save 8 bytes per * Node), whereas the version in the textbook uses a non-static nested * class (for simplicity). * * % more tobe.txt * to be or not to - be - - that - - - is * * % java Stack < tobe.txt * to be not that or be (2 left on stack) * ******************************************************************************/ package algs4; import java.util.Iterator; import java.util.NoSuchElementException; /** * The {@code Stack} class represents a last-in-first-out (LIFO) stack of generic items. * It supports the usual push and pop operations, along with methods * for peeking at the top item, testing if the stack is empty, and iterating through * the items in LIFO order. *

* This implementation uses a singly-linked list with a static nested class for * linked-list nodes. See {@link LinkedStack} for the version from the * textbook that uses a non-static nested class. * The push, pop, peek, size, and is-empty * operations all take constant time in the worst case. *

* For additional documentation, * see Section 1.3 of * Algorithms, 4th Edition by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne * * @param the generic type of an item in this stack */ public class Stack implements Iterable { private Node first; // top of stack private int n; // size of the stack // helper linked list class private static class Node { private Item item; private Node next; } /** * Initializes an empty stack. */ public Stack() { first = null; n = 0; } /** * Returns true if this stack is empty. * * @return true if this stack is empty; false otherwise */ public boolean isEmpty() { return first == null; } /** * Returns the number of items in this stack. * * @return the number of items in this stack */ public int size() { return n; } /** * Adds the item to this stack. * * @param item the item to add */ public void push(Item item) { Node oldfirst = first; first = new Node(); first.item = item; first.next = oldfirst; n++; } /** * Removes and returns the item most recently added to this stack. * * @return the item most recently added * @throws NoSuchElementException if this stack is empty */ public Item pop() { if (isEmpty()) throw new NoSuchElementException("Stack underflow"); Item item = first.item; // save item to return first = first.next; // delete first node n--; return item; // return the saved item } /** * Returns (but does not remove) the item most recently added to this stack. * * @return the item most recently added to this stack * @throws NoSuchElementException if this stack is empty */ public Item peek() { if (isEmpty()) throw new NoSuchElementException("Stack underflow"); return first.item; } /** * Returns a string representation of this stack. * * @return the sequence of items in this stack in LIFO order, separated by spaces */ public String toString() { StringBuilder s = new StringBuilder(); for (Item item : this) { s.append(item); s.append(' '); } return s.toString(); } /** * Returns an iterator to this stack that iterates through the items in LIFO order. * * @return an iterator to this stack that iterates through the items in LIFO order */ public Iterator iterator() { return new ListIterator(first); } // an iterator, doesn't implement remove() since it's optional private class ListIterator implements Iterator { private Node current; public ListIterator(Node first) { current = first; } public boolean hasNext() { return current != null; } public void remove() { throw new UnsupportedOperationException(); } public Item next() { if (!hasNext()) throw new NoSuchElementException(); Item item = current.item; current = current.next; return item; } } /** * Unit tests the {@code Stack} data type. * * @param args the command-line arguments */ public static void main(String[] args) { Stack stack = new Stack(); while (!StdIn.isEmpty()) { String item = StdIn.readString(); if (!item.equals("-")) stack.push(item); else if (!stack.isEmpty()) StdOut.print(stack.pop() + " "); } StdOut.println("(" + stack.size() + " left on stack)"); } } /****************************************************************************** * Copyright 2002-2016, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * * Algorithms, 4th edition by Robert Sedgewick and Kevin Wayne, * Addison-Wesley Professional, 2011, ISBN 0-321-57351-X. * http://algs4.cs.princeton.edu * * * algs4.jar is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * algs4.jar is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with algs4.jar. If not, see http://www.gnu.org/licenses. ******************************************************************************/ ================================================ FILE: code/algs4/StdDraw.java ================================================ /****************************************************************************** * Compilation: javac StdDraw.java * Execution: java StdDraw * Dependencies: none * * Standard drawing library. This class provides a basic capability for * creating drawings with your programs. It uses a simple graphics model that * allows you to create drawings consisting of points, lines, and curves * in a window on your computer and to save the drawings to a file. * * Todo * ---- * - Add support for gradient fill, etc. * - Fix setCanvasSize() so that it can only be called once. * - On some systems, drawing a line (or other shape) that extends way * beyond canvas (e.g., to infinity) dimensions does not get drawn. * * Remarks * ------- * - don't use AffineTransform for rescaling since it inverts * images and strings * ******************************************************************************/ package algs4; import java.awt.BasicStroke; import java.awt.Color; import java.awt.FileDialog; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.MediaTracker; import java.awt.RenderingHints; import java.awt.Toolkit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.geom.Arc2D; import java.awt.geom.Ellipse2D; import java.awt.geom.GeneralPath; import java.awt.geom.Line2D; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.awt.image.DirectColorModel; import java.awt.image.WritableRaster; import java.io.File; import java.io.IOException; import java.net.URL; import java.util.LinkedList; import java.util.TreeSet; import java.util.NoSuchElementException; import javax.imageio.ImageIO; import javax.swing.ImageIcon; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.KeyStroke; /** * The {@code StdDraw} class provides a basic capability for * creating drawings with your programs. It uses a simple graphics model that * allows you to create drawings consisting of points, lines, squares, * circles, and other geometric shapes in a window on your computer and * to save the drawings to a file. Standard drawing also includes * facilities for text, color, pictures, and animation, along with * user interaction via the keyboard and mouse. *

* Getting started. * To use standard drawing, you must have {@code StdDraw.class} in your * Java classpath. If you used our autoinstaller, you should be all set. * Otherwise, download * StdDraw.java * and put a copy in your working directory. *

* Now, type the following short program into your editor: *

 *   public class TestStdDraw {
 *       public static void main(String[] args) {
 *           StdDraw.setPenRadius(0.05);
 *           StdDraw.setPenColor(StdDraw.BLUE);
 *           StdDraw.point(0.5, 0.5);
 *           StdDraw.setPenColor(StdDraw.MAGENTA);
 *           StdDraw.line(0.2, 0.2, 0.8, 0.2);
 *       }
 *   }
 *  
* If you compile and execute the program, you should see a window * appear with a thick magenta line and a blue point. * This program illustrates the two main types of methods in standard * drawing—methods that draw geometric shapes and methods that * control drawing parameters. * The methods {@code StdDraw.line()} and {@code StdDraw.point()} * draw lines and points; the methods {@code StdDraw.setPenRadius()} * and {@code StdDraw.setPenColor()} control the line thickness and color. *

* Points and lines. * You can draw points and line segments with the following methods: *

*

* The x- and y-coordinates must be in the drawing area * (between 0 and 1 and by default) or the points and lines will not be visible. *

* Squares, circles, rectangles, and ellipses. * You can draw squares, circles, rectangles, and ellipses using * the following methods: *

*

* All of these methods take as arguments the location and size of the shape. * The location is always specified by the x- and y-coordinates * of its center. * The size of a circle is specified by its radius and the size of an ellipse is * specified by the lengths of its semi-major and semi-minor axes. * The size of a square or rectangle is specified by its half-width or half-height. * The convention for drawing squares and rectangles is parallel to those for * drawing circles and ellipses, but may be unexpected to the uninitiated. *

* The methods above trace outlines of the given shapes. The following methods * draw filled versions: *

*

* Circular arcs. * You can draw circular arcs with the following method: *

*

* The arc is from the circle centered at (x, y) of the specified radius. * The arc extends from angle1 to angle2. By convention, the angles are * polar (counterclockwise angle from the x-axis) * and represented in degrees. For example, {@code StdDraw.arc(0.0, 0.0, 1.0, 0, 90)} * draws the arc of the unit circle from 3 o'clock (0 degrees) to 12 o'clock (90 degrees). *

* Polygons. * You can draw polygons with the following methods: *

*

* The points in the polygon are ({@code x[i]}, {@code y[i]}). * For example, the following code fragment draws a filled diamond * with vertices (0.1, 0.2), (0.2, 0.3), (0.3, 0.2), and (0.2, 0.1): *

 *   double[] x = { 0.1, 0.2, 0.3, 0.2 };
 *   double[] y = { 0.2, 0.3, 0.2, 0.1 };
 *   StdDraw.filledPolygon(x, y);
 *  
*

* Pen size. * The pen is circular, so that when you set the pen radius to r * and draw a point, you get a circle of radius r. Also, lines are * of thickness 2r and have rounded ends. The default pen radius * is 0.005 and is not affected by coordinate scaling. This default pen * radius is about 1/200 the width of the default canvas, so that if * you draw 100 points equally spaced along a horizontal or vertical line, * you will be able to see individual circles, but if you draw 200 such * points, the result will look like a line. *

*

* For example, {@code StdDraw.setPenRadius(0.025)} makes * the thickness of the lines and the size of the points to be five times * the 0.005 default. * To draw points with the minimum possible radius (one pixel on typical * displays), set the pen radius to 0.0. *

* Pen color. * All geometric shapes (such as points, lines, and circles) are drawn using * the current pen color. By default, it is black. * You can change the pen color with the following methods: *

*

* The first method allows you to specify colors using the RGB color system. * This color picker * is a convenient way to find a desired color. * The second method allows you to specify colors using the * {@link Color} data type that is discussed in Chapter 3. Until then, * you can use this method with one of these predefined colors in standard drawing: * {@link #BLACK}, {@link #BLUE}, {@link #CYAN}, {@link #DARK_GRAY}, {@link #GRAY}, * {@link #GREEN}, {@link #LIGHT_GRAY}, {@link #MAGENTA}, {@link #ORANGE}, * {@link #PINK}, {@link #RED}, {@link #WHITE}, and {@link #YELLOW}. * For example, {@code StdDraw.setPenColor(StdDraw.MAGENTA)} sets the * pen color to magenta. *

* Canvas size. * By default, all drawing takes places in a 512-by-512 canvas. * The canvas does not include the window title or window border. * You can change the size of the canvas with the following method: *

*

* This sets the canvas size to be width-by-height pixels. * It also erases the current drawing and resets the coordinate system, * pen radius, pen color, and font back to their default values. * Ordinarly, this method is called once, at the very beginning of a program. * For example, {@code StdDraw.setCanvasSize(800, 800)} * sets the canvas size to be 800-by-800 pixels. *

* Canvas scale and coordinate system. * By default, all drawing takes places in the unit square, with (0, 0) at * lower left and (1, 1) at upper right. You can change the default * coordinate system with the following methods: *

*

* The arguments are the coordinates of the minimum and maximum * x- or y-coordinates that will appear in the canvas. * For example, if you wish to use the default coordinate system but * leave a small margin, you can call {@code StdDraw.setScale(-.05, 1.05)}. *

* These methods change the coordinate system for subsequent drawing * commands; they do not affect previous drawings. * These methods do not change the canvas size; so, if the x- * and y-scales are different, squares will become rectangles * and circles will become ellipsoidal. *

* Text. * You can use the following methods to annotate your drawings with text: *

*

* The first two methods write the specified text in the current font, * centered at (x, y). * The second method allows you to rotate the text. * The last two methods either left- or right-align the text at (x, y). *

* The default font is a Sans Serif font with point size 16. * You can use the following method to change the font: *

*

* You use the {@link Font} data type to specify the font. This allows you to * choose the face, size, and style of the font. For example, the following * code fragment sets the font to Arial Bold, 60 point. *

 *   Font font = new Font("Arial", Font.BOLD, 60);
 *   StdDraw.setFont(font);
 *   StdDraw.text(0.5, 0.5, "Hello, World");
 *  
*

* Images. * You can use the following methods to add images to your drawings: *

*

* These methods draw the specified image, centered at (x, y). * The supported image formats are JPEG, PNG, and GIF. * The image will display at its native size, independent of the coordinate system. * Optionally, you can rotate the image a specified number of degrees counterclockwise * or rescale it to fit snugly inside a width-by-height bounding box. *

* Saving to a file. * You save your image to a file using the File → Save menu option. * You can also save a file programatically using the following method: *

*

* The supported image formats are JPEG and PNG. The filename must have either the * extension .jpg or .png. * We recommend using PNG for drawing that consist solely of geometric shapes and JPEG * for drawings that contains pictures. *

* Clearing the canvas. * To clear the entire drawing canvas, you can use the following methods: *

*

* The first method clears the canvas to white; the second method * allows you to specify a color of your choice. For example, * {@code StdDraw.clear(StdDraw.LIGHT_GRAY)} clears the canvas to a shade * of gray. *

* Computer animations and double buffering. * Double buffering is one of the most powerful features of standard drawing, * enabling computer animations. * The following methods control the way in which objects are drawn: *

*

* By default, double buffering is disabled, which means that as soon as you * call a drawing * method—such as {@code point()} or {@code line()}—the * results appear on the screen. *

* When double buffering is enabled by calling {@link #enableDoubleBuffering()}, * all drawing takes place on the offscreen canvas. The offscreen canvas * is not displayed. Only when you call * {@link #show()} does your drawing get copied from the offscreen canvas to * the onscreen canvas, where it is displayed in the standard drawing window. You * can think of double buffering as collecting all of the lines, points, shapes, * and text that you tell it to draw, and then drawing them all * simultaneously, upon request. *

* The most important use of double buffering is to produce computer * animations, creating the illusion of motion by rapidly * displaying static drawings. To produce an animation, repeat * the following four steps: *

*

* The {@link #clear()}, {@link #show()}, and {@link #pause(int dt)} methods * support the first, third, and fourth of these steps, respectively. *

* For example, this code fragment animates two balls moving in a circle. *

 *   StdDraw.setScale(-2, +2);
 *   StdDraw.enableDoubleBuffering();
 *
 *   for (double t = 0.0; true; t += 0.02) {
 *       double x = Math.sin(t);
 *       double y = Math.cos(t);
 *       StdDraw.clear();
 *       StdDraw.filledCircle(x, y, 0.05);
 *       StdDraw.filledCircle(-x, -y, 0.05);
 *       StdDraw.show();
 *       StdDraw.pause(20);
 *   }
 *  
*

* Keyboard and mouse inputs. * Standard drawing has very basic support for keyboard and mouse input. * It is much less powerful than most user interface libraries provide, but also much simpler. * You can use the following methods to intercept mouse events: *

*

* The first method tells you whether a mouse button is currently being pressed. * The last two methods tells you the x- and y-coordinates of the mouse's * current position, using the same coordinate system as the canvas (the unit square, by default). * You should use these methods in an animation loop that waits a short while before trying * to poll the mouse for its current state. * You can use the following methods to intercept keyboard events: *

*

* If the user types lots of keys, they will be saved in a list until you process them. * The first method tells you whether the user has typed a key (that your program has * not yet processed). * The second method returns the next key that the user typed (that your program has * not yet processed) and removes it from the list of saved keystrokes. * The third method tells you whether a key is currently being pressed. *

* Accessing control parameters. * You can use the following methods to access the current pen color, pen radius, * and font: *

*

* These methods are useful when you want to temporarily change a * control parameter and reset it back to its original value. *

* Corner cases. * To avoid clutter, the API doesn't explicitly refer to arguments that are * null, infinity, or NaN. *

*

* Performance tricks. * Standard drawing is capable of drawing large amounts of data. * Here are a few tricks and tips: *

*

* Known bugs and issues. *

*

* Reference. * For additional documentation, * see Section 1.5 of * Introduction to Programming in Java: An Interdisciplinary Approach * by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne */ public final class StdDraw implements ActionListener, MouseListener, MouseMotionListener, KeyListener { /** * The color black. */ public static final Color BLACK = Color.BLACK; /** * The color blue. */ public static final Color BLUE = Color.BLUE; /** * The color cyan. */ public static final Color CYAN = Color.CYAN; /** * The color dark gray. */ public static final Color DARK_GRAY = Color.DARK_GRAY; /** * The color gray. */ public static final Color GRAY = Color.GRAY; /** * The color green. */ public static final Color GREEN = Color.GREEN; /** * The color light gray. */ public static final Color LIGHT_GRAY = Color.LIGHT_GRAY; /** * The color magenta. */ public static final Color MAGENTA = Color.MAGENTA; /** * The color orange. */ public static final Color ORANGE = Color.ORANGE; /** * The color pink. */ public static final Color PINK = Color.PINK; /** * The color red. */ public static final Color RED = Color.RED; /** * The color white. */ public static final Color WHITE = Color.WHITE; /** * The color yellow. */ public static final Color YELLOW = Color.YELLOW; /** * Shade of blue used in Introduction to Programming in Java. * It is Pantone 300U. The RGB values are approximately (9, 90, 166). */ public static final Color BOOK_BLUE = new Color(9, 90, 166); /** * Shade of light blue used in Introduction to Programming in Java. * The RGB values are approximately (103, 198, 243). */ public static final Color BOOK_LIGHT_BLUE = new Color(103, 198, 243); /** * Shade of red used in Algorithms, 4th edition. * It is Pantone 1805U. The RGB values are approximately (150, 35, 31). */ public static final Color BOOK_RED = new Color(150, 35, 31); // default colors private static final Color DEFAULT_PEN_COLOR = BLACK; private static final Color DEFAULT_CLEAR_COLOR = WHITE; // current pen color private static Color penColor; // default canvas size is DEFAULT_SIZE-by-DEFAULT_SIZE private static final int DEFAULT_SIZE = 512; private static int width = DEFAULT_SIZE; private static int height = DEFAULT_SIZE; // default pen radius private static final double DEFAULT_PEN_RADIUS = 0.002; // current pen radius private static double penRadius; // show we draw immediately or wait until next show? private static boolean defer = false; // boundary of drawing canvas, 0% border // private static final double BORDER = 0.05; private static final double BORDER = 0.00; private static final double DEFAULT_XMIN = 0.0; private static final double DEFAULT_XMAX = 1.0; private static final double DEFAULT_YMIN = 0.0; private static final double DEFAULT_YMAX = 1.0; private static double xmin, ymin, xmax, ymax; // for synchronization private static Object mouseLock = new Object(); private static Object keyLock = new Object(); // default font private static final Font DEFAULT_FONT = new Font("SansSerif", Font.PLAIN, 16); // current font private static Font font; // double buffered graphics private static BufferedImage offscreenImage, onscreenImage; private static Graphics2D offscreen, onscreen; // singleton for callbacks: avoids generation of extra .class files private static StdDraw std = new StdDraw(); // the frame for drawing to the screen private static JFrame frame; // mouse state private static boolean mousePressed = false; private static double mouseX = 0; private static double mouseY = 0; // queue of typed key characters private static LinkedList keysTyped = new LinkedList(); // set of key codes currently pressed down private static TreeSet keysDown = new TreeSet(); // time in milliseconds (from currentTimeMillis()) when we can draw again // used to control the frame rate private static long nextDraw = -1; // singleton pattern: client can't instantiate private StdDraw() { } // static initializer static { init(); } /** * Sets the canvas (drawing area) to be 512-by-512 pixels. * This also erases the current drawing and resets the coordinate system, * pen radius, pen color, and font back to their default values. * Ordinarly, this method is called once, at the very beginning * of a program. */ public static void setCanvasSize() { setCanvasSize(DEFAULT_SIZE, DEFAULT_SIZE); } /** * Sets the canvas (drawing area) to be width-by-height pixels. * This also erases the current drawing and resets the coordinate system, * pen radius, pen color, and font back to their default values. * Ordinarly, this method is called once, at the very beginning * of a program. * * @param canvasWidth the width as a number of pixels * @param canvasHeight the height as a number of pixels * @throws IllegalArgumentException unless both {@code width} and * {@code height} are positive */ public static void setCanvasSize(int canvasWidth, int canvasHeight) { if (canvasWidth <= 0 || canvasHeight <= 0) throw new IllegalArgumentException("width and height must be positive"); width = canvasWidth; height = canvasHeight; init(); } // init private static void init() { if (frame != null) frame.setVisible(false); frame = new JFrame(); offscreenImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); onscreenImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); offscreen = offscreenImage.createGraphics(); onscreen = onscreenImage.createGraphics(); setXscale(); setYscale(); offscreen.setColor(DEFAULT_CLEAR_COLOR); offscreen.fillRect(0, 0, width, height); setPenColor(); setPenRadius(); setFont(); clear(); // add antialiasing RenderingHints hints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); offscreen.addRenderingHints(hints); // frame stuff ImageIcon icon = new ImageIcon(onscreenImage); JLabel draw = new JLabel(icon); draw.addMouseListener(std); draw.addMouseMotionListener(std); frame.setContentPane(draw); frame.addKeyListener(std); // JLabel cannot get keyboard focus frame.setResizable(false); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // closes all windows // frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); // closes only current window frame.setTitle("Standard Draw"); frame.setJMenuBar(createMenuBar()); frame.pack(); frame.requestFocusInWindow(); frame.setVisible(true); } // create the menu bar (changed to private) private static JMenuBar createMenuBar() { JMenuBar menuBar = new JMenuBar(); JMenu menu = new JMenu("File"); menuBar.add(menu); JMenuItem menuItem1 = new JMenuItem(" Save... "); menuItem1.addActionListener(std); menuItem1.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); menu.add(menuItem1); return menuBar; } /*************************************************************************** * User and screen coordinate systems. ***************************************************************************/ /** * Sets the x-scale to be the default (between 0.0 and 1.0). */ public static void setXscale() { setXscale(DEFAULT_XMIN, DEFAULT_XMAX); } /** * Sets the y-scale to be the default (between 0.0 and 1.0). */ public static void setYscale() { setYscale(DEFAULT_YMIN, DEFAULT_YMAX); } /** * Sets the x-scale and y-scale to be the default * (between 0.0 and 1.0). */ public static void setScale() { setXscale(); setYscale(); } /** * Sets the x-scale to the specified range. * * @param min the minimum value of the x-scale * @param max the maximum value of the x-scale * @throws IllegalArgumentException if {@code (max == min)} */ public static void setXscale(double min, double max) { double size = max - min; if (size == 0.0) throw new IllegalArgumentException("the min and max are the same"); synchronized (mouseLock) { xmin = min - BORDER * size; xmax = max + BORDER * size; } } /** * Sets the y-scale to the specified range. * * @param min the minimum value of the y-scale * @param max the maximum value of the y-scale * @throws IllegalArgumentException if {@code (max == min)} */ public static void setYscale(double min, double max) { double size = max - min; if (size == 0.0) throw new IllegalArgumentException("the min and max are the same"); synchronized (mouseLock) { ymin = min - BORDER * size; ymax = max + BORDER * size; } } /** * Sets both the x-scale and y-scale to the (same) specified range. * * @param min the minimum value of the x- and y-scales * @param max the maximum value of the x- and y-scales * @throws IllegalArgumentException if {@code (max == min)} */ public static void setScale(double min, double max) { double size = max - min; if (size == 0.0) throw new IllegalArgumentException("the min and max are the same"); synchronized (mouseLock) { xmin = min - BORDER * size; xmax = max + BORDER * size; ymin = min - BORDER * size; ymax = max + BORDER * size; } } // helper functions that scale from user coordinates to screen coordinates and back private static double scaleX(double x) { return width * (x - xmin) / (xmax - xmin); } private static double scaleY(double y) { return height * (ymax - y) / (ymax - ymin); } private static double factorX(double w) { return w * width / Math.abs(xmax - xmin); } private static double factorY(double h) { return h * height / Math.abs(ymax - ymin); } private static double userX(double x) { return xmin + x * (xmax - xmin) / width; } private static double userY(double y) { return ymax - y * (ymax - ymin) / height; } /** * Clears the screen to the default color (white). */ public static void clear() { clear(DEFAULT_CLEAR_COLOR); } /** * Clears the screen to the specified color. * * @param color the color to make the background */ public static void clear(Color color) { offscreen.setColor(color); offscreen.fillRect(0, 0, width, height); offscreen.setColor(penColor); draw(); } /** * Returns the current pen radius. * * @return the current value of the pen radius */ public static double getPenRadius() { return penRadius; } /** * Sets the pen size to the default size (0.002). * The pen is circular, so that lines have rounded ends, and when you set the * pen radius and draw a point, you get a circle of the specified radius. * The pen radius is not affected by coordinate scaling. */ public static void setPenRadius() { setPenRadius(DEFAULT_PEN_RADIUS); } /** * Sets the radius of the pen to the specified size. * The pen is circular, so that lines have rounded ends, and when you set the * pen radius and draw a point, you get a circle of the specified radius. * The pen radius is not affected by coordinate scaling. * * @param radius the radius of the pen * @throws IllegalArgumentException if {@code radius} is negative */ public static void setPenRadius(double radius) { if (!(radius >= 0)) throw new IllegalArgumentException("pen radius must be nonnegative"); penRadius = radius; float scaledPenRadius = (float) (radius * DEFAULT_SIZE); BasicStroke stroke = new BasicStroke(scaledPenRadius, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND); // BasicStroke stroke = new BasicStroke(scaledPenRadius); offscreen.setStroke(stroke); } /** * Returns the current pen color. * * @return the current pen color */ public static Color getPenColor() { return penColor; } /** * Set the pen color to the default color (black). */ public static void setPenColor() { setPenColor(DEFAULT_PEN_COLOR); } /** * Sets the pen color to the specified color. *

* The predefined pen colors are * {@code StdDraw.BLACK}, {@code StdDraw.BLUE}, {@code StdDraw.CYAN}, * {@code StdDraw.DARK_GRAY}, {@code StdDraw.GRAY}, {@code StdDraw.GREEN}, * {@code StdDraw.LIGHT_GRAY}, {@code StdDraw.MAGENTA}, {@code StdDraw.ORANGE}, * {@code StdDraw.PINK}, {@code StdDraw.RED}, {@code StdDraw.WHITE}, and * {@code StdDraw.YELLOW}. * * @param color the color to make the pen */ public static void setPenColor(Color color) { if (color == null) throw new NullPointerException(); penColor = color; offscreen.setColor(penColor); } /** * Sets the pen color to the specified RGB color. * * @param red the amount of red (between 0 and 255) * @param green the amount of green (between 0 and 255) * @param blue the amount of blue (between 0 and 255) * @throws IllegalArgumentException if {@code red}, {@code green}, * or {@code blue} is outside its prescribed range */ public static void setPenColor(int red, int green, int blue) { if (red < 0 || red >= 256) throw new IllegalArgumentException("amount of red must be between 0 and 255"); if (green < 0 || green >= 256) throw new IllegalArgumentException("amount of green must be between 0 and 255"); if (blue < 0 || blue >= 256) throw new IllegalArgumentException("amount of blue must be between 0 and 255"); setPenColor(new Color(red, green, blue)); } /** * Returns the current font. * * @return the current font */ public static Font getFont() { return font; } /** * Sets the font to the default font (sans serif, 16 point). */ public static void setFont() { setFont(DEFAULT_FONT); } /** * Sets the font to the specified value. * * @param font the font */ public static void setFont(Font font) { if (font == null) throw new NullPointerException(); StdDraw.font = font; } /*************************************************************************** * Drawing geometric shapes. ***************************************************************************/ /** * Draws a line segment between (x0, y0) and * (x1, y1). * * @param x0 the x-coordinate of one endpoint * @param y0 the y-coordinate of one endpoint * @param x1 the x-coordinate of the other endpoint * @param y1 the y-coordinate of the other endpoint */ public static void line(double x0, double y0, double x1, double y1) { offscreen.draw(new Line2D.Double(scaleX(x0), scaleY(y0), scaleX(x1), scaleY(y1))); draw(); } /** * Draws one pixel at (x, y). * This method is private because pixels depend on the display. * To achieve the same effect, set the pen radius to 0 and call {@code point()}. * * @param x the x-coordinate of the pixel * @param y the y-coordinate of the pixel */ private static void pixel(double x, double y) { offscreen.fillRect((int) Math.round(scaleX(x)), (int) Math.round(scaleY(y)), 1, 1); } /** * Draws a point centered at (x, y). * The point is a filled circle whose radius is equal to the pen radius. * To draw a single-pixel point, first set the pen radius to 0. * * @param x the x-coordinate of the point * @param y the y-coordinate of the point */ public static void point(double x, double y) { double xs = scaleX(x); double ys = scaleY(y); double r = penRadius; float scaledPenRadius = (float) (r * DEFAULT_SIZE); // double ws = factorX(2*r); // double hs = factorY(2*r); // if (ws <= 1 && hs <= 1) pixel(x, y); if (scaledPenRadius <= 1) pixel(x, y); else offscreen.fill(new Ellipse2D.Double(xs - scaledPenRadius/2, ys - scaledPenRadius/2, scaledPenRadius, scaledPenRadius)); draw(); } /** * Draws a circle of the specified radius, centered at (x, y). * * @param x the x-coordinate of the center of the circle * @param y the y-coordinate of the center of the circle * @param radius the radius of the circle * @throws IllegalArgumentException if {@code radius} is negative */ public static void circle(double x, double y, double radius) { if (!(radius >= 0)) throw new IllegalArgumentException("radius must be nonnegative"); double xs = scaleX(x); double ys = scaleY(y); double ws = factorX(2*radius); double hs = factorY(2*radius); if (ws <= 1 && hs <= 1) pixel(x, y); else offscreen.draw(new Ellipse2D.Double(xs - ws/2, ys - hs/2, ws, hs)); draw(); } /** * Draws a filled circle of the specified radius, centered at (x, y). * * @param x the x-coordinate of the center of the circle * @param y the y-coordinate of the center of the circle * @param radius the radius of the circle * @throws IllegalArgumentException if {@code radius} is negative */ public static void filledCircle(double x, double y, double radius) { if (!(radius >= 0)) throw new IllegalArgumentException("radius must be nonnegative"); double xs = scaleX(x); double ys = scaleY(y); double ws = factorX(2*radius); double hs = factorY(2*radius); if (ws <= 1 && hs <= 1) pixel(x, y); else offscreen.fill(new Ellipse2D.Double(xs - ws/2, ys - hs/2, ws, hs)); draw(); } /** * Draws an ellipse with the specified semimajor and semiminor axes, * centered at (x, y). * * @param x the x-coordinate of the center of the ellipse * @param y the y-coordinate of the center of the ellipse * @param semiMajorAxis is the semimajor axis of the ellipse * @param semiMinorAxis is the semiminor axis of the ellipse * @throws IllegalArgumentException if either {@code semiMajorAxis} * or {@code semiMinorAxis} is negative */ public static void ellipse(double x, double y, double semiMajorAxis, double semiMinorAxis) { if (!(semiMajorAxis >= 0)) throw new IllegalArgumentException("ellipse semimajor axis must be nonnegative"); if (!(semiMinorAxis >= 0)) throw new IllegalArgumentException("ellipse semiminor axis must be nonnegative"); double xs = scaleX(x); double ys = scaleY(y); double ws = factorX(2*semiMajorAxis); double hs = factorY(2*semiMinorAxis); if (ws <= 1 && hs <= 1) pixel(x, y); else offscreen.draw(new Ellipse2D.Double(xs - ws/2, ys - hs/2, ws, hs)); draw(); } /** * Draws an ellipse with the specified semimajor and semiminor axes, * centered at (x, y). * * @param x the x-coordinate of the center of the ellipse * @param y the y-coordinate of the center of the ellipse * @param semiMajorAxis is the semimajor axis of the ellipse * @param semiMinorAxis is the semiminor axis of the ellipse * @throws IllegalArgumentException if either {@code semiMajorAxis} * or {@code semiMinorAxis} is negative */ public static void filledEllipse(double x, double y, double semiMajorAxis, double semiMinorAxis) { if (!(semiMajorAxis >= 0)) throw new IllegalArgumentException("ellipse semimajor axis must be nonnegative"); if (!(semiMinorAxis >= 0)) throw new IllegalArgumentException("ellipse semiminor axis must be nonnegative"); double xs = scaleX(x); double ys = scaleY(y); double ws = factorX(2*semiMajorAxis); double hs = factorY(2*semiMinorAxis); if (ws <= 1 && hs <= 1) pixel(x, y); else offscreen.fill(new Ellipse2D.Double(xs - ws/2, ys - hs/2, ws, hs)); draw(); } /** * Draws a circular arc of the specified radius, * centered at (x, y), from angle1 to angle2 (in degrees). * * @param x the x-coordinate of the center of the circle * @param y the y-coordinate of the center of the circle * @param radius the radius of the circle * @param angle1 the starting angle. 0 would mean an arc beginning at 3 o'clock. * @param angle2 the angle at the end of the arc. For example, if * you want a 90 degree arc, then angle2 should be angle1 + 90. * @throws IllegalArgumentException if {@code radius} is negative */ public static void arc(double x, double y, double radius, double angle1, double angle2) { if (radius < 0) throw new IllegalArgumentException("arc radius must be nonnegative"); while (angle2 < angle1) angle2 += 360; double xs = scaleX(x); double ys = scaleY(y); double ws = factorX(2*radius); double hs = factorY(2*radius); if (ws <= 1 && hs <= 1) pixel(x, y); else offscreen.draw(new Arc2D.Double(xs - ws/2, ys - hs/2, ws, hs, angle1, angle2 - angle1, Arc2D.OPEN)); draw(); } /** * Draws a square of side length 2r, centered at (x, y). * * @param x the x-coordinate of the center of the square * @param y the y-coordinate of the center of the square * @param halfLength one half the length of any side of the square * @throws IllegalArgumentException if {@code halfLength} is negative */ public static void square(double x, double y, double halfLength) { if (!(halfLength >= 0)) throw new IllegalArgumentException("half length must be nonnegative"); double xs = scaleX(x); double ys = scaleY(y); double ws = factorX(2*halfLength); double hs = factorY(2*halfLength); if (ws <= 1 && hs <= 1) pixel(x, y); else offscreen.draw(new Rectangle2D.Double(xs - ws/2, ys - hs/2, ws, hs)); draw(); } /** * Draws a filled square of the specified size, centered at (x, y). * * @param x the x-coordinate of the center of the square * @param y the y-coordinate of the center of the square * @param halfLength one half the length of any side of the square * @throws IllegalArgumentException if {@code halfLength} is negative */ public static void filledSquare(double x, double y, double halfLength) { if (!(halfLength >= 0)) throw new IllegalArgumentException("half length must be nonnegative"); double xs = scaleX(x); double ys = scaleY(y); double ws = factorX(2*halfLength); double hs = factorY(2*halfLength); if (ws <= 1 && hs <= 1) pixel(x, y); else offscreen.fill(new Rectangle2D.Double(xs - ws/2, ys - hs/2, ws, hs)); draw(); } /** * Draws a rectangle of the specified size, centered at (x, y). * * @param x the x-coordinate of the center of the rectangle * @param y the y-coordinate of the center of the rectangle * @param halfWidth one half the width of the rectangle * @param halfHeight one half the height of the rectangle * @throws IllegalArgumentException if either {@code halfWidth} or {@code halfHeight} is negative */ public static void rectangle(double x, double y, double halfWidth, double halfHeight) { if (!(halfWidth >= 0)) throw new IllegalArgumentException("half width must be nonnegative"); if (!(halfHeight >= 0)) throw new IllegalArgumentException("half height must be nonnegative"); double xs = scaleX(x); double ys = scaleY(y); double ws = factorX(2*halfWidth); double hs = factorY(2*halfHeight); if (ws <= 1 && hs <= 1) pixel(x, y); else offscreen.draw(new Rectangle2D.Double(xs - ws/2, ys - hs/2, ws, hs)); draw(); } /** * Draws a filled rectangle of the specified size, centered at (x, y). * * @param x the x-coordinate of the center of the rectangle * @param y the y-coordinate of the center of the rectangle * @param halfWidth one half the width of the rectangle * @param halfHeight one half the height of the rectangle * @throws IllegalArgumentException if either {@code halfWidth} or {@code halfHeight} is negative */ public static void filledRectangle(double x, double y, double halfWidth, double halfHeight) { if (!(halfWidth >= 0)) throw new IllegalArgumentException("half width must be nonnegative"); if (!(halfHeight >= 0)) throw new IllegalArgumentException("half height must be nonnegative"); double xs = scaleX(x); double ys = scaleY(y); double ws = factorX(2*halfWidth); double hs = factorY(2*halfHeight); if (ws <= 1 && hs <= 1) pixel(x, y); else offscreen.fill(new Rectangle2D.Double(xs - ws/2, ys - hs/2, ws, hs)); draw(); } /** * Draws a polygon with the vertices * (x0, y0), * (x1, y1), ..., * (xn−1, yn−1). * * @param x an array of all the x-coordinates of the polygon * @param y an array of all the y-coordinates of the polygon * @throws IllegalArgumentException unless {@code x[]} and {@code y[]} * are of the same length */ public static void polygon(double[] x, double[] y) { if (x == null) throw new NullPointerException(); if (y == null) throw new NullPointerException(); int n1 = x.length; int n2 = y.length; if (n1 != n2) throw new IllegalArgumentException("arrays must be of the same length"); int n = n1; GeneralPath path = new GeneralPath(); path.moveTo((float) scaleX(x[0]), (float) scaleY(y[0])); for (int i = 0; i < n; i++) path.lineTo((float) scaleX(x[i]), (float) scaleY(y[i])); path.closePath(); offscreen.draw(path); draw(); } /** * Draws a polygon with the vertices * (x0, y0), * (x1, y1), ..., * (xn−1, yn−1). * * @param x an array of all the x-coordinates of the polygon * @param y an array of all the y-coordinates of the polygon * @throws IllegalArgumentException unless {@code x[]} and {@code y[]} * are of the same length */ public static void filledPolygon(double[] x, double[] y) { if (x == null) throw new NullPointerException(); if (y == null) throw new NullPointerException(); int n1 = x.length; int n2 = y.length; if (n1 != n2) throw new IllegalArgumentException("arrays must be of the same length"); int n = n1; GeneralPath path = new GeneralPath(); path.moveTo((float) scaleX(x[0]), (float) scaleY(y[0])); for (int i = 0; i < n; i++) path.lineTo((float) scaleX(x[i]), (float) scaleY(y[i])); path.closePath(); offscreen.fill(path); draw(); } /*************************************************************************** * Drawing images. ***************************************************************************/ // get an image from the given filename private static Image getImage(String filename) { if (filename == null) throw new NullPointerException(); // to read from file ImageIcon icon = new ImageIcon(filename); // try to read from URL if ((icon == null) || (icon.getImageLoadStatus() != MediaTracker.COMPLETE)) { try { URL url = new URL(filename); icon = new ImageIcon(url); } catch (Exception e) { /* not a url */ } } // in case file is inside a .jar (classpath relative to StdDraw) if ((icon == null) || (icon.getImageLoadStatus() != MediaTracker.COMPLETE)) { URL url = StdDraw.class.getResource(filename); if (url != null) icon = new ImageIcon(url); } // in case file is inside a .jar (classpath relative to root of jar) if ((icon == null) || (icon.getImageLoadStatus() != MediaTracker.COMPLETE)) { URL url = StdDraw.class.getResource("/" + filename); if (url == null) throw new IllegalArgumentException("image " + filename + " not found"); icon = new ImageIcon(url); } return icon.getImage(); } /*************************************************************************** * [Summer 2016] Should we update to use ImageIO instead of ImageIcon()? * Seems to have some issues loading images on some systems * and slows things down on other systems. * especially if you don't call ImageIO.setUseCache(false) * One advantage is that it returns a BufferedImage. ***************************************************************************/ /* private static BufferedImage getImage(String filename) { if (filename == null) throw new NullPointerException(); // from a file or URL try { URL url = new URL(filename); BufferedImage image = ImageIO.read(url); return image; } catch (IOException e) { // ignore } // in case file is inside a .jar (classpath relative to StdDraw) try { URL url = StdDraw.class.getResource(filename); BufferedImage image = ImageIO.read(url); return image; } catch (IOException e) { // ignore } // in case file is inside a .jar (classpath relative to root of jar) try { URL url = StdDraw.class.getResource("/" + filename); BufferedImage image = ImageIO.read(url); return image; } catch (IOException e) { // ignore } throw new IllegalArgumentException("image " + filename + " not found"); } */ /** * Draws the specified image centered at (x, y). * The supported image formats are JPEG, PNG, and GIF. * As an optimization, the picture is cached, so there is no performance * penalty for redrawing the same image multiple times (e.g., in an animation). * However, if you change the picture file after drawing it, subsequent * calls will draw the original picture. * * @param x the center x-coordinate of the image * @param y the center y-coordinate of the image * @param filename the name of the image/picture, e.g., "ball.gif" * @throws IllegalArgumentException if the image filename is invalid */ public static void picture(double x, double y, String filename) { // BufferedImage image = getImage(filename); Image image = getImage(filename); double xs = scaleX(x); double ys = scaleY(y); // int ws = image.getWidth(); // can call only if image is a BufferedImage // int hs = image.getHeight(); int ws = image.getWidth(null); int hs = image.getHeight(null); if (ws < 0 || hs < 0) throw new IllegalArgumentException("image " + filename + " is corrupt"); offscreen.drawImage(image, (int) Math.round(xs - ws/2.0), (int) Math.round(ys - hs/2.0), null); draw(); } /** * Draws the specified image centered at (x, y), * rotated given number of degrees. * The supported image formats are JPEG, PNG, and GIF. * * @param x the center x-coordinate of the image * @param y the center y-coordinate of the image * @param filename the name of the image/picture, e.g., "ball.gif" * @param degrees is the number of degrees to rotate counterclockwise * @throws IllegalArgumentException if the image filename is invalid */ public static void picture(double x, double y, String filename, double degrees) { // BufferedImage image = getImage(filename); Image image = getImage(filename); double xs = scaleX(x); double ys = scaleY(y); // int ws = image.getWidth(); // can call only if image is a BufferedImage // int hs = image.getHeight(); int ws = image.getWidth(null); int hs = image.getHeight(null); if (ws < 0 || hs < 0) throw new IllegalArgumentException("image " + filename + " is corrupt"); offscreen.rotate(Math.toRadians(-degrees), xs, ys); offscreen.drawImage(image, (int) Math.round(xs - ws/2.0), (int) Math.round(ys - hs/2.0), null); offscreen.rotate(Math.toRadians(+degrees), xs, ys); draw(); } /** * Draws the specified image centered at (x, y), * rescaled to the specified bounding box. * The supported image formats are JPEG, PNG, and GIF. * * @param x the center x-coordinate of the image * @param y the center y-coordinate of the image * @param filename the name of the image/picture, e.g., "ball.gif" * @param scaledWidth the width of the scaled image (in screen coordinates) * @param scaledHeight the height of the scaled image (in screen coordinates) * @throws IllegalArgumentException if either {@code scaledWidth} * or {@code scaledHeight} is negative * @throws IllegalArgumentException if the image filename is invalid */ public static void picture(double x, double y, String filename, double scaledWidth, double scaledHeight) { Image image = getImage(filename); if (scaledWidth < 0) throw new IllegalArgumentException("width is negative: " + scaledWidth); if (scaledHeight < 0) throw new IllegalArgumentException("height is negative: " + scaledHeight); double xs = scaleX(x); double ys = scaleY(y); double ws = factorX(scaledWidth); double hs = factorY(scaledHeight); if (ws < 0 || hs < 0) throw new IllegalArgumentException("image " + filename + " is corrupt"); if (ws <= 1 && hs <= 1) pixel(x, y); else { offscreen.drawImage(image, (int) Math.round(xs - ws/2.0), (int) Math.round(ys - hs/2.0), (int) Math.round(ws), (int) Math.round(hs), null); } draw(); } /** * Draws the specified image centered at (x, y), rotated * given number of degrees, and rescaled to the specified bounding box. * The supported image formats are JPEG, PNG, and GIF. * * @param x the center x-coordinate of the image * @param y the center y-coordinate of the image * @param filename the name of the image/picture, e.g., "ball.gif" * @param scaledWidth the width of the scaled image (in screen coordinates) * @param scaledHeight the height of the scaled image (in screen coordinates) * @param degrees is the number of degrees to rotate counterclockwise * @throws IllegalArgumentException if either {@code scaledWidth} * or {@code scaledHeight} is negative * @throws IllegalArgumentException if the image filename is invalid */ public static void picture(double x, double y, String filename, double scaledWidth, double scaledHeight, double degrees) { if (scaledWidth < 0) throw new IllegalArgumentException("width is negative: " + scaledWidth); if (scaledHeight < 0) throw new IllegalArgumentException("height is negative: " + scaledHeight); Image image = getImage(filename); double xs = scaleX(x); double ys = scaleY(y); double ws = factorX(scaledWidth); double hs = factorY(scaledHeight); if (ws < 0 || hs < 0) throw new IllegalArgumentException("image " + filename + " is corrupt"); if (ws <= 1 && hs <= 1) pixel(x, y); offscreen.rotate(Math.toRadians(-degrees), xs, ys); offscreen.drawImage(image, (int) Math.round(xs - ws/2.0), (int) Math.round(ys - hs/2.0), (int) Math.round(ws), (int) Math.round(hs), null); offscreen.rotate(Math.toRadians(+degrees), xs, ys); draw(); } /*************************************************************************** * Drawing text. ***************************************************************************/ /** * Write the given text string in the current font, centered at (x, y). * * @param x the center x-coordinate of the text * @param y the center y-coordinate of the text * @param text the text to write */ public static void text(double x, double y, String text) { if (text == null) throw new NullPointerException(); offscreen.setFont(font); FontMetrics metrics = offscreen.getFontMetrics(); double xs = scaleX(x); double ys = scaleY(y); int ws = metrics.stringWidth(text); int hs = metrics.getDescent(); offscreen.drawString(text, (float) (xs - ws/2.0), (float) (ys + hs)); draw(); } /** * Write the given text string in the current font, centered at (x, y) and * rotated by the specified number of degrees. * @param x the center x-coordinate of the text * @param y the center y-coordinate of the text * @param text the text to write * @param degrees is the number of degrees to rotate counterclockwise */ public static void text(double x, double y, String text, double degrees) { if (text == null) throw new NullPointerException(); double xs = scaleX(x); double ys = scaleY(y); offscreen.rotate(Math.toRadians(-degrees), xs, ys); text(x, y, text); offscreen.rotate(Math.toRadians(+degrees), xs, ys); } /** * Write the given text string in the current font, left-aligned at (x, y). * @param x the x-coordinate of the text * @param y the y-coordinate of the text * @param text the text */ public static void textLeft(double x, double y, String text) { if (text == null) throw new NullPointerException(); offscreen.setFont(font); FontMetrics metrics = offscreen.getFontMetrics(); double xs = scaleX(x); double ys = scaleY(y); int hs = metrics.getDescent(); offscreen.drawString(text, (float) xs, (float) (ys + hs)); draw(); } /** * Write the given text string in the current font, right-aligned at (x, y). * * @param x the x-coordinate of the text * @param y the y-coordinate of the text * @param text the text to write */ public static void textRight(double x, double y, String text) { if (text == null) throw new NullPointerException(); offscreen.setFont(font); FontMetrics metrics = offscreen.getFontMetrics(); double xs = scaleX(x); double ys = scaleY(y); int ws = metrics.stringWidth(text); int hs = metrics.getDescent(); offscreen.drawString(text, (float) (xs - ws), (float) (ys + hs)); draw(); } /** * Copies the offscreen buffer to the onscreen buffer, pauses for t milliseconds * and enables double buffering. * @param t number of milliseconds * @deprecated replaced by {@link #enableDoubleBuffering}, {@link #show()}, and {@link #pause} */ @Deprecated public static void show(int t) { // sleep until the next time we're allowed to draw long millis = System.currentTimeMillis(); if (millis < nextDraw) { try { Thread.sleep(nextDraw - millis); } catch (InterruptedException e) { System.out.println("Error sleeping"); } millis = nextDraw; } show(); enableDoubleBuffering(); // when are we allowed to draw again nextDraw = millis + t; } /** * Pause for t milliseconds. This method is intended to support computer animations. * @param t number of milliseconds */ public static void pause(int t) { // sleep until the next time we're allowed to draw long millis = System.currentTimeMillis(); if (millis < nextDraw) { try { Thread.sleep(nextDraw - millis); } catch (InterruptedException e) { System.out.println("Error sleeping"); } millis = nextDraw; } // when are we allowed to draw again nextDraw = millis + t; } /** * Copies offscreen buffer to onscreen buffer. There is no reason to call * this method unless double buffering is enabled. */ public static void show() { onscreen.drawImage(offscreenImage, 0, 0, null); frame.repaint(); } // draw onscreen if defer is false private static void draw() { if (!defer) show(); } /** * Enable double buffering. All subsequent calls to * drawing methods such as {@code line()}, {@code circle()}, * and {@code square()} will be deffered until the next call * to show(). Useful for animations. */ public static void enableDoubleBuffering() { defer = true; } /** * Disable double buffering. All subsequent calls to * drawing methods such as {@code line()}, {@code circle()}, * and {@code square()} will be displayed on screen when called. * This is the default. */ public static void disableDoubleBuffering() { defer = false; } /*************************************************************************** * Save drawing to a file. ***************************************************************************/ /** * Saves the drawing to using the specified filename. * The supported image formats are JPEG and PNG; * the filename suffix must be {@code .jpg} or {@code .png}. * * @param filename the name of the file with one of the required suffixes */ public static void save(String filename) { if (filename == null) throw new NullPointerException(); File file = new File(filename); String suffix = filename.substring(filename.lastIndexOf('.') + 1); // png files if (suffix.toLowerCase().equals("png")) { try { ImageIO.write(onscreenImage, suffix, file); } catch (IOException e) { e.printStackTrace(); } } // need to change from ARGB to RGB for JPEG // reference: http://archives.java.sun.com/cgi-bin/wa?A2=ind0404&L=java2d-interest&D=0&P=2727 else if (suffix.toLowerCase().equals("jpg")) { WritableRaster raster = onscreenImage.getRaster(); WritableRaster newRaster; newRaster = raster.createWritableChild(0, 0, width, height, 0, 0, new int[] {0, 1, 2}); DirectColorModel cm = (DirectColorModel) onscreenImage.getColorModel(); DirectColorModel newCM = new DirectColorModel(cm.getPixelSize(), cm.getRedMask(), cm.getGreenMask(), cm.getBlueMask()); BufferedImage rgbBuffer = new BufferedImage(newCM, newRaster, false, null); try { ImageIO.write(rgbBuffer, suffix, file); } catch (IOException e) { e.printStackTrace(); } } else { System.out.println("Invalid image file type: " + suffix); } } /** * This method cannot be called directly. */ @Override public void actionPerformed(ActionEvent e) { FileDialog chooser = new FileDialog(StdDraw.frame, "Use a .png or .jpg extension", FileDialog.SAVE); chooser.setVisible(true); String filename = chooser.getFile(); if (filename != null) { StdDraw.save(chooser.getDirectory() + File.separator + chooser.getFile()); } } /*************************************************************************** * Mouse interactions. ***************************************************************************/ /** * Returns true if the mouse is being pressed. * * @return {@code true} if the mouse is being pressed; {@code false} otherwise */ public static boolean mousePressed() { synchronized (mouseLock) { return mousePressed; } } /** * Returns the x-coordinate of the mouse. * * @return the x-coordinate of the mouse */ public static double mouseX() { synchronized (mouseLock) { return mouseX; } } /** * Returns the y-coordinate of the mouse. * * @return y-coordinate of the mouse */ public static double mouseY() { synchronized (mouseLock) { return mouseY; } } /** * This method cannot be called directly. */ @Override public void mouseClicked(MouseEvent e) { } /** * This method cannot be called directly. */ @Override public void mouseEntered(MouseEvent e) { } /** * This method cannot be called directly. */ @Override public void mouseExited(MouseEvent e) { } /** * This method cannot be called directly. */ @Override public void mousePressed(MouseEvent e) { synchronized (mouseLock) { mouseX = StdDraw.userX(e.getX()); mouseY = StdDraw.userY(e.getY()); mousePressed = true; } } /** * This method cannot be called directly. */ @Override public void mouseReleased(MouseEvent e) { synchronized (mouseLock) { mousePressed = false; } } /** * This method cannot be called directly. */ @Override public void mouseDragged(MouseEvent e) { synchronized (mouseLock) { mouseX = StdDraw.userX(e.getX()); mouseY = StdDraw.userY(e.getY()); } } /** * This method cannot be called directly. */ @Override public void mouseMoved(MouseEvent e) { synchronized (mouseLock) { mouseX = StdDraw.userX(e.getX()); mouseY = StdDraw.userY(e.getY()); } } /*************************************************************************** * Keyboard interactions. ***************************************************************************/ /** * Returns true if the user has typed a key (that has not yet been processed). * * @return {@code true} if the user has typed a key (that has not yet been processed * by {@link #nextKeyTyped()}; {@code false} otherwise */ public static boolean hasNextKeyTyped() { synchronized (keyLock) { return !keysTyped.isEmpty(); } } /** * Returns the next key that was typed by the user (that your program has not already processed). * This method should be preceded by a call to {@link #hasNextKeyTyped()} to ensure * that there is a next key to process. * This method returns a Unicode character corresponding to the key * typed (such as {@code 'a'} or {@code 'A'}). * It cannot identify action keys (such as F1 and arrow keys) * or modifier keys (such as control). * * @return the next key typed by the user (that your program has not already processed). * @throws NoSuchElementException if there is no remaining key */ public static char nextKeyTyped() { synchronized (keyLock) { if (keysTyped.isEmpty()) { throw new NoSuchElementException("your program has already processed all keystrokes"); } return keysTyped.removeLast(); } } /** * Returns true if the given key is being pressed. *

* This method takes the keycode (corresponding to a physical key) * as an argument. It can handle action keys * (such as F1 and arrow keys) and modifier keys (such as shift and control). * See {@link KeyEvent} for a description of key codes. * * @param keycode the key to check if it is being pressed * @return {@code true} if {@code keycode} is currently being pressed; * {@code false} otherwise */ public static boolean isKeyPressed(int keycode) { synchronized (keyLock) { return keysDown.contains(keycode); } } /** * This method cannot be called directly. */ @Override public void keyTyped(KeyEvent e) { synchronized (keyLock) { keysTyped.addFirst(e.getKeyChar()); } } /** * This method cannot be called directly. */ @Override public void keyPressed(KeyEvent e) { synchronized (keyLock) { keysDown.add(e.getKeyCode()); } } /** * This method cannot be called directly. */ @Override public void keyReleased(KeyEvent e) { synchronized (keyLock) { keysDown.remove(e.getKeyCode()); } } /** * Test client. * * @param args the command-line arguments */ public static void main(String[] args) { StdDraw.square(.2, .8, .1); StdDraw.filledSquare(.8, .8, .2); StdDraw.circle(.8, .2, .2); StdDraw.setPenColor(StdDraw.BOOK_RED); StdDraw.setPenRadius(.02); StdDraw.arc(.8, .2, .1, 200, 45); // draw a blue diamond StdDraw.setPenRadius(); StdDraw.setPenColor(StdDraw.BOOK_BLUE); double[] x = { .1, .2, .3, .2 }; double[] y = { .2, .3, .2, .1 }; StdDraw.filledPolygon(x, y); // text StdDraw.setPenColor(StdDraw.BLACK); StdDraw.text(0.2, 0.5, "black text"); StdDraw.setPenColor(StdDraw.WHITE); StdDraw.text(0.8, 0.8, "white text"); } } /****************************************************************************** * Copyright 2002-2016, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * * Algorithms, 4th edition by Robert Sedgewick and Kevin Wayne, * Addison-Wesley Professional, 2011, ISBN 0-321-57351-X. * http://algs4.cs.princeton.edu * * * algs4.jar is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * algs4.jar is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with algs4.jar. If not, see http://www.gnu.org/licenses. ******************************************************************************/ ================================================ FILE: code/algs4/StdIn.java ================================================ /****************************************************************************** * Compilation: javac StdIn.java * Execution: java StdIn (interactive test of basic functionality) * Dependencies: none * * Reads in data of various types from standard input. * ******************************************************************************/ package algs4; import java.util.ArrayList; import java.util.InputMismatchException; import java.util.Locale; import java.util.NoSuchElementException; import java.util.Scanner; import java.util.regex.Pattern; /** * The {@code StdIn} class provides static methods for reading strings * and numbers from standard input. * These functions fall into one of four categories: *

*

* Generally, it is best not to mix functions from the different * categories in the same program. *

* Reading tokens from standard input one at a time, * and converting to numbers and strings. * You can use the following methods to read numbers, strings, and booleans * from standard input: *

*

* Each method skips over any input that is whitespace. Then, it reads * the next token and attempts to convert it into a value of the specified * type. If it succeeds, it returns that value; otherwise, it * throws a {@link InputMismatchException}. *

* Whitespace includes spaces, tabs, and newlines; the full definition * is inherited from {@link Character#isWhitespace(char)}. * A token is a maximal sequence of non-whitespace characters. * The precise rules for describing which tokens can be converted to * integers and floating-point numbers are inherited from * Scanner, * using the locale {@link Locale#US}; the rules * for floating-point numbers are slightly different * from those in {@link Double#valueOf(String)}, * but unlikely to be of concern to most programmers. *

* Reading characters from standard input, one at a time. * You can use the following two methods to read characters from standard input: *

*

* The first method returns true if standard input has more input (including whitespace). * The second method reads and returns the next character of input on standard * input (possibly a whitespace character). *

* As an example, the following code fragment reads characters from standard input, * one character at a time, and prints it to standard output. *

 *  while (StdIn.hasNextChar()) {
 *      char c = StdIn.readChar();
 *      StdOut.print(c);
 *  }
 *  
*

* Reading lines from standard input, one at a time. * You can use the following two methods to read lines from standard input: *

*

* The first method returns true if standard input has more input (including whitespace). * The second method reads and returns the remaining portion of * the next line of input on standard input (possibly whitespace), * discarding the trailing line separator. *

* A line separator is defined to be one of the following strings: * {@code \n} (Linux), {@code \r} (old Macintosh), * {@code \r\n} (Windows), * {@code \u2028}, {@code \u2029}, or {@code \u0085}. *

* As an example, the following code fragment reads text from standard input, * one line at a time, and prints it to standard output. *

 *  while (StdIn.hasNextLine()) {
 *      String line = StdIn.readLine();
 *      StdOut.println(line);
 *  }
 *  
*

* Reading a sequence of values of the same type from standard input. * You can use the following methods to read a sequence numbers, strings, * or booleans (all of the same type) from standard input: *

*

* The first three methods read of all of remaining token on standard input * and dconverts the tokens to values of * the specified type, as in the corresponding * {@code readDouble}, {@code readInt}, and {@code readString()} methods. * The {@code readAllLines()} method reads all remaining lines on standard * input and returns them as an array of strings. * The {@code readAll()} method reads all remaining input on standard * input and returns it as a string. *

* As an example, the following code fragment reads all of the remaining * tokens from standard input and returns them as an array of strings. *

 *  String[] words = StdIn.readAllStrings();
 *  
*

* Differences with Scanner. * {@code StdIn} and {@link Scanner} are both designed to parse * tokens and convert them to primitive types and strings. * Some of the main differences are summarized below: *

*

* Historical note: {@code StdIn} preceded {@code Scanner}; when * {@code Scanner} was introduced, this class was reimplemented to use {@code Scanner}. *

* Using standard input. * Standard input is fundamental operating system abstraction, on Mac OS X, * Windows, and Linux. * The methods in {@code StdIn} are blocking, which means that they * will wait until you enter input on standard input. * If your program has a loop that repeats until standard input is empty, * you must signal that the input is finished. * To do so, depending on your operating system and IDE, * use either {@code } or {@code }, on its own line. * If you are redirecting standard input from a file, you will not need * to do anything to signal that the input is finished. *

* Known bugs. * Java's UTF-8 encoding does not recognize the optional * byte-order mask. * If the input begins with the optional byte-order mask, {@code StdIn} * will have an extra character {@code \uFEFF} at the beginning. *

* Reference. * For additional documentation, * see Section 1.5 of * Introduction to Programming in Java: An Interdisciplinary Approach * by Robert Sedgewick and Kevin Wayne. * * @author David Pritchard * @author Robert Sedgewick * @author Kevin Wayne */ public final class StdIn { /*** begin: section (1 of 2) of code duplicated from In to StdIn. */ // assume Unicode UTF-8 encoding private static final String CHARSET_NAME = "UTF-8"; // assume language = English, country = US for consistency with System.out. private static final Locale LOCALE = Locale.US; // the default token separator; we maintain the invariant that this value // is held by the scanner's delimiter between calls private static final Pattern WHITESPACE_PATTERN = Pattern.compile("\\p{javaWhitespace}+"); // makes whitespace significant private static final Pattern EMPTY_PATTERN = Pattern.compile(""); // used to read the entire input private static final Pattern EVERYTHING_PATTERN = Pattern.compile("\\A"); /*** end: section (1 of 2) of code duplicated from In to StdIn. */ private static Scanner scanner; // it doesn't make sense to instantiate this class private StdIn() { } //// begin: section (2 of 2) of code duplicated from In to StdIn, //// with all methods changed from "public" to "public static" /** * Returns true if standard input is empty (except possibly for whitespace). * Use this method to know whether the next call to {@link #readString()}, * {@link #readDouble()}, etc will succeed. * * @return {@code true} if standard input is empty (except possibly * for whitespace); {@code false} otherwise */ public static boolean isEmpty() { return !scanner.hasNext(); } /** * Returns true if standard input has a next line. * Use this method to know whether the * next call to {@link #readLine()} will succeed. * This method is functionally equivalent to {@link #hasNextChar()}. * * @return {@code true} if standard input is empty; * {@code false} otherwise */ public static boolean hasNextLine() { return scanner.hasNextLine(); } /** * Returns true if standard input has more inputy (including whitespace). * Use this method to know whether the next call to {@link #readChar()} will succeed. * This method is functionally equivalent to {@link #hasNextLine()}. * * @return {@code true} if standard input has more input (including whitespace); * {@code false} otherwise */ public static boolean hasNextChar() { scanner.useDelimiter(EMPTY_PATTERN); boolean result = scanner.hasNext(); scanner.useDelimiter(WHITESPACE_PATTERN); return result; } /** * Reads and returns the next line, excluding the line separator if present. * * @return the next line, excluding the line separator if present; * {@code null} if no such line */ public static String readLine() { String line; try { line = scanner.nextLine(); } catch (NoSuchElementException e) { line = null; } return line; } /** * Reads and returns the next character. * * @return the next character * @throws NoSuchElementException if standard input is empty */ public static char readChar() { scanner.useDelimiter(EMPTY_PATTERN); String ch = scanner.next(); assert ch.length() == 1 : "Internal (Std)In.readChar() error!" + " Please contact the authors."; scanner.useDelimiter(WHITESPACE_PATTERN); return ch.charAt(0); } /** * Reads and returns the remainder of the input, as a string. * * @return the remainder of the input, as a string * @throws NoSuchElementException if standard input is empty */ public static String readAll() { if (!scanner.hasNextLine()) return ""; String result = scanner.useDelimiter(EVERYTHING_PATTERN).next(); // not that important to reset delimeter, since now scanner is empty scanner.useDelimiter(WHITESPACE_PATTERN); // but let's do it anyway return result; } /** * Reads the next token and returns the {@code String}. * * @return the next {@code String} * @throws NoSuchElementException if standard input is empty */ public static String readString() { return scanner.next(); } /** * Reads the next token from standard input, parses it as an integer, and returns the integer. * * @return the next integer on standard input * @throws NoSuchElementException if standard input is empty * @throws InputMismatchException if the next token cannot be parsed as an {@code int} */ public static int readInt() { return scanner.nextInt(); } /** * Reads the next token from standard input, parses it as a double, and returns the double. * * @return the next double on standard input * @throws NoSuchElementException if standard input is empty * @throws InputMismatchException if the next token cannot be parsed as a {@code double} */ public static double readDouble() { return scanner.nextDouble(); } /** * Reads the next token from standard input, parses it as a float, and returns the float. * * @return the next float on standard input * @throws NoSuchElementException if standard input is empty * @throws InputMismatchException if the next token cannot be parsed as a {@code float} */ public static float readFloat() { return scanner.nextFloat(); } /** * Reads the next token from standard input, parses it as a long integer, and returns the long integer. * * @return the next long integer on standard input * @throws NoSuchElementException if standard input is empty * @throws InputMismatchException if the next token cannot be parsed as a {@code long} */ public static long readLong() { return scanner.nextLong(); } /** * Reads the next token from standard input, parses it as a short integer, and returns the short integer. * * @return the next short integer on standard input * @throws NoSuchElementException if standard input is empty * @throws InputMismatchException if the next token cannot be parsed as a {@code short} */ public static short readShort() { return scanner.nextShort(); } /** * Reads the next token from standard input, parses it as a byte, and returns the byte. * * @return the next byte on standard input * @throws NoSuchElementException if standard input is empty * @throws InputMismatchException if the next token cannot be parsed as a {@code byte} */ public static byte readByte() { return scanner.nextByte(); } /** * Reads the next token from standard input, parses it as a boolean, * and returns the boolean. * * @return the next boolean on standard input * @throws NoSuchElementException if standard input is empty * @throws InputMismatchException if the next token cannot be parsed as a {@code boolean}: * {@code true} or {@code 1} for true, and {@code false} or {@code 0} for false, * ignoring case */ public static boolean readBoolean() { String s = readString(); if (s.equalsIgnoreCase("true")) return true; if (s.equalsIgnoreCase("false")) return false; if (s.equals("1")) return true; if (s.equals("0")) return false; throw new InputMismatchException(); } /** * Reads all remaining tokens from standard input and returns them as an array of strings. * * @return all remaining tokens on standard input, as an array of strings */ public static String[] readAllStrings() { // we could use readAll.trim().split(), but that's not consistent // because trim() uses characters 0x00..0x20 as whitespace String[] tokens = WHITESPACE_PATTERN.split(readAll()); if (tokens.length == 0 || tokens[0].length() > 0) return tokens; // don't include first token if it is leading whitespace String[] decapitokens = new String[tokens.length-1]; for (int i = 0; i < tokens.length - 1; i++) decapitokens[i] = tokens[i+1]; return decapitokens; } /** * Reads all remaining lines from standard input and returns them as an array of strings. * @return all remaining lines on standard input, as an array of strings */ public static String[] readAllLines() { ArrayList lines = new ArrayList(); while (hasNextLine()) { lines.add(readLine()); } return lines.toArray(new String[0]); } /** * Reads all remaining tokens from standard input, parses them as integers, and returns * them as an array of integers. * @return all remaining integers on standard input, as an array * @throws InputMismatchException if any token cannot be parsed as an {@code int} */ public static int[] readAllInts() { String[] fields = readAllStrings(); int[] vals = new int[fields.length]; for (int i = 0; i < fields.length; i++) vals[i] = Integer.parseInt(fields[i]); return vals; } /** * Reads all remaining tokens from standard input, parses them as longs, and returns * them as an array of longs. * @return all remaining longs on standard input, as an array * @throws InputMismatchException if any token cannot be parsed as a {@code long} */ public static long[] readAllLongs() { String[] fields = readAllStrings(); long[] vals = new long[fields.length]; for (int i = 0; i < fields.length; i++) vals[i] = Long.parseLong(fields[i]); return vals; } /** * Reads all remaining tokens from standard input, parses them as doubles, and returns * them as an array of doubles. * @return all remaining doubles on standard input, as an array * @throws InputMismatchException if any token cannot be parsed as a {@code double} */ public static double[] readAllDoubles() { String[] fields = readAllStrings(); double[] vals = new double[fields.length]; for (int i = 0; i < fields.length; i++) vals[i] = Double.parseDouble(fields[i]); return vals; } //// end: section (2 of 2) of code duplicated from In to StdIn // do this once when StdIn is initialized static { resync(); } /** * If StdIn changes, use this to reinitialize the scanner. */ private static void resync() { setScanner(new Scanner(new java.io.BufferedInputStream(System.in), CHARSET_NAME)); } private static void setScanner(Scanner scanner) { StdIn.scanner = scanner; StdIn.scanner.useLocale(LOCALE); } /** * Reads all remaining tokens, parses them as integers, and returns * them as an array of integers. * @return all remaining integers, as an array * @throws InputMismatchException if any token cannot be parsed as an {@code int} * @deprecated Replaced by {@link #readAllInts()}. */ @Deprecated public static int[] readInts() { return readAllInts(); } /** * Reads all remaining tokens, parses them as doubles, and returns * them as an array of doubles. * @return all remaining doubles, as an array * @throws InputMismatchException if any token cannot be parsed as a {@code double} * @deprecated Replaced by {@link #readAllDoubles()}. */ @Deprecated public static double[] readDoubles() { return readAllDoubles(); } /** * Reads all remaining tokens and returns them as an array of strings. * @return all remaining tokens, as an array of strings * @deprecated Replaced by {@link #readAllStrings()}. */ @Deprecated public static String[] readStrings() { return readAllStrings(); } /** * Interactive test of basic functionality. * * @param args the command-line arguments */ public static void main(String[] args) { StdOut.print("Type a string: "); String s = StdIn.readString(); StdOut.println("Your string was: " + s); StdOut.println(); StdOut.print("Type an int: "); int a = StdIn.readInt(); StdOut.println("Your int was: " + a); StdOut.println(); StdOut.print("Type a boolean: "); boolean b = StdIn.readBoolean(); StdOut.println("Your boolean was: " + b); StdOut.println(); StdOut.print("Type a double: "); double c = StdIn.readDouble(); StdOut.println("Your double was: " + c); StdOut.println(); } } /****************************************************************************** * Copyright 2002-2016, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * * Algorithms, 4th edition by Robert Sedgewick and Kevin Wayne, * Addison-Wesley Professional, 2011, ISBN 0-321-57351-X. * http://algs4.cs.princeton.edu * * * algs4.jar is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * algs4.jar is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with algs4.jar. If not, see http://www.gnu.org/licenses. ******************************************************************************/ ================================================ FILE: code/algs4/StdOut.java ================================================ /****************************************************************************** * Compilation: javac StdOut.java * Execution: java StdOut * Dependencies: none * * Writes data of various types to standard output. * ******************************************************************************/ package algs4; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.util.Locale; /** * This class provides methods for printing strings and numbers to standard output. *

* Getting started. * To use this class, you must have {@code StdOut.class} in your * Java classpath. If you used our autoinstaller, you should be all set. * Otherwise, download * StdOut.java * and put a copy in your working directory. *

* Here is an example program that uses {@code StdOut}: *

 *   public class TestStdOut {
 *       public static void main(String[] args) {
 *           int a = 17;
 *           int b = 23;
 *           int sum = a + b;
 *           StdOut.println("Hello, World");
 *           StdOut.printf("%d + %d = %d\n", a, b, sum);
 *       }
 *   }
 *  
*

* Differences with System.out. * The behavior of {@code StdOut} is similar to that of {@link System#out}, * but there are a few subtle differences: *

*

* Reference. * For additional documentation, * see Section 1.5 of * Introduction to Programming in Java: An Interdisciplinary Approach * by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne */ public final class StdOut { // force Unicode UTF-8 encoding; otherwise it's system dependent private static final String CHARSET_NAME = "UTF-8"; // assume language = English, country = US for consistency with StdIn private static final Locale LOCALE = Locale.US; // send output here private static PrintWriter out; // this is called before invoking any methods static { try { out = new PrintWriter(new OutputStreamWriter(System.out, CHARSET_NAME), true); } catch (UnsupportedEncodingException e) { System.out.println(e); } } // don't instantiate private StdOut() { } /** * Closes standard output. */ public static void close() { out.close(); } /** * Terminates the current line by printing the line-separator string. */ public static void println() { out.println(); } /** * Prints an object to this output stream and then terminates the line. * * @param x the object to print */ public static void println(Object x) { out.println(x); } /** * Prints a boolean to standard output and then terminates the line. * * @param x the boolean to print */ public static void println(boolean x) { out.println(x); } /** * Prints a character to standard output and then terminates the line. * * @param x the character to print */ public static void println(char x) { out.println(x); } /** * Prints a double to standard output and then terminates the line. * * @param x the double to print */ public static void println(double x) { out.println(x); } /** * Prints an integer to standard output and then terminates the line. * * @param x the integer to print */ public static void println(float x) { out.println(x); } /** * Prints an integer to standard output and then terminates the line. * * @param x the integer to print */ public static void println(int x) { out.println(x); } /** * Prints a long to standard output and then terminates the line. * * @param x the long to print */ public static void println(long x) { out.println(x); } /** * Prints a short integer to standard output and then terminates the line. * * @param x the short to print */ public static void println(short x) { out.println(x); } /** * Prints a byte to standard output and then terminates the line. *

* To write binary data, see {@link BinaryStdOut}. * * @param x the byte to print */ public static void println(byte x) { out.println(x); } /** * Flushes standard output. */ public static void print() { out.flush(); } /** * Prints an object to standard output and flushes standard output. * * @param x the object to print */ public static void print(Object x) { out.print(x); out.flush(); } /** * Prints a boolean to standard output and flushes standard output. * * @param x the boolean to print */ public static void print(boolean x) { out.print(x); out.flush(); } /** * Prints a character to standard output and flushes standard output. * * @param x the character to print */ public static void print(char x) { out.print(x); out.flush(); } /** * Prints a double to standard output and flushes standard output. * * @param x the double to print */ public static void print(double x) { out.print(x); out.flush(); } /** * Prints a float to standard output and flushes standard output. * * @param x the float to print */ public static void print(float x) { out.print(x); out.flush(); } /** * Prints an integer to standard output and flushes standard output. * * @param x the integer to print */ public static void print(int x) { out.print(x); out.flush(); } /** * Prints a long integer to standard output and flushes standard output. * * @param x the long integer to print */ public static void print(long x) { out.print(x); out.flush(); } /** * Prints a short integer to standard output and flushes standard output. * * @param x the short integer to print */ public static void print(short x) { out.print(x); out.flush(); } /** * Prints a byte to standard output and flushes standard output. * * @param x the byte to print */ public static void print(byte x) { out.print(x); out.flush(); } /** * Prints a formatted string to standard output, using the specified format * string and arguments, and then flushes standard output. * * * @param format the format string * @param args the arguments accompanying the format string */ public static void printf(String format, Object... args) { out.printf(LOCALE, format, args); out.flush(); } /** * Prints a formatted string to standard output, using the locale and * the specified format string and arguments; then flushes standard output. * * @param locale the locale * @param format the format string * @param args the arguments accompanying the format string */ public static void printf(Locale locale, String format, Object... args) { out.printf(locale, format, args); out.flush(); } /** * Unit tests some of the methods in {@code StdOut}. * * @param args the command-line arguments */ public static void main(String[] args) { // write to stdout StdOut.println("Test"); StdOut.println(17); StdOut.println(true); StdOut.printf("%.6f\n", 1.0/7.0); } } /****************************************************************************** * Copyright 2002-2016, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * * Algorithms, 4th edition by Robert Sedgewick and Kevin Wayne, * Addison-Wesley Professional, 2011, ISBN 0-321-57351-X. * http://algs4.cs.princeton.edu * * * algs4.jar is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * algs4.jar is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with algs4.jar. If not, see http://www.gnu.org/licenses. ******************************************************************************/ ================================================ FILE: code/algs4/StdRandom.java ================================================ /****************************************************************************** * Compilation: javac StdRandom.java * Execution: java StdRandom * Dependencies: StdOut.java * * A library of static methods to generate pseudo-random numbers from * different distributions (bernoulli, uniform, gaussian, discrete, * and exponential). Also includes a method for shuffling an array. * * * % java StdRandom 5 * seed = 1316600602069 * 59 16.81826 true 8.83954 0 * 32 91.32098 true 9.11026 0 * 35 10.11874 true 8.95396 3 * 92 32.88401 true 8.87089 0 * 72 92.55791 true 9.46241 0 * * % java StdRandom 5 * seed = 1316600616575 * 96 60.17070 true 8.72821 0 * 79 32.01607 true 8.58159 0 * 81 59.49065 true 9.10423 1 * 96 51.65818 true 9.02102 0 * 99 17.55771 true 8.99762 0 * * % java StdRandom 5 1316600616575 * seed = 1316600616575 * 96 60.17070 true 8.72821 0 * 79 32.01607 true 8.58159 0 * 81 59.49065 true 9.10423 1 * 96 51.65818 true 9.02102 0 * 99 17.55771 true 8.99762 0 * * * Remark * ------ * - Relies on randomness of nextDouble() method in java.util.Random * to generate pseudorandom numbers in [0, 1). * * - This library allows you to set and get the pseudorandom number seed. * * - See http://www.honeylocust.com/RngPack/ for an industrial * strength random number generator in Java. * ******************************************************************************/ package algs4; import java.util.Random; /** * The {@code StdRandom} class provides static methods for generating * random number from various discrete and continuous distributions, * including Bernoulli, uniform, Gaussian, exponential, pareto, * Poisson, and Cauchy. It also provides method for shuffling an * array or subarray. *

* For additional documentation, * see Section 2.2 of * Introduction to Programming in Java: An Interdisciplinary Approach * by Robert Sedgewick and Kevin Wayne. * * @author Robert Sedgewick * @author Kevin Wayne */ public final class StdRandom { private static Random random; // pseudo-random number generator private static long seed; // pseudo-random number generator seed // static initializer static { // this is how the seed was set in Java 1.4 seed = System.currentTimeMillis(); random = new Random(seed); } // don't instantiate private StdRandom() { } /** * Sets the seed of the pseudorandom number generator. * This method enables you to produce the same sequence of "random" * number for each execution of the program. * Ordinarily, you should call this method at most once per program. * * @param s the seed */ public static void setSeed(long s) { seed = s; random = new Random(seed); } /** * Returns the seed of the pseudorandom number generator. * * @return the seed */ public static long getSeed() { return seed; } /** * Returns a random real number uniformly in [0, 1). * * @return a random real number uniformly in [0, 1) */ public static double uniform() { return random.nextDouble(); } /** * Returns a random integer uniformly in [0, n). * * @param n number of possible integers * @return a random integer uniformly between 0 (inclusive) and {@code N} (exclusive) * @throws IllegalArgumentException if {@code n <= 0} */ public static int uniform(int n) { if (n <= 0) throw new IllegalArgumentException("Parameter N must be positive"); return random.nextInt(n); } /////////////////////////////////////////////////////////////////////////// // STATIC METHODS BELOW RELY ON JAVA.UTIL.RANDOM ONLY INDIRECTLY VIA // THE STATIC METHODS ABOVE. /////////////////////////////////////////////////////////////////////////// /** * Returns a random real number uniformly in [0, 1). * * @return a random real number uniformly in [0, 1) * @deprecated Replaced by {@link #uniform()}. */ @Deprecated public static double random() { return uniform(); } /** * Returns a random integer uniformly in [a, b). * * @param a the left endpoint * @param b the right endpoint * @return a random integer uniformly in [a, b) * @throws IllegalArgumentException if {@code b <= a} * @throws IllegalArgumentException if {@code b - a >= Integer.MAX_VALUE} */ public static int uniform(int a, int b) { if (b <= a) throw new IllegalArgumentException("Invalid range"); if ((long) b - a >= Integer.MAX_VALUE) throw new IllegalArgumentException("Invalid range"); return a + uniform(b - a); } /** * Returns a random real number uniformly in [a, b). * * @param a the left endpoint * @param b the right endpoint * @return a random real number uniformly in [a, b) * @throws IllegalArgumentException unless {@code a < b} */ public static double uniform(double a, double b) { if (!(a < b)) throw new IllegalArgumentException("Invalid range"); return a + uniform() * (b-a); } /** * Returns a random boolean from a Bernoulli distribution with success * probability p. * * @param p the probability of returning {@code true} * @return {@code true} with probability {@code p} and * {@code false} with probability {@code p} * @throws IllegalArgumentException unless {@code p >= 0.0} and {@code p <= 1.0} */ public static boolean bernoulli(double p) { if (!(p >= 0.0 && p <= 1.0)) throw new IllegalArgumentException("Probability must be between 0.0 and 1.0"); return uniform() < p; } /** * Returns a random boolean from a Bernoulli distribution with success * probability 1/2. * * @return {@code true} with probability 1/2 and * {@code false} with probability 1/2 */ public static boolean bernoulli() { return bernoulli(0.5); } /** * Returns a random real number from a standard Gaussian distribution. * * @return a random real number from a standard Gaussian distribution * (mean 0 and standard deviation 1). */ public static double gaussian() { // use the polar form of the Box-Muller transform double r, x, y; do { x = uniform(-1.0, 1.0); y = uniform(-1.0, 1.0); r = x*x + y*y; } while (r >= 1 || r == 0); return x * Math.sqrt(-2 * Math.log(r) / r); // Remark: y * Math.sqrt(-2 * Math.log(r) / r) // is an independent random gaussian } /** * Returns a random real number from a Gaussian distribution with mean μ * and standard deviation σ. * * @param mu the mean * @param sigma the standard deviation * @return a real number distributed according to the Gaussian distribution * with mean {@code mu} and standard deviation {@code sigma} */ public static double gaussian(double mu, double sigma) { return mu + sigma * gaussian(); } /** * Returns a random integer from a geometric distribution with success * probability p. * * @param p the parameter of the geometric distribution * @return a random integer from a geometric distribution with success * probability {@code p}; or {@code Integer.MAX_VALUE} if * {@code p} is (nearly) equal to {@code 1.0}. * @throws IllegalArgumentException unless {@code p >= 0.0} and {@code p <= 1.0} */ public static int geometric(double p) { if (!(p >= 0.0 && p <= 1.0)) throw new IllegalArgumentException("Probability must be between 0.0 and 1.0"); // using algorithm given by Knuth return (int) Math.ceil(Math.log(uniform()) / Math.log(1.0 - p)); } /** * Returns a random integer from a Poisson distribution with mean λ. * * @param lambda the mean of the Poisson distribution * @return a random integer from a Poisson distribution with mean {@code lambda} * @throws IllegalArgumentException unless {@code lambda > 0.0} and not infinite */ public static int poisson(double lambda) { if (!(lambda > 0.0)) throw new IllegalArgumentException("Parameter lambda must be positive"); if (Double.isInfinite(lambda)) throw new IllegalArgumentException("Parameter lambda must not be infinite"); // using algorithm given by Knuth // see http://en.wikipedia.org/wiki/Poisson_distribution int k = 0; double p = 1.0; double expLambda = Math.exp(-lambda); do { k++; p *= uniform(); } while (p >= expLambda); return k-1; } /** * Returns a random real number from the standard Pareto distribution. * * @return a random real number from the standard Pareto distribution */ public static double pareto() { return pareto(1.0); } /** * Returns a random real number from a Pareto distribution with * shape parameter α. * * @param alpha shape parameter * @return a random real number from a Pareto distribution with shape * parameter {@code alpha} * @throws IllegalArgumentException unless {@code alpha > 0.0} */ public static double pareto(double alpha) { if (!(alpha > 0.0)) throw new IllegalArgumentException("Shape parameter alpha must be positive"); return Math.pow(1 - uniform(), -1.0/alpha) - 1.0; } /** * Returns a random real number from the Cauchy distribution. * * @return a random real number from the Cauchy distribution. */ public static double cauchy() { return Math.tan(Math.PI * (uniform() - 0.5)); } /** * Returns a random integer from the specified discrete distribution. * * @param probabilities the probability of occurrence of each integer * @return a random integer from a discrete distribution: * {@code i} with probability {@code probabilities[i]} * @throws NullPointerException if {@code probabilities} is {@code null} * @throws IllegalArgumentException if sum of array entries is not (very nearly) equal to {@code 1.0} * @throws IllegalArgumentException unless {@code probabilities[i] >= 0.0} for each index {@code i} */ public static int discrete(double[] probabilities) { if (probabilities == null) throw new NullPointerException("argument array is null"); double EPSILON = 1E-14; double sum = 0.0; for (int i = 0; i < probabilities.length; i++) { if (!(probabilities[i] >= 0.0)) throw new IllegalArgumentException("array entry " + i + " must be nonnegative: " + probabilities[i]); sum += probabilities[i]; } if (sum > 1.0 + EPSILON || sum < 1.0 - EPSILON) throw new IllegalArgumentException("sum of array entries does not approximately equal 1.0: " + sum); // the for loop may not return a value when both r is (nearly) 1.0 and when the // cumulative sum is less than 1.0 (as a result of floating-point roundoff error) while (true) { double r = uniform(); sum = 0.0; for (int i = 0; i < probabilities.length; i++) { sum = sum + probabilities[i]; if (sum > r) return i; } } } /** * Returns a random integer from the specified discrete distribution. * * @param frequencies the frequency of occurrence of each integer * @return a random integer from a discrete distribution: * {@code i} with probability proportional to {@code frequencies[i]} * @throws NullPointerException if {@code frequencies} is {@code null} * @throws IllegalArgumentException if all array entries are {@code 0} * @throws IllegalArgumentException if {@code frequencies[i]} is negative for any index {@code i} * @throws IllegalArgumentException if sum of frequencies exceeds {@code Integer.MAX_VALUE} (231 - 1) */ public static int discrete(int[] frequencies) { if (frequencies == null) throw new NullPointerException("argument array is null"); long sum = 0; for (int i = 0; i < frequencies.length; i++) { if (frequencies[i] < 0) throw new IllegalArgumentException("array entry " + i + " must be nonnegative: " + frequencies[i]); sum += frequencies[i]; } if (sum == 0) throw new IllegalArgumentException("at least one array entry must be positive"); if (sum >= Integer.MAX_VALUE) throw new IllegalArgumentException("sum of frequencies overflows an int"); // pick index i with probabilitity proportional to frequency double r = uniform((int) sum); sum = 0; for (int i = 0; i < frequencies.length; i++) { sum += frequencies[i]; if (sum > r) return i; } // can't reach here assert false; return -1; } /** * Returns a random real number from an exponential distribution * with rate λ. * * @param lambda the rate of the exponential distribution * @return a random real number from an exponential distribution with * rate {@code lambda} * @throws IllegalArgumentException unless {@code lambda > 0.0} */ public static double exp(double lambda) { if (!(lambda > 0.0)) throw new IllegalArgumentException("Rate lambda must be positive"); return -Math.log(1 - uniform()) / lambda; } /** * Rearranges the elements of the specified array in uniformly random order. * * @param a the array to shuffle * @throws NullPointerException if {@code a} is {@code null} */ public static void shuffle(Object[] a) { if (a == null) throw new NullPointerException("argument array is null"); int n = a.length; for (int i = 0; i < n; i++) { int r = i + uniform(n-i); // between i and n-1 Object temp = a[i]; a[i] = a[r]; a[r] = temp; } } /** * Rearranges the elements of the specified array in uniformly random order. * * @param a the array to shuffle * @throws NullPointerException if {@code a} is {@code null} */ public static void shuffle(double[] a) { if (a == null) throw new NullPointerException("argument array is null"); int n = a.length; for (int i = 0; i < n; i++) { int r = i + uniform(n-i); // between i and n-1 double temp = a[i]; a[i] = a[r]; a[r] = temp; } } /** * Rearranges the elements of the specified array in uniformly random order. * * @param a the array to shuffle * @throws NullPointerException if {@code a} is {@code null} */ public static void shuffle(int[] a) { if (a == null) throw new NullPointerException("argument array is null"); int n = a.length; for (int i = 0; i < n; i++) { int r = i + uniform(n-i); // between i and n-1 int temp = a[i]; a[i] = a[r]; a[r] = temp; } } /** * Rearranges the elements of the specified subarray in uniformly random order. * * @param a the array to shuffle * @param lo the left endpoint (inclusive) * @param hi the right endpoint (inclusive) * @throws NullPointerException if {@code a} is {@code null} * @throws IndexOutOfBoundsException unless {@code (0 <= lo) && (lo <= hi) && (hi < a.length)} * */ public static void shuffle(Object[] a, int lo, int hi) { if (a == null) throw new NullPointerException("argument array is null"); if (lo < 0 || lo > hi || hi >= a.length) { throw new IndexOutOfBoundsException("Illegal subarray range"); } for (int i = lo; i <= hi; i++) { int r = i + uniform(hi-i+1); // between i and hi Object temp = a[i]; a[i] = a[r]; a[r] = temp; } } /** * Rearranges the elements of the specified subarray in uniformly random order. * * @param a the array to shuffle * @param lo the left endpoint (inclusive) * @param hi the right endpoint (inclusive) * @throws NullPointerException if {@code a} is {@code null} * @throws IndexOutOfBoundsException unless {@code (0 <= lo) && (lo <= hi) && (hi < a.length)} */ public static void shuffle(double[] a, int lo, int hi) { if (a == null) throw new NullPointerException("argument array is null"); if (lo < 0 || lo > hi || hi >= a.length) { throw new IndexOutOfBoundsException("Illegal subarray range"); } for (int i = lo; i <= hi; i++) { int r = i + uniform(hi-i+1); // between i and hi double temp = a[i]; a[i] = a[r]; a[r] = temp; } } /** * Rearranges the elements of the specified subarray in uniformly random order. * * @param a the array to shuffle * @param lo the left endpoint (inclusive) * @param hi the right endpoint (inclusive) * @throws NullPointerException if {@code a} is {@code null} * @throws IndexOutOfBoundsException unless {@code (0 <= lo) && (lo <= hi) && (hi < a.length)} */ public static void shuffle(int[] a, int lo, int hi) { if (a == null) throw new NullPointerException("argument array is null"); if (lo < 0 || lo > hi || hi >= a.length) { throw new IndexOutOfBoundsException("Illegal subarray range"); } for (int i = lo; i <= hi; i++) { int r = i + uniform(hi-i+1); // between i and hi int temp = a[i]; a[i] = a[r]; a[r] = temp; } } /** * Unit test. * * @param args the command-line arguments */ public static void main(String[] args) { int n = Integer.parseInt(args[0]); if (args.length == 2) StdRandom.setSeed(Long.parseLong(args[1])); double[] probabilities = { 0.5, 0.3, 0.1, 0.1 }; int[] frequencies = { 5, 3, 1, 1 }; String[] a = "A B C D E F G".split(" "); StdOut.println("seed = " + StdRandom.getSeed()); for (int i = 0; i < n; i++) { StdOut.printf("%2d ", uniform(100)); StdOut.printf("%8.5f ", uniform(10.0, 99.0)); StdOut.printf("%5b ", bernoulli(0.5)); StdOut.printf("%7.5f ", gaussian(9.0, 0.2)); StdOut.printf("%1d ", discrete(probabilities)); StdOut.printf("%1d ", discrete(frequencies)); StdRandom.shuffle(a); for (String s : a) StdOut.print(s); StdOut.println(); } } } /****************************************************************************** * Copyright 2002-2016, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * * Algorithms, 4th edition by Robert Sedgewick and Kevin Wayne, * Addison-Wesley Professional, 2011, ISBN 0-321-57351-X. * http://algs4.cs.princeton.edu * * * algs4.jar is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * algs4.jar is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with algs4.jar. If not, see http://www.gnu.org/licenses. ******************************************************************************/ ================================================ FILE: code/chapter1_1_Programming_Model/BinarySearch.java ================================================ package chapter1_1_Programming_Model; import java.util.Arrays; import algs4.*; /** * 二分查找 * @author huang */ public class BinarySearch { public static int rank(int key, int[] a) { // 数组必须有序 int lo = 0; int hi = a.length - 1; while(lo <= hi) { int mid = lo + (hi - lo) / 2; if(key < a[mid]) hi = mid - 1; else if(key > a[mid]) lo = mid + 1; else return mid; } return -1; } public static void main(String[] args) { int[] whitelist = In.readInts(args[0]); Arrays.sort(whitelist); while(!StdIn.isEmpty()){ int key = StdIn.readInt(); if(rank(key, whitelist) < 0) StdOut.println(key); } } } ================================================ FILE: code/chapter1_1_Programming_Model/BouncingBall.java ================================================ package chapter1_1_Programming_Model; import algs4.StdDraw; public class BouncingBall { public static void main(String[] args) { // set the scale of the coordinate system StdDraw.setXscale(-1.0, 1.0); StdDraw.setYscale(-1.0, 1.0); StdDraw.enableDoubleBuffering(); // initial values double rx = .480, ry = .860; // position double vx = .015, vy = .023; // velocity double radius = .05; // radius // main animation loop while(true) { // bounce off wall according to law of elastic collision if(Math.abs(rx + vx) > 1.0 - radius) vx = -vx; if(Math.abs(ry + vy) > 1.0 - radius) vy = -vy; // update position rx = rx + vx; ry = ry + vy; // clear the background StdDraw.setPenColor(StdDraw.GRAY); StdDraw.filledSquare(0, 0, 1.0); // draw ball on the screen StdDraw.setPenColor(StdDraw.BLACK); StdDraw.filledCircle(rx, ry, radius); // display and pause for 20 ms StdDraw.show(); StdDraw.pause(20); } } } ================================================ FILE: code/chapter1_1_Programming_Model/Ex1.java ================================================ package chapter1_1_Programming_Model; import algs4.StdOut; public class Ex1 { public static void main(String[] args) { StdOut.println((0 + 15) / 2); StdOut.println(2.0e-6 * 100000000.1); StdOut.println(true && false || true && true); } } ================================================ FILE: code/chapter1_1_Programming_Model/Ex10.java ================================================ package chapter1_1_Programming_Model; import algs4.StdOut; public class Ex10 { public static void main(String[] args) { int[] a = new int[10]; for(int i = 0; i < 10; i++) a[i] = i*i; for(int i = 9; i >= 0; i--) StdOut.printf("%d ", a[i]); } } ================================================ FILE: code/chapter1_1_Programming_Model/Ex11.java ================================================ package chapter1_1_Programming_Model; import algs4.StdOut; import algs4.StdRandom; public class Ex11 { public static void main(String[] args) { boolean[][] arr = new boolean[7][6]; for(int i = 0; i < 7; i++) { for(int j = 0; j < 6; j++) { double num = StdRandom.uniform(); if(num > 0.5) arr[i][j] = true; else arr[i][j] = false; } } printBoolArr(arr); } public static void printBoolArr (boolean[][] arr) { StdOut.print(' '); for (int i = 0; i < arr[0].length; i++) { StdOut.print(i); } StdOut.println(); for (int i = 0; i < arr.length; i++) { StdOut.print(i); for (int j = 0; j < arr[i].length; j++) { StdOut.print(arr[i][j] ? '*' : ' '); } StdOut.println(); } } } ================================================ FILE: code/chapter1_1_Programming_Model/Ex13.java ================================================ package chapter1_1_Programming_Model; import algs4.StdOut; import algs4.StdRandom; public class Ex13 { public static void main(String[] args) { int M = 3, N = 4; int[][] arr = new int[M][N]; for (int i = 0; i < arr.length; i++) for (int j = 0; j < arr[0].length; j++) arr[i][j] = StdRandom.uniform(20); StdOut.println("oldArr: "); printArr(arr); StdOut.println("newArr: "); printArr(arrReverse(arr)); } public static int[][] arrReverse(int[][] arr) { int[][] newArr = new int[arr[0].length][arr.length]; for(int i = 0; i < newArr.length; i++) for(int j = 0; j < newArr[0].length; j++) newArr[i][j] = arr[j][i]; return newArr; } public static void printArr(int[][] arr) { for (int i = 0; i < arr.length; i++) { for(int j = 0; j < arr[0].length; j++) { StdOut.printf("%d ",arr[i][j]); } StdOut.println(); } } } ================================================ FILE: code/chapter1_1_Programming_Model/Ex14.java ================================================ package chapter1_1_Programming_Model; import algs4.StdOut; public class Ex14 { public static void main(String[] args) { StdOut.print(lg(8)); } public static int lg(int N) { int i = 0; while(n2(i) <= N) i++; return i-1; } public static int n2(int n) { int result = 1; for(int i = 0; i < n; i++) result *= 2; return result; } } ================================================ FILE: code/chapter1_1_Programming_Model/Ex15.java ================================================ package chapter1_1_Programming_Model; import algs4.StdOut; public class Ex15 { public static void main(String[] args) { int[] arr = {0, 1, 1, 1}; int[] timesArr = histogram(arr, 2); for(int j = 0; j < timesArr.length; j++) { StdOut.printf("%d ", timesArr[j]); } } public static int[] histogram(int[] a, int M) { int[] arr = new int[M]; for(int i = 0; i < M; i++){ int times = 0; for(int j = 0; j < a.length; j++){ if(a[j] == i) times++; } arr[i] = times; } return arr; } } ================================================ FILE: code/chapter1_1_Programming_Model/Ex19.java ================================================ package chapter1_1_Programming_Model; import algs4.StdOut; public class Ex19 { static final int M = 100; static long[] arr = new long[M]; public static long F(int N) { if(N == 0) arr[N] = 0; else if(N == 1) arr[N] = 1; else arr[N] = arr[N - 1] + arr[N - 2]; return arr[N]; } public static void main(String[] args) { for(int N = 0; N < M; N++) StdOut.println(N + " " + F(N)); } } ================================================ FILE: code/chapter1_1_Programming_Model/Ex2.java ================================================ package chapter1_1_Programming_Model; import algs4.StdOut; public class Ex2 { public static void main(String[] args) { StdOut.println((1 + 2.236) / 2); StdOut.println(1 + 2 + 3 + 4.0); StdOut.println(4.1 >= 4); StdOut.println(1 + 2 + "3"); } } ================================================ FILE: code/chapter1_1_Programming_Model/Ex20.java ================================================ package chapter1_1_Programming_Model; import algs4.StdOut; public class Ex20 { public static void main(String[] args) { StdOut.print(ln10(9)); } public static double ln10(int N) { if(N == 1) return Math.log10(1); return Math.log10(N) + ln10(N - 1); } } ================================================ FILE: code/chapter1_1_Programming_Model/Ex3.java ================================================ package chapter1_1_Programming_Model; import algs4.StdIn; import algs4.StdOut; public class Ex3 { public static void main(String[] args) { int a = StdIn.readInt(); int b = StdIn.readInt(); int c = StdIn.readInt(); if(a == b && a == c) { StdOut.println("equal"); } else { StdOut.println("not equal"); } } } ================================================ FILE: code/chapter1_1_Programming_Model/Ex30.java ================================================ package chapter1_1_Programming_Model; import algs4.StdOut; public class Ex30 { public static void main(String[] args) { int N = 10; boolean[][] arr = new boolean [N][N]; setArrBoolean(arr); for(int i = 0; i < arr.length; i++) { for(int j = 0; j < arr.length; j++) { StdOut.printf("%b ", arr[i][j]); } StdOut.println(); } } public static void setArrBoolean(boolean[][] arr) { for(int i = 0; i < arr.length; i++) { for(int j = 0; j < arr.length; j++) { arr[i][j] = true; for(int k = 2; k <= i; k++) { if((i % k == 0) && (j % k == 0)) { arr[i][j] = false; } } } } } } ================================================ FILE: code/chapter1_1_Programming_Model/Ex31.java ================================================ package chapter1_1_Programming_Model; import java.awt.Color; import algs4.StdDraw; import algs4.StdRandom; public class Ex31 { static class Point { double x, y; public Point(double x, double y) { super(); this.x = x; this.y = y; } } public static void main(String[] args) { int N = Integer.parseInt(args[0]); double p = Double.parseDouble(args[1]); if(p < 0 || p > 1) { System.out.println("p is not valid!"); return; } Point[] points = new Point[N]; double angle = 360.0 / N; StdDraw.circle(.5, .5, .5); StdDraw.setPenRadius(.05); for(int i = 0; i < N; i++) { points[i] = new Point(.5 + .5 * Math.cos(angle * i * Math.PI / 180), .5 + .5 * Math.sin(angle * i * Math.PI / 180)); StdDraw.point(points[i].x, points[i].y); } StdDraw.setPenRadius(.01); StdDraw.setPenColor(Color.GRAY); for(int i = 0; i < N - 1; i++) { for(int j = i + 1; j < N; j++) { if(StdRandom.bernoulli(p)) { StdDraw.line(points[i].x, points[i].y, points[j].x, points[j].y); } } } } } ================================================ FILE: code/chapter1_1_Programming_Model/Ex32.java ================================================ package chapter1_1_Programming_Model; import java.util.ArrayList; import java.util.Scanner; import algs4.StdDraw; public class Ex32 { public static void main(String[] args) { @SuppressWarnings("resource") Scanner scanner = new Scanner(System.in); ArrayList list = new ArrayList<>(); while (scanner.hasNextDouble()) { list.add(scanner.nextDouble()); } int N = Integer.parseInt(args[0]); double l = Double.parseDouble(args[1]), r = Double.parseDouble(args[2]); double length = (r - l) / N, start = l; while(start < r) { int height = 0; for(int i = 0; i < list.size(); i++) { if(start <= list.indexOf(i) && list.indexOf(i) < start + length) { height++; } } StdDraw.filledRectangle(start, height, length / 2, height); start += length; } } } ================================================ FILE: code/chapter1_1_Programming_Model/Ex6.java ================================================ package chapter1_1_Programming_Model; import algs4.StdOut; public class Ex6 { public static void main(String[] args) { int f = 0; int g = 1; for(int i = 0; i <= 15; i++){ StdOut.println(f); f = f + g; g = f - g; } } } ================================================ FILE: code/chapter1_1_Programming_Model/Ex7a.java ================================================ package chapter1_1_Programming_Model; import algs4.StdOut; public class Ex7a { public static void main(String[] args) { double t = 9.0; while(Math.abs(t - 9.0 / t) > .001) t = (9.0 / t + t) / 2.0; StdOut.printf("%.5f\n", t); } } ================================================ FILE: code/chapter1_1_Programming_Model/Ex7b.java ================================================ package chapter1_1_Programming_Model; import algs4.StdOut; public class Ex7b { public static void main(String[] args) { int sum = 0; for(int i = 1; i < 1000; i++) for(int j = 0; j < i; j++) sum++; StdOut.println(sum); } } ================================================ FILE: code/chapter1_1_Programming_Model/Ex7c.java ================================================ package chapter1_1_Programming_Model; import algs4.StdOut; public class Ex7c { public static void main(String[] args) { int sum = 0; for(int i = 1; i < 1000; i *= 2) for(int j = 0; j < 1000; j++) sum++; StdOut.println(sum); } } ================================================ FILE: code/chapter1_1_Programming_Model/Ex8.java ================================================ package chapter1_1_Programming_Model; public class Ex8 { public static void main(String[] args) { System.out.println('b'); System.out.println('b' + 'c'); System.out.println((char)('a' + 4)); } } ================================================ FILE: code/chapter1_1_Programming_Model/Ex9.java ================================================ package chapter1_1_Programming_Model; import algs4.StdOut; public class Ex9 { public static void main(String[] args) { int a = Integer.parseInt(args[0]); String result = ""; for(int n = a; n > 0; n /= 2) { result = (n % 2) + result; } StdOut.println(result); } } ================================================ FILE: code/chapter1_1_Programming_Model/RightTriangle.java ================================================ package chapter1_1_Programming_Model; import algs4.StdDraw; public class RightTriangle { public static void main(String[] args) { StdDraw.square(.5, .5, .5); StdDraw.setPenColor(StdDraw.BLUE); StdDraw.line(.5, .5, .9, .5); StdDraw.line(.9, .5, .5, .8); StdDraw.line(.5, .5, .5, .8); StdDraw.circle(.7, .65, .25); } } ================================================ FILE: code/chapter1_1_Programming_Model/Sattolo.java ================================================ package chapter1_1_Programming_Model; import algs4.StdIn; import algs4.StdOut; public class Sattolo { private Sattolo() { } public static void cycle(Object[] a) { int n = a.length; for(int i = n; i > 1; i--) { int r = (int)(Math.random() * (i - 1)); Object swap = a[r]; a[r] = a[i - 1]; a[i - 1] = swap; } } public static void main(String[] args) { String[] a = StdIn.readAllStrings(); Sattolo.cycle(a); for(int i = 0; i < a.length; i++) StdOut.println(a[i]); } } ================================================ FILE: code/chapter1_1_Programming_Model/StdDrawTest.java ================================================ package chapter1_1_Programming_Model; import java.util.Arrays; import algs4.*; public class StdDrawTest { public static void main(String[] args) { // int N = 100; // StdDraw.setXscale(0, N); // StdDraw.setYscale(0, N*N); // StdDraw.setPenRadius(.01); // for(int i = 1; i <= N; i++){ // StdDraw.point(i, i); // StdDraw.point(i, i*i); // StdDraw.point(i, i*Math.log(i)); // } int N = 50; double[] a = new double[N]; for (int i = 0; i < N; i++) { a[i] = StdRandom.uniform(); } Arrays.sort(a); for (int i = 0; i < N; i++) { double x = 1.0 * i / N; double y = a[i] / 2.0; double rw = 0.5 / N; double rh = a[i] / 2.0; StdDraw.filledRectangle(x, y, rw, rh); } } } ================================================ FILE: code/chapter1_2_Data_Abstraction/Accumulator.java ================================================ package chapter1_2_Data_Abstraction; public class Accumulator { private double total; private int N; public void addDataValue(double val) { N++; total += val; } public double mean() { return total / N; } public String toString() { return "Mean (" + N + " values): " + String.format("%7.5f", mean()); } } ================================================ FILE: code/chapter1_2_Data_Abstraction/AccumulatorTest.java ================================================ package chapter1_2_Data_Abstraction; import algs4.StdOut; import algs4.StdRandom; public class AccumulatorTest { public static void main(String[] args) { int T = Integer.parseInt(args[0]); Accumulator a = new Accumulator(); for(int t = 0; t < T; t++) { a.addDataValue(StdRandom.uniform()); } StdOut.println(a); } } ================================================ FILE: code/chapter1_2_Data_Abstraction/Cat.java ================================================ package chapter1_2_Data_Abstraction; import algs4.In; import algs4.Out; public class Cat { public static void main(String[] args) { // 将所有输入文件复制到输出流(最后一个参数)中 Out out = new Out(args[args.length - 1]); for(int i = 0; i < args.length - 1; i++) { // 将第 i 个输入文件复制到输出流中 In in = new In(args[i]); String str = in.readAll(); out.println(str); in.close(); } out.close(); } } ================================================ FILE: code/chapter1_2_Data_Abstraction/Date.java ================================================ package chapter1_2_Data_Abstraction; public class Date { private final int month; private final int day; private final int year; public Date(int m, int d, int y) { this.month = m; this.day = d; this.year = y; } public int month() { return month; } public int day() { return day; } public int year() { return year; } public String toString() { return month() + "/" + day() + "/" + year(); } public boolean equals(Object x) { if(this == x) return true; if(x == null) return false; if(this.getClass() != x.getClass()) return false; Date that = (Date) x; if(this.day != that.day) return false; if(this.month != that.month) return false; if(this.year != that.year) return false; return true; } } ================================================ FILE: code/chapter1_2_Data_Abstraction/Flips.java ================================================ package chapter1_2_Data_Abstraction; import algs4.Counter; import algs4.StdOut; import algs4.StdRandom; public class Flips { public static void main(String[] args) { int T = Integer.parseInt(args[0]); Counter heads = new Counter("heads"); Counter tails = new Counter("tails"); for(int t = 0; t < T; t++) { if(StdRandom.bernoulli(.5)) heads.increment(); else tails.increment(); } StdOut.println(heads); StdOut.println(tails); int d = heads.tally() - tails.tally(); StdOut.println("delta: " + Math.abs(d)); } } ================================================ FILE: code/chapter1_2_Data_Abstraction/FlipsMax.java ================================================ package chapter1_2_Data_Abstraction; import algs4.Counter; import algs4.StdOut; import algs4.StdRandom; public class FlipsMax { public static Counter max(Counter x, Counter y) { if(x.tally() > y.tally()) { return x; } return y; } public static void main(String[] args) { int T = Integer.parseInt(args[0]); Counter heads = new Counter("heads"); Counter tails = new Counter("tails"); for(int t = 0; t < T; t++) { if(StdRandom.bernoulli(.5)) heads.increment(); else tails.increment(); } if(heads.tally() == tails.tally()) StdOut.println("Tie"); else StdOut.println(max(heads, tails) + " wins"); } } ================================================ FILE: code/chapter1_2_Data_Abstraction/Interval2DTest.java ================================================ package chapter1_2_Data_Abstraction; import algs4.Counter; import algs4.Interval1D; import algs4.Interval2D; import algs4.Point2D; import algs4.StdOut; public class Interval2DTest { public static void main(String[] args) { double xlo = Double.parseDouble(args[0]); double xhi = Double.parseDouble(args[1]); double ylo = Double.parseDouble(args[2]); double yhi = Double.parseDouble(args[3]); int T = Integer.parseInt(args[4]); Interval1D xinterval = new Interval1D(xlo, xhi); Interval1D yinterval = new Interval1D(ylo, yhi); Interval2D box = new Interval2D(xinterval, yinterval); box.draw(); Counter c = new Counter("hits"); for(int t = 0; t < T; t++) { double x = Math.random(); double y = Math.random(); Point2D p = new Point2D(x, y); if(box.contains(p)) c.increment(); else p.draw(); } StdOut.println(c); StdOut.println(box.area()); } } ================================================ FILE: code/chapter1_2_Data_Abstraction/Rolls.java ================================================ package chapter1_2_Data_Abstraction; import algs4.Counter; import algs4.StdOut; import algs4.StdRandom; public class Rolls { public static void main(String[] args) { int T = Integer.parseInt(args[0]); int SIDES = 6; Counter[] rolls = new Counter[SIDES + 1]; for(int i = 0; i <= SIDES; i++) { rolls[i] = new Counter(i + "'s"); } for(int t = 0; t < T; t++) { int result = StdRandom.uniform(1, SIDES + 1); rolls[result].increment(); } for(int i = 1; i <= SIDES; i++) { StdOut.println(rolls[i]); } } } ================================================ FILE: code/chapter1_2_Data_Abstraction/StaticSETofInts.java ================================================ package chapter1_2_Data_Abstraction; import java.util.Arrays; public class StaticSETofInts { private int[] a; public StaticSETofInts(int[] keys) { a = new int[keys.length]; for(int i = 0; i < keys.length; i++) { a[i] = keys[i]; // 保护性复制 } Arrays.sort(a); } public boolean contains(int key) { return rank(key) != -1; } private int rank(int key) { // 二分查找 int lo = 0; int hi = a.length; while(lo <= hi) { int mid = lo + (hi - lo) / 2; if(key > a[mid]) { lo = mid + 1; } else if(key < a[mid]) { hi = mid - 1; } else { return mid; } } return -1; } } ================================================ FILE: code/chapter1_2_Data_Abstraction/VisualAccumulator.java ================================================ package chapter1_2_Data_Abstraction; import algs4.StdDraw; public class VisualAccumulator { private double total; private int N; public VisualAccumulator(int trials, double max) { StdDraw.setXscale(0, trials); StdDraw.setYscale(0, max); StdDraw.setPenRadius(.005); } public void addDataValue(double val) { N++; total += val; StdDraw.setPenColor(StdDraw.DARK_GRAY); StdDraw.point(N, val); StdDraw.setPenColor(StdDraw.RED); StdDraw.point(N, mean()); } public double mean() { return total / N; } public String toString() { return "Mean (" + N + " values): " + String.format("%7.5f", mean()); } } ================================================ FILE: code/chapter1_2_Data_Abstraction/VisualAccumulatorTest.java ================================================ package chapter1_2_Data_Abstraction; import algs4.StdOut; import algs4.StdRandom; public class VisualAccumulatorTest { public static void main(String[] args) { int T = Integer.parseInt(args[0]); VisualAccumulator a = new VisualAccumulator(T, 1.0); for(int t = 0; t < T; t++) { a.addDataValue(StdRandom.uniform()); } StdOut.println(a); } } ================================================ FILE: code/chapter1_2_Data_Abstraction/WhiteList.java ================================================ package chapter1_2_Data_Abstraction; import algs4.In; import algs4.StdIn; import algs4.StdOut; public class WhiteList { public static void main(String[] args) { int[] w = In.readInts(args[0]); StaticSETofInts seTofInts = new StaticSETofInts(w); while(!StdIn.isEmpty()) { // 读取键,如果不在白名单中则打印它 int key = StdIn.readInt(); if(!seTofInts.contains(key)) StdOut.println(key); } } } ================================================ FILE: code/chapter1_2_Data_Abstraction/in1.txt ================================================ This is ================================================ FILE: code/chapter1_2_Data_Abstraction/in2.txt ================================================ a tiny test. ================================================ FILE: code/chapter1_3_Bags_Queues_Stacks/ResizingArrayStack.java ================================================ package chapter1_3_Bags_Queues_Stacks; import java.util.Iterator; public class ResizingArrayStack implements Iterable{ private Item[] a = (Item[]) new Object[1]; // 栈元素 private int N = 0; public boolean isEmpty() { return N == 0; } public int size() { return N; } public void resize(int max) { // 将栈移动到一个大小为 max 的新数组 Item[] temp = (Item[]) new Object[max]; for(int i = 0; i < N; i++) temp[i] = a[i]; a = temp; } public void push(Item item) { if(N == a.length) resize(2 * a.length); a[N++] = item; } public Item pop() { Item item = a[--N]; a[N] = null; if(N > 0 && N == a.length / 4) resize(a.length / 2); return item; } @Override public Iterator iterator() { return new ReverseArrayIterator(); } private class ReverseArrayIterator implements Iterator { private int i = N; public boolean hasNext() { return i > 0; } public Item next() { return a[--i]; } public void remove() {} } } ================================================ FILE: code/chapter1_4_Analysis_of_Algorithms/DoublingRatio.java ================================================ package chapter1_4_Analysis_of_Algorithms; import algs4.StdOut; import algs4.StdRandom; public class DoublingRatio { public static double timeTrial(int N) { // 为处理 N 个随机的六位整数的 ThreeSum.count() 计时 int MAX = 1000000; int[] a = new int[N]; for(int i = 0; i < N; i++) a[i] = StdRandom.uniform(-MAX, MAX); Stopwatch timer = new Stopwatch(); int cnt = ThreeSum.count(a); return timer.elapsedTime(); } public static void main(String[] args) { double prev = timeTrial(125); for(int N = 250; true; N *= 2) { double time = timeTrial(N); StdOut.printf("%6d %7.1f ", N, time); StdOut.printf("%5.1f\n", time/prev); prev = time; } } } ================================================ FILE: code/chapter1_4_Analysis_of_Algorithms/DoublingTest.java ================================================ package chapter1_4_Analysis_of_Algorithms; import algs4.StdOut; import algs4.StdRandom; public class DoublingTest { public static double timeTrial(int N) { // 为处理 N 个随机的六位整数的 ThreeSum.count() 计时 int MAX = 1000000; int[] a = new int[N]; for(int i = 0; i < N; i++) a[i] = StdRandom.uniform(-MAX, MAX); Stopwatch timer = new Stopwatch(); int cnt = ThreeSum.count(a); return timer.elapsedTime(); } public static void main(String[] args) { // 打印运行时间的表格 for(int N = 250; true; N *= 2) { double time = timeTrial(N); StdOut.printf("%7d %5.1f\n", N, time); } } } ================================================ FILE: code/chapter1_4_Analysis_of_Algorithms/Stopwatch.java ================================================ package chapter1_4_Analysis_of_Algorithms; public class Stopwatch { private final long start; public Stopwatch() { start = System.currentTimeMillis(); } public double elapsedTime() { long now = System.currentTimeMillis(); return (now - start) / 1000.0; } } ================================================ FILE: code/chapter1_4_Analysis_of_Algorithms/StopwatchTest.java ================================================ package chapter1_4_Analysis_of_Algorithms; import algs4.StdOut; import algs4.StdRandom; public class StopwatchTest { public static void main(String[] args) { int N = Integer.parseInt(args[0]); int[] a = new int[N]; for(int i = 0; i < N; i++) a[i] = StdRandom.uniform(-1000000, 1000000); Stopwatch timer = new Stopwatch(); int cnt = ThreeSum.count(a); double time = timer.elapsedTime(); StdOut.println(cnt + " triples " + time + " seconds"); } } ================================================ FILE: code/chapter1_4_Analysis_of_Algorithms/ThreeSum.java ================================================ package chapter1_4_Analysis_of_Algorithms; import algs4.In; import algs4.StdOut; public class ThreeSum { public static int count(int[] a) { int N = a.length; int cnt = 0; for(int i = 0; i < N; i++) for(int j = i + 1; j < N; j++) for(int k = j + 1; k < N; k++) if(a[i] + a[j] + a[k] == 0) cnt++; return cnt; } public static void main(String[] args) { int[] a = In.readInts(args[0]); StdOut.println(count(a)); } } ================================================ FILE: code/chapter1_5_Case_Study_Union_Find/UF.java ================================================ package chapter1_5_Case_Study_Union_Find; import algs4.StdIn; import algs4.StdOut; public class UF { private int[] id; // 分量id(以触点作为索引) private int count; // 分量数量 public UF(int N) { // 初始化分量 id 数组 count = N; id = new int[N]; for (int i = 0; i < N; i++) id[i] = i; } public int count() { return count; } public boolean connected(int p, int q) { return find(p) == find(q); } /* quick-find 算法 */ // public int find(int p) { // return id[p]; // } // // public void union(int p, int q) { // // 将 p 和 q 归并到同样的分量中 // int pID = find(p); // int qID = find(q); // // // 如果 p 和 q 已经在相同的分量之中则不需要采取任何行动 // if(pID == qID) // return; // // // 将 p 的分量重命名为 q 的名称 // for(int i = 0; i < id.length; i++) // if(id[i] == pID) // id[i] = qID; // count--; // } /* quick-union 算法 */ private int find(int p) { // 找出分量的名称 while(p != id[p]) p = id[p]; return p; } private void union(int p, int q) { // 将 p 和 q 的根节点统一 int pRoot = find(p); int qRoot = find(q); if(pRoot == qRoot) return; id[pRoot] = qRoot; count--; } public static void main(String[] args) { // 解决由StdIn得到的动态连通性问题 int N = StdIn.readInt(); // 读取触点数量 UF uf = new UF(N); // 初始化 N 个分量 while (!StdIn.isEmpty()) { int p = StdIn.readInt(); int q = StdIn.readInt(); // 读取整数对 if (uf.connected(p, q)) // 如果已经连通则忽略 continue; uf.union(p, q); // 归并分量 StdOut.println(p + " " + q); // 打印连接 } StdOut.println(uf.count + "components"); } } ================================================ FILE: code/chapter1_5_Case_Study_Union_Find/WeightedQuickUnionUF.java ================================================ package chapter1_5_Case_Study_Union_Find; /** * 加权 quick-union 算法 * @author huang */ public class WeightedQuickUnionUF { private int[] id; // 父链接数组(由触点索引) private int[] sz; // (由触点索引的)各个根节点所对应的分量的大小 private int count; // 连通分量的数量 public WeightedQuickUnionUF(int N) { count = N; id = new int[N]; for(int i = 0; i < N; i++) id[i] = i; sz = new int[N]; for(int i = 0; i < N; i++) sz[i] = 1; } public int count() { return count; } public boolean connected(int p, int q) { return find(p) == find(q); } public int find(int p) { // 跟随链接找到根节点 while(p != id[p]) p = id[p]; return p; } public void union(int p, int q) { int pRoot = find(p); int qRoot = find(q); if(pRoot == qRoot) return; // 将小树的根节点连接到大树的根节点 if(sz[pRoot] < sz[qRoot]) { id[pRoot] = qRoot; sz[qRoot] += sz[pRoot]; } else { id[qRoot] = pRoot; sz[pRoot] += sz[qRoot]; } count--; } } ================================================ FILE: code/chapter2_1_Elementary_Sorts/Insertion.java ================================================ package chapter2_1_Elementary_Sorts; import algs4.StdOut; /** * 插入排序 * @author huang */ @SuppressWarnings({"rawtypes", "unused", "unchecked"}) public class Insertion { public static void sort(Comparable[] a) { // 将 a[] 按升序排列 int N = a.length; for(int i = 1; i < N; i++) { // 将 a[i] 插入到 a[i-1]、a[i-2]、a[i-3]...之中 /** 有改进空间,见练习 2.1.25 */ for(int j = i; j > 0 && less(a[j], a[j-1]); j--) exch(a, j, j-1); } } private static boolean less(Comparable v, Comparable w) { // 对元素进行比较 return v.compareTo(w) < 0; } private static void exch(Comparable[] a, int i, int j) { // 将元素交换位置 Comparable t = a[i]; a[i] = a[j]; a[j] = t; } private static void show(Comparable[] a) { // 在单行中打印数组 for(int i = 0; i < a.length; i++) StdOut.print(a[i] + " "); StdOut.println(); } public static boolean isSorted(Comparable[] a) { // 测试数组元素是否有序 for(int i = 1; i < a.length; i++) if(less(a[i], a[i - 1])) return false; return true; } } ================================================ FILE: code/chapter2_1_Elementary_Sorts/Selection.java ================================================ package chapter2_1_Elementary_Sorts; import algs4.StdOut; /** * 选择排序 * @author huang */ @SuppressWarnings({"rawtypes", "unused", "unchecked"}) public class Selection { public static void sort(Comparable[] a) { // 将 a[] 按升序排列 int N = a.length; for(int i = 0 ; i < N; i++) { // 将 a[i] 和 a[i...N]中最小的元素交换 int min = i; // 最小元素的索引 for(int j = i+1; j < N; j++) if(less(a[j], a[min])) min = j; exch(a, i, min); } } private static boolean less(Comparable v, Comparable w) { // 对元素进行比较 return v.compareTo(w) < 0; } private static void exch(Comparable[] a, int i, int j) { // 将元素交换位置 Comparable t = a[i]; a[i] = a[j]; a[j] = t; } private static void show(Comparable[] a) { // 在单行中打印数组 for(int i = 0; i < a.length; i++) StdOut.print(a[i] + " "); StdOut.println(); } public static boolean isSorted(Comparable[] a) { // 测试数组元素是否有序 for(int i = 1; i < a.length; i++) if(less(a[i], a[i - 1])) return false; return true; } } ================================================ FILE: code/chapter2_1_Elementary_Sorts/Shell.java ================================================ package chapter2_1_Elementary_Sorts; import algs4.StdOut; /** * 希尔排序 * @author huang */ @SuppressWarnings({"rawtypes", "unused", "unchecked"}) public class Shell { public static void sort(Comparable[] a) { // 将 a[] 按升序排列 int N = a.length; int h = 1; while(h < N / 3) h = 3 * h + 1; // 1, 4, 13, 40, 121, 364, 1093, ... while(h >= 1) { // 将数组变为 h 有序 for(int i = h; i < N; i++) { // 将 a[i] 插入到 a[i-h],a[i-2*h],a[i-3*h]... 之中 for(int j = i; j >= h && less(a[j], a[j -h]); j -= h) exch(a, j, j-h); } h /= 3; } } private static boolean less(Comparable v, Comparable w) { // 对元素进行比较 return v.compareTo(w) < 0; } private static void exch(Comparable[] a, int i, int j) { // 将元素交换位置 Comparable t = a[i]; a[i] = a[j]; a[j] = t; } private static void show(Comparable[] a) { // 在单行中打印数组 for(int i = 0; i < a.length; i++) StdOut.print(a[i] + " "); StdOut.println(); } public static boolean isSorted(Comparable[] a) { // 测试数组元素是否有序 for(int i = 1; i < a.length; i++) if(less(a[i], a[i - 1])) return false; return true; } } ================================================ FILE: code/chapter2_1_Elementary_Sorts/SortCompare.java ================================================ package chapter2_1_Elementary_Sorts; import algs4.StdOut; import algs4.StdRandom; import chapter1_4_Analysis_of_Algorithms.Stopwatch; /** * @author huang * 比较两种排序算法 */ @SuppressWarnings({"rawtypes"}) public class SortCompare { public static double time(String alg, Comparable[] a) { Stopwatch timer = new Stopwatch(); if(alg.equals("Insertion")) Insertion.sort(a); if(alg.equals("Selection")) Selection.sort(a); if(alg.equals("Shell")) Shell.sort(a); // if(alg.equals("Merge")) // Merge.sort(a); // if(alg.equals("Quick")) // Quick.sort(a); // if(alg.equals("Heap")) // Heap.sort(a); return timer.elapsedTime(); } public static double timeRandomInput(String alg, int N, int T) { // 使用算法 alg 将 T 个长度为 N 的数组排序 double total = 0.0; Double[] a = new Double[N]; for(int t = 0; t < T; t++) { // 进行一次测试(生成一个数组并排序) for(int i = 0; i < N; i ++) a[i] = StdRandom.uniform(); /** 练习 2.5.31 */ total += time(alg, a); } return total; } public static void main(String[] args) { String alg1 = args[0]; String alg2 = args[1]; int N = Integer.parseInt(args[2]); int T = Integer.parseInt(args[3]); double t1 = timeRandomInput(alg1, N, T); // 算法 1 的总时间 double t2 = timeRandomInput(alg2, N, T); // 算法 2 的总时间 StdOut.printf("For %d random Doubles\n %s is", N, alg1); StdOut.printf(" %.1f times faster than %s\n", t2/t1, alg2); } } ================================================ FILE: code/chapter2_2_Mergesort/Merge.java ================================================ package chapter2_2_Mergesort; import algs4.StdOut; /** * @author huang * 自顶向下的归并排序 */ @SuppressWarnings({"rawtypes", "unused", "unchecked"}) public class Merge { private static Comparable[] aux; // 归并所需的辅助数组 public static void sort(Comparable[] a) { aux = new Comparable[a.length]; sort(a, 0, a.length - 1); } private static void sort(Comparable[] a, int lo, int hi) { // 将数组 a[lo..hi] 排序 if(hi <= lo) return; int mid = lo + (hi - lo) / 2; sort(a, lo, mid); // 将左半边排序 sort(a, mid + 1, hi); // 将右半边排序 if(less(a[mid+1], a[mid])) // 为 false 则认为数组已经是有序的,跳过 merge() merge(a, lo, mid, hi); // 归并结果 } public static void merge(Comparable[] a, int lo, int mid, int hi) { // 将 a[lo..mid] 和 a[mid+1..hi] 归并 int i = lo, j = mid + 1; for(int k = lo; k <= hi; k++) // 将 a[lo..hi] 复制到 aux[lo..hi] aux[k] = a[k]; for(int k = lo; k <= hi; k++) if(i > mid) // 左半边元素用尽 a[k] = aux[j++]; else if(j > hi) // 右半边元素用尽 a[k] = aux[i++]; else if(less(aux[i], aux[j])) a[k] = aux[i++]; else a[k] = aux[j++]; } private static boolean less(Comparable v, Comparable w) { // 对元素进行比较 return v.compareTo(w) < 0; } private static void exch(Comparable[] a, int i, int j) { // 将元素交换位置 Comparable t = a[i]; a[i] = a[j]; a[j] = t; } private static void show(Comparable[] a) { // 在单行中打印数组 for(int i = 0; i < a.length; i++) StdOut.print(a[i] + " "); StdOut.println(); } public static boolean isSorted(Comparable[] a) { // 测试数组元素是否有序 for(int i = 1; i < a.length; i++) if(less(a[i], a[i - 1])) return false; return true; } } ================================================ FILE: code/chapter2_2_Mergesort/MergeBU.java ================================================ package chapter2_2_Mergesort; import algs4.StdOut; /** * @author huang * 自底向上的归并排序 */ @SuppressWarnings({"rawtypes", "unused", "unchecked"}) public class MergeBU { private static Comparable[] aux; // 归并所需的辅助数组 public static void sort(Comparable[] a) { // 进行 lgN 次两两归并 int N = a.length; aux = new Comparable[N]; for(int sz = 1; sz < N; sz = sz+sz) for(int lo = 0; lo < N - sz; lo += sz+sz) merge(a, lo, lo+sz-1, Math.min(lo+sz+sz-1, N-1)); // 最后一个子数组的大小只有在数组大小是 sz 的偶数倍时才会等于 sz,否则小于 sz } public static void merge(Comparable[] a, int lo, int mid, int hi) { // 将 a[lo..mid] 和 a[mid+1..hi] 归并 int i = lo, j = mid + 1; for(int k = lo; k <= hi; k++) // 将 a[lo..hi] 复制到 aux[lo..hi] aux[k] = a[k]; for(int k = lo; k <= hi; k++) if(i > mid) // 左半边元素用尽 a[k] = aux[j++]; else if(j > hi) // 右半边元素用尽 a[k] = aux[i++]; else if(less(aux[i], aux[j])) a[k] = aux[i++]; else a[k] = aux[j++]; } private static boolean less(Comparable v, Comparable w) { // 对元素进行比较 return v.compareTo(w) < 0; } private static void exch(Comparable[] a, int i, int j) { // 将元素交换位置 Comparable t = a[i]; a[i] = a[j]; a[j] = t; } private static void show(Comparable[] a) { // 在单行中打印数组 for(int i = 0; i < a.length; i++) StdOut.print(a[i] + " "); StdOut.println(); } public static boolean isSorted(Comparable[] a) { // 测试数组元素是否有序 for(int i = 1; i < a.length; i++) if(less(a[i], a[i - 1])) return false; return true; } } ================================================ FILE: code/chapter2_3_Quicksort/Ex25.java ================================================ package chapter2_3_Quicksort; import algs4.StdOut; import algs4.StdRandom; import chapter1_4_Analysis_of_Algorithms.Stopwatch; import chapter1_4_Analysis_of_Algorithms.ThreeSum; import chapter2_1_Elementary_Sorts.Insertion; import chapter2_1_Elementary_Sorts.SortCompare; /** * 快速排序切换到插入排序 * @author huang */ @SuppressWarnings({"rawtypes", "unused", "unchecked"}) public class Ex25 { public static void main(String[] args) { for(int N = 1000; N <= 1000000; N *= 10) { Comparable[] a = new Comparable[N]; for(int i = 0; i < N; i++) a[i] = StdRandom.uniform(-1000000, 1000000); for(int M = 0; M <= 30; M++) { Comparable[] b = a.clone(); Stopwatch timer = new Stopwatch(); sort(b, 1); double time = timer.elapsedTime(); StdOut.println("Array size: " + N + ", M = " + M + ", time: " + time + " seconds"); } } } public static void sort(Comparable[] a, int M) { StdRandom.shuffle(a); // 消除对输入的依赖 sort(a,0, a.length - 1, M); } private static void sort(Comparable[] a, int lo, int hi, int M) { if(hi <= lo + M) { insertSort(a, lo, hi); return; } int j = partition(a, lo, hi); // 切分 sort(a, lo, j-1, M); // 将左半部分 a[lo .. j-1] sort(a, j+1, hi, M); // 将右半部分 a[j+1 .. hi] } private static void insertSort(Comparable[] a, int lo, int hi) { int N = a.length; for(int i = lo; i < hi+1; i++) { for(int j = i; j > 0 && less(a[j], a[j-1]); j--) exch(a, j, j-1); } } private static int partition(Comparable[] a, int lo, int hi) { // 将数组切分为 a[lo .. i-1],a[i],a[i+1 .. hi] int i = lo, j = hi+1; // 左右扫描指针 Comparable v = a[lo]; // 切分元素 while(true) { // 扫描左右,检查扫描是否结束并交换元素 while(less(a[++i], v)) if(i == hi) break; while(less(v, a[--j])) if(j == lo) break; if(i >= j) break; exch(a, i, j); } exch(a, lo, j); return j; } private static boolean less(Comparable v, Comparable w) { // 对元素进行比较 return v.compareTo(w) < 0; } private static void exch(Comparable[] a, int i, int j) { // 将元素交换位置 Comparable t = a[i]; a[i] = a[j]; a[j] = t; } private static void show(Comparable[] a) { // 在单行中打印数组 for(int i = 0; i < a.length; i++) StdOut.print(a[i] + " "); StdOut.println(); } public static boolean isSorted(Comparable[] a) { // 测试数组元素是否有序 for(int i = 1; i < a.length; i++) if(less(a[i], a[i - 1])) return false; return true; } } ================================================ FILE: code/chapter2_3_Quicksort/Quick.java ================================================ package chapter2_3_Quicksort; import algs4.StdOut; import algs4.StdRandom; /** * @author huang * 快速排序 */ @SuppressWarnings({"rawtypes", "unused", "unchecked"}) public class Quick { public static void sort(Comparable[] a) { StdRandom.shuffle(a); // 消除对输入的依赖 sort(a,0, a.length - 1); } private static void sort(Comparable[] a, int lo, int hi) { if(hi <= lo) return; int j = partition(a, lo, hi); // 切分 sort(a, lo, j-1); // 将左半部分 a[lo .. j-1] sort(a, j+1, hi); // 将右半部分 a[j+1 .. hi] } private static int partition(Comparable[] a, int lo, int hi) { // 将数组切分为 a[lo .. i-1],a[i],a[i+1 .. hi] int i = lo, j = hi+1; // 左右扫描指针 Comparable v = a[lo]; // 切分元素 while(true) { // 扫描左右,检查扫描是否结束并交换元素 while(less(a[++i], v)) if(i == hi) break; while(less(v, a[--j])) if(j == lo) break; if(i >= j) break; exch(a, i, j); } exch(a, lo, j); return j; } private static boolean less(Comparable v, Comparable w) { // 对元素进行比较 return v.compareTo(w) < 0; } private static void exch(Comparable[] a, int i, int j) { // 将元素交换位置 Comparable t = a[i]; a[i] = a[j]; a[j] = t; } private static void show(Comparable[] a) { // 在单行中打印数组 for(int i = 0; i < a.length; i++) StdOut.print(a[i] + " "); StdOut.println(); } public static boolean isSorted(Comparable[] a) { // 测试数组元素是否有序 for(int i = 1; i < a.length; i++) if(less(a[i], a[i - 1])) return false; return true; } } ================================================ FILE: code/chapter2_3_Quicksort/Quick3way.java ================================================ package chapter2_3_Quicksort; import algs4.StdOut; import algs4.StdRandom; /** * @author huang * 三向切分的快速排序 */ @SuppressWarnings({"rawtypes", "unused", "unchecked"}) public class Quick3way { public static void sort(Comparable[] a) { StdRandom.shuffle(a); // 消除对输入的依赖 sort(a,0, a.length - 1); } private static void sort(Comparable[] a, int lo, int hi) { if(hi <= lo) return; int lt = lo, i = lo+1, gt = hi; // lt: b 开始, gt: b 结束, i: c 开始 Comparable v = a[lo]; while(i <= gt) { int cmp = a[i].compareTo(v); if(cmp < 0) exch(a, lt++, i++); else if(cmp > 0) exch(a, i, gt--); else i++; } // 现在 a[lo .. lt-1] < v = a[lt .. gt] < a[gt+1 .. hi] 成立 sort(a, lo, lt - 1); sort(a, gt + 1, hi); } private static int partition(Comparable[] a, int lo, int hi) { // 将数组切分为 a[lo .. i-1],a[i],a[i+1 .. hi] int i = lo, j = hi+1; // 左右扫描指针 Comparable v = a[lo]; // 切分元素 while(true) { // 扫描左右,检查扫描是否结束并交换元素 while(less(a[++i], v)) if(i == hi) break; while(less(v, a[--j])) if(j == lo) break; if(i >= j) break; exch(a, i, j); } exch(a, lo, j); return j; } private static boolean less(Comparable v, Comparable w) { // 对元素进行比较 return v.compareTo(w) < 0; } private static void exch(Comparable[] a, int i, int j) { // 将元素交换位置 Comparable t = a[i]; a[i] = a[j]; a[j] = t; } private static void show(Comparable[] a) { // 在单行中打印数组 for(int i = 0; i < a.length; i++) StdOut.print(a[i] + " "); StdOut.println(); } public static boolean isSorted(Comparable[] a) { // 测试数组元素是否有序 for(int i = 1; i < a.length; i++) if(less(a[i], a[i - 1])) return false; return true; } } ================================================ FILE: code/chapter2_4_Priority_Queues/MaxPQ.java ================================================ package chapter2_4_Priority_Queues; /** * 基于堆的优先队列 * * @author huang */ public class MaxPQ> { private Key[] pq; // 基于堆的完全二叉树 private int N = 0; // 存储于 pq[1 .. N] 中,pq[0] 没有使用 @SuppressWarnings("unchecked") MaxPQ(int maxN) { // 创建一个初始容量为 max 的优先队列 pq = (Key[]) new Comparable[maxN + 1]; } // 向优先队列中插入一个元素 void insert(Key v) { pq[++N] = v; swim(N); } // 删除并返回最大元素 public Key delMax() { Key max = pq[1]; // 从根结点得到最大元素 exch(1, N--); // 将其和最后一个结点交换 pq[N+1] = null; // 防止对象游离 sink(1); // 恢复堆的有序性 return max; } boolean isEmpty() { // 返回队列是否为空 return N == 0; } int size() { // 返回优先队列中的元素个数 return N; } private boolean less(int i, int j) { return pq[i].compareTo(pq[j]) < 0; } private void exch(int i, int j) { Key t = pq[i]; pq[i] = pq[j]; pq[j] = t; } // 由下至上的堆有序化(上浮)的实现 private void swim(int k) { while(k > 1 && less(k/2, k)) { exch(k/2, k); k = k/2; } } // 由上至下的堆有序化(下沉)的实现 private void sink(int k) { while(2 * k <= N) { int j = 2 * k; if(j < N && less(j, j+1)) j++; if(!less(k, j)) break; exch(k, j); k = j; } } } ================================================ FILE: code/chapter2_4_Priority_Queues/TopM.java ================================================ package chapter2_4_Priority_Queues; import algs4.MinPQ; import algs4.Stack; import algs4.StdIn; import algs4.StdOut; public class TopM { public static void main(String[] args) { // 打印输入流中最大的 M 行 int M = Integer.parseInt(args[0]); MinPQ pq = new MinPQ<>(M+1); while(StdIn.hasNextLine()) { // 为下一行输入创建一个元素并放入优先队列中 pq.insert(new Transaction(StdIn.readLine())); if(pq.size() > M) pq.delMin(); // 如果优先队列中存在 M+1 } // 最大的 M 个元素都在优先队列中 Stack stack = new Stack(); while(!pq.isEmpty()) stack.push(pq.delMin()); for(Transaction t : stack) StdOut.println(t); } } ================================================ FILE: code/chapter3_1_Symbol_Tables/BinarySearchST.java ================================================ package chapter3_1_Symbol_Tables; import algs4.Queue; @SuppressWarnings("unchecked") public class BinarySearchST, Value> { private Key[] keys; private Value[] vals; private int N; public BinarySearchST(int capacity) { keys = (Key[]) new Comparable[capacity]; vals = (Value[]) new Object[capacity]; } private void resize(int max) { // 将栈移动到一个大小为 max 的新数组 Key[] tempKeys = (Key[]) new Comparable[max]; Value[] tempValues = (Value[]) new Object[max]; for(int i = 0; i < N; i++) { tempKeys[i] = keys[i]; tempValues[i] = vals[i]; } keys = tempKeys; vals = tempValues; } public int size() { return N; } public Value get(Key key) { if(isEmpty()) return null; int i = rank(key); if(i < N && keys[i].compareTo(key) == 0) return vals[i]; else return null; } public boolean isEmpty() { return N == 0; } public int rank(Key key) { int lo = 0, hi = N-1; while(lo <= hi) { int mid = lo + (hi - lo) / 2; int cmp = key.compareTo(keys[mid]); if(cmp < 0) hi = mid - 1; else if(cmp > 0) lo = mid + 1; else return mid; } return lo; } public void put(Key key, Value val) { // 查找键,找到则更新值,否则创建新的元素 int i = rank(key); if(i < N && keys[i].compareTo(key) == 0) { vals[i] = val; return; } if(N == keys.length) // 调整数组大小 resize(2 * keys.length); for(int j = N; j > i; j--) { keys[j] = keys[j-1]; vals[j] = vals[j-1]; } keys[i] = key; vals[i] = val; N++; } public Key min() { return keys[0]; } public Key max() { return keys[N-1]; } public Key select(int k) { return keys[k]; } public Key ceiling(Key key) { int i = rank(key); return keys[i]; } // public Key floor(Key key) { // // } public Key delete(Key key) { int i = rank(key); if(!(i < N && keys[i].compareTo(key) == 0)) { return null; } if(N < keys.length / 2) // 调整数组大小 resize(keys.length / 2); for(int j = i; j < N; j++) { keys[j] = keys[j+1]; vals[j] = vals[j+1]; } keys[N] = null; vals[N] = null; N--; return key; } public Iterable keys(Key lo, Key hi) { Queue q = new Queue(); for(int i = rank(lo); i < rank(hi); i++) q.enqueue(keys[i]); if(contains(hi)) q.enqueue(keys[rank(hi)]); return q; } public boolean contains(Key key) { int i = rank(key); return i < N && keys[i].compareTo(key) == 0; } } ================================================ FILE: code/chapter3_1_Symbol_Tables/FrequencyCounter.java ================================================ package chapter3_1_Symbol_Tables; import algs4.ST; import algs4.StdIn; import algs4.StdOut; /** * 符号表的用例 * @author huang */ public class FrequencyCounter { public static void main(String[] args) { int minlen = Integer.parseInt(args[0]); // 最小键长 ST st = new ST(); while(!StdIn.isEmpty()) { // 构造符号表并统计频率 String word = StdIn.readString(); if(word.length() < minlen) continue; // 忽略较短的单词 if(!st.contains(word)) st.put(word, 1); else st.put(word, st.get(word)+1); } // 找出出现频率最高的单词 String max = " "; st.put(max, 0); for(String word : st.keys()) if(st.get(word) > st.get(max)) max = word; StdOut.println(max + " " + st.get(max)); } } ================================================ FILE: code/chapter3_1_Symbol_Tables/SequentialSearchST.java ================================================ package chapter3_1_Symbol_Tables; public class SequentialSearchST{ private Node first; // 链表首结点 private class Node { Key key; Value val; Node next; public Node(Key key, Value val, Node next) { this.key = key; this.val = val; this.next = next; } } public Value get(Key key) { // 查找给定的键,返回相关联的值 for(Node x = first; x != null; x = x.next) if(key.equals(x.key)) return x.val; // 命中 return null; // 未命中 } public void put(Key key, Value val) { // 查找给定的键,找到则更新其值,否则在表中新建结点 for(Node x = first; x != null; x = x.next) if(key.equals(x.key)) { x.val = val; return; // 命中,更新 } first = new Node(key, val, first); // 未命中,新建结点 } } ================================================ FILE: code/chapter3_2_Binary_Search_Trees/BST.java ================================================ package chapter3_2_Binary_Search_Trees; import algs4.Queue; public class BST, Value> { private Node root; // 二叉查找树的根结点 private class Node { private Key key; // 键 private Value val; // 值 private Node left, right; // 指向子树的链接 private int N; // 以该结点为根的子树中的结点总数 public Node(Key key, Value val, int N) { this.key = key; this.val = val; this.N = N; } } public int size() { return size(root); } private int size(Node x) { if(x == null) return 0; else return x.N; } public Value get(Key key) { return get(root, key); } private Value get(Node x, Key key) { // 在以 x 为根结点的子树中查找并返回 key 所对应的值 if(x == null) return null; int cmp = key.compareTo(x.key); if(cmp < 0) return get(x.left, key); else if(cmp > 0) return get(x.right, key); else return x.val; } public void put(Key key, Value val) { // 查找 key,找到则更新它的值,否则为它创建一个新的结点 root = put(root, key, val); } private Node put(Node x, Key key, Value val) { // 如果 key 存在于以 x 为根结点的子树中则更新它的值; // 否则将以 key 和 val 为键值对的新结点插入到该子树中 if(x == null) return new Node(key, val, 1); int cmp = key.compareTo(x.key); if(cmp < 0) x.left = put(x.left, key, val); else if(cmp > 0) x.right = put(x.right, key, val); else x.val = val; x.N = size(x.left) + size(x.right) + 1; return x; } public Key min() { return min(root).key; } private Node min(Node x) { if(x.left == null) return x; return min(x.left); } public Key max() { return max(root).key; } private Node max(Node x) { if(x.right == null) return x; return min(x.right); } public Key floor(Key key) { Node x = floor(root, key); if(x == null) return null; return x.key; } private Node floor(Node x, Key key) { if(x == null) return null; int cmp = key.compareTo(x.key); if(cmp == 0) return x; if(cmp < 0) return floor(x.left, key); Node t = floor(x.right, key); if(t != null) return t; else return x; } public Key select(int k) { return select(root, k).key; } private Node select(Node x, int k) { // 返回排名为 k 的结点 if(x == null) return null; int t = size(x.left); if(t > k) return select(x.left, k); else if(t < k) return select(x.right, k-t-1); else return x; } public int rank(Key key) { return rank(key, root); } private int rank(Key key, Node x) { // 返回以 x 为根结点的子树中小于 x.key 的键的数量 if(x == null) return 0; int cmp = key.compareTo(x.key); if(cmp < 0) return rank(key, x.left); else if(cmp > 0) return 1 + size(x.left) + rank(key, x.left); else return size(x.left); } public void deleteMin() { root = deleteMin(root); } private Node deleteMin(Node x) { if(x.left == null) return x.right; x.left = deleteMin(x.left); x.N = size(x.left) + size(x.right) + 1; return x; } public void delete(Key key) { root = delete(root, key); } private Node delete(Node x, Key key) { if(x == null) return null; int cmp = key.compareTo(x.key); if(cmp < 0) x.left = delete(x.left, key); else if(cmp > 0) x.right = delete(x.right, key); else { if(x.right == null) return x.left; if(x.left == null) return x.right; Node t = x; x = min(t.right); x.right = deleteMin(t.right); x.left = t.left; } x.N = size(x.left) + size(x.right) + 1; return x; } public Iterable keys() { return keys(min(), max()); } public Iterable keys(Key lo, Key hi) { Queue queue = new Queue(); keys(root, queue, lo, hi); return queue; } private void keys(Node x, Queue queue, Key lo, Key hi) { if(x == null) return; int cmplo = lo.compareTo(x.key); int cmphi = hi.compareTo(x.key); if(cmplo < 0) keys(x.left, queue, lo, hi); if(cmplo <= 0 && cmplo >= 0) queue.enqueue(x.key); if(cmphi > 0) keys(x.right, queue, lo, hi); } } ================================================ FILE: code/chapter3_4_Hash_Tables/LinearProbingHashST.java ================================================ package chapter3_4_Hash_Tables; import algs4.Queue; @SuppressWarnings("unchecked") public class LinearProbingHashST { private int N; // 符号表中键值对的总数 private int M = 16; // 线性探测表的大小 private Key[] keys; // 键 private Value[] vals; // 值 public LinearProbingHashST() { keys = (Key[]) new Object[M]; vals = (Value[]) new Object[M]; } public LinearProbingHashST(int cap) { this.M = cap; keys = (Key[]) new Object[M]; vals = (Value[]) new Object[M]; } public int size() { return this.M; } private int hash(Key key) { return (key.hashCode() & 0x7fffffff) % M; } private void resize(int cap) { LinearProbingHashST t; t = new LinearProbingHashST(cap); for(int i = 0; i < M; i++) if(keys[i] != null) t.put(keys[i], vals[i]); keys = t.keys; vals = t.vals; M = t.M; } public void put(Key key, Value val) { if(N >= M/2) resize(2 * M); int i; for(i = hash(key); keys[i] != null; i = (i + 1) % M) if(keys[i].equals(key)) { vals[i] = val; return; } keys[i] = key; vals[i] = val; N++; } public Value get(Key key) { for(int i = hash(key); keys[i] != null; i = (i + 1) % M) if(keys[i].equals(key)) return vals[i]; return null; } public boolean contains(Key key) { return get(key) != null; } public void delete(Key key) { if(!contains(key)) return; int i = hash(key); while(!key.equals(keys[i])) i = (i + 1) % M; keys[i] = null; vals[i] = null; i = (i + 1) % M; while(keys[i] != null) { Key keyToRedo = keys[i]; Value valToRedo = vals[i]; keys[i] = null; vals[i] = null; N--; put(keyToRedo, valToRedo); i = (i + 1) % M; } N--; if(N > 0 && N == M/8) resize(M/2); } public Iterable keys() { Queue queue = new Queue(); for(int i = 0; i < M; i++) if(keys[i] != null) queue.enqueue(keys[i]); return queue; } } ================================================ FILE: code/chapter3_4_Hash_Tables/SeparateChainingHashST.java ================================================ package chapter3_4_Hash_Tables; import java.util.Iterator; import algs4.Queue; import chapter3_1_Symbol_Tables.SequentialSearchST; @SuppressWarnings("unchecked") public class SeparateChainingHashST { private int N; // 键值对总数 private int M; // 散列表的大小 private SequentialSearchST[] st; // 存放链表对象的数组 public SeparateChainingHashST() { this(997); } public SeparateChainingHashST(int M) { // 创建 M 条链表 this.M = M; st = (SequentialSearchST[]) new SequentialSearchST[M]; for(int i = 0; i < M; i++) st[i] = new SequentialSearchST(); } private int hash(Key key) { return (key.hashCode() & 0x7fffffff) % M; } public Value get(Key key) { return (Value) st[hash(key)].get(key); } public void put(Key key, Value val) { st[hash(key)].put(key, val); } public Iterable keys() { Queue queue = new Queue<>(); for(int i = 0; i < M; i++) for(Iterator iter = (Iterator) st[i].keys(); iter.hasNext();) queue.enqueue(iter.next()); return queue; } } ================================================ FILE: code/chapter3_5_Searching_Applications/SparseVector.java ================================================ package chapter3_5_Searching_Applications; import chapter3_4_Hash_Tables.LinearProbingHashST; /** * @author huang * 能够完成点乘的稀疏向量 */ public class SparseVector { private LinearProbingHashST st; public SparseVector() { st = new LinearProbingHashST(); } public int size() { return st.size(); } public void put(int i, double x) { st.put(i, x); } public double get(int i) { if(!st.contains(i)) return 0.0; return st.get(i); } public double dot(double[] that) { double sum = 0.0; for(int i : st.keys()) sum += that[i] * this.get(i); return sum; } } ================================================ FILE: code/chapter3_5_Searching_Applications/WhiteFilter.java ================================================ package chapter3_5_Searching_Applications; import java.util.HashSet; import algs4.In; import algs4.StdIn; import algs4.StdOut; /** * @author huang * 白名单过滤器 */ public class WhiteFilter { public static void main(String[] args) { HashSet set; set = new HashSet(); In in = new In(args[0]); while(!in.isEmpty()) set.add(in.readString()); while(!StdIn.isEmpty()) { String word = StdIn.readString(); if(set.contains(word)) StdOut.print(word + " "); } } } ================================================ FILE: code/chapter4_1_Undirected_Graphs/BreadthFirstPaths.java ================================================ package chapter4_1_Undirected_Graphs; import algs4.Queue; import algs4.Stack; public class BreadthFirstPaths { private boolean[] marked; // 到达该顶点的最短路径已知吗? private int[] edgeTo; // 到达该顶点的已知路径上的最后一个顶点 private final int s; // 起点 public BreadthFirstPaths(Graph G, int s) { marked = new boolean[G.V()]; edgeTo = new int[G.V()]; this.s = s; bfs(G, s); } private void bfs(Graph G, int s) { Queue queue = new Queue(); marked[s] = true; // 标记起点 queue.enqueue(s); // 将它加入队列 while(!queue.isEmpty()) { int v = queue.dequeue(); // 从队列中删去下一顶点 for(int w : G.adj(v)) if(!marked[w]) { edgeTo[w] = v; // 保存最短路径的最后一条边 marked[w] = true; // 标记它,因为最短路径已知 queue.enqueue(w); // 并将它添加到队列中 } } } public boolean hasPathTo(int v) { return marked[v]; } public Iterable pathTo(int v) { if(!hasPathTo(v)) return null; Stack path = new Stack<>(); for(int x = v; x != s; x = edgeTo[x]) path.push(x); path.push(s); return path; } } ================================================ FILE: code/chapter4_1_Undirected_Graphs/CC.java ================================================ package chapter4_1_Undirected_Graphs; /** * 查找连通分量 * @author huang */ public class CC { private boolean[] marked; private int[] id; private int count; // 既用来计量连通分量,同时作为连通分量的下标 public CC(Graph G) { marked = new boolean[G.V()]; id = new int[G.V()]; for(int s = 0; s < G.V(); s++) if(!marked[s]) { dfs(G, s); count++; } } private void dfs(Graph G, int v) { marked[v] = true; id[v] = count; for(int w : G.adj(v)) if(!marked[w]) dfs(G, w); } public boolean connected(int v, int w) { return id[v] == id[w]; } public int id(int v) { return id[v]; } public int count() { return count; } } ================================================ FILE: code/chapter4_1_Undirected_Graphs/Cycle.java ================================================ package chapter4_1_Undirected_Graphs; /** * 利用深度优先,判断 G 是否是无向图(假设不存在自环或平行边) * @author huang */ public class Cycle { private boolean[] marked; private boolean hasCycle; public Cycle(Graph G) { marked = new boolean[G.V()]; for(int s = 0; s < G.V(); s++) if(!marked[s]) dfs(G, s, s); } private void dfs(Graph G, int v, int u) { marked[v] = true; for(int w : G.adj(v)) { if(!marked[w]) dfs(G, w, v); else if(w != u) // v 是从 u 深度遍历得到的。因为不存在平行边,所以 w != v 即刻认为遇到了之前遍历过的点(即有环) hasCycle = true; } } public boolean hasCycle() { return hasCycle; } } ================================================ FILE: code/chapter4_1_Undirected_Graphs/DegreesOfSeparation.java ================================================ package chapter4_1_Undirected_Graphs; import algs4.StdIn; import algs4.StdOut; /** * 间隔的度数 * @author huang */ public class DegreesOfSeparation { public static void main(String[] args) { SymbolGraph sGraph = new SymbolGraph(args[0], args[1]); Graph G = sGraph.G(); String source = args[2]; if(!sGraph.contains(source)) { StdOut.println(source + "not in database."); return; } int s = sGraph.index(source); BreadthFirstPaths bfs = new BreadthFirstPaths(G, s); while(!StdIn.isEmpty()) { String sink = StdIn.readLine(); if(sGraph.contains(sink)) { int t = sGraph.index(sink); if(bfs.hasPathTo(t)) for(int v : bfs.pathTo(t)) StdOut.println(" " + sGraph.name(v)); else StdOut.println("Not connected"); } else StdOut.println("Not in database."); } } } ================================================ FILE: code/chapter4_1_Undirected_Graphs/DepthFirstPaths.java ================================================ package chapter4_1_Undirected_Graphs; import algs4.Stack; /** * edgeTo[w]=v 表示 v-w 是第一次访问 w 时经过的边 * edgeTo[] 数组是一颗用父链接表示的以 s 为根且含有所有与 s 连通的顶点的树 * @author huang */ public class DepthFirstPaths { private boolean[] marked; // 这个顶点上调用过 dfs() 了吗? private int[] edgeTo; // 从起点到一个顶点的已知路径上的最后一个顶点 private final int s; // 起点 public DepthFirstPaths(Graph G, int s) { marked = new boolean[G.V()]; edgeTo = new int[G.V()]; this.s = s; dfs(G, s); } private void dfs(Graph G, int v) { marked[v] = true; for(int w : G.adj(v)) if(!marked[w]) { edgeTo[w] = v; dfs(G, w); } } public boolean hasPathTo(int v) { return marked[v]; } public Iterable pathTo(int v) { if(!hasPathTo(v)) return null; Stack path = new Stack<>(); for(int x = v; x != s; x = edgeTo[x]) path.push(x); path.push(s); return path; } } ================================================ FILE: code/chapter4_1_Undirected_Graphs/DepthFirstSearch.java ================================================ package chapter4_1_Undirected_Graphs; public class DepthFirstSearch { private boolean[] marked; private int count; public DepthFirstSearch(Graph G, int s) { marked = new boolean[G.V()]; dfs(G, s); } private void dfs(Graph G, int v) { marked[v] = true; count++; for(int w : G.adj(v)) if(!marked[w]) dfs(G, w); } public boolean marked(int w) { return marked[w]; } public int count() { return count; } } ================================================ FILE: code/chapter4_1_Undirected_Graphs/Graph.java ================================================ package chapter4_1_Undirected_Graphs; import algs4.Bag; import algs4.In; public class Graph { private final int V; // 顶点数目 private int E; // 边的数目 private Bag[] adj; // 邻接表 @SuppressWarnings("unchecked") public Graph(int V) { this.V = V; this.E = 0; adj = (Bag[]) new Bag[V]; // 创建邻接表 for(int v = 0; v < V; v++) adj[v] = new Bag(); } public Graph(In in) { this(in.readInt()); // 读取 V 并将图初始化 int E = in.readInt(); // 读取 E for(int i = 0; i < E; i++) { // 添加一条边 int v = in.readInt(); // 读取一个顶点 int w = in.readInt(); // 读取另一个顶点 addEdge(v, w); // 添加一条连接它们的边 } } public int V() { return V; } public int E() { return E; } public void addEdge(int v, int w) { adj[v].add(w); adj[w].add(v); E++; } public Iterable adj(int v) { return adj[v]; } } ================================================ FILE: code/chapter4_1_Undirected_Graphs/SymbolGraph.java ================================================ package chapter4_1_Undirected_Graphs; import algs4.In; import algs4.ST; public class SymbolGraph { private ST st; // 符号名 -> 索引 private String[] keys; // 索引 -> 符号名 private Graph G; // 使用索引表示顶点的图 public SymbolGraph(String filename, String delim) { st = new ST(); In in = new In(filename); // 第一遍 while(in.hasNextLine()) { String[] a = in.readLine().split(delim); // 读取字符串 for(int i = 0; i < a.length; i++) if(!st.contains(a[i])) st.put(a[i], st.size()); } keys = new String[st.size()]; // 用来获得顶点名的反向索引是一个数组 for(String name : st.keys()) keys[st.get(name)] = name; G = new Graph(st.size()); in = new In(filename); // 第二遍 while(in.hasNextLine()) { // 构造图 String[] a = in.readLine().split(delim); // 将每一行的第一个顶点和该行的其他顶点相连 int v = st.get(a[0]); for(int i = 1; i < a.length; i++) G.addEdge(v, st.get(a[i])); } } // key 是一个顶点吗 boolean contains(String key) { return st.contains(key); } // key 的索引 int index(String key) { return st.get(key); } // 索引 v 的顶点名 String name(int v) { return keys[v]; } // 隐藏的 Graph 对象 Graph G() { return G; } } ================================================ FILE: code/chapter4_1_Undirected_Graphs/TestCC.java ================================================ package chapter4_1_Undirected_Graphs; import algs4.Bag; import algs4.In; import algs4.StdOut; /** * 找出一幅图的所有连通分量 * @author huang */ public class TestCC { @SuppressWarnings("unchecked") public static void main(String[] args) { Graph G = new Graph(new In(args[0])); CC cc = new CC(G); int M = cc.count(); StdOut.println(M + " components"); Bag[] components; components = (Bag[]) new Bag[M]; for(int i = 0; i < M; i++) components[i] = new Bag(); for(int v = 0; v < G.V(); v++) components[cc.id(v)].add(v); for(int i = 0; i < M; i++) { for(int v : components[i]) StdOut.print(v + " "); StdOut.println(); } } } ================================================ FILE: code/chapter4_1_Undirected_Graphs/TestPaths.java ================================================ package chapter4_1_Undirected_Graphs; import algs4.In; import algs4.StdOut; /** * 单点路径问题 * @author huang */ public class TestPaths { public static void main(String[] args) { Graph G = new Graph(new In(args[0])); int s = Integer.parseInt(args[1]); DepthFirstPaths search = new DepthFirstPaths(G, s); for(int v = 0; v < G.V(); v++) { StdOut.print(s + " to " + v + ": "); if(search.hasPathTo(v)) for(int x : search.pathTo(v)) if(x == s) StdOut.print(x); else StdOut.print("-" + x); StdOut.println(); } } } ================================================ FILE: code/chapter4_1_Undirected_Graphs/TestSearch.java ================================================ package chapter4_1_Undirected_Graphs; import algs4.In; import algs4.StdOut; public class TestSearch { public static void main(String[] args) { Graph G = new Graph(new In(args[0])); int s = Integer.parseInt(args[1]); Search search = new Search(G, s); for(int v = 0; v < G.V(); v++) if(search.marked(v)) StdOut.println(v + " "); StdOut.println(); if(search.count() != G.V()) StdOut.print("NOT "); StdOut.println("connected"); } } ================================================ FILE: code/chapter4_1_Undirected_Graphs/TestSymbolGraph.java ================================================ package chapter4_1_Undirected_Graphs; import algs4.StdIn; import algs4.StdOut; public class TestSymbolGraph { public static void main(String[] args) { String filename = args[0]; String delim = args[1]; SymbolGraph sGraph = new SymbolGraph(filename, delim); Graph G = sGraph.G(); while(StdIn.hasNextLine()) { String source = StdIn.readLine(); for(int w : G.adj(sGraph.index(source))) StdOut.println(" " + sGraph.name(w)); } } } ================================================ FILE: code/chapter4_1_Undirected_Graphs/TwoColor.java ================================================ package chapter4_1_Undirected_Graphs; /** * 判断 G 是否为二分图 * @author huang */ public class TwoColor { private boolean[] marked; private boolean[] color; private boolean isTwoColorable = true; public TwoColor(Graph G) { marked = new boolean[G.V()]; color = new boolean[G.V()]; for(int s = 0; s < G.V(); s++) if(!marked[s]) dfs(G, s); } private void dfs(Graph G, int v) { marked[v] = true; for(int w : G.adj(v)) if(!marked[w]) { color[w] = !color[v]; dfs(G, w); } else if(color[w] == color[v]) isTwoColorable = false; } public boolean isBipartite() { return isTwoColorable; } } ================================================ FILE: code/chapter4_2_Directed_Graphs/DepthFirstOrder.java ================================================ package chapter4_2_Directed_Graphs; import algs4.Queue; import algs4.Stack; public class DepthFirstOrder { private boolean[] marked; private Queue pre; // 所有顶点的前序排列 private Queue post; // 所有顶点的后序排列 private Stack reversePost; // 所有顶点的逆后序排列 public DepthFirstOrder(Digraph G) { pre = new Queue<>(); post = new Queue<>(); reversePost = new Stack<>(); marked = new boolean[G.V()]; for(int v = 0; v < G.V(); v++) if(!marked[v]) dfs(G, v); } private void dfs(Digraph G, int v) { pre.enqueue(v); marked[v] = true; for(int w : G.adj(v)) if(!marked[w]) dfs(G, w); post.enqueue(v); reversePost.push(v); } public Iterable pre() { return pre; } public Iterable post() { return post; } public Iterable reversePost() { return reversePost; } } ================================================ FILE: code/chapter4_2_Directed_Graphs/Digraph.java ================================================ package chapter4_2_Directed_Graphs; import algs4.Bag; import algs4.In; public class Digraph { private final int V; private int E; private Bag[] adj; @SuppressWarnings("unchecked") public Digraph(int V) { // 创建一幅含有 V 个顶点但没有边的有向图 this.V = V; this.E = 0; adj = (Bag[]) new Bag[V]; for(int v = 0; v < V; v++) { adj[v] = new Bag(); } } public Digraph(In in) { // 从输入流 in 中读取一幅有向图 this(in.readInt()); // 读取 V 并将图初始化 int E = in.readInt(); // 读取 E for(int i = 0; i < E; i++) { // 添加一条边 int v = in.readInt(); // 读取一个顶点 int w = in.readInt(); // 读取另一个顶点 addEdge(v, w); // 添加一条连接它们的边 } } public int V() { // 顶点总数 return V; } public int E() { // 边的总数 return E; } void addEdge(int v, int w) { // 向有向图中添加一条边 v -> w adj[v].add(w); E++; } public Iterable adj(int v) { // 由 v 指出的边所连接的所有顶点 return adj[v]; } public Digraph reverse() { // 该图的反向图 Digraph R = new Digraph(V); for(int v = 0; v < V; v++) for(int w : adj(v)) R.addEdge(w, v); return R; } } ================================================ FILE: code/chapter4_2_Directed_Graphs/DirectedCycle.java ================================================ package chapter4_2_Directed_Graphs; import algs4.Stack; public class DirectedCycle { private boolean[] marked; private int[] edgeTo; private Stack cycle; // 有向环中的所有顶点(如果存在) private boolean[] onStack; // 递归调用的栈上的所有顶点 public DirectedCycle(Digraph G) { onStack = new boolean[G.V()]; edgeTo = new int[G.V()]; marked = new boolean[G.V()]; for(int v = 0; v < G.V(); v++) { if(!marked[v]) dfs(G, v); } } private void dfs(Digraph G, int v) { onStack[v] = true; marked[v] = true; for(int w : G.adj(v)) if(this.hasCycle()) return; else if(!marked[w]) { edgeTo[w] = v; dfs(G, w); } else if(onStack[w]) { cycle = new Stack(); for(int x = v; x != w; x = edgeTo[x]) cycle.push(x); cycle.push(w); cycle.push(v); } onStack[v] = false; } public boolean hasCycle() { return cycle != null; } public Iterable cycle() { return cycle; } } ================================================ FILE: code/chapter4_2_Directed_Graphs/DirectedDFS.java ================================================ package chapter4_2_Directed_Graphs; import algs4.Bag; import algs4.In; import algs4.StdOut; /* * 深度优先搜索,解决单点可达性和多点可达性问题 */ public class DirectedDFS { private boolean[] marked; public DirectedDFS(Digraph G, int s) { marked = new boolean[G.V()]; dfs(G, s); } public DirectedDFS(Digraph G, Iterable sources) { marked = new boolean[G.V()]; for(int s : sources) if(!marked[s]) dfs(G, s); } private void dfs(Digraph G, int v) { marked[v] = true; for(int w : G.adj(v)) if(!marked[w]) dfs(G, w); } public boolean marked(int v) { return marked[v]; } public static void main(String[] args) { Digraph G = new Digraph(new In(args[0])); Bag sources = new Bag(); for(int i = 1; i < args.length; i++) sources.add(Integer.parseInt(args[i])); DirectedDFS reachable = new DirectedDFS(G, sources); for(int v = 0; v < G.V(); v++) if(reachable.marked(v)) StdOut.print(v + " "); StdOut.println(); } } ================================================ FILE: code/chapter4_2_Directed_Graphs/KosarajuSCC.java ================================================ package chapter4_2_Directed_Graphs; /* * 计算强连通分量的 Kosaraju 算法 */ public class KosarajuSCC { private boolean[] marked; // 已访问过的顶点 private int[] id; // 强连通分量的标识符 private int count; // 强连通分量的数量 public KosarajuSCC(Digraph G) { marked = new boolean[G.V()]; id = new int[G.V()]; DepthFirstOrder order = new DepthFirstOrder(G.reverse()); for(int s : order.reversePost()) if(!marked[s]) { dfs(G, s); count++; } } private void dfs(Digraph G, int v) { marked[v] = true; id[v] = count; for(int w : G.adj(v)) if(!marked[w]) dfs(G, w); } public boolean stronglyConnected(int v, int w) { return id[v] == id[w]; } public int id(int v) { return id[v]; } public int count() { return count; } } ================================================ FILE: code/chapter4_2_Directed_Graphs/SymbolDigraph.java ================================================ package chapter4_2_Directed_Graphs; import algs4.In; import algs4.ST; public class SymbolDigraph { private ST st; // 符号名 -> 索引 private String[] keys; // 索引 -> 符号名 private Digraph G; // 使用索引表示顶点的图 public SymbolDigraph(String filename, String delim) { st = new ST(); In in = new In(filename); // 第一遍 while(in.hasNextLine()) { String[] a = in.readLine().split(delim); // 读取字符串 for(int i = 0; i < a.length; i++) if(!st.contains(a[i])) st.put(a[i], st.size()); } keys = new String[st.size()]; // 用来获得顶点名的反向索引是一个数组 for(String name : st.keys()) keys[st.get(name)] = name; G = new Digraph(st.size()); in = new In(filename); // 第二遍 while(in.hasNextLine()) { // 构造图 String[] a = in.readLine().split(delim); // 将每一行的第一个顶点和该行的其他顶点相连 int v = st.get(a[0]); for(int i = 1; i < a.length; i++) G.addEdge(v, st.get(a[i])); } } // key 是一个顶点吗 boolean contains(String key) { return st.contains(key); } // key 的索引 int index(String key) { return st.get(key); } // 索引 v 的顶点名 public String name(int v) { return keys[v]; } // 隐藏的 Digraph 对象 public Digraph G() { return G; } } ================================================ FILE: code/chapter4_2_Directed_Graphs/Topological.java ================================================ package chapter4_2_Directed_Graphs; import algs4.StdOut; /* * 拓扑排序 */ public class Topological { private Iterable order; // 顶点的拓扑排序 public Topological(Digraph G) { DirectedCycle cyclefinder = new DirectedCycle(G); if(!cyclefinder.hasCycle()) { DepthFirstOrder dfs = new DepthFirstOrder(G); order = dfs.reversePost(); // 一幅有向无环图的拓扑顺序即为所有顶点的逆后序排列 } } public Iterable order() { return order; } public boolean isDAG() { return order != null; } public static void main(String[] args) { String filename = args[0]; String separator = args[1]; SymbolDigraph sGraph = new SymbolDigraph(filename, separator); Topological top = new Topological(sGraph.G()); for(int v : top.order()) StdOut.println(sGraph.name(v)); } } ================================================ FILE: code/chapter4_2_Directed_Graphs/TransitiveClosure.java ================================================ package chapter4_2_Directed_Graphs; /* * 有向图顶点对可达性 */ public class TransitiveClosure { private DirectedDFS[] all; public TransitiveClosure(Digraph G) { all = new DirectedDFS[G.V()]; for(int v = 0; v < G.V(); v++) all[v] = new DirectedDFS(G, v); } public boolean reachable(int v, int w) { return all[v].marked(w); } } ================================================ FILE: code/chapter4_3_Minimum_Spanning_Tree/Edge.java ================================================ package chapter4_3_Minimum_Spanning_Tree; /* * 带权重的边 */ public class Edge implements Comparable { private final int v; // 顶点之一 private final int w; // 另一个顶点 private final double weight; // 边的权重 public Edge(int v, int w, double weight) { this.v = v; this.w = w; this.weight = weight; } public double weight() { return weight; } // 边两端的顶点之一 public int either() { return v; } // 另一个顶点 public int other(int vertex) { if(vertex == v) return w; else if(vertex == w) return v; else throw new RuntimeException("Inconsistent edge"); } public int compareTo(Edge that) { if(this.weight() < that.weight()) return -1; else if(this.weight() > that.weight()) return +1; else return 0; } public String toString() { return String.format("%d-%d %.2f", v, w, weight); } } ================================================ FILE: code/chapter4_3_Minimum_Spanning_Tree/EdgeWeightedGraph.java ================================================ package chapter4_3_Minimum_Spanning_Tree; import algs4.Bag; import algs4.In; /* * 加权无向图 */ public class EdgeWeightedGraph { private final int V; // 顶点总数 private int E; // 边的总数 private Bag[] adj; // 邻接表 @SuppressWarnings("unchecked") public EdgeWeightedGraph(int V) { this.V = V; this.E = 0; adj = (Bag[]) new Bag[V]; for(int v = 0; v < V; v++) adj[v] = new Bag(); } public EdgeWeightedGraph(In in) { this(in.readInt()); // 读取 V 并将图初始化 int E = in.readInt(); // 读取 E for(int i = 0; i < E; i++) { // 添加一条边 int v = in.readInt(); // 读取一个顶点 int w = in.readInt(); // 读取另一个顶点 double weight = in.readDouble(); // 读取权重 Edge e = new Edge(v, w, weight); addEdge(e); // 添加一条连接它们的边 } } public int V() { return V; } public int E() { return E; } public void addEdge(Edge e) { int v = e.either(), w = e.other(v); adj[v].add(e); adj[w].add(e); E++; } public Iterable adj(int v) { return adj[v]; } // 图的所有边 public Iterable edges() { Bag b = new Bag(); for(int v = 0; v < V; v++) for(Edge e : adj[v]) if(e.other(v) > v) b.add(e); return b; } } ================================================ FILE: code/chapter4_3_Minimum_Spanning_Tree/KruskalMST.java ================================================ package chapter4_3_Minimum_Spanning_Tree; import algs4.MinPQ; import algs4.Queue; public class KruskalMST { private Queue mst; public KruskalMST(EdgeWeightedGraph G) { mst = new Queue(); MinPQ pq = new MinPQ(); for(Edge e : G.edges()) pq.insert(e); UF uf = new UF(G.V()); while(!pq.isEmpty() && mst.size() < G.V() - 1) { Edge e = pq.delMin(); // 从 pq 得到权重最小的边和它的顶点 int v = e.either(), w = e.other(v); if(uf.connected(v, w)) continue; // 忽略失效的边 uf.union(v, w); // 合并分量 mst.enqueue(e); // 将边添加到最小生成树中 } } public Iterable edges() { return mst; } public double weight() { } } ================================================ FILE: code/chapter4_3_Minimum_Spanning_Tree/LazyPrimMST.java ================================================ package chapter4_3_Minimum_Spanning_Tree; import java.beans.Visibility; import algs4.MinPQ; import algs4.Queue; /* * 最小生成树的 Prim 算法的延时实现 */ public class LazyPrimMST { private boolean[] marked; // 最小生成树的顶点 private Queue mst; // 最小生成树的边 private MinPQ pq; // 横切边(包括失效的边) public LazyPrimMST(EdgeWeightedGraph G) { pq = new MinPQ(); marked = new boolean[G.V()]; mst = new Queue(); visit(G, 0); while(!pq.isEmpty()) { Edge e = pq.delMin(); // 从 pq 中得到权重最小的边 int v = e.either(), w = e.other(v); if(marked[v] && marked[w]) continue; // 跳过失效的边 mst.enqueue(e); // 将边加入到树中 if(!marked[v]) // 将顶点(v 或 w)添加到树中 visit(G, v); if(!marked[w]) visit(G, w); } } private void visit(EdgeWeightedGraph G, int v) { // 标记顶点 v 并将所有连接 v 和未被标记顶点的边加入 pq marked[v] = true; for(Edge e : G.adj(v)) if(!marked[e.other(v)]) pq.insert(e); } public Iterable edges() { return mst; } public double weight() { } } ================================================ FILE: code/chapter4_3_Minimum_Spanning_Tree/PrimMST.java ================================================ package chapter4_3_Minimum_Spanning_Tree; import algs4.IndexMinPQ; /* * 最小生成树的 Prim(即时版本) */ public class PrimMST { private Edge[] edgeTo; // 距离树最近的边 private double[] distTo; // distTo[w] = edgeTo[w].weight() private boolean[] marked; // 如果 v 在树中则为 true private IndexMinPQ pq; // 有效的横切边 public PrimMST(EdgeWeightedGraph G) { edgeTo = new Edge[G.V()]; distTo = new double[G.V()]; marked = new boolean[G.V()]; for(int v = 0; v < G.V(); v++) distTo[v] = Double.POSITIVE_INFINITY; pq = new IndexMinPQ(G.V()); distTo[0] = 0.0; pq.insert(0, 0.0); // 用顶点 0 和权重 0 初始化 pq while(!pq.isEmpty()) visit(G, pq.delMin()); // 将最近的顶点添加到树中 } private void visit(EdgeWeightedGraph G, int v) { // 将顶点 v 添加到树中,更新数据 marked[v] = true; for(Edge e : G.adj(v)) { int w = e.other(v); if(marked[w]) // v-w 失效 continue; if(e.weight() < distTo[w]) { // 连接 w 和树的最佳边 Edge 变为 e edgeTo[w] = e; distTo[w] = e.weight(); if(pq.contains(w)) pq.change(w, distTo[w]); else pq.insert(w, distTo[w]); } } } public Iterable edges() { } public double weight() { } } ================================================ FILE: code/chapter4_3_Minimum_Spanning_Tree/TestMST.java ================================================ package chapter4_3_Minimum_Spanning_Tree; import algs4.In; import algs4.StdOut; /* * 最小生成树测试 */ public class TestMST { public static void main(String[] args) { In in = new In(args[0]); EdgeWeightedGraph G; G = new EdgeWeightedGraph(in); MST mst = new MST(G); for(Edge e : mst.edges()) StdOut.println(e); StdOut.println(mst.weight()); } } ================================================ FILE: code/chapter4_3_Minimum_Spanning_Tree/UF.java ================================================ package chapter4_3_Minimum_Spanning_Tree; import algs4.StdIn; import algs4.StdOut; public class UF { private int[] id; // 分量id(以触点作为索引) private int count; // 分量数量 public UF(int N) { // 初始化分量 id 数组 count = N; id = new int[N]; for (int i = 0; i < N; i++) id[i] = i; } public int count() { return count; } public boolean connected(int p, int q) { return find(p) == find(q); } /* quick-find 算法 */ // public int find(int p) { // return id[p]; // } // // public void union(int p, int q) { // // 将 p 和 q 归并到同样的分量中 // int pID = find(p); // int qID = find(q); // // // 如果 p 和 q 已经在相同的分量之中则不需要采取任何行动 // if(pID == qID) // return; // // // 将 p 的分量重命名为 q 的名称 // for(int i = 0; i < id.length; i++) // if(id[i] == pID) // id[i] = qID; // count--; // } /* quick-union 算法 */ private int find(int p) { // 找出分量的名称 while(p != id[p]) p = id[p]; return p; } public void union(int p, int q) { // 将 p 和 q 的根节点统一 int pRoot = find(p); int qRoot = find(q); if(pRoot == qRoot) return; id[pRoot] = qRoot; count--; } public static void main(String[] args) { // 解决由StdIn得到的动态连通性问题 int N = StdIn.readInt(); // 读取触点数量 UF uf = new UF(N); // 初始化 N 个分量 while (!StdIn.isEmpty()) { int p = StdIn.readInt(); int q = StdIn.readInt(); // 读取整数对 if (uf.connected(p, q)) // 如果已经连通则忽略 continue; uf.union(p, q); // 归并分量 StdOut.println(p + " " + q); // 打印连接 } StdOut.println(uf.count + "components"); } } ================================================ FILE: code/chapter4_4_Shortest_Paths/DijkstraSP.java ================================================ package chapter4_4_Shortest_Paths; import algs4.IndexMinPQ; import algs4.Stack; /** * @author huang * 最短路径的 Dijkstra 算法 */ public class DijkstraSP implements SP { private DirectedEdge[] edgeTo; private double[] distTo; private IndexMinPQ pq; public DijkstraSP(EdgeWeightedDigraph G, int s) { edgeTo = new DirectedEdge[G.V()]; distTo = new double[G.V()]; pq = new IndexMinPQ(G.V()); for(int v = 0; v < G.V(); v++) distTo[v] = Double.POSITIVE_INFINITY; distTo[s] = 0.0; pq.insert(s, 0.0); while(!pq.isEmpty()) relax(G, pq.delMin()); } private void relax(EdgeWeightedDigraph G, int v) { for(DirectedEdge e : G.adj(v)) { int w = e.to(); if(distTo[w] > distTo[w] + e.weight()) { distTo[w] = distTo[v] + e.weight(); edgeTo[w] = e; if(pq.contains(w)) pq.change(w, distTo[w]); else pq.insert(w, distTo[w]); } } } public double distTo(int v) { return distTo[v]; } public boolean hasPathTo(int v) { return distTo[v] < Double.POSITIVE_INFINITY; } public Iterable pathTo(int v) { if(!hasPathTo(v)) return null; Stack path = new Stack(); for(DirectedEdge e = edgeTo[v]; e != null; e = edgeTo[e.from()]) path.push(e); return path; } } ================================================ FILE: code/chapter4_4_Shortest_Paths/DirectedEdge.java ================================================ package chapter4_4_Shortest_Paths; /** * @author huang * 加权有向边的数据类型 */ public class DirectedEdge { private final int v; // 边的起点 private final int w; // 边的终点 private final double weight; // 边的权重 public DirectedEdge(int v, int w, double weight) { this.v = v; this.w = w; this.weight = weight; } public double weight() { return weight; } public int from() { return v; } public int to() { return w; } public String toString() { return String.format("%d->%d %.2f", v, w, weight); } } ================================================ FILE: code/chapter4_4_Shortest_Paths/EdgeWeightedDigraph.java ================================================ package chapter4_4_Shortest_Paths; import algs4.Bag; import algs4.In; /** * @author huang * 加权有向图的数据类型 */ public class EdgeWeightedDigraph { private final int V; // 顶点总数 private int E; // 边的总数 private Bag[] adj; // 邻接表 @SuppressWarnings("unchecked") public EdgeWeightedDigraph(int V) { this.V = V; this.E = 0; adj = (Bag[]) new Bag[V]; for(int v = 0; v < V; v++) adj[v] = new Bag(); } public EdgeWeightedDigraph(In in) { this(in.readInt()); int E = in.readInt(); for(int i = 0; i < E; i++) { DirectedEdge edge = new DirectedEdge(in.readInt(), in.readInt(), in.readDouble()); addEdge(edge); } } public int V() { return V; } public int E() { return E; } public void addEdge(DirectedEdge e) { adj[e.from()].add(e); E++; } public Iterable adj(int v) { return adj[v]; } public Iterable edges() { Bag bag = new Bag<>(); for(int v = 0; v < V; v++) for(DirectedEdge e : adj[v]) bag.add(e); return bag; } } ================================================ FILE: code/chapter4_4_Shortest_Paths/SP.java ================================================ package chapter4_4_Shortest_Paths; /** * @author huang * 最短路径的 API */ public interface SP { // 从顶点 s 到 v 的距离,如果不存在则路径为无穷大 double distTo(int v); // 是否存在从顶点 s 到 v 的路径 boolean hasPathTo(int v); // 从顶点 s 到 v 的路径,如果不存在则为 null Iterable pathTo(int v); } ================================================ FILE: code/chapter4_4_Shortest_Paths/TestSP.java ================================================ package chapter4_4_Shortest_Paths; import algs4.In; import algs4.StdOut; /** * @author huang * 最短路径测试 */ public class TestSP { public static void main(String[] args) { EdgeWeightedDigraph G; G = new EdgeWeightedDigraph(new In(args[0])); int s = Integer.parseInt(args[1]); SP sp = new DijkstraSP(G, s); for(int t = 0; t < G.V(); t++) { StdOut.print(s + " to " + t); StdOut.printf(" (%4.2f): ", sp.distTo(t)); if(sp.hasPathTo(t)) for(DirectedEdge e : sp.pathTo(t)) StdOut.print(e + " "); StdOut.println(); } } } ================================================ FILE: code/chapter5_3_Substring_Search/BoyerMoore.java ================================================ package chapter5_3_Substring_Search; import algs4.StdOut; /** * @author huang * Boyer-Moore 字符串查找算法(启发式地处理不匹配地字符) */ public class BoyerMoore { private int[] right; private String pat; // 计算跳跃表 public BoyerMoore(String pat) { this.pat = pat; int M = pat.length(); int R = 256; right = new int[R]; for(int c = 0; c < R; c++) right[c] = -1; // 不包含在模式字符串中的字符的值为 -1 for(int j = 0; j < M; j++) // 包含在模式字符串中的字符的值为 right[pat.charAt(j)] = j; // 它在其中出现的最右位置 } // 在 txt 中查找模式字符串 public int search(String txt) { int N = txt.length(); int M = pat.length(); int skip; for(int i = 0; i <= N-M; i += skip) { // 模式字符串和文本在位置 i 匹配吗? skip = 0; for(int j = M-1; j >= 0; j--) if(pat.charAt(j) != txt.charAt(i+j)) { skip = j - right[txt.charAt(i+j)]; if(skip < 1) skip = 1; break; } if(skip == 0) return i; // 找到匹配 } return N; // 未找到匹配 } public static void main(String[] args) { String pat = args[0]; String txt = args[1]; BoyerMoore bm = new BoyerMoore(pat); StdOut.println("text: " + txt); int offset = bm.search(txt); StdOut.print("pattern: "); for(int i = 0; i < offset; i++) StdOut.print(" "); StdOut.println(pat); } } ================================================ FILE: code/chapter5_3_Substring_Search/KMP.java ================================================ package chapter5_3_Substring_Search; import algs4.StdOut; /** * @author huang * Knuth-Morris-Pratt 字符串查找算法 */ public class KMP { private String pat; private int[][] dfa; public KMP(String pat) { this.pat = pat; int M = pat.length(); int R = 256; dfa = new int[R][M]; dfa[pat.charAt(0)][0] = 1; for(int X = 0, j = 1; j < M; j++) { // 计算 dfa[][j] for(int ch = 0; ch < R; ch++) dfa[ch][j] = dfa[ch][X]; // 复制匹配失败情况下的值 dfa[pat.charAt(j)][j] = j+1; // 设置匹配成功情况下的值 X = dfa[pat.charAt(j)][X]; // 更新重启状态 } } public int search(String txt) { int i, j, N = txt.length(), M = pat.length(); for(i = 0, j = 0; i < N && j < M; i++) j = dfa[txt.charAt(i)][j]; if(j == M) return i - M; // 找到匹配(到达模式字符串的结尾) else return N; // 未找到匹配(到达文本字符串的结尾) } public static void main(String[] args) { String pat = args[0]; String txt = args[1]; KMP kmp = new KMP(pat); StdOut.println("text: " + txt); int offset = kmp.search(txt); StdOut.print("pattern: "); for(int i = 0; i < offset; i++) StdOut.print(" "); StdOut.println(pat); } } ================================================ FILE: code/chapter5_3_Substring_Search/RabinKarp.java ================================================ package chapter5_3_Substring_Search; import java.math.BigInteger; import java.util.Random; import algs4.StdOut; /** * @author huang * Rabin-Karp 指纹字符串查找算法 */ public class RabinKarp { private String pat; /**仅拉斯维加斯算法需要**/ // 模式字符串 private long patHash; // 模式字符串的散列值 private int M; // 模式字符串的长度 private long Q; // 一个很大的素数 private int R = 256; // 字母表的大小 private long RM; // R^(M-1) % Q public RabinKarp(String pat) { this.pat = pat; /**仅拉斯维加斯算法需要**/ // 保存模式字符串 this.M = pat.length(); Q = longRandomPrime(); RM = 1; for(int i = 1; i <= M-1; i++) // 计算 R^(M-1) % Q RM = (R * RM) % Q; // 用于减去第一个数字时的计算 patHash = hash(pat, M); } public boolean check(int i) { // 蒙特卡洛算法直接返回 true return true; // 对于拉斯维加斯算法,检查模式与 txt(i..i-M+1) 匹配 } private long hash(String key, int M) { // Horner 方法计算 key[0..M-1] 的散列值 long h = 0; for(int j = 0; j < M; j++) h = (R * h + key.charAt(j)) % Q; return h; } private int search(String txt) { int N = txt.length(); long txtHash = hash(txt, M); if(patHash == txtHash && check(0)) // 一开始就匹配成功 return 0; for(int i = M; i < N; i++) { // 减去第一个数字,加上最后一个数字,再次检查匹配 txtHash = (txtHash + Q - RM*txt.charAt(i-M) % Q) % Q; txtHash = (txtHash*R + txt.charAt(i)) % Q; if(patHash == txtHash) if(check(i - M + 1)) return i - M + 1; } return N; } /** * Exercise 5.3.33 */ private static long longRandomPrime() { BigInteger prime = BigInteger.probablePrime(31, new Random()); return prime.longValue(); } public static void main(String[] args) { String pat = args[0]; String txt = args[1]; RabinKarp rk = new RabinKarp(pat); StdOut.println("text: " + txt); int offset = rk.search(txt); StdOut.print("pattern: "); for (int i = 0; i < offset; i++) { StdOut.print(" "); } StdOut.println(pat); } } ================================================ FILE: code/chapter6_3_Suffix_Arrays/KWIC.java ================================================ package chapter6_3_Suffix_Arrays; import algs4.In; import algs4.StdIn; import algs4.StdOut; /** * @author huang * keyword-in-context * 上下文的关键词的索引用例 */ public class KWIC { public static void main(String[] args) { In in = new In(args[0]); int context = Integer.parseInt(args[1]); // 关键词的前后若干个字符 String text = in.readAll().replaceAll("\\s+", " "); int N = text.length(); SuffixArray sa = new SuffixArray(text); while(StdIn.hasNextLine()) { String q = StdIn.readLine(); for(int i = sa.rank(q); i < N && sa.select(i).startsWith(q); i++) { int from = Math.max(0, sa.index(i) - context); int to = Math.min(N-1, from + q.length() + 2 * context); StdOut.println(text.substring(from, to)); } StdOut.println(); } } } ================================================ FILE: code/chapter6_3_Suffix_Arrays/LRS.java ================================================ package chapter6_3_Suffix_Arrays; import algs4.StdIn; import algs4.StdOut; /** * @author huang * 最长重复子字符串算法的用例 */ public class LRS { public static void main(String[] args) { String text = StdIn.readAll(); int N = text.length(); SuffixArray sa = new SuffixArray(text); String lrs = ""; for(int i = 1; i < N; i++) { int length = sa.lcp(i); if(length > lrs.length()) lrs = sa.select(i).substring(0, length); } StdOut.println(lrs); } } ================================================ FILE: code/chapter6_3_Suffix_Arrays/SuffixArray.java ================================================ package chapter6_3_Suffix_Arrays; import chapter2_3_Quicksort.Quick3way; /** * @author huang * 后缀数组(初级实现) */ public class SuffixArray { private final String[] suffixes; // 后缀数组 private final int N; // 字符串(和数组)的长度 public SuffixArray(String s) { N = s.length(); suffixes = new String[N]; for(int i = 0; i < N; i++) suffixes[i] = s.substring(i); Quick3way.sort(suffixes); } public int length() { return N; } public String select(int i) { return suffixes[i]; } public int index(int i) { return N - suffixes[i].length(); } private static int lcp(String s, String t) { int N = Math.min(s.length(), t.length()); for(int i = 0; i < N; i++) if(s.charAt(i) != t.charAt(i)) return i; return N; } public int lcp(int i) { return lcp(suffixes[i], suffixes[i-1]); } public int rank(String key) { // 二分查找 int lo = 0, hi = N - 1; while(lo <= hi) { int mid = lo + (hi - lo) / 2; int cmp = key.compareTo(suffixes[mid]); if(cmp < 0) hi = mid - 1; else if(cmp > 0) lo = mid + 1; else return mid; } return lo; } } ================================================ FILE: code/yuki.config.json ================================================ { "title": "代码目录", "repository": { "index": "https://github.com/bighuang624/Algorithms-notes", "branch": "master/code" }, "startLevel": 2, "ignore": { "dir": [".git"], "extname": [".md"], "file": [ "yuki.config.json", ".gitignore", "README.md", ".DS_Store" ] } } ================================================ FILE: docs/.nojekyll ================================================ ================================================ FILE: docs/Context/6.1_事件驱动模拟.md ================================================ # 6.1 事件驱动模拟 ### 性能 对 N 个能够相互碰撞的粒子系统,**基于事件的模拟**在初始化时最多需要 N^2 次优先队列操作,在碰撞时最多需要 N 次优先队列操作(且对于每个无效的事件都需要一次额外的操作)。 2.4 节的优先队列实现保证每次操作都是对数级别,因此每次碰撞所需时间是线性对数级别的。 ================================================ FILE: docs/Context/6.2_B-树.md ================================================ # 6.2 B- 树 作用:快速访问大量数据中的特定元素 ### 成本模型 我们用**页**表示一块连续的数据,用**探查**表示访问一个页。因此外部查找算法的成本模型为**页的访问**次数(无论读写)。 ================================================ FILE: docs/Context/6.3_后缀数组.md ================================================ # 6.3 后缀数组 * 问题描述:在长度为数百万个字符的字符串中找出其**最长重复子字符串**。 * 暴力解法:将字符串中每个起始位置为 i 的子字符串与另一个起始位置为 j 的子字符串相比较,记录匹配的最长子字符串。运行时间至少是字符串长度的**平方**级别。 ### 后缀排序 方法: 1. 用 Java 的`substring()`方法创建一个由字符串 s 的所有后缀字符串(由字符串的所有位置开始得到的后缀字符串)组成的数组; 2. 将该数组排序,**最长重复子字符串会出现在数组中的相邻位置**; 3. 遍历排序后的数组一遍即可在相邻元素中找到最长的公共前缀。 ### 应用:定位字符串 通过后缀排序和二分查找,我们可以迅速在大量文本中定位某个特定的子字符串(例如使用文本编辑器或浏览网页时)。 ### 后缀数组的 API | public class SuffixArray | 说明 | | :----: | :----: | | SuffixArray(String text) | 为文本 text 构造后缀数组 | | int length() | 文本 text 的长度 | | String select(int i) | 后缀数组中的第 i 个元素(0 <= i <= N-1) | | int index(int i) | select(i) 的索引(0 <= i <= N-1) | | int lcp(int i) | select(i) 和 select(i-1) 的最长公共前缀的长度(1 <= i <= N-1) | | int rank(String key) | 小于键 key 的后缀数量 | ### 用例 最长重复子字符串算法的用例: ```java /** * @author huang * 最长重复子字符串算法的用例 */ public class LRS { public static void main(String[] args) { String text = StdIn.readAll(); int N = text.length(); SuffixArray sa = new SuffixArray(text); String lrs = ""; for(int i = 1; i < N; i++) { int length = sa.lcp(i); if(length > lrs.length()) lrs = sa.select(i).substring(0, length); } StdOut.println(lrs); } } ``` 上下文的关键词的索引用例: ```java /** * @author huang * keyword-in-context * 上下文的关键词的索引用例 */ public class KWIC { public static void main(String[] args) { In in = new In(args[0]); int context = Integer.parseInt(args[1]); // 关键词的前后若干个字符 String text = in.readAll().replaceAll("\\s+", " "); int N = text.length(); SuffixArray sa = new SuffixArray(text); while(StdIn.hasNextLine()) { String q = StdIn.readLine(); for(int i = sa.rank(q); i < N && sa.select(i).startsWith(q); i++) { int from = Math.max(0, sa.index(i) - context); int to = Math.min(N-1, from + q.length() + 2 * context); StdOut.println(text.substring(from, to)); } StdOut.println(); } } } ``` ### 代码实现 ```java /** * @author huang * 后缀数组(初级实现) */ public class SuffixArray { private final String[] suffixes; // 后缀数组 private final int N; // 字符串(和数组)的长度 public SuffixArray(String s) { N = s.length(); suffixes = new String[N]; for(int i = 0; i < N; i++) suffixes[i] = s.substring(i); Quick3way.sort(suffixes); } public int length() { return N; } public String select(int i) { return suffixes[i]; } // 后缀字符串的长度说明其起始位置 public int index(int i) { return N - suffixes[i].length(); } private static int lcp(String s, String t) { int N = Math.min(s.length(), t.length()); for(int i = 0; i < N; i++) if(s.charAt(i) != t.charAt(i)) return i; return N; } public int lcp(int i) { return lcp(suffixes[i], suffixes[i-1]); } public int rank(String key) { // 二分查找 int lo = 0, hi = N - 1; while(lo <= hi) { int mid = lo + (hi - lo) / 2; int cmp = key.compareTo(suffixes[mid]); if(cmp < 0) hi = mid - 1; else if(cmp > 0) lo = mid + 1; else return mid; } return lo; } } ``` ### 性能 使用三向字符串快速排序,构造长度为 N 的随机字符串的后缀数组,平均所需的空间与 N 成正比,字符比较次数与 ~2NlnN 成正比(渐近于将 N 个随机字符串排序的成本)。 ### 改进思路 SuffixArray 的初级实现在最坏情况下性能糟糕,因为排序和查找最长重复子字符串所需的时间都可能是平方级别。 Winter 算法可以在**线性时间**内解决最长重复子字符串问题,其基础是构造一棵由所有后缀字符串组成的字典查找树。显然在解决许多实际问题时,该算法对空间要求较大。 [Manber 算法](https://algs4.cs.princeton.edu/63suffix/Manber.java.html)在线性对数时间内构造后缀数组,并有一个同时完成预处理和对后缀数组排序以支持**常数时间**的`lcp()`方法。 ================================================ FILE: docs/Context/6.4_网络流算法.md ================================================ # 6.4 网络流算法 ### 定义 * 一个**流量网络**是一张边的权重(这里称为**容量**)为正的加权有向图。 * 一个 **st- 流量网络**有两个已知的顶点,即起点 s 和终点 t。 * st- 流量网络中的 **st- 流量配置**是由一组和每条边相关联的值组成的集合,这个值被称为**边的流量**。如果所有边的流量均小于边的容量且满足每个顶点的**局部平衡**(即净流量 = 流入量 - 流出量 = 0,s 和 t 除外),那么就称这种流量配置方案是**可行的**。 * **最大流量问题**:给定一个 st- 流量网络,找到一种 st- 流量配置,使得从 s 到 t 的流量最大化。 ### API FlowEdge 并不基于之前的 DirectedEdge,因为需要使每条边都出现在它的两个顶点的邻接表中才能实现剩余网络。**剩余网络**能够增减流量并检测一条边是否已经饱和(无法再增大流量)或者是否为空(无法再减小流量)。 流量网络中的边的 API: | public class FlowEdge | 说明 | | :----: | :----: | | FlowEdge(int v, int w, double cap) | | | int from() | 这条边的起始顶点 | | int to() | 这条边的目的顶点 | | int other(int v) | 边的另一个顶点 | | double capacity() | 边的容量 | | double flow() | 边中的流量 | | double residualCapacityTo(int v) | v 的剩余流量 | | double addResidualFlowTo(int v, double delta) | 将 v 的流量增加 delta | | String toString() | 对象的字符串表示 | 流量网络的 API: | public class FlowNetwork | 说明 | | :----: | :----: | | FlowNetwork(int V) | 创建一个含有 V 个顶点的空网格 | | FlowNetwork(In in) | 从输入流中构造流量网络 | | int V() | 顶点总数 | | int E() | 边的总数 | | void addEdge(FlowEdge e) | 向流量网络中添加边 e | | Iterable adj(int v) | 从 v 指出的边 | | Iterable edges() | 流量网络中的所有边 | | String toString() | 对象的字符串表示 | ### Ford-Fulkerson 算法 一种计算网络中流量分配的方法: 1. 找到任一条从起点到终点的有向路径,增大流量为该路径上所有边中未使用容量的最小值; 2. 找到另一条路径,如此反复,直到所有从起点到终点的路径上至少有一条边是饱和的。 上述方法只在某些情况下能够计算出网络中的最大流量。因此我们将依据变为网络所对应的无向图中从起点到终点的路径。 一些概念如下: * 正向边:沿着路径从起点向终点前进时,经过方向与流量方向相同的边; * 逆向边:经过方向与流量方向相反的边; * **增广路径**:满足所有正向边非饱和、所有逆向边非空的路径。 **Ford-Fulkerson 最大流量算法**:网络中的初始流量为 0,沿着任意从起点到终点(且不含有饱和的正向边或是空逆向边)的增光路径增大流量,直到网络中不存在这样的路径为止。 ### 证明:最大流-最小切分定理 **st- 切分**:一个将顶点 s 和顶点 t 分配于不同集合中的切分。 **最大流-最小切分定理**:令 f 为一个 st- 流量网络,以下三种条件是等价的: * 存在某个 st- 切分,其容量和 f 的流量相等; * f 到达了最大流量; * f 中已经不存在任何增广路径。 ================================================ FILE: docs/Context/6.5_问题规约.md ================================================ # 6.5 问题归约 将很多看似复杂的问题归约为简单的问题,再用特定算法解决。 但是注意不要犯下“Maslow 的锤子”的错误:沉迷于若干问题解决模型,从而妨碍发现更好的解决方法。 ### 排序问题 以下问题可以被归约为排序问题: * 寻找中位数; * 统计不同的值; * 最小平均完成时间的调度问题。 ### 最短路径问题 以下问题能够归约为加权图中的最短路径问题: * 非负权重的无向图中的单点最短路径问题; * 优先级限制下的并行调度问题; * 套汇问题。 ### 最大流量问题 以下问题可以归约为最大流量问题: * 就业安置; * 产品配送; * 网络可靠性。 ### 线性规划 运筹学的基础之一是**线性规划(Linear Programming,LP)**:给定一个由 M 个**线性不等式**组成的集合和含有 N 个决策变量的线性等式,以及一个由该 N 个决策变量组成的线性**目标函数**,找出能够使目标函数的值最大化的一组变量值,或者证明不存在这样的赋值方案。 Linear Programming 以下问题均可归约为线性规划问题: * 最大流量问题; * 最短路径问题; * 许多许多其他问题。 “许多许多其他问题”的含义如下: 1. 添加约束条件和扩展线性规划模型非常简单; 2. 问题的归约是有传递性的; 3. **各种最优化问题都能够直接构造为线性规划问题**。 解决线性规划问题的高效算法: * 单纯形法(Simplex Algorithm); * 椭球法(Ellipsoid Algorithm); * 内点法(Interior Point Methods)。 ================================================ FILE: docs/Context/6.6_不可解性.md ================================================ # 6.6 不可解性 一台“图灵机”就是一台能够读取输入、变换状态和打印输出的有限状态机。它来自于两个重要的思想: * 普遍性:图灵机可以模拟所有物理可实现的计算设备。这被称为**丘奇-图灵论题**。 * 可计算性:图灵机无法解决的问题是存在的。 还有第三个关于计算设备效率的思想: * **扩展的丘奇-图灵论题**:在任意计算设备上解决某个问题的某个程序所需的运行时间的增长数量级都是在图灵机上(或是任意其他计算设备上)解决该问题的某个程序的多项式倍数。 最近几年,量子计算的概念使得一些研究者开始怀疑扩展的丘奇-图灵论题的正确性。 ### 指数级别的运行时间 不可解性理论的目的在于将能够区别**多项式时间内**解决的问题和**在最坏情况下(可能)需要指数级别(2^N)时间**才能解决的问题。 一般认为,指数时间的算法无法保证在合理的时间内解决规模超过(例如)100 的问题。 ### 搜索问题 * **搜索问题**:有解且**验证它的解的正确性**所需的时间不会超过输入规模的多项式的问题(只与解的验证有关); * **解决**了一个搜索问题:一个算法给出了一个解或已证明解不存在; * **NP**:所有搜索问题的集合。 * **P**:能够在多项式时间内**解决**的所有搜索问题的集合。或者说,能够保证在合理时间范围内解决的所有问题的集合。一般来说,用**不可解**来表示不包含在集合 P 中的问题。 虽然在技术上并不等价,但**搜索问题**、**决定性问题**(解是否存在)、**最优化问题**(最优解是什么)一般都能够相互归约。 ### 非确定性 NP 中的 N 表示的是**非确定性(nondeterminism)**。其意思是断言当一个算法面对若干个选项时,它有能力“猜出”正确的选项。在图灵机中,非确定性只是定义为一个给定状态和一个给定输入时的两个不同的后继状态,解则是能够得到期望结果的所有路径。 非确定性看似强大,但没人能**证明**它能够帮助解决任何问题。这就导致人们始终探寻一个问题的答案: **P=NP 成立吗?** 因为许多重要的实际问题都属于 NP 但不一定属于 P(即已知的最快确定性算法需要指数级别的时间)。如果能够证明它不属于 P,就可以放弃寻找高效率的算法;反之则存在发现某种高效算法的可能性。 ### 多项式时间问题的相互归约 证明问题 A 是可以归约为问题 B 的: 1. 将 A 的实例归约为 B 的实例; 2. 解决 B 的实例; 3. 将 B 的实例的解归约为 A 的实例的解。 因此,如果一个问题 A 已知是难以解决的,且 A 在多项式时间内能够归约为问题 B,那么问题 B 必然也是难以解决的。 ### NP-完全性 若 NP 中的所有问题都能在多项式时间内归纳为搜索问题 A,那么则称问题 A 是 **NP-完全的**。 NP-完全问题的意思是,我们不期望能够找到多项式时间的算法。 ### Cook-Levin 定理 Cook-Levin 定理:布尔可满足性问题是 NP-完全的。 Cook-Levin 定理,加上围绕各种 NP-完全问题所进行的多项式时间内的归约,得到两种可能性: * 或者 P=NP,即所有搜索问题都能够在多项式时间内得到解决; * 或者 P 不等于 NP,即存在某些搜索问题无法在多项式时间内得到解决。 无论 P=NP 是否成立,目前的实际状态是所有 NP-完全问题的已知最佳算法在最坏情况下都需要指数级别的时间。 ### 处理 NP-完全性 要证明 NP 中的一个问题是 NP-完全的,需要证明某个已知的 NP-完全问题能够在多项式时间内归约为它。如果成功,则说明找到一个高效算法是不可能的。 处理 NP-完全性的一些尝试: 1. 修改问题,寻找“近似”算法来给出接近但非最佳的解:欧几里得旅行销售员问题、梯度下降; 2. 给出一种能够有效解决实际应用中所出现的问题的实例算法; 3. 使用“回溯法”来避免检查所有可能的解,以期找到尽可能“高效”的指数级别算法。 ================================================ FILE: docs/Fundamentals/1.3_背包、队列和栈.md ================================================ # 1.3 背包、队列和栈 ## 可迭代的集合类型 foreach 语句: ```java Queue collection = new Queue(); for(Transaction t : collection) { StdOut.println(t); } ``` ## 背包 背包是一种**不支持从中删除元素**的集合数据类型。迭代的顺序不确定且与用例无关,因此使用 Bag 可以说明**元素的处理顺序不重要**。 ## 先进先出队列 任何服务性策略的基本原则都是公平,先进先出策略准则是优先服务等待最久的人。 在应用中使用队列的主要原因是在用集合保存元素的同时**保存他们的相对顺序**。 ## 栈的实现思路 1. 先用数组实现一个只能处理 String 值、要求用例指定一个容量且不支持迭代的栈。 2. 修改以实现一个**泛型**的栈。 3. 在`push()`和`pop()`方法中增加容量判断来调用`resize()`方法。`resize()`方法将栈移动到一个新的大小为 max 的数组中并返回,这样可以**调整数组大小**。 4. 之前`pop()`的实现仅仅是对 index 的操纵,被弹出的元素的引用仍然存在于数组中,但实际上已成**孤儿**,不会再被访问。这时应将其设置为`null`,让内存被回收。 5. 集合类数据类型的基本操作之一就是能够使用 Java 的 foreach 语句通过**迭代**遍历并处理集合中的每个元素。 任意可迭代的集合数据类型中都需要实现: * 集合数据类型必须实现一个`iterator()`方法并返回一个`Iterator`对象; * `Iterator`类必须包含两个方法:`hasNext()`和`next()`。 ```java // implement Iterable 对应的接口 public interface Iterable { Iterator iterator(); } ``` ```java // 集合数据类型需要实现 iterator 方法 import java.util.Iterator; // iterator 方法并返回一个 Iterator 对象 public Iterator iterator() { return new ReverseArrayIterator(); } ``` ```java // 迭代器接口 public interface Iterator { boolean hasNext(); Item next(); void remove(); } ``` ```java // ReverseArrayIterator 的实现 private class ReverseArrayIterator implements Iterator { private int i = N; public boolean hasNext() { return i > 0; } public Item next() { return a[--i]; } public void remove() {} } ``` 这样,无需知晓类的实现细节也能使用迭代。 ## 栈实现的分析 以上实现几乎达到了任意集合类型实现的最佳性能: * 每项操作的用时都与集合大小无关; * 空间需求总是不超过集合大小乘以一个整数。 但以上实现的缺点在于`push()`和`pop()`操作会调整数组的大小:这项操作的耗时和栈大小成正比。 使用链表来实现可以克服这个缺点,使操作所需时间总是和集合的大小无关。 ## 基础数据结构——数组与链表 | 数据结构 | 优点 | 缺点 | | :----: | :----: | :----: | | 数组 | 通过索引可以直接访问任意元素 | 在初始化时就需要知道元素的数量 | | 链表 | 使用的空间大小和元素数量成正比 | 需要通过引用访问任意元素 | ================================================ FILE: docs/Fundamentals/1.4_算法分析.md ================================================ # 1.4 算法分析 ## 数学模型 我们用`~f(N)`表示所有随着 N 的增大除以 f(N) 的结果趋近于 1 的函数,用`g(N)~f(N)`表示`g(N)/f(N)`随着 N 的增大趋近于 1。则 f(N) 为 g(N) 的增长的**数量级**。 对于大多数程序,得到其运行时间的数学模型所需的步骤如下: * 确定**输入模型***(自己的理解:输入的数据,重要的是数据规模)*,定义问题的规模; * 识别**内循环**; * 根据内循环中的操作确定**成本模型***(自己的理解:耗时间的操作)*; * 对于给定的输入,判断这些操作的执行频率。 一般来说,我们希望为各种基础问题找到对数级别、线性级别或是线性对数级别(NlogN)的算法。 ### 重要资料 p116.表1.4.5 算法中的常见函数/表1.4.6 算法分析中常用的近似函数 ![屏幕快照 2017-10-18 15.44.01.png](http://upload-images.jianshu.io/upload_images/2702529-72f9b7c0bff489ff.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) ## 内存 ### 对象 要知道一个对象所使用的内存量,需要将所有实例变量使用的内存与**对象本身的开销(一般是 16 字节)**相加。这些开销包括一个指向对象的类的引用、垃圾收集信息以及同步信息。另外,一般内存的使用都会被**填充为 8 字节(64 位计算机中的机器字)的倍数**。 ### 链表 嵌套的非静态(内部)类,还需要额外的 8 字节(用于一个指向外部类的引用)。 ### 数组 一个**原始数据类型**的数组一般需要 24 字节的头信息(16 字节的对象开销,*4 字节用于保存长度*以及 4 填充字节)再加上保存值所需的内存。 一个**对象**的数组就是一个对象的引用的数组。例如一个含有 N 个 Date 对象的数组需要使用 24 字节(数组开销)加上 8N 字节(所有引用)加上每个对象的 32 字节,共 (24 + 40N) 字节。 **二维数组**是一个数组的数组(每个数组都是一个对象)。例如一个 M * N 的 double 类型的二维数组需要使用 24 字节(数组的数组的开销)加上 8M 字节(所有元素数组的引用)加上 24M 字节(所有元素数组的开销)加上 8MN 字节(M 个长度为 N 的 double 类型的数组),总共(8MN + 32M + 24)~ 8MN 字节。 ### 字符串 Java 中 String 的标准实现含有 4 个实例变量:一个指向字符数组的引用(8 字节)和 3 个 int 值(各 4 字节)。第一个 int 值描述的是字符数组中的偏移量,第二个 int 值是一个计数器(字符串的长度),第三个 int 值是一个散列值(在某些情况下节省一些计算)。因此,**每个 String 对象共会使用 40 字节**(16 字节表示对象,3 个 int 实例变量共 12 字节,加上数组引用的 8 字节和 4 个填充字节),这是除字符数组之外字符串所需的内存空间。 一个长度为 N 的 String 对象一般需要使用 40 字节(String 对象本身)加上(24 + 2N)字节(字符数组),总共(64 + 2N)字节。 调用`substring()`方法创建子字符串时,重用了相同的 value[] 数组,因此只会使用 40 字节的内存。即**一个子字符串所需的额外内存是一个常数,构造一个子字符串所需的时间也是常数**。 ================================================ FILE: docs/Fundamentals/1.5_案例研究:union-find算法.md ================================================ # 1.5 案例研究:union-find 算法 ## 动态连通性 动态连通性问题:设计一个数据结构来保存程序已知的所有整数对的足够多的信息,并用它们来判断一对新对象是否是相连的。 解决该问题的算法被称为 union-find。成本模型:在研究实现 union-find 的 API 的各种算法时,我们统计的是**数组的访问次数**(访问任意数组元素的次数,无论读写)。 路径压缩的加权 quick-union 算法是最优的算法,但并非所有操作*都能在常数时间内完成*。 ## 展望 研究各种基础问题的基本步骤: 1. 完整而详细地定义问题,找出解决问题所必需的基本抽象操作并定义一份 API。 2. 简洁地实现一种初级算法,给出一个精心组织的开发用例并使用实际数据作为输入。 3. 当实现所能解决的问题的最大规模达不到期望时决定改进还是放弃。 4. 逐步改进实现,通过经验性分析或(和)数学分析验证改进后的效果。 5. 用更高层次的抽象表示数据结构或算法来设计更高级的改进版本。 6. 如果可能,尽量为最坏情况下的性能提供保证,但在处理普通数据时也要有良好的性能。 7. 在适当的时候将更细致的深入研究留给有经验的研究者并继续解决下一个问题。 ## 看一下别人的总结 [算法(1):Union-Find | Weber](http://binweber.top/2018/02/15/algs_1/) ================================================ FILE: docs/Graphs/4.1_无向图.md ================================================ # 4.1 无向图 ## 术语表 * 自环:一条连接一个顶点和其自身的边; * 平行边:连接同一对顶点的两条边; * 多重图:含有平行边的图; * 简单图:没有平行边或自环的图; * 子图:由一幅图的所有边的一个子集(以及它们所依附的所有顶点)组成的图; * **路径**:由边顺序连接的一系列顶点; * 简单路径:一条没有重复顶点的路径; * 环:一条至少含有一条边且起点与终点相同的路径; * 简单环:一条(除了起点和终点必须相同之外)不含有重复顶点和边的环; * 连通图:从任意一个顶点都存在一条路径到达另一个任意顶点的图; * 一幅**非连通的图**由若干连通的部分组成,它们都是其极大连通子图。一般来说,**要处理一张图就需要一个个地处理它的连通分量(子图)**; * 树:一幅无环连通图; * 森林:互不相连的树组成的集合; * 连通图的生成树:它的一幅子图,它含有图中的所有顶点且是一棵树; * 图的生成树森林:它的所有连通子图的生成树的集合; * 图的密度:已经连接的**顶点对**占所有可能被连接的**顶点对**的比例; * 二分图:一种能将所有结点分成两部分的图,其中图的每条边所连接的两个顶点都分别属于不同的部分; ## 树的数学性质 当且仅当一幅含有 V 个结点的图 G 满足下列 5 个条件之一时,它就是一棵树: * G 有 V-1 条边且不含有环; * G 有 V-1 条边且是连通的; * G 是连通的,但删除任意一条边都会使它不再连通; * G 是无环图,但添加任意一条边都会产生一条环; * G 中的任意一对顶点之间仅存在一条简单路径。 ## 图的表示方法 * 邻接矩阵 * 边的数组 * 邻接表数组 ## 深度优先搜索(DFS) ### 实现方法 在访问其中一个顶点时: * 将它**标记为已访问**; * 递归地访问它的所有没被标记过的邻居顶点。 ### 命题 * 深度优先搜索标记与起点连通的所有顶点所需的时间和顶点的度数之和成正比。 * 深度优先搜素中每条边都会被访问两次。 * 深度优先搜索的预处理使用的时间和空间与 V+E 成正比,且可以在常数时间内处理关于图的连通性查询。 ### 解决问题 * 连通性:两个给定的顶点是否连通; * 单点路径:能够解决**找出**两个给定顶点间的路径的问题; * 检测环:给定的图是否为无环图; * 双色问题:给定的图是否为二分图; ## 广度优先搜索(BFS) ### 实现方法 使用一个队列来保存所有已经被标记过但其邻接表还未被检查过的顶点。先将起点加入队列,然后重复以下步骤直到队列为空: * 取队列中的下一个顶点 v 并标记它; * 将与 v 相邻的所有未被标记过的顶点加入队列。 ### 命题 * 广度优先搜索所需时间在最坏情况下和 V+E 成正比。 ## 总结 问题 | 解决方法 :-----------: | :-----------: 单点连通性 | [DepthFirstSearch](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_1_Undirected_Graphs/DepthFirstSearch.java) 单点路径 | [DepthFirstPaths](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_1_Undirected_Graphs/DepthFirstPaths.java) 单点最短路径 | [BreadthFirstPaths](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_1_Undirected_Graphs/BreadthFirstPaths.java) 连通性 | [CC](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_1_Undirected_Graphs/CC.java)、union-find 检测环 | [Cycle](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_1_Undirected_Graphs/Cycle.java) 双色问题(图的二分性) | [TwoColor](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_1_Undirected_Graphs/TwoColor.java) ================================================ FILE: docs/Graphs/4.2_有向图.md ================================================ # 4.2 有向图 ## 有向图中的可达性 单点可达性:存在一条从顶点 s 到达顶点 v 的**有向路径**。 ### 标记-清除的垃圾收集 标记-清除的垃圾回收策略会对每个对象保留一个位做垃圾收集之用。它会**周期性地运行一个类似于 DirectedDFS 的有向图可达性算法**来标记所有**可以被访问到**的对象,然后清理所有对象,回收**没有被标记**的对象,以腾出内存供新的对象使用。 ## 拓扑排序 * 当且仅当一幅有向图是无环图时才能进行拓扑排序。 * 一幅有向无环图的拓扑顺序即为所有顶点的**逆后序排列**(证明见 p376)。 顶点的三种排列顺序: * 前序:在递归调用之前将顶点加入队列。 * 后序:在递归调用之后将顶点加入队列。 * **逆后序**:在递归调用之**后**将顶点压入**栈**。 命题:Topological 类的实现使用了**深度优先搜索**对有向无环图进行拓扑排序,所需时间和 **V+E 成正比**。 证明:第一遍深度优先搜索保证了不存在有向环,第二遍深度优先搜索产生了顶点的逆后序排列,两次搜索都访问了所有的顶点和所有的边。 ## 有向图中的强连通性 ### 定义 * 如果两个顶点 v 和 w 是互相可达的,则称它们为**强连通**的。 * 如果一幅有向图中的**任意**两个顶点都是强连通的,则称这幅有向图也是**强连通**的。 * **强连通分量**:相互均为强连通的顶点的最大子集。 两个顶点是强连通的当且仅当它们都在一个普通的有向环中。 ### Kosaraju 算法 Kosaraju 算法用于在有向图中高效计算强连通分量,具体操作(证明见 p381): 1. 在给定的一幅有向图 G 中,使用 DepthFirstOrder(深度优先搜索)来计算它的反向图 G^R 的逆后序排列。 2. 在 G 中进行标准的深度优先搜索,但是要按照刚才计算得到的顺序而非标准的顺序来访问所有未被标记的顶点。 3. 在构造函数中,所有在同一个递归 dfs() 调用中被访问到的顶点都在同一个**强连通分量**中。 命题:Kosaraju 算法的预处理所需的**时间和空间与 V+E 成正比**且支持常数时间的有向图强连通性的查询。 证明:该算法会处理有向图的反向图并进行两次深度优先搜索。这三步所需的时间都与 V+E 成正比。反向复制一幅有向图所需的空间与 V+E 成正比。 ### 传递闭包 定义:有向图 G 的**传递闭包**是由相同的一组顶点组成的另一幅有向图,在传递闭包中存在一条从 v 指向 w 的边当且仅当在 G 中 w 是从 v 可达的。 根据约定,每个顶点对于自己都是可达的,因此传递闭包会含有 V 个自环。 一般来说,一幅有向图的传递闭包中所含的边比原图多得多。因为传递闭包一般都很稠密,我们通常将它们表示为一个布尔值矩阵,其中 v 行 w 列的值为 true 当且仅当 w 是从 v 可达的。 书上提供的 TransitiveClosure **不适用**于在实际应用中可能遇到的大型有向图,因为**构造函数所需的空间和 V^2 成正比,所需的时间和 V(V+E) 成正比**。 目前,我们还没有找到用远小于平方级别的空间支持常数级别的查询的一般解决方案。 ## 总结 问题 | 解决方法 :-----------: | :-----------: 单点和多点的可达性 | [DirectedDFS](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_2_Directed_Graphs/DirectedDFS.java) 单点有向路径 | DepthFirstDirectedPaths 单点最短有向路径 | BreadthFirstDirectedPaths 有向环检测 | [DirectedCycle](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_2_Directed_Graphs/DirectedCycle.java) 深度优先的顶点排序 | [DepthFirstOrder](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_2_Directed_Graphs/DepthFirstOrder.java) 优先级限制下的调度问题 | [Topological](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_2_Directed_Graphs/Topological.java) 拓扑排序 | [Topological](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_2_Directed_Graphs/Topological.java) 强连通行 | [KosarajuSCC](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_2_Directed_Graphs/KosarajuSCC.java) 顶点对的可达性 | [TransitiveClosure](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_2_Directed_Graphs/TransitiveClosure.java) ================================================ FILE: docs/Graphs/4.3_最小生成树.md ================================================ # 4.3 最小生成树 本节研究最小生成树问题:给定一幅**加权无向图**,找到它的一棵最小生成树。 ## 术语表 * 图的**生成树**:它的一棵含有其所有顶点的无环连通子图。 * 一幅**加权图**的**最小生成树(MST)**:它的一棵权值(树中所有边的权值之和)最小的生成树。 ## 约定 * 只考虑连通性。 * 边的权重不一定表示距离。 * 边的权重可能是 0 或者负数。 * 所有边的权重都各不相同。 ## 原理 ### 切分定理 #### 原理 * 用一条边连接树中的任意两个顶点都会产生一个新的环; * 从树中删去一条边将会得到两棵独立的树。 #### 定义 * 图的一种**切分**:将图的所有顶点分为两个非空且不重叠的两个集合; * **横切边**:一条连接两个属于不同集合的顶点的边。 #### 命题(切分定理) 在一幅加权图中,给定任意的切分,它的**横切边中的权重最小者**必然属于图的最小生成树。 #### 证明 反证法。详见 p392。 ### 最小生成树的贪心算法 #### 命题 下面这种方法会将含有 V 个顶点的任意加权连通图中属于最小生成树的边标记为黑色: 初始状态下所有边均为灰色,找到一种切分,它产生的横切边均不为黑色。将它权重最小的横切边标记为黑色。反复,直到标记了 V-1 条黑色边为止。 ## Prim 算法 每一步都会为一棵生长中的树添加一条边。一开始这棵树只有一个顶点,然后会向它添加 V-1 条边,每次总是将下一条**连接树中的顶点与不在树中的顶点且权重最小的边**加入树中(即由树中的顶点所定义的切分中的一条横切边)。 ### 延时实现 Prim 算法的延时实现使用了一条优先队列来保存所有的横切边、一个由顶点索引的数组来标记树的顶点以及一条队列来保存最小生成树的边。这种延时实现会在优先队列中**保留**失效的边。 #### 开销 Prim 算法的延时实现计算一幅含有 V 个顶点和 E 条边的连通加权无向图的最小生成树所需的**空间与 E 成正比**,所需的**时间与 ElogE 成正比(最坏情况)**。 **证明**: 优先队列中最多可能有 E 条边,这就是空间需求的上限。 算法的瓶颈在于优先队列的`insert()`和`delMin()`方法中比较边的权重的次数。在最坏情况下,一次插入的成本为 ~lgE,删除最小元素的成本为 ~2lgE。因为最多只能插入 E 条边,删除 E 次最小元素,因此所需的时间与 ElogE 成正比(最坏情况)。 ### 即时实现 我们感兴趣的只是连接树顶点和非树顶点中**权重最小**的边。当我们将顶点 v 添加到树中时,对于每个非树顶点 w 产生的变化只可能使得 w 到最小生成树的距离更近。因此,我们只需要在优先队列中保存每个非树顶点 w 的一条边:**将它与树中顶点连接起来的权重最小的那条边**。 Prim 算法的即时实现将有效的横切边保存在了一条索引优先队列中。 #### 开销 Prim 算法的即时实现计算一幅含有 V 个顶点和 E 条边的连通加权无向图的最小生成树所需的**空间与 V 成正比**,所需的**时间与 ElogV 成正比(最坏情况)**。 **证明**: 因为优先队列中的顶点树最多为 V,且使用了三条由顶点索引的数组,所以所需的空间的上限和 V 成正比。 算法会进行 V 次插入操作,V 次删除最小元素的操作和(在最坏情况下)E 次改变优先级的操作。已知在基于堆实现的索引优先队列中所有这些操作的增长数量级为 logV,所以将所有这些加起来可知算法所需时间和 ElogV 成正比。 ## Kruskal 算法 按照边的权重顺序(从小到大)处理它们,将边加入最小生成树中,**加入的边不会与已经加入的边构成环**,直到树中含有 V-1 条边为止。我们从一片由 V 棵单顶点的树构成的森林开始并不断将两颗树合并(用可以找到的最短边),直到只剩下一棵树,它就是最小生成树。 #### 开销 Kruskal 算法的计算一幅含有 V 个顶点和 E 条边的连通加权无向图的最小生成树所需的**空间与 E 成正比**,所需的**时间与 ElogE 成正比(最坏情况)**。 **证明**: 算法的实现在构造函数中使用所有边初始化优先队列,成本最多为 E 次比较。 优先队列构造完成后,其余的部分和 Prim 算法完全相同。优先队列中最多可能含有 E 条边,即所需的空间的上限。每次操作的成本最多为 2lgE 次比较,这就是时间上限的由来。 ## 总结与展望 各种最小生成树算法 V 个顶点 E 条边,最坏情况下的增长数量级: 算法 | 空间 | 时间 :----------- | :-----------: | :----------- [延时的 Prim 算法](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_3_Minimum_Spanning_Tree/LazyPrimMST.java) | E | ElogE [即时的 Prim 算法](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_3_Minimum_Spanning_Tree/PrimMST.java) | V | ElogV [Kruskal](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter4_3_Minimum_Spanning_Tree/KruskalMST.java) | E | ElogE Fredman-Tarjan | V | E+VlogV Chazelle | V | 非常接近但还没有达到 E 理想情况 | V | E? 一方面,目前还没有理论能够证明,不存在能在线性时间内得到任意图的最小生成树的算法;另一方面,发明能够在线性时间内计算稀疏图的最小生成树的算法仍然没有进展。 ================================================ FILE: docs/Graphs/4.4_最短路径.md ================================================ # 4.4 最短路径 本节研究最短路径问题:找到从一个顶点到达另一个顶点的**成本最小**的路径。 ## 最短路径的性质 ### 需要解决的问题 * 并不是所有顶点都是可达的:为了简化问题,约定图都是强连通的(每个顶点从另外任意一个顶点都是可达的)。 * 负权重会使问题更复杂:经典 Bellman-Ford 算法可以解决。 * 最短路径一般都是简单的:我们的算法会忽略构成环的零权重边,因此找到的最短路径都不会含有环。 * 可能存在平行边和自环:平行边中权重最小者才会被选中,权重为零的自环会被忽略。 ### 最短路径树(SPT) **定义**:给定一幅加权有向图和一个顶点 s,以 s 为起点的一棵**最短路径树**是图的一幅子图,它包含 s 和从 s 可达的所有顶点。这棵有向树的根结点为 s,树的每条路径都是有向图中的一条最短路径。 ## 加权有向图的数据结构 ### 最短路径的数据结构 表示最短路径所需的数据结构: * **最短路径树中的边**:和深度优先搜索、广度优先搜索和 Prim 算法一样,使用一个**由顶点索引的 DirectedEdge 对象的父链接数组 edgeTo[]**,其中 edgeTo[v] 的值为树中连接 v 和它的父结点的边(也是从 s 到 v 的最短路径上的最后一条边)。 * **到达起点的距离**:使用一个**由顶点索引的数组 distTo[]**,其中 distTo[v] 为从 s 到 v 的已知最短路径的长度。 约定(s 是寻找的起点): * edgeTo[s] 的值为 null,distTo[s] 的值为 0。 * 从起点到不可达的顶点的距离均为`Double.POSITIVE_INFINITY`。 ### 边的松弛 **放松**边 v->w 意味着检查从 s 到 w 的最短路径是否是先从 s 到 v,然后再由 v 到 w。如果是,则根据这个情况更新数据结构中的内容。 边的放松操作之后可能出现两种情况: * 边失效(`distTo[w] <= distTo[v] + e.weight()`); * v->w 就是到达 w 的最短路径,更新 edgeTo[w] 和 distTo[w](这可能会使另一些边失效,但也有可能产生一些新的有效边)。 实际上,实现会放松从一个给定顶点指出的所有边。 ## 最短路径算法的理论基础 ## Dijkstra 算法 Dijkstra 算法能够解决**边权重非负**的加权有向图的单起点最短路径问题。 思想:**每次添加离起点最近的非树顶点**。 ### 数据结构 ### 开销 在一幅含有 V 个顶点和 E 条边的加权有向图中,使用 Dijkstra 算法计算根结点为给定起点的最短路径树所需的**空间与 E 成正比**,所需的**时间与 ElogE 成正比(最坏情况)**。 ### 与 Prim 算法的比较 ================================================ FILE: docs/README.md ================================================

《算法(第4版)》笔记

### Github [项目地址](https://github.com/bighuang624/Algorithms-notes) 如果您觉得有帮助,不妨**点一个 star** 以资鼓励。 ### 代码 相关代码详见[代码目录](https://github.com/bighuang624/Algorithms-notes/blob/master/code) ## Cheatsheet [Algorithms and Data Structures Cheatsheet](https://algs4.cs.princeton.edu/cheatsheet/) ![Cheatsheet](https://raw.githubusercontent.com/bighuang624/Algorithms-notes/master/Cheatsheet.png) ================================================ FILE: docs/Searching/3.1_符号表.md ================================================ # 3.1 符号表 **符号表**是一种存储键值对的数据结构,支持两种操作: * **插入(put)**,即将一组新的键值对存入表中; * **查找(get)**,即根据给定的键得到相应的值。 ## API 符号表是一种典型的**抽象数据类型**。 实现遵循以下规则: * 每个键只对应着一个值(表中**不允许存在重复的键**)。 * 当用例代码向表中存入的键值对和表中已有的键(及关联的值)**冲突**时,**新的值会替代旧的值**。 * 键不能为空。 * 值不能为空。这个规定是我们的 API 定义中当键不存在时`get()`方法会反悔空。 ### 迭代 对于符号表,我们不使用`implements Iterable`来强制所有实现必须包含`iterator()`方法来返回一个实现了`hasNext()`和`next()`方法的迭代器,而是定义了`keys()`方法来返回一个`Iterable`对象以方便用例遍历所有的键。 ## 有序符号表 许多符号表的实现都利用了 Comparable 接口带来的键的有序性来更好地实现`put()`和`get()`方法。 ### 成本模型 在学习符号表的实现时,我们会统计**比较**的次数(等价性测试或是键的相互比较)。在内循环不进行比较(极少)的情况下,我们会统计**数组的访问次数**。 ## 无序链表中的顺序查找 符号表中使用的数据结构的一个简单选择是链表,每个结点存储一个键值对。 在含有 N 对键值的基于(无序)链表的符号表中,未命中的查找和插入操作都需要 N 次比较。命中的查找在最坏情况下需要 N 次比较,特别地,向一个空表中插入 N 个不同的键需要 ~N^2/2 次比较。 ## 有序数组中的二分查找 有序符号表的一种实现使用一对平行的数组,一个存储键一个存储值。这份实现的核心在于`rank()`方法,它返回表中小于给定键的键的数量(不管表中是否存在该键)。 ## 对二分查找的分析 在 N 个键的**有序数组**中进行二分查找最多需要 (lgN+1) 次比较(无论是否成功)。 向大小为 N 的**有序数组**中插入一个新的元素在最坏情况下需要访问 ~2N 次数组,因此向一个空符号表中插入 N 个元素在最坏情况下需要访问 ~N^2 次数组。 ## 预览 | 算法(数据结构) | N 次插入后最坏情况下的查找成本 | N 次插入后最坏情况下的插入成本 | N 次随机插入后平均情况下的查找成本 | N 次随机插入后平均情况下的查找成本 | 是否高效支持有序性相关的操作 | :----: | :----: | :----: | :----: | :----: | :----: | | 顺序查找(无序链表) | N | N | N/2 | N | 否 | 二分查找(有序数组) | lgN | 2N | lgN | N | 是 现代应用需要**同时**能够支持高效的查找和插入两种操作的符号表实现。 为了将二分查找的效率和链表的灵活性结合起来,**二叉查找树**应运而生。 | 使用的数据结构 | 实现 | 优点 | 缺点 | :----: | :----: | :----: | :----: | | 链表(顺序查找) | [SequentialSearchST]() | 适用于小型问题 | 对于大型符号表很慢 | 有序数组(二分查找) | [BinarySearchST]() | 最优的查找效率和空间需求,能够进行有序性相关的操作 | 插入操作很慢 | 二叉查找树 | [BST]() | 实现简单,能够进行有序性操作相关的操作 | | 平衡二叉查找树 | [RedBlackBST]() | 最优的查找和插入效率,能够进行有序性相关的操作 | | 散列表 | [SeparateChainHashST]()

[LinearProbingHashST]() | 能够快速地查找和插入常见类型的数据 | ================================================ FILE: docs/Searching/3.2_二叉查找树.md ================================================ # 3.2 二叉查找树 **定义**:一棵**二叉查找树(BST,或二叉搜索树)**是一棵二叉树,其中每个结点都含有一个 Comparable 的键(以及相关联的值)且每个结点的键都大于其左子树中的任意结点的键而小于右子树的任意结点的键。 ## 基本实现 ### 数据表示 二叉查找树上的一个结点,**左链接**指向一棵**小于**该结点的所有键组成的二叉查找树,**右链接**指向一棵由**大于**该结点的所有键组成的二叉查找树。 ### 查找 在二叉查找树中查找一个键的递归算法:如果树是空的,则查找未命中;如果被查找的键和根结点的键相等,查找命中,否则我们就(递归地)在适当的子树中继续查找。如果被查找的键较小就选择左子树,较大则选择右子树。 ### 删除* 对于删除一个拥有两个子结点的情况,在删除结点 x 后用它的**后继结点**填补它的位置。因为 x 有一个右子结点,因此它的后继结点就是**其右子树中的最小结点**。这样的替换仍然能保证树的有序性,因为 x.key 和它的后继结点的键之间不存在其他的键。 用 4 个简单的步骤能够完成将 x 替换为它的后继结点的任务: 1. 将指向即将被删除的结点的链接保存为 t; 2. 将 x 指向它的后继结点`min(t.right)`; 3. 将 x 的**右链接**(原本指向一棵所有结点都大于 x.key 的二叉查找树)指向`deleteMin(t.right)`,也就是在删除后所有结点仍然大于 x.key 的子二叉查找树; 4. 将 x 的**左链接**(本为空)设为 t.left(其下所有的键都小于被删除的结点和它的后继结点)。 对于某些大规模的实际应用,这种方法可能会有一点性能上的问题。 ## 分析 使用二叉查找树的算法的运行时间取决于树的形状,而树的形状又取决于键被插入的先后顺序。 * **最好**情况:一棵含有 N 个结点的树是完全平衡的,每条空链接和根结点的距离都为 ~lgN。 * **最坏**情况:搜索路径上可能有 N 个结点。 在由 N 个随机键构造的二叉查找树中,查找命中平均所需的比较次数为 ~2lnN(约 1.39 lgN)。 在由 N 个随机键构造的二叉查找树中插入操作和查找未命中平均所需的比较次数为 ~2lnN(约 1.39 lgN)。 ## 简单的符号表实现的成本总结 | 算法(数据结构) | N 次插入后最坏情况下的查找成本 | N 次插入后最坏情况下的插入成本 | N 次随机插入后平均情况下的查找成本 | N 次随机插入后平均情况下的查找成本 | 是否高效支持有序性相关的操作 | :----: | :----: | :----: | :----: | :----: | :----: | | 顺序查找(无序链表) | N | N | N/2 | N | 否 | 二分查找(有序数组) | lgN | 2N | lgN | N | 是 | 二叉树查找(二叉查找树) | N | N | 1.39lgN | 1.39lgN | 是 随机键构造的二叉查找树的平均高度为树中结点数的对数级别。而对于不随机的构造树的键,有**平衡二叉查找树**来保证无论键的插入顺序如何,树的高度都将是总键数的对数。 ================================================ FILE: docs/Searching/3.3_平衡查找树.md ================================================ # 3.3 平衡查找树 前几种算法在最坏情况下的性能还是很糟糕。本节中介绍一种二分查找树并能保证无论如何构造它,它的运行时间都是对数级别的。 理想情况下,我们希望能够保持二分查找树的平衡性,以使树高为 ~lgN,这样就能保证所有查找都能在 ~lgN 次比较内结束。 ## 2-3 查找树 **定义**:一棵 **2-3 查找树**或为一棵空树,或由以下结点组成: * **2-结点**,含有一个键(及其对应的值)和两条链接,左链接指向的 2-3 树中的键都小于该结点,右链接指向的 2-3 树中的键都大于该结点; * **3-结点**,含有两个键(及其对应的值)和三条链接,左链接指向的 2-3 树中的键都小于该结点,**中链接**指向的 2-3 树中的键都位于该结点的两个键之间,右链接指向的 2-3 树中的键都大于该结点。 将指向一棵空树的链接称为**空链接**,一棵**完美平衡**的 2-3 查找树中的所有空链接到根结点的距离都应该是相同的。 ### 插入 在一棵大小为 N 的 2-3 树中,查找和插入操作访问的结点必然不超过 lgN 个。 ## 尽管可以用不同的数据类型表示 2-结点和 3-结点,但这种直白的表示方法需要维护两种不同类型不同类型的结点,将被查找的键和结点中的每个键进行比较,将链接和其中信息从一种结点复制到另一种结点, ## 红黑二叉查找树 **定义**:**红黑树**是含有红黑链接并满足下列条件的二叉查找树: * 红链接均为左链接; * 没有任何一个结点同时和两条红链接相连; * 该树是**完美黑色平衡**的,即任意空链接到根结点的路径上的黑链接数量相同。 **红链接**将两个 2-结点连接起来构成一个 3-结点,**黑链接**则是 2-3 树中的普通链接。确切来说,3-结点被表示为由一条**左斜**的红色链接相连的两个 2-结点(其中一个是另一个的左子结点)。 ## 各种符号表实现的成本总结 | 算法(数据结构) | N 次插入后最坏情况下的查找成本 | N 次插入后最坏情况下的插入成本 | N 次随机插入后平均情况下的查找成本 | N 次随机插入后平均情况下的查找成本 | 是否高效支持有序性相关的操作 | :----: | :----: | :----: | :----: | :----: | :----: | | 顺序查找(无序链表) | N | N | N/2 | N | 否 | 二分查找(有序数组) | lgN | 2N | lgN | N | 是 | 二叉树查找(BST) | N | N | 1.39lgN | 1.39lgN | 是 | 2-3 树查找(红黑树) | 2lgN | 2lgN | lgN | lgN | 是 ================================================ FILE: docs/Searching/3.4_散列表.md ================================================ # 3.4 散列表 使用散列的查找算法分为两步: 1. 使用**散列函数**将被查找的键转化为数组的一个索引; 2. **处理碰撞冲突**。 两种解决碰撞的方法:**拉链法**和**线性探测法**。 散列表是算法在**时间**和**空间**上做出权衡的经典例子,只需要调整散列算法的参数就可以在空间和时间之间做出取舍。使用散列表可以实现在一般应用中拥有(均摊后)**常数级别**的查找和插入操作的符号表。 ## 散列函数 如果我们有一个能够保存 M 个键值对的数组,则需要能够将**任意键**转化为**该数组范围内的索引([0, M-1] 范围内的整数)**的**散列函数**,这个散列函数应该**易于计算**并且能够**均匀分布**所有的键。 ### 键为正整数 将**整数**散列的最常用方法是**除留余数法**:选择大小为**素数**(特别地,不是 2 的幂) M 的数组,对于任意正整数 k,计算 k 除以 M 的余数。 如果 M 不是素数,可能无法均匀地散列散列值。 ### 键为浮点数 两种方法: * 对于 0 到 1 之间的实数,可以乘以 M 并四舍五入得到一个 0 至 M-1 之间的索引值。但这种方式会使键的高位起的作用更大,最低位对散列的结果没有影响; * 将键表示二进制数,然后使用除留余数法(Java 使用同样的方法)。 ### 键为字符串 一种叫 Horner 方法的经典算法用 N 次乘法、加法和取余来计算一个字符串的散列值。 ```java int hash = 0; for (int i = 0; i < s.length(); i++) hash = (R * hash + s.charAt(i)) % M; ``` 只要 R 足够小,不造成溢出(这里我认为是指 int 类型的溢出),那么结果就能够落在 0 到 M-1 之内。 ### 键为组合 可以用 String 类型一样的方法进行混合处理。例如对于由两个数字表示的 day、两个数字表示的 month、四个数字表示的 year 所构成的键类型 Date,可以这样计算散列值: ```java int hash = (((day * R + month) % M) * R + year) % M; ``` ### Java 的相关处理 Java 令所有数据类型都继承了一个能够返回一个 32 比特整数的`hashCode()`方法,且每一种数据类型的`hashCode()`方法都必须和`equals()`方法一致,即`a.equals(b) == true`说明`a.hashCode() == b.hashCode()`(但反过来不一定成立)。 如果要为自定义的数据类型定义散列函数,需要同时重写`hashCode()`和`equals()`两个方法。 ### 将`hashCode()`的返回值转化为数组索引 因为需要的是数组索引而非一个 32 位的整数,在实现中会将默认的`hashCode()`方法和除留余数法结合产生一个 0 到 M-1 的整数: ```java private int hash(Key k) { return (x.hashCode() & 0x7fffffff) % M; } ``` 这段代码会将符号位屏蔽(将一个 32 位整数变为一个 31 位非负整数),然后用除留余数法计算它除以 M 的余数。 ### 软缓存 如果散列值的计算很耗时,可以考虑将**每个键的散列值缓存起来**,即在每个键中使用一个 hash 变量来保存它的`hashCode()`的返回值。 ### 小结 总的来说,要为一个数据类型实现一个优秀的散列方法需要满足三个条件: * 一致性:等价的键必然产生相等的散列值; * 高效性:计算简便; * 均匀性:均匀地散列所有的键。 其中,保证均匀性的最好方法是保证键的每一位都在散列值的计算中起到了相同的作用。 实现散列函数时有一个指导思想,称作**均匀散列假设**:我们使用的散列函数能够均匀并独立地将所有的键散布于 0 到 M-1 之间。这是一个实际无法达到的理想模型。 ## 基于拉链法的散列表 **拉链法**:将大小为 M 的数组中的每个元素指向一条链表,链表中的每个结点都存储了散列值为该元素的索引的键值对。 查找分两步:首先根据散列值找到对应的链表,然后沿着链表顺序查找相应的键。 命题:在一张含有 M 条链表和 N 个键的散列表中,(在均匀散列假设成立的前提下)任意一条链表中的键的数量均在 N/M 的常数因子范围内的概率无限趋向于 1。 性质:在一张含有 M 条链表和 N 个键的散列表中,未命中查找和插入操作所需的比较次数为 ~N/M。 ### 散列表的大小 选择适当的数组大小 M,既不会因为空链表而浪费大量内存,也不会因为链表太长而在查找上浪费太多时间。拉链法的一个**好处**是这并不是关键性的选择,对性能的影响没有那么大。 一种更可靠的方案是动态调整链表数组的大小,这样无论在符号表中有多少键值对都能保证链表较短。 ### 有序性相关的操作 散列最主要的目的在于均匀地将键散布开来,因此在计算散列后键的顺序信息就丢失了。如果要执行有序性相关的操作,散列表不是最合适的选择,因为这些操作的运行时间都会是线性的。 ## 基于线性探测法的散列表 实现散列表的另一种方式是用大小为 M 的数组保存 N 个键值对,其中 M > N,需要依靠数组中的**空位**解决碰撞冲突。基于这种策略的所有方法被统称为**开放地址**散列表,其中最简单的是**线性探测法**:当碰撞发生时(当一个键的散列值已经被另一个不同的键占用),直接检查散列表中的下一个位置(将索引值加 1)。这样的线性探测可能会产生三种结果: * 命中,该位置的键和被查找的键相同; * 未命中,键为空(该位置没有键); * 继续查找,该位置的键和被查找的键不同。 ### 删除操作 实现删除操作时,直接将该键所在的位置设为 null 是不行的,因为这会使得在此位置之后的元素无法被查找。因此,我们需要将簇中被删除的右侧的所有键重新插入散列表。 和拉链法一样,开放地址类的散列表的性能也依赖于 a = N/M 的比值,但意义有所不同。我们将 a 称为散列表的**使用率**: * 对于基于拉链法的散列表,a 是每条链表的长度,因此一般大于 1; * 对于基于线性探测的散列表,a 是表中已被占用的空间的比例,不可能大于 1,且不允许达到 1(散列表被占满,未命中的查找会导致无限循环)。会动态调整保证使用率在 1/8 到 1/2 之间。 ### 键簇 线性探测的平均成本取决于**元素在插入数组后聚集成的一组连续的条目**,也叫做**键簇**。短小的键簇才能保证较高的效率。 基于均匀散列假设,数组的每个位置都有相同的可能性被插入一个新键,长键簇更长的可能性比短键簇更大,因为新建的散列值无论落在簇中的任何位置都会使簇的长度加 1。 ### 性能分析 在一张大小为 M 并含有 N = aM 个键的基于线性探测的散列表中,基于均匀散列假设, * 命中的查找所需的探测次数为:~1/2 (1 + 1/(1 - a)) * 未命中的查找所需的探测次数为:~1/2 (1 + 1/(1 - a)^2) ## 调整数组大小 动态调整数组大小可以为我们保证 a 不大于 1/2。 * 对于拉链法,如果能够准确估计用例所需的散列表的大小 N,调整数组的工作不是必须的,选取一个适当的 M 即可; * 对于线性探测法,调整数组的大小是必须的,因为当用例插入的键值对数量超过预期时,它的查找时间不仅会变得非常长,还会在散列表被填满时进入无限循环。 假设一张散列表能够自己调整数组的大小,初始为空。基于均匀散列假设,执行任意顺序的 t 次**查找**、**插入**和**删除**操作所需的时间和 t 成正比,所使用的内存量总是在表中的键的总数的常数因子范围内。 ## 总结 ### 符号表的内存使用 | 方法 | N 个元素所需的内存(引用类型) | | :--: | :--: | | 基于拉链法的散列表 | ~48N+32M | | 基于线性探测的散列表 | 在 ~32N 和 ~128N 之间 | | 各种二叉查找树 | ~56N | ### 散列表无法达到理论最优性能原因 * 每种类型的键都需要一个优秀的散列函数; * 性能保证来自于散列函数的质量; * 散列函数的计算可能复杂而且昂贵; * 难以支持有序性相关的符号表操作。 ================================================ FILE: docs/Searching/3.5_应用.md ================================================ # 3.5 应用 ## 对符号表实现的选择 | 算法(数据结构) | N 次插入后最坏情况下的查找成本 | N 次插入后最坏情况下的插入成本 | N 次随机插入后平均情况下的查找成本 | N 次随机插入后平均情况下的查找成本 | 是否高效支持有序性相关的操作 | :----: | :----: | :----: | :----: | :----: | :----: | | 顺序查找(无序链表) | N | N | N/2 | N | 否 | 二分查找(有序数组) | lgN | 2N | lgN | N | 是 | 二叉树查找(BST) | N | N | 1.39lgN | 1.39lgN | 是 | 2-3 树查找(红黑树) | 2lgN | 2lgN | lgN | lgN | 是 | 拉链法*(链表数组) | 0 && less(a[j], a[j-1]); j--) exch(a, j, j-1); } } ``` 对于随机排列的长度为 N 且主键不重复的数组,平均情况下插入排序需要 ~N^2/4 次比较以及 ~N^2/4 次交换。最坏情况下需要 ~N^2/2 次比较和 ~N^2/2 次交换,最好情况下需要 N-1 次比较和 0 次交换。 插入排序所需的时间取决于输入中元素的初始顺序。因此,插入排序对于**部分有序**的数组十分高效,也很适应小规模数组。 选择排序和插入排序的可视化对比: ![选择排序和插入排序的可视化对比](https://algs4.cs.princeton.edu/21elementary/images/bars.png) ## 希尔排序 **希尔排序**是一种基于插入排序的快速的排序算法,为了加快速度简单地改进了插入排序,交换不相邻的元素以对数组的局部进行排序,并最终用插入排序将局部有序的数组排序。 **思想**:使数组中任意间隔为 h 的元素都是有序的。这样的数组被称为 **h 有序数组**。换句话说,一个 h 有序数组就是 h 个互相独立的有序数组编织在一起的一个数组。 ![2017-12-16 21 04 05](https://algs4.cs.princeton.edu/21elementary/images/h-sorted.png) 希尔排序更高效的原因是它权衡了子数组的规模和有序性。排序之初各个子数组都很短,排序之后的子数组都是部分有序的,这两种情况都很适合插入排序。 使用递增序列 1,4,13,40,121,364...的希尔排序所需的比较次数不会超出 N 的若干倍乘以递增序列的长度。 希尔排序的代码量很小,且不需要使用额外的内存空间。对于中等大小的数组,它的运行时间是可以接受的。 ```java public static void sort(Comparable[] a) { // 将 a[] 按升序排列 int N = a.length; int h = 1; while(h < N / 3) h = 3 * h + 1; // 1, 4, 13, 40, 121, 364, 1093, ... while(h >= 1) { // 将数组变为 h 有序 for(int i = h; i < N; i++) { // 将 a[i] 插入到 a[i-h],a[i-2*h],a[i-3*h]... 之中 for(int j = i; j >= h && less(a[j], a[j -h]); j -= h) exch(a, j, j-h); } h /= 3; } } ``` ================================================ FILE: docs/Sorting/2.2_归并排序.md ================================================ # 2.2 归并排序 **归并**:将两个有序的数组归并成一个更大的有序数组。 归并排序能保证将任意长度为 N 的数组排序所需时间和 NlogN 成正比;但主要缺点是所需的额外空间和 N 成正比。 每一次归并将涉及的所有元素复制到一个辅助数组中,再把归并的结果放回原数组中。 ## 自顶向下的归并排序 归并算法是算法设计中**分治思想**的典型应用。 **比较次数**:对于长度为 N 的任意数组,自顶向下的归并排序需要 1/2NlgN 至 NlgN 次比较。 注:N = 2 ^ n ==> n = lgN **访问数组次数**:对于长度为 N 的任意数组,自顶向下的归并排序最多需要访问数组 6NlgN 次。 ### 性能优化点 #### 对小规模子数组使用插入排序 **递归**会使**小规模问题**中方法的调用过于频繁,所以改进对它们的处理方法就能改进整个算法。 对排序来说,插入排序(或选择排序)可能在小数组上比归并排序更快。 ## 自底向上的归并排序 对于长度为 N 的任意数组,自底向上的归并排序需要 1/2NlgN 至 NlgN 次比较,最多需要访问数组 6NlgN 次。 自底向上的归并排序比较适合用**链表**组织的数据。将链表按大小为 1 的子链表进行排序,然后是大小为 2 的子链表,然后是大小为 4 的子链表等。这种方法只需要重新组织链表链接就能将链表**原地**排序。 ## 排序算法的复杂度 **命题**:没有任何基于比较的算法能够保证使用少于 lg(N!) ~ NlgN 次比较将长度为 N 的数组排序。 **证明**: 假设没有重复的主键(因为这是所有排序算法的必要需求)。N 个不同的主键会有 N! 种不同的排列。 任何基于比较的排序算法都对应着一颗高 h 的比较树,其中`N! <= 叶子结点的数量 <= 2^h`。h 的值就是最坏情况下的比较次数,因此对不等式的两边取对数可得到任意算法的比较次数至少是 lgN!。 根据斯特灵公式可得 lgN! ~ NlgN。 注:斯特灵公式:`lgN! = lg1 + lg2 + ... + lgN ~ NlgN` 归并排序是一种渐进最优的基于比较排序的算法。也就是说,归并排序在最坏情况下的比较次数和任意基于比较的排序算法所需的最少比较次数都是 ~NlgN。 ================================================ FILE: docs/Sorting/2.3_快速排序.md ================================================ # 2.3 快速排序 **特点:** * 原地排序(只需要一个很小的辅助栈); * 将长度为 N 的数组排序所需的时间和 NlgN 成正比。 ## 基本算法 快速排序的**切分**方法: 一般策略是先随意地选取 a[lo] 作为**切分元素**,即那个会被排定的元素,然后我们从数组的左端开始向右扫描直到找到一个**大于等于**它的元素,再从数组的右端开始向左扫描直到找到一个小于等于它的元素。这两个元素显然是没有排定的,因此我们交换它们的位置。 如此继续,我们就可以保证左指针 i 的左侧元素都不大于切分元素,右指针 j 的右侧元素都不小于切分元素。当两个指针相遇时,我们只需要将切分元素 a[lo] 和左子数组最右侧的元素(a[j])交换然后返回 j 即可。 ## 性能特点 快速排序的两个速度优势: 1. 切分方法的内循环会用一个递增的索引将数组元素和一个定值比较,而归并排序、希尔排序等还在内循环中移动数据; 2. 比较次数少。 将长度为 N 的无重复数组排序,快速排序平均需要 ~2NlnN 次比较(以及 1/6 的交换)。 ### 潜在缺点 快速排序的最好情况是每次都正好能将数组对半分。而**在切分不平衡**时,这个程序可能会最多需要约 **N^2/2** 次比较。例如,如果第一次从最小的元素切分,第二次从第二小的元素切分,如此这般,每次调用只会移除一个元素。 因此,我们在快速排序前将数组随机排序,以避免上述情况。 ## 算法改进 ### 切换到插入排序 和大多数递归排序算法一样,改进快速排序性能的一个简单办法基于以下两点: * 对于小数组,快速排序比插入排序慢; * 因为递归,快速排序的`sort()`方法在小数组中也会调用自己。 因此,在排序**小数组**时应该切换到插入排序。 ### 三取样切分 使用子数组中的一小部分元素的中位数来切分数组。这样做得到的切分更好,但代价是需要计算中位数。 ### 熵最优的排序 在**有大量重复元素**的情况下,快速排序的递归性会使元素全部重复的子数组经常出现,这就有很大的改进潜力,将当前实现的线性对数级的性能提高到**线性级别**。 一个简单的想法是将数组切分为**三部分**,分别对应小于、等于和大于切分元素的数组元素。这种做法被称为“三向切分”。 命题 1:不存在任何基于比较的排序算法能够保证在 NH-N 次比较之内将 N 个元素排序,其中 H 为由主键值出现频率定义的香农信息量。 命题 2:对于大小为 N 的数组,三向切分的快速排序需要 ~(2ln2)NH 次比较。其中 H 为由主键值出现频率定义的香农信息量。 **香农信息素**是对信息含量的一种标准的度量方法。给定包含 k 个不同值的 N 个主键,对于从 1 到 k 的每个 i,定义 fi 为第 i 个主键值出现的次数,pi 为 fi/N,即为随机抽取一个数组元素时第 i 个主键值出现的概率。那么所有主键的**香农信息素**可以定义为: H = -(p1lgp1 + p2lgp2 + .. + pklgpk) ================================================ FILE: docs/Sorting/2.4_优先队列.md ================================================ # 2.4 优先队列 优先队列最重要的操作就是**插入元素**和**删除最大元素**。 **应用场景:**在某些数据处理的场合,**总数据量太大**(可以认为输入是无限的),无法排序(甚至无法全部装进内存)。如果将每个新的输入和已知的 M 个最大(或最小)元素比较,除非 M 较小,否则这种比较的代价会非常高昂。如果有了**优先队列**,就只用一个能存储 M 个元素的队列即可。 ## 初级实现 * 数组实现(无序):惰性方法,仅在必要的时候找出最大元素; * 数组实现(有序):积极方法:在插入时就保持列表有序,使后续操作更高效; * 链表表示法 在上述优先队列的初级实现中,**删除最大元素**和**插入元素**这两个操作之一在最坏情况下需要**线性**时间来完成。 | 数据结构 | 插入元素 | 删除最大元素 | | :----: | :----: | :----: | | 有序数组 | N | 1 | | 无序数组 | 1 | N | | 堆 | logN | logN | | 理想情况 | 1 | 1 | ## 堆的定义 当一棵二叉树的每个结点都**大于等于**它的两个子结点时,它被称为**堆有序**。 根结点是堆有序的二叉树中的最大结点。 ### 二叉堆表示法 如果使用指针来表示堆有序的二叉树,那么每个元素都需要**三个指针**来找到它的上下结点。 但使用完全二叉树,只需要数组而不需要指针就可以表示,十分方便。具体方法是将二叉树的结点按照**层级顺序**放入数组中。 **定义**:**二叉堆**(后文简称为堆)是一组能够用堆有序的完全二叉树排序的元素,并在数组中按照层级储存(不使用数组的第一个位置)。 在一个堆中,位置`k`的结点的父结点的位置为`⌊k/2⌋`,子结点位置分别为`2k`和`2k+1`。 **高性能的原因**:利用在数组中无需指针即可沿树上下移动的便利。 注:完全二叉树:除最后一层外,每一层上的结点数均达到最大值;在最后一层上只缺少右边的若干结点。 一棵大小为 N 的完全二叉树的高度为`⌊lgN⌋`。 ## 堆的算法 **堆的有序化**:打破堆的状态,然后再遍历堆并按照要求将堆的状态恢复。 **插入元素**时,将新元素加到数组末尾,增加堆的大小并让这个新元素上浮到合适的位置。 **删除最大元素**时,从数组顶端删去最大的元素并将数组的最后一个元素放到顶端,减小堆的大小并让这个元素下沉到合适的位置。 这样,**删除最大元素**和**插入元素**这两个操作的用时和队列的大小仅成**对数**关系。对于一个含有 N 个元素的基于堆的优先队列,插入元素操作只需不超过 (lgN+1) 次比较,删除最大元素的操作需要不超过 2lgN 次比较。 ## 堆排序 用下沉操作由 N 个元素构造堆只需少于 2N 次比较以及少于 N 次交换。 将 N 个元素排序,堆排序只需少于 (2NlgN+2N) 次比较(以及一半次数的交换)。 ### 特点 **堆排序的优点**:所知的唯一能够同时最优地利用空间和时间的方法; **堆排序的缺点**:**无法利用缓存**。数组元素很少和相邻的其他元素进行比较,因此缓存未命中的次数要远远高于大多数比较都在相邻元素间进行的算法(如快速排序、归并排序、希尔排序)。 **堆排序的使用场景**:当空间十分紧张时(例如在嵌入式系统或低成本的移动设备中)很流行,但现代系统的许多应用很少使用它。 ================================================ FILE: docs/Sorting/2.5_应用.md ================================================ # 2.5 应用 ## 稳定性 如果一个排序算法能够保留数组中**重复元素的相对位置**则可以被称为是**稳定**的。 * 稳定的排序算法:插入排序、归并排序 * 不稳定的排序算法:选择排序、希尔排序、快速排序和堆排序 一般只有在稳定性是必要的情况下,稳定的排序算法才有优势。 ## 各种排序算法的性能特点 | 算法 | 是否稳定 | 是否为原地排序 | 时间复杂度 | 空间复杂度 | 备注 | :----: | :----: | :----: | :----: | :----: | :----: | | 选择排序 | 否 | 是 | N^2 | 1 | | 插入排序 | 是 | 是 | 介于 N 和 N^2 之间 | 1 | 取决于输入元素的排列情况 | 希尔排序 | 否 | 是 | NlogN? | 1 | | 快速排序 | 否 | 是 | NlogN | lgN | 运行效率由概率提供保证 | 三向快速排序 | 否 | 是 | 介于 N 和 NlogN 之间 | lgN | 运行效率由概率保证,同时也取决于输入元素的分布情况 | 归并排序 | 是 | 否 | NlogN | N | | 堆排序 | 否 | 是 | NlogN | 1 | 快速排序是最快的通用排序算法。 ### Java 系统库的排序算法 Java 的系统程序员选择对**原始数据**类型使用(三向切分的)快速排序,对**引用类型**使用归并排序。暗示着用速度和空间(对于原始数据类型)来换取稳定性(对于引用类型)。 ================================================ FILE: docs/Strings/5.2_单词查找树.md ================================================ # 5.2 单词查找树 * 查找命中所需的时间与被查找的键的长度成正比; * 查找未命中只需检查若干个字符。 ## 单词查找树 单词查找树是由链接的结点所组成的数据结构,这些链接可能为空,也可能指向其他结点。每个结点都含有 R 条链接,其中 R 为字母表的大小(即字母表中字母的数量)。 **值为空的结点在符号表中没有对应的键,它们的存在是为了简化单词查找树中的查找操作。** ### 查找操作 ### 插入操作 ### 结点的表示 ## 单词查找树的性质 单词查找树的链表结构(形状)和键的插入或删除顺序无关:对于任意给定的一组键,其单词查找树都是唯一的。 ## 三向单词查找树 ## 总结 如果空间足够,R 向单词查找树的速度是最快的,能够在**常数**次字符比较内完成查找。 对应大型字母表,R 向单词查找树所需的空间可能无法满足时,三向查找树是最佳的选择,因为它对“字符”比较次数是**对数级别**的比较,而二叉查找树中键的比较次数是对数级别的。 ![字符串查找算法特点](https://user-images.githubusercontent.com/18595460/34460004-4c0ccdf2-ee3c-11e7-9786-2c380daaf47d.png) ================================================ FILE: docs/Strings/5.3_子字符串查找.md ================================================ # 5.3 子字符串查找 * 问题描述:给定一段长度为 N 的文本和一个长度为 M 的**模式(pattern)**字符串,在文本中找到一个和该模式相符的子字符串。 * 限制:模式相对文本较短(M 可能等于 100 或者 1000),而文本相对模式很长(N 可能等于 100 万或者 10 亿)。 ## 暴力子字符串查找算法 在最坏情况下运行时间与 MN 成正比;但由于绝大多数比较在第一个字符时就会产生不匹配,实际运行时间一般与 M+N 成正比。 实现 1: ```java public static int search(String pat, String txt) { int M = pat.length(); int N = txt.length(); for(int i = 0; i <= N-M; i++) { int j; for(j = 0; j < M; j++) if(txt.charAt(j+i) != pat.charAt(j)) break; if(j == M) return i; // 找到匹配 } return N; // 未找到匹配 } ``` 实现 2:显式回退 ```java public static int search(String pat, String txt) { int M = pat.length(); int N = txt.length(); int i, j; for(i = 0, j = 0; i < N && j < M; i++) { if(txt.charAt(i) == pat.charAt(j)) j++; else { i -= j; j = 0; } } if(j == M) return i - M; // 找到匹配 else return N; // 未找到匹配 } ``` ## KMP 子字符串查找算法 基本思想:在匹配失败之前,一部分文本的内容已经和模式相匹配。因此不该完全从头开始匹配。 设置: * 文本指针 i,模式指针 j * 数组 dfa[][]:记录匹配失败时模式指针 j 应该回退多远。 在查找中,`dfa[txt.charAt(i)][j]`是在比较了`txt.charAt(i)`和`pat.charAt(j)`之后应该和`txt.charAt(i+1)`比较的模式字符位置。 ### DFA 模拟 ![kmp-dfa](https://user-images.githubusercontent.com/18595460/38723782-bc4dffd4-3f34-11e8-8cf3-a35ace403528.png) 自动机每次从左向右从文本中读取一个字符并移动到一个新的状态。 ```java for(i = 0, j = 0; i < N && j < M; i++) j = dfa[txt.charAt(i)][j]; ``` * 对于一个匹配的转换,j 就向右移动一位,因为`dfa[pat.charAt(j)][j]`的值总是`j+1`; * 对于一个非匹配转换,j 就向左移动。 循环结束后,若`j == M`,说明找到匹配,返回`i - M`。 ### 构造 DFA 当在`pat.charAt(j)`处匹配失败时,如果回退了文本指针并在右移一位之后重新扫描已知的文本字符,DFA 的状态会是什么? => **重启状态 X**:将 DFA 重置到的适当状态,如同已经回退过文本指针一样。 例如,对于 A B A B A C,要判断在 j=5 时匹配失败后 DFA 应该怎么做。通过 DFA 可以知道完全回退之后算法会扫描 B A B A 并达到状态 3,因此可以**将`dfa[][3]`复制到`dfa[][5]`**并将 C 所对应的元素的值设为 6(因为`pat.charAt(5)`是 C)。 因为在计算 DFA 的第 j 个状态时,只需要知道 DFA 是如何处理前 j-1 个字符的,所以总能从尚不完整的 DFA 中得到所需信息。 ```java dfa[pat.charAt(0)][0] = 1; for(int X = 0, j = 1; j < M; j++) { // 计算 dfa[][j] for(int ch = 0; ch < R; ch++) dfa[ch][j] = dfa[ch][X]; // 复制匹配失败情况下的值 dfa[pat.charAt(j)][j] = j+1; // 设置匹配成功情况下的值 X = dfa[pat.charAt(j)][X]; // 更新重启状态(和前面自动机从文本读取字符并移动相似) } ``` ### 代码实现 ```java public class KMP { private String pat; private int[][] dfa; public KMP(String pat) { this.pat = pat; int M = pat.length(); int R = 256; dfa = new int[R][M]; dfa[pat.charAt(0)][0] = 1; for(int X = 0, j = 1; j < M; j++) { // 计算 dfa[][j] for(int ch = 0; ch < R; ch++) dfa[ch][j] = dfa[ch][X]; // 复制匹配失败情况下的值 dfa[pat.charAt(j)][j] = j+1; // 设置匹配成功情况下的值 X = dfa[pat.charAt(j)][X]; // 更新重启状态 } } public int search(String txt) { int i, j, N = txt.length(), M = pat.length(); for(i = 0, j = 0; i < N && j < M; i++) j = dfa[txt.charAt(i)][j]; if(j == M) return i - M; // 找到匹配(到达模式字符串的结尾) else return N; // 未找到匹配(到达文本字符串的结尾) } } ``` ### 性能总结 对于长度为 M 的模式字符串和长度为 N 的文本,KMP 字符串查找算法**访问的字符不会超过 M+N 个**:模式字符串每个字符一次,文本字符串最坏情况下每个字符一次。 KMP 比暴力算法的速度优势并不十分明显,但优点是不需要在输入中回退。因此,**KMP 更适合在长度不确定的输入流中进行查找**,需要回退的算法在这种情况下则需要复杂的缓冲机制(但当回退很容易时,可以比 KMP 快得多)。 ## Boyer-Moore 字符串查找算法 基本思想:使用数组`right[]`记录字母表中的每个字符串在模式中出现的**最靠右**的地方(如果字符在模式中不存在则表示为 -1)。这个值表示如果该字符造成匹配失败时,应该向右跳跃多远。 ```java for(int c = 0; c < R; c++) right[c] = -1; // 不包含在模式字符串中的字符的值为 -1 for(int j = 0; j < M; j++) // 包含在模式字符串中的字符的值为 right[pat.charAt(j)] = j; // 它在其中出现的最右位置 ``` 文本索引 i 从左向右移动,匹配时模式索引 j **从右向左**移动。匹配失败会遇到以下三种情况: * 如果造成匹配失败的字符不包含在模式字符串中,`i`增加`j+1`; * 如果造成匹配失败的字符包含在模式字符串中,则对齐,`i`增加`j-right[txt.charAt(i+j)]`; * 如果这种情况无法增大`i`(例如,对齐导致模式字符串向左移动),那就直接将`i`加 1 来保证模式字符串至少向右移动了一个位置。 其中,通过将不在模式字符串的字符`ch`的`right[ch]`设置为 -1,可以将情况 1 合并到情况 2 中。 ![boyer-moore](https://user-images.githubusercontent.com/18595460/38733263-d01cf2d0-3f53-11e8-9928-44833bc02153.jpg) ### 代码实现 ```java public class BoyerMoore { private int[] right; private String pat; // 计算跳跃表 public BoyerMoore(String pat) { this.pat = pat; int M = pat.length(); int R = 256; right = new int[R]; for(int c = 0; c < R; c++) right[c] = -1; // 不包含在模式字符串中的字符的值为 -1 for(int j = 0; j < M; j++) // 包含在模式字符串中的字符的值为 right[pat.charAt(j)] = j; // 它在其中出现的最右位置 } // 在 txt 中查找模式字符串 public int search(String txt) { int N = txt.length(); int M = pat.length(); int skip; for(int i = 0; i <= N-M; i += skip) { // 模式字符串和文本在位置 i 匹配吗? skip = 0; for(int j = M-1; j >= 0; j--) if(pat.charAt(j) != txt.charAt(i+j)) { skip = j - right[txt.charAt(i+j)]; if(skip < 1) skip = 1; break; } if(skip == 0) return i; // 找到匹配 } return N; // 未找到匹配 } } ``` ### 性能总结 在许多实际应用场景中,模式字符串中仅含有字母表中的若干字符是很常见的,因此几乎所有的比较都会使算法跳过 M 个字符。 由此可知,在一般情况下,对于长度为 N 的文本和长度为 M 的模式字符串,Boyer-Moore 算法需要 ~N/M 次字符比较。 ## Rabin-Karp 指纹字符串查找算法 “指纹”表示算法基于**散列**并用极少信息表示了模式字符串。 ![Rabin-Karp.png](https://upload-images.jianshu.io/upload_images/2702529-4b3cf0a06d503a2d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) ### 验证正确性 为了确保得到了一个匹配的字符串(而非散列值相同),可能需要添加回退文本指针并逐个字符串比较的代码,这种方式被称为**拉斯维加斯**算法。 相对的,将散列表规模设为任意大的一个值以将冲突概率降低到绝对小的方法被称为**蒙特卡洛**算法。 ### 性能总结 * 使用蒙特卡洛算法的 Rabin-Karp 子字符串查找算法的运行时间是线性级别的,且出错的概率极小; * 使用拉斯维加斯算法的 Rabin-Karp 子字符串查找算法能够保证正确性且性能及其接近线性级别。 ## 总结 * 暴力查找算法: * 优点:实现简单,且在一般的情况下都工作良好; * 缺点:所需时间可能与 MN 成正比; * Java 的 String 类型的`indexOf()`方法使用。 * [KMP 算法](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter5_3_Substring_Search/KMP.java): * 优点:保证线性级别的性能,且不需要在正文中回退; * 缺点:需要额外的内存空间。 * [Boyer-Moore 算法](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter5_3_Substring_Search/BoyerMoore.java): * 优点:一般情况下性能为亚线性级别(可能是线性级别的 M 倍); * 缺点:需要额外的内存空间。 * [Rabin-Karp 算法](https://github.com/bighuang624/Algorithms-notes/blob/master/code/chapter5_3_Substring_Search/RabinKarp.java): * 优点:线性级别; * 缺点:内循环很长(若干次算术运算,而其他算法只需要比较字符)。 ![cost-of-substring-searching](https://user-images.githubusercontent.com/18595460/38733998-9f872d40-3f56-11e8-8e37-d517e014ed2a.jpg) ================================================ FILE: docs/_sidebar.md ================================================ - **第 1 章 基础** - 1.1 基础编程模型 - 1.2 数据抽象 - [1.3 背包、队列和栈](Fundamentals/1.3_背包、队列和栈) - [1.4 算法分析](Fundamentals/1.4_算法分析) - [1.5 案例研究:union-find算法](Fundamentals/1.5_案例研究:union-find算法) - **第 2 章 排序** - [2.1 初级排序算法](Sorting/2.1_初级排序算法) - [2.2 归并排序](Sorting/2.2_归并排序) - [2.3 快速排序](Sorting/2.3_快速排序) - [2.4 优先队列](Sorting/2.4_优先队列) - [2.5 应用](Sorting/2.5_应用) - **第 3 章 查找** - [3.1 符号表](Searching/3.1_符号表) - [3.2 二叉查找树](Searching/3.2_二叉查找树) - [3.3 平衡查找树](Searching/3.3_平衡查找树) - [3.4 散列表](Searching/3.4_散列表) - [3.5 应用](Searching/3.5_应用) - **第 4 章 图** - [4.1 无向图](Graphs/4.1_无向图) - [4.2 有向图](Graphs/4.2_有向图) - [4.3 最小生成树](Graphs/4.3_最小生成树) - [4.4 最短路径](Graphs/4.4_最短路径) - **第 5 章 字符串** - 5.1 字符串排序 - [5.2 单词查找树](Strings/5.2_单词查找树) - [5.3 子字符串查找](Strings/5.3_子字符串查找) - 5.4 正则表达式 - 5.5 数据压缩 - **第 6 章 背景** - [6.1 事件驱动模拟](Context/6.1_事件驱动模拟) - [6.2 B- 树](Context/6.2_B-树) - [6.3 后缀数组](Context/6.3_后缀数组) - [6.4 网络流算法](Context/6.4_网络流算法) - [6.5 问题规约](Context/6.5_问题规约) - [6.6 不可解性](Context/6.6_不可解性) ================================================ FILE: docs/index.html ================================================ 《算法(第4版)》笔记

加载中...
================================================ FILE: package.json ================================================ { "name": "Algorithms-notes", "version": "1.0.0", "description": "《算法(第4版)》笔记", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "preupdate": "cd code && yuki && cd .. && git add docs code README.md", "update": "git commit -m", "postupdate": "git push origin master" }, "repository": { "type": "git", "url": "git+https://github.com/bighuang624/LeetCode-everyday.git" }, "author": "", "license": "ISC", "bugs": { "url": "https://github.com/bighuang624/LeetCode-everyday/issues" }, "homepage": "https://github.com/bighuang624/LeetCode-everyday#readme" } ================================================ FILE: 每一节可以再看一遍的题.md ================================================ ## 每一节可以再看一遍的题 ### 1.1 1.1.9 将一个正整数 N 用二进制表示并转换为一个 String 类型的 s ================================================ FILE: 相关问题解决方法.md ================================================ ## Eclipse命令行参数使用 Run---->Run Configurations----->右边 Arguments 里 Program arguments 中写。 如果写的是文件,默认该文件要放在项目的根目录里。当然也可以换位置,勾选 Working directory 的 Other,然后选择文件目录即可。