├── .gitattributes ├── .gitignore ├── Chapter01 ├── README.md ├── exercise01.cpp └── exercise01.h ├── Chapter02 ├── README.md ├── example.h ├── exercise02.cpp └── exercise02.h ├── Chapter03 ├── List.h ├── Queue.h ├── README.md ├── SimpleCalculator.h ├── Stack.h ├── Vector.h └── test.cpp ├── Chapter04 ├── AVLTree.h ├── BinarySearchTree.h ├── README.md ├── exercise04.h └── test.cpp ├── Chapter05 ├── HashTable(Open_Addressing_Hashing).h ├── HashTable(Separate_Chaining).h ├── README.md └── test.cpp ├── Chapter06 ├── BinaryHeap.h ├── BinomialQueue.h ├── LeftistHeap.h ├── README.md └── test.cpp ├── Chapter07 ├── README.md ├── Sort.h ├── Timer.h └── test.cpp ├── Chapter08 ├── DisjSets.h ├── README.md └── test.cpp ├── Data-Structure.sln ├── Data-Structure.vcxproj ├── Data-Structure.vcxproj.filters ├── Data-Structure.vcxproj.user ├── LICENSE └── README.md /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | *.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | *.vcxproj merge=binary 29 | *.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | *.jpg binary 44 | *.png binary 45 | *.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | # *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | [Xx]64/ 19 | [Xx]86/ 20 | [Bb]uild/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | 85 | # Visual Studio profiler 86 | *.psess 87 | *.vsp 88 | *.vspx 89 | *.sap 90 | 91 | # TFS 2012 Local Workspace 92 | $tf/ 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | *.DotSettings.user 101 | 102 | # JustCode is a .NET coding add-in 103 | .JustCode 104 | 105 | # TeamCity is a build add-in 106 | _TeamCity* 107 | 108 | # DotCover is a Code Coverage Tool 109 | *.dotCover 110 | 111 | # NCrunch 112 | _NCrunch_* 113 | .*crunch*.local.xml 114 | nCrunchTemp_* 115 | 116 | # MightyMoose 117 | *.mm.* 118 | AutoTest.Net/ 119 | 120 | # Web workbench (sass) 121 | .sass-cache/ 122 | 123 | # Installshield output folder 124 | [Ee]xpress/ 125 | 126 | # DocProject is a documentation generator add-in 127 | DocProject/buildhelp/ 128 | DocProject/Help/*.HxT 129 | DocProject/Help/*.HxC 130 | DocProject/Help/*.hhc 131 | DocProject/Help/*.hhk 132 | DocProject/Help/*.hhp 133 | DocProject/Help/Html2 134 | DocProject/Help/html 135 | 136 | # Click-Once directory 137 | publish/ 138 | 139 | # Publish Web Output 140 | *.[Pp]ublish.xml 141 | *.azurePubxml 142 | 143 | # TODO: Un-comment the next line if you do not want to checkin 144 | # your web deploy settings because they may include unencrypted 145 | # passwords 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # NuGet Packages 150 | *.nupkg 151 | # The packages folder can be ignored because of Package Restore 152 | **/packages/* 153 | # except build/, which is used as an MSBuild target. 154 | !**/packages/build/ 155 | # Uncomment if necessary however generally it will be regenerated when needed 156 | #!**/packages/repositories.config 157 | # NuGet v3's project.json files produces more ignoreable files 158 | *.nuget.props 159 | *.nuget.targets 160 | 161 | # Microsoft Azure Build Output 162 | csx/ 163 | *.build.csdef 164 | 165 | # Microsoft Azure Emulator 166 | ecf/ 167 | rcf/ 168 | 169 | # Windows Store app package directory 170 | AppPackages/ 171 | BundleArtifacts/ 172 | 173 | # Visual Studio cache files 174 | # files ending in .cache can be ignored 175 | *.[Cc]ache 176 | # but keep track of directories ending in .cache 177 | !*.[Cc]ache/ 178 | 179 | # Others 180 | ClientBin/ 181 | [Ss]tyle[Cc]op.* 182 | ~$* 183 | *~ 184 | *.dbmdl 185 | *.dbproj.schemaview 186 | *.pfx 187 | *.publishsettings 188 | node_modules/ 189 | orleans.codegen.cs 190 | 191 | # RIA/Silverlight projects 192 | Generated_Code/ 193 | 194 | # Backup & report files from converting an old project file 195 | # to a newer Visual Studio version. Backup files are not needed, 196 | # because we have git ;-) 197 | _UpgradeReport_Files/ 198 | Backup*/ 199 | UpgradeLog*.XML 200 | UpgradeLog*.htm 201 | 202 | # SQL Server files 203 | *.mdf 204 | *.ldf 205 | 206 | # Business Intelligence projects 207 | *.rdl.data 208 | *.bim.layout 209 | *.bim_*.settings 210 | 211 | # Microsoft Fakes 212 | FakesAssemblies/ 213 | 214 | # GhostDoc plugin setting file 215 | *.GhostDoc.xml 216 | 217 | # Node.js Tools for Visual Studio 218 | .ntvs_analysis.dat 219 | 220 | # Visual Studio 6 build log 221 | *.plg 222 | 223 | # Visual Studio 6 workspace options file 224 | *.opt 225 | 226 | # Visual Studio LightSwitch build output 227 | **/*.HTMLClient/GeneratedArtifacts 228 | **/*.DesktopClient/GeneratedArtifacts 229 | **/*.DesktopClient/ModelManifest.xml 230 | **/*.Server/GeneratedArtifacts 231 | **/*.Server/ModelManifest.xml 232 | _Pvt_Extensions 233 | 234 | # LightSwitch generated files 235 | GeneratedArtifacts/ 236 | ModelManifest.xml 237 | 238 | # Paket dependency manager 239 | .paket/paket.exe 240 | 241 | # FAKE - F# Make 242 | .fake/ 243 | -------------------------------------------------------------------------------- /Chapter01/README.md: -------------------------------------------------------------------------------- 1 | # 第一章 引论 2 | 3 | ## 内容 4 | 5 | * 介绍基本数学知识 6 | * 简要复习递归 7 | * 介绍用到的C++知识 8 | 9 | ## 递归的四条基本法则 10 | 1. 基准情形。必须总有某些基准情形不用递归就能求解。 11 | 2. 不断推进。对于那些需要递归求解的情形,递归调用必须总能够朝着基准情形的方向推进。 12 | 3. 设计法则。假设所有的递归调用都能运行。 13 | 4. 合成效益法则。在求解一个问题的同一实例时,切勿在不同的递归调用中做重复性的工作。 -------------------------------------------------------------------------------- /Chapter01/exercise01.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "exercise01.h" 4 | 5 | int main() 6 | { 7 | OrderedCollection ARR; 8 | 9 | int temp = 0; 10 | cout << "Please input some numbers(-1 means end): "; 11 | while ((cin >> temp) && (temp != -1)) 12 | ARR.insert(temp); 13 | 14 | if (ARR.size()) { 15 | cout << "Max number is: " << *ARR.findMax() << endl; 16 | cout << "Min number is: " << *ARR.findMin() << endl; 17 | } 18 | 19 | return 0; 20 | } -------------------------------------------------------------------------------- /Chapter01/exercise01.h: -------------------------------------------------------------------------------- 1 | #ifndef DS_CH01_EXERCISE01_H 2 | #define DS_CH01_EXERCISE01_H 3 | 4 | // ex 1.5 5 | int ones(int n) 6 | { 7 | if (n < 2) 8 | return n; 9 | return (n & 1) + ones(n >> 1); 10 | } 11 | 12 | #include 13 | 14 | using namespace std; 15 | 16 | //ex 1.13 17 | template 18 | class Collection { 19 | public: 20 | typedef typename vector::const_iterator const_iterator; 21 | typedef typename vector::size_type size_type; 22 | 23 | public: 24 | size_type size() const { return arr.size(); } 25 | bool isEmpty() const { return arr.empty(); } 26 | void makeEmpty() { arr.clear(); } 27 | bool contains(const Object &obj) { return _find(obj) != arr.cend(); } 28 | void insert(const Object &obj) { arr.push_back(obj); } 29 | void remove(const Object &obj) 30 | { 31 | auto it = _find(obj); 32 | if (it != arr.cend()) 33 | arr.erase(it); 34 | } 35 | private: 36 | const_iterator _find(const Object &obj) 37 | { 38 | auto it = arr.cbegin(); 39 | while (it != arr.cend()) { 40 | if (*it == obj) 41 | return it; 42 | ++it; 43 | } 44 | return it; 45 | } 46 | 47 | private: 48 | vector arr; 49 | }; 50 | 51 | //ex 1.14 52 | template 53 | class OrderedCollection { 54 | typedef typename vector::const_iterator const_iterator; 55 | typedef typename vector::size_type size_type; 56 | 57 | public: 58 | size_type size() const { return arr.size(); } 59 | bool isEmpty() const { return arr.empty(); } 60 | void makeEmpty() { arr.clear(); } 61 | void insert(const Comparable &obj) { arr.push_back(obj); } 62 | void remove(const Comparable &obj) 63 | { 64 | auto it = arr.cbegin(); 65 | while (it != arr.cend()) { 66 | if (*it == obj) { 67 | arr.erase(it); 68 | break; 69 | } 70 | } 71 | } 72 | const_iterator findMin() const 73 | { 74 | size_type minIdx = 0; 75 | for (size_type i = 1; i < size(); ++i) { 76 | if (arr[minIdx] > arr[i]) 77 | minIdx = i; 78 | } 79 | return arr.cbegin() + minIdx; 80 | } 81 | const_iterator findMax() const 82 | { 83 | size_type minIdx = 0; 84 | for (size_type i = 1; i < size(); ++i) { 85 | if (arr[minIdx] < arr[i]) 86 | minIdx = i; 87 | } 88 | return arr.cbegin() + minIdx; 89 | } 90 | 91 | private: 92 | vector arr; 93 | }; 94 | 95 | #endif // DS_CH01_EXERCISE01_H 96 | -------------------------------------------------------------------------------- /Chapter02/README.md: -------------------------------------------------------------------------------- 1 | # 第二章 算法分析 2 | 3 | ## 内容 4 | * 主要内容是复杂度分析 5 | * 大O标记 6 | * 计算大O时的一般法则 7 | - 对数规律的一般法则 8 | 如果一个算法用常数时间(O(1))将问题的大小削减为其一部分(通常是1/2),那么该算法就是O(logN)的。 9 | 10 | ## 例子 11 | 1. 二分搜索提供了O(logN)的查找算法 12 | 2. 最大公因数的欧几里得算法也是O(logN)的 13 | 3. 幂运算的递归算法 -------------------------------------------------------------------------------- /Chapter02/example.h: -------------------------------------------------------------------------------- 1 | #ifndef DS_CH02_EXAMPLE_H 2 | #define DS_CH02_EXAMPLE_H 3 | 4 | #include 5 | using std::vector; 6 | 7 | // 二分搜索 8 | template 9 | int binarySearch(const vector &a, const Comparable &x) 10 | { 11 | int low = 0, high = a.size() - 1, mid = 0; 12 | while (low <= high) { 13 | mid = (low + high) >> 1; 14 | if (a[mid] < x) 15 | low = mid + 1; 16 | else if (a[mid] > x) 17 | high = mid - 1; 18 | else 19 | return mid; 20 | } 21 | return -1; // -1 means NOT FOUND 22 | } 23 | 24 | // 最大公因数的欧几里得算法 25 | long gcd(long m, long n) 26 | { 27 | while (n != 0) { 28 | long rem = m % n; 29 | m = n; 30 | n = rem; 31 | } 32 | return m; 33 | } 34 | 35 | // 幂运算的递归算法 36 | long pow(long x, int n) 37 | { 38 | if (n == 0) 39 | return 1; 40 | if (n & 1) 41 | return pow(x, n - 1) * x; 42 | else 43 | return pow(x * x, n >> 1); 44 | } 45 | 46 | #endif // DS_CH02_EXAMPLE_H 47 | -------------------------------------------------------------------------------- /Chapter02/exercise02.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "exercise02.h" 4 | 5 | using namespace std; 6 | 7 | int main() 8 | { 9 | maxInfo i; 10 | vector arr; 11 | int temp = 0; 12 | cout << "Please input some numbers: "; 13 | while (cin >> temp) { 14 | arr.push_back(temp); 15 | } 16 | 17 | cout << "最小连续子序列和: " << minSubSum(arr) << endl; 18 | 19 | i = maxSubSum(arr); 20 | cout << "最大连续子序列和: " << i.Sum << ", 起始下标: " << i.begin << ", 结束下标: " << i.end << endl; 21 | 22 | return 0; 23 | } -------------------------------------------------------------------------------- /Chapter02/exercise02.h: -------------------------------------------------------------------------------- 1 | #ifndef DS_CH02_EXERCISE02_H 2 | #define DS_CH02_EXERCISE02_H 3 | 4 | #include 5 | using std::vector; 6 | 7 | //2.17 8 | //a.求最小连续子序列和 O(N)算法 9 | int minSubSum(const vector &a) 10 | { 11 | int minSum = 0, thisSum = 0; 12 | for (int i = 0; i < a.size(); ++i) { 13 | thisSum += a[i]; 14 | if (thisSum < minSum) 15 | minSum = thisSum; 16 | else if (thisSum > 0) 17 | thisSum = 0; 18 | } 19 | return minSum; 20 | } 21 | 22 | //2.19 23 | //返回最大连续子序列和以及对应的下标 24 | struct maxInfo { 25 | maxInfo() = default; 26 | int Sum; 27 | int begin; 28 | int end; 29 | }; 30 | 31 | maxInfo info; 32 | 33 | maxInfo maxSubSum(const vector &a) 34 | { 35 | int thisSum = 0; 36 | bool isBegin = false; 37 | for (int i = 0; i < a.size(); ++i) { 38 | thisSum += a[i]; 39 | if (thisSum > info.Sum) { 40 | info.Sum = thisSum; 41 | if (!isBegin) { 42 | isBegin = true; 43 | info.begin = i; 44 | } 45 | info.end = i; 46 | } else if (thisSum < 0) { 47 | thisSum = 0; 48 | isBegin = false; 49 | } 50 | } 51 | return info; 52 | } 53 | 54 | #endif // DS_CH02_EXERCISE02_H 55 | -------------------------------------------------------------------------------- /Chapter03/List.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/i-square/Data-Structure/412e38cd88d20c1b928445998cd0ae098c1fce3e/Chapter03/List.h -------------------------------------------------------------------------------- /Chapter03/Queue.h: -------------------------------------------------------------------------------- 1 | #ifndef DS_CH03_QUEUE_H 2 | #define DS_CH03_QUEUE_H 3 | 4 | #include "List.h" 5 | 6 | template 7 | class LinkQueue { 8 | private: 9 | struct Node { 10 | Node(const T &d = T(), Node *n = nullptr) 11 | : data(d), next(n) 12 | {} 13 | T data; 14 | Node *next; 15 | }; 16 | 17 | public: 18 | LinkQueue() : front(nullptr), rear(nullptr) {} 19 | LinkQueue(const LinkQueue &rhs) { operator=(rhs); } 20 | ~LinkQueue() { clear(); } 21 | 22 | public: 23 | LinkQueue &operator=(const LinkQueue &rhs) 24 | { 25 | if (this != &rhs) { 26 | clear(); 27 | 28 | Node *src = rhs.front; 29 | while (src != nullptr) { 30 | enqueue(src->data); 31 | src = src->next; 32 | } 33 | } 34 | return *this; 35 | } 36 | 37 | void enqueue(const T &x) 38 | { 39 | Node *ptr = new Node(x); 40 | if (rear != nullptr) 41 | rear = rear->next = ptr; 42 | else 43 | front = rear = ptr; 44 | } 45 | T dequeue() 46 | { 47 | T tmp = front->data; 48 | Node *ptr = front; 49 | if (front->next == nullptr) 50 | front = rear = nullptr; 51 | else 52 | front = front->next; 53 | delete ptr; 54 | return tmp; 55 | } 56 | void clear() 57 | { 58 | while (front != nullptr) 59 | dequeue(); 60 | } 61 | 62 | private: 63 | Node *front; 64 | Node *rear; 65 | }; 66 | 67 | #include 68 | using std::vector; 69 | 70 | template 71 | class ArrQueue { 72 | public: 73 | ArrQueue(int size = 16) : maxSize(size), front(0), rear(0) { theArray.resize(maxSize); } 74 | ArrQueue(const ArrQueue &rhs) { operator=(rhs); } 75 | ~ArrQueue() { clear(); } 76 | 77 | public: 78 | ArrQueue &operator=(const ArrQueue &rhs) 79 | { 80 | if (this != &rhs) { 81 | clear(); 82 | 83 | front = rhs.front; 84 | rear = rhs.rear; 85 | maxSize = rhs.maxSize; 86 | theArray.resize(maxSize); 87 | for (int i = 0; i < maxSize; ++i) 88 | theArray[i] = rhs.theArray[i]; 89 | } 90 | return *this; 91 | } 92 | 93 | void enqueue(const T &x) 94 | { 95 | if (!full()) { 96 | theArray[rear] = x; 97 | rear = (rear + 1) % maxSize; 98 | } 99 | } 100 | T dequeue() 101 | { 102 | T tmp = theArray[front]; 103 | front = (front + 1) % maxSize; 104 | return tmp; 105 | } 106 | void clear() 107 | { 108 | while (!empty()) 109 | dequeue(); 110 | } 111 | bool empty() const { return front == rear; } 112 | bool full() const { return (rear + 1) % maxSize == front; } 113 | 114 | private: 115 | int front, rear; 116 | int maxSize; 117 | vector theArray; 118 | }; 119 | 120 | #endif // DS_CH03_QUEUE_H 121 | -------------------------------------------------------------------------------- /Chapter03/README.md: -------------------------------------------------------------------------------- 1 | # 表、栈和队列 2 | 3 | ## 内容 4 | * 介绍三种基本的数据结构 5 | * 介绍抽象数据类型(ADT, abstract data type)的概念 6 | * 介绍栈ADT及其在实现递归方面的应用 7 | * 介绍队列ADT及其在操作系统和算法设计中的应用 8 | * 给出vector和list的重要子集的实现 9 | 10 | ## 栈 11 | ### 实现 12 | 栈是一个表,因此任何实现表的方法都能实现栈。 13 | ### 应用 14 | 1. 符号平衡 15 | 2. 后缀(逆波兰)表达式计算 16 | 3. 中缀到后缀的转换 17 | 4. 函数调用 18 | (代码实现了一个简单的计算器,应保证输入合法) 19 | 20 | ## 总结 21 | ### 快慢指针 22 | ex 3.34 提示:判断一个链表是否有环,只使用O(1)的额外空间,使用两个迭代器p,q p每次递增1,q每次递增2,若q到了末尾则没环,否则pq必定在环中间相遇 23 | 24 | 也可用于快速找出单链表的中间节点 -------------------------------------------------------------------------------- /Chapter03/SimpleCalculator.h: -------------------------------------------------------------------------------- 1 | #ifndef DS_CH03_SIMPLECALCULATOR_H 2 | #define DS_CH03_SIMPLECALCULATOR_H 3 | 4 | #include 5 | using std::string; 6 | 7 | #include "Vector.h" 8 | #include "Stack.h" 9 | 10 | class SimpleCalculator { 11 | public: 12 | SimpleCalculator() = default; 13 | explicit SimpleCalculator(const string &s) { _rpn = toRPN(s); } 14 | SimpleCalculator(const SimpleCalculator &rhs) = delete; 15 | ~SimpleCalculator() = default; 16 | 17 | public: 18 | double calcu(const string &s) 19 | { 20 | Vector rpn = toRPN(s); 21 | double d = 0.0, d1 = 0.0, d2 = 0.0; 22 | LinkStack dstk; 23 | string strT; 24 | 25 | for (auto it = rpn.begin(); it != rpn.end(); ++it) { 26 | strT = *it; 27 | if (isdigit(strT[0])) { 28 | dstk.push(std::stod(strT)); 29 | } else { 30 | d1 = dstk.top(); 31 | dstk.pop(); 32 | d2 = dstk.top(); 33 | dstk.pop(); 34 | 35 | if (strT == "+") { 36 | d = d2 + d1; 37 | } else if (strT == "-") { 38 | d = d2 - d1; 39 | } else if (strT == "*") { 40 | d = d2 * d1; 41 | } else if (strT == "/") { 42 | d = d2 / d1; 43 | } 44 | 45 | dstk.push(d); 46 | } 47 | } 48 | return dstk.top(); 49 | } 50 | double getResult() 51 | { 52 | double d = 0.0, d1 = 0.0, d2 = 0.0; 53 | LinkStack dstk; 54 | string strT; 55 | 56 | for (auto it = _rpn.begin(); it != _rpn.end(); ++it) { 57 | strT = *it; 58 | if (isdigit(strT[0])) { 59 | dstk.push(std::stod(strT)); 60 | } else { 61 | d1 = dstk.top(); 62 | dstk.pop(); 63 | d2 = dstk.top(); 64 | dstk.pop(); 65 | 66 | if (strT == "+") { 67 | d = d2 + d1; 68 | } else if (strT == "-") { 69 | d = d2 - d1; 70 | } else if (strT == "*") { 71 | d = d2 * d1; 72 | } else if (strT == "/") { 73 | d = d2 / d1; 74 | } 75 | 76 | dstk.push(d); 77 | } 78 | } 79 | return dstk.top(); 80 | } 81 | 82 | private: 83 | const Vector toRPN(const string &s) 84 | { 85 | Vector vec; 86 | LinkStack opStk; 87 | char tch[2] = { 0 }; 88 | 89 | for (auto it = s.cbegin(); it != s.cend(); ++it) { 90 | if (isdigit(*it)) { 91 | auto tit = it; 92 | while (isdigit(*tit)) 93 | ++tit; 94 | vec.push_back(string(it, tit)); 95 | it = --tit; 96 | } else { 97 | switch (*it) { 98 | case '(': 99 | opStk.push(*it); 100 | break; 101 | case ')': 102 | tch[0] = opStk.top(); 103 | while (tch[0] != '(') { 104 | vec.push_back(string(tch)); 105 | opStk.pop(); 106 | tch[0] = opStk.top(); 107 | } 108 | opStk.pop(); 109 | break; 110 | case '+': 111 | case '-': 112 | while (!opStk.empty()) { 113 | tch[0] = opStk.top(); 114 | if (tch[0] == '(') 115 | break; 116 | opStk.pop(); 117 | vec.push_back(string(tch)); 118 | } 119 | opStk.push(*it); 120 | break; 121 | case '*': 122 | case '/': 123 | while (!opStk.empty()) { 124 | tch[0] = opStk.top(); 125 | if (tch[0] == '(' || tch[0] == '+' || tch[0] == '-') 126 | break; 127 | opStk.pop(); 128 | vec.push_back(string(tch)); 129 | } 130 | opStk.push(*it); 131 | break; 132 | default: 133 | break; 134 | } 135 | } 136 | } 137 | while (!opStk.empty()) { 138 | tch[0] = opStk.top(); 139 | opStk.pop(); 140 | vec.push_back(string(tch)); 141 | } 142 | return vec; 143 | } 144 | int priority(const char &ch) 145 | { 146 | switch (ch) { 147 | case '+': 148 | case '-': 149 | return 1; 150 | case '*': 151 | case '/': 152 | return 2; 153 | case '(': 154 | return 3; 155 | default: 156 | break; 157 | } 158 | return -1; 159 | } 160 | 161 | private: 162 | Vector _rpn; 163 | }; 164 | 165 | #endif // DS_CH03_SIMPLECALCULATOR_H 166 | -------------------------------------------------------------------------------- /Chapter03/Stack.h: -------------------------------------------------------------------------------- 1 | #ifndef DS_CH03_STACK_H 2 | #define DS_CH03_STACK_H 3 | 4 | template 5 | class LinkStack { 6 | private: 7 | struct Node { 8 | Node(const T &d = T(), Node *n = nullptr) 9 | : data(d), next(n) 10 | { } 11 | T data; 12 | Node *next; 13 | }; 14 | 15 | public: 16 | LinkStack() : stackSize(0), head(nullptr) { } 17 | LinkStack(const LinkStack &rhs) : stackSize(0), head(nullptr) { operator=(rhs); } 18 | ~LinkStack() { clear(); } 19 | LinkStack &operator=(const LinkStack &rhs) 20 | { 21 | if (this != &rhs) { 22 | clear(); 23 | 24 | Node *src = rhs.head; 25 | 26 | while (src != nullptr) { 27 | push(src->data); 28 | src = src->next; 29 | } 30 | } 31 | return *this; 32 | } 33 | 34 | void push(const T &x) 35 | { 36 | Node *ptr = new Node(x, head); 37 | head = ptr; 38 | ++stackSize; 39 | } 40 | void pop() 41 | { 42 | Node *ptr = head->next; 43 | delete head; 44 | head = ptr; 45 | --stackSize; 46 | } 47 | T &top() 48 | { 49 | return head->data; 50 | } 51 | const T &top() const 52 | { 53 | return head->data; 54 | } 55 | bool empty() const { return size() == 0; } 56 | int size() const { return stackSize; } 57 | void clear() 58 | { 59 | while (!empty()) 60 | pop(); 61 | } 62 | 63 | private: 64 | int stackSize; 65 | Node *head; 66 | }; 67 | 68 | template 69 | class ArrStack { 70 | enum { SPARE_CAPACITY = 16 }; 71 | 72 | public: 73 | explicit ArrStack(int initSize = 0) 74 | : topOfStack(-1), theCapacity(initSize + SPARE_CAPACITY) 75 | { 76 | theArray = new T[theCapacity]; 77 | } 78 | ArrStack(const ArrStack &rhs) : theArray(nullptr) 79 | { 80 | operator=(rhs); 81 | } 82 | ~ArrStack() 83 | { 84 | delete[] theArray; 85 | } 86 | 87 | ArrStack &operator=(const ArrStack &rhs) 88 | { 89 | if (this != &rhs) { 90 | delete[] theArray; 91 | topOfStack = rhs.topOfStack; 92 | theCapacity = rhs.theCapacity; 93 | 94 | theArray = new T[theCapacity]; 95 | for (int i = 0; i < size(); ++i) 96 | theArray[i] = rhs.theArray[i]; 97 | } 98 | return *this; 99 | } 100 | 101 | void push(const T &x) 102 | { 103 | if (++topOfStack == theCapacity) 104 | reserve(2 * theCapacity + 1); 105 | theArray[topOfStack] = x; 106 | } 107 | T &pop() 108 | { 109 | return theArray[topOfStack--]; 110 | } 111 | const T &pop() const 112 | { 113 | return theArray[topOfStack--]; 114 | } 115 | T &top() 116 | { 117 | return theArray[topOfStack]; 118 | } 119 | const T &top() const 120 | { 121 | return theArray[topOfStack]; 122 | } 123 | 124 | int size() const { return topOfStack + 1; } 125 | bool empty() const { return topOfStack == -1; } 126 | void clear() 127 | { 128 | while (!empty()) 129 | pop(); 130 | } 131 | 132 | private: 133 | void reserve(int newCapacity) 134 | { 135 | T *oldArray = theArray; 136 | 137 | theArray = new T[newCapacity]; 138 | for (int i = 0; i < size(); ++i) 139 | theArray[i] = oldArray[i]; 140 | 141 | theCapacity = newCapacity; 142 | 143 | delete[] oldArray; 144 | } 145 | 146 | private: 147 | int topOfStack; 148 | int theCapacity; 149 | T *theArray; 150 | }; 151 | 152 | #endif // DS_CH03_STACK_H 153 | -------------------------------------------------------------------------------- /Chapter03/Vector.h: -------------------------------------------------------------------------------- 1 | #ifndef DS_CH03_VECTOR_H 2 | #define DS_CH03_VECTOR_H 3 | 4 | #include 5 | 6 | template 7 | class Vector { 8 | enum { SPARE_CAPACITY = 16 }; 9 | typedef Object *iterator; 10 | typedef const Object *const_iterator; 11 | 12 | public: 13 | explicit Vector(int initSize = 0) 14 | : theSize(initSize), theCapacity(initSize + SPARE_CAPACITY) 15 | { 16 | objects = new Object[theCapacity]; 17 | } 18 | Vector(const Vector &rhs) : objects(nullptr) 19 | { 20 | operator=(rhs); 21 | } 22 | ~Vector() 23 | { 24 | delete[] objects; 25 | } 26 | 27 | Vector &operator=(const Vector &rhs) 28 | { 29 | if (this != &rhs) { 30 | delete[] objects; 31 | theSize = rhs.size(); 32 | theCapacity = rhs.capacity(); 33 | 34 | objects = new Object[theCapacity]; 35 | for (int i = 0; i < size(); ++i) 36 | objects[i] = rhs.objects[i]; 37 | } 38 | return *this; 39 | } 40 | 41 | void resize(int newSize) 42 | { 43 | if (newSize > theCapacity) 44 | reserve(newSize * 2 + 1); 45 | theSize = newSize; 46 | } 47 | 48 | void reserve(int newCapacity) 49 | { 50 | if (newCapacity < theSize) 51 | return; 52 | Object *oldArray = objects; 53 | 54 | objects = new Object[newCapacity]; 55 | for (int i = 0; i < theSize; ++i) 56 | objects[i] = oldArray[i]; 57 | 58 | theCapacity = newCapacity; 59 | 60 | delete[] oldArray; 61 | } 62 | 63 | Object &operator[](int index) 64 | { 65 | // ex3.7 添加边界检测 66 | if (index >= 0 && index < size()) 67 | return objects[index]; 68 | else 69 | throw std::runtime_error("index out of bounds"); 70 | } 71 | const Object &operator[](int index) const 72 | { 73 | // ex3.7 添加边界检测 74 | if (index >= 0 && index < size()) 75 | return objects[index]; 76 | else 77 | throw std::runtime_error("index out of bounds"); 78 | } 79 | 80 | bool empty() const { return size() == 0; } 81 | void clear() { theSize = 0; } 82 | int size() const { return theSize; } 83 | int capacity() const { return theCapacity;} 84 | 85 | void push_back(const Object &x) 86 | { 87 | if (theSize == theCapacity) 88 | reserve(2 * theCapacity + 1); 89 | objects[theSize++] = x; 90 | } 91 | 92 | void pop_back() { --theSize; } 93 | 94 | const Object &back() const { return objects[theSize - 1]; } 95 | 96 | iterator begin() { return &objects[0]; } 97 | const_iterator begin() const { return &objects[0]; } 98 | iterator end() { return &objects[size()]; } 99 | const_iterator end() const { return &objects[size()]; } 100 | 101 | private: 102 | int theSize; 103 | int theCapacity; 104 | Object *objects; 105 | }; 106 | 107 | #endif // DS_CH03_VECTOR_H 108 | -------------------------------------------------------------------------------- /Chapter03/test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "SimpleCalculator.h" 4 | #include "Queue.h" 5 | #include "List.h" 6 | 7 | using namespace std; 8 | 9 | int main() 10 | { 11 | SimpleCalculator cal("14+2-32+8*9+(54*7+92-4)"); 12 | cout << cal.getResult() << endl; 13 | cout << cal.calcu("13+2-32+8*9+(54*7+92-4)") << endl; 14 | 15 | int a[20] = { 29, 14, 15, 84, 65, 85, 92, 81, 51, 13, 5, 18, -64, 89, 66, 21, 32, 27, 24, 52 }; 16 | LinkQueue que; 17 | SingleList lst; 18 | List dlst; 19 | 20 | for (int i = 0; i < 20; ++i) { 21 | que.enqueue(a[i]); 22 | lst.add(a[i]); 23 | dlst.push_back(a[i]); 24 | } 25 | 26 | cout << "queue: "; 27 | for (int i = 0; i < 20; ++i) 28 | cout << que.dequeue() << "→"; 29 | cout << endl; 30 | 31 | cout << "single list: "; 32 | lst.print(); 33 | 34 | cout << dlst.back() << " pop_back: "; 35 | dlst.pop_back(); 36 | cout << dlst.back() << endl; 37 | 38 | getchar(); 39 | 40 | return 0; 41 | } -------------------------------------------------------------------------------- /Chapter04/AVLTree.h: -------------------------------------------------------------------------------- 1 | #ifndef DS_CH04_AVLTREE_H 2 | #define DS_CH04_AVLTREE_H 3 | 4 | #include 5 | #include 6 | using std::cout; 7 | using std::endl; 8 | 9 | template 10 | class AVLTree { 11 | private: 12 | struct AVLNode { 13 | AVLNode (const T &element, AVLNode *lt, AVLNode *rt, int h = 0) 14 | : ele(element), left(lt), right(rt), height(h) 15 | {} 16 | T ele; 17 | AVLNode *left; 18 | AVLNode *right; 19 | int height; 20 | }; 21 | 22 | public: 23 | AVLTree() : root(nullptr) {} 24 | AVLTree(const AVLTree &rhs) { operator=(rhs); } 25 | ~AVLTree() { makeEmpty(); } 26 | 27 | public: 28 | AVLTree &operator=(const AVLTree &rhs) 29 | { 30 | if (this != &rhs) { 31 | makeEmpty(); 32 | root = clone(rhs.root); 33 | } 34 | return *this; 35 | } 36 | 37 | const T &findMin() const { return findMin(root)->ele; } //应保证非空调用 38 | const T &findMax() const { return findMax(root)->ele; } //应保证非空调用 39 | bool contains(const T &x) { return contains(x, root); } 40 | bool empty() const { return root == nullptr; } 41 | void printTree() const { printTree(root); } 42 | 43 | void makeEmpty() { makeEmpty(root); } 44 | void insert(const T &x) { insert(x, root); } 45 | void remove(const T &x) { remove(x, root); } 46 | 47 | private: 48 | int height(AVLNode *t) const { return t == nullptr ? -1 : t->height; } 49 | 50 | AVLNode *clone(AVLNode *t) const 51 | { 52 | if (t == nullptr) 53 | return nullptr; 54 | 55 | return new AVLNode(t->ele, clone(t->left), clone(t->right), t->height); 56 | } 57 | AVLNode *findMin(AVLNode *t) const 58 | { 59 | if (t == nullptr) 60 | return nullptr; 61 | if (t->left == nullptr) 62 | return t; 63 | return findMin(t->left); 64 | } 65 | AVLNode *findMax(AVLNode *t) const 66 | { 67 | if (t != nullptr) { 68 | while (t->right != nullptr) 69 | t = t->right; 70 | } 71 | return t; 72 | } 73 | bool contains(const T &x, AVLNode *t) 74 | { 75 | if (t == nullptr) 76 | return false; 77 | else if (x < t->ele) 78 | return contains(x, t->left); 79 | else if (t->ele < x) 80 | return contains(x, t->right); 81 | else 82 | return true; 83 | } 84 | void printDepth(const T &x, int depth = 0) const 85 | { 86 | while (depth--) 87 | cout << "\t"; 88 | cout << "[" << x << "]" << endl; 89 | } 90 | void printTree(AVLNode *t, int depth = 0) const 91 | { 92 | if (t == nullptr) 93 | return; // do nothing 94 | printTree(t->left, depth + 1); 95 | printDepth(t->ele, depth); //这句放的位置决定了前中后序遍历 96 | printTree(t->right, depth + 1); 97 | } 98 | void makeEmpty(AVLNode *&t) 99 | { 100 | if (t != nullptr) { 101 | makeEmpty(t->left); 102 | makeEmpty(t->right); 103 | delete t; 104 | } 105 | t = nullptr; 106 | } 107 | 108 | // 针对情况1左左,节点和左儿子一起顺时针旋转 109 | void rotateWithLeft(AVLNode *&k2) 110 | { 111 | AVLNode *k1 = k2->left; 112 | k2->left = k1->right; 113 | k1->right = k2; 114 | 115 | k2->height = max(height(k2->left), height(k2->right)) + 1; 116 | k1->height = max(height(k1->left), k2->height) + 1; // 右子树高度就是k2的高度 117 | 118 | k2 = k1; // 保证上一层节点链接到旋转后的节点 119 | } 120 | // 针对情况4右右,节点和右儿子一起逆时针旋转 121 | void rotateWithRight(AVLNode *&k1) 122 | { 123 | AVLNode *k2 = k1->right; 124 | k1->right = k2->left; 125 | k2->left = k1; 126 | 127 | k1->height = max(height(k1->left), height(k1->right)) + 1; 128 | k2->height = max(k1->height, height(k2->right)) + 1; // 左子树高度就是k2的高度 129 | 130 | k1 = k2; // 保证上一层节点链接到旋转后的节点 131 | } 132 | // 针对情况2左右,节点的左儿子先逆时针旋转,接着节点顺时针旋转 133 | void doubleRotateWithLeft(AVLNode *&k3) 134 | { 135 | //rotateWithRight(k3->left); 136 | //rotateWithLeft(k3); 137 | 138 | //ex 3.26 编写一个双旋转,效率高于两次单旋转 139 | //分析旋转过程不难总结出来 140 | AVLNode *k1 = k3->left; 141 | AVLNode *k2 = k1->right; 142 | 143 | k1->right = k2->left; 144 | k3->left = k2->right; 145 | k2->left = k1; 146 | k2->right = k3; 147 | 148 | k1->height = max(height(k1->left), height(k1->right)) + 1; 149 | k3->height = max(height(k3->left), height(k3->right)) + 1; 150 | k2->height = max(k1->height, k3->height) + 1; 151 | 152 | k3 = k2; // 保证上一层节点链接到旋转后的节点 153 | } 154 | // 针对情况3右左,节点的右儿子先顺时针旋转,接着节点逆时针旋转 155 | void doubleRotateWithRight(AVLNode *&k1) 156 | { 157 | //rotateWithLeft(k3->right); 158 | //rotateWithRight(k3); 159 | 160 | //ex 3.26 编写一个双旋转,效率高于两次单旋转 161 | //分析旋转过程不难总结出来 162 | AVLNode *k3 = k1->right; 163 | AVLNode *k2 = k3->left; 164 | 165 | k1->right = k2->left; 166 | k3->left = k2->right; 167 | k2->left = k1; 168 | k2->right = k3; 169 | 170 | k1->height = max(height(k1->left), height(k1->right)) + 1; 171 | k3->height = max(height(k3->left), height(k3->right)) + 1; 172 | k2->height = max(k1->height, k3->height) + 1; 173 | 174 | k1 = k2; // 保证上一层节点链接到旋转后的节点 175 | } 176 | 177 | void insert(const T &x, AVLNode *&t) 178 | { 179 | if (t == nullptr) 180 | t = new AVLNode(x, nullptr, nullptr); 181 | else if (x < t->ele) { 182 | insert(x, t->left); 183 | if (height(t->left) - height(t->right) == 2) { 184 | if (x < t->left->ele) 185 | rotateWithLeft(t); 186 | else 187 | doubleRotateWithLeft(t); 188 | } 189 | } else if (t->ele < x) { 190 | insert(x, t->right); 191 | if (height(t->right) - height(t->left) == 2) { 192 | if (t->right->ele < x) 193 | rotateWithRight(t); 194 | else 195 | doubleRotateWithRight(t); 196 | } 197 | } else 198 | ; //do nothing 199 | t->height = max(height(t->left), height(t->right)) + 1; 200 | } 201 | 202 | //AVLTree删除节点的思路: 203 | //1.像二叉查找树一样删除节点 204 | //2.重新计算节点高度 205 | //3.检查是否满足平衡条件,不满足需要调整 206 | void remove(const T &x, AVLNode *&t) 207 | { 208 | if (t == nullptr) 209 | return; //not found, do nothing 210 | if (x < t->ele) { 211 | remove(x, t->left); 212 | 213 | t->height = std::max(height(t->left), height(t->right)) + 1; 214 | 215 | //删了左子树一个节点,需要判断右>左 216 | if (height(t->right) - height(t->left) == 2) { 217 | if (height(t->right->right) >= height(t->right->left)) 218 | rotateWithRight(t); 219 | else 220 | doubleRotateWithRight(t); 221 | } 222 | } else if (t->ele < x) { 223 | remove(x, t->right); 224 | 225 | t->height = std::max(height(t->left), height(t->right)) + 1; 226 | 227 | //删了右子树一个节点,需要判断左>右 228 | if (height(t->left) - height(t->right) == 2) { 229 | if (height(t->left->left) >= height(t->left->right)) 230 | rotateWithLeft(t); 231 | else 232 | doubleRotateWithLeft(t); 233 | } 234 | } else if (t->left != nullptr && t->right != nullptr) { //2 children 235 | t->ele = findMin(t->right)->ele; //用右子树的最小值替代当前值 236 | remove(t->ele, t->right); //删掉右子树的最小值 237 | 238 | t->height = std::max(height(t->left), height(t->right)) + 1; 239 | 240 | //删了右子树一个节点,需要判断左>右 241 | if (height(t->left) - height(t->right) == 2) { 242 | if (height(t->left->left) >= height(t->left->right)) 243 | rotateWithLeft(t); 244 | else 245 | doubleRotateWithLeft(t); 246 | } 247 | } else { 248 | AVLNode *old = t; 249 | t = (t->left != nullptr) ? t->left : t->right; 250 | delete old; 251 | } 252 | } 253 | 254 | private: 255 | AVLNode *root; 256 | }; 257 | 258 | #endif // DS_CH04_AVLTREE_H 259 | -------------------------------------------------------------------------------- /Chapter04/BinarySearchTree.h: -------------------------------------------------------------------------------- 1 | #ifndef DS_CH04_BINARYSEARCHTREE_H 2 | #define DS_CH04_BINARYSEARCHTREE_H 3 | 4 | #include 5 | using std::cout; 6 | using std::endl; 7 | 8 | template 9 | class BinarySearchTree { 10 | private: 11 | struct BinaryNode { 12 | BinaryNode (const T &element, BinaryNode *lt, BinaryNode *rt) 13 | : ele(element), left(lt), right(rt) 14 | {} 15 | T ele; 16 | BinaryNode *left; 17 | BinaryNode *right; 18 | }; 19 | 20 | public: 21 | BinarySearchTree() : root(nullptr) {} 22 | BinarySearchTree(const BinarySearchTree &rhs) { operator=(rhs); } 23 | ~BinarySearchTree() { makeEmpty(); } 24 | 25 | public: 26 | BinarySearchTree &operator=(const BinarySearchTree &rhs) 27 | { 28 | if (this != &rhs) { 29 | makeEmpty(); 30 | root = clone(rhs.root); 31 | } 32 | return *this; 33 | } 34 | 35 | const T &findMin() const { return findMin(root)->ele; } //应保证非空调用 36 | const T &findMax() const { return findMax(root)->ele; } //应保证非空调用 37 | bool contains(const T &x) const { return contains(x, root); } 38 | bool empty() const { return root == nullptr; } 39 | void printTree() const { printTree(root); } 40 | 41 | void makeEmpty() { makeEmpty(root); } 42 | void insert(const T &x) { insert(x, root); } 43 | void remove(const T &x) { remove(x, root); } 44 | 45 | private: 46 | BinaryNode *clone(BinaryNode *t) const 47 | { 48 | if (t == nullptr) 49 | return nullptr; 50 | 51 | return new BinaryNode(t->ele, clone(t->left), clone(t->right)); 52 | } 53 | BinaryNode *findMin(BinaryNode *t) const 54 | { 55 | if (t == nullptr) 56 | return nullptr; 57 | if (t->left == nullptr) 58 | return t; 59 | return findMin(t->left); 60 | } 61 | BinaryNode *findMax(BinaryNode *t) const 62 | { 63 | if (t != nullptr) { 64 | while (t->right != nullptr) 65 | t = t->right; 66 | } 67 | return t; 68 | } 69 | bool contains(const T &x, BinaryNode *t) const 70 | { 71 | if (t == nullptr) 72 | return false; 73 | else if (x < t->ele) 74 | return contains(x, t->left); 75 | else if (t->ele < x) 76 | return contains(x, t->right); 77 | else 78 | return true; 79 | } 80 | void printDepth(const T &x, int depth = 0) const 81 | { 82 | while (depth--) 83 | cout << "\t"; 84 | cout << "[" << x << "]" << endl; 85 | } 86 | void printTree(BinaryNode *t, int depth = 0) const 87 | { 88 | if (t == nullptr) 89 | return; // do nothing 90 | printTree(t->left, depth + 1); 91 | printDepth(t->ele, depth); //这句放的位置决定了前中后序遍历 92 | printTree(t->right, depth + 1); 93 | } 94 | void makeEmpty(BinaryNode *&t) 95 | { 96 | if (t != nullptr) { 97 | makeEmpty(t->left); 98 | makeEmpty(t->right); 99 | delete t; 100 | } 101 | t = nullptr; 102 | } 103 | void insert(const T &x, BinaryNode *&t) 104 | { 105 | if (t == nullptr) 106 | t = new BinaryNode(x, nullptr, nullptr); 107 | else if (x < t->ele) 108 | insert(x, t->left); 109 | else if (t->ele < x) 110 | insert(x, t->right); 111 | else 112 | ; //do nothing 113 | } 114 | void remove(const T &x, BinaryNode *&t) 115 | { 116 | if (t == nullptr) 117 | ; //not found, do nothing 118 | if (x < t->ele) 119 | remove(x, t->left); 120 | else if (t->ele < x) 121 | remove(x, t->right); 122 | else if (t->left != nullptr && t->right != nullptr) { //2 children 123 | t->ele = findMin(t->right)->ele; //用右子树的最小值替代当前值 124 | remove(t->ele, t->right); //删掉右子树的最小值 125 | } else { 126 | BinaryNode *old = t; 127 | t = (t->left != nullptr) ? t->left : t->right; 128 | delete old; 129 | } 130 | } 131 | 132 | private: 133 | BinaryNode *root; 134 | }; 135 | 136 | #endif // DS_CH04_BINARYSEARCHTREE_H 137 | -------------------------------------------------------------------------------- /Chapter04/README.md: -------------------------------------------------------------------------------- 1 | # 树 2 | 3 | ## 内容 4 | * 了解树是如何用于实现文件系统的 5 | * 了解树如何用来计算算术表达式的值 6 | * 了解如何用树实现O(logN)时间进行搜素 7 | * 讨论并使用set和map 8 | 9 | ## 二叉树的遍历 10 | * 前序:先处理自己后处理左右儿子 11 | * 中序:先处理左儿子再处理自己再处理右儿子 12 | * 后序:先处理左右儿子再处理自己 13 | 14 | ## 二叉查找树(平均深度O(logN)) 15 | 性质:对于树中的每个节点X,左子树中所有项的值小于X中的项,右子树中所有项的值大于X中的项 16 | 缺点:不能动态调整,若输入为已排序序列则构造出最坏情况下的斜树 17 | 18 | ## AVL树 19 | * 带有**平衡条件**的二叉查找树 20 | * 一棵AVL树是每个节点的左子树和右子树的高度最多相差1的二叉查找树(空树高度定义为-1) 21 | * 插入新节点可能破坏AVL树的平衡,需要通过**旋转**解决 22 | 23 | 把需要平衡的节点叫α 24 | 25 | 1. 对α的左儿子的左子树进行一次插入 26 | 2. 对α的左儿子的右子树进行一次插入 27 | 3. 对α的右儿子的左子树进行一次插入 28 | 4. 对α的右儿子的右子树进行一次插入 29 | 30 | 1和4(左左,右右)发生在外边,进行一次**单旋转**即可,2和3(左右,右左)则发生在内部,需要通过**双旋转**调整 31 | 32 | ## 伸展树 33 | 节点可以达到任意深度,每次访问某节点后把该节点调整为根节点,任意连续M次操作花费O(MlogN)时间 34 | 35 | ## B树(平衡M路树) 36 | M=3时:2-3树,实现平衡查找树的另一种方法 37 | 38 | ## 注意 39 | 通过插入元素构造查找树,然后执行中序遍历,可以得到排序后的元素。 40 | 这是一种O(NlogN)的排序算法 -------------------------------------------------------------------------------- /Chapter04/exercise04.h: -------------------------------------------------------------------------------- 1 | #ifndef DS_CH04_EXERCISE04_H 2 | #define DS_CH04_EXERCISE04_H 3 | 4 | //ex4.11 未考虑end()如何实现 5 | template 6 | class Set { 7 | private: 8 | struct BinaryNode { 9 | BinaryNode() : left(nullptr), right(nullptr), parent(nullptr) 10 | {} 11 | BinaryNode(const T &element) 12 | : ele(element), left(nullptr), right(nullptr), parent(nullptr) 13 | {} 14 | BinaryNode(const T &element, BinaryNode *lt, BinaryNode *rt, BinaryNode *pt) 15 | : ele(element), left(lt), right(rt), parent(pt) 16 | {} 17 | T ele; 18 | BinaryNode *left; 19 | BinaryNode *right; 20 | BinaryNode *parent; 21 | }; 22 | 23 | public: 24 | class const_iterator { 25 | public: 26 | const_iterator() : current(nullptr) {} 27 | const T &operator*() const { return retrieve(); } 28 | const_iterator &operator++() 29 | { 30 | BinaryNode *t; 31 | if (current->right) { //若存在右儿子,则下一个是右子树的最小值 32 | t = current->right; 33 | while (t->left != nullptr) 34 | t = t->left; 35 | current = t; 36 | } else { //否则上溯,若本身是右儿子则持续上溯 37 | t = current->parent; 38 | while (t != nullptr && t->ele < current->ele) 39 | t = t->parent; 40 | current = t; 41 | } 42 | return *this; 43 | } 44 | const_iterator &operator++(int) 45 | { 46 | const_iterator old = *this; 47 | ++(*this); 48 | return old; 49 | } 50 | 51 | bool operator==(const const_iterator &rhs) const 52 | { 53 | return current == rhs.current; 54 | } 55 | bool operator!=(const const_iterator &rhs) const 56 | { 57 | return !(*this == rhs); 58 | } 59 | 60 | protected: 61 | friend class Set; 62 | const_iterator(BinaryNode *p) : current(p) {} 63 | T &retrieve() const { return current->ele; } 64 | BinaryNode *current; 65 | }; 66 | 67 | class iterator : public const_iterator { 68 | public: 69 | iterator() {} 70 | 71 | T &operator*() { return retrieve(); } 72 | const T &operator*() const { return const_iterator::operator*(); } 73 | 74 | iterator &operator++() 75 | { 76 | BinaryNode *t; 77 | if (current->right) { //若存在右儿子,则下一个是右子树的最小值 78 | t = current->right; 79 | while (t->left != nullptr) 80 | t = t->left; 81 | current = t; 82 | } else { //否则上溯,若本身是右儿子则持续上溯 83 | t = current->parent; 84 | while (t != nullptr && t->ele < current->ele) 85 | t = t->parent; 86 | current = t; 87 | } 88 | return *this; 89 | } 90 | iterator &operator++(int) 91 | { 92 | iterator old = *this; 93 | ++(*this); 94 | return old; 95 | } 96 | 97 | bool operator==(const iterator &rhs) const 98 | { 99 | return current == rhs.current; 100 | } 101 | bool operator!=(const iterator &rhs) const 102 | { 103 | return !(*this == rhs); 104 | } 105 | protected: 106 | friend class Set; 107 | iterator(BinaryNode *p) : const_iterator(p) {} 108 | }; 109 | 110 | public: 111 | Set() : root(nullptr) {} 112 | Set(const Set &rhs) : root(nullptr) { operator=(rhs); } 113 | ~Set() { clear(); } 114 | 115 | public: 116 | Set &operator=(const Set &rhs) 117 | { 118 | if (this != &rhs) { 119 | clear(); 120 | const_iterator itr = rhs.begin(); 121 | for (int i = 0; i < rhs.size(); ++i) { 122 | insert(*itr); 123 | ++itr; 124 | } 125 | } 126 | return *this; 127 | } 128 | 129 | iterator insert(const T &x) { return insert(x, root, root); } 130 | void erase(const T &x) { erase(x, root); } 131 | 132 | bool contains(const T &x) const { return contains(x, root); } 133 | bool empty() const { return size() == 0; } 134 | int size() const { return theSize; } 135 | void clear() 136 | { 137 | while (!empty()) 138 | erase(*begin()); 139 | } 140 | 141 | iterator begin() 142 | { 143 | BinaryNode *t = root; 144 | while (t->left != nullptr) 145 | t = t->left; 146 | return iterator(t); 147 | } 148 | const_iterator begin() const 149 | { 150 | BinaryNode *t = root; 151 | while (t->left != nullptr) 152 | t = t->left; 153 | return const_iterator(t); 154 | } 155 | 156 | private: 157 | bool contains(const T &x, BinaryNode *t) const 158 | { 159 | if (t == nullptr) 160 | return false; 161 | else if (x < t->ele) 162 | return contains(x, t->left); 163 | else if (t->ele < x) 164 | return contains(x, t->right); 165 | else 166 | return true; 167 | } 168 | iterator insert(const T &x, BinaryNode *&t, BinaryNode *p) 169 | { 170 | if (t == nullptr) { 171 | t = new BinaryNode(x, nullptr, nullptr, p); 172 | ++theSize; 173 | return iterator(t); 174 | } 175 | else if (x < t->ele) 176 | return insert(x, t->left, t); 177 | else if (t->ele < x) 178 | return insert(x, t->right, t); 179 | return iterator(t); 180 | } 181 | void erase(const T &x, BinaryNode *&t) 182 | { 183 | if (t == nullptr) 184 | ; 185 | else if (x < t->ele) 186 | erase(x, t->left); 187 | else if (t->ele < x) 188 | erase(x, t->right); 189 | else if (t->left != nullptr && t->right != nullptr) { //2 children 190 | BinaryNode *tmp = t->right; 191 | while (tmp->left != nullptr) 192 | tmp = tmp->left; 193 | t->ele = tmp->ele; //用右子树的最小值替代当前值 194 | erase(t->ele, t->right); //删掉右子树的最小值 195 | } else { 196 | BinaryNode *old = t; 197 | t = (t->left != nullptr) ? t->left : t->right; 198 | if (t != nullptr) 199 | t->parent = old->parent; 200 | delete old; 201 | --theSize; 202 | } 203 | } 204 | 205 | private: 206 | int theSize; 207 | BinaryNode *root; 208 | }; 209 | 210 | #endif // DS_CH04_EXERCISE04_H 211 | -------------------------------------------------------------------------------- /Chapter04/test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | #include "BinarySearchTree.h" 8 | #include "AVLTree.h" 9 | #include "exercise04.h" 10 | 11 | int main() 12 | { 13 | default_random_engine e(static_cast(time(nullptr))); 14 | uniform_int_distribution u(-50, 50); 15 | 16 | int arr[50] = { 0 }; 17 | for (int i = 0; i < 50; ++i) 18 | arr[i] = u(e); 19 | 20 | cout << "BinarySearchTree Test:" << endl; 21 | BinarySearchTree BSTree; 22 | for (int i = 0; i < 20; ++i) 23 | BSTree.insert(arr[i]); 24 | 25 | cout << "max: " << BSTree.findMax() << endl; 26 | cout << "min: " << BSTree.findMin() << endl; 27 | cout << "是否包含5 ? " << boolalpha << BSTree.contains(5) << endl; 28 | BSTree.printTree(); 29 | cout << endl << endl << endl; 30 | 31 | cout << "AVLTree Test:" << endl; 32 | AVLTree AvlTree; 33 | 34 | for (int i = 0; i < 30; ++i) 35 | AvlTree.insert(arr[i+20]); 36 | 37 | AvlTree.printTree(); 38 | 39 | cout << endl << "Remove "; 40 | for (int i = 0; i < 5; ++i) { 41 | int t = arr[i + 20 + 4 * i]; 42 | cout << t << ","; 43 | AvlTree.remove(t); 44 | } 45 | cout << endl << endl; 46 | 47 | AvlTree.printTree(); 48 | 49 | //ex4.11 50 | Set st; 51 | for (int i = 0; i < 50; ++i) 52 | st.insert(arr[i]); 53 | 54 | cout << "Set contains: " << endl; 55 | Set::const_iterator itr = st.begin(); 56 | for (int i = 0; i < st.size(); ++i, ++itr) { 57 | cout << *itr << ","; 58 | } 59 | cout << endl; 60 | cout << "erase: " << arr[10] << endl; 61 | st.erase(arr[10]); 62 | cout << "Now contains: " << endl; 63 | itr = st.begin(); 64 | for (int i = 0; i < st.size(); ++i, ++itr) { 65 | cout << *itr << ","; 66 | } 67 | cout << endl; 68 | 69 | getchar(); 70 | return 0; 71 | } -------------------------------------------------------------------------------- /Chapter05/HashTable(Open_Addressing_Hashing).h: -------------------------------------------------------------------------------- 1 | #ifndef DS_CH05_HASHTABLE_OAH_H 2 | #define DS_CH05_HASHTABLE_OAH_H 3 | 4 | #include 5 | #include 6 | 7 | //平方探测 8 | template 9 | class HashTable_OAH { 10 | enum EntryType { ACTIVE, EMPTY, DELETED }; 11 | 12 | int primes[24] = { 13 | 53, 97, 193, 389, 769, 1453, 3079, 6151, 12893, 24593, 49157, 98317, 14 | 196613, 393241, 786433, 1572869, 3145739, 6291469, 12582917, 15 | 25165843, 50331653, 100663319, 201326611, -1 16 | }; 17 | 18 | int nextPrime(int n) 19 | { 20 | for (int i = 0; i < 24; ++i) 21 | if (n < primes[i]) 22 | return primes[i]; 23 | return primes[23]; 24 | } 25 | 26 | private: 27 | struct HashEntry { 28 | HashEntry(const T &e = T(), EntryType i = EMPTY) : ele(e), info(i) {} 29 | T ele; 30 | EntryType info; 31 | }; 32 | 33 | public: 34 | HashTable_OAH() = delete; 35 | explicit HashTable_OAH(int size) : curSize(0), arr(size) { makeEmpty(); } 36 | HashTable_OAH(const HashTable_OAH &rhs) { operator=(rhs); } 37 | ~HashTable_OAH() = default; 38 | HashTable_OAH &operator=(const HashTable_OAH &rhs) 39 | { 40 | if (this != &rhs) { 41 | makeEmpty(); 42 | for (const auto HE : rhs.arr) { 43 | insert(HE); 44 | } 45 | } 46 | return *this; 47 | } 48 | 49 | int size() const { return curSize; } 50 | bool contains(const T &x) const 51 | { 52 | return isActive(findPos(x)); 53 | } 54 | bool insert(const T &x) 55 | { 56 | int curPos = findPos(x); 57 | if (isActive(curPos)) 58 | return false; 59 | 60 | arr[curPos] = HashEntry(x, ACTIVE); 61 | 62 | if (++curSize > (arr.size() >> 1)) 63 | rehash(); 64 | 65 | return true; 66 | } 67 | bool remove(const T &x) 68 | { 69 | int curPos = findPos(x); 70 | if (!isActive(curPos)) 71 | return false; 72 | 73 | arr[curPos].info = DELETED; 74 | --curSize; 75 | return true; 76 | } 77 | 78 | void makeEmpty() 79 | { 80 | curSize = 0; 81 | for (auto &HE : arr) { 82 | HE.info = EMPTY; 83 | } 84 | } 85 | 86 | private: 87 | bool isActive(int curPos) const 88 | { 89 | return arr[curPos].info == ACTIVE; 90 | } 91 | int findPos(const T &x) const 92 | { 93 | int offset = 1; 94 | int curPos = myHash(x); 95 | 96 | while (arr[curPos].info != EMPTY && 97 | arr[curPos].ele != x) { 98 | //平方探测的快速方法 99 | //由 f(i) = f(i-1) + 2i - 1 推得 100 | curPos += offset; 101 | offset += 2; 102 | if (curPos >= arr.size()) 103 | curPos -= int(arr.size()); 104 | } 105 | 106 | return curPos; 107 | } 108 | void rehash() 109 | { 110 | auto oldArr = arr; 111 | arr.resize(nextPrime(int(oldArr.size() << 1))); 112 | for (auto HE : arr) 113 | HE.info = EMPTY; 114 | 115 | curSize = 0; 116 | for (const auto HE : oldArr) 117 | if (HE.info == ACTIVE) 118 | insert(HE.ele); 119 | } 120 | int myHash(const T &x) const 121 | { 122 | int hashVal = int(hash_value(x)); 123 | hashVal %= arr.size(); 124 | if (hashVal < 0) 125 | hashVal += int(arr.size()); 126 | return hashVal; 127 | } 128 | private: 129 | vector arr; 130 | int curSize; 131 | }; 132 | 133 | #endif // DS_CH05_HASHTABLE_OAH_H 134 | -------------------------------------------------------------------------------- /Chapter05/HashTable(Separate_Chaining).h: -------------------------------------------------------------------------------- 1 | #ifndef DS_CH05_HASHTABLE_SC_H 2 | #define DS_CH05_HASHTABLE_SC_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | template 11 | class HashTable_SC { 12 | int primes[24] = { 13 | 53, 97, 193, 389, 769, 1453, 3079, 6151, 12893, 24593, 49157, 98317, 14 | 196613, 393241, 786433, 1572869, 3145739, 6291469, 12582917, 15 | 25165843, 50331653, 100663319, 201326611, -1 16 | }; 17 | 18 | int nextPrime(int n) 19 | { 20 | for (int i = 0; i < 24; ++i) 21 | if (n < primes[i]) 22 | return primes[i]; 23 | return primes[23]; 24 | } 25 | 26 | public: 27 | HashTable_SC() = delete; 28 | explicit HashTable_SC(int size) : curSize(0), theLists(size) { makeEmpty(); } 29 | HashTable_SC(const HashTable_SC &rhs) { operator=(rhs); } 30 | ~HashTable_SC() = default; 31 | HashTable_SC &operator=(const HashTable_SC &rhs) 32 | { 33 | if (this != &rhs) { 34 | makeEmpty(); 35 | for (const auto l : rhs.theLists) { 36 | for (const auto t : l) { 37 | insert(t); 38 | } 39 | } 40 | } 41 | return *this; 42 | } 43 | 44 | int size() const { return curSize; } 45 | bool contains(const T &x) const 46 | { 47 | const auto whichList = theLists[myHash(x)]; 48 | return find(whichList.cbegin(), whichList.cend(), x) != whichList.cend(); 49 | } 50 | bool insert(const T &x) 51 | { 52 | auto &whichList = theLists[myHash(x)]; 53 | if (find(whichList.cbegin(), whichList.cend(), x) != whichList.cend()) 54 | return false; 55 | 56 | whichList.push_back(x); 57 | 58 | if (++curSize > theLists.size()) 59 | rehash(); 60 | 61 | return true; 62 | } 63 | bool remove(const T &x) 64 | { 65 | auto &whichList = theLists[myHash(x)]; 66 | auto itr = find(whichList.cbegin(), whichList.cend(), x); 67 | if (itr == whichList.cend()) 68 | return false; 69 | 70 | whichList.erase(itr); 71 | --curSize; 72 | return true; 73 | } 74 | 75 | void makeEmpty() 76 | { 77 | curSize = 0; 78 | for (auto &l : theLists) { 79 | l.clear(); 80 | } 81 | } 82 | 83 | private: 84 | void rehash() 85 | { 86 | auto oldLists = theLists; 87 | theLists.resize(nextPrime(int(theLists.size() << 1))); 88 | for (auto &l : theLists) { 89 | l.clear(); 90 | } 91 | 92 | curSize = 0; 93 | for (const auto l : oldLists) { 94 | for (const auto t : l) { 95 | insert(t); 96 | } 97 | } 98 | } 99 | int myHash(const T &x) const 100 | { 101 | int hashVal = int(hash_value(x)); 102 | hashVal %= theLists.size(); 103 | if (hashVal < 0) 104 | hashVal += int(theLists.size()); 105 | return hashVal; 106 | } 107 | private: 108 | vector> theLists; 109 | int curSize; 110 | }; 111 | 112 | #endif // DS_CH05_HASHTABLE_SC_H 113 | -------------------------------------------------------------------------------- /Chapter05/README.md: -------------------------------------------------------------------------------- 1 | # 散列 2 | 散列表(hash table)的实现通常称为散列(hashing),指用于以O(1)时间执行插入、删除和查找的技术,但不支持需要排序信息的树操作,比如findMin、findMax以及在线性时间内按顺序打印整个表都不支持 3 | 4 | ## 内容 5 | 中心数据结构是**散列表** 6 | 7 | * 实现散列表的几种方法 8 | * 分析比较几种方法 9 | * 介绍散列的多种应用 10 | * 比较散列表与二叉查找树 11 | 12 | ## 散列函数 13 | 基本思想:将每个键(Key)映射到从[0, TableSize)这个范围中的某个数,并且将其放到适当的单元中,这个映射就称为**散列函数**。 14 | 问题:选择一个函数,决定当两个键散列到同一个值的时候(称为**冲突(collision)**应该做什么以及如何确定散列表的大小。 15 | _注:一般使表的大小为素数,有助于避免部分冲突问题_ 16 | 17 | ## 装填因子(load factor) 18 | 定义散列表的装填因子 λ 为散列表中的元素个数与散列表大小的比值。 19 | 20 | ## 分离链接法 21 | 将散列到同一个值的所有元素保留到一个链表中。 22 | 一般法则:使 λ ≈ 1,控制链表的长度,若 λ > 1 则通过再散列扩充 23 | 24 | ## 开放定址法 25 | 不用链表存储,实现分配较大空间,称为**探测散列表** 26 | hi(x) = (hash(x) + f(i)) mod TableSize, f(0) = 0. 27 | 一般 λ > 0.5 就要再散列 28 | 29 | * 线性探测 f(i) = i 30 | * 平方探测 f(i) = i^2 31 | * 双散列 f(i) = i * hash2(x), hash2(x) = R - (x mod R) 这样的函数会起作用,其中R为小于TableSize的素数 32 | 33 | ## 再散列(rehash) 34 | 1. 只要表到一半就再散列 35 | 2. 只有插入失败时才再散列 36 | 3. 途中策略:当表到达某一个装填因子时进行再散列(最优) 37 | -------------------------------------------------------------------------------- /Chapter05/test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | #include "HashTable(Separate_Chaining).h" 8 | #include "HashTable(Open_Addressing_Hashing).h" 9 | 10 | int main() 11 | { 12 | default_random_engine e(static_cast(time(nullptr))); 13 | uniform_int_distribution u(0, 10000); 14 | 15 | int arr[10000] = { 0 }; 16 | for (int i = 0; i < 10000; ++i) 17 | arr[i] = u(e); 18 | 19 | HashTable_SC tableSC(100); 20 | HashTable_OAH tableOAH(100); 21 | 22 | for (int i = 0; i < 10000; ++i) { 23 | tableSC.insert(arr[i]); 24 | tableOAH.insert(arr[i]); 25 | } 26 | 27 | cout << "SC size: " << tableSC.size() << ", find " << arr[5] << " ? " 28 | << boolalpha << tableSC.contains(arr[5]) << endl; 29 | cout << "OAH size: " << tableOAH.size() << ", find " << arr[5] << " ? " 30 | << boolalpha << tableOAH.contains(arr[5]) << endl; 31 | 32 | getchar(); 33 | return 0; 34 | } 35 | -------------------------------------------------------------------------------- /Chapter06/BinaryHeap.h: -------------------------------------------------------------------------------- 1 | #ifndef DS_CH06_BINARYHEAP_H 2 | #define DS_CH06_BINARYHEAP_H 3 | 4 | #include 5 | #include 6 | 7 | template 8 | class BinaryHeap { 9 | public: 10 | BinaryHeap() = delete; 11 | explicit BinaryHeap(int capacity) : arr(capacity), curSize(0) { } 12 | BinaryHeap(const vector &items) 13 | : arr(items.size() + 10), curSize(int(items.size())) 14 | { 15 | if (!empty()) { 16 | for (int i = 0; i < items.size(); ++i) 17 | arr[i + 1] = items[i]; 18 | buildHeap(); 19 | } 20 | } 21 | BinaryHeap(const BinaryHeap &rhs) : curSize(0), arr(100) { operator=(rhs); } 22 | ~BinaryHeap() = default; 23 | 24 | public: 25 | BinaryHeap &operator=(const BinaryHeap &rhs) 26 | { 27 | if (this != &rhs) { 28 | makeEmpty(); 29 | 30 | arr.resize(rhs.arr.capacity()); 31 | curSize = rhs.size(); 32 | for (int i = 0; i < rhs.size(); ++i) 33 | arr[i + 1] = rhs.arr[i + 1]; 34 | } 35 | return *this; 36 | } 37 | 38 | int size() const { return curSize; } 39 | bool empty() const { return size() == 0; } 40 | const T &findMin() const 41 | { 42 | if (empty()) 43 | throw std::underflow_error("当前堆为空"); 44 | return arr[1]; 45 | } 46 | 47 | void insert(const T &x) 48 | { 49 | if (curSize == arr.size() - 1) 50 | arr.resize(arr.size() << 1); 51 | 52 | //percolateUp 53 | int hole = ++curSize; 54 | //这样的操作避免了交换,只需替换 55 | for (; hole > 1 && x < arr[hole >> 1]; hole >>= 1) 56 | arr[hole] = arr[hole >> 1]; 57 | 58 | arr[hole] = x; 59 | } 60 | void deleteMin() 61 | { 62 | if (empty()) 63 | throw std::underflow_error("当前堆为空"); 64 | 65 | arr[1] = arr[curSize--]; 66 | percolateDown(1); 67 | } 68 | void deleteMin(T &minItem) //删除min并将min值返回给minItem 69 | { 70 | if (empty()) 71 | throw std::underflow_error("当前堆为空"); 72 | 73 | minItem = arr[1]; 74 | arr[1] = arr[curSize--]; 75 | percolateDown(1); 76 | } 77 | void makeEmpty() { curSize = 0; arr.clear(); } 78 | 79 | private: 80 | void buildHeap() 81 | { 82 | for (int i = curSize >> 1; i > 0; --i) 83 | percolateDown(i); 84 | } 85 | void percolateDown(int hole) 86 | { 87 | int child = 0; 88 | T tmp = arr[hole]; 89 | for (; (child = hole << 1) <= curSize; hole = child) { 90 | //下面if里的第一个判断保证最后一个节点是左儿子的情况下的正确性 91 | if (child != curSize && arr[child + 1] < arr[child]) 92 | ++child; 93 | if (arr[child] < tmp) 94 | arr[hole] = arr[child]; 95 | else 96 | break; 97 | } 98 | arr[hole] = tmp; 99 | } 100 | 101 | private: 102 | int curSize; 103 | vector arr; 104 | }; 105 | 106 | #endif // DS_CH06_BINARYHEAP_H 107 | -------------------------------------------------------------------------------- /Chapter06/BinomialQueue.h: -------------------------------------------------------------------------------- 1 | #ifndef DS_CH06_BINOMIALQUEUE_H 2 | #define DS_CH06_BINOMIALQUEUE_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | using std::cout; 9 | using std::endl; 10 | 11 | template 12 | class BinomialQueue { 13 | enum { DEFAULT_TREES = 1 }; 14 | private: 15 | struct BinomialNode { 16 | BinomialNode(const T &theElement, BinomialNode *lt, BinomialNode *rt) 17 | : element(theElement), leftChild(lt), nextSibling(rt) 18 | {} 19 | T element; 20 | BinomialNode *leftChild; 21 | BinomialNode *nextSibling; 22 | }; 23 | 24 | public: 25 | BinomialQueue() : curSize(0), theTrees(DEFAULT_TREES) { init(); } 26 | explicit BinomialQueue(const T &x) : curSize(0), theTrees(DEFAULT_TREES) 27 | { 28 | init(); 29 | ++curSize; 30 | theTrees[0] = new BinomialNode(x, nullptr, nullptr); 31 | } 32 | BinomialQueue(const BinomialQueue &rhs) { operator=(rhs); } 33 | ~BinomialQueue() 34 | { 35 | makeEmpty(); 36 | } 37 | 38 | public: 39 | BinomialQueue &operator=(const BinomialQueue &rhs) 40 | { 41 | if (this != &rhs) { 42 | makeEmpty(); 43 | curSize = rhs.curSize; 44 | theTrees.resize(rhs.capacity()); 45 | init(); 46 | for (int i = 0; i < rhs.curSize; ++i) { 47 | if (rhs.theTrees[i] != nullptr) 48 | theTrees[i] = clone(rhs.theTrees[i]); 49 | } 50 | } 51 | return *this; 52 | } 53 | 54 | void printForest() const 55 | { 56 | for (const auto t : theTrees) { 57 | if (t != nullptr) { 58 | printTree(t); 59 | cout << "=============================================" << endl; 60 | } 61 | } 62 | } 63 | 64 | bool empty() const { return curSize == 0; } 65 | const T &findMin() const 66 | { 67 | if (empty()) 68 | throw std::underflow_error("空二项队列"); 69 | return theTrees[findMinIndex()]->element; 70 | } 71 | 72 | void insert(const T &x) 73 | { 74 | merge(BinomialQueue(x)); 75 | } 76 | void deleteMin() 77 | { 78 | if (empty()) 79 | throw std::underflow_error("空二项队列"); 80 | 81 | int minIndex = findMinIndex(); 82 | BinomialNode *oldRoot = theTrees[minIndex]; 83 | BinomialNode *deletedTree = oldRoot->leftChild; 84 | delete oldRoot; 85 | 86 | // H'' 长度是min所在tree - 1(去掉min) 87 | BinomialQueue deletedQueue; 88 | deletedQueue.theTrees.resize(minIndex + 1); 89 | deletedQueue.curSize = (1 << minIndex) - 1; 90 | for (int j = minIndex - 1; j >= 0; --j) { 91 | deletedQueue.theTrees[j] = deletedTree; 92 | deletedTree = deletedTree->nextSibling; 93 | deletedQueue.theTrees[j]->nextSibling = nullptr; 94 | } 95 | 96 | // H' size是去掉H''和min 97 | theTrees[minIndex] = nullptr; 98 | curSize -= deletedQueue.curSize + 1; 99 | 100 | merge(deletedQueue); 101 | } 102 | void deleteMin(T &minItem) 103 | { 104 | if (empty()) 105 | throw std::underflow_error("空二项队列"); 106 | 107 | int minIndex = findMinIndex(); 108 | minItem = theTrees[minIndex]->element; 109 | 110 | BinomialNode *oldRoot = theTrees[minIndex]; 111 | BinomialNode *deletedTree = oldRoot->leftChild; 112 | delete oldRoot; 113 | 114 | // H'' 长度是min所在tree - 1(去掉min) 115 | BinomialQueue deletedQueue; 116 | deletedQueue.theTrees.resize(minIndex + 1); 117 | deletedQueue.curSize = (1 << minIndex) - 1; 118 | for (int j = minIndex - 1; j >= 0; --j) { 119 | deletedQueue.theTrees[j] = deletedTree; 120 | deletedTree = deletedTree->nextSibling; 121 | deletedQueue.theTrees[j]->nextSibling = nullptr; 122 | } 123 | 124 | // H' size是去掉H''和min 125 | theTrees[minIndex] = nullptr; 126 | curSize -= deletedQueue.curSize + 1; 127 | 128 | merge(deletedQueue); 129 | } 130 | 131 | void makeEmpty() 132 | { 133 | for (auto &t : theTrees) { 134 | deleteTree(t); 135 | } 136 | curSize = 0; 137 | } 138 | void merge(BinomialQueue &rhs) 139 | { 140 | if (this == &rhs) 141 | return; 142 | 143 | curSize += rhs.curSize; 144 | 145 | if (curSize > capacity()) { 146 | auto oldNumTrees = theTrees.size(); 147 | auto newNumTrees = std::max(theTrees.size(), rhs.theTrees.size()) + 1; 148 | theTrees.resize(newNumTrees); 149 | for (auto i = oldNumTrees; i < newNumTrees; ++i) 150 | theTrees[i] = nullptr; 151 | } 152 | 153 | BinomialNode *carry = nullptr; //用来进位 154 | for (int i = 0, j = 1; j <= curSize; ++i, j <<= 1) { 155 | BinomialNode *t1 = theTrees[i]; 156 | BinomialNode *t2 = i < rhs.theTrees.size() ? rhs.theTrees[i] : nullptr; 157 | 158 | int whichCase = t1 == nullptr ? 0 : 1; 159 | whichCase += t2 == nullptr ? 0 : 2; 160 | whichCase += carry == nullptr ? 0 : 4; 161 | 162 | switch (whichCase) { 163 | case 0: //空树 164 | case 1: //rhs空 165 | break; 166 | case 2: //this空 167 | theTrees[i] = t2; 168 | break; 169 | case 4: //只有carry 170 | theTrees[i] = carry; 171 | carry = nullptr; 172 | break; 173 | case 3: //this和rhs 174 | carry = combineTrees(t1, t2); 175 | theTrees[i] = rhs.theTrees[i] = nullptr; 176 | break; 177 | case 5: //this和carry 178 | carry = combineTrees(t1, carry); 179 | theTrees[i] = nullptr; 180 | break; 181 | case 6: //rhs和carry 182 | carry = combineTrees(t2, carry); 183 | rhs.theTrees[i] = nullptr; 184 | break; 185 | case 7: //都有 186 | theTrees[i] = carry; 187 | carry = combineTrees(t1, t2); 188 | rhs.theTrees[i] = nullptr; 189 | break; 190 | } 191 | } 192 | 193 | rhs.init(); 194 | } 195 | 196 | private: 197 | void printDepth(const T &x, int depth = 0) const 198 | { 199 | while (depth--) 200 | cout << "\t"; 201 | cout << "[" << x << "]" << endl; 202 | } 203 | void printTree(BinomialNode *t, int depth = 0) const 204 | { 205 | if (t == nullptr) 206 | return; // do nothing 207 | printTree(t->leftChild, depth + 1); 208 | printDepth(t->element, depth); //这句放的位置决定了前中后序遍历 209 | printTree(t->nextSibling, depth + 1); 210 | } 211 | void init() 212 | { 213 | curSize = 0; 214 | for (auto &t : theTrees) 215 | t = nullptr; 216 | } 217 | void deleteTree(BinomialNode *&t) 218 | { 219 | if (t != nullptr) { 220 | deleteTree(t->leftChild); 221 | deleteTree(t->nextSibling); 222 | delete t; 223 | t = nullptr; 224 | } 225 | } 226 | int findMinIndex() const 227 | { 228 | int i = 0, minIndex = 0; 229 | while (theTrees[i] == nullptr) 230 | ++i; 231 | for (minIndex = i; i < theTrees.size(); ++i) { 232 | if (theTrees[i] != nullptr && 233 | theTrees[i]->element < theTrees[minIndex]->element) { 234 | minIndex = i; 235 | } 236 | } 237 | return minIndex; 238 | } 239 | int capacity() const { return static_cast(theTrees.capacity()); } 240 | BinomialNode *combineTrees(BinomialNode *t1, BinomialNode *t2) 241 | { 242 | if (t2->element < t1->element) 243 | return combineTrees(t2, t1); 244 | t2->nextSibling = t1->leftChild; 245 | t1->leftChild = t2; 246 | return t1; 247 | } 248 | BinomialNode *clone(BinomialNode *t) const 249 | { 250 | if (t == nullptr) 251 | return nullptr; 252 | return new BinomialNode(t->element, clone(t->leftChild), clone(t->nextSibling)); 253 | } 254 | 255 | private: 256 | int curSize; 257 | vector theTrees; //若13个元素,则theTrees里是[0 0 0 0 1 1 0 1]存储 258 | }; 259 | 260 | #endif // DS_CH06_BINOMIALQUEUE_H 261 | -------------------------------------------------------------------------------- /Chapter06/LeftistHeap.h: -------------------------------------------------------------------------------- 1 | #ifndef DS_CH06_LEFTISTHEAP_H 2 | #define DS_CH06_LEFTISTHEAP_H 3 | 4 | #include 5 | #include 6 | using std::cout; 7 | using std::endl; 8 | 9 | template 10 | class LeftistHeap { 11 | private: 12 | struct LeftistNode { 13 | LeftistNode(const T &theEle, LeftistNode *lt = nullptr, 14 | LeftistNode *rt = nullptr, int np = 0) 15 | : ele(theEle), left(lt), right(rt), npl(np) {} 16 | LeftistNode *left; 17 | LeftistNode *right; 18 | int npl; 19 | T ele; 20 | }; 21 | 22 | public: 23 | LeftistHeap() : root(nullptr) {} 24 | LeftistHeap(const LeftistHeap &rhs) { operator=(rhs); } 25 | ~LeftistHeap() { makeEmpty(); } 26 | 27 | public: 28 | LeftistHeap &operator=(const LeftistHeap &rhs) 29 | { 30 | if (this != &rhs) { 31 | makeEmpty(); 32 | root = clone(rhs.root); 33 | } 34 | return *this; 35 | } 36 | 37 | bool empty() const { return root == nullptr; } 38 | const T &findMin() const { return root->ele; } //应保证非空调用 39 | 40 | void printTree() const { printTree(root); } 41 | //插入可以看成是合并一个单节点堆,时间界也是O(logN) 42 | void insert(const T &x) 43 | { 44 | root = merge(new LeftistNode(x), root); 45 | } 46 | //删除可以看成是删除根结点,再合并左右子树,时间界也是O(logN) 47 | void deleteMin() 48 | { 49 | if (empty()) 50 | throw std::underflow_error("当前堆为空"); 51 | 52 | LeftistNode *oldRoot = root; 53 | root = merge(root->left, root->right); 54 | delete oldRoot; 55 | } 56 | void deleteMin(T &minItem) 57 | { 58 | minItem = findMin(); 59 | deleteMin(); 60 | } 61 | void makeEmpty() { makeEmpty(root); } 62 | //合并两个堆的时间界为O(logN) 63 | void merge(LeftistHeap &rhs) 64 | { 65 | if (this != &rhs) { 66 | root = merge(root, rhs.root); 67 | rhs.makeEmpty(); 68 | } 69 | } 70 | 71 | private: 72 | void printDepth(const T &x, int depth = 0) const 73 | { 74 | while (depth--) 75 | cout << "\t"; 76 | cout << "[" << x << "]" << endl; 77 | } 78 | void printTree(LeftistNode *t, int depth = 0) const 79 | { 80 | if (t == nullptr) 81 | return; // do nothing 82 | printTree(t->left, depth + 1); 83 | printDepth(t->ele, depth); //这句放的位置决定了前中后序遍历 84 | printTree(t->right, depth + 1); 85 | } 86 | void makeEmpty(LeftistNode *&t) 87 | { 88 | if (t != nullptr) { 89 | makeEmpty(t->left); 90 | makeEmpty(t->right); 91 | delete t; 92 | } 93 | t = nullptr; 94 | } 95 | LeftistNode *clone(LeftistNode *t) const 96 | { 97 | if (t == nullptr) 98 | return nullptr; 99 | 100 | return new LeftistNode(t->ele, clone(t->left), clone(t->right), t->npl); 101 | } 102 | LeftistNode *merge(LeftistNode *h1, LeftistNode *h2) 103 | { 104 | if (h1 == nullptr) 105 | return h2; 106 | if (h2 == nullptr) 107 | return h1; 108 | return h1->ele < h2->ele ? merge1(h1, h2) : merge1(h2, h1); 109 | } 110 | LeftistNode *merge1(LeftistNode *h1, LeftistNode *h2) 111 | { 112 | if (h1->left == nullptr) { 113 | h1->left = h2; 114 | } else { 115 | h1->right = merge(h1->right, h2); 116 | if (h1->left->npl < h1->right->npl) 117 | swapChildren(h1); 118 | h1->npl = h1->right->npl + 1; 119 | } 120 | return h1; 121 | } 122 | void swapChildren(LeftistNode *t) 123 | { 124 | LeftistNode *temp = t->left; 125 | t->left = t->right; 126 | t->right = temp; 127 | } 128 | 129 | private: 130 | LeftistNode *root; 131 | }; 132 | 133 | #endif // DS_CH06_LEFTISTHEAP_H 134 | -------------------------------------------------------------------------------- /Chapter06/README.md: -------------------------------------------------------------------------------- 1 | # 优先队列(堆) 2 | 本章讨论优先队列(priority queue),介绍优先队列在离散事件模拟中的应用 3 | 作者评价:这类数据结构属于计算机科学中最雅致的一种 4 | 5 | ## 内容 6 | * 优先队列ADT的高效实现 7 | * 优先队列的使用 8 | * 优先队列的高级实现 9 | 10 | ## 二叉堆 (binary heap) 11 | 插入删除最坏O(logN),实际上插入花费常数平均时间,若无删除干扰,该结构将以线性时间建立一个具有N项的优先队列。 12 | 与二叉查找树一样,堆具有两个性质,堆的操作必须满足所有性质才能终止。 13 | 14 | ### 结构性质 15 | 堆是一棵**完全二叉树**(三角形缺右下角),特例是满二叉树(三角形),最底层元素必须从左往右填入,如有空缺则不是完全二叉树 16 | 一棵高为h的完全二叉树有[2^h , 2^(h+1) - 1]个节点,这意味着完全二叉树的高是 下取整(logN),显然它是O(logN)的 17 | 因为此规律,所以堆可以用数组表示而不用链表,对于数组中任一位置i上的元素,其左儿子在位置2i上,右儿子在左儿子后的(2i+1)上,它的父亲在位置 下取整(i/2) 上 18 | 19 | ### 堆序性质 20 | 在堆中,除根节点以外,每一个节点的值都大于(或等于)它的父节点的值 21 | 根据堆序性质,最小值总在根结点,因此可以以O(1)时间做findMin 22 | 相应地,通过改变堆序性质,也可以建立一个max堆,以O(1)时间做findMax 23 | 24 | ### 插入(上滤策略) 25 | 为了插入新元素X,在堆的下一个可用位置(为了满足结构性质)创建一个空穴,若X放入空穴仍满足堆序性质,则插入完成,否则交换空穴和其父节点,直到X被放入并满足堆序性质为止 26 | 27 | ### 删除(下滤策略) 28 | 找出最小元很容易,难的是删除它。 29 | 当删除一个最小元时,堆中最后一个元素X必须移动到该堆的某个地方。策略是在根节点建立一个空穴,然后将两个儿子中的较小者移入空穴,重复该步骤直到X可以被放入空穴中。代码中则是用X直接替换根结点的值,然后下滤。 30 | 31 | ### 注意 32 | 在堆的实现中经常出现的错误是,当堆中存在偶数个元素时,将出现一个节点只有一个儿子的情况。因此我们必须以节点不总有两个儿子为前提,这需要额外的测试。 33 | 34 | ### 应用 35 | #### 选择问题 36 | 输入N个元素及整数k,找出第k个最大的元素,极端情况是k=上取整(N/2),此时实际上是找中位数,以下两个算法都能在找中位数的情况下以O(NlogN)时间运行 37 | 38 | * A 将N个元素读入数组,对数组应用buildHeap,再执行k次deleteMin,最后根节点上的就是第k个最小值,构造一个最大堆就可以找到第k个最大值 39 | * B 用buildHeap将前k个元素构造成一个最大堆,若下一个元素大于堆里的最小值,则删除最小值,插入新元素,最终的最小值就是所求的第k个最大值 40 | 41 | ## d堆 42 | 类似B树,深度变浅,每个节点有d个儿子 43 | 44 | ## 左式堆 (leftist heap) 45 | 左式堆也是二叉树,但它不是理想平衡的,事实上是趋于非常不平衡 46 | 47 | 定义任一节点X的**零路径长(null path length)**npl(X)为从X到一个不具有两个儿子的节点的最短路径长 48 | 因此,具有0个或1个儿子的节点npl为0,而npl(NULL)=-1 49 | 注意,任一节点的npl比它儿子节点的npl的最小值多1 50 | 51 | ### 左式堆性质 52 | 对于堆中的每一个节点X,左儿子的npl至少与右儿子的npl一样大 53 | 这个性质导致树向左增加深度,沿左式堆右侧的右路径是堆中最短的路径 54 | 定理:在右路径上有r个节点的左式堆必然至少有2^r -1个节点 55 | 56 | 对左式堆的基本操作是合并。插入可以看成是合并一个单节点堆,删除即是删掉根结点,然后合并左右子树。 57 | 58 | ## 斜堆 (skew heap) 59 | 斜堆是左式堆的自调节形式,具有堆序,但不存在结构限制。斜堆不需要存储npl,每次合并无条件交换左右儿子。 60 | 61 | ## 二项队列 (binomial queue) 62 | 以最坏O(logN)支持插入、合并、deleteMin,插入操作平均花费常数时间 63 | 64 | 实质是由**二项树**(binomial tree)构成的**森林**(forest)。 65 | 每一个高度上最多存在一棵二项树。高度为k的二项树Bk是通过将一棵二项树B(k-1)附接到另一棵二项树B(k-1)的根上构成的。高度为k的二项树有2^k个节点,在深度d处的节点数是二项系数C(d,k) 66 | 67 | 如果把堆序性质施加到二项树上并允许任意高度上最多一棵二项树,则可以用二项树的集合唯一地表示任意大小的优先队列。如大小为13的优先队列可以用B3,B2,B0表示,可以写成1101,同时也是13的二进制形式。 68 | 69 | ### 操作 70 | 基本操作仍然是合并,思想是从小到大合并相同高度的二项树 71 | 插入是特殊情况下的合并 72 | deleteMin将原二项队列一分为二,再合并 73 | 74 | 编程需要注意**进位**的实现 -------------------------------------------------------------------------------- /Chapter06/test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | using namespace std; 6 | 7 | #include "BinaryHeap.h" 8 | #include "LeftistHeap.h" 9 | #include "BinomialQueue.h" 10 | 11 | int main() 12 | { 13 | const int maxSize = 13; 14 | default_random_engine e(static_cast(time(nullptr))); 15 | uniform_int_distribution u(1, maxSize); 16 | 17 | int arr[maxSize] = { 0 }; 18 | for (int i = 0; i < maxSize; ++i) 19 | arr[i] = u(e); 20 | 21 | BinaryHeap BHeap(maxSize); 22 | LeftistHeap LHeap; 23 | BinomialQueue BQ; 24 | 25 | for (int i = 0; i < maxSize; ++i) { 26 | BHeap.insert(arr[i]); 27 | LHeap.insert(arr[i]); 28 | BQ.insert(arr[i]); 29 | } 30 | 31 | cout << "BHeap size: " << BHeap.size() << ", min: " << BHeap.findMin() << endl; 32 | cout << "LHeap min: " << LHeap.findMin() << endl; 33 | cout << "BQ min: " << BQ.findMin() << endl; 34 | cout << "LHeap:" << endl; 35 | LHeap.printTree(); 36 | cout << endl << "BinomialQueue:" << endl; 37 | BQ.printForest(); 38 | BQ.deleteMin(); 39 | cout << "deleteMin:" << endl; 40 | BQ.printForest(); 41 | 42 | getchar(); 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /Chapter07/README.md: -------------------------------------------------------------------------------- 1 | # 排序 2 | 在内存里的排序称为内部排序,而在磁盘上的排序称为外部排序。 3 | 假设输入数据支持"<"和">"操作符,除赋值运算外,这种运算是仅有的允许对输入数据进行的操作,在此条件下的排序称为基于比较的排序。 4 | 5 | ## 内容 6 | 对内部排序的考查将指出: 7 | 8 | * 存在几种直观的算法以O(N^2)排序,如冒泡、选择、插入排序 9 | * 希尔排序编程简单,以o(N^2)运行,在实践中很有效 10 | * 还有一些稍微复杂的O(NlogN)算法 11 | * 任何只使用比较的排序算法在最坏情形下和平均情形下均需要Ω(NlogN)次比较 12 | 13 | ## 插入排序 (insertion sort) 14 | 插入排序由N-1趟(pass)排序组成,排序策略是,在第p趟,将位置p上的元素向左移动至它在前p+1个元素中的正确位置上。 15 | 16 | ### 分析 17 | O(N^2) 精确界,反序输入可达。 18 | 若已排序输入,则O(N) 19 | 平均情形Θ(N^2) 20 | 21 | ## 一些简单排序算法的下界 22 | 定理1 N个互异元素的数组的平均逆序数是N(N-1)/4 23 | 定理2 通过交换相邻元素进行排序的任何算法平均需要Ω(N^2)时间 24 | 对冒泡排序、选择排序、插入排序都有效 25 | 定理2告诉我们,为了以o(N^2)排序,必须执行比较,特别是要对相距较远的元素进行交换。排序通过删除逆序得以继续进行,为了有效进行,必须每次交换删除多个逆序。 26 | 27 | ## 希尔排序 (shell sort) 28 | 发明者是Donald Shell,该算法是冲破二次时间屏障的第一批算法之一,不过,直到它最初被发现的若干年后才证明了它的亚二次时间界。 29 | 30 | 它通过比较相距一定间隔的元素来工作,各趟比较所用的距离随着算法的进行而减小,直到只比较相邻元素的最后一趟排序为止。因此,希尔排序又是也叫做**缩减增量排序**(diminishing increment sort) 31 | 32 | ### 分析 33 | 使用希尔增量的最坏情形Θ(N^2) 34 | Hibbard增量:1,3,7,…… ,2^k - 1 35 | 使用Hibbard增量的最坏情形Θ(N^(3/2)) 36 | Sedgewick提出了几种增量序列,最坏情形时间O(N^(4/3)) 37 | 希尔排序的性能在实践中是可以接受的,由于编程简单,适度数量的输入数据经常选用。 38 | 39 | ## 堆排序 (heap sort) 40 | 如第六章所说,优先队列可以用O(NlogN)时间进行排序,基于该思想的算法称为堆排序 41 | 42 | 由数组建立N个元素的二叉堆花费O(N)时间,每次deleteMin花费O(logN),N次总共花费O(NlogN) 43 | 使用了附加数组,存储需求增加了一倍 44 | 45 | 避免使用附加数组的方法:每次deleteMin之后把min放到刚刚空出来的位置上,N次deleteMin之后,数组将是递减顺序,因此可以构建max堆 46 | 47 | 1. 以O(N)建立max堆 48 | 2. 交换最后一个和第一个元素,堆大小减1并下滤,相当于执行deleteMax 49 | 3. 循环执行步骤2,N-1次 50 | 51 | ### 分析 52 | 在最坏情形下堆排序最多使用2NlogN-O(N)次比较 53 | 堆排序非常稳定:它平均使用的比较只比最坏情形界指出的略少 54 | 55 | 定理1 对N个互异项的随机排列进行堆排序,所用的比较平均次数为2NlogN-O(NloglogN) 56 | 57 | 可以证明,堆排序总是至少使用NlogN-O(N)次比较,而且存在达到这个界的数据。似乎平均情形也应该是2NlogN-O(N)次比较(而不是定理1中的第二项),但目前无法证明 58 | 59 | ## 归并排序 (merge sort) 60 | 以最坏情形O(NlogN)时间运行,所使用的比较次数几乎是最优的,它是递归算法的一个很好的实例 61 | 62 | 算法的基本操作是合并两个已排序的表,取两个输入A、B,一个输出C,每次将A、B中的小者放入C,相关的位置推进,这显然是线性的 63 | 64 | ### 算法 65 | 基准情形:N=1时,结果是显然的 66 | 否则,递归地将前半部分和后半部分各自归并排序,再将两部分合并 67 | 68 | 该算法是经典的**分治**策略,它将问题**分**(divide)成一些小问题然后递归求解,而**治**(conquering)的阶段则是将分的阶段解得的各答案合并在一起 69 | 70 | ### 分析 71 | 分析递归例程技巧的经典实例:必须给运行时间写出一个递推关系。 72 | 假设N是2的幂,从而总可以将它分裂成相等的两部分。对于N=1,所用时间是常数,将其记为1。则有 73 | T(1) = 1 74 | T(N) = 2T(N/2) + N 75 | 求解得 T(N) = NlogN + N = O(NlogN) 76 | 77 | 利弊:在java中比较耗时多于移动,因此在java中归并排序是一般目的排序的最佳选择;但在C++中,比较耗时少而复制对象代价很大,因此实践中不常用 78 | 79 | ## 快速排序 (quick sort) 80 | 快排是实践中最快的已知排序算法,平均运行时间是O(NlogN),最坏情形是O(N^2),但稍作努力就可避免。 81 | 通过将堆排序与快速排序结合,可以在堆排序O(NlogN)最坏运行时间下,得到几乎所有输入的最快运行时间。 82 | 83 | 快排也是分治的递归算法,排序数组S步骤如下: 84 | 85 | 1. 若S中元素数是0或1,则返回 86 | 2. 取S中任一元素v,称之为**枢纽元**(pivot) 87 | 3. 将S-{v}(S中其余元素)**划分**成两个不相交的集合:S1={x∈S-{v}|x≤v}和S2={x∈S-{v}|x≥v} 88 | 4. 返回{quickSort(S1),后跟v,继而quickSort(S2)} 89 | 90 | 第三步中划分的标准不是唯一的,因此这就成了设计决策。一部分好的实现方法是将这种情形尽可能有效地处理。直观地看,我们希望枢纽元能将元素对半分,一半在S1,另一半在S2。 91 | 92 | ### 选取枢纽元 93 | 1. 一种典型的错误是将第一个元素选作枢纽元。若输入随机,那么这是可以接受的,但实际情况有很多预排序的序列,这样的分割是劣质的。类似的还有选取前2个元素的大者,这是一样的,不要使用。 94 | 2. 一种安全的做法是随机选取枢纽元,但这取决于随机数生成器的质量,而且声称随机数的代价一般也是很昂贵的。 95 | 3. 三数中值分割法 96 | 一组N个数的中值是第上取整(N/2)个最大的数。枢纽元的最好选择是数组的中值,但算出中值代价太高。一般的做法是选取左端、右端和中心位置上的三个元素的中值作为枢纽元。显然该方法消除了预排序输入的不好情形,并且减少了约14%的比较次数。 97 | 98 | ### 分割策略 99 | 1. 将枢纽元与最后的元素交换 100 | 2. i从第一个元素开始,j从倒数第二个元素开始 101 | 3. 当i在j左边时,右移i,移过小于枢纽元的元素,j左移,移过大于枢纽元的元素,i,j都停止时交换两个元素,直到i,j交错 102 | 4. 将枢纽元与i所指向的元素交换 103 | 104 | 如何处理等于枢纽元的元素? 105 | 若等于,则停止移动 106 | 107 | ### 小数组 108 | 对于很小的数组(N≤20),快速排序不如插入排序,而且,因为快排是递归的,这样的情形经常发生。通常的解决办法是,对于小数组使用插入排序。一种好的截止范围(cutoff range)是N=10 109 | 110 | ### 分析 111 | 最坏情形:O(N^2) 112 | 最佳情形:O(NlogN) 113 | 平均情形:O(NlogN) 114 | 115 | ## 快速选择 (quick select) 116 | 修改快速排序以解决选择问题,即找第k个最大(小)元。 117 | 118 | 前3步和快速排序一样 119 | 第4步 120 | 121 | * 若k≤S1,那么k必然在S1中,返回quickSelect(S1, K) 122 | * 若k = 1 + |S1|,那么枢纽元就是第k个最小元 123 | * 否则,第k个最小元就在S2中,它是S2中的第(k-|S1|-1)个最小元,返回quickSelect(S2, k-|S1|-1) 124 | 125 | ### 分析 126 | 与快排相比,快速选择只进行了一次递归调用而不是两次 127 | 128 | 最坏情形:O(N^2),当S1和S2一个是空时 129 | 平均情形:O(N) -------------------------------------------------------------------------------- /Chapter07/Sort.h: -------------------------------------------------------------------------------- 1 | #ifndef DS_CH07_SORT_H 2 | #define DS_CH07_SORT_H 3 | 4 | #include 5 | #include 6 | 7 | //插入排序 8 | template 9 | void insertionSort(vector &a) 10 | { 11 | int j = 0; 12 | T tmp; 13 | 14 | for (int p = 1; p < a.size() ; ++p) { 15 | tmp = a[p]; 16 | for (j = p; j > 0 && tmp < a[j - 1]; --j) { 17 | a[j] = a[j - 1]; 18 | } 19 | a[j] = tmp; 20 | } 21 | } 22 | 23 | template 24 | void insertionSort(vector &a, int left, int right) 25 | { 26 | int j = 0; 27 | T tmp; 28 | 29 | for (int p = left + 1; p < right + 1; ++p) { 30 | tmp = a[p]; 31 | for (j = p; j > 0 && tmp < a[j - 1]; --j) { 32 | a[j] = a[j - 1]; 33 | } 34 | a[j] = tmp; 35 | } 36 | } 37 | 38 | //希尔排序 使用Shell增量 39 | template 40 | void shellSort(vector &a) 41 | { 42 | int i = 0, j = 0; 43 | T tmp; 44 | 45 | for (int gap = static_cast(a.size() >> 1); gap > 0; gap >>= 1) { 46 | for (i = gap; i < a.size(); ++i) { 47 | tmp = a[i]; 48 | for (j = i; j >= gap && tmp < a[j - gap]; j -= gap) { 49 | a[j] = a[j - gap]; 50 | } 51 | a[j] = tmp; 52 | } 53 | } 54 | } 55 | 56 | //堆排序 不使用附加数组 57 | inline int leftChild(int i) 58 | { 59 | return (i << 1) + 1; 60 | } 61 | 62 | template 63 | void percolateDown(vector &a, int hole, int n) 64 | { 65 | int child = 0; 66 | T tmp; 67 | for (tmp = a[hole]; leftChild(hole) < n; hole = child) { 68 | child = leftChild(hole); 69 | //下面if里的第一个判断保证最后一个节点是左儿子的情况下的正确性 70 | if (child != n - 1 && a[child] < a[child + 1]) 71 | ++child; 72 | if (tmp < a[child]) 73 | a[hole] = a[child]; 74 | else 75 | break; 76 | } 77 | a[hole] = tmp; 78 | } 79 | 80 | template 81 | void heapSort(vector &a) 82 | { 83 | //buildheap 84 | for (int i = static_cast(a.size() >> 1); i >= 0; --i) 85 | percolateDown(a, i, static_cast(a.size())); 86 | 87 | //deleteMax 88 | for (int i = static_cast(a.size() - 1); i > 0; --i) { 89 | std::swap(a[0], a[i]); 90 | percolateDown(a, 0, i); 91 | } 92 | } 93 | 94 | //归并排序 递归实现 95 | template 96 | void merge(vector &a, vector &tmp, int leftPos, int rightPos, int rightEnd) 97 | { 98 | //进入这里的时候,A,B均已排序 99 | //A的范围[leftPos, leftEnd], B的范围[rightPos, rightEnd] 100 | int leftEnd = rightPos - 1; 101 | int tmpPos = leftPos; 102 | int numElements = rightEnd - leftPos + 1; 103 | 104 | //A,B里的小者放入C 相关下标推进 105 | while (leftPos <= leftEnd && rightPos <= rightEnd) { 106 | if (a[leftPos] <= a[rightPos]) 107 | tmp[tmpPos++] = a[leftPos++]; 108 | else 109 | tmp[tmpPos++] = a[rightPos++]; 110 | } 111 | 112 | while (leftPos <= leftEnd) //拷贝左半边 113 | tmp[tmpPos++] = a[leftPos++]; 114 | 115 | while (rightPos <= rightEnd) //或者拷贝右半边 116 | tmp[tmpPos++] = a[rightPos++]; 117 | 118 | for (int i = 0; i < numElements; ++i, --rightEnd) 119 | a[rightEnd] = tmp[rightEnd]; //拷贝回去 120 | } 121 | 122 | template 123 | void mergeSort(vector &a, vector &tmp, int left, int right) 124 | { 125 | if (left < right) { 126 | int center = (left + right) >> 1; 127 | mergeSort(a, tmp, left, center); 128 | mergeSort(a, tmp, center + 1, right); 129 | merge(a, tmp, left, center + 1, right); 130 | } 131 | } 132 | 133 | template 134 | void mergeSort(vector &a) 135 | { 136 | vector tmp(a.size()); 137 | mergeSort(a, tmp, 0, static_cast(a.size() - 1)); 138 | } 139 | 140 | //ex 7.16 归并排序的非递归实现 141 | template 142 | void mergeSort2(vector &a) 143 | { 144 | int n = static_cast(a.size()); 145 | vector tmp(n); 146 | int part1Start = 0, part2Start = 0, part2End = 0; 147 | 148 | for (int subListSize = 1; subListSize < n ; subListSize <<= 1) { 149 | part1Start = 0; 150 | while (part1Start + subListSize < n - 1) { 151 | part2Start = part1Start + subListSize; 152 | part2End = std::min(n - 1, part2Start + subListSize - 1); 153 | merge(a, tmp, part1Start, part2Start, part2End); 154 | part1Start = part2End + 1; 155 | } 156 | } 157 | } 158 | 159 | //快速排序 160 | template 161 | const T &median3(vector &a, int left, int right) 162 | { 163 | int center = (left + right) >> 1; 164 | if (a[center] < a[left]) 165 | std::swap(a[left], a[center]); 166 | if (a[right] < a[left]) 167 | std::swap(a[left], a[right]); 168 | if (a[right] < a[center]) 169 | std::swap(a[right], a[center]); 170 | 171 | std::swap(a[center], a[right - 1]); 172 | return a[right - 1]; 173 | } 174 | 175 | template 176 | void quickSort(vector &a, int left, int right) 177 | { 178 | if (left + 10 <= right) { 179 | T pivot = median3(a, left, right); 180 | 181 | int i = left, j = right - 1; 182 | for (;;) { 183 | while (a[++i] < pivot) {} 184 | while (pivot < a[--j]) {} 185 | if (i < j) 186 | std::swap(a[i], a[j]); 187 | else 188 | break; 189 | } 190 | 191 | std::swap(a[i], a[right - 1]); 192 | 193 | quickSort(a, left, i - 1); 194 | quickSort(a, i + 1, right); 195 | } else { 196 | insertionSort(a, left, right); 197 | } 198 | } 199 | 200 | template 201 | void quickSort(vector &a) 202 | { 203 | quickSort(a, 0, static_cast(a.size() - 1)); 204 | } 205 | 206 | //快速选择 执行完后第k个最小元是a[k-1] 207 | template 208 | void quickSelect(vector &a, int left, int right, int k) 209 | { 210 | if (left + 10 <= right) { 211 | T pivot = median3(a, left, right); 212 | 213 | int i = left, j = right - 1; 214 | for (;;) { 215 | while (a[++i] < pivot) {} 216 | while (pivot < a[--j]) {} 217 | if (i < j) 218 | std::swap(a[i], a[j]); 219 | else 220 | break; 221 | } 222 | 223 | std::swap(a[i], a[right - 1]); 224 | 225 | if (k <= i) 226 | quickSelect(a, left, i - 1, k); 227 | else if (k > i + 1) 228 | quickSelect(a, i + 1, right, k); 229 | } else { 230 | insertionSort(a, left, right); 231 | } 232 | } 233 | 234 | #endif // DS_CH07_SORT_H 235 | -------------------------------------------------------------------------------- /Chapter07/Timer.h: -------------------------------------------------------------------------------- 1 | #ifndef DS_CH07_TIMER_H 2 | #define DS_CH07_TIMER_H 3 | 4 | #include 5 | using namespace std::chrono; 6 | 7 | class Timer { 8 | public: 9 | using s = seconds; 10 | using ms = milliseconds; 11 | using us = microseconds; 12 | using ns = nanoseconds; 13 | 14 | public: 15 | Timer() : tpStart(high_resolution_clock::now()), tpStop(tpStart) {} 16 | 17 | public: 18 | void start() { tpStart = high_resolution_clock::now(); } 19 | void stop() { tpStop = high_resolution_clock::now(); } 20 | 21 | template 22 | auto delta() const { return duration_cast(tpStop - tpStart).count(); } 23 | 24 | template 25 | auto stop_delta() { stop(); return duration_cast(tpStop - tpStart).count(); } 26 | 27 | template 28 | auto stop_delta_start() 29 | { 30 | stop(); 31 | auto ts = duration_cast(tpStop - tpStart).count(); 32 | start(); 33 | return ts; 34 | } 35 | 36 | private: 37 | time_point tpStart; 38 | time_point tpStop; 39 | }; 40 | 41 | #endif // DS_CH07_TIMER_H 42 | -------------------------------------------------------------------------------- /Chapter07/test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace std; 5 | 6 | #include "Timer.h" 7 | #include "Sort.h" 8 | 9 | #define PRINT_SORT_TIME(A, sortfun) \ 10 | {\ 11 | vector arrt(A);\ 12 | Timer ts;\ 13 | sortfun(arrt);\ 14 | cout << #sortfun##" " << maxSize << " 个随机数用时: " << ts.stop_delta() << " ms" << endl;\ 15 | } 16 | 17 | int main() 18 | { 19 | const int maxSize = 10000; 20 | default_random_engine e(static_cast(time(nullptr))); 21 | uniform_int_distribution u(1, maxSize); 22 | 23 | vector arr; 24 | for (int i = 0; i < maxSize; ++i) 25 | arr.push_back(u(e)); 26 | 27 | if (maxSize <= 1000) 28 | PRINT_SORT_TIME(arr, insertionSort); 29 | PRINT_SORT_TIME(arr, shellSort); 30 | PRINT_SORT_TIME(arr, heapSort); 31 | PRINT_SORT_TIME(arr, mergeSort); 32 | PRINT_SORT_TIME(arr, mergeSort2); 33 | PRINT_SORT_TIME(arr, quickSort); 34 | { 35 | vector arr1(arr); 36 | quickSelect(arr1, 0, maxSize - 1, maxSize >> 1); 37 | cout << "the median num is: " << arr1[(maxSize >> 1) - 1] << endl; 38 | } 39 | 40 | getchar(); 41 | return 0; 42 | } -------------------------------------------------------------------------------- /Chapter08/DisjSets.h: -------------------------------------------------------------------------------- 1 | #ifndef DS_CH08_DISJSETS_H 2 | #define DS_CH08_DISJSETS_H 3 | 4 | #include 5 | 6 | class DisjSets { 7 | public: 8 | explicit DisjSets(int numElements) : s(numElements, -1) {} 9 | 10 | int find(int x) const 11 | { 12 | if (s[x] < 0) 13 | return x; 14 | else 15 | return find(s[x]); 16 | } 17 | //路径压缩 18 | int find(int x) 19 | { 20 | if (s[x] < 0) 21 | return x; 22 | else 23 | return s[x] = find(s[x]); 24 | } 25 | //按高度求并 初始为-1,存储高度-1 26 | void unionSets(int root1, int root2) 27 | { 28 | if (s[root2] < s[root1]) 29 | s[root1] = root2; 30 | else { 31 | if (s[root1] == s[root2]) 32 | --s[root1]; //更新高度 33 | s[root2] = root1; 34 | } 35 | } 36 | 37 | private: 38 | std::vector s; 39 | }; 40 | 41 | #endif // DS_CH08_DISJSETS_H 42 | -------------------------------------------------------------------------------- /Chapter08/README.md: -------------------------------------------------------------------------------- 1 | # 不相交集类 2 | 这一章介绍解决等价问题的一种有效数据结构。实现简单,也非常快,每种操作只需要常数平均时间。 3 | 4 | ## 等价关系 (equivalence relation) 5 | 若对于每一对元素(a,b),a,b∈S, `a R b`或者为true或者为false,则称在集合S上定义关系R。如果`a R b`为true,我们说a和b有关系。 6 | 7 | **等价关系**是满足下列三个性质的关系R: 8 | 9 | 1. 自反性:对于所有的a∈S,`a R a` 10 | 2. 对称性:`a R b`当且仅当`b R a` 11 | 3. 传递性:若`a R b`且b R c则`a R c` 12 | 13 | 元素a∈S的**等价类**(equivalence class)是S的子集,它包含所有与a有(等价)关系的元素。注意,等价类形成对S的一个划分:S的每一个成员恰好出现在一个等价类中。为确定是否a~b,我们只需验证a和b是否都在同一个等价类中。 14 | 15 | 输入数据最初是N个集合(collection)的类,每个集合含有一个元素。初始的描述是所有的关系均为false(自反的关系除外)。每个集合都有一个不同的元素,从而`Si∩Sj=⊙`,称为**不相交**(disjoint) 16 | 17 | 基本操作有两种,称为**求并/查找**(union/find)算法。 18 | 19 | ## 灵巧求并算法 20 | 直观的union操作相当随意,它简单地通过使第二棵树成为第一棵树的子树而完成合并。对其进行简单改进,使得总是较小的树成为较大的树的子树,称为**按大小求并**(union by size),它保证树的深度最大是O(logN)。 21 | 连续M次操作平均需要O(M)时间。 22 | 23 | 另一种方法是**按高度求并**(union by height),它同样保证树的深度最大是O(logN)。做法是使浅的树成为深的树的子树。 24 | 25 | ## 一个应用 26 | 应用求并/查找数据结构的一个例子是迷宫的生成。初始化时所有格子都在自己的等价类中,之后不断合并,最终生成迷宫。 -------------------------------------------------------------------------------- /Chapter08/test.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | 4 | #include "DisjSets.h" 5 | 6 | int main() 7 | { 8 | DisjSets One(5000); 9 | 10 | int root1 = 0, root2 = 0; 11 | for (int i = 10; i < 1000; ++i) { 12 | root1 = One.find(2 * i + 1); 13 | root2 = One.find(3 * i + 1); 14 | One.unionSets(root1, root2); 15 | } 16 | for (int i = 0; i < 1000; ++i) { 17 | cout << i << " : " << One.find(i) << endl; 18 | } 19 | 20 | getchar(); 21 | return 0; 22 | } -------------------------------------------------------------------------------- /Data-Structure.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Data-Structure", "Data-Structure.vcxproj", "{BB2BB86B-D2ED-42B0-AF95-C1F72D5031E6}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {BB2BB86B-D2ED-42B0-AF95-C1F72D5031E6}.Debug|x64.ActiveCfg = Debug|x64 17 | {BB2BB86B-D2ED-42B0-AF95-C1F72D5031E6}.Debug|x64.Build.0 = Debug|x64 18 | {BB2BB86B-D2ED-42B0-AF95-C1F72D5031E6}.Debug|x86.ActiveCfg = Debug|Win32 19 | {BB2BB86B-D2ED-42B0-AF95-C1F72D5031E6}.Debug|x86.Build.0 = Debug|Win32 20 | {BB2BB86B-D2ED-42B0-AF95-C1F72D5031E6}.Release|x64.ActiveCfg = Release|x64 21 | {BB2BB86B-D2ED-42B0-AF95-C1F72D5031E6}.Release|x64.Build.0 = Release|x64 22 | {BB2BB86B-D2ED-42B0-AF95-C1F72D5031E6}.Release|x86.ActiveCfg = Release|Win32 23 | {BB2BB86B-D2ED-42B0-AF95-C1F72D5031E6}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /Data-Structure.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | {BB2BB86B-D2ED-42B0-AF95-C1F72D5031E6} 29 | Data-Structure 30 | 8.1 31 | 32 | 33 | 34 | Application 35 | true 36 | v140 37 | MultiByte 38 | 39 | 40 | Application 41 | false 42 | v140 43 | true 44 | MultiByte 45 | 46 | 47 | Application 48 | true 49 | v140 50 | MultiByte 51 | 52 | 53 | Application 54 | false 55 | v140 56 | true 57 | MultiByte 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | Level3 81 | Disabled 82 | true 83 | 84 | 85 | 86 | 87 | Level3 88 | Disabled 89 | true 90 | 91 | 92 | 93 | 94 | Level3 95 | MaxSpeed 96 | true 97 | true 98 | true 99 | 100 | 101 | true 102 | true 103 | 104 | 105 | 106 | 107 | Level3 108 | MaxSpeed 109 | true 110 | true 111 | true 112 | 113 | 114 | true 115 | true 116 | 117 | 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /Data-Structure.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | 头文件 20 | 21 | 22 | 23 | 24 | 源文件 25 | 26 | 27 | -------------------------------------------------------------------------------- /Data-Structure.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | true 5 | 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. 166 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Data-Structure 2 | 3 | ## 代码内容 4 | 《数据结构与算法分析C++描述拆分》上的代码实现, 5 | 按照该书的章节顺序,主要实现书上给出的例子,包括部分课后习题。 6 | 7 | ## 文件内容 8 | 所有实现均~~计划~~给出`.h` `.cpp`文件以及部分用于测试的`test.cpp`文件 9 | 10 | ## 环境 11 | - Windows 10 \& 8.1 12 | - Visual Studio 2015 with update 3 13 | - C++ (部分C++11语法) 14 | 15 | ## 章节列表 16 | ### 第一章 引论 & 第二章 算法分析 17 | :white_check_mark: 部分课后习题+简单例程 18 | ### 第三章 表、栈和队列 19 | :white_check_mark: Vector和List 20 | :white_check_mark: 链表 21 | :white_check_mark: 栈 22 | :white_check_mark: 队列 23 | ### 第四章 树 24 | :white_check_mark: 二叉查找树 25 | :white_check_mark: AVL树 26 | ### 第五章 散列 27 | :white_check_mark: 哈希表(分离链接法) 28 | :white_check_mark: 哈希表(开放定址法/平方探测) 29 | ### 第六章 优先队列(堆) 30 | :white_check_mark: 二叉堆 31 | :white_check_mark: 左式堆 32 | :white_check_mark: 二项队列 33 | ### 第七章 排序 34 | :white_check_mark: 插入排序 35 | :white_check_mark: 希尔排序 36 | :white_check_mark: 堆排序 37 | :white_check_mark: 归并排序 38 | :white_check_mark: 快速排序 39 | :white_check_mark: 快速选择 40 | ### 第八章 不相交集类 41 | :white_check_mark: 不相交集 42 | ### 第九章 图论算法 43 | :white_large_square: 邻接表(Version 1,2) 44 | :white_large_square: 拓扑排序(Version 1,2) 45 | :white_large_square: 单源最短路径算法 46 | :white_large_square: 最大网络流 47 | :white_large_square: 最小生成树 48 | :white_large_square: 深度优先搜索 49 | :white_large_square: 双连通性 50 | :white_large_square: 欧拉回路 51 | ### 第十章 算法设计技巧 52 | :white_large_square: 分治算法:最近点问题 53 | :white_large_square: 动态规划:斐波那契数列,递归关系,矩阵乘法顺序,最优搜索二叉树 54 | :white_large_square: 随机化算法:跳表 55 | :white_large_square: 回溯法:收费公路重建,三连棋游戏(带AI) 56 | ### 第十二章 高级数据结构及其实现 57 | :white_large_square: 红黑树:自顶向下插入,自顶向下删除 58 | :white_large_square: AA树 59 | :white_large_square: Treap树 60 | :white_large_square: Kd树 61 | :white_large_square: 配对堆 --------------------------------------------------------------------------------