廣義表是非線性結構,其定義是遞歸的。
以下給出幾種簡單的廣義表模型:
由上圖我們可以看到,廣義表的節點類型無非head、value、sub三種,這裏設置枚舉類型,利用枚舉變量來記錄每個節點的類型:
enum Type { HEAD, //頭節點 VALUE, //值節點 SUB, //子表節點 };
每個節點都有自己的類型以及next指針,除此之外,如果該節點是VALUE類型還要分配空間存儲該節點的有效值;但是若該節點是SUB類型,就需定義一個指針指向子表的頭。
這裏我們可以用聯合來解決這個問題。
(聯合(或共同體)是一種不同數據類型成員之間共享存儲空間的方法,並且聯合體對象在同一時間只能存儲一個成員值)
構造節點:
struct GeneralizedNode { Type _type; // 1.類型 GeneralizedNode* _next; //2.指向同層的下一個節點 union { char _value; // 3.有效值 GeneralizedNode* _subLink; // 3.指向子表的指針 }; GeneralizedNode(Type type = HEAD, char value = '0') :_value(value) ,_type(type) , _next(NULL) { if (_type == SUB) { _subLink = NULL; } } };
廣義表的定義及基本操作:
class Generalized { public: //無參的構造函數,建立空的廣義表 Generalized(); //建造廣義表,有參數的構造函數 Generalized(const char* str); //打印廣義表 void Print(); //獲取值節點的個數 size_t Amount(); //獲取廣義表的深度 size_t Depth(); //拷貝構造 Generalized(const Generalized& g); ////賦值運算符的重載 Generalized& operator=(const Generalized& g); ////析構函數 ~Generalized(); protected: void _Print(GeneralizedNode* head); GeneralizedNode* _CreatList(const char*& str); size_t _Amount(GeneralizedNode* head); GeneralizedNode* _Copy(GeneralizedNode* head); void _Destory(GeneralizedNode* head); protected: GeneralizedNode* _head; //記錄廣義表頭指針 };
初始化建立廣義表進行循環遞歸。遍歷字符串時遇到字符就建立值節點,遇到'('就進行遞歸併建立子表;遇到')'就結束當前子表的建立,並返回當前子表的頭指針。
GeneralizedNode* _CreatList(const char*& str) { assert(*str == '('); GeneralizedNode* head = new GeneralizedNode(HEAD,'0'); GeneralizedNode* cur = head; str++; while (str != '\0') { if ((*str >= '0'&&*str <= '9') || (*str >= 'a'&&*str <= 'z') || (*str >= 'A'&&*str <= 'Z')) { cur->_next = new GeneralizedNode(VALUE, *str); cur = cur->_next; } else if (*str == '(') { cur->_next = new GeneralizedNode(SUB); cur = cur->_next; cur->_subLink = _CreatList(str); } else if (*str == ')') { return head; } str++; } return head; }
打印廣義表:當節點的類型爲SUB時進行遞歸,最後不要忘了每打印完一層要打印一個後括號。
void _Print(GeneralizedNode* head) { if (head == NULL) { cout << "Generalized table is NULL" << endl; return; } GeneralizedNode* cur = head; while (cur) { if (cur->_type == HEAD) { cout << '('; } else if (cur->_type == VALUE) { cout << cur->_value; if (cur->_next) { cout << ','; } } else if (cur->_type == SUB) { _Print(cur->_subLink); if (cur->_next) { cout << ','; } } cur = cur->_next; } cout << ')'; }
獲取值節點的個數:設置count變量,遇到值節點就加1,遇到SUB節點進行遞歸併將返回值加給count
size_t _Amount(GeneralizedNode* head) { GeneralizedNode* begin = head; size_t count = 0; while (begin) { if (begin->_type == VALUE) { count++; } if (begin->_type == SUB) { count += _Amount(begin->_subLink); } begin = begin->_next; } return count; }
廣義表的深度:設置變量dp和max分別用來記錄當前子表即當前SUB節點指向的子表深度,以及本層所有的SUB節點中深度最大的子表的深度。
size_t _Depth(GeneralizedNode* head) { if (_head == NULL) { return 0; } size_t dp=0; GeneralizedNode* cur = head; size_t max = 0; while (cur) { if (cur->_type == SUB) { dp=_Depth(cur->_subLink); if (max < dp) { max = dp; } } cur = cur->_next; } return max+1; }
銷燬廣義表:依次遍歷節點,遇到子表遞歸,將子表的節點delete完成後,再回到當前層繼續遍歷。
void _Destory(GeneralizedNode* head) { if (head == NULL) { return; } while (head) { GeneralizedNode* begin = head->_next; if (head->_type == SUB) { _Destory(head->_subLink); } delete head; head = begin; } }
廣義表的拷貝構造及賦值運算符重載與構造函數的遞歸方式類似,這裏就不再闡述冗餘信息。