山東大學軟體學院2017-18學年大二資料結構實驗五、六、七
注:實驗程式碼均為自己編寫,均能滿足實驗要求,但實現方法不唯一僅供參考。
實驗五 二叉樹操作
1.實驗內容(題目內容,輸入要求,輸出要求)
- 輸入一個完全二叉樹的層次遍歷字串,建立這個二叉樹,輸出這個二叉樹的前序遍歷字串、中序遍歷字串、後序遍歷字串、結點數目、二叉樹高度(上述每一個結果獨立一行顯示)。
- 輸入二叉樹前序序列和中序序列(各元素各不相同),建立這個二叉樹,輸出該二叉樹的後序序列、層次遍歷。
2.資料結構與演算法描述 (整體思路描述,所需要的資料結構與演算法)
- 運用連結串列實現二叉樹,樹節點中有資料域與兩個地址域(左右孩子)。
- 層次遍歷建立二叉樹時,運用佇列處理當前節點的左右子節點,其中對輸入的char陣列運用二叉樹的線性公式化描述去為連結串列實現二叉樹中的節點賦值,某個節點的子節點在陣列中的位置分別為2i+1與2i+2。
- 前序中序後序遍歷二叉樹均採用遞迴方法。
- 結點數目運用前序遍歷的函式結構把visit改成結點數count++
- 二叉樹高度用遞迴實現,分別對左右子樹進行遞迴,返回判斷的時候比較左右字數高度返回高的那棵樹的高度,如果一樣高返回右子樹的高度+1。
- 前序中序生成二叉樹利用前序中第一個元素先找到中序的根節點處,然後用current標記,左側為左子樹右側為右子樹。此時前序加一便是中序current所指根節點的左子樹的根,然後利用遞迴,分別生成左右子樹。
3.實現原始碼
#include<iostream>
#include<string>
#include<queue>
using namespace std;
template<class T>
class BinaryTreeNode
{
template<class T> friend class BinaryTree;
public:
BinaryTreeNode() { LeftChild = RightChild = NULL; };
BinaryTreeNode(const T & data) { this->data = data; LeftChild = RightChild = NULL; };
BinaryTreeNode(
private:
T data;
BinaryTreeNode *LeftChild;
BinaryTreeNode *RightChild;
};
template<class T>
class BinaryTree
{
public:
BinaryTreeNode<T> *root;
BinaryTree() { root = 0; _count = 0; };
~BinaryTree() {};
void MakeTreeByLevelOrder(const char* levelOrder, const int length);
void MakeTreeByPreOrderAndInOrder(const char* preOrder, const char* inOrder, const int length);
void MakeTreeByPreOrderAndInOrder(BinaryTreeNode<T>& t, const char* preOrder,int p1,int p2, const char* inOrder,int i1,int i2);
int Height()const { return Height(root); }
int NodeNumber(BinaryTreeNode<T> *x) {
if (x) {
_count++; //把visit一個節點改成節點數加一
NodeNumber(x->LeftChild);
NodeNumber(x->RightChild);
}
return _count;
}
void visit(BinaryTreeNode<T> *x) { if (x->data != NULL) { cout << x->data; cout << ","; } }
void levelOrder(BinaryTreeNode<T> *t) //層次遍歷
{
queue<BinaryTreeNode<T>*> Q;
while (t)
{
visit(t); //訪問t並將t的左右孩子放入佇列
if (t->LeftChild) Q.push(t->LeftChild);
if (t->RightChild) Q.push(t->RightChild);
if (!Q.empty()) {
t = Q.front();
Q.pop(); //訪問下一個節點 此時t被賦為了佇列的第一個元素
}
else
{
return;
}
}
}
void preOrder(BinaryTreeNode<T> *t) //前序遍歷
{
if (t)
{
visit(t);
preOrder(t->LeftChild);
preOrder(t->RightChild);
}
}
void inOrder(BinaryTreeNode<T> *t) //中序遍歷
{
if (t != NULL)
{
inOrder(t->LeftChild);
visit(t);
inOrder(t->RightChild);
}
}
void postOrder(BinaryTreeNode<T> *t) //後序遍歷
{
if (t != NULL)
{
postOrder(t->LeftChild);
postOrder(t->RightChild);
visit(t);
}
}
private:
int _count;
int Height(BinaryTreeNode<T>*t)const;
};
template<class T>
void BinaryTree<T>::MakeTreeByLevelOrder(const char* levelOrder, const int length)
{
queue<BinaryTreeNode<T>*> queue; //運用佇列處理左右節點
for (int i = 0; i < length; i++)
{
if (i == 0) //root根的處理 樹根的初始化
{
switch (length)
{
case 1:
root = new BinaryTreeNode<T>(levelOrder[0]);
break;
case 2:
root = new BinaryTreeNode<T>(levelOrder[0], new BinaryTreeNode<T>(levelOrder[1]),NULL);
break;
case 3:
root = new BinaryTreeNode<T>(levelOrder[0], new BinaryTreeNode<T>(levelOrder[1]), new BinaryTreeNode<T>(levelOrder[2]));
break;
default:
root = new BinaryTreeNode<T>(levelOrder[0], new BinaryTreeNode<T>(levelOrder[1]), new BinaryTreeNode<T>(levelOrder[2]));
queue.push(root->LeftChild);
queue.push(root->RightChild);
break;
}
}
else //不為根節點時
{
if (2 * i + 2 <= length-1) //如果當前節點有右孩子
{
queue.front()->LeftChild = new BinaryTreeNode<T>(levelOrder[2 * i + 1]);
queue.front()->RightChild = new BinaryTreeNode<T>(levelOrder[2 * i + 2]);
queue.push(queue.front()->LeftChild);
queue.push(queue.front()->RightChild);
queue.pop();
}
if (2 * i + 2 > length-1 && 2 * i + 1 <= length-1) //如果當前節點 沒有右孩子且 有左孩子
{
queue.front()->LeftChild = new BinaryTreeNode<T>(levelOrder[2 * i + 1]);
queue.pop();
}
}
}
}
template<class T>
void BinaryTree<T>::MakeTreeByPreOrderAndInOrder(const char* preOrder, const char* inOrder, const int length)
{
root = new BinaryTreeNode<T>();
MakeTreeByPreOrderAndInOrder(*root, preOrder, 0, length - 1, inOrder, 0, length - 1);
}
template<class T>
void BinaryTree<T>::MakeTreeByPreOrderAndInOrder(BinaryTreeNode<T>& t, const char * preOrder, int p1, int p2, const char * inOrder, int i1, int i2)
{
//i1為inOrder的開頭,p1為preOrder的開頭,i2p2分別為結尾
t.data = preOrder[p1];
t.LeftChild = t.RightChild = NULL; //根的構建
int current = i1; //從中序i1處 宣告current位置
while (inOrder[current] != preOrder[p1]) //用current記錄與前序p1位置相同數的位置
current++;
int length = current - i1; //記錄current根左子樹的長度
if (current > i1) { //如果current有左子樹就新建他的LeftChild並且用遞迴
t.LeftChild = new BinaryTreeNode<T>();
MakeTreeByPreOrderAndInOrder(*(t.LeftChild), preOrder, p1 + 1, p1 + length, inOrder, i1, current - 1);//inOrder的左樹
}
if (current < i2) {
t.RightChild = new BinaryTreeNode<T>(); //如果current有右子樹就新建他的RightChild並且用遞迴
MakeTreeByPreOrderAndInOrder(*(t.RightChild), preOrder, p1 + length + 1, p2, inOrder, current + 1, i2);//inOrder的右樹
}
}
template<class T>
int BinaryTree<T>::Height(BinaryTreeNode<T>* t) const
{
if (!t) return 0;
int heightOfLeft = Height(t->LeftChild);
int heightOfRight = Height(t->RightChild);
if (heightOfLeft > heightOfRight)
{
return ++heightOfLeft;
}
else
{
return ++heightOfRight;
}
}
int main() {
cout << "Input1" << endl;
string levelOrderString;
cin >> levelOrderString;
const char*levelOrder = levelOrderString.c_str();
BinaryTree<char>*a = new BinaryTree<char>();
a->MakeTreeByLevelOrder(levelOrder, levelOrderString.length());
cout << "Output1" << endl;
a->preOrder(a->root);
cout << endl;
a->inOrder(a->root);
cout << endl;
a->postOrder(a->root);
cout << endl;
cout<< a->NodeNumber(a->root)<<endl;
cout << a->Height() << endl;
cout << "Input2" << endl;
string preOrderString;
cin >> preOrderString;
const char*preOrder = preOrderString.c_str();
string inOrderString;
cin >> inOrderString;
const char*inOrder = inOrderString.c_str();
BinaryTree<char>*b = new BinaryTree<char>();
b->MakeTreeByPreOrderAndInOrder(preOrder, inOrder, preOrderString.length());
cout << "Output2" << endl;
b->postOrder(b->root);
cout << endl;
b->levelOrder(b->root);
cout << endl;
cout << "End" << endl;
system("pause");
}
實驗六 堆和搜尋樹
1.實驗內容(題目內容,輸入要求,輸出要求)
- 輸入一系列不為零的正整數(最多不超過20個),遇到0代表輸入結束(不包含0)。
- 根據輸入的資料,建立最大堆,輸出最大堆的層次序列。
- 輸出用堆排序後的排序結果。
- 根據輸入的資料,建立二叉搜尋樹,輸出二叉搜尋樹的前序序列。
- 輸出二叉搜尋樹的中序序列。
- 根據輸入的資料作為字母出現的頻率(第1個數代表A頻率,第2個數代表B頻率,……),建立Huffman樹,輸出Huffman編碼,要求左子樹權值小於右子樹權值,左邊為0,右邊為1。輸出按字數順序由小到大輸出,格式採用“字母:編碼”,例如A:0,B:10……
2.資料結構與演算法描述 (整體思路描述,所需要的資料結構與演算法)
①、前五題運用最大堆及堆排序、最後一題運用最小堆生成霍夫曼樹,運用遞迴實現霍夫曼編碼的輸出。
②、採用陣列的形式實現最大堆,從2分之陣列大小處向前依次移動,通過position不斷*2來比較此時節點與其子節點中最大的一個數的大小,如果比其子節點中最大的一個數還大那麼就不動,否則就把大孩子覆蓋到該節點處,該節點下移到其子節點位置接著向下2*position處重複比較。
③、堆排序用不斷刪除堆頂最大元素在for迴圈中i--進行從小到大的排序。
④、構造二叉搜尋樹通過依次加入資料從root節點依次向下比較,小的放左面大的放右面。
⑤、首先按構造最大堆的方式構造最小堆,然後定義霍夫曼類 其中有生成霍夫曼編碼的方法,與私有變數一個霍夫曼樹節點 一個權重。另外單獨一個生成霍夫曼樹的方法,其中先宣告一個霍夫曼型別的陣列,其中的權重與霍夫曼節點全部賦值,霍夫曼節點的data為ABCD...符號 權重為輸入的資料。
再定義一個存霍夫曼型別的最小堆,通過其權重進行排序,每次取權重最小的兩個生成一個霍夫曼類,其data為0,權重為兩節點相加,生成後再插入最小堆中,然後不斷進行取兩個生成一個的操作,直到最後返回剩下的最後一個節點,即為生成好的霍夫曼樹的根節點。
⑥、霍夫曼編碼通過遞迴,不斷從根節點遍歷到每個葉子節點,其中向左走字串+0,向右走字串+1,最後輸出每個符號的霍夫曼編碼。
3.實現原始碼
#include<iostream>
#include<queue>
#include<string>
using namespace std;
template<class T>
class MaxHeap
{
public:
MaxHeap() {
currentSize = 0; //陣列中從1開始
}
int Size()const { return currentSize; }
void creat(T a[], int size);
void outputByLevelOrder();
MaxHeap<T>& DeleteMax(T &x);
void HeapSort(int size);
T *heap;
private:
int maxSize=20;
int currentSize;
};
template<class T>
void MaxHeap<T>::creat(T a[], int size)
{
heap = a;
currentSize = size;
for (int i = currentSize/2; i >= 1; i--)
{
T rootOfChild = heap[i];//記錄子樹的根的值
int position = 2 * i; //子樹根如果移動的話應該在的位置
while (position<=currentSize)
{
if (position<currentSize&&heap[position]<heap[position+1]) //如果當前元素有右孩子並且左孩子小於右孩子
{
position++; //position指到右孩子的位置 否則position還在左孩子位置
}
if (rootOfChild >= heap[position]) //如果此時子樹根大於兩孩子中最大的一個跳出迴圈 位置不變
{
break;
}
heap[position / 2] = heap[position]; //否則 孩子中最大的內個覆蓋子樹根位置的值
position *= 2; //再把position指到內個大孩子的左子樹根處 重複迴圈
}
heap[position / 2] = rootOfChild;
}
}
template<class T>
void MaxHeap<T>::outputByLevelOrder()
{
for (int i = 1; i <=currentSize; i++)
{
cout << heap[i];
if (i!=currentSize)
{
cout << ",";
}
else
{
cout << endl;
}
}
}
template<class T>
MaxHeap<T>& MaxHeap<T>::DeleteMax(T & x) //將最大元素放入x,並從堆中刪除最大元素
{
if (currentSize == 0) //如果堆為空返回
return *this;
x = heap[1];
T y = heap[currentSize--];//儲存最後一個元素
int i = 1; //堆的當前節點
int position = 2;
while (position <= currentSize) {
if (position<currentSize&&heap[position + 1]>heap[position]) //如果當前元素有右孩子並且左孩子小於右孩子
position++; //position指到右孩子的位置 否則position還在左孩子位置
if (y >= heap[position]) //如果此時最後一個元素大於當前節點兩孩子中最大的一個跳出迴圈
break;
heap[i] = heap[position]; //否則 孩子中最大的內個覆蓋