提交 1b8eca8b 编写于 作者: wnma3mz's avatar wnma3mz

first commit

上级
文件已添加
#include <iostream>
using namespace std;
// 用指针进行交换
void swap(int *a, int *b)
{
int temp;
temp = *a;
*a = *b;
*b = temp;
}
void minheapfixdown(int a[], int i, int n)
{
int j, temp;
// 当前的元素
temp = a[i];
// 当前元素的左子节点的索引
j = 2 * i + 1;
// 如果索引没有超过数组长度
while (j < n)
{
// 如果索引在数组长度范围内,且当前元素的右节点小于左节点,则对右节点进行操作
if (j + 1 < n and a[j + 1] < a[j])
j++;
// 如果当前元素的子节点大于当前元素,则说明不需要替换,直接退出
if (a[j] >= temp)
break;
// 进行交换, 并将当前元素的子节点的子节点继续进行遍历
a[i] = a[j];
i = j;
j = 2 * j + 1;
}
// 最后将原有的当前元素替换
a[i] = temp;
}
void makeheap(int a[], int n)
{
// 从数组中间开始到数组头部
for (int i = n / 2 - 1; i >= 0; i--)
{
minheapfixdown(a, i, n);
}
}
// 堆排序函数
void minheapsort(int a[], int n)
{
int j = 0;
for (int i = n - 1; i >= 1; i--)
{
swap(&a[0], &a[i]);
minheapfixdown(a, 0, i);
}
}
int main()
{
int a[100];
int n;
// 读入数据
cin >> n;
for (int i = 0; i < n; i++)
cin >> a[i];
// 构造最小堆
makeheap(a, n);
for (int i = n - 1; i >= 0; i--)
cout << a[i] << '\t';
cout << endl;
// 最小堆排序
minheapsort(a, n);
for (int i = n - 1; i >= 0; i--)
cout << a[i] << '\t';
return 0;
}
\ No newline at end of file
#include"Coordinate.h"
#include<iostream>
using namespace std;
Coordinate::Coordinate(int x, int y) {
m_iX = x;
m_iY = y;
}
void Coordinate::printCoordinate() {
cout << "(" << m_iX << "," << m_iY << ")" << endl;
}
ostream &operator<<(ostream &out, Coordinate &coor) {
out << "(" << coor.m_iX << "," << coor.m_iY << ")" << endl;
return out;
}
bool Coordinate::operator==(Coordinate &coor) { // 实现比较运算符的重载
if (this->m_iX == coor.m_iX && this->m_iY == coor.m_iY) {
return true;
}
return false;
}
#ifndef COORDINATE_H
#define COORDINATE_H
#include <stdio.h>
#include<ostream>
using namespace std;
class Coordinate {
friend ostream &operator<<(ostream &out, Coordinate &coor); // 实现重载
public:
Coordinate(int x = 0, int y = 0);
void printCoordinate();
bool operator==(Coordinate &cool);
private:
int m_iX;
int m_iY;
};
#endif
#include"List.h"
#include<iostream>
using namespace std;
List::List(int size) {
m_iSize = size;
m_pList = new Coordinate[m_iSize];
m_iLength = 0;
}
List::~List() {
delete []m_pList;
m_pList = NULL;
}
void List::ClearList() {
m_iLength = 0;
}
bool List::ListEmpty() {
return m_iLength == 0 ? true:false;
}
int List::ListLength() {
return m_iLength;
}
bool List::GetElem(int i, Coordinate *e) {
// 指定序号获取元素
if (i < 0 || i >= m_iLength) {
return false;
}
*e = m_pList[i];
return true;
}
int List::LocateElem(Coordinate *e) {
// 指定元素获取该元素的序号
for (int i = 0; i < m_iLength; i++) {
if (m_pList[i] == *e) {
return i;
}
}
return -1;
}
bool List::PriorElem(Coordinate *currentElem, Coordinate *preElem) {
// 指定元素获取该元素的前一个元素
int temp = LocateElem(currentElem);
if (temp == -1 || temp == 0) { // 如果元素不存在或者该元素是第一个结点,则返回false
return false;
} else {
*preElem = m_pList[temp - 1];
return true;
}
}
bool List::NextElem(Coordinate *currentElem, Coordinate *NextElem) {
// 指定元素获取该元素的后一个元素
int temp = LocateElem(currentElem);
if (temp == -1 || temp == m_iLength - 1) { // 如果元素不存在或者该元素是最后一个结点,则返回false
return false;
} else {
*NextElem = m_pList[temp + 1];
return true;
}
}
bool List::ListInsert(int i, Coordinate *e) {
// 在指定位置插入指定元素
if ((i < 0) || (i > m_iLength)) {
return false;
}
// 从最后一个元素到指定位置的元素,全部后移一位
for (int k = m_iLength - 1; k >= i; k--) {
m_pList[k+1] = m_pList[k];
}
m_pList[i] = *e;
m_iLength++;
return true;
}
bool List::ListDelete(int i, Coordinate *e) {
// 指定位置删除元素
if ((i < 0) || (i >= m_iLength)) {
return false;
}
*e = m_pList[i];
// 从指定位置到最后一个元素,全部前移一位
for (int k = i + 1; k < m_iLength; k++) {
m_pList[k-1] = m_pList[k];
}
m_iLength--;
return true;
}
void List::ListTraverse() {
for (int i = 0; i < m_iLength; i++) {
cout << m_pList[i] << endl;
// m_pList[i].printCoordinate();
}
}
#ifndef LIST_H
#define LIST_H
#include"Coordinate.h"
// 顺序表
class List {
public:
List(int size); // 创建线性表
~List(); // 销毁线性表
void ClearList(); // 清空线性表
bool ListEmpty(); // 判断线性表是否为空
int ListLength(); // 获取线性表长度
bool GetElem(int i, Coordinate *e); // 获取指定元素
int LocateElem(Coordinate *e); // 寻找第一个满足e的数据元素的位序
bool PriorElem(Coordinate *currentElem, Coordinate *preElem); // 获取指定元素的前驱
bool NextElem(Coordinate *currentElem, Coordinate *nextElem); // 获取指定元素的后继
bool ListInsert(int i, Coordinate *e); // 在第i个位置插入元素
bool ListDelete(int i, Coordinate *e); // 删除第i个位置的元素
void ListTraverse(); // 遍历线性表
private:
Coordinate *m_pList;
int m_iSize;
int m_iLength;
};
#endif
#include"List2.h"
#include<iostream>
using namespace std;
List2::List2() {
m_pList = new Node;
// m_pList->data = 0; // 实现Node类的时候,注释掉这一句,防止报错
m_pList->next = NULL;
m_iLength = 0;
}
List2::~List2() {
ClearList();
delete m_pList;
m_pList = NULL;
}
void List2::ClearList() {
// 清空单链表
Node *currentNode = m_pList->next;
while (currentNode != NULL) { // 遍历整个链表,删除每个结点
Node *temp = currentNode->next;
delete currentNode;
currentNode = temp;
}
m_pList->next = NULL;
m_iLength = 0;
}
bool List2::ListEmpty() {
return m_iLength == 0 ? true:false;
}
int List2::ListLength() {
return m_iLength;
}
bool List2::GetElem(int i, Node *pNode) {
// 指定位置获取元素
if (i < 0 || i >= m_iLength) {
return false;
}
// 用两个结点来找元素,当前结点和当前结点之前的一个结点。(暂时没懂为什么要用前一个结点)
Node *currentNode = m_pList;
Node *currentNodeBefore = NULL;
for (int k = 0; k <= i; k++) {
currentNodeBefore = currentNode;
currentNode = currentNode->next;
}
pNode->data = currentNode->data;
return true;
}
int List2::LocateElem(Node *pNode) {
// 指定元素获取该元素的位置
Node *currentNode = m_pList;
int count = 0;
while (currentNode->next != NULL) { // 用while循环不断寻找元素,count记录元素位置
currentNode = currentNode->next;
if (currentNode->data == pNode->data) {
return count;
}
count++;
}
return -1;
}
bool List2::PriorElem(Node *pCurrentNode, Node *pPreNode) {
// 指定元素找到该元素的前一个结点
Node *currentNode = m_pList;
Node *tempNode = NULL;
while (currentNode->next != NULL) { // 用while循环来进行遍历,tempNode保存前驱结点
tempNode = currentNode;
currentNode = currentNode->next;
if (currentNode->data == pCurrentNode->data) {
if (tempNode == m_pList) { // 当前节点不能为头节点
return false;
}
pPreNode->data = tempNode->data;
return true;
}
}
return false;
}
bool List2::NextElem(Node *pCurrentNode, Node *pNextNode) {
// 指定元素找到该元素的后一个结点
Node *currentNode = m_pList;
while (currentNode->next != NULL) { // 用while循环来遍历,不需要另一个遍历存储的原因是可以直接返回改结点的next
currentNode = currentNode->next;
if (currentNode->data == pCurrentNode->data) {
if (currentNode->next == NULL) { // 当前节点不能为尾节点
return false;
}
pNextNode->data = currentNode->next->data;
return true;
}
}
return false;
}
bool List2::ListInsert(int i, Node *pNode) {
if ((i < 0) || (i > m_iLength)) { // 插入的位置是否合法,可以插入最后一个节点所以可以不等于m_iLength
return false;
}
Node *currentNode = m_pList; // 找到需要插入节点的位置i
for (int k = 0; k < i; k++) {
currentNode = currentNode->next;
}
Node *newNode = new Node; // 申请内存,失败就返回false
if (newNode == NULL) {
return false;
}
newNode->data = pNode->data; // 传入需要插入的数据域
newNode->next = currentNode->next; // 指针指向原来的指针指向的位置
currentNode->next = newNode; // 将newNode传给当前链表
m_iLength++;
return true;
}
bool List2::ListDelete(int i, Node *pNode) {
// 假设要删除的节点为a,那么我们需要找到a的上一个节点,才能删除节点a
if ((i < 0) || (i >= m_iLength)) { // 插入的位置是否合法,不能删除最后一个位置的下一个数据,所以不能等于m_iLength
return false;
}
Node *currentNode = m_pList; // 插入需要删除节点的位置和前一个节点的位置
Node *currentNodeBefore = NULL;
for (int k = 0; k <= i; k++) {
currentNodeBefore = currentNode;
currentNode = currentNode->next;
}
currentNodeBefore->next = currentNode->next; // 将节点a的next赋值给节点a之前那个节点的next
pNode->data = currentNode->data; // 取出节点a的数据
delete currentNode; // 删除节点a
m_iLength--;
return true;
}
bool List2::ListInsertHead(Node *pNode) {
// 插入到头部
Node *temp = m_pList->next; // 暂存一开始的指针域
Node *newNode = new Node; // 申请内存,内存申请失败就返回false
if (newNode == NULL) {
return false;
}
newNode->data = pNode->data; // 只需要传入的数据域,不需要指针
newNode->next = temp; // 指针域重新变为之前的m_pList的指针域,完成连接链表
m_pList->next = newNode; // 将数据给链表
m_iLength++; // 已经插入成功,链表长度+1
return true;
}
bool List2::ListInsertTail(Node *pNode) {
// 插入到尾部
Node *currentNode = m_pList; // 遍历整个链表,直到找到最后一个节点,特征是最后一个节点的指针指向NULL
while (currentNode->next != NULL) {
currentNode = currentNode->next;
}
Node *newNode = new Node; // 申请内存,内存申请失败就返回false
if (newNode == NULL) {
return false;
}
newNode->data = pNode->data; // 传入数据域
newNode->next = NULL; // 最后一个节点的指针域指向NULL
currentNode->next = newNode; // 将newNode放在当前节点(原最后一个节点)的位置上
m_iLength++; // 已经插入成功,链表长度+1
return true;
}
void List2::ListTraverse() {
Node *currentNode = m_pList;
while (currentNode->next != NULL) { // 遍历操作
currentNode = currentNode->next;
currentNode->printNode();
}
}
#ifndef LIST2_H
#define LIST2_H
#include"Node.h"
#include"Person.h"
// 加上Node类实现,单链表
class List2 {
public:
List2();
~List2();
void ClearList(); // 清空线性表
bool ListEmpty(); // 判断线性表是否为空
int ListLength(); // 获取线性表长度
bool GetElem(int i, Node *pNode); // 获取指定元素
int LocateElem(Node *pNode); // 寻找第一个满足e的数据元素的位序
bool PriorElem(Node *pCurrentNode, Node *pPreNode); // 获取指定元素的前驱
bool NextElem(Node *pCurrentNode, Node *pNextNode); // 获取指定元素的后继
bool ListInsert(int i, Node *pNode); // 在第i个位置插入元素
bool ListDelete(int i, Node *pNode); // 删除第i个位置的元素
bool ListInsertHead(Node *pNode); // 从头部插入元素
bool ListInsertTail(Node *pNode); // 从尾部插入元素
void ListTraverse(); // 遍历线性表
private:
Node *m_pList;
int m_iLength;
};
#endif
#include"Node.h"
#include<iostream>
using namespace std;
void Node::printNode() {
cout << data << endl;
}
#ifndef NODE_H
#define NODE_H
#include"Person.h"
class Node {
public:
Person data;
Node *next;
void printNode();
};
#endif
#include"Person.h"
ostream &operator<<(ostream &out, Person &person) { // 实现输出运算符的重载
out << person.name << "--------" << person.phone << endl;
return out;
}
Person &Person::operator=(Person &person) { // 实现赋值运算符的重载
this->name = person.name;
this->phone = person.phone;
return *this;
}
bool Person::operator==(Person &person) { // 实现比较运算符的重载
if (this->name == person.name && this->phone == person.phone) {
return true;
}
return false;
}
#ifndef PERSON_H
#define PERSON_H
#include<ostream>
#include<string>
using namespace std;
class Person {
friend ostream &operator<<(ostream &out, Person &person);
public:
string name;
string phone;
Person &operator=(Person &person);
bool operator==(Person &person);
};
#endif
## 链表
线性表,并不是一定按线性的顺序进行存储,而是依靠指针进行存储。
这里仅仅实现了单向链表和顺序表。
### 顺序表
实现以下功能
1. 创建、销毁、清空
2. 判断是否为空
3. 长度
4. 根据索引获取指定元素
5. 根据元素获取第一个满足索引
6. 根据元素获取它的前驱、后继
7. 指定位置插入、删除元素
8. 遍历
### 单链表
在顺序表的基础上加了两个功能
1. 从头部插入元素
2. 从尾部插入元素
且调用了指针来完成单链表
```bash
# 编译各个demo所需要的cpp
g++ demo.cpp List.cpp -o demo
g++ demo2.cpp List.cpp Coordinate.cpp -o demo2
g++ demo3.cpp List2.cpp Node.cpp -o demo3
g++ demo4.cpp List2.cpp Node.cpp Person.cpp -o demo4
```
文件已添加
#include"List.h"
#include<stdlib.h>
#include<iostream>
using namespace std;
int main(void) {
// 3 5 7 2 9 1 8
int e1 = 3;
int e2 = 5;
int e3 = 7;
int e4 = 2;
int e5 = 9;
int e6 = 100;
int temp = 0;
List *list1 = new List(10);
// cout << "length:" << list1->ListLength() << endl;
list1->ListInsert(0, &e1);
// cout << "length:" << list1->ListLength() << endl;
list1->ListInsert(1, &e2);
list1->ListInsert(2, &e3);
list1->ListInsert(3, &e4);
list1->ListInsert(4, &e5);
list1->ListInsert(2, &e6);
// list1->ListDelete(0, &temp);
// if (!list1->ListEmpty())
// {
// cout << "not empty" << endl;
// }
// list1->ClearList();
cout << "#" << temp << endl;
// if (list1->ListEmpty())
// {
// cout << "is empty" << endl;
// }
// list1->ListTraverse();
list1->GetElem(0, &temp);
cout << "temp:" << temp << endl;
temp = 101;
cout << list1->LocateElem(&temp) << endl;
list1->PriorElem(&e4, &temp);
cout << "temp:" << temp << endl;
list1->NextElem(&e4, &temp);
cout << "temp:" << temp << endl;
delete list1;
list1 = NULL;
return 0;
}
文件已添加
#include"List.h"
#include"Coordinate.h"
#include<stdlib.h>
#include<iostream>
using namespace std;
int main(void) {
// 3 5 7 2 9 1 8
Coordinate e1 (3, 2);
Coordinate e2 (5, 4);
Coordinate e3 (7, 1);
Coordinate e4 (2, 1);
Coordinate e5 (9, 1);
Coordinate e6 (100, 1);
Coordinate temp (0, 0);
List *list1 = new List(10);
// cout << "length:" << list1->ListLength() << endl;
list1->ListInsert(0, &e1);
// cout << "length:" << list1->ListLength() << endl;
list1->ListInsert(1, &e2);
list1->ListInsert(2, &e3);
list1->ListInsert(3, &e4);
list1->ListInsert(4, &e5);
list1->ListInsert(2, &e6);
// list1->ListDelete(0, &temp);
// if (!list1->ListEmpty())
// {
// cout << "not empty" << endl;
// }
// list1->ClearList();
// cout << "#" << temp << endl;
// if (list1->ListEmpty())
// {
// cout << "is empty" << endl;
// }
list1->ListTraverse();
cout << "GetElem" << endl;
list1->GetElem(10, &temp);
cout << "temp:" << temp << endl;
// temp = 101;
// cout << list1->LocateElem(&temp) << endl;
// list1->PriorElem(&e4, &temp);
// cout << "temp:" << temp << endl;
// list1->NextElem(&e4, &temp);
// cout << "temp:" << temp << endl;
delete list1;
list1 = NULL;
return 0;
}
文件已添加
#include"List2.h"
#include<stdlib.h>
#include<iostream>
using namespace std;
int main(void) {
Node node1;
node1.data = 3;
Node node2;
node2.data = 4;
Node node3;
node3.data = 5;
Node node4;
node4.data = 6;
Node node5;
node5.data = 7;
List *pList = new List();
// pList->ListInsertHead(&node1);
// pList->ListInsertHead(&node2);
// pList->ListInsertHead(&node3);
// pList->ListInsertHead(&node4);
// pList->ListInsertHead(&node5);
// pList->ListInsertTail(&node1);
// pList->ListInsertTail(&node2);
// pList->ListInsertTail(&node3);
// pList->ListInsertTail(&node4);
// pList->ListInsertTail(&node5);
Node temp;
pList->ListInsert(0, &node1);
pList->ListInsert(1, &node2);
// pList->ListDelete(1, &temp);
cout << "temp:" << temp.data << endl;
pList->GetElem(1, &temp);
// cout << "temp:" << temp.data << endl;
cout << "getelem: " << temp.data << endl;
pList->PriorElem(&node2, &temp);
cout << "priorelem: " << temp.data << endl;
pList->NextElem(&node1, &temp);
cout << "nextelem: " << temp.data << endl;
pList->ListTraverse();
delete pList;
pList = NULL;
return 0;
}
文件已添加
#include"List2.h"
#include<stdlib.h>
#include<iostream>
using namespace std;
int main(void) {
Node node1;
node1.data.name = "test1";
node1.data.phone = "123456";
Node node2;
node2.data.name = "test2";
node2.data.phone = "234567";
List2 *pList = new List2();
pList->ListInsertTail(&node1);
pList->ListInsertTail(&node2);
pList->ListTraverse();
delete pList;
pList = NULL;
return 0;
}
#include"CMap.h"
#include"Edge.h"
#include<iostream>
#include<string.h>
#include<vector>
using namespace std;
CMap::CMap(int capacity) {
m_iCapacity = capacity;
m_iNodeCount = 0;
m_pNodeArray = new Node[m_iCapacity];
m_pMatrix = new int[m_iCapacity * m_iCapacity]; // 这里是一维数组
memset(m_pMatrix, 0, m_iCapacity * m_iCapacity * sizeof(int));
// 或者使用下面的方法
/*
* for (int i = 0; i < m_iCapacity * m_iCapacity; i++)
* {
* m_pMatrix[i] = 0;
* }
*/
m_pEdge = new Edge[m_iCapacity - 1];
}
CMap::~CMap() {
delete []m_pNodeArray;
delete []m_pMatrix;
delete []m_pEdge;
}
bool CMap::addNode(Node *pNode) {
if (pNode == NULL) {
return false;
}
m_pNodeArray[m_iNodeCount].m_cData = pNode->m_cData; // 传递数据
m_iNodeCount++;
return true;
}
void CMap::resetNode() {
// 重置结点,所有的结点都设置为未被访问
for (int i = 0; i < m_iNodeCount; i++) {
m_pNodeArray[i].m_bIsVisted = false;
}
}
bool CMap::setValueToMatrixForDirectedGraph(int row, int col, int val) {
// 检验传入的row和col是否在范围
if ((row < 0 ) || (row >= m_iCapacity) || (col < 0 ) || (col >= m_iCapacity)) {
return false;
}
// 向有向图中设置权值。因为是有向图,所以ab点的权值可能与ba点的权值不同。所以只需要设置一遍。由于是一维数组,所以可以抽象的理解每m_iCapacity为一行,使用了如下的赋值方式
m_pMatrix[row * m_iCapacity + col] = val;
return true;
}
bool CMap::setValueToMatrixForUndirectedGraph(int row, int col, int val) {
if ((row < 0 ) || (row >= m_iCapacity) || (col < 0 ) || (col >= m_iCapacity)) {
return false;
}
// 向无向图中设置权值。因为是无向图,所以ab两点权值等于ba两点之前的权值。所以每次需要向两个地方同时进行赋值。
m_pMatrix[row * m_iCapacity + col] = val;
m_pMatrix[col * m_iCapacity + row] = val;
return true;
}
bool CMap::getValueFromMatrix(int row, int col, int &val) {
// 指定row和col,进行取值
val = m_pMatrix[row * m_iCapacity + col];
return true;
}
void CMap::printMatrix() {
// 这里使用两个循环格式化输出。换行输出。
for (int i = 0; i < m_iCapacity; i++) {
for (int k = 0; k < m_iCapacity; k++) {
cout << m_pMatrix[i * m_iCapacity + k] << " ";
}
cout << endl;
}
}
void CMap::depthFirstTraverse(int nodeIndex) {
// 深度优先搜索
int value = 0;
// 打印当前传入索引的结点
cout << m_pNodeArray[nodeIndex].m_cData << " ";
m_pNodeArray[nodeIndex].m_bIsVisted = true; // 设置已访问该结点
// 通过邻接矩阵判断是否与其他顶点有连接
for (int i = 0; i < m_iCapacity; i++) {
getValueFromMatrix(nodeIndex, i, value); // 遍历从当前节点到图中每个节点,取出对应的权值
if ((value != 0) && (!m_pNodeArray[i].m_bIsVisted)) { // 两点之间有连接且未被访问过
depthFirstTraverse(i); // 满足条件,继续搜索。存在连接(弧)且未被访问过
} // 如果没有去向索引为i的顶点的弧,则循环继续。即这一点不与当前要查找的点有连接(弧)
}
}
void CMap::breadthFirstTraverse(int nodeIndex) {
// 广度优先遍历
cout << m_pNodeArray[nodeIndex].m_cData << " "; // 打印起始结点
m_pNodeArray[nodeIndex].m_bIsVisted = true; // 设置已经访问过该结点
vector<int> curVec; // 该层的节点(起始节点),用curVec存储
curVec.push_back(nodeIndex);
breadthFirstTraverseImpl(curVec); // 广搜的主要代码,将该层节点传入
}
void CMap::breadthFirstTraverseImpl(vector<int> preVec) {
int value = 0;
vector<int> curVec;
// preVec:上一层的所有结点
for (int j = 0; j < (int)preVec.size(); j++) { // 遍历上一层的所有节点到当前层所有节点
for (int i = 0; i < m_iCapacity; i++) {
getValueFromMatrix(preVec[j], i, value); // 从上一层的节点到图中的所有节点,取出对应的权值
if ((value != 0) && (!m_pNodeArray[i].m_bIsVisted)) { // 存在连接,且没有被访问过
cout << m_pNodeArray[i].m_cData << " "; // 输出当前节点的值
m_pNodeArray[i].m_bIsVisted = true; // 并将此点设置为已经访问过了
curVec.push_back(i); // 此点加入到当前层的节点中
}
}
}
// 使用vector的size方法的时候,如果不使用int转换为整型,微软的编译器会报warning,Linux上的g++不会
if ((int)curVec.size() == 0) { // 如果当前层已经不存在节点,则说明遍历完成,否则继续进行遍历
// 返回空,即返回调用函数处
return ;
} else {
breadthFirstTraverseImpl(curVec);
}
}
// 普利姆最小生成树
void CMap::primTree(int nodeIndex) {
int value = 0;
int edgeCount = 0; // 当前最小生成树的边数
vector<int> nodeVec; // 存放已经遍历的点集合
vector<Edge> edgeVec; // 存放权值最小的边集合
cout << m_pNodeArray[nodeIndex].m_cData << endl; // 输出起始节点
nodeVec.push_back(nodeIndex); // 放入起始节点
m_pNodeArray[nodeIndex].m_bIsVisted = true; // 设置已经被选择过了
// 当最小生成树的边数小于所有的点数减1,此时最小生成树构造完成
while (edgeCount < m_iCapacity - 1) {
int temp = nodeVec.back(); // 取出nodeVec中最后一个元素,上一个遍历的节点
for (int i = 0; i < m_iCapacity; i++) { // 遍历上一个节点到图中所有的节点
getValueFromMatrix(temp, i, value);
if ((value != 0) && (!m_pNodeArray[i].m_bIsVisted)) { // 两点间有连接且未被访问过
Edge edge(temp, i, value); // 构造一个边的对象
edgeVec.push_back(edge); // 将对象放入"被选边集合"中
}
}
// 从上面找到的可选边集合中找出最小的边,并更改边的状态
int edgeIndex = getMinEdge(edgeVec);
edgeVec[edgeIndex].m_bSelected = true;
// 打印从A节点到B节点,及对应边的权值
cout << edgeVec[edgeIndex].m_iNodeIndexA << "----" << edgeVec[edgeIndex].m_iNodeIndexB << " " << edgeVec[edgeIndex].m_iWeightValue << " ";
// 找出当前这一条边连接的另一个点
int nextNodeIndex = edgeVec[edgeIndex].m_iNodeIndexB;
nodeVec.push_back(nextNodeIndex); // 将找到的点放入点集合中
m_pNodeArray[nextNodeIndex].m_bIsVisted = true; // 同时,标记此点已被选择
cout << m_pNodeArray[nextNodeIndex].m_cData << endl; // 打印边连接的另一个点
edgeCount++; // 找到的点数加1
}
}
int CMap::getMinEdge(vector<Edge> edgeVec) {
// 从边集合中找到没有被选过的权值最小的边,返回对应边的索引
int INT_MAX = 2147483647; // 整型变量的最大值
int minWeight = INT_MAX; // 设定一个最小权值,应该为最大值
int edgeIndex = 0; // 最小权值对应边的索引
for (int i = 0; i < (int)edgeVec.size(); i++) {
if ((!edgeVec[i].m_bSelected) && (minWeight > edgeVec[i].m_iWeightValue)) {
minWeight = edgeVec[i].m_iWeightValue;
edgeIndex = i;
}
}
// 如果所有的边都被选择过了,则最小权值为初始设定的值
if (minWeight == INT_MAX) {
cout << minWeight << endl;
return -1;
}
return edgeIndex;
}
// 克鲁斯卡尔最小生成树
void CMap::kruskalTree() {
/*
* 定义存放结点集合的数组
*
* 1. 取出所有边
* 2. 从所有边中取出组成最小生成树的边
* 1. 找到算法结束条件————边数等于点数-1
* 2. 从边集合中找到最小边
* 3. 找出最小边连接的点
* 4. 找出点所在的点集合
* 5. 根据点所在集合的不同做出不同的处理 
*
*/
int value = 0;
int edgeCount = 0;
vector<vector<int> > nodeSets; // 定义存放结点集合的二维数组,第一维存放点集合,第二维存放点集合中并入的点集合
vector<Edge> edgeVec; // 定义存放边的集合
// 取出所有边,只要邻接矩阵的右上角部分
for (int i = 0; i < m_iCapacity; i++) {
for (int k = i + 1; k < m_iCapacity; k++) {
getValueFromMatrix(i, k, value); // 进行遍历,有连接(弧)的边放入集合
if (value != 0) {
Edge edge(i, k, value);
edgeVec.push_back(edge);
}
}
}
// 从所有边中取出组成最小生成树的边
// 同普利姆算法的结束条件
while (edgeCount < m_iCapacity - 1) {
// 从边集合中找到最小边,且选择状态改为被已选择
int minEdgeIndex = getMinEdge(edgeVec);
edgeVec[minEdgeIndex].m_bSelected = true;
// 根据最小边找到两个连接点
int nodeAIndex = edgeVec[minEdgeIndex].m_iNodeIndexA;
int nodeBIndex = edgeVec[minEdgeIndex].m_iNodeIndexB;
// 设定点A、B在点集合中的索引
int nodeAInSetLabel = -1;
int nodeBInSetLabel = -1;
// 如果某点在点集合中,就将对应点的索引改为A、B在点集合中的索引
for (int i = 0; i < (int)nodeSets.size(); i++) {
nodeAInSetLabel = isInSet(nodeSets[i], nodeAIndex) ? i : nodeAInSetLabel;
nodeBInSetLabel = isInSet(nodeSets[i], nodeBIndex) ? i : nodeBInSetLabel;
}
// 根据点所在集合的不同做出不同的处理 
if (nodeAInSetLabel == -1 && nodeBInSetLabel == -1) { // 如果都不在点集合中,就将节点A、B放入点集合中
vector<int> vec;
vec.push_back(nodeAIndex);
vec.push_back(nodeBIndex);
nodeSets.push_back(vec);
} else if ((nodeAInSetLabel == -1) && (nodeBInSetLabel != -1)) { // 如果点A不在集合中,点B在集合中。就将点A放入点集合中点B的索引位置
nodeSets[nodeBInSetLabel].push_back(nodeAIndex);
} else if ((nodeAInSetLabel != -1) && (nodeBInSetLabel == -1)) { // 同上
nodeSets[nodeAInSetLabel].push_back(nodeBIndex);
} else if ((nodeAInSetLabel != -1) && (nodeBInSetLabel != -1) && (nodeAInSetLabel != nodeBInSetLabel)) { // 如果两点都在点集合中,且点AB索引位置不同,就合并点AB集合(将B集合并入A集合),且删除原来点集合中点B集合的位置(将点B集合后的集合全部前移一位,覆盖点B集合)
mergeNodeSet(nodeSets[nodeAInSetLabel], nodeSets[nodeBInSetLabel]);
for (int k = nodeBInSetLabel; k < (int)nodeSets.size() - 1; k++) {
nodeSets[k] = nodeSets[k + 1];
}
} else if ((nodeAInSetLabel != -1) && (nodeBInSetLabel != -1) && (nodeAInSetLabel == nodeBInSetLabel)) { // 如果两点都在点集合中,且AB索引位置相同,就不做操作
continue;
}
// 打印节点A、B的值及对应边的权值
cout << edgeVec[minEdgeIndex].m_iNodeIndexA << "--" << edgeVec[minEdgeIndex].m_iNodeIndexB << " ";
cout << edgeVec[minEdgeIndex].m_iWeightValue << endl;
edgeCount++;
}
}
bool CMap::isInSet(vector<int> nodeSet, int target) {
// 判断点是否在点集合中
for (int i = 0; i < (int)nodeSet.size(); i++) {
if (nodeSet[i] == target) {
return true;
}
}
return false;
}
void CMap::mergeNodeSet(vector<int> &nodeSetA, vector<int> nodeSetB) {
// 合并B集合到A集合中
for (int i = 0; i < (int)nodeSetB.size(); i++) {
nodeSetA.push_back(nodeSetB[i]);
}
}
// 迪杰斯特拉最短路径
void CMap::Dijkstra(int nodeIndex) {
/*
* 定义一个数组vector存放已经遍历的点,暂时未加上路径
* 循环终止条件————数组长度等于顶点数
* 循环过程:
* 1. 从U中取出与原顶点距离最小的顶点k,加入S
* 2. 以k为新顶点,修改U中各顶点的距离;确保顶点之间的距离是最小的。
* 3. 重复上述步骤
*/
int INFINITY = 10000000;
int node_count = 1;
int nodeValue = 0;
int temp_node = 0;
vector<int> nodeVec;
// 点到其他各点的距离,若不存在则为无穷大
for (int i = 0; i < m_iCapacity; i++) {
getValueFromMatrix(nodeIndex, i, nodeValue);
nodeValue = nodeValue == 0 ? INFINITY : nodeValue;
nodeVec.push_back(nodeValue);
}
nodeVec[nodeIndex] = 0; // 自己到自己的距离为零
m_pNodeArray[nodeIndex].m_bIsVisted = true;
// 终止条件是遍历完所有的点(除自己及之前的点)
while (node_count < m_iCapacity - nodeIndex) {
// 先找出看似最小的路径,获取边的另一个点
int min_num = INFINITY;
for (int i = nodeIndex; i < m_iCapacity; i++) {
if ((!m_pNodeArray[i].m_bIsVisted) && (nodeVec[i] < min_num)) {
min_num = nodeVec[i];
temp_node = i;
}
}
m_pNodeArray[temp_node].m_bIsVisted = true;
// 边的另一个点到其余各点的距离是否小于原始点到各点的距离
for (int i = nodeIndex; i < m_iCapacity; i++) {
getValueFromMatrix(temp_node, i, nodeValue);
nodeValue = nodeValue == 0 ? INFINITY : nodeValue;
if ((nodeValue + min_num < nodeVec[i]) && (!m_pNodeArray[i].m_bIsVisted)) {
nodeVec[i] = nodeValue + min_num;
}
}
cout << m_pNodeArray[nodeIndex].m_cData << "-->" << m_pNodeArray[temp_node].m_cData << " "<< nodeVec[temp_node] << endl;
node_count++;
}
}
// 弗洛伊德最短路径
void CMap::Floyd() {
int INFINITY = 100000;
int value = 0;
// D重新定义邻接矩阵,两点若不存在距离,则距离为无穷大
// P存储点点之间的索引
vector<vector <int> > D_vec;
vector<vector <int> > P_vec;
for (int i = 0; i < m_iCapacity; i++) {
vector<int> d_vec;
vector<int> p_vec;
for (int j = 0; j < m_iCapacity; j++) {
getValueFromMatrix(i, j, value);
value = value == 0 ? INFINITY : value;
d_vec.push_back(value);
p_vec.push_back(j);
}
D_vec.push_back(d_vec);
P_vec.push_back(p_vec);
}
// 三重循环,里面的两个循环为不经过第三个点,最外层循环表示经过第三个点
for (int i = 0; i < m_iCapacity; i++) {
for (int j = 0; j < m_iCapacity; j++) {
for (int k = 0; k < m_iCapacity; k++) {
if (D_vec[j][k] > D_vec[j][i] + D_vec[i][k]) {
D_vec[j][k] = D_vec[j][i] + D_vec[i][k];
P_vec[j][k] = P_vec[j][i];
}
}
}
}
// 输出点到各点的距离及路径
for (int i = 0; i < m_iCapacity; i++) {
for (int j = i + 1; j < m_iCapacity; j++) {
cout << m_pNodeArray[i].m_cData << "-" << m_pNodeArray[j].m_cData << " weight: " << D_vec[i][j] << endl;
int node_index = P_vec[i][j];
cout << "路径:" << m_pNodeArray[i].m_cData;
while (node_index != j) {
cout << "->" << m_pNodeArray[node_index].m_cData;
node_index = P_vec[node_index][j];
}
cout << "->" << m_pNodeArray[node_index].m_cData << endl;
}
cout << " " << endl;
}
}
#ifndef CMAP_H
#define CMAP_H
#include<vector>
#include"Node.h"
#include"Edge.h"
using namespace std;
class CMap {
public:
CMap(int capacity);
~CMap();
bool addNode(Node *pNode); // 往图中添加顶点(结点)
void resetNode(); // 重置顶点
bool setValueToMatrixForDirectedGraph(int row, int col, int val = 1); // 为有向图设置邻接矩阵
bool setValueToMatrixForUndirectedGraph(int row, int col, int val = 1); // 为无向图设置邻接矩阵
void printMatrix(); // 打印邻接矩阵
void depthFirstTraverse(int nodeIndex); // 深度优先遍历
void breadthFirstTraverse(int nodeIndex); // 广度优先遍历
void primTree(int nodeIndex); // 普里姆生成树
void kruskalTree(); // 克鲁斯卡尔生成树
void Dijkstra(int nodeIndex); // 迪杰斯特拉最短路径
void Floyd(); // 弗洛伊德最短路径
private:
bool getValueFromMatrix(int row, int col, int &val); // 从矩阵中获取权值
void breadthFirstTraverseImpl(vector<int> preVec); // 广度优先遍历实现函数
int getMinEdge(vector<Edge> edgeVec); // 从当前的边集合中,找出权值最小的边
bool isInSet(vector<int> nodeSet, int target); // 判断顶点是否在点集合中
void mergeNodeSet(vector<int> &nodeSetA, vector<int> nodeSetB); // 合并两个顶点集合
private:
int m_iCapacity; // 图中最多可以容纳的顶点数
int m_iNodeCount; // 已经添加的顶点(结点)个数
Node *m_pNodeArray; // 存放顶点数组
int *m_pMatrix; // 存放邻接矩阵
Edge *m_pEdge; // 存放边所连接的两个点和对应的权值
};
#endif
#include"Edge.h"
Edge::Edge(int nodeIndexA, int nodeIndexB, int weightValue) {
m_iNodeIndexA = nodeIndexA;
m_iNodeIndexB = nodeIndexB;
m_iWeightValue = weightValue;
m_bSelected = false;
}
#ifndef EDGE_H
#define EDGE_H
class Edge {
public:
Edge(int nodeIndexA = 0, int nodeIndexB = 0, int weightValue = 0);
int m_iNodeIndexA; // 连接的A点
int m_iNodeIndexB; // 连接的B点
int m_iWeightValue; // 边的权值
bool m_bSelected; // 此边是否被选择了,生成最小生成树的时候需要使用
};
#endif
#include"Node.h"
Node::Node(char data) {
m_cData = data;
m_bIsVisted = false;
}
#ifndef NODE_H
#define NODE_H
class Node {
public:
Node(char data = 0);
char m_cData;
bool m_bIsVisted; // 用于判断是否被访问过,遍历树的时候需要使用
};
#endif
##图
[TOC]
这里不做术语介绍,详细请查阅[wiki链接](https://zh.wikipedia.org/wiki/%E5%9B%BE_(%E6%95%B0%E5%AD%A6))
实现功能
1. 创建、销毁
2. 添加结点
3. 重置结点
4. 分别为有、无向图设置邻接矩阵
5. 打印邻接矩阵
6. 深度优先遍历
7. 广度优先遍历
8. 普利姆最小生成树
9. 克鲁斯卡尔最小生成树
```bash
# 编译各个demo所需要的cpp
g++ demo.cpp CMap.cpp Node.cpp -o demo
g++ demo2.cpp CMap.cpp Node.cpp Edge.cpp -o demo2
```
### 搜索
假设我们要构造如下的图。
这里采取的办法是使用邻接矩阵。有值的地方表示存在连接,没有值的地方表示不存在连接,所以上面的图就可以转换成下面这个矩阵。这里值可以理解为边的权值,方便起见我们设置为1。同样为了方便起见,我们使用无向图进行说明。无向图在邻接矩阵上的体现为**[对称矩阵](https://baike.baidu.com/item/%E5%AF%B9%E7%A7%B0%E7%9F%A9%E9%98%B5)**,简单来说就是$a_{ij}=a_{ji}$。
深度优先(优先向深走),用的数据结构为栈,主要用递归实现
广度优先(优先向近走),用的数据结构为队列,主要用迭代实现
```bash
图例:
A
/ \
B D
/ \ / \
C F G-H
\ /
E
A B C D E F G H
A 1 1
B 1 1 1
C 1 1 1
D 1 1 1
E 1
F 1 1
G 1 1
H 1 1
输出:
深度优先遍历
A B C E F D G H
广度优先遍历
A B D C F G H E
```
#### 深度优先搜索
从顶点出发,访问与其连接的点(未被访问过),接下来继续进行上面的过程,如果当前点的所有连接点都被访问过,那么就回退到当前点的上一个节点重复上面的过程。最终直到所有的节点都被访问过。
按照给的案例,路线就应该是:
我们约定当前点有多条连接点时选择其最左侧顶点。这里要注意的是,F、G之间没有连接,F、D也没有,图上可能显示有点歧义,可以从邻接矩阵中看出。
所以我们从A点出发,一路前进到F。
A->B->C->E->F
接下来,F访问B,但是B已经被访问过了,所以回退到E。E同F的情况,进行回退,一直回退到A。则从A开始访问D。
D->G->H
H访问D,但是D已经被返回过了,所以回退,一直回退到A。发现所有的点都被访问过了,所以结束搜索。
综上,总的路程应该是A->B->C->E->F->D->G->H。
根据思路,代码应该使用递归的思想来完成。这里可参考`CMap.p``depthFirstTraverse`部分。
#### 广度优先搜索
不同于深度优先搜索,广度优先搜索从理解上,会比深度优先搜索更好理解。
从顶点出发,访问它的所有连接的点。接下来,再分别以它所有连接点为顶点进行访问,即访问连接点的连接点。这里要注意的是,不能访问已经被访问过的点。
按照给的案例,路线应该是:
同样我们约定有多个连接点的时候,选择最左侧的点。
从A出发,访问A的连接点B、D
A->B->D
接下来分别访问B、D的连接点
C->F、G->H
接下来,访问C、F、G、H的连接点
C向下只有一个连接点,F向下的连接点E被访问过了,G、H向下无连接点。
所以,综上总的路程应该是A->B->D->C->F->G->H->E
其实直观来看,广度优先搜索就是按层搜索,一层一层的进行搜索。
这里用代码实现就是使用循环来完成。这里可参考`CMap.p``breadthFirstTraverse`部分
### 最小生成树
下面说明两种最小生成树的算法。分别是**普利姆**算法和**克鲁斯卡尔**算法。我们以下面的图为案例进行说明。同样,这张图也是无向图,但是边具有权值。我们一样使用上面的邻接矩阵进行转换。邻接矩阵可以用代码生成。在这里,我们主要说明算法过程。
```bash
A
/ | \
B--F--E
\/ \/
C---D
A B C D E F
0 1 2 3 4 5
A-B 6 B-C 3 C-D 7 D-E 2 E-F 9
A-E 5 B-F 2 C-F 8 D-F 4
A-F 1
输出
普利姆最小生成树
起始点: A
0----5 1 F
5----1 2 B
1----2 3 C
5----3 4 D
3----4 2 E
克鲁斯卡尔最小生成树
0--5 1
1--5 2
3--4 2
1--2 3
3--5 4
```
#### 普利姆最小生成树
从顶点出发,遍历所有的连接边的权值,选最小权值的边,加入边集合,之后将边连接的点加入点集合。以边连接的另一个点作为顶点,重复上述过程。直到完成最小生成树(最小生成树的边数等于顶点数减1)。
初始化,点集合{A},边集合{}
这里我们从顶点A出发,有三条连接边,A-B,A-E,A-F。发现A-F最小,所以此边加入边集合。点集合{A,F},边集合{A-F}
从顶点F出发,有五条连接边,F-A,F-B,F-C,F-D,F-E。F-A被选择过了,所以排除F-A。在剩下的边中F-B最小,所以加入集合。点集合{A,F,B},边集合{A-F, F-B}
所以下面从顶点B出发,B-C最小。点集合{A,F,B,C},边集合{A-F,F-B,B-C}
接下来从顶点C出发,C-D最小。点集合{A,F,B,C,D},边集合{A-F,F-B,B-C,C-D}
接下来从顶点D出发,D-E最小。点集合{A,F,B,C,D,E},边集合{A-F,F-B,B-C,C-D,D-E}
此时,边数=顶点数-1。所以结束,最小生成树构造完成。
代码部分,可参考`CMap.p``primTree`部分。
#### 克鲁斯卡尔最小生成树
首先,初始化一个二维数组,用于存放点。需要确保不构成回路,所以需要二维数组。接着初始化一个边集合,存放边,作用同普利姆算法的边集合。
第二步,构造一个集合,里面存放了所有的边。取出里面权值最小的边。
所以取出A-F,加入边集合,对应的点加入点集合。
点集合{{A},{F}},边集合{A-F}
接下来继续取出权值最小的边B-F。(权值为2的有两条边,根据遍历规则先取出的是B-F)
此时,点F已经在二维数组中,所以将另一个点B,放入F集合中。
点集合{{A},{F,B}},边集合{A-F,B-F}
继续D-E。点集合{{A},{F,B},{D},{E}},边集合{A-F,B-F,D-E
继续B-C。此时B已经在点集合中的某个集合里了,所以将C放入对应的集合。点集合{{A},{F,B,C},{D},{E}},边集合{A-F,B-F,D-E,B-C}
继续D-F。此时D和F都在点集合中,但是位于不同位置。合并这两个集合。点集合{{A},{F,B,C,D},{E}},边集合{A-F,B-F,D-E,B-C,D-F}
此时最小生成树的边数=顶点数-1。所以结束。同普利姆算法的终止条件。
代码部分,可参考`CMap.p``kruskalTree `部分。
### 最短路径问题
在一个图中,找出任意点到其他点的最短路径问题。这里主要介绍**迪杰斯特拉(Dijkstra)算法****弗洛伊德(Floyd)**算法。这里以下图为例子进行说明。
![shortPath](https://coding.net/u/wnma3mz/p/C_plus_Test/git/blob/master/map_demo/shortPath.png)
具体如何插入到数据结构的方法可以见`demo3.cpp`,这里不做介绍。值得一提的是**Dijkstra**的时间复杂度为$O(n^2)$,**Floyd**的时间复杂度为$o(n^3)$。但是前者比后者更难以实现。
#### 迪杰斯特拉(Dijkstra)
1. 构造一个点集合,初始时里面只有起始点
2. 构造一个数组,里面是从起始点到其他点(包括本身)的距离。(如果不存在,则这两点距离初始化为无穷大)
3. 从上面的数组中选取最小的点,放到点集合中。
4. 接下来以新放入的这个点为起始点,重新构造步骤2中的数组。比较两次的距离,比如A->C可能有两条路径A->C或者A->B->C,在此步骤中进行比较。选取较小的值为最短路径
5. 重复2-4步,直到所有点都在点集合中
以上图为例
1. 点集合中包含{A},A->A=0。构造数组[A->B=6,A->C=3,A->其他=无穷大]。选出最小的点C放入点集合中。
2. 点集合中包含{A,C},A->A=0,A->C=3。构造数组[A->C->B=5,A->C->D=6,A->C->E=7,A->C->其他=无穷大]。这里发现A->C->B小于上面的A->B,所以放入A->C->B即点B到点集合中
3. 点集合中包含{A,C,B},A->A=0,A->C=3,A->C->B=5。构造数组[A->C->B->D=10,A->C->B->其他=无穷大]。这里发现A->C->B->D大于上面的A->C->D,所以放入A->C->D即点D到点集合中
4. 之后就是同上,不断重复。最后的结果是A->A=0,A->C=3,A->C->B=5,A->C->D=6,A->C->E=7,A->C->D->F=9
代码部分可参考`CMap.p``Dijkstra`部分。此部分输出暂时没有包含路径部分。
#### 弗洛伊德(Folyd)
这种方法是用了一种动态规划的思想来完成的。
如果我们要求出i点到j点的距离(用D[i][j]表示),产生了一个问题这个路径会不会经过k点。
答案无非就是两种情况,会与不会。
1. 会。路径距离转换为D[i][k]+D[k][j]
2. 不会。路径距离还是D[i][j]
换个角度想,最短路径其实就是从上面两种情况得出,即min(D[i][j], D[i][k] + D[k][j])。
Emmmm,其实算法核心就是这样,构造一个三重循环,比较这两种情况的大小。初始情况D是由任意两点之间的距离组成的一个二维数组。
具体路径用P这个二维数组进行存储,如果会经过k点,那么P[i][j]就相应的改为P[i][k]
代码部分,可参考`CMap.p``Floyd`部分。
文件已添加
#include<iostream>
#include<stdlib.h>
#include"CMap.h"
using namespace std;
/*
* 图例:
* A
* / \
* B D
* / \ / \
* C F G-H
* \ /
* E
*
* A B C D E F G H
* A 1 1
* B 1 1 1
* C 1 1 1
* D 1 1 1
* E 1
* F 1 1
* G 1 1
* H 1 1
*
* 正确结果
*
* 深度优先遍历
* A B C E F D G H
* 广度优先遍历
* A B D C F G H E
*/
int main(void) {
CMap *pMap = new CMap(8);
Node *pNodeA = new Node('A');
Node *pNodeB = new Node('B');
Node *pNodeC = new Node('C');
Node *pNodeD = new Node('D');
Node *pNodeE = new Node('E');
Node *pNodeF = new Node('F');
Node *pNodeG = new Node('G');
Node *pNodeH = new Node('H');
pMap->addNode(pNodeA);
pMap->addNode(pNodeB);
pMap->addNode(pNodeC);
pMap->addNode(pNodeD);
pMap->addNode(pNodeE);
pMap->addNode(pNodeF);
pMap->addNode(pNodeG);
pMap->addNode(pNodeH);
pMap->setValueToMatrixForUndirectedGraph(0, 1);
pMap->setValueToMatrixForUndirectedGraph(0, 3);
pMap->setValueToMatrixForUndirectedGraph(1, 2);
pMap->setValueToMatrixForUndirectedGraph(1, 5);
pMap->setValueToMatrixForUndirectedGraph(3, 6);
pMap->setValueToMatrixForUndirectedGraph(3, 7);
pMap->setValueToMatrixForUndirectedGraph(6, 7);
pMap->setValueToMatrixForUndirectedGraph(2, 4);
pMap->setValueToMatrixForUndirectedGraph(4, 5);
pMap->printMatrix();
cout << "深度优先遍历" << endl;
pMap->depthFirstTraverse(0);
cout << endl;
pMap->resetNode();
cout << "广度优先遍历" << endl;
pMap->breadthFirstTraverse(0);
return 0;
}
文件已添加
#include<iostream>
#include<stdlib.h>
#include"CMap.h"
#include"Edge.h"
using namespace std;
/*
*
* A
* / | \
* B--F--E
* \/ \/
* C---D
*
* A B C D E F
* 0 1 2 3 4 5
*
* A-B 6 B-C 3 C-D 7 D-E 2 E-F 9
* A-E 5 B-F 2 C-F 8 D-F 4
* A-F 1
*
* 正确结果
*
* A
* 0----5 1
* F
* 5----1 2
* B
* 1----2 3
* C
* 5----3 4
* D
* 3----4 2
* E
* 0--5 1
* 1--5 2
* 3--4 2
* 1--2 3
* 3--5 4
*
*/
int main(void) {
CMap *pMap = new CMap(6);
Node *pNodeA = new Node('A');
Node *pNodeB = new Node('B');
Node *pNodeC = new Node('C');
Node *pNodeD = new Node('D');
Node *pNodeE = new Node('E');
Node *pNodeF = new Node('F');
pMap->addNode(pNodeA);
pMap->addNode(pNodeB);
pMap->addNode(pNodeC);
pMap->addNode(pNodeD);
pMap->addNode(pNodeE);
pMap->addNode(pNodeF);
pMap->setValueToMatrixForUndirectedGraph(0, 1, 6);
pMap->setValueToMatrixForUndirectedGraph(0, 4, 5);
pMap->setValueToMatrixForUndirectedGraph(0, 5, 1);
pMap->setValueToMatrixForUndirectedGraph(1, 2, 3);
pMap->setValueToMatrixForUndirectedGraph(1, 5, 2);
pMap->setValueToMatrixForUndirectedGraph(2, 5, 8);
pMap->setValueToMatrixForUndirectedGraph(2, 3, 7);
pMap->setValueToMatrixForUndirectedGraph(3, 5, 4);
pMap->setValueToMatrixForUndirectedGraph(3, 4, 2);
pMap->setValueToMatrixForUndirectedGraph(4, 5, 9);
cout << "普利姆最小生成树" << endl;
cout << "起始点: ";
pMap->primTree(0);
cout << "克鲁斯卡尔最小生成树" << endl;
pMap->kruskalTree();
return 0;
}
文件已添加
#include<iostream>
#include<stdlib.h>
#include"CMap.h"
#include"Edge.h"
#include<vector>
using namespace std;
/*
*
* A
* / | \
* B--F--E
* \/ \/
* C---D
*
* A B C D E F
* 0 1 2 3 4 5
*
* A-B 6 B-C 3 C-D 7 D-E 2 E-F 9
* A-E 5 B-F 2 C-F 8 D-F 4
* A-F 1
*
* 正确结果
*
* A
* 0----5 1
* F
* 5----1 2
* B
* 1----2 3
* C
* 5----3 4
* D
* 3----4 2
* E
* 0--5 1
* 1--5 2
* 3--4 2
* 1--2 3
* 3--5 4
*
*/
int main(void) {
CMap *pMap = new CMap(6);
Node *pNodeA = new Node('A');
Node *pNodeB = new Node('B');
Node *pNodeC = new Node('C');
Node *pNodeD = new Node('D');
Node *pNodeE = new Node('E');
Node *pNodeF = new Node('F');
pMap->addNode(pNodeA);
pMap->addNode(pNodeB);
pMap->addNode(pNodeC);
pMap->addNode(pNodeD);
pMap->addNode(pNodeE);
pMap->addNode(pNodeF);
pMap->setValueToMatrixForUndirectedGraph(0, 1, 6);
pMap->setValueToMatrixForUndirectedGraph(0, 2, 3);
pMap->setValueToMatrixForUndirectedGraph(1, 2, 2);
pMap->setValueToMatrixForUndirectedGraph(1, 3, 5);
pMap->setValueToMatrixForUndirectedGraph(2, 3, 3);
pMap->setValueToMatrixForUndirectedGraph(2, 4, 4);
pMap->setValueToMatrixForUndirectedGraph(3, 4, 2);
pMap->setValueToMatrixForUndirectedGraph(3, 5, 3);
pMap->setValueToMatrixForUndirectedGraph(4, 5, 5);
//pMap->printMatrix();
pMap->Dijkstra(0);
pMap->Floyd();
return 0;
}
#include<iostream>
#include"Customer.h"
using namespace std;
Customer::Customer(string name, int age) {
m_strName = name;
m_iAge = age;
}
void Customer::printInfo() const {
cout << "姓名: " << m_strName << endl;
cout << "年龄:" << m_iAge << endl;
cout << endl;
}
#ifndef CUSTOMER_H
#define CUSTOMER_H
#include<string>
using namespace std;
class Customer {
public:
Customer(string name="", int age=0);
void printInfo() const;
private:
string m_strName;
int m_iAge;
};
#endif
#include"MyQueue.h"
#include<iostream>
using namespace std;
MyQueue::MyQueue(int queueCapacity) {
m_iQueueCapacity = queueCapacity; // 队列的容量
m_pQueue = new Customer[m_iQueueCapacity]; // new一个新的队列
ClearQueue(); // 清空队列,确保队列为空
}
MyQueue::~MyQueue() {
delete []m_pQueue; // 删除队列
m_pQueue = NULL;
}
void MyQueue::ClearQueue() {
// 初始化队列的头、尾、队列长度
m_iHead = 0;
m_iTail = 0;
m_iQueueLen = 0;
}
bool MyQueue::QueueEmpty() {
return m_iQueueLen == 0 ? true:false; // 如果队列长度为空就返回true否则返回false
}
bool MyQueue::QueueFull() {
return m_iQueueLen == m_iQueueCapacity ? true:false; // 如果队列长度达到了队列容量大小就返回true否则就返回false
}
int MyQueue::QueueLength() {
return m_iQueueLen; // 返回队列的长度
}
bool MyQueue::EnQueue(Customer element) {
/*
* 在队列没有满的情况下,
* 向队尾进行赋值,队尾指针后移
* 队尾指针重新赋值为,队尾指针对队列容量取余
* 队列长度+1
*/
if (QueueFull()) {
return false;
}
m_pQueue[m_iTail] = element;
m_iTail++;
m_iTail = m_iTail % m_iQueueCapacity;
m_iQueueLen++;
return true;
}
bool MyQueue::DeQueue(Customer &element) {
/*
* 如果队列不为空的话,
* 取出队首元素,队首指针后移
* 队首指针重新赋值为,队首指针对队列容量取余
*/
if (QueueEmpty()) {
return false;
}
element = m_pQueue[m_iHead];
m_iHead++;
m_iHead = m_iHead % m_iQueueCapacity;
m_iQueueLen--;
return true;
}
void MyQueue::QueueTraverse() {
// 遍历队列元素,从队头到队列长度+队头,
cout << endl;
for (int i = m_iHead; i < m_iQueueLen + m_iHead; i++ ) {
m_pQueue[i%m_iQueueCapacity].printInfo();
cout << "前面还有" << (i-m_iHead) << "人" << endl;
}
cout << endl;
}
#ifndef MYQUEUE_H
#define MYQUEUE_H
#include "Customer.h"
class MyQueue {
public:
MyQueue(int queueCapacity); // InitQueue(&Q) 创建队列
~MyQueue(); // DestroyQueue(&Q) 销毁队列
void ClearQueue(); // ClearQueue(&Q) 清空队列
bool QueueEmpty(); // QueueEmpty(Q) 判空队列
bool QueueFull();
int QueueLength(); // QueueLength(Q) 队列长度
bool EnQueue(Customer element); // EnQueue(&Q, element) 新元素入队
bool DeQueue(Customer &element); // DeQueue(&Q, &element) 首元素出队
void QueueTraverse(); // QueueTraverse(Q, visit()) 遍历队列
private:
Customer *m_pQueue; // 队列数组指针
int m_iQueueLen; // 队列元素个数
int m_iQueueCapacity; // 队列数组容量
int m_iHead;
int m_iTail;
};
# endif
#include<iostream>
#include<stdlib.h>
#include"MyQueue.h"
using namespace std;
int main(void) {
MyQueue *p = new MyQueue(4);
// 插入
p->EnQueue(10);
p->EnQueue(12);
p->EnQueue(16);
p->EnQueue(20);
p->EnQueue(22); // 第五个元素插入失败
cout << "插入4个元素后遍历" << endl;
p->QueueTraverse(); // 遍历
// 删除头部元素
int e = 0;
p->DeQueue(e);
cout << "第一次删除头部元素" << endl;
cout << e << endl;
// 继续删除头部元素
p->DeQueue(e);
cout << "第二次删除头部元素" << endl;
cout << e << endl;
cout << "两次删除后的遍历" << endl;
cout << endl;
p->QueueTraverse(); // 遍历
// 删除所有元素
p->ClearQueue();
cout << "删除所有元素后的遍历" << endl;
p->QueueTraverse(); // 遍历
// 新插入两个元素
p->EnQueue(20);
p->EnQueue(30);
cout << "新插入两个元素进行遍历" << endl;
p->QueueTraverse();
delete p;
p = NULL;
//system("pause");
return 0;
}
#include<iostream>
#include<stdlib.h>
#include"MyQueue.h"
#include"Customer.h"
using namespace std;
int main(void) {
MyQueue *p = new MyQueue(4);
Customer c1("zhangsan", 20);
Customer c2("lisi", 30);
Customer c3("wangwu", 24);
p->EnQueue(c1);
p->EnQueue(c2);
p->EnQueue(c3);
Customer c4("", 0);
p->DeQueue(c4);
c4.printInfo();
p->QueueTraverse();
p->QueueTraverse();
delete p;
p = NULL;
//system("pause");
return 0;
}
文件已添加
文件已添加
## 数据结构
> 来源:[数据结构探险系列代码](https://www.imooc.com/u/1349694/courses?sort=publish)
>
> 贡献者:[wnma3mz](https://github.com/wnma3mz)
>
> 欢迎任何人参与和完善:一个人可以走的很快,但是一群人却可以走的更远。
- [ApacheCN 机器学习交流群 629470233](http://shang.qq.com/wpa/qunwpa?idkey=30e5f1123a79867570f665aa3a483ca404b1c3f77737bc01ec520ed5f078ddef)
- [ApacheCN 学习资源](http://www.apachecn.org/)
目前,只在课程基础上用C++实现了一遍,并加入了堆和平衡二叉树的实现。欢迎提交完善C++版本和其他语言版本。若有疑问,欢迎提issus。
## 说明
C++在Linux下编译
1. `g++ *.cpp -o outfile`
2. `./outfile`
### Stack/
栈的实现
| 名字 | 功能 |
| -------------- | ------------------------------------- |
| `MyStack.h` | 实现了栈的数据结构,并且可拓展 |
| `MyStack.cpp` | 可以不需要编译,因为代码已经转移到了`MyStack.h`中,为了实现模板 |
| `Coordinate.h` | 实现坐标类,`m_iX``m_iY` |
| `demo.cpp` | 检验栈的实现 |
| `demo2.cpp` | 引入新的`Coordinate.h`实现栈 |
| `demo3.cpp` | 实现进制转换 |
| `demo4.cpp` | 实现符号匹配`[(){}]` |
### Queue/
队列的实现(环形队列)
| 名字 | 功能 |
| ------------ | --------------------------------- |
| `MyQueue.h` | 实现了队列的数据结构,编译的时候需要使用`MyQueue.cpp` |
| `Customer.h` | 实现顾客类,`m_strName``m_iAge` |
| `demo.cpp` | 检验队列的实现 |
| `demo2.cpp` | 引入`Customer.h`实现队列 |
### List/
**线性表**的实现(**顺序表****单向表**
| 名字 | 功能 |
| -------------- | ---------------------------------------- |
| `List.h` | 实现**顺序表** |
| `List2.h` | 实现**单链表** |
| `Node.h` | 实现`List2.h`中需要的`Node`类,`Person`类和`Node`类的指针 |
| `Person.h` | 使用`Person`类,有两个属性`name``phone` |
| `Coordinate.h` | 实现坐标类,同`stack_demo/` |
| `demo.cpp` | 检验**顺序表**的实现 |
| `demo2.cpp` | 引入`Coordinate.h`实现**顺序表** |
| `demo3.cpp` | 引入`Node.h`检验**单链表**的实现 |
| `demo4.cpp` | 引入`Person.h`实现**单链表** |
### Tree/
树的实现(二叉树数组和二叉树链表)
| 名字 | 功能 |
| ----------- | ---------------------------------------- |
| `Tree.h` | 实现二叉树数组 |
| `Tree2.h` | 实现二叉树链表 |
| `Node.h` | 实现二叉树链表所需要的结点。`index`索引、`data`数值、`Node *pLChild, *pRChild, *pParent`左右孩子父结点 |
| `demo.cpp` | 检验二叉树数组的实现 |
| `demo2.cpp` | 检验二叉树链表的实现 |
| `BSTree.h` | 实现二叉查找树 |
| `demo3.cpp` | 检验二叉查找树的实现 |
|`AVLTree.h`|平衡二叉树|
### Map/
图的实现
| 名字 | 功能 |
| ----------- | ---------------------------------------- |
| `CMap.h` | 实现图 |
| `Edge.h` | 实现图所需要的边,`nodeIndexA``nodeIndexB``weightValue`,节点A、B的索引及对应边的权值 |
| `Node.h` | 实现图所需要的点,`m_cData`节点的值,`m_bIsVisted`改节点是否被访问过 |
| `demo.cpp` | 检验图的实现,深度优先遍历和广度优先遍历 |
| `demo2.cpp` | 检验最小生成树算法的实现,普利姆最小生成树和克鲁斯卡尔最小生成树 |
| `demo3.cpp` | 检验最短路径算法的实现,迪杰斯特拉最短路径和弗洛伊德最短路径 |
### Heap/
堆的实现
| 名字 | 功能 |
| ----------- | ---------------------------------------- |
| `Heap.cpp` | 最小堆排序 |
\ No newline at end of file
#include"Coordinate.h"
#include<iostream>
using namespace std;
Coordinate::Coordinate(int x, int y) {
m_iX = x;
m_iY = y;
}
void Coordinate::printCoordinate() {
cout << "(" << m_iX << "," << m_iY << ")" << endl;
}
ostream &operator<<(ostream &out, Coordinate &coor) { // 实现重载输出运算符
out << "(" << coor.m_iX << "," << coor.m_iY << ")" << endl;
return out;
}
#ifndef COORDINATE_H
#define COORDINATE_H
#include <stdio.h>
#include<ostream>
using namespace std;
class Coordinate {
friend ostream &operator<<(ostream &out, Coordinate &coor); // 实现重载
public:
Coordinate(int x = 0, int y = 0);
void printCoordinate();
private:
int m_iX;
int m_iY;
};
#endif
#include"MyStack.h"
#include"Coordinate.h"
#include<iostream>
using namespace std;
template <typename T> // 实现模板操作,声明T
MyStack<T>::MyStack(int size) {
m_iSize = size;
m_pBuffer = new T[m_iSize];
m_iTop = 0;
}
template <typename T>
MyStack<T>::~MyStack() {
delete []m_pBuffer;
}
template <typename T>
bool MyStack<T>::stackEmpty() {
return 0 == m_iTop ? true:false // if(m_iTop == 0)
}
template <typename T>
bool MyStack<T>::stackFull() {
return m_iSize == m_iTop ? true:false // >=
}
template <typename T>
void MyStack<T>::clearStack() {
m_iTop = 0;
}
int MyStack<T>::stackLength() {
return m_iTop;
}
template <typename T>
bool MyStack<T>::push(T elem) {
if (stackFull()) {
return false;
}
m_pBuffer[m_iTop] = elem;
m_iTop++;
return true;
}
template <typename T>
bool MyStack<T>::pop(T &elem) {
if (stackEmpty()) {
return false;
}
m_iTop--;
elem = m_pBuffer[m_iTop];
return true;
}
template <typename T>
void MyStack<T>::stackTraverse(bool isFromButtom) {
if (isFromButtom) {
for (int i = 0; i < m_iTop; i++) {
// m_pBuffer[i].printCoordinate();
cout << m_pBuffer[i];
}
} else {
for (int i = m_iTop - 1; i >= 0; i--) {
// m_pBuffer[i].printCoordinate();
cout << m_pBuffer[i];
}
}
}
#ifndef MYSTACK_H
#define MYSTACK_H
#include "Coordinate.h"
#include<stdio.h>
#include<iostream>
using namespace std;
template <typename T>
class MyStack {
public:
MyStack(int size); // 分配内存初始化栈空间,设定栈容量,栈顶
~MyStack(); // 回收栈空间内存
bool stackEmpty(); // 判定栈是否为空,为空返回true,否则返回false
bool stackFull() ; // 判定栈是否已满,为满返回true,否则返回false
void clearStack(); // 清空栈
int stackLength(); // 栈中已有元素个数
bool push(T elem); // 元素入栈,栈顶上升
bool pop(T &elem); // 元素出栈,栈顶下降
void stackTraverse(bool isFromButtom); // 遍历栈中所有元素
private:
T *m_pBuffer; // 栈空间指针
int m_iSize; // 栈容量
int m_iTop; // 栈顶或者栈中元素个数,不需要额外一个变量说明栈中个数和位置,因为这是栈
};
template <typename T>
MyStack<T>::MyStack(int size) {
m_iSize = size;
m_pBuffer = new T[m_iSize];
m_iTop = 0;
}
template <typename T>
MyStack<T>::~MyStack() {
delete []m_pBuffer;
}
template <typename T>
bool MyStack<T>::stackEmpty() {
if (0 == m_iTop) { // if(m_iTop == 0)
return true;
}
return false;
}
template <typename T>
bool MyStack<T>::stackFull() {
if (m_iSize == m_iTop) { // >=
return true;
}
return false;
}
template <typename T>
void MyStack<T>::clearStack() {
m_iTop = 0;
}
template <typename T>
int MyStack<T>::stackLength() {
return m_iTop;
}
template <typename T>
bool MyStack<T>::push(T elem) {
if (stackFull()) {
return false;
}
m_pBuffer[m_iTop] = elem; // 因为栈的特性,所以可以直接在最后一个位置进行赋值
m_iTop++;
return true;
}
template <typename T>
bool MyStack<T>::pop(T &elem) {
if (stackEmpty()) {
return false;
}
m_iTop--;
elem = m_pBuffer[m_iTop]; // 因为栈的特性,可以直接将m_iTop减1
return true;
}
template <typename T>
void MyStack<T>::stackTraverse(bool isFromButtom) {
// 遍历栈,由isFromButtom决定顺序
if (isFromButtom) {
for (int i = 0; i < m_iTop; i++) {
// m_pBuffer[i].printCoordinate();
cout << m_pBuffer[i];
}
} else {
for (int i = m_iTop - 1; i >= 0; i--) {
// m_pBuffer[i].printCoordinate();
cout << m_pBuffer[i];
}
}
}
#endif
#include<iostream>
#include"stdlib.h"
#include"MyStack.h"
using namespace std;
int main(void) {
MyStack *pStack = new MyStack(5);
pStack->push('h'); // 栈底
pStack->push('e');
pStack->push('l');
pStack->push('l');
pStack->push('o'); // 栈顶
pStack->stackTraverse(true);
char elem = 0;
pStack->pop(elem);
cout << elem << endl;
pStack->stackTraverse(false);
pStack->clearStack();
cout << pStack->stackLength() << endl;
if (pStack->stackEmpty()) {
cout << "栈为空" << endl;
}
if (pStack->stackFull()) {
cout << "栈为满" << endl;
}
delete pStack;
pStack = NULL;
// system("pause");
return 0;
}
#include<iostream>
#include"stdlib.h"
#include"MyStack.h"
#include"Coordinate.h"
using namespace std;
int main(void) {
MyStack<char> *pStack = new MyStack<char>(5);
Coordinate coor1(1,2);
Coordinate coor2(3,2);
pStack->push('2'); // 栈底
pStack->push('2');
pStack->stackTraverse(true);
pStack->stackTraverse(false);
pStack->clearStack();
cout << pStack->stackLength() << endl;
if (pStack->stackEmpty()) {
cout << "栈为空" << endl;
}
if (pStack->stackFull()) {
cout << "栈为满" << endl;
}
delete pStack;
pStack = NULL;
// system("pause");
return 0;
}
#include<iostream>
#include"stdlib.h"
#include"MyStack.h"
#include"Coordinate.h"
using namespace std;
#define BINARY 2
#define OCTONARY 8
#define HEXADECIMAL 16
int main(void) {
char num[] = "0123456789ABCDEF";
MyStack<int> *pStack = new MyStack<int>(30);
int N = 2016;
int mod = 0;
while (N > 0) {
mod = N % HEXADECIMAL;
pStack->push(mod);
N /= HEXADECIMAL;
}
/* for (int i =pStack->stackLength() - 1; i >= 0;)
{
num[pStack]
}
*/
int elem = 0;
while (!pStack->stackEmpty()) {
pStack->pop(elem);
cout << num[elem];
}
//pStack->stackTraverse(false);
delete pStack;
pStack = NULL;
return 0;
}
#include<iostream>
#include"stdlib.h"
#include"MyStack.h"
#include"Coordinate.h"
using namespace std;
int strlen(const char *str) {
// 实现strlen,返回字符串长度
if ('\0' == *str)
return 0;
else
return strlen(str + 1) + 1;
}
int main(void) {
MyStack<char> *pStack = new MyStack<char>(30);
MyStack<char> *pNeedStack = new MyStack<char>(30);
char str[] = "[()])";
char currentNeed = 0;
for (int i = 0; i < strlen(str); i++) {
if (str[i] != currentNeed) {
pStack->push(str[i]);
switch(str[i]) {
case '[':
if (currentNeed != 0) {
pNeedStack->push(currentNeed);
}
currentNeed = ']';
break;
case '(':
if (currentNeed != 0) {
pNeedStack->push(currentNeed);
}
currentNeed = ')';
break;
case '{':
if (currentNeed != 0) {
pNeedStack->push(currentNeed);
}
currentNeed = '}';
break;
default:
cout << "fail" << endl;
return 0;
}
} else {
char elem;
pStack->pop(elem);
pNeedStack->pop(currentNeed);
}
}
if (pStack->stackEmpty()) {
cout << "success" << endl;
} else {
cout << "fail" << endl;
}
delete pStack;
pStack = NULL;
delete pNeedStack;
pNeedStack = NULL;
return 0;
}
文件已添加
#ifndef __AVLTREE_H__
#define __AVLTREE_H__
#include <stdlib.h>
#include <iostream>
using namespace std;
struct AVLNode {
int _key;
AVLNode* _lchild;
AVLNode* _rchild;
AVLNode* _parent;
int nHeight;
};
class AVLTree {
public:
AVLTree() : _Root(NULL), nNodeCount(0) {}
~AVLTree() {
DeleteTree(&_Root);
}
public:
int Insert(int _key); // 插入节点
int Delete(int _key); // 删除节点
int Find(int _key) const; // 查找节点
int GetNodeCount() const; // 获取总共的节点数
void Display() const; // 打印AVL树
private:
int Max(int a, int b) const; // 用于比较获取最大的节点
int Height(const AVLNode* pNode) const; // 获取AVL树的高度
AVLNode* CreateNode(int _key); // 构造新节点
AVLNode* DeleteNode(int _key, AVLNode* pNode); // 删除节点
AVLNode* BalanceAdjust(AVLNode* pNode); // 平衡二叉树
AVLNode* RotateLeft(AVLNode* pNode); // 左旋
AVLNode* RotateRight(AVLNode* pNode); // 右旋
AVLNode* RotateLeftRight(AVLNode* pNode); // 左右旋
AVLNode* RotateRightLeft(AVLNode* pNode); // 右左旋
void DeleteTree(AVLNode** p_Root); // 删除树
void PrintTree(AVLNode* pNode) const; // 打印AVL树
AVLTree(const AVLTree&) {}
AVLTree& operator=(const AVLTree&) {}
private:
AVLNode* _Root;
int nNodeCount;
};
int AVLTree::Max(int a, int b) const {
// 返回两个数大的那个数
return (a > b ? a : b);
}
int AVLTree::Height(const AVLNode* pNode) const {
if (NULL == pNode)
return -1;
return pNode->nHeight;
}
int AVLTree::Insert(int _key) {
// 如果树不存在,就构造树
if(_Root == NULL) {
_Root = CreateNode(_key);
return nNodeCount;
}
AVLNode* pInsertNode = _Root;
// 遍历树直到到叶子节点为止
while (pInsertNode != NULL) {
// 如果待插入的数小于当前节点,就遍历左边
if (_key < pInsertNode->_key) {
// 如果当前节点的左节点为NULL, 就将待插入的数放入到当前节点的左节点
if (pInsertNode->_lchild == NULL) {
pInsertNode->_lchild = CreateNode(_key);
pInsertNode->_lchild->_parent = pInsertNode;
// 平衡树
_Root = BalanceAdjust(pInsertNode->_lchild);
break;
}
// 如果当前节点的左节点不为NULL, 就继续向当前节点的左节点遍历
pInsertNode = pInsertNode->_lchild;
}
// 如果待插入的数大于当前节点,就遍历右边
if (_key > pInsertNode->_key) {
// 基本步骤同上
if (pInsertNode->_rchild == NULL) {
pInsertNode->_rchild = CreateNode(_key);
pInsertNode->_rchild->_parent = pInsertNode;
_Root = BalanceAdjust(pInsertNode->_rchild);
break;
}
pInsertNode = pInsertNode->_rchild;
}
// 如果待插入的节点等于当前节点,就不进行插入
if (_key == pInsertNode->_key) {
return nNodeCount;
}
}
return nNodeCount;
}
int AVLTree::Delete(int _key) {
//cout << "Delete _key = " << _key << endl;
//cout << "pNode->_key = " << pNode->_key << endl;
//cout << "pPNode->_key = " << pPNode->_key << endl;
AVLNode* pCurNode = _Root;
// 如果当前节点不为NULL, 开始进行查找
while(pCurNode != NULL) {
// 如果需要删除的数大于当前节点值,就继续向左节点搜索
if (_key > pCurNode->_key)
pCurNode = pCurNode->_rchild;
// 如果需要删除的数小于当前节点值,就继续向右节点搜索
if (_key < pCurNode->_key)
pCurNode = pCurNode->_lchild;
// 如果需要删除的数等于当前节点值,则删除当前节点
if (_key == pCurNode->_key)
_Root = DeleteNode(_key, pCurNode);
break;
}
// 如果当前节点遍历到最后,没有找到该元素,则进行输出
if (pCurNode == NULL)
cout << "没有找到元素 _key = " << _key << endl;
// int x;
// cin >> x;
// 没有找到要删除的元素
return nNodeCount;
}
AVLNode* AVLTree::DeleteNode(int _key, AVLNode* pNode) {
// 首先减少树的节点个数
nNodeCount--;
// 如果删除的节点有左右子树
if (pNode->_lchild && pNode->_rchild) {
// 从删除节点的左子树中,找出最大的节点代替删除节点
AVLNode* pLMaxNode = pNode->_lchild;
// 删除节点左子树中最大节点的父节点
AVLNode* pLMaxPNode = pNode;
// 如果删除节点的左节点不存在右节点
if (pLMaxNode->_rchild == NULL) {
// 可以直接将这个节点换为待删除节点
pNode->_key = pLMaxNode->_key;
pNode->_lchild = pLMaxNode->_lchild;
// 将待删除节点的左节点的左子树,全部重新衔接到当前待删除节点(删除节点的左节点需要删除)
if (pLMaxNode->_lchild != NULL)
pLMaxNode->_lchild->_parent = pNode;
// 如果删除节点的左节点存在右节点,就在右节点的子树搜索
} else {
// 一直搜索找到没有右子树为止(存储节点的父节点),即找到最大节点
while (pLMaxNode->_rchild) {
pLMaxPNode = pLMaxNode;
pLMaxNode = pLMaxNode->_rchild;
}
// 将待删除节点的值改为找到的最大节点的值
pNode->_key = pLMaxNode->_key;
// 找到最大节点为父节点的左节点还是右节点,如果是右节点就将最大节点的子树接到最大节点的父节点上
if (pLMaxNode == pLMaxPNode->_rchild)
pLMaxPNode->_rchild = pLMaxNode->_lchild;
// 如果是左节点,它已经是叶子节点了,就将这个节点赋值为NULL
else if (pLMaxNode == pLMaxPNode->_lchild)
pLMaxPNode->_lchild = NULL;
}
// 删除最大节点
delete pLMaxNode;
return BalanceAdjust(pLMaxPNode);
// 如果删除的节点只有左节点
} else if (pNode->_lchild) {
// 删除节点的左子树
AVLNode* _lchild = pNode->_lchild;
pNode->_key = _lchild->_key;
// 将左子树的左节点赋值为待删除节点的左节点
pNode->_lchild = _lchild->_lchild;
// 如果左子树的左节点存在,那么左节点赋值为待删除节点
if (_lchild->_lchild)
_lchild->_lchild->_parent = pNode;
// 将左子树的右节点赋值为待删除节点的右节点
pNode->_rchild = _lchild->_rchild;
// 如果左子树的右节点存在,那么就将右节点赋值为待删除节点
if (_lchild->_rchild)
_lchild->_rchild->_parent = pNode;
delete _lchild;
return BalanceAdjust(pNode);
// 删除节点只有右子树
} else if (pNode->_rchild) {
AVLNode* _rchild = pNode->_rchild;
pNode->_key = _rchild->_key;
pNode->_lchild = _rchild->_lchild;
if (_rchild->_lchild != NULL)
_rchild->_lchild->_parent = pNode;
pNode->_rchild = _rchild->_rchild;
if (_rchild->_rchild != NULL)
_rchild->_rchild->_parent = pNode;
delete _rchild;
return BalanceAdjust(pNode);
//删除节点没有子树
} else {
AVLNode* pPNode = pNode->_parent;
if (pPNode->_lchild == pNode)
pPNode->_lchild = NULL;
else if (pPNode->_rchild == pNode)
pPNode->_rchild = NULL;
delete pNode;
return BalanceAdjust(pPNode);
}
}
AVLNode* AVLTree::BalanceAdjust(AVLNode* pNode) {
AVLNode* _Root;
AVLNode* pPNode;
while(pNode != NULL) { //删除节点的子节点进行平衡
pPNode = pNode->_parent;
bool bIsLeft = false;
if(pPNode != NULL && pNode == pPNode->_lchild)
bIsLeft = true;
pNode->nHeight = Max(Height(pNode->_lchild), Height(pNode->_rchild)) + 1;
if (Height(pNode->_lchild) - Height(pNode->_rchild) == 2) { // AVL树不平衡 执行LL型或者LR型旋转
if (Height(pNode->_lchild->_lchild) - Height(pNode->_lchild->_rchild) == -1)
pNode = RotateLeftRight(pNode);
else
pNode = RotateLeft(pNode);
if(pPNode != NULL && bIsLeft)
pPNode->_lchild = pNode;
else if(pPNode != NULL)
pPNode->_rchild = pNode;
} else if(Height(pNode->_lchild) - Height(pNode->_rchild) == -2) { // AVL树不平衡 执行RR型或者RL型旋转
if (Height(pNode->_rchild->_lchild) - Height(pNode->_rchild->_rchild) == 1)
pNode = RotateRightLeft(pNode);
else
pNode = RotateRight(pNode);
if (pPNode != NULL && bIsLeft)
pPNode->_lchild = pNode;
else if(pPNode != NULL)
pPNode->_rchild = pNode;
}
_Root = pNode;
pNode = pPNode;
}
return _Root;
}
AVLNode* AVLTree::CreateNode(int _key) {
nNodeCount++;
AVLNode* pNewNode = new AVLNode();
pNewNode->_key = _key;
pNewNode->nHeight = 0;
pNewNode->_lchild = pNewNode->_rchild = NULL;
return pNewNode;
}
int AVLTree::Find(int _key) const {
AVLNode* pFindNode = _Root;
while(pFindNode) {
if(_key < pFindNode->_key)
pFindNode = pFindNode->_lchild;
else if(_key > pFindNode->_key)
pFindNode = pFindNode->_rchild;
else
return pFindNode->_key;
}
return -1;
}
AVLNode* AVLTree::RotateLeft(AVLNode* pNode) { //左单
AVLNode* _lchildChild;
_lchildChild = pNode->_lchild;
pNode->_lchild = _lchildChild->_rchild;
_lchildChild->_rchild = pNode;
_lchildChild->_parent = pNode->_parent;
pNode->_parent = _lchildChild;
if(pNode->_lchild)
pNode->_lchild->_parent = pNode;
// 结点的位置改变,节点高度要重新计算
pNode->nHeight = Max(Height(pNode->_lchild), Height(pNode->_rchild)) + 1;
_lchildChild->nHeight = Max(Height(_lchildChild->_lchild), pNode->nHeight) + 1;
return _lchildChild;
}
AVLNode* AVLTree::RotateRight(AVLNode* pNode) { //右单
AVLNode* _rchildChild;
_rchildChild = pNode->_rchild;
pNode->_rchild = _rchildChild->_lchild;
_rchildChild->_lchild = pNode;
_rchildChild->_parent = pNode->_parent;
pNode->_parent = _rchildChild;
if(pNode->_rchild)
pNode->_rchild->_parent = pNode;
// 结点的位置改变,节点高度要重新计算
pNode->nHeight = Max(Height(pNode->_lchild), Height(pNode->_rchild)) + 1;
_rchildChild->nHeight = Max(Height(_rchildChild->_rchild), pNode->nHeight) + 1;
return _rchildChild;
}
AVLNode* AVLTree::RotateLeftRight(AVLNode* pNode) { //左双
pNode->_lchild = RotateRight(pNode->_lchild);
return RotateLeft(pNode);
}
AVLNode* AVLTree::RotateRightLeft(AVLNode* pNode) { //右双
pNode->_rchild = RotateLeft(pNode->_rchild);
return RotateRight(pNode);
}
// 后序遍历树以删除树
void AVLTree::DeleteTree(AVLNode** p_Root) {
if (NULL == p_Root || NULL == *p_Root)
return;
DeleteTree(&((*p_Root)->_lchild));
DeleteTree(&((*p_Root)->_rchild));
delete *p_Root;
*p_Root = NULL;
}
int AVLTree::GetNodeCount() const {
return nNodeCount;
}
void AVLTree::Display() const {
PrintTree(this->_Root);
}
void AVLTree::PrintTree(AVLNode* pNode) const {
if (NULL == _Root)
return;
if (NULL == pNode) {
return;
}
static int n = 0;
if(pNode == _Root) {
std::cout << "[" << ++n << "]_key = " << pNode->_key << ",nParentData= 0 ,";
if(pNode->_lchild)
std::cout << "nLeftData= " << pNode->_lchild->_key << " ,";
if(pNode->_rchild)
std::cout << "nRightData= " << pNode->_rchild->_key << " ,";
std::cout << "nHeight = " << pNode->nHeight << std::endl;
} else {
std::cout << "[" << ++n << "]_key = " << pNode->_key << ",nParentData= " << pNode->_parent->_key << " ,";
if(pNode->_lchild)
std::cout << "nLeftData= " << pNode->_lchild->_key << " ,";
if(pNode->_rchild)
std::cout << "nRightData= " << pNode->_rchild->_key << " ,";
std::cout << "nHeight = " << pNode->nHeight << std::endl;
}
PrintTree(pNode->_lchild);
PrintTree(pNode->_rchild);
}
#endif //__AVLTREE_H__
#ifndef _BINARY_SEARCH_TREE_
#define _BINARY_SEARCH_TREE_
#include <iostream>
using namespace std;
template<typename T>
//树结点结构
class BSTNode {
public:
T _key; // 节点值
BSTNode *_lchild; // 左孩子
BSTNode *_rchild; // 右孩子
BSTNode *_parent; // 父节点
//构造函数
BSTNode(T key,BSTNode *lchild,BSTNode *rchild,BSTNode *parent):
_key(key),_lchild(lchild),_rchild(rchild),_parent(parent) {};
};
template<typename T>
class BSTree {
private:
BSTNode<T> *_Root ; //根结点
public:
BSTree():_Root(NULL) {};
~BSTree() {};
void insert(T key); // 插入
BSTNode<T>* search(T key); //查找
void preOrder(); //前序遍历
void inOrder(); //中序遍历
void postOrder(); //后序遍历
BSTNode<T>* minimumNode(); //查找最小的节点
BSTNode<T>* maximumNode(); //查找最大的节点
T minimumKey(); //查找最小的键值
T maximumKey(); //查找最大的键值
void print(); //打印二叉树
void remove(T key); //移除某个节点
BSTNode<T>* predecessor(BSTNode<T>* x);//查找某个结点的前驱,树中所有数取出之后排序从小到大排序,靠近x左边的数
BSTNode<T>* sucessor(BSTNode<T>* x); //查找某个结点的后继,树中所有数取出之后排序从小到大排序,靠近x右边的数
void destory ();
//内部使用函数,供外部接口调用
private:
void insert(BSTNode<T>* &tree,BSTNode<T>* z);
BSTNode<T>* search(BSTNode<T>* &tree,T key) const;
void preOrder(BSTNode<T>*&tree) const;
void inOrder(BSTNode<T>*&tree) const;
void postOrder(BSTNode<T>*&tree) const;
BSTNode<T>* minimumNode(BSTNode<T> *&tree);
BSTNode<T>* maximumNode (BSTNode<T> *&tree);
void print(BSTNode<T>*& tree);
BSTNode<T>* remove(BSTNode<T>* &tree, BSTNode<T> *z);
void destory(BSTNode<T>*& tree);
};
// 非递归实现,内部使用函数
template<typename T>
void BSTree<T> ::insert(BSTNode<T>* &tree,BSTNode<T>* z) {
BSTNode<T>* parent = NULL;
BSTNode<T>* temp = tree;
//寻找插入点
while(temp != NULL) {
parent = temp;
// 待插入的值大于当前值,就继续寻找右节点,否则寻找左节点
temp = (z->_key > temp->_key) ? temp->_rchild : temp->_lchild;
}
// 当前节点的父节点为刚刚找到的节点
z->_parent = parent;
// 如果树为空树,就直接把节点插入到根节点中;如果小于父节点就为父节点的左节点,否则为右节点
if (parent == NULL)
tree = z;
else if(z->_key > parent->_key)
parent->_rchild = z;
else
parent->_lchild = z;
}
// 接口
template <typename T>
void BSTree<T>::insert(T key) {
// 创建一个新的节点,使用构造函数初始化
BSTNode<T>* z= new BSTNode<T>(key, NULL, NULL, NULL);
if (!z) //如果创建失败就直接返回
return ;
//调用内部函数进行插入
insert(_Root, z);
}
// 非递归实现,内部使用函数
template <typename T>
BSTNode<T>* BSTree<T>::search(BSTNode<T>* &tree,T key) const {
BSTNode<T>* temp = tree;
// 因为是二叉查找树,所以只需要对左或右节点进行遍历
while(temp != NULL) {
if (temp->_key == key)
return temp;
else if (temp->_key > key)
temp = temp->_lchild;
else
temp = temp->_rchild;
}
return NULL;
}
/*//查找算法的递归实现
template<typename T>
BSTNode<T>* BSTree<T>::search( BSTNode<T>* &tree,T key) const
{
if(!tree)
{
if(tree->_key==key)
return tree;
if(tree->_key>key)
return search(tree->_lchild,key);
if(tree->_key<z->_key)
return search(tree->_rchild,key);
}
return NULL;
}
*/
// 接口
template <typename T>
BSTNode<T> * BSTree<T>::search(T key) {
return search(_Root,key);
}
// 三种遍历, 内部调用的接口
template<typename T>
void BSTree<T>::preOrder(BSTNode<T>*&tree) const {
if(tree) {
cout<<tree->_key<<" ";
preOrder(tree->_lchild);
preOrder(tree->_rchild);
}
}
template <typename T>
void BSTree<T>::inOrder(BSTNode<T>*&tree) const {
if(tree) {
inOrder(tree->_lchild);
cout<<tree->_key<<" ";
inOrder(tree->_rchild);
}
}
template <typename T>
void BSTree<T>::postOrder(BSTNode<T>*&tree) const {
if(tree) {
postOrder(tree->_lchild);
postOrder(tree->_rchild);
cout<<tree->_key<<" ";
}
}
// 外部调用的接口
template<typename T>
void BSTree<T>::preOrder() {
preOrder(_Root);
}
template<typename T>
void BSTree<T>::inOrder() {
inOrder(_Root);
}
template<typename T>
void BSTree<T>::postOrder() {
postOrder(_Root);
}
// 查找最小的结点,内部调用函数
template <typename T>
BSTNode<T>* BSTree<T>::minimumNode(BSTNode<T>*&tree) {
BSTNode<T>* temp = tree;
// 因为左节点小于右节点,所以只需要在左节点进行查找
while (temp->_lchild) {
temp= temp->_lchild;
}
return temp;
}
// 接口
template<typename T>
BSTNode<T>* BSTree<T>::minimumNode() {
return minimumNode(_Root);
}
// 查找最大的节点,内部调用函数
template<typename T>
BSTNode<T>* BSTree<T>::maximumNode(BSTNode<T>* &tree) {
// 因为左节点小于右节点,所以只需要在右节点进行查找
BSTNode<T>* temp=tree;
while (temp->_rchild) {
temp= temp->_rchild;
}
return temp;
}
// 接口
template<typename T>
BSTNode<T>* BSTree<T>::maximumNode() {
return maximumNode(_Root);
}
// 查找最小的节点值,外部接口函数, 调用内部函数minimumNode实现
template<typename T>
T BSTree<T>::minimumKey() {
BSTNode<T> *temp = minimumNode(_Root);
return temp->_key;
}
// 查找最大的节点值,外部接口函数, 调用内部函数maximumNode实现
template<typename T>
T BSTree<T>::maximumKey() {
BSTNode<T> *temp = maximumNode(_Root);
return temp->_key;
}
// 打印出平衡二叉树
template<typename T>
void BSTree<T>::print(BSTNode<T>*& tree) {
//如果tree不为空
if (tree) {
//结点有左孩子
if (tree->_lchild) {
cout << "节点" << tree->_key << "有左孩子为" << tree->_lchild->_key << endl;
} else {
cout << "节点" << tree->_key << "无左孩子" << endl;
}
if (tree->_rchild) {
cout << "节点" << tree->_key << "有右孩子为" << tree->_rchild->_key << endl;
} else {
cout << "节点" << tree->_key << "无右孩子" << endl;
}
print(tree->_lchild);
print(tree->_rchild);
}
}
//接口
template<typename T>
void BSTree<T>::print() {
print(_Root);
}
// 查找某个节点x的前驱, 外部函数调用
template <typename T>
BSTNode<T>* BSTree<T>::predecessor(BSTNode<T>* x) {
//如果x是最小的结点,则它没有前驱
if (x->_key == minimumNode(_Root)->_key)
return NULL;
//先获取二叉树中键值与x的键值相同的结点y
BSTNode<T>* y = NULL;
y = search(_Root,x->_key);
if (y == NULL)
return NULL;
//如果y有左孩子,则x的前驱为“以x的左孩为根的子树的最大结点”
if (y->_lchild != NULL)
return maximumNode(y->_lchild);
//如果y没有左孩子,则x有两种可能:
//1.y是一个右孩子,此时x的前驱为其双亲节点
BSTNode<T>* parent = y->_parent;
if (parent->_rchild == y)
return parent;
//2.y是一个左孩子,则其前驱为其”父结点的父节点中第一个拥有右孩子结点”的结点
while (parent != NULL && parent->_rchild == NULL) {
parent = parent->_parent;
}
return parent;
}
// 查找某个节点x的后继, 外部调用接口
template <typename T>
BSTNode<T>* BSTree<T>::sucessor(BSTNode<T>* x) {
//如果x是键值最大的,则x没有后继结点
if (x->_key == maximumNode(_Root)->_key)
return NULL;
//获取x在二叉树中的结点y
BSTNode<T>* y = NULL;
y = search(_Root,x->_key);
if (!y) //若二叉树没有此结点
return NULL;
//如果y有右孩子,则y的后继为其右孩子的最小结点
if (y->_rchild != NULL)
return minimumNode(y->_rchild);
//如果y没有右孩子,则可分为两种情况:
//1.y 是左孩子。此时y的后继为y的父结点
BSTNode <T>* parent = y->_parent;
if (y->_parent->_lchild == y)
return parent;
//2.y是右孩子。此时y的后继结点为“它的父节点的父节点中第一个拥有左孩”的结点
while (parent!=NULL) {
if (parent->_lchild != NULL && parent != y->_parent)
return parent;
parent = parent->_parent;
}
return NULL;
}
// 删除结点, BSTree类内部调用函数
template <class T>
BSTNode<T>* BSTree<T>::remove(BSTNode<T>* &tree, BSTNode<T> *z) {
/* 将待删除节点命名为z
* 情况一:如果z为叶子节点(没有左子树和没有右子树),那么就可以直接删除该节点再修改其父节点的指针
* 情况二:如果z为单支节点(只有左子树或者只有右子树), 那么就让z的子树连接z的父节点,再删除z
* 情况三:如果z左右子树均不为空。找到z的后继y(y一定没有左子树),所以可以删除y, 让y的父节点成为y的右子树的父节点,用y代替z
*/
BSTNode<T> *y=NULL;
BSTNode<T> *zl = z->_lchild;
BSTNode<T> *zr = z->_rchild;
BSTNode<T> *zpl = z->parent->_lchild;
BSTNode<T> *zpr = z->parent->_rchild;
// 情况一
if (zl == NULL && zr == NULL) {
if (zpl == z)
zpl = NULL;
if (zpr == z)
zpr = NULL;
return z;
}
// 情况二
if (zl == NULL || zr == NULL) {
if (zpl == z)
zpl = (zl != NULL) ? zl : zr;
if (zpr == z)
zpr = (zl != NULL) ? zl : zr;
return z;
}
// 情况三
if (zl != NULL && zr != NULL) {
y = sucessor(z);
z->_key = y->_key;
if (y->_parent->_lchild == y)
y->_parent->_lchild = NULL;
if (y->_parent->_rchild == y)
y->_parent->_rchild = NULL;
return y;
}
}
// 接口
template<typename T>
void BSTree<T>::remove(T key) {
BSTNode<T> *z, *node;
if ((z = search(_Root, key)) != NULL)
if ((node = remove(_Root, z)) != NULL)
delete node;
}
// 销毁查找二叉树, 内部调用函数
template<typename T>
void BSTree<T>::destory(BSTNode<T>*& tree) {
if(tree->_lchild != NULL)
destory(tree->_lchild);
if(tree->_rchild != NULL)
destory(tree->_rchild);
if(tree->_lchild == NULL && tree->_rchild == NULL) {
delete(tree);
tree = NULL;
}
}
// 接口
template<typename T>
void BSTree<T>::destory() {
destory(_Root);
}
#endif
#include"Node.h"
#include<iostream>
#include"stdio.h"
using namespace std;
Node::Node() {
// 进行初始化
index = 0;
data = 0;
pLChild = NULL;
pRChild = NULL;
pParent = NULL;
}
Node *Node::SearchNode(int nodeIndex) {
// 指定结点位置获取结点
// 如果是当前结点,则返回当前结点
if (this->index == nodeIndex) {
return this;
}
// 如果左右孩子存在,那么继续对左右孩子进行搜索
Node *temp = NULL;
if (this->pLChild != NULL) {
// 如果是当前孩子结点,则返回当前孩子结点,否则进行以当前孩子结点为结点继续进行搜索,这里调用了递归
if (this->pLChild->index == nodeIndex) {
return this->pLChild;
} else {
temp = this->pLChild->SearchNode(nodeIndex);
// 如果找了结点,就是temp
if (temp != NULL) {
return temp;
}
}
}
// 同上
if (this->pRChild != NULL) {
if (this->pRChild->index == nodeIndex) {
return this->pRChild;
} else {
temp = this->pRChild->SearchNode(nodeIndex);
return temp;
}
}
// 没有找到则返回NULL
return NULL;
}
void Node::DeleteNode() {
// 删除左右孩子结点,如果不为空的话,这里调用了递归,不断删除孩子结点
if (this->pLChild != NULL) {
this->pLChild->DeleteNode();
}
if (this->pRChild != NULL) {
this->pRChild->DeleteNode();
}
// 令自己为NULL,首先需要知道自己是父结点的左还是右孩子。
if (this->pParent != NULL) {
if (this->pParent->pLChild == this) {
this->pParent->pLChild = NULL;
}
if (this->pParent->pRChild == this) {
this->pParent->pRChild = NULL;
}
}
// 删除自己
delete this;
}
void Node::PreorderTraversal() {
// 前序遍历。先输出自己,再遍历左孩子,输出左孩子,再遍历右孩子,输出右孩子
cout << this->index << " " << this->data << endl;
if (this->pLChild != NULL) {
this->pLChild->PreorderTraversal();
}
if (this->pRChild != NULL) {
this->pRChild->PreorderTraversal();
}
}
void Node::InorderTraversal() {
// 中序遍历。先遍历左孩子,输出左孩子,再遍历右孩子,输出右孩子
if (this->pLChild != NULL) {
this->pLChild->InorderTraversal();
}
cout << this->index << " " << this->data << endl;
if (this->pRChild != NULL) {
this->pRChild->InorderTraversal();
}
}
void Node::PostorderTraversal() {
// 后序遍历。先遍历左孩子,再遍历右孩子,输出右孩子,输出左孩子
if (this->pLChild != NULL) {
this->pLChild->PostorderTraversal();
}
if (this->pRChild != NULL) {
this->pRChild->PostorderTraversal();
}
cout << this->index << " " << this->data << endl;
}
#ifndef NODE_H
#define NODE_H
#include"stdio.h"
class Node {
public:
Node();
Node *SearchNode(int nodeIndex); // 用Node.h来代替Tree2.h实现查找过程
void DeleteNode(); // 用Node.h来代替Tree2.h实现删除操作
void PreorderTraversal(); // 实现前序遍历
void InorderTraversal(); // 实现中序遍历
void PostorderTraversal(); // 实现后序遍历
int index;
int data;
Node *pLChild;
Node *pRChild;
Node *pParent;
};
#endif
## 关于树的一些说明
前序遍历二叉树 == 深度优先遍历(DFS)
中序遍历二叉树(按层遍历二叉树) == 广度优先遍历(BFS)
[数据结构中各种树](http://www.cnblogs.com/maybe2030/p/4732377.html#_label1)
## 二叉查找树(BSTree.h)
### 定义
又称为是二叉排序树(Binary Sort Tree)或二叉搜索树。二叉排序树或者是一棵空树,或者是具有下列性质的二叉树:
1. 若左子树不空,则左子树上所有结点的值均小于它的根结点的值;
2. 若右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值;
3. 左、右子树也分别为二叉排序树;
4. 没有键值相等的节点。
### 性质
对二叉查找树进行中序遍历,即可得到有序的数列。
  
### 时间复杂度
它和二分查找一样,插入和查找的时间复杂度均为O(logn),但是在最坏的情况下仍然会有O(n)的时间复杂度。原因在于插入和删除元素的时候,树没有保持平衡。我们希望在最坏的情况下仍然有较好的时间复杂度,这就是平衡查找树设计的初衷。二叉查找树的高度决定了二叉查找树的查找效率。
### 插入:
1. 若当前的二叉查找树为空,则插入的元素为根节点;
2. 若插入的元素值小于根节点值,则将元素插入到左子树中;
3. 若插入的元素值不小于根节点值,则将元素插入到右子树中。
### 删除:
将待删除节点命名为z
1. 如果z为叶子节点(没有左子树和没有右子树),那么就可以直接删除该节点再修改其父节点的指针
2. 如果z为单支节点(只有左子树或者只有右子树), 那么就让z的子树连接z的父节点,再删除z
3. 如果z左右子树均不为空。找到z的后继y(y一定没有左子树),所以可以删除y, 让y的父节点成为y的右子树的父节点,用y代替z
### 前驱与后继
先找到键值相同的节点
#### 前驱:
1. 如果键值最小,则没有前驱
2. 如果该节点有左孩子,则前驱为“以当前节点的左孩子为根的子树的最大节点”。应该是该节点的左孩子的右孩子(如果有的话,否则为根节点)
3. 如果该节点没有左孩子
1. 如果该节点是一个右孩子,则前驱就为它的父节点
2. 如果该节点是一个左孩子,则前驱为它父节点的父节点第一个拥有右孩子的节点(需要不断遍历)
#### 后继:
1. 如果键值最大,则没有后继
2. 如果该节点有右孩子,则后继为右孩子中的最小节点
3. 如果该节点没有右孩子
1. 如果该节点是一个左孩子,则后继就为它的父节点
2. 如果该节点是一个右孩子,则后继为它父节点的父节点第一个拥有左孩子的节点
## 平衡二叉树(AVL)
### 定义
​父节点的左子树和右子树的高度之差不能大于1,也就是说不能高过1层,否则该树就失衡了,此时就要旋转节点,在编码时,我们可以记录当前节点的高度,比如空节点是-1,叶子节点是0,非叶子节点的height往根节点递增。
### 插入
每次插入后需要平衡二叉树
1. 如果插入的数小于当前节点,遍历左边
1. 如果当前节点的左节点为NULL,则插入到当前节点的左节点中
2. 如果当前节点的左节点不为NULL,则继续遍历当前节点的左节点
2. 如果插入的数大于当前节点,遍历右边
1. 如果当前节点的右节点为NULL,则插入到当前节点的右节点中
2. 如果当前节点的右节点不为NULL,则继续遍历当前节点的右节点
3. 如果插入的数等于当前节点,则不插入
### 删除
每次删除后需要平衡二叉树
先找到需要删除的节点
1. 需要删除的数大于当前节点,继续遍历左边
2. 需要删除的数小于当前节点,继续遍历右边
3. 需要删除的数等于当前节点,进行删除
删除节点
1. 删除节点有左右子树
1. 找出左子树中最大的节点,代替删除节点(交换key值)
1. 如果删除节点的左节点没有右节点,则最大节点就是左子树的根节点。将最大节点(待删除节点的左节点),衔接到当前待删除的节点
2. 如果删除节点的左节点有右节点,则一直向右节点搜索,直到没有右节点为止(即找到了最大节点的值)。判断最大节点是父节点的左节点还是右节点。
1. 如果是右节点,就将最大节点的子树接到最大节点的父节点上
2. 如果是左节点,就说明它已经是叶子节点了,就将这个节点赋值为NULL
2. 删除节点有左子树
1. 删除节点的左子树的左节点如果存在,就将该节点赋值为待删除节点
2. 删除节点的左子树的右节点如果存在,就将该节点复制为待删除节点
3. 删除节点有右子树
同步骤2
4. 删除节点没有子树
判断删除节点是父节点的左节点还是右节点,直接删除即可
#include<iostream>
#include"Tree.h"
using namespace std;
Tree::Tree(int size, int *pRoot) {
m_iSize = size;
m_pTree = new int[m_iSize];
// 初始化树中每个结点为0
for (int i = 0; i < size; i++) {
m_pTree[i] = 0;
}
// 根节点的索引,进行赋值
m_pTree[0] = *pRoot;
}
Tree::~Tree() {
delete []m_pTree;
m_pTree = NULL;
}
int *Tree::SearchNode(int nodeIndex) {
// 指定结点索引,找到结点
if ((nodeIndex < 0) || (nodeIndex >= m_iSize)) { // 检查是否不在范围内
return NULL;
}
if (m_pTree[nodeIndex] == 0) { // 原来的结点上是否有值,如果为0则表示原来的结点没有值,寻找失败
return NULL;
}
return &m_pTree[nodeIndex];
}
bool Tree::AddNode(int nodeIndex, int direction, int *pNode) {
// 指定索引,指定方位(左右孩子),指定Node,进行插入
if ((nodeIndex < 0) || (nodeIndex >= m_iSize)) {
return false;
}
if (m_pTree[nodeIndex] == 0) { // 需要插入的父结点是否有值,如果父结点无值,则插入失败
return false;
}
// direction 定义0为左,1为右。number是根据direction算出需要插入的位置
int number = (direction == 0) ? nodeIndex * 2 + 1 : nodeIndex * 2 + 2;
if (number >= m_iSize) {
return false;
}
if (m_pTree[number] != 0) { // 插入的位置上是否有值,如果不为0,原来位置有值,插入失败
return false;
}
m_pTree[number] = *pNode;
return true;
}
bool Tree::DeleteNode(int nodeIndex, int *pNode) {
// 指定结点索引,删除结点
if ((nodeIndex < 0) || (nodeIndex >= m_iSize)) {
return false;
}
if (m_pTree[nodeIndex] == 0) {
return false;
}
// 取出结点,将原来的结点值重新赋值为0
*pNode = m_pTree[nodeIndex];
m_pTree[nodeIndex] = 0;
return true;
}
void Tree::TreeTraverse() {
// 遍历树
for (int i = 0; i < m_iSize; i++) {
cout << m_pTree[i] << " ";
}
}
#ifndef TREE_H
#define TREE_H
#include<stdio.h>
class Tree {
public:
Tree(int size, int *pRoot);
~Tree();
int *SearchNode(int nodeIndex); //根据索引寻找结点
bool AddNode(int nodeIndex, int direction, int *pNode); // 添加结点
bool DeleteNode(int nodeIndex, int *pNode); // 删除结点
void TreeTraverse(); // 遍历结点
private:
int *m_pTree;
int m_iSize;
};
#endif
#include"Tree2.h"
#include<iostream>
#include<string.h>
#include<vector>
#include"Node.h"
using namespace std;
Tree2::Tree2() {
m_pRoot = new Node();
}
Tree2::~Tree2() {
// DeleteNode(0, NULL);
m_pRoot->DeleteNode();
}
Node *Tree2::SearchNode(int nodeIndex) {
return m_pRoot->SearchNode(nodeIndex);
}
bool Tree2::AddNode(int nodeIndex, int direction, Node *pNode) {
// 指定结点索引,指定方向(左右结点),指定元素,插入元素
Node *temp = SearchNode(nodeIndex); // 需要判断插入位置的父结点是否存在
if (temp == NULL) {
return false;
}
// 定义一个新node变量存储需要插入的pNode
Node *node = new Node();
node->index = pNode->index;
node->data = pNode->data;
node->pParent = temp;
// direction为0表示插入到左边,为1表示插入到右边
if (direction == 0) {
temp->pLChild = node;
}
if (direction == 1) {
temp->pRChild = node;
}
return true;
}
bool Tree2::DeleteNode(int nodeIndex, Node *pNode) {
// 指定结点索引,删除结点
Node *temp = SearchNode(nodeIndex); // 找到需要删除结点
if (temp == NULL) {
return false;
}
if (pNode != NULL) { // 将删除结点的值传给pNode->data
pNode->data = temp->data;
}
temp->DeleteNode(); // 进行删除结点(同时删除了其所有的子结点)
return true;
}
void Tree2::PreorderTraversal() {
m_pRoot->PreorderTraversal(); // 前序遍历
}
void Tree2::InorderTraversal() {
m_pRoot->InorderTraversal(); // 中序遍历
}
void Tree2::PostorderTraversal() {
m_pRoot->PostorderTraversal(); // 后序遍历
}
int Tree2::MaxDepth() {
int depth = realMaxDepth(m_pRoot);
return depth;
}
int Tree2::realMaxDepth(Node *pNode) {
if (!pNode) {
return 0;
}
//cout << pNode->pLChild->index;
int lDepth = realMaxDepth(pNode->pLChild);
int rDepth = realMaxDepth(pNode->pRChild);
return lDepth > rDepth ? lDepth + 1:rDepth + 1;
}
int Tree2::MaxWidth() {
int width = realMaxWidth(m_pRoot);
return width;
}
int Tree2::realMaxWidth(Node *pNode) {
if (pNode != NULL) {
//Node *temp = new Node();
//Node *temp = pNode;
vector<Node*> vec;
vec.push_back(pNode);
int MaxWidth = 1;
int LastWidth = 1;
int CurWidth = 0;
while ((int)vec.size() > 0) {
while (LastWidth > 0) {
Node* temp_node = vec[0];
vec.erase(vec.begin());
if (temp_node->pLChild != NULL) {
vec.push_back(temp_node->pLChild);
}
if (temp_node->pRChild != NULL) {
vec.push_back(temp_node->pRChild);
}
LastWidth--;
}
LastWidth = (int)vec.size();
CurWidth = (int)vec.size();
MaxWidth = CurWidth > MaxWidth ? CurWidth : MaxWidth;
}
return MaxWidth;
}
return 0;
}
#ifndef TREE2_H
#define TREE2_H
#include"stdio.h"
#include"Node.h"
// 二叉树链表实现
class Tree2 {
public:
Tree2(); // 创建树
~Tree2(); // 销毁树
Node *SearchNode(int nodeIndex); // 搜索结点
bool AddNode(int nodeIndex, int direction, Node *pNode); // 添加结点
bool DeleteNode(int nodeIndex, Node *pNode); // 删除结点
void PreorderTraversal(); // 前序遍历
void InorderTraversal(); // 中序遍历
void PostorderTraversal(); // 后序遍历
int MaxDepth(); // 树的最大深度
int MaxWidth(); // 树的最大宽度
private:
int realMaxDepth(Node *pNode);
int realMaxWidth(Node *pNode);
private:
Node *m_pRoot;
};
#endif
文件已添加
#include<iostream>
#include<stdlib.h>
#include"Tree.h"
using namespace std;
int main(void) {
int root = 3;
Tree *pTree = new Tree(10, &root);
int node1 = 5;
int node2 = 8;
pTree->AddNode(0, 0, &node1);
pTree->AddNode(0, 1, &node2);
int node3 = 2;
int node4 = 6;
int node5 = 9;
int node6 = 7;
pTree->AddNode(1, 0, &node3);
pTree->AddNode(1, 1, &node4);
pTree->AddNode(2, 0, &node5);
pTree->AddNode(2, 1, &node6);
pTree->TreeTraverse();
int *p = pTree->SearchNode(2);
cout << endl << "寻找的2号结点" << *p << endl;
int node = 0;
pTree->DeleteNode(6, &node);
cout << "删除的6号结点" << node << endl;
pTree->TreeTraverse();
delete pTree;
return 0;
}
文件已添加
#include<iostream>
#include<stdlib.h>
#include"Tree2.h"
#include"Node.h"
#include<vector>
#include<algorithm>
using namespace std;
// 0 5 8 2 6 9 7
int main(void) {
Node *node1 = new Node();
node1->index = 1;
node1->data = 5;
Node *node2 = new Node();
node2->index = 2;
node2->data = 8;
Node *node3 = new Node();
node3->index = 3;
node3->data = 2;
Node *node4 = new Node();
node4->index = 4;
node4->data = 6;
Node *node5 = new Node();
node5->index = 5;
node5->data = 9;
Node *node6 = new Node();
node6->index = 6;
node6->data = 7;
Tree2 *tree = new Tree2();
tree->AddNode(0, 0, node1);
tree->AddNode(0, 1, node2);
tree->AddNode(1, 0, node3);
tree->AddNode(1, 1, node4);
tree->AddNode(2, 0, node5);
tree->AddNode(2, 1, node6);
//tree->DeleteNode(6, NULL);
/*
cout << "前序:" << endl;
tree->PreorderTraversal();
cout << "中序:" << endl;
tree->InorderTraversal();
cout << "后序: " << endl;
tree->PostorderTraversal();
*/
//Node *node0 =
// cout << tree->MaxDepth();
cout << tree->MaxWidth() << endl;
delete tree;
return 0;
}
文件已添加
#include <iostream>
#include "BSTree.h"
using namespace std;
int main() {
BSTree<int> s;
// int a ;
cout << "请输入二叉树结点以构造二叉查找树:" << endl;
// 插入数据
int a[] = {88, 47, 19, 55, 50, 98};
for (int i = 0; i < 6; i++) {
s.insert(a[i]);
}
// 遍历二叉树
cout << "前序遍历二叉查找树:" << endl;
s.postOrder();
cout << endl;
cout << "中序遍历二叉查找树:" << endl;
s.inOrder();
cout << endl;
cout << "后序遍历二叉查找树:" << endl;
s.postOrder();
cout << endl;
cout << "打印二叉查找树" << endl;
s.print();
// 查找一个数, 如果找不到就插入
int a = 10;
BSTNode<int>* findnode = s.search(a);
if(!findnode) {
cout<<"查找失败"<<endl;
s.insert(a);
cout<<"已经将"<<a<<"插入二叉查找树,现在二叉查找树为:"<<endl;
s.inOrder();
cout<<endl;
} else {
cout<<findnode->_key<<"查找成功"<<endl;
}
// 找一个数的前驱或者后继
BSTNode<int>* findPreNode= new BSTNode<int>(1, NULL, NULL, NULL);
findPreNode->_key = 1;
BSTNode<int>* preNode ;
if((preNode = s.predecessor(findPreNode)) != NULL) {
cout << "其前驱结点为:";
cout << preNode->_key << endl;
} else
cout << "没有前驱结点" << endl;
if((preNode = s.sucessor(findPreNode)) != NULL) {
cout << "其后继结点为:";
cout << preNode->_key << endl;
} else
cout << "没有后继结点" << endl;
// 删除一个节点
int a = 1;
s.remove(a);
cout<<"删除后的二叉排序树:"<<endl;
s.print();
BSTNode<int>* maxNode = s.minimumNode();
if(!maxNode)
cout<<"最小的节点为:"<<maxNode->_key<<endl;
BSTNode<int>* minNode = s.maximumNode();
if(!minNode)
cout<<"最大的节点为:"<<minNode->_key<<endl;
cout<<"销毁二叉树"<<endl;
s.destory();
s.inOrder();
system("pause");
return 0;
}
文件已添加
#include "AVLTree.h"
#include <iostream>
#include <exception>
int main() {
try {
AVLTree avl;
for(int i = 1; i < 10; i++) {
avl.Insert(i);
}
avl.Delete(4);
avl.Display();
} catch (std::exception& e) {
std::cout << e.what() << std::endl;
}
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册