第 1 章 緒論
時間複雜度
空間複雜度
第 2 章 線性表
概念
順序表
單鏈表
#include<iostream> using namespace std; //構建一個節點類 template<typename DataType> class Node { public: DataType data; //數據域 Node<DataType>* next; //指針域 Node<DataType>() { data = 0; next = nullptr; } }; //構建一個單鏈表類 template<class DataType> class LinkList { private: Node<DataType>* head; //頭結點 public: LinkList(); //構建 ~LinkList(); //銷燬 void CreateLinkList(int n); //建立 void TraversalLinkList(); //遍歷 int GetLength(); //獲取長度 bool IsEmpty(); //判斷是否爲空 Node<DataType>* Find(DataType data); //查找節點 void InsertElemAtEnd(DataType data); //在尾部插入指定的元素 void InsertElemAtIndex(DataType data, int location); //在指定位置插入指定元素 void InsertElemAtHead(DataType data); //在頭部插入指定元素 void DeleteElemAtEnd(); //在尾部刪除元素 void DeleteAll(); //刪除全部數據 void DeleteElemAtPoint(DataType data); //刪除指定的數據 void DeleteElemAtHead(); //在頭部刪除節點 }; //初始化單鏈表 template<class DataType> LinkList<DataType>::LinkList() { head = new Node<DataType>; head->data = 0; head->next = NULL; } //銷燬單鏈表 template<class DataType> LinkList<DataType>::~LinkList() { delete head; } //建立一個單鏈表 template<class DataType> void LinkList<DataType>::CreateLinkList(int n) { Node<DataType>* pnew = nullptr; Node<DataType>* temp = head; if (n < 0) { //處理異常 cout << "輸入的節點個數有誤" << endl; exit(EXIT_FAILURE); } for (int i = 0; i < n; i++) { pnew = new Node<DataType>; cout << "請輸入第" << i + 1 << "個值: "; cin >> pnew->data; pnew->next = NULL; temp->next = pnew; temp = temp->next; } } //遍歷單鏈表 template<class DataType> void LinkList<DataType>::TraversalLinkList() { if (head->next == NULL) { cout << "鏈表爲空表" << endl; } Node<DataType>* p = head; while (p->next != NULL) { p = p->next; cout << p->data << " "; } } //獲取單鏈表的長度 template<class DataType> int LinkList<DataType>::GetLength() { int count = 0; Node<DataType>* p = head->next; while (p != NULL) { count++; p = p->next; } return count; } //判斷單鏈表是否爲空 template<class DataType> bool LinkList<DataType>::IsEmpty() { if (head->next == NULL) { return true; } return false; } //查找節點 template<class DataType> Node<DataType>* LinkList<DataType>::Find(DataType data) { Node<DataType>* p = head; if (p->next == nullptr) { //當爲空表時,報異常 cout << "此鏈表爲空鏈表" << endl; return nullptr; } else { while (p->next != NULL) { p = p->next; if (p->data == data) { return p; } } return nullptr; } } //在尾部插入指定的元素 template<class DataType> void LinkList<DataType>::InsertElemAtEnd(DataType data) { Node<DataType>* newNode = new Node<DataType>; newNode->data = data; Node<DataType>* p = head; if (head == NULL) { //當頭結點爲空時,設置newNode爲頭結點 head = newNode; } else //循環知道最後一個節點,將newNode放置在最後 { while (p->next != NULL) { p = p->next; } p->next = newNode; } } //在指定位置插入指定元素 template<class DataType> void LinkList<DataType>::InsertElemAtIndex(DataType data, int n) { if (n<1 || n>GetLength()) cout << "輸入的值錯誤" << endl; else { Node<DataType>* ptemp = new Node<DataType>; ptemp->data = data; Node<DataType>* p = head; int i = 1; while (n > i) //遍歷到指定的位置 { p = p->next; i++; } ptemp->next = p->next; //將新節點插入到指定位置 p->next = ptemp; } } //在頭部插入指定元素 template<class DataType> void LinkList<DataType>::InsertElemAtHead(DataType data) { Node<DataType>* newNode = new Node<DataType>; newNode->data = data; Node<DataType>* p = head; //定義指針p指向頭結點 if (head == NULL) { //當頭結點爲空時,設置newNode爲頭結點 head = newNode; } newNode->next = p->next; //將新節點插入到指定位置 p->next = newNode; } //在尾部刪除元素 template<class DataType> void LinkList<DataType >::DeleteElemAtEnd() { Node<DataType>* p = head; //建立一個指針指向頭結點 Node<DataType>* ptemp = NULL; //建立一個佔位節點 if (p->next == NULL) { //判斷鏈表是否爲空 cout << "單鏈表空" << endl; } else { while (p->next != NULL) //循環到尾部的前一個 { ptemp = p; //將ptemp指向尾部的前一個節點 p = p->next; //p指向最後一個節點 } delete p; //刪除尾部節點 p = NULL; ptemp->next = NULL; } } //刪除全部數據 template<class DataType> void LinkList<DataType>::DeleteAll() { Node<DataType>* p = head->next; Node<DataType>* ptemp = new Node<DataType>; while (p != NULL) //在頭結點的下一個節點逐個刪除節點 { ptemp = p; p = p->next; head->next = p; ptemp->next = NULL; delete ptemp; } head->next = NULL; //頭結點的下一個節點指向NULL } //刪除指定的數據 template<class DataType> void LinkList<DataType>::DeleteElemAtPoint(DataType data) { Node<DataType>* ptemp = Find(data); //查找到指定數據的節點位置 if (ptemp == head->next) { //判斷是否是頭結點的下一個節點,若是是就從頭部刪了它 DeleteElemAtHead(); } else { Node<DataType>* p = head; //p指向頭結點 while (p->next != ptemp) //p循環到指定位置的前一個節點 { p = p->next; } p->next = ptemp->next; //刪除指定位置的節點 delete ptemp; ptemp = NULL; } } //在頭部刪除節點 template<class DataType> void LinkList<DataType>::DeleteElemAtHead() { Node<DataType>* p = head; if (p == NULL || p->next == NULL) { //判斷是否爲空表,報異常 cout << "該鏈表爲空表" << endl; } else { Node<DataType>* ptemp = NULL; //建立一個佔位節點 p = p->next; ptemp = p->next; //將頭結點的下下個節點指向佔位節點 delete p; //刪除頭結點的下一個節點 p = NULL; head->next = ptemp; //頭結點的指針更換 } } #include"seqList.h" //測試函數 int main() { LinkList<int> linklist; int i; cout << "1.建立單鏈表 " << endl; cout << "2.遍歷單鏈表 " << endl; cout << "3.獲取單鏈表的長度 " << endl; cout << "4.判斷單鏈表是否爲空 " << endl; cout << "5.獲取節點 " << endl; cout << "6.在尾部插入指定元素 " << endl; cout << "7.在指定位置插入指定元素 " << endl; cout << "8.在頭部插入指定元素 " << endl; cout << "9.在尾部刪除元素" << endl; cout << "10.刪除全部元素 " << endl; cout << "11.刪除指定元素 " << endl; cout << "12.在頭部刪除元素" << endl; cout << "0.退出 " << endl; do { cout << "請輸入要執行的操做: "; cin >> i; switch (i) { case 1: int n; cout << "請輸入單鏈表的長度: "; cin >> n; linklist.CreateLinkList(n); break; case 2: linklist.TraversalLinkList(); break; case 3: cout << "該單鏈表的長度爲" << linklist.GetLength() << endl; break; case 4: if (linklist.IsEmpty() == 1) cout << "該單鏈表是空表" << endl; else { cout << "該單鏈表不是空表" << endl; } break; case 5: int data; cout << "請輸入要獲取節點的值: "; cin >> data; cout << "該節點的值爲" << linklist.Find(data)->data << endl; break; case 6: int endData; cout << "請輸入要在尾部插入的值: "; cin >> endData; linklist.InsertElemAtEnd(endData); break; case 7: int pointData; int index; cout << "請輸入要插入的數據: "; cin >> pointData; cout << "請輸入要插入數據的位置: "; cin >> index; linklist.InsertElemAtIndex(pointData, index); break; case 8: int headData; cout << "請輸入要在頭部插入的值: "; cin >> headData; linklist.InsertElemAtHead(headData); break; case 9: linklist.DeleteElemAtEnd(); break; case 10: linklist.DeleteAll(); break; case 11: int pointDeleteData; cout << "請輸入要刪除的數據: "; cin >> pointDeleteData; linklist.DeleteElemAtPoint(pointDeleteData); break; case 12: linklist.DeleteElemAtHead(); break; default: break; } } while (i != 0); system("pause"); return 0; }
雙鏈表
#include<iostream> using namespace std; //構建一個節點類 template<typename DataType> class Node { public: DataType data; Node<DataType>* prev; Node<DataType>* next; Node<DataType>() { data = 0; prev = next = nullptr; } Node<DataType>(DataType data, Node <DataType>* prev, Node<DataType>* next) { this->data = data; this->prev = prev; this->next = next; } }; //構建一個雙鏈表類 template<class DataType> class LinkList { private: Node<DataType>* head; //頭結點 Node<DataType>* tail; //尾結點 public: LinkList(); //構建 ~LinkList(); //銷燬 void CreateLinkList(int n); //建立 void TraversalLinkList(); //遍歷 int GetLength(); //獲取長度 bool IsEmpty(); //判斷是否爲空 Node<DataType>* Find(DataType data); //查找節點 void InsertElemAtEnd(DataType data); //在尾部插入指定的元素 void InsertElemAtIndex(DataType data, int location); //在指定位置插入指定元素 void InsertElemAtHead(DataType data); //在頭部插入指定元素 void DeleteElemAtEnd(); //在尾部刪除元素 void DeleteAll(); //刪除全部數據 void DeleteElemAtPoint(DataType data); //刪除指定的數據 void DeleteElemAtHead(); //在頭部刪除節點 }; //初始化雙鏈表 template<class DataType> LinkList<DataType>::LinkList() { head = new Node<DataType>(); tail = new Node<DataType>(); head->prev = head->next = head; tail->prev = tail->next = tail; tail = head; } //銷燬雙鏈表 template<class DataType> LinkList<DataType>::~LinkList() { Node<DataType>* temp = new Node<DataType>(); Node<DataType>* deleNode = new Node<DataType>(); temp = head; while (temp->next != head) { deleNode = temp->next; temp = deleNode; delete deleNode; } delete head; } //建立一個雙鏈表 template<class DataType> void LinkList<DataType>::CreateLinkList(int n) { Node<DataType>* pnew = new Node<DataType>(); if (n < 0) { //處理異常 cout << "輸入的節點個數有誤" << endl; exit(EXIT_FAILURE); } for (int i = 0; i < n; i++) { pnew = new Node<DataType>(); cout << "請輸入第" << i + 1 << "個值: "; cin >> pnew->data; pnew->prev = tail; pnew->next = head->prev; tail->next = pnew; head->prev = pnew->next; tail = pnew; } } //遍歷雙鏈表 template<class DataType> void LinkList<DataType>::TraversalLinkList() { if (head->next == head) { cout << "鏈表爲空表" << endl; return; } Node<DataType>* p = head; while (p->next != head) { p = p->next; cout << p->data << " "; } } //獲取雙鏈表的長度 template<class DataType> int LinkList<DataType>::GetLength() { int count = 0; Node<DataType>* p = head->next; while (p != head) { count++; p = p->next; } return count; } //判斷雙鏈表是否爲空 template<class DataType> bool LinkList<DataType>::IsEmpty() { if (head->next == head) { return true; } return false; } //查找節點 template<class DataType> Node<DataType>* LinkList<DataType>::Find(DataType data) { Node<DataType>* p = head; if (p->next == head) { //當爲空表時,報異常 cout << "此鏈表爲空鏈表" << endl; return nullptr; } else { while (p->next != head) { p = p->next; if (p->data == data) { return p; } } return nullptr; } } //在尾部插入指定的元素 template<class DataType> void LinkList<DataType>::InsertElemAtEnd(DataType data) { Node<DataType>* newNode = new Node<DataType>(); newNode->data = data; newNode->prev = tail; newNode->next = head; tail->next = newNode; head->prev = newNode->next; tail = newNode; } //在指定位置插入指定元素 template<class DataType> void LinkList<DataType>::InsertElemAtIndex(DataType data, int n) { if (n<1 || n>GetLength()) cout << "輸入的值錯誤" << endl; else { Node<DataType>* ptemp = new Node<DataType>(); ptemp->data = data; Node<DataType>* p = head; int i = 1; while (n > i) //遍歷到指定的位置 { p = p->next; i++; } //在該位置以後插入數據 ptemp->next = p->next; p->next->prev = ptemp; p->next = ptemp; ptemp->prev = p; } } //在頭部插入指定元素 template<class DataType> void LinkList<DataType>::InsertElemAtHead(DataType data) { Node<DataType>* newNode = new Node<DataType>(); newNode->data = data; newNode->next = head->next; head->next->prev = newNode; head->next = newNode; newNode->prev = head; } //在尾部刪除元素 template<class DataType> void LinkList<DataType >::DeleteElemAtEnd() { Node<DataType>* p = head; Node<DataType>* ptemp = tail; if (p->next == head) { cout << "雙鏈表空" << endl; } else { tail = tail->prev; tail->next = head->prev; head->prev = tail->next; delete ptemp; } } //刪除全部數據 template<class DataType> void LinkList<DataType>::DeleteAll() { Node<DataType>* p = head->next; Node<DataType>* ptemp = new Node<DataType>(); while (p != head) //在頭結點的下一個節點逐個刪除節點 { ptemp = p; p = p->next; head->next = p; delete ptemp; } head->next = head->prev; //頭結點的下一個節點指向NULL } //刪除指定的數據 template<class DataType> void LinkList<DataType>::DeleteElemAtPoint(DataType data) { Node<DataType>* ptemp = Find(data); //查找到指定數據的節點位置 ptemp->prev->next = ptemp->next; ptemp->next->prev = ptemp->prev; delete ptemp; } //測試函數 int main() { LinkList<int> linklist; int i; cout << "1.建立雙鏈表 " << endl; cout << "2.遍歷雙鏈表 " << endl; cout << "3.獲取雙鏈表的長度 " << endl; cout << "4.判斷雙鏈表是否爲空 " << endl; cout << "5.獲取節點 " << endl; cout << "6.在尾部插入指定元素 " << endl; cout << "7.在指定位置插入指定元素 " << endl; cout << "8.在頭部插入指定元素 " << endl; cout << "9.在尾部刪除元素" << endl; cout << "10.刪除全部元素 " << endl; cout << "11.刪除指定元素 " << endl; cout << "0.退出 " << endl; do { cout << "請輸入要執行的操做: "; cin >> i; switch (i) { case 1: int n; cout << "請輸入雙鏈表的長度: "; cin >> n; linklist.CreateLinkList(n); break; case 2: linklist.TraversalLinkList(); break; case 3: cout << "該雙鏈表的長度爲" << linklist.GetLength() << endl; break; case 4: if (linklist.IsEmpty() == 1) cout << "該雙鏈表是空表" << endl; else { cout << "該雙鏈表不是空表" << endl; } break; case 5: int data; cout << "請輸入要獲取節點的值: "; cin >> data; cout << "該節點的值爲" << linklist.Find(data)->data << endl; break; case 6: int endData; cout << "請輸入要在尾部插入的值: "; cin >> endData; linklist.InsertElemAtEnd(endData); break; case 7: int pointData; int index; cout << "請輸入要插入的數據: "; cin >> pointData; cout << "請輸入要插入數據的位置: "; cin >> index; linklist.InsertElemAtIndex(pointData, index); break; case 8: int headData; cout << "請輸入要在頭部插入的值: "; cin >> headData; linklist.InsertElemAtHead(headData); break; case 9: linklist.DeleteElemAtEnd(); break; case 10: linklist.DeleteAll(); break; case 11: int pointDeleteData; cout << "請輸入要刪除的數據: "; cin >> pointDeleteData; linklist.DeleteElemAtPoint(pointDeleteData); break; default: break; } } while (i != 0); system("pause"); return 0; }
順序棧
#include<stdlib.h> #include<iostream> using namespace std; template<class T> class Stack { private: int top; int maxSize; T* stack; public: Stack(int maxSize); ~Stack(); bool IsEmpty(); bool IsFull(); T Top(); bool Push(T x); bool Pop(); }; template<class T> Stack<T>::Stack(int maxSize) { top = -1; this->maxSize = maxSize; stack = new T[maxSize]; } template<class T> Stack<T>::~Stack() { delete[] stack; } template<class T> bool Stack<T>::IsEmpty() { if (top == -1) { return true; } return false; } template<class T> bool Stack<T>::IsFull() { if (top == maxSize - 1) { return true; } return false; } template<class T> T Stack<T>::Top() { return stack[top]; } template<class T> inline bool Stack<T>::Push(T x) { if (top < maxSize - 1) { top++; stack[top] = x; return true; } return false; } template<class T> bool Stack<T>::Pop() { if (top > -1) { top--; return true; } return false; } int main() { Stack<int> stack(3); stack.Push(1); stack.Push(2); stack.Push(3); stack.Pop(); cout << stack.Top() << endl; system("pause"); return 0; }
鏈式棧
#include<stdlib.h> #include<iostream> using namespace std; template<class T> class Node { public: T data; Node<T>* link; Node() { data = 0; link = nullptr; } }; template<class T> class Stack { private: Node<T>* top; public: Stack(); ~Stack(); bool IsEmpty(); T Top(); void Push(T x); void Pop(); }; template<class T> Stack<T>::Stack() { top = new Node<T>; } template<class T> Stack<T>::~Stack() { while (top->link!= nullptr) { Node<T>* temp = new Node<T>; temp = top; top = temp->link; delete temp; } } template<class T> bool Stack<T>::IsEmpty() { if (top->link==nullptr) { return true; } return false; } template<class T> T Stack<T>::Top() { return top->data; } template<class T> void Stack<T>::Push(T x) { Node<T>* temp = new Node<T>(); temp->data = x; temp->link = top; top = temp; } template<class T> void Stack<T>::Pop() { Node<T>* temp = new Node<T>(); temp = top; top = top->link; delete temp; } //測試函數 int main() { Stack<int> stack; stack.Push(1); stack.Push(2); stack.Push(3); stack.Pop(); cout << stack.Top() << endl; system("pause"); return 0; }
棧與遞歸
隊列
順序隊列
鏈式隊列
字符串
字符串運算的算法實現
字符串模式匹配
第 3 章 樹
基本概念
【性質1】樹中的結點數等於其全部的結點的度數加1html
【性質2】度爲m的數,其第i層上至多有 m^i 個結點node
【性質3】高度爲h(深度爲爲h-1)度爲m的樹至多有 個結點ios
【性質4】具備n個結點的度爲m的樹,其最小高度 web
外部路徑長度E:擴充二叉樹裏從根結點到每一個外部結點的路徑長度之和算法
內部路徑長度I:擴充二叉樹裏從根結點到每一個內部結點的路徑長度之和shell
二叉樹的性質:api
【性質1】度爲0的結點比度爲2的結點多1 --- 數組
【性質2】第i層上至多有 個結點數據結構
【性質3】高度爲 的樹至多有 個結點函數
【性質4】非空滿二叉樹的葉子結點的數量等於其分支結點的數量加1
【性質5】有n個結點的徹底二叉樹的高度爲
/* 樹的存儲結構 */ #include<iostream> #include<vector> #include<stack> #include<unordered_map> #include<queue> using namespace std; template<class T> class BinaryTreeNode { public: BinaryTreeNode(); ~BinaryTreeNode(); BinaryTreeNode(const T& element); BinaryTreeNode(const T& element, BinaryTreeNode<T>* leftChild, BinaryTreeNode<T>* rightChild); void setLeftChild(BinaryTreeNode<T>* leftchild); void setRightChild(BinaryTreeNode<T>* rightchild); BinaryTreeNode<T>* getLeftChild(); BinaryTreeNode<T>* getRightChild(); T getInfo(); private: T info; BinaryTreeNode<T>* leftChild; BinaryTreeNode<T>* rightChild; }; template<class T> BinaryTreeNode<T>::BinaryTreeNode(const T& element) { this->info = element; this->leftChild = nullptr; this->rightChild = nullptr; } template<class T> BinaryTreeNode<T>::BinaryTreeNode(const T& element, BinaryTreeNode<T>* leftChild, BinaryTreeNode<T>* rightChild) { this->info = element; this->leftChild = leftChild; this->rightChild = rightChild; } template<class T> BinaryTreeNode<T>::BinaryTreeNode() { } template<class T> BinaryTreeNode<T>::~BinaryTreeNode() { } template<class T> void BinaryTreeNode<T>::setLeftChild(BinaryTreeNode<T>* leftchild) { this->leftChild = leftchild; } template<class T> void BinaryTreeNode<T>::setRightChild(BinaryTreeNode<T>* rightchild) { this->rightChild = rightchild; } template<class T> BinaryTreeNode<T>* BinaryTreeNode<T>::getLeftChild() { return this->leftChild; } template<class T> BinaryTreeNode<T>* BinaryTreeNode<T>::getRightChild() { return this->rightChild; } template<class T> T BinaryTreeNode<T>::getInfo() { return this->info; } template<class T> class BinaryTree { public: BinaryTree(); ~BinaryTree(); BinaryTree(BinaryTreeNode<T>* root); BinaryTreeNode<T>* buildTree_in_post(vector<T> vin, vector<T> vpost); //經過先序遍歷結果和中序遍歷結果構造一棵二叉樹 BinaryTreeNode<T>* buildTree_pre_in(vector<T> pre, vector<T> vin); vector<T> LeverOrder(BinaryTreeNode<T>* root); // 層序遍歷二叉樹:隊列實現 void RecPreOrder(BinaryTreeNode<T>* root); // 遞歸前序遍歷二叉樹 void RecInOrder(BinaryTreeNode<T>* root); // 遞歸中序遍歷二叉樹 void RecPostOrder(BinaryTreeNode<T>* root); // 遞歸後序遍歷二叉樹 vector<T> PreOrder(BinaryTreeNode<T>* root); // 前序遍歷二叉樹 vector<T> InOrder(BinaryTreeNode<T>* root); // 中序遍歷二叉樹 vector<T> PostOrder(BinaryTreeNode<T>* root); // 後序遍歷二叉樹 public: BinaryTreeNode<T>* root; };
二叉樹的遍歷
廣度優先遍歷
template<class T> vector<T> BinaryTree<T>::LeverOrder(BinaryTreeNode<T>* root) { vector<T> v; queue<BinaryTreeNode<T>*> q; BinaryTreeNode<T>* pointer = root; if (pointer != nullptr) { q.push(pointer); } while (!q.empty()) { pointer = q.front(); q.pop(); v.push_back(pointer->getInfo()); cout << pointer->getInfo() << " "; if (pointer->getLeftChild() != nullptr) { q.push(pointer->getLeftChild()); } if (pointer->getRightChild()) { q.push(pointer->getRightChild()); } } cout << endl; return v; }
先序遍歷
template<class T> vector<T> BinaryTree<T>::PreOrder(BinaryTreeNode<T>* root) { stack<BinaryTreeNode<T>*> s; vector<T> v; if (!root) { return v; } BinaryTreeNode<T>* pointer = root; while (!s.empty() || pointer != nullptr) { if (pointer) { v.push_back(pointer->getInfo()); cout << pointer->getInfo() << " "; if (pointer->getRightChild() != nullptr) { s.push(pointer->getRightChild()); } pointer = pointer->getLeftChild(); }else{ pointer = s.top(); s.pop(); } } cout << endl; return v; } template<class T> vector<T> BinaryTree<T>::PreOrder(BinaryTreeNode<T>* root) { stack<BinaryTreeNode<T>*> s; vector<T> v; BinaryTreeNode<T>* pointer = root; s.push(pointer->getRightChild()); while (!s.empty() && pointer != nullptr) { v.push_back(pointer->getInfo()); cout << pointer->getInfo() << " "; if (pointer->getRightChild() != nullptr) { s.push(pointer->getRightChild()); } if (pointer->getLeftChild() != nullptr) { pointer = pointer->getLeftChild(); } else { pointer = s.top(); s.pop(); } } cout << endl; return v; } template<class T> void BinaryTree<T>::RecPreOrder(BinaryTreeNode<T>* root) { if (root == nullptr) { return; } cout << root->getInfo() << " "; if (root->getLeftChild() != nullptr) { RecPreOrder(root->getLeftChild()); } if (root->getRightChild()) { RecPreOrder(root->getRightChild()); } }
中序遍歷
template<class T> vector<T> BinaryTree<T>::InOrder(BinaryTreeNode<T>* root) { stack<BinaryTreeNode<T>*> s; vector<T> v; BinaryTreeNode<T>* pointer = root; while (!s.empty() || pointer != nullptr) { if (pointer) { s.push(pointer); pointer = pointer->getLeftChild(); } else { pointer = s.top(); s.pop(); v.push_back(pointer->getInfo()); pointer = pointer->getRightChild(); } } return v; } template<class T> void BinaryTree<T>::RecInOrder(BinaryTreeNode<T>* root) { if (root == nullptr) { return; } if (root->getLeftChild() != nullptr) { RecInOrder(root->getLeftChild()); } cout << root->getInfo() << " "; if (root->getRightChild() != nullptr) { RecInOrder(root->getRightChild()); } }
後序遍歷
template<class T> vector<T> BinaryTree<T>::PostOrder(BinaryTreeNode<T>* root) { stack<BinaryTreeNode<T>*> s; vector<T> v; BinaryTreeNode<T>* pointer = root; BinaryTreeNode<T>* pre = nullptr; while (!s.empty() || pointer != nullptr) { //降低到最左結點 while (pointer != nullptr) { s.push(pointer); pointer = pointer->getLeftChild(); } pointer = s.top(); if (pointer->getRightChild() != nullptr && pointer->getRightChild() != pre) { pointer = pointer->getRightChild(); } else { s.pop(); v.push_back(pointer->getInfo()); pre = pointer; pointer = nullptr; } } return v; } template<class T> void BinaryTree<T>::RecPostOrder(BinaryTreeNode<T>* root) { if (root == nullptr) { return; } if (root->getLeftChild() != nullptr) { RecPostOrder(root->getLeftChild()); } if (root->getRightChild() != nullptr) { RecPostOrder(root->getRightChild()); } cout << root->getInfo() << " "; }
二叉樹的構造
先序ABDGCEF,中序DGBAECF,後序GDBEFCA
先序、中序構造二叉樹
//經過先序遍歷結果和中序遍歷結果構造一棵二叉樹 template<class T> BinaryTreeNode<T>* BinaryTree<T>::buildTree_pre_in(vector<T> pre, vector<T> vin) { int vinlen = vin.size(); if (vinlen == 0) return NULL; vector<T> pre_left, pre_right, vin_left, vin_right; //建立根節點,根節點確定是前序遍歷的第一個數 BinaryTreeNode<T>* head = new BinaryTreeNode<T>(pre[0]); //找到中序遍歷根節點所在位置,存放於變量gen中 int gen = 0; for (int i = 0; i < vinlen; i++) { if (vin[i] == pre[0]) { gen = i; break; } } //對於中序遍歷,根節點左邊的節點位於二叉樹的左邊,根節點右邊的節點位於二叉樹的右邊 // 左子樹 for (int i = 0; i < gen; i++) { vin_left.push_back(vin[i]); pre_left.push_back(pre[i + 1]);//先序第一個爲根節點 } // 右子樹 for (int i = gen + 1; i < vinlen; i++) { vin_right.push_back(vin[i]); pre_right.push_back(pre[i]); } //遞歸,執行上述步驟,區分子樹的左、右子子樹,直到葉節點 head->setLeftChild(buildTree_pre_in(pre_left, vin_left)); head->setRightChild(buildTree_pre_in(pre_right, vin_right)); return head; }
後序、中序構造二叉樹
template<class T> BinaryTreeNode<T>* BinaryTree<T>::buildTree_in_post(vector<T> vin, vector<T> vpost) { int vinlen = vin.size(); if (vinlen == 0) return NULL; vector<T> post_left, post_right, vin_left, vin_right; //建立根節點,根節點確定是後序遍歷的最後數 BinaryTreeNode<T>* head = new BinaryTreeNode<T>(vpost[vinlen - 1]); //找到中序遍歷根節點所在位置,存放於變量gen中 int gen = 0; for (int i = 0; i < vinlen; i++) { if (vin[i] == vpost[vinlen - 1]) { gen = i; break; } } //對於中序遍歷,根節點左邊的節點位於二叉樹的左邊,根節點右邊的節點位於二叉樹的右邊 // 左子樹 for (int i = 0; i < gen; i++) { vin_left.push_back(vin[i]); post_left.push_back(vpost[i]);//先序第一個爲根節點 } // 右子樹 for (int i = gen + 1; i < vinlen; i++) { vin_right.push_back(vin[i]); post_right.push_back(vpost[i - 1]); } //遞歸,執行上述步驟,區分子樹的左、右子子樹,直到葉節點 head->setLeftChild(buildTree_in_post(vin_left, post_left)); head->setRightChild(buildTree_in_post(vin_right, post_right)); return head; }
二叉搜索樹
線索二叉樹
平衡二叉樹
// AVL.h #pragma once //做用:防止頭文件的重複包含和編譯 #ifndef __AVL_H__ #define __AVL_H__ typedef struct AVLTreeNode { int key; int height; //結點高度,用來計算當前結點爲根結點的子樹是否是平衡的 struct AVLTreeNode* lchild; struct AVLTreeNode* rchild; }AvlNode, * pavlNode; //height,當根結點爲空,height=0,一個結點=1,根結點等價數的層數 int AvlTreeHeight(AvlNode* root); //求最大值 int max(int a, int b); pavlNode LL_Right_Rotate(pavlNode& root); pavlNode LR_Left_Right_Rotate(pavlNode& root); pavlNode RL_Right_Left_Rotate(pavlNode& root); pavlNode AvlTreeInsertNode(pavlNode& root, int key); AvlNode* AvlTreeNodePre(pavlNode root, int key); //找子樹前驅,也就是最右結點,最大值結點 AvlNode* AvlTreeNodePost(pavlNode root, int key); //找子樹後繼,也就是最左結點,最小值結點 static enum errorFlag { delFalse = 0, delTrue } AvlTreeDeleteNodeErrorFlag; pavlNode AvlTreeDeleteNode(pavlNode& root, int key); AvlNode* AvlTreeNodePre(pavlNode root, int key); AvlNode* AvlTreeNodePost(pavlNode root, int key); void preorderTraversalAVL(const pavlNode& root); void inorderTraversalAVL(const pavlNode& root); void AvlTreeDestroy(pavlNode& root); #endif // AVL.cpp #include "AVL.h" #include<iostream> using namespace std; //height,當根結點爲空,height=0,一個結點=1,根結點等價數的層數 int AvlTreeHeight(AvlNode* root) { return (nullptr == root) ? 0 : (root->height); } //求最大值 int max(int a, int b) { return a > b ? a : b; } //LL 左子樹插入或刪除結點致使左子樹不平衡,要右旋轉,返回旋轉調整後的樹根結點 pavlNode LL_Right_Rotate(pavlNode& root) { if (nullptr == root) return nullptr; //定義一個指針指向root的左子樹 AvlNode* left = root->lchild; root->lchild = left->rchild; left->rchild = root; //此時根結點變爲left //調整樹的每一個結點的高度 root->height = max(AvlTreeHeight(root->lchild), AvlTreeHeight(root->rchild)) + 1; //加一是自身節點 left->height = max(AvlTreeHeight(left->lchild), root->height) + 1; return left; //新的根結點 } //RR 右子樹插入或刪除結點致使右子樹不平衡,要左旋轉,返回調整後的樹根結點 pavlNode RR_Left_Rotate(pavlNode& root) { if (nullptr == root) return nullptr; AvlNode* right = root->rchild; root->rchild = right->lchild; right->lchild = root; root->height = max(AvlTreeHeight(root->lchild), AvlTreeHeight(root->rchild)) + 1; right->height = max(AvlTreeHeight(right->rchild), root->height) + 1; return right; } //LR 左子樹的右子樹插入或刪除結點致使不平衡,也就是左子樹和左子樹的右子樹平衡因子一正一負 //先左子樹的右子樹左旋轉,而後左子樹右旋轉 pavlNode LR_Left_Right_Rotate(pavlNode& root) { root->lchild = RR_Left_Rotate(root->lchild); //得到旋轉後的根結點,前面必定要補貨最後旋轉玩後的root->lchild return LL_Right_Rotate(root); } //RL 右子樹的左子樹插入或刪除結點致使不平衡,也就是右子樹和右子樹的左子樹平衡因子一負一正 //先右子樹的左子樹右旋轉,而後右子樹坐旋轉 pavlNode RL_Right_Left_Rotate(pavlNode& root) { root->rchild = LL_Right_Rotate(root->rchild); return RR_Left_Rotate(root); } //AVL樹插入一個結點 //思路:若是樹爲空,直接插入,最後計算樹的高度並返回根結點地址 //不空,採用遞歸,新結點只能插入到樹的最後,插入完後計算新結點的高度, //遞歸層層返回,每返回一層就計算當前根結點的左右子樹高度差,也就是上一次遞歸返回的時候就算的,發現不平衡(高度>1) //說明從當前結點開始的子樹即不平衡了,當即旋轉調整,判斷是在當前子樹的左邊仍是右邊插入的,採起合適的旋轉策略 pavlNode AvlTreeInsertNode(pavlNode& root, int key) { if (nullptr == root) { root = new AvlNode(); if (nullptr == root) { cout << "new 開闢AvlNode空間失敗" << endl; return nullptr; } root->height = 0; //初始爲0,函數最後會計算更新 root->key = key; root->lchild = root->rchild = nullptr; } else if (key < root->key) //比根結點小,左子樹尋找 { root->lchild = AvlTreeInsertNode(root->lchild, key); //遞歸尋找 //遞歸返回,判斷子樹仍是不是平衡的了 //由於只在左子樹插入的,只會增長左子樹的高度,對右子樹沒有影響 if (2 == AvlTreeHeight(root->lchild) - AvlTreeHeight(root->rchild)) //模擬遞歸的層層嵌套,從在葉子結點插入新結點的位置回溯,這裏不用加取絕對值運算的,不會出現負數 { //LL,左子樹的左子樹插入引發不平衡 BF 2 1 LL if (key < root->lchild->key) root = LL_Right_Rotate(root); //旋轉調整,返回新的根結點 else //BF 2 -1 沒有Bf 2 0的狀況 root = LR_Left_Right_Rotate(root); } } else if (key >= root->key) //大於根結點,在右子樹插入 { root->rchild = AvlTreeInsertNode(root->rchild, key); if (2 == AvlTreeHeight(root->rchild) - AvlTreeHeight(root->lchild)) { //RR BF -2 -1 if (key > root->rchild->key) root = RR_Left_Rotate(root); else //RL BF -2 1 root = RL_Right_Left_Rotate(root); } } //else if(key==root->key) //{ // cout<<"該關鍵字存在,禁止插入"<<endl; // return root; //} root->height = max(AvlTreeHeight(root->lchild), AvlTreeHeight(root->rchild)) + 1; //最後這裏才能計算更新height,由於遞歸返回的時候迴旋轉跳轉子樹 return root; } //思路:和插入操做同樣,遞歸尋找要刪除的結點, //若是關鍵字有左右子樹,根據左右子樹的高度,選擇高度高的一遍的相應結點替換要刪除的關鍵字, //好比左子樹高,就選左子樹的最右結點,也就是關鍵字的前驅 //右子樹高,就選右子樹的最左結點,也就是關鍵字的後繼 //替換以後再在對應的子樹裏面刪除剛用來替換的結點 //若是左右子樹不是存在,則選擇不爲空的一個直接替換 //刪除完最後還要更新高度 pavlNode AvlTreeDeleteNode(pavlNode& root, int key) { AvlTreeDeleteNodeErrorFlag = delFalse; if (nullptr == root) { AvlTreeDeleteNodeErrorFlag = delTrue; //若是是最後一個結點刪除,也會返回nullptr,因此加一個錯誤標誌 return nullptr; } if (key < root->key) { root->lchild = AvlTreeDeleteNode(root->lchild, key); //若是左子樹刪除導不平衡,左子樹刪除可能致使和右子樹不平衡 //若是不平衡,是右子樹的右子樹過高致使的仍是右子樹的左子樹左子樹致使的 if (2 == AvlTreeHeight(root->rchild) - AvlTreeHeight(root->lchild)) { //在左子樹刪掉的,只能右子樹高於左子樹2 //動態調整root->rchild //判斷右子樹的左右子樹高度,決定是RL仍是RR if (AvlTreeHeight(root->rchild->lchild) <= AvlTreeHeight(root->rchild->rchild)) {//RR,右邊高->平衡因子負 先左旋轉 root=-2,root->rchild->lchild = -1 一負負,RR_Left_Rotate 或者BF -2 0 root = RR_Left_Rotate(root); } else {//RL root=-2,root->rchild->lchild = 1 只有這種狀況纔是RL root = RL_Right_Left_Rotate(root); } } } else if (key > root->key) { root->rchild = AvlTreeDeleteNode(root->rchild, key); if (2 == AvlTreeHeight(root->lchild) - AvlTreeHeight(root->rchild)) { if (AvlTreeHeight(root->lchild->lchild) >= AvlTreeHeight(root->lchild->rchild)) {//LL BF 2 1 BF 2 0 root = LL_Right_Rotate(root); } else {//LR BF 2 -1 root = LR_Left_Right_Rotate(root); } } } else if (key == root->key) {//找到,刪除 if (root->lchild != nullptr && root->rchild != nullptr) {//左右子樹都不空,只能選當前結點的前驅或者後繼來替換,而後刪了這個前驅或後繼 //爲了保持平衡,通常選當前要刪除結點的左右子樹中較高的一方 if (AvlTreeHeight(root->lchild) > AvlTreeHeight(root->rchild)) {//左子樹中找前驅 AvlNode* delNode = AvlTreeNodePre(root->lchild, key); root->key = delNode->key; //修改替換數值 //左子樹中刪除delNode root->lchild = AvlTreeDeleteNode(root->lchild, delNode->key); } else { AvlNode* delNode = AvlTreeNodePost(root->rchild, key); root->key = delNode->key; root->rchild = AvlTreeDeleteNode(root->rchild, delNode->key); } } else //刪除結點至少有一邊沒有孩子 { AvlNode* tmp = root; root = nullptr == root->lchild ? root->rchild : root->lchild; delete tmp; tmp = nullptr; } } //更新結點高度 if (nullptr != root) //刪除只有一個結點的特殊狀況 { root->height = (max(AvlTreeHeight(root->lchild), AvlTreeHeight(root->rchild))) + 1; } return root; } AvlNode* AvlTreeNodePre(pavlNode root, int key) { if (nullptr == root) return nullptr; while (nullptr != root->rchild) root = root->rchild; return root; } AvlNode* AvlTreeNodePost(pavlNode root, int key) { if (nullptr == root) return nullptr; while (nullptr != root->lchild) root = root->lchild; return root; } void preorderTraversalAVL(const pavlNode& root) { if (nullptr == root) return; cout << root->key << "(" << root->height << ")" << " "; preorderTraversalAVL(root->lchild); preorderTraversalAVL(root->rchild); } void inorderTraversalAVL(const pavlNode& root) { if (nullptr == root) return; inorderTraversalAVL(root->lchild); cout << root->key << "(" << root->height << ")" << " "; inorderTraversalAVL(root->rchild); } void AvlTreeDestroy(pavlNode& root) { if (nullptr == root) return; if (nullptr != root->lchild) AvlTreeDestroy(root->lchild); if (nullptr != root->rchild) AvlTreeDestroy(root->rchild); delete root; root = nullptr; } //main.cpp #include"AVL.h" #include<iostream> using namespace std; #define len 10 int main() { int a[len] = { 3,2,1,4,5,6,7,10,9,8 }; //int a[len] = {62,88,58,47,35,73,51,99,37,93}; cout << "待插入元素序列:"; for (int idx = 0; idx != len; ++idx) { cout << a[idx] << " "; } cout << endl; pavlNode root = nullptr; for (int idx = 0; idx != len; ++idx) { root = AvlTreeInsertNode(root, a[idx]); //由於在插入過程當中會修改根結點 if (nullptr == root) cout << "insert " << a[idx] << " fail!" << endl; } cout << "中序遍歷:"; inorderTraversalAVL(root); cout << endl; cout << "後序遍歷:"; preorderTraversalAVL(root); cout << endl << endl; //AvlTreeDestroy(root); for (int idx = 0; idx != len; ++idx) { if (nullptr == AvlTreeDeleteNode(root, a[idx]) && delTrue == AvlTreeDeleteNodeErrorFlag) cout << "delete " << a[idx] << " fail!" << endl; else { cout << "刪除" << a[idx] << ",中序遍歷:"; inorderTraversalAVL(root); cout << endl; cout << "刪除" << a[idx] << ",前序遍歷:"; preorderTraversalAVL(root); cout << endl << endl; } } } //待插入元素序列:3 2 1 4 5 6 7 10 9 8 //中序遍歷:1(1) 2(2) 3(1) 4(4) 5(1) 6(2) 7(3) 8(1) 9(2) 10(1) //前序遍歷:4(4) 2(2) 1(1) 3(1) 7(3) 6(2) 5(1) 9(2) 8(1) 10(1) // //刪除3,中序遍歷:1(1) 2(2) 4(4) 5(1) 6(2) 7(3) 8(1) 9(2) 10(1) //刪除3,前序遍歷:4(4) 2(2) 1(1) 7(3) 6(2) 5(1) 9(2) 8(1) 10(1) // //刪除2,中序遍歷:1(1) 4(3) 5(1) 6(2) 7(4) 8(1) 9(2) 10(1) //刪除2,前序遍歷:7(4) 4(3) 1(1) 6(2) 5(1) 9(2) 8(1) 10(1) // //刪除1,中序遍歷:4(1) 5(2) 6(1) 7(3) 8(1) 9(2) 10(1) //刪除1,前序遍歷:7(3) 5(2) 4(1) 6(1) 9(2) 8(1) 10(1) // //刪除4,中序遍歷:5(2) 6(1) 7(3) 8(1) 9(2) 10(1) //刪除4,前序遍歷:7(3) 5(2) 6(1) 9(2) 8(1) 10(1) // //刪除5,中序遍歷:6(1) 7(3) 8(1) 9(2) 10(1) //刪除5,前序遍歷:7(3) 6(1) 9(2) 8(1) 10(1) // //刪除6,中序遍歷:7(2) 8(1) 9(3) 10(1) //刪除6,前序遍歷:9(3) 7(2) 8(1) 10(1) // //刪除7,中序遍歷:8(1) 9(2) 10(1) //刪除7,前序遍歷:9(2) 8(1) 10(1) // //刪除10,中序遍歷:8(1) 9(2) //刪除10,前序遍歷:9(2) 8(1) // //刪除9,中序遍歷:8(1) //刪除9,前序遍歷:8(1) // //刪除8,中序遍歷: //刪除8,前序遍歷: // //請按任意鍵繼續. . . // //待插入元素序列:62 88 58 47 35 73 51 99 37 93 //中序遍歷:35(2) 37(1) 47(3) 51(1) 58(2) 62(4) 73(1) 88(3) 93(1) 99(2) //前序遍歷:62(4) 47(3) 35(2) 37(1) 58(2) 51(1) 88(3) 73(1) 99(2) 93(1) // //刪除62,中序遍歷:35(2) 37(1) 47(3) 51(1) 58(2) 73(4) 88(1) 93(2) 99(1) //刪除62,前序遍歷:73(4) 47(3) 35(2) 37(1) 58(2) 51(1) 93(2) 88(1) 99(1) // //刪除88,中序遍歷:35(2) 37(1) 47(3) 51(1) 58(2) 73(4) 93(2) 99(1) //刪除88,前序遍歷:73(4) 47(3) 35(2) 37(1) 58(2) 51(1) 93(2) 99(1) // //刪除58,中序遍歷:35(2) 37(1) 47(3) 51(1) 73(4) 93(2) 99(1) //刪除58,前序遍歷:73(4) 47(3) 35(2) 37(1) 51(1) 93(2) 99(1) // //刪除47,中序遍歷:35(1) 37(2) 51(1) 73(3) 93(2) 99(1) //刪除47,前序遍歷:73(3) 37(2) 35(1) 51(1) 93(2) 99(1) // //刪除35,中序遍歷:37(2) 51(1) 73(3) 93(2) 99(1) //刪除35,前序遍歷:73(3) 37(2) 51(1) 93(2) 99(1) // //刪除73,中序遍歷:37(2) 51(1) 93(3) 99(1) //刪除73,前序遍歷:93(3) 37(2) 51(1) 99(1) // //刪除51,中序遍歷:37(1) 93(2) 99(1) //刪除51,前序遍歷:93(2) 37(1) 99(1) // //刪除99,中序遍歷:37(1) 93(2) //刪除99,前序遍歷:93(2) 37(1) // //刪除37,中序遍歷:93(1) //刪除37,前序遍歷:93(1) // //刪除93,中序遍歷: //刪除93,前序遍歷: // //請按任意鍵繼續. . .
紅黑樹
堆與優先隊列
#include<iostream> using namespace std; constexpr auto min = -9999;; class heap { public: int* A; public: //堆的構造 heap(int* A) { this->A = A; } //建最大堆 void build_max_heap() { for (int i = this->A[0] / 2; i >= 1; i--) { max_heapify(i); } } //維護堆的性質:核心 void max_heapify(int i) { int temp = 0; int left = 2 * i, right = 2 * i + 1; int largest = i; if (left <= A[0] && A[i] < A[left]) { largest = left; } if (right <= A[0] && A[i] < A[right] && A[left] < A[right]) { largest = right; } if (largest != i) { temp = A[largest]; A[largest] = A[i]; A[i] = temp; max_heapify(largest); } } //堆排序:輸入一個序列,將其用最大堆進行排序 void heapSort(int* A) { heap* h = new heap(A); h->build_max_heap(); int temp = 0; for (int i = h->A[0]; i >= 1; i--) { temp = h->A[1]; h->A[1] = h->A[i]; h->A[i] = temp; h->A[0]--; h->max_heapify(1); } } }; class priorityQueue { public: //將元素x的關鍵字值增長到k void INCREASE_KEY(int A[], int x, int key) { if (key < A[x]) cout << "error!"; A[x] = key; while (x > 1 && A[x / 2] < A[x]) { swap(A[x], A[x / 2]); x = x / 2; } } //在優先隊列中插入key void INSERT(int A[], int key) { A[0]++; A[A[0]] = min; //MIN爲負無窮 INCREASE_KEY(A, A[0], key); } //返回隊列中具備最大關鍵字的元素 int MAXIMUM(int A[]) { return A[1]; } //維護優先隊列最大堆的性質 void max_heapify(int A[], int i) { int temp = 0; int left = 2 * i, right = 2 * i + 1; int largest = i; if (left <= A[0] && A[i] < A[left]) { largest = left; } if (right <= A[0] && A[i] < A[right] && A[left] < A[right]) { largest = right; } if (largest != i) { temp = A[largest]; A[largest] = A[i]; A[i] = temp; max_heapify(A,largest); } } //去掉並返回隊列中具備最大關鍵字的元素 int EXTRACT_MAX(int A[]) { if (A[0] < 1) cout << "error!"; int max = A[1]; A[1] = A[A[0]]; A[0]--; max_heapify(A, 1); return max; } }; int main() { //設A[1]爲根結點,A[0]存放數組長度 int A[] = { 0,4,1,3,2,16,9,10,14,8,7 }; int len = sizeof(A)/sizeof(A[0]); A[0] = len - 1; heap* h = new heap(A); h->build_max_heap(); priorityQueue* pq = new priorityQueue(); cout << pq->MAXIMUM(A) << endl; cout << pq->EXTRACT_MAX(A) << endl; pq->INSERT(A, 66); pq->INCREASE_KEY(A, 3, 88); cout << pq->MAXIMUM(A) << endl; cout << pq->EXTRACT_MAX(A) << endl; for (int i = 1; i < len; i++) { cout << A[i] << " "; } cout << endl; return 0; }
#include <iostream> #include<stdlib.h> using namespace std; template <class T> class MinHeap { private: T * heapArray; //存放堆數據的數組 int CurrentSize; //當前堆中元素數目 int MaxSize; //堆所能容納的最大元素數目 public: MinHeap(T* array, int num, int max) { this->heapArray = new T[num]; for (int i = 0; i<num; i++) { this->heapArray[i] = array[i]; } this->CurrentSize = num; this->MaxSize = max; } virtual ~MinHeap() {}; //析構函數 void BuildHeap(); bool isLeaf(int pos) const; //若是是葉結點,返回TRUE int leftchild(int pos) const; //返回左孩子位置 int rightchild(int pos) const; //返回右孩子位置 bool Remove(int pos, T& node); //刪除給定下標的元素 void SiftDown(int left);//篩選法函數,參數left表示開始處理的數組下標 void SiftUp(int position); //從position向上開始調整,使序列成爲堆 bool Insert(const T& newNode); //向堆中插入新元素newNode void MoveMin(); //從堆頂移動最小值到尾部 T& RemoveMin(); //從堆頂刪除最小值 T* getMinHeap(); int getCurrSize(); }; template<class T> void MinHeap<T>::BuildHeap() { for (int i = CurrentSize / 2 - 1; i >= 0; i--) SiftDown(i); } template<class T> T* MinHeap<T>::getMinHeap() { return heapArray; } template<class T> int MinHeap<T>::getCurrSize() { return CurrentSize; } template<class T> T& MinHeap<T>::RemoveMin() { //刪除堆頂元素 if (CurrentSize == 0) { //空堆狀況 cout << "Can't Delete"; exit(1); } else { T temp = heapArray[0]; //取堆頂元素 heapArray[0] = heapArray[CurrentSize - 1];//將堆尾元素上升至堆頂 CurrentSize--; //堆中元素數量減1 if (CurrentSize > 1) //堆中元素個數大於1時才須要調整 //從堆頂開始篩選 SiftDown(0); cout << temp << ' '; return temp; } } template<class T> void MinHeap<T>::SiftDown(int left) { //準備 int i = left; //標識父結點 int j = 2 * i + 1; //標識左子結點 T temp = heapArray[i]; //保存父結點的關鍵碼 //過篩 while (j < CurrentSize) { if ((j < CurrentSize - 1) && (heapArray[j] > heapArray[j + 1])) j++; //該結點有右孩子且右孩子的關鍵碼小於左孩子的關鍵碼時,j指向右子結點 if (temp>heapArray[j]) { //該結點的關鍵碼大於左右孩子中比較小的那個時 heapArray[i] = heapArray[j]; //交換對應值 i = j; j = 2 * j + 1; //向下繼續判斷是否知足最大堆的性質 } else break; } heapArray[i] = temp; } int main() { int a[10] = { 15,2,7,17,5,30,13,12,9,18 }; MinHeap<int> mh1(a, 10, 20); mh1.BuildHeap(); int *b = mh1.getMinHeap(); cout << "最小堆的構建結果:"; for (int i = 0; i<10; i++) { cout << b[i] << ' '; } cout << endl; cout << "優先隊列的出隊結果:"; while (mh1.getCurrSize()>0) { mh1.RemoveMin(); } return 0; }
Huffman編碼樹
第 4 章 圖
基本概念
圖的存儲
#include<iostream> #include<stack> #include<queue> using namespace std; template<class EdgeType> class Edge { public: int start, end; //起點和終點 EdgeType weight; //邊的權重 Edge() { start = 0; end = 0; weight = 0; } Edge(int s, int e, EdgeType w) { start = s; end = e; weight = w; } bool operator >(Edge oneEdge) { return weight > oneEdge.weight; } bool operator <(Edge oneEdge) { return weight < oneEdge.weight; } }; template<class EdgeType> class AdjGraph { private: int** matrix; //矩陣 int vertexNum; //頂點數目 int EdgeNum; //邊的數目 int* Mark; //標記---UNVISITED、VISITED public: AdjGraph(){} AdjGraph(int vm) { //構造函數加初始化 EdgeNum = 0; vertexNum = vm; Mark = new int[vm]; matrix = (int**) new int* [vm]; /*for (int i = 0; i < vm; i++) matrix[i] = new int[vm]; for (int i = 0; i < vm; i++) for (int j = 0; j < vm; j++) matrix[i][j] = 0;*/ for (int i = 0; i < vm; i++) { matrix[i] = new int[vm]; for (int j = 0; j < vm; j++) { matrix[i][j] = 0; } } } ~AdjGraph() { //析構函數 for (int i = 0; i < vertexNum; i++) delete[] matrix[i]; delete[] matrix; } int VertexsNum() { //返回結點個數 return vertexNum; } int EdgesNum() { //返回邊數 return EdgeNum; } bool IsEdge(Edge<EdgeType> oneEdge) { //判斷是否是一條合理的邊 if (oneEdge.weight > 0 && oneEdge.weight < 99999999 && oneEdge.end >= 0) return true; else return false; } int StartVertex(Edge<EdgeType> oneEdge) { //返回邊的起點 return oneEdge.start; } int EndVertex(Edge<EdgeType> oneEdge) { //返回邊的終點 return oneEdge.end; } EdgeType Weight(Edge<EdgeType> oneEdge) { //返回邊的權重 return oneEdge.weight; } Edge<EdgeType> FirstEdge(int oneVertex) { //返回頂點oneVertex爲起點的第一條邊 Edge<EdgeType> temp; temp.start = oneVertex; for (int i = 0; i < vertexNum; i++) { if (matrix[oneVertex][i] != 0) { //cout << "$"<<i<<"#"; temp.end = i; temp.weight = matrix[oneVertex][i]; break; } } return temp; } Edge<EdgeType> NextEdge(Edge<EdgeType> oneEdge) { //返回與邊oneEdge有相同起點的下一條邊 Edge<EdgeType> temp; for (int i = oneEdge.end + 1; i < vertexNum; i++) { if (matrix[oneEdge.start][i] != 0) { //cout << "@@"<<i<<"@@"; temp.start = oneEdge.start; temp.end = i; temp.weight = matrix[oneEdge.start][i]; break; } } return temp; } void setEdge(int s, int e, EdgeType w) { if (matrix[s][e] == 0) { EdgeNum++; } matrix[s][e] = w; } void delEdge(int s, int e) { if (matrix[s][e] != 0) { EdgeNum--; } matrix[s][e] = 0; } void print() { for (int i = 0; i < vertexNum; i++) { for (int j = 0; j < vertexNum; j++) cout << matrix[i][j] << " "; cout << endl; } }
圖的鄰接矩陣表示法
圖的鄰接表表示法
十字鏈表和鄰接多重表
DFS (==樹的先序遍歷)
/* 深度優先搜索遍歷圖的遞歸實現 */ void DFS(vector<vector<int>> matrix, int v){ vector<int> res; int size = matrix.size(); int* mark = new int[size]; //標記是否被訪問過 for (int i = 0; i < size; i++) { mark[i] = 0; } int pointer = v; mark[pointer] = 1; res.push_back(pointer); // 遍歷該頂點的全部鄰接頂點。如果沒有訪問過,那麼繼續往下走 for (int i = 0; i < size; i++) { if (matrix[pointer][i] != INT_MAX && mark[i] == 0) { DFS(matrix, i); } } }
void DFS(vector<vector<int>> matrix, int v) { vector<int> res; int size = matrix.size(); int* mark = new int[size]; //標記是否被訪問過 for (int i = 0; i < size; i++) { mark[i] = 0; } int u, v; stack<int> s; for (int i = v; i < size + v - 1; i++) { int pointer = i % size; if (mark[pointer] == 0) { s.push(pointer); res.push_back(pointer); cout << pointer << " "; mark[pointer] = 1; while (!s.empty()) { pointer = s.top(); s.pop(); for (int k = 0; k < size; k++) { if (matrix[pointer][k] != INT_MAX && mark[k] == 0) { s.push(k); cout << k << " "; mark[k] = 1; } } } } } }
BFS
/* 廣度優先搜索 mark: 標記頂點是否被訪問過,核心!! matrix: 圖的矩陣存儲 v: 開始遍歷的結點 */ vector<int> BFS(vector<vector<int>> matrix, int v) { vector<int> res; int size = matrix.size(); int* mark = new int[size]; //標記是否被訪問過 for (int i = 0; i < size; i++) { mark[i] = 0; } int pointer = v; queue<int> q; q.push(v); while (!q.empty()) { pointer = q.front(); res.push_back(pointer); cout << pointer << " "; mark[pointer] = 1; for (int i = 0; i < size; i++) { if (matrix[pointer][i] != INT_MAX && mark[i] == 0) { q.push(i); } } } //此刻圖中可能還有未遍歷的結點 for (int i = 0; i < size; i++) { if (mark[i] == 0) { BFS(matrix, i); } } }
最小生成樹
普利姆(Prim)算法
pre[i] 起點(U)----邊--->v 終點(V-U):找到最小的邊nearest------>將終點加入U中
nearest[i]:U中的點到V-U中的點的最小邊權值 pre:將要加入的最小邊的起點,pre[i]=-1說明已經在U中了
**普里姆算法:**可在加權連通圖裏搜索最小生成樹。由此算法搜索到的邊子集所構成的樹中,不但包括了連通圖裏的全部頂點,n-1條邊,且其全部邊的權值之和最小。
N = ( V , E ) 是具備 n 個頂點的連通圖,設 U 是最小生成樹中頂點的集合,設 TE 是最小生成樹中邊的集合;
初始,U = { u1 } ,TE = { }
重複執行:
-
在全部 u∈U,v∈V-U 的邊 ( u , v ) 中尋找代價最小的邊( u’ , v’ ) ,並歸入集合 TE 中;
-
將 v’ 歸入集合 U 中;
-
直至 U = V 爲止
在Prim算法中,要考慮如何有效地找出知足條件i在S中,j也在V-S中,且權最小的邊(i,j)。實現這個目的的較簡單的辦法是設置2個數組closest和lowcost
在Prim算法執行過程當中,先找出V-U中使lowcost值最小的頂點j,而後根據數組closest選取邊(j,closest[j]),最後將j添加到S中,並對closest和lowcost做必要的修改。
用這個辦法實現的Prim算法所需的計算時間爲O(n2).
/* 從s點出發獲得的最小生成樹 */ int** Prim(vector<vector<int>> matrix,int s) { int verNum = matrix.size(); ////存儲最小生成樹 int** MST =(int**) new int* [verNum]; for (int i = 0; i < verNum; i++) { MST[i] = new int[verNum]; for (int j = 0; j < verNum; j++) { MST[i][j] = INT_MAX; } } //核心!!! int* nearest = new int[verNum]; //U中的點到V-U中的點的最小邊權值 int* pre = new int[verNum]; //將要加入的最小邊的起點,pre[i]=-1說明已經在U中了 //初始化,U中只有s pre[s] = -1; for (int i = 0; i < verNum; i++) { if (i != s) { nearest[i] = matrix[s][i]; pre[i] = s; } } for (int i = 1; i < verNum; i++) { int minWeight = INT_MAX; int v = -1; //記錄下一個將要加到樹中的點 for (int j = 0; j < verNum; j++) { if (minWeight > nearest[j] && pre[j] != -1) { minWeight = nearest[j]; v = j; } } if (v >= 0) { //將v加入U MST[pre[v]][v] = minWeight; pre[v] = -1; for (int k = 0; k < verNum; k++) { if (pre[k] != -1 && nearest[k] > matrix[v][k]) { nearest[k] = matrix[v][k]; pre[k] = v; } } } } delete[] nearest; delete[] pre; return MST; }
克魯斯卡爾(Kruskal)算法
每次取最小的邊(堆排序)、避免環(等價類)
基本思想:按照權值從小到大的順序選擇n-1條邊,並保證這n-1條邊不構成迴路。 具體作法:首先構造一個只含n個頂點的森林,而後依權值從小到大從連通網中選擇邊加入到森林中,並使森林中不產生迴路,直至森林變成一棵樹爲止。
/* 最小堆 */ #include <iostream> #include<stdlib.h> using namespace std; template <class T> class MinHeap { private: T * heapArray; //存放堆數據的數組 int CurrentSize; //當前堆中元素數目 int MaxSize; //堆所能容納的最大元素數目 public: MinHeap(T* array, int num, int max) { this->heapArray = new T[num]; for (int i = 0; i<num; i++) { this->heapArray[i] = array[i]; } this->CurrentSize = num; this->MaxSize = max; } virtual ~MinHeap() {}; //析構函數 void BuildHeap(); bool isLeaf(int pos) const; //若是是葉結點,返回TRUE int leftchild(int pos) const; //返回左孩子位置 int rightchild(int pos) const; //返回右孩子位置 bool Remove(int pos, T& node); //刪除給定下標的元素 void SiftDown(int left);//篩選法函數,參數left表示開始處理的數組下標 void SiftUp(int position); //從position向上開始調整,使序列成爲堆 bool Insert(const T& newNode); //向堆中插入新元素newNode void MoveMin(); //從堆頂移動最小值到尾部 T& RemoveMin(); //從堆頂刪除最小值 T* getMinHeap(); int getCurrSize(); }; template<class T> void MinHeap<T>::BuildHeap() { for (int i = CurrentSize / 2 - 1; i >= 0; i--) SiftDown(i); } template<class T> T* MinHeap<T>::getMinHeap() { return heapArray; } template<class T> int MinHeap<T>::getCurrSize() { return CurrentSize; } template<class T> T& MinHeap<T>::RemoveMin() { //刪除堆頂元素 if (CurrentSize == 0) { //空堆狀況 cout << "Can't Delete"; exit(1); } else { T temp = heapArray[0]; //取堆頂元素 heapArray[0] = heapArray[CurrentSize - 1];//將堆尾元素上升至堆頂 CurrentSize--; //堆中元素數量減1 if (CurrentSize > 1) //堆中元素個數大於1時才須要調整 //從堆頂開始篩選 SiftDown(0); cout << temp << ' '; return temp; } } template<class T> void MinHeap<T>::SiftDown(int left) { //準備 int i = left; //標識父結點 int j = 2 * i + 1; //標識左子結點 T temp = heapArray[i]; //保存父結點的關鍵碼 //過篩 while (j < CurrentSize) { if ((j < CurrentSize - 1) && (heapArray[j] > heapArray[j + 1])) j++; //該結點有右孩子且右孩子的關鍵碼小於左孩子的關鍵碼時,j指向右子結點 if (temp>heapArray[j]) { //該結點的關鍵碼大於左右孩子中比較小的那個時 heapArray[i] = heapArray[j]; //交換對應值 i = j; j = 2 * j + 1; //向下繼續判斷是否知足最大堆的性質 } else break; } heapArray[i] = temp; } int main() { int a[10] = { 15,2,7,17,5,30,13,12,9,18 }; MinHeap<int> mh1(a, 10, 20); mh1.BuildHeap(); int *b = mh1.getMinHeap(); cout << "最小堆的構建結果:"; for (int i = 0; i<10; i++) { cout << b[i] << ' '; } cout << endl; cout << "優先隊列的出隊結果:"; while (mh1.getCurrSize()>0) { mh1.RemoveMin(); } return 0; } #include<algorithm> #include"minheap.h" class UFsets { private: int n;//等價類中 等價元的個數 int *root;//root[i]表示元素i所在的等價類的表明元素編號 int *next;//next[i]表示在等價類中,i的後面元素編號 int *length;//length[i]表示i所表明的 等價類的元素個數 public: UFsets(int size) { n = size;//初始size個元素的等價類 root = new int[n]; next = new int[n]; length = new int[n]; for (int i = 0; i < n; i++) { root[i] = next[i] = i;//各個元素獨自成一個等價類 length[i] = 1; } } int Find(int v) { if (v < n) { return root[v]; }//返回等價類中的表明元素編號 else {//邊界檢查 cout << "參數不合法" << endl; } } void Union(int v, int u);//合併v和u所在的等價類,將元素少的合併到元素多的裏面去 }; void UFsets::Union(int v, int u) { if (root[u] == root[v]) { //若是兩個在同一個等價類中,就返回 return; } else if (length[root[v]] <= length[root[u]]) { //若是u的長度比v的長度長,那麼就把v合到u裏面去 int rt = root[v];//記錄v所在的等價類的表明元素 length[root[u]] = length[root[u]] + length[root[v]];//修改u所在的等價類的元素的個數 root[rt] = root[u];//下面來修改v所在的等價類裏面的元素的表明元素 for (int j = next[rt]; j != rt; j = next[j]) { root[j] = root[u]; } //下面交換兩個表明元素 rt,root[u] 的next值 int temp; temp = next[rt]; next[rt] = next[root[u]]; next[root[u]] = temp; } else if (length[root[v]] > length[root[u]]) { //相反的同樣 int rt = root[u]; length[root[v]] = length[root[v]] + length[root[u]]; root[rt] = root[v]; for (int k = next[rt]; k != rt; k = next[k]) { root[k] = root[v]; } int temp; temp = next[rt]; next[rt] = next[root[v]]; next[root[v]] = temp; } } Edge* Kruskal(AdjGraph &G) {//最小生成樹的Kruskal算法 //求含有n個頂點、e條邊的連通圖G的最小生成樹 返回邊的集合 int n = G.vertexNum;//記錄頂點數目 UFsets sets(n);//定義n個結點的等價類 Edge *MST = new Edge[n - 1];//要返回的最小生成樹的邊 MinHeap<Edge> MinH(G.edgeNum);//定義含有e個元素的最小堆,用於尋找權值最小的邊 Edge edge; for (int i = 0; i < n; i++) { for (edge = G.FirstEdge(i); G.IsEdge(edge); edge = G.NextEdge(edge)) { if (edge.start < edge.end) { //限制起始點的編號大小順序,防止無向圖中的邊被重複加入 MinH.Insert(edge); } } } int edgeNum = 0;//生成邊的個數 while (edgeNum < n) {//n個結點的連通圖的生成樹有n-1條邊 if (MinH.getCurrSize() != 0) { //若是堆不空 edge = MinH.RemoveMin();//找到權重最小的未處理的邊 int v = edge.start; int u = edge.end; if (sets.Find(v) != sets.Find(u)) { //判斷該邊關聯的頂點是否在一個連通份量 sets.Union(v, u);//合併兩個頂點所在的等價類 MST[edgeNum] = edge;//將符合條件的邊添加到生成樹的邊集合中 edgeNum++; } } else { cout << "不存在最小生成樹." << endl; return nullptr; } } return MST; }
最短路徑
單源最短路徑 dijkstra
U V-U
以源點爲起點的最短邊,將這條邊的終點加入U,而後將其做爲當前源點繼續求最短
算法步驟:
-
初始時,S只包含源點,即S={v},v的距離爲0。U包含除v外的其餘頂點,即:U={其他頂點},若v與U中頂點u有邊,則<u,v>正常有權值,若u不是v的出邊鄰接點,則<u,v>權值爲∞
-
從U中選取一個距離v最小的頂點k,把k,加入S中(該選定的距離就是v到k的最短路徑長度)。
-
以k爲新考慮的中間點,修改U中各頂點的距離;若從源點v到頂點u的距離(通過頂點k)比原來距離(不通過頂點k)短,則修改頂點u的距離值,修改後的距離值的頂點k的距離加上邊上的權。
-
重複步驟b和c直到全部頂點都包含在S中。
void dijkstra(vector<vector<int>> matrix,int u)//主函數,參數是源點編號 { int verNum = matrix.size(); int* dis = new int[verNum]; //dis數組,dis[i]存儲第i號頂點到源點的估計值 int* mark = new int[verNum];//book[i]表明這個點有沒有被當作源點去搜索過,1爲有,0爲沒有。這樣就不會重複搜索了。 int n, m; for (int i = 0; i < verNum; i++) { dis[i] = INT_MAX; } int start = u;//先從源點搜索 mark[start] = 1;//標記源點已經搜索過 for (int i = 0; i < verNum; i++) { dis[i] = min(dis[i], matrix[start][i]);//先更新一遍 } for (int i = 0; i < verNum-1; i++) { int minn = INT_MAX;//謝評論區,改正一下:這裏的minn不是題解上的minn,這表明的是最近點到源點的距離,start才表明最近的點、 for (int j = 0; j < verNum; j++) { if (mark[j] == 0 && minn > dis[j]) { minn = dis[j]; start = j;//找到離源點最近的點,而後把編號記錄下來,用於搜索。 } } mark[start] = 1; for (int j = 0; j < verNum; j++) { dis[j] = min(dis[j], dis[start] + matrix[start][j]);//以新的點來更新dis。 } } }
4.5.2 多源最短路徑
三層循環
if ((G[i][k] + G[k][j]) < G[i][j]) { G[i][j] = G[i][k] + G[k][j]; }
若是要讓任意兩點a,b之間的路程變短,只能引入第三個點(頂點k),並經過這個頂點k中轉即a->k->b,纔可能縮短原來從頂點a點到頂點b的路程
假如如今只容許通過1號頂點,求任意兩點之間的最短路程,應該如何求呢?只需判斷是否比要小便可。表示的是從i號頂點到j號頂點之間的路程。表示的是從i號頂點先到1號頂點,再從1號頂點到j號頂點的路程之和
最開始只容許通過1號頂點進行中轉,接下來只容許通過1和2號頂點進行中轉……容許通過1~n號全部頂點進行中轉
vector<vector<int> > Floyd(vector<vector<int> > G ) { int n = G.size(); //G中vector元素的個數 //只容許經過0——k進行中轉 for (int k = 0; k < n; k++) { for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { if ((G[i][k] + G[k][j]) < G[i][j]) { G[i][j] = G[i][k] + G[k][j]; } } } } return G; }
拓撲排序
關鍵路徑
第 5 章 查找
提升查找效率的方法。
-
**創建索引:**須要消耗必定的存儲空間,可是查找時能夠充分利用索引的信息提升查找效率。
-
**預排序:**查找前對數據元素進行排序,對於排好序的數據進行查找能夠採用有效的折半查找方式。
-
散列技術
查找成功時的平均查找長度:
注:查找 datai的機率是Pi 且sum(pi) = 1,查找到datai須要通過Ci次比較
靜態查找
靜態查找:在查找過程當中不更改數據集中的數據。
順序查找
優勢:插入數據時間複雜度O(1)
缺點:順序查找的平均查找長度較大,平均和最壞的時間複雜度都是O(n)
查找成功的平均查找長度:
查找不成功時的關鍵字比較次數:
int OrderSearch(int A[], int key) { int n = A[0]; for (int i = 1; i <= n; i++) { if (A[i] == key) { return i; } } return 0; }
5.1.2 折半查找法
查找的前提:記錄有序地存儲在線性表中
**思想:**中間位置的元素與待查找元素比較,若相等,則查找成功,若不相等,則縮小查找範圍,直到查找範圍的中間元素等於待查找元素或者查找範圍無效爲止。
**優勢:**查找效率較高
**缺點:**只適用於順序存儲的有序表,且向有序表中插入或刪除數據較複雜
利用斷定樹分析折半查找的性能:
性質1:具備n個結點的斷定樹的高度爲,折半查找法在查找成功時的比較次數最多爲斷定樹的高度。
int BiSearch(int A[], int key) { int n = A[0]; int left = 1, right = n, mid = (left + right) / 2; while (left <= right) { if (A[mid] == key) { return mid; } else if (key < A[mid]) { right = mid - 1; } else { left = mid + 1; } } return 0; }
5.1.3 分塊查找
思想:將元素分塊,塊中元素沒必要有序,塊與塊之間有序,即前一塊中的最大關鍵碼必須小於後一塊中的最小關鍵碼。對每個數據塊創建一個索引表(指針項:第一個記錄的位置;關鍵碼:此塊最大值)
**優勢:**插入、刪除相對較易,沒有大量記錄移動
**缺點:**增長了一個輔助數組額存儲空間、初始線性表分塊排序、當大量插入、刪除時、或結點分佈不均勻時、速度降低
5.2 動態查找
**動態查找:**查找不成功時將要查找的數據添加到數據集中。
動態查找方式:
-
二叉搜索樹
-
平衡二叉搜索樹
-
紅黑樹
-
B樹
-
B+樹
雖然,當二叉搜索樹的結點數據都在內存中時查找效率很高,當從存儲在磁盤上的大規模數據中查找時,二叉搜索樹的性能優勢就沒法體現了。
B樹和B+樹是兩種常見的高效外存數據結構
5.2.1 B樹 or B-樹
**B樹:**一種平衡的多分樹、關鍵碼分佈在全部結點。
一棵m階的B樹,或者是空樹,或者是知足下列性質的m叉樹:
-
根結點至少有兩棵子樹,至多有m棵子樹
-
非根非終端結點至少有棵子樹,至多有m棵子樹
-
全部葉子結點都出如今同一層,可用來「查找失敗」處理
-
有k個子結點的非根結點包含個關鍵碼
2-3樹
2-3樹是一個3階的B樹
性質:
-
每一個內部結點有2個子女(一個關鍵碼),或者3個子女(兩個關鍵碼)
-
全部葉子結點都在樹的同一層
-
樹的最大深度是
**2-3樹查找:**比較次數不超過樹的深度
2-3樹插入:
-
葉子結點只包含1個記錄
-
葉子結點只包含2個記錄---分裂、提高
2-3樹刪除:
-
從包含2個記錄的葉子結點刪除1個記錄---直接刪除
-
從包含1個記錄的葉子結點中刪除這個記錄---向兄弟結點借一個記錄,同時修改雙親結點的記錄
-
從包含1個記錄的葉子結點中刪除這個記錄---向兄弟結點借一個記錄,同時修改雙親結點的記錄,兄弟結點不夠借,須要合併相鄰結點,並影響雙親結點
-
從包含1個記錄的葉子結點中刪除這個記錄---向兄弟結點借一個記錄,同時修改雙親結點的記錄,兄弟結點不夠借,須要合併相鄰結點,並影響雙親結點,這可能減小樹的高度
-
從內部結點刪除一個記錄---將被刪除記錄用右邊子樹中的最小關鍵碼Y代替( Y必定在某個葉子結點中),而後再刪除Y
5.2.2 B+樹
B+樹:是B樹的一種變形,在葉結點上存儲信息的樹,全部的關鍵碼均出如今葉結點上,各層結點中的關鍵碼均是下一層相應結點中最大關鍵碼(或最小關鍵碼)的複寫
m階B+樹的結構定義以下:
-
每一個結點至多有m個子結點;
-
每一個結點(除根外)至少有 個子結點
-
根結點至少有兩個子結點
-
有k個子結點的結點必有k個關鍵碼
B+樹查找:
-
查找應該到葉結點層
-
在上層已找到待查的關鍵碼,並不中止,而是繼續沿指針向下一直查到葉結點層的這個關鍵碼
-
B+樹的葉結點通常連接起來,造成一個雙鏈表
-
適合順序檢索(範圍檢索)
-
實際應用更廣
B+樹插入:
與B樹相同,B+樹的插入也僅在葉結點上進行
B+樹的刪除:
-
當關鍵碼不滿時,與左右兄弟進行調整、合併的處理和B樹相似
-
關鍵碼在葉結點層刪除後,其在上層的複本能夠保留,作爲一個「分界關鍵碼」存在,也能夠替換爲新的最大關鍵碼(或最小關鍵碼)
5.3 散列查找
隨機存儲中,查找某一條記錄須要進行一系列的**「比較」。查找的效率依賴於比較的次數。可否在記錄的關鍵字和存儲地址之間構造這樣一種關係 f ,使得關鍵字和存儲地址一一對應** ?此對應關係 f 稱爲散列函數。
負載因子
-
散列表的空間大小爲m
-
填入表中的結點數爲n
衝突:
-
某個散列函數對於不相等的關鍵碼計算出了相同的散列地址
-
在實際應用中,不產生衝突的散列函數極少存在
**同義詞:**發生衝突的兩個關鍵碼
5.3.2 散列函數
**散列函數:**把關鍵碼值映射到存儲位置的函數,一般用 h 來表示
經常使用散列函數選取方法:
-
除留餘數法
-
摺疊法
-
平方取中法
-
基數轉換法
-
直接定址法
5.3.3 衝突解決方案
產生緣由:
-
主觀設計不當
-
客觀存在,哈希地址是有限的,而記錄是無限的
解決方案:
-
對於因主觀因素產生的衝突
-
提升素質
-
因地制宜
-
-
對於客觀存在的衝突:
-
開放定址法 or 閉散列法:產生衝突時,使用某種方法爲key生成一個探查地址序列,依次探查
-
線性探查法
-
二次探查法
-
僞隨機探查法
-
雙散列法
-
-
**連接法 or 開散列法 or 拉鍊法:**散列表的每一個地址都是一個鏈表的表頭,不會產生溢出。若是整個散列表存儲在內存,用拉鍊法比較容易實現,可是,若是整個散列表存儲在磁盤中,將每一個同義詞存儲在一個新地址的拉鍊法就不太合適。由於一個同義詞鏈表中的元素可能存儲不一樣的磁盤塊中,這就會致使在查詢一個特定關鍵字時屢次訪問磁盤,從而增長了查找時間
-
桶定址法:把記錄分爲若干存儲桶,每一個存儲桶包含一個或多個存儲位置,一個存儲桶內的各存儲位置用指針鏈接起來。散列函數將關鍵字映射到號桶,若是桶滿了,按照開放地址法解決衝突。有衝突彙集現象
-
第 6 章 排序
內部排序: 指的是待排序記錄存放在計算機隨機存儲器中進行的排序過程。
外部排序: 指的是待排序記錄的數量很大,以至內存一次不能容納所有記錄,在排序過程當中尚需對外存進行訪問的排序過程。
穩定性: 相同排序碼排序後相對次序保持不變
插入排序
直接插入排序
從第二個元素開始,將其插入前面已排好序的子數組中(插撲克)
#include<iostream> using namespace std;
//注意:要用key將a[i]存儲起來,否則向後移的時候把a[i]值改變了!!! void INSERTION_SORT(int* a,int len) { int i = 0; int j = 0; int key = 0; for (i = 1; i < len; i++) { key = a[i]; int j = i - 1; while (j >= 0 && a[j] > key) { a[j + 1] = a[j]; j--; } a[j + 1] = key; } }
折半插入排序
-
在插入第i個記錄時,前面的記錄已是有序的了
-
能夠用二分法查找第i個記錄的正確位置
#include<iostream> #include<math.h> using namespace std;
//與直接插入排序的差異在於對於前面已經排序好的序列進行折半插入 void BinaryInsertSort(int R[], int n) { for (int i = 1; i < n; i++) { //共進行n-1次插入 int left = 0, right = i - 1; int temp = R[i]; while (left <= right) { int middle = (left + right) / 2; //取中點
if (temp < R[middle]) right = middle - 1; //取左區間 else left = middle + 1; //取右區間
} for (int j = i - 1; j >= left; j--) R\[j + 1\] = R\[j\]; //元素後移空出插入位 R\[left\] = temp; }
}
6.1.3 希爾排序
對於n個待排序的數列,取一個小於n的整數gap(gap被稱爲步長)將待排序元素分紅若干個組子序列,全部距離爲gap的倍數的記錄放在同一個組中;而後,對各組內的元素進行直接插入排序。 這一趟排序完成以後,每個組的元素都是有序的。而後減少gap的值,並重復執行上述的分組和排序。重複這樣的操做,當時,整個數列就是有序的。
/* * 希爾排序 * * 參數說明: * a -- 待排序的數組 * n -- 數組的長度 / void shellSort1(int a, int n) { int i, j, gap;
// gap爲步長,每次減爲原來的一半。 for (gap = n / 2; gap > 0; gap /= 2) { // 共gap個組,對每一組都執行直接插入排序 for (i = 0; i < gap; i++) { for (j = i + gap; j < n; j += gap) { // 若是a\[j\] < a\[j-gap\],則尋找a\[j\]位置,並將後面數據的位置都後移。 if (a\[j\] < a\[j - gap\]) { int tmp = a\[j\]; int k = j - gap; while (k >= 0 && a\[k\] > tmp) { a\[k + gap\] = a\[k\]; k -= gap; } a\[k + gap\] = tmp; } } } }
}
代碼優化:
/* * 對希爾排序中的單個組進行排序 * * 參數說明: * a -- 待排序的數組 * n -- 數組總的長度 * i -- 組的起始位置 * gap -- 組的步長 * * 組是"從i開始,將相隔gap長度的數都取出"所組成的! / void groupSort(int a, int n, int i, int gap) { int j;
for (j = i + gap; j < n; j += gap) { // 若是a\[j\] < a\[j-gap\],則尋找a\[j\]位置,並將後面數據的位置都後移。 if (a\[j\] < a\[j - gap\]) { int tmp = a\[j\]; int k = j - gap; while (k >= 0 && a\[k\] > tmp) { a\[k + gap\] = a\[k\]; k -= gap; } a\[k + gap\] = tmp; } }
}
/* * 希爾排序 * * 參數說明: * a -- 待排序的數組 * n -- 數組的長度 / void shellSort2(int a, int n) { int i, gap;
// gap爲步長,每次減爲原來的一半。 for (gap = n / 2; gap > 0; gap /= 2) { // 共gap個組,對每一組都執行直接插入排序 for (i = 0; i < gap; i++) groupSort(a, n, i, gap); }
}
6.2 交換排序
6.2.1 冒泡排序
void BubbleSort1(int Array[], int n) { bool NoSwap; // 是否發生了交換的標誌 int i, j; for (i = 0; i < n - 1; i++) { NoSwap = true; // 標誌初始爲真 for (j = n - 1; j > i; j--) if (Array[j] < Array[j - 1]) {// 判斷是否逆置 swap(Array[j], Array[j - 1]); // 交換逆置對 NoSwap = false; // 發生了交換,標誌變爲假 } if (NoSwap) // 沒發生交換,則排好序 return; } }
void BubbleSort2(int Array[], int n) { bool NoSwap; int i, j; for (i = 0; i < n - 1; i++) { for (j = 0; j < n-1-i; j++) if (Array[j] < Array[j + 1]) { swap(Array[j], Array[j + 1]); } } }
6.2.2 快速排序
-
從數列中挑出一個元素,稱爲 "基準"(pivot);
-
從新排序數列,全部元素比基準值小的擺放在基準前面,全部元素比基準值大的擺在基準的後面(相同的數能夠到任一邊)。在這個分區退出以後,該基準就處於數列的中間位置。這個稱爲分區(partition)操做;
-
遞歸地(recursive)把小於基準值元素的子數列和大於基準值元素的子數列排序;
/* 將A[left]---A[right]以A[left]爲軸值分爲左右兩部分 / int classify(int A, int left, int right) { int piro = A[left]; int temp = left; left++; while (left <= right) { while (left <= right && A[left] <= piro) { left++; } while (left <= right && A[right] >= piro) { right--; } if (left < right) { swap(A[left], A[right]); } } swap(A[temp], A[right]); return right; }
void QuickSort(int* A, int left, int right) {
if (left < right) { int mid = classify(A, left, right); QuickSort(A, left, mid-1); QuickSort(A, mid + 1, right); }
}
6.3 選擇排序
6.3.1 直接選擇排序
選出剩下的未排序記錄中的最小記錄,而後直接與數組中第i個記錄交換
#include<iostream> #include<math.h> using namespace std; void selectSort(int a[], int len) {
int minindex, temp; for (int i = 0; i < len - 1; i++) { minindex = i; for (int j = i + 1; j < len; j++) { if (a\[j\] < a\[minindex\]) minindex = j; } temp = a\[i\]; a\[i\] = a\[minindex\]; a\[minindex\] = temp; }
}
6.3.2 堆排序
#include<iostream> using namespace std; constexpr auto min = -9999;; class heap { public: int heap_size; int* A; public: //堆的構造 heap(int* A,int size) { this->heap_size = size - 1; this->A = A; }
//建最大堆 void build\_max\_heap() { for (int i = this->heap_size / 2; i >= 1; i--) { max_heapify(i); } } //維護堆的性質:核心 void max_heapify(int i) { int temp = 0; int left = 2 * i, right = 2 * i + 1; int largest = i; if (left <= heap_size && A\[i\] < A\[left\]) { largest = left; } if (right <= heap_size && A\[i\] < A\[right\] && A\[left\] < A\[right\]) { largest = right; } if (largest != i) { temp = A\[largest\]; A\[largest\] = A\[i\]; A\[i\] = temp; max_heapify(largest); } } //堆排序:輸入一個序列,將其用最大堆進行排序 void heapSort(int* A,int len) { heap* h = new heap(A, len); h->build\_max\_heap(); int temp = 0; for (int i = h->heap_size; i >= 1; i--) { temp = h->A\[1\]; h->A\[1\] = h->A\[i\]; h->A\[i\] = temp; h->heap_size--; h->max_heapify(1); } }
};
int main() { //設A[1]爲根結點,A[0]只是一個佔位元素,爲了下標更好計算 int A[] = { 0,4,1,3,2,16,9,10,14,8,7 }; int len = sizeof(A)/sizeof(A[0]); heap* h = new heap(A,len); h->build_max_heap(); h->heapSort(A, len); for (int i = 1; i < len; i++) { cout << h->A[i] << " "; } cout << endl;
return 0;
}
6.4 歸併排序
歸併排序算法徹底遵循分治模式:
-
分解:分解待排序的n個元素的序列爲各具備n/2個元素的子序列
-
解決:使用歸併排序遞歸的排序兩個子序列
-
合併:合併兩個已排序的子序列以產生已排序的答案
#include<iostream> using namespace std; constexpr auto MAX = 99999;;
//歸併過程:a[p..q]與a[q+1..r]是兩個已經排好序的子序列 void merge(int* a, int p, int q,int r) { int i = 0; int j = 0; int n1 = q - p + 1; int n2 = r - q; int* Left = new int[n1 + 1]; int* Right = new int[n2 + 1]; for (i = 0; i < n1; i++) { //我居然特麼寫成了Left[i]=p+i,我是傻子嗎!!!!!! Left[i] = a[p + i]; } for (i = 0; i < n2; i++) { Right[i] = a[q + i + 1]; } Left[n1] = MAX; Right[n2] = MAX; i = j = 0; for (int k = p; k <= r; k++) { if (Left[i] <= Right[j]) { a[k] = Left[i]; i++; }else { a[k] = Right[j]; j++; } }
}
//分解過程:將規模爲n的問題分解爲本質同樣的規模更小的問題,一般用遞歸實現 int* mergeSort(int* a, int p, int r) { int q = 0; if (p < r) { q = (p + r) / 2; mergeSort(a, p, q); mergeSort(a, q + 1, r); merge(a, p, q, r); } return a; }
int main() { int a[10] = { 4,3,5,2,7,8,6,9,0,11 }; mergeSort(a, 0, 9); for (int i = 0; i < 10; i++) { cout << a[i] << " "; } return 0; }
6.5 桶排序
/** * 桶排序:C++ */
#include <iostream> #include <cstring> using namespace std;
/* * 桶排序 * * 參數說明: * a -- 待排序數組 * n -- 數組a的長度 * max -- 數組a中最大值的範圍 / void bucketSort(int a, int n, int max) { int i, j; int *buckets;
if (a==NULL || n<1 || max<1) return ; // 建立一個容量爲max的數組buckets,而且將buckets中的全部數據都初始化爲0。 if ((buckets = new int\[max\])==NULL) return ; memset(buckets, 0, max*sizeof(int)); // 1\. 計數 for(i = 0; i < n; i++) buckets\[a\[i\]\]++; // 2\. 排序 for (i = 0, j = 0; i < max; i++) while( (buckets\[i\]--) >0 ) a\[j++\] = i; delete\[\] buckets;
}
int main() { int i; int a[] = {8,2,3,4,3,6,6,3,9}; int ilen = (sizeof(a)) / (sizeof(a[0]));
cout << "before sort:"; for (i=0; i<ilen; i++) cout << a\[i\] << " "; cout << endl; bucketSort(a, ilen, 10); // 桶排序 cout << "after sort:"; for (i=0; i<ilen; i++) cout << a\[i\] << " "; cout << endl; return 0;
}
6.6 基數排序
基數排序(Radix Sort)是**桶排序**的擴展,它的基本思想是:將整數按位數切割成不一樣的數字,而後按每一個位數分別比較。 具體作法是:將全部待比較數值統一爲一樣的數位長度,數位較短的數前面補零。而後,從最低位開始,依次進行一次排序。這樣從最低位排序一直到最高位排序完成之後, 數列就變成一個有序序列。
#include<iostream> using namespace std;
/* * 獲取數組a中最大值 * * 參數說明: * a -- 數組 * n -- 數組長度 */ int getMax(int a[], int n) { int i, max;
max = a\[0\]; for (i = 1; i < n; i++) if (a\[i\] > max) max = a\[i\]; return max;
}
/* * 對數組按照"某個位數"進行排序(桶排序) * * 參數說明: * a -- 數組 * n -- 數組長度 * exp -- 指數。對數組a按照該指數進行排序。 * * 例如,對於數組a={50, 3, 542, 745, 2014, 154, 63, 616}; * (01) 當exp=1表示按照"個位"對數組a進行排序 * (02) 當exp=10表示按照"十位"對數組a進行排序 * (03) 當exp=100表示按照"百位"對數組a進行排序 * ... / void countSort(int a[], int n, int exp) { int output = new int[n]; // 存儲"被排序數據"的臨時數組 int i, buckets[10] = { 0 };
// 將數據出現的次數存儲在buckets\[\]中 for (i = 0; i < n; i++) buckets\[(a\[i\] / exp) % 10\]++; // 更改buckets\[i\]。目的是讓更改後的buckets\[i\]的值,是該數據在output\[\]中的位置。 for (i = 1; i < 10; i++) buckets\[i\] += buckets\[i - 1\]; // 將數據存儲到臨時數組out put\[\]中 for (i = n - 1; i >= 0; i--) { output\[buckets\[(a\[i\] / exp) % 10\] - 1\] = a\[i\]; buckets\[(a\[i\] / exp) % 10\]--; } // 將排序好的數據賦值給a\[\] for (i = 0; i < n; i++) a\[i\] = output\[i\];
}
/* * 基數排序 * * 參數說明: * a -- 數組 * n -- 數組長度 */ void radixSort(int a[], int n) { int exp; // 指數。當對數組按各位進行排序時,exp=1;按十位進行排序時,exp=10;... int max = getMax(a, n); // 數組a中的最大值
// 從個位開始,對數組a按"指數"進行排序 for (exp = 1; max / exp > 0; exp *= 10) countSort(a, n, exp);
}
int main() { int i; int a[] = { 53, 3, 542, 748, 14, 214, 154, 63, 616 }; int ilen = (sizeof(a)) / (sizeof(a[0]));
cout << "before sort:"; for (i = 0; i < ilen; i++) cout << a\[i\] << " "; cout << endl; radixSort(a, ilen); // 基數排序 cout << "after sort:"; for (i = 0; i < ilen; i++) cout << a\[i\] << " "; cout << endl; return 0;
}
6.7 計數排序
計數排序並不基於元素的比較,而是一種利用數組下標來肯定元素正確位置的算法。計數排序的核心在於將輸入的數據值轉化爲鍵值存儲在額外開闢的數組空間中。做爲一種線性時間複雜度的排序,計數排序算法的時間複雜度O(n + k)(k爲整數的範圍)。
簡單描述就是,在一個有肯定範圍的整數空間中,創建一個長度更大的數組,如當輸入的元素是 n 個 0 到 k 之間的整數時,創建一個長度大於等於k的數組。該數組的每個下標位置的值表明了數組中對應整數出現的次數。根據這個統計結果,直接遍歷數組,輸出數組元素的下標值,元素的值是幾, 就輸出幾回。
#include<iostream> using namespace std;
void counting_sort(int* A, int* B,int k) { int* C = new int[k + 1]; for (int i = 0; i <= k; i++) { C[i] = 0; } for (int i = 1; i <= A[0]; i++) { C[A[i]]++; } for (int i = 1; i <= k; i++) { C[i] = C[i] + C[i - 1]; } for (int i = A[0]; i >= 1; i--) { B[C[A[i]]] = A[i]; C[A[i]]--; } }
class Solution { public: void coutSort(int* data, int length) { if (data == nullptr || length <= 0) return;
//肯定數列最大值 int max = data\[0\]; for (int i = 1; i < length; ++i) { if (data\[i\] > max) max = data\[i\]; } // 肯定統計數組長度並進行初始化 int* coutData = new int\[max + 1\]; for (int i = 0; i <= max; ++i) coutData\[i\] = 0; // 遍歷數組,統計每一個數出現的次數 for (int i = 0; i < length; ++i) ++coutData\[data\[i\]\]; // 排序數組,某個數出現了幾回,便在data裏累計輸出幾回 int index = 0; for (int i = 0; i <= max; ++i) { for (int j = 0; j < coutData\[i\]; ++j) { data\[index++\] = i; } } }
};
優化版(穩定排序):
-
找出待排序的數組中最大和最小的元素
-
統計數組中每一個值爲i的元素出現的次數,存入數組的第i項
-
對全部的計數累加(從數組中的第一個元素開始,每一項和前一項相加)
-
反向填充目標數組:將每一個元素i放在新數組的第(i)項,每放一個元素就將(i)減去1
class Solution { public: int* coutSort(int* data, int length) { if (data == nullptr || length <= 0) return nullptr;
//肯定數列最大值 int max = data\[0\]; int min = data\[0\]; for (int i = 1; i < length; ++i) { if (data\[i\] > max) max = data\[i\]; if (data\[i\] < min) min = data\[i\]; } int d = max - min; // 肯定統計數組長度並進行初始化 int* coutData = new int\[d + 1\]; for (int i = 0; i <= d; ++i) coutData\[i\] = 0; // 遍歷數組,統計每一個數出現的次數 for (int i = 0; i < length; ++i) ++coutData\[data\[i\] - min\]; // 統計數組作變形,後面的元素等於前面的元素之和 for (int i = 1; i <= d; ++i) coutData\[i\] += coutData\[i - 1\]; // 倒序遍歷原始數列,從統計數組找到正確的位置,輸出到結果數組 int* sortedArray = new int\[length\]; for (int i = length - 1; i >= 0; i--) { sortedArray\[coutData\[data\[i\] - min\] - 1\] = data\[i\]; // 找到data\[i\]對應的coutData的值,值爲多少,表示原來排序多少,(由於從1開始,因此再減1) coutData\[data\[i\] - min\]--; // 而後將相應的coutData的值減1,表示下次再遇到此值時,原來的排序是多少。 } return sortedArray; }
};
6.6 各類內部排序算法的比較和選擇