Qt繪制二叉樹


介紹

Qt繪制二叉樹是大二時數據結構的一個實習題目,當時的功能要求如下:

  • 鍵盤輸入二叉樹結點序列(前序或層次),創建一棵二叉樹
  • 實現SwapTree方法,以根結點為參數,交換每個結點的左子樹和右子樹(提示:前序遞歸)
  • 實現Find方法,查找值為key的結點,並輸出該結點的所有祖先結點
  • 輸入一棵二叉樹的前序遍歷序列和中序遍歷序列,重構這棵二叉樹(這個序列里面是不帶空結點’#‘的)

二叉樹的前序和中序創建要求如下:

  • 要求鍵盤輸入二叉樹結點序列
  • 結點序列可以是前序,也可以是層次
  • 空結點以#表示

由題目可知呢主要就是可視化一顆二叉樹,另外需要說清的是僅僅前序遍歷是無法確定一顆二叉樹的順序的,但是如果前序中加上空節點‘#’,是可以確定的

示例1(前序和層次的):

QTbinarytree.png

示例2(前序=“ABC##DE#G##F###” 或者 層次=“AB#CD##EF#G####”):

QTbinarytree3

示例3(前序="ABHFDECKG"和中序="HBDFAEKCG"):

QTbinarytree4

主要建樹思路

  1. 主要功能就前序構造、層次構造、交換節點、查找關鍵字、重新構建這幾個,所以為了圖便捷,就直接在Qt提供的ui界面上加上這幾個菜單項,可以參考下圖

QTbinarytree2

  1. 二叉樹根據前序生成一顆樹。編寫了一個函數CreateBinTree,利用遞歸進行二叉樹的生成。思路也比較簡單可以看下面的代碼。
//前序創造節點
//i代表第幾個字母
void BinaryTree::CreateBinTree(QString &str, BinTreeNode *&Node,int &i)
{
//  qDebug()<<str;
if(str[i]!='#')//說明不是空結點
{
   Node=new BinTreeNode(str[i]);
   Treesize++;
   i++;
   this->CreateBinTree(str,Node->left,i);
   this->CreateBinTree(str,Node->right,i);
}
else
{
   i++;
   Node=nullptr;
}
}
  1. 二叉樹的層次遍歷生成一顆二叉樹。這個利用隊列來完成,利用隊列遍歷字符串,先將字符串第一個字符塞進隊列作為根節點,然后按順序遍歷字符串並且創建對應的孩子節點,具體如下。
int j=0;
    Treesize=0;
    QQueue<BinTreeNode *>Q;
    BinTreeNode *p=nullptr;
    if(str[j]=='#')   //先創建根節點
    {
        Treesize=0;
        return;
    }
    root=new BinTreeNode(str[j]);
    Treesize++;
    Q.enqueue(root);
    j++;
while(j<(str.size()-1))
    {
       if(Q.isEmpty())
            break;
         else
            p=Q.dequeue();
			if(str[j]!='#')   //如果字符不為‘#’,創建左結點
        {
            p->left=new BinTreeNode(str[j]);
            Treesize++;
            Q.enqueue(p->left);
        }
         j++;
        if(str[j]!='#')   //如果字符不為‘#’,創建右結點
        {
            p->right=new BinTreeNode(str[j]);
            Treesize++;
            Q.enqueue(p->right);
        }
         j++;
    }
  1. 通過前序和中序建樹。前序和中序確定樹的順序思想比較簡單,利用前序的特性找到父節點,然后利用中序確定左右子樹,然后重復這樣的過程。代碼如下,借鑒一下就行,看以前的代碼自己都想吐槽。
//前序和中序建樹
//pre代表前序字符串
//in代表中序字符串
//n代表pre可以到的位置
//測試用例: 前序:"ABHFDECKG",中序:"HBDFAEKCG"
BinTreeNode *BinaryTree::creatBinaryTree(QString pre, QString in, int n)
{
  qDebug()<<pre;
  qDebug()<<in;
  if(n==0) return nullptr;
  int k=0;
  while(pre[0]!=in[k]&&k<in.length())k++;
  if(k>=in.length()) return nullptr;  //理論上應該需要拋出異常的,
  BinTreeNode *t=new BinTreeNode(pre[0]);
  //以位置k分為左子樹和右子樹
  t->left=creatBinaryTree(pre.mid(1),in,k);//從0-k是左子樹,所以在這里pre只能遍歷到k
  t->right=creatBinaryTree(pre.mid(k+1),in.mid(k+1),n-k-1);
  //由於pre和in同時都只保留右子樹部分,所以pre
  return t;
}

上面建樹的例子看看就行,尤其是最后一個前中序建樹,也不知道自己當時是怎么寫出這么魔性的代碼。下面就專門介紹一下畫二叉樹的部分。

主要畫樹思路

畫樹是利用了Qt的繪圖事件,直接進行畫圖,畫圖是在建樹已經完成的基礎之上完成的。想法比較簡單,所以畫出來的比較難看,先在這里說明一下

  1. 二叉樹的節點是圓形的,半徑為25。二叉樹的子節點和父節點之間x軸上相差45,y軸上100。舉個例子:父節點的坐標為(x,y),則左孩子坐標為(x-45,y+100),右孩子坐標為(x+45,y+100)。根節點的坐標設置為(500,75),這些數據都可以根據自己的需求改,不定死。

  2. 數據結構設計的時候,對於二叉樹的節點BinTreeNode,設計一個data(QChar類型,用於存儲數據)、point(QPoint類型,用於存儲位置)

  3. 數據結構設計時,對於二叉樹BinaryTree,設計Mypoints(QPoint *類型,存儲樹各個結點坐標)、My_lines( QLine *類型,存儲需要畫的線的條數)

  4. 寫一個函數setMyPoints,通過層次遍歷,完成各個坐標的匹配。其中對於Mypoints直接存儲節點的中心,然后畫圓;對於線段,從上面的例子也看得出來是父節點的中心向下半徑個位置作為起點,子節點中心向上半徑個位置為終點。具體代碼如下所示

    //為坐標組設置應的坐標,以及得到相應的線段
    void BinaryTree::setMyPoints()
    {
          //設置父節點和子節點間橫坐標相差的距離
    
        int i=0;
    //    int H=height();
        Mypoints=new QPoint[Treesize];  //動態分配空間
        My_lines=new QLine[Treesize-1];
    
        QQueue<BinTreeNode *>Q;         //調用隊列
        BinTreeNode *p=root;
        root->setpoint(QPoint(500,75));  //為根節點設置坐標
        Q.enqueue(root);
        Mypoints[i]=root->point;
    
        //通過層次遍歷,完成各個坐標的匹配
        while(!Q.isEmpty())
        {
            p=Q.dequeue();
            if(p->left!=nullptr)
            {
                i++;
                int h=height(p);
                p->left->setpoint(p->point-QPoint(45*h,-100));
                Mypoints[i]=p->left->point;
                My_lines[i-1].setP1(p->point+QPoint(0,25));//線
                My_lines[i-1].setP2(p->left->point-QPoint(0,25));
                Q.enqueue(p->left);
            }
    
            if(p->right!=nullptr)
            {
                i++;
                int h=height(p);
                p->right->setpoint(p->point+QPoint(45*h,100));
                Mypoints[i]=p->right->point;
                My_lines[i-1].setP1(p->point+QPoint(0,25));
                My_lines[i-1].setP2(p->right->point-QPoint(0,25));
                Q.enqueue(p->right);
                h--;
            }
    
        }
    
    }
    
  5. 對於左側顯示的字符,這個就比較簡單了,直接在建樹之后進行相對應的前序、中序、后續、層次遍歷,然后將字符串保存下來即可,在這里就不展開詳細講解

總結

我畫二叉樹的思想比較簡單,所以畫出來也不是很好看,代碼雖然可以運行,但是也有一些小細節上的問題,如果有什么更好的意見歡迎指教。

源碼傳輸門


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM