【练习】2021下半年数据结构刷题笔记和总结 (二) 树、查找-- 不同的排序算法、二叉排序树 平衡二叉树、哈希表查找、线索二叉树、
記錄自己下半年寫題目的記錄。題目來(lái)自書或者網(wǎng)站。
練習(xí)(一)的地址:
https://blog.csdn.net/qq_41358574/article/details/117098620?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522162343250816780357289113%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fblog.%2522%257D&request_id=162343250816780357289113&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2blogfirst_rank_v2~rank_v29-2-117098620.nonecase&utm_term=%E5%88%B7%E9%A2%98&spm=1018.2226.3001.4450
文章目錄
- 知識(shí)點(diǎn)整理
- 1.一棵二叉排序樹,設(shè)計(jì)算法查找兩個(gè)結(jié)點(diǎn)之和等于給定值x的結(jié)點(diǎn)
- 2.樹的層次遍歷
- 3.一二叉排序樹中的關(guān)鍵字由整數(shù)構(gòu)成,為了查找某關(guān)鍵字k,會(huì)得到一個(gè)查找序列,判斷一個(gè)數(shù)組a中的序列是否為該二叉排序的查找序列
- 4.求關(guān)鍵字分別為x,y的結(jié)點(diǎn)的最近公共祖先
- 5.設(shè)計(jì)算法將二叉排序樹變?yōu)橛行蜴湵?#xff0c;要求不能創(chuàng)建新結(jié)點(diǎn),只修改指針
- 6.設(shè)計(jì)算法交換二叉樹b的所有左右子樹要求空間復(fù)雜度為1
- 7.設(shè)計(jì)算法交換二叉樹b的所有左右子樹要求不破壞原二叉樹結(jié)構(gòu)
- 8.設(shè)計(jì)算法利用結(jié)點(diǎn)的右孩子指針將一棵二叉樹的葉結(jié)點(diǎn)從左往右串成單鏈表
- 9.設(shè)計(jì)算法輸出所有根結(jié)點(diǎn)到葉結(jié)點(diǎn)的路徑及其路徑和
- 10.【面試題9-11★★★】一個(gè)含有13個(gè)元素的數(shù)組為前半部分、后半部分是遞增有序,但整個(gè)數(shù)組不是遞增數(shù)組,且不知道前、后兩個(gè)部分中的元素個(gè)數(shù),怎么最快地找出其中的一個(gè)數(shù)
- 11.一個(gè)非降序數(shù)組,若target在數(shù)組中出現(xiàn),返回位置,否則返回它將插入的位置
- 12.二分插入排序
- 13.希爾排序
- 14.桶排序?qū)崙?zhàn):用鏈表表示桶,從文件中讀取數(shù)字
- 15.一個(gè)序列用帶頭結(jié)點(diǎn)的單鏈表存儲(chǔ),采用快速排序求遞增排序
- 16.二叉查找樹的操作
- 遍歷操作:
- 插入結(jié)點(diǎn)
- 二叉查找樹的刪除操作
- 二叉查找樹的查找操作
- 判斷一個(gè)數(shù)組是否是二叉查找樹的后序遍歷
- 17.線索二叉樹
- 數(shù)據(jù)結(jié)構(gòu)如下:
- 尋找中序遍歷線索二叉樹的前驅(qū)和后繼結(jié)點(diǎn)
- 先序、后序遍歷的前驅(qū)后繼
- 線索二叉樹中插入結(jié)點(diǎn)
- . 二叉樹的線索化算法實(shí)現(xiàn)
- 遍歷線索二叉樹
- 18.分塊查找
知識(shí)點(diǎn)整理
- 平衡二叉樹插入結(jié)點(diǎn)時(shí)速度比較慢
- 平衡二叉樹的各項(xiàng)操作的時(shí)間復(fù)雜度為o(logn)
- 有序數(shù)組的查找性能高但刪除性能低,有序鏈表的查找性能低但刪除性能高,AVL和哈希表的查找和刪除性能都高
1.一棵二叉排序樹,設(shè)計(jì)算法查找兩個(gè)結(jié)點(diǎn)之和等于給定值x的結(jié)點(diǎn)
1.思路一 中序遍歷產(chǎn)生中序序列(左子樹所有結(jié)點(diǎn)的關(guān)鍵字均小于根節(jié)點(diǎn)關(guān)鍵字)
typedef struct node {char data;struct node* lchild;struct node* rchild; }btnode; void InOrder(btnode* bt, vector<btnode*>& order) {//order:中序遍歷產(chǎn)生的序列InOrder(bt->lchild, order);order.push_back(bt);InOrder(bt->rchild, order); } bool FindSum(vector<btnode*>order, int x, btnode* &p1, btnode* &p2)//p1 p2傳入的是引用 這樣能得到和為x的結(jié)點(diǎn) {//查找兩個(gè)結(jié)點(diǎn)值之和等于x的p1和p2int i = 0, j = order.size()-1;while (i < j){if (order[i]->data + order[j]->data == x) {p1 = order[i];p2 = order[j];return true;}else if (order[i]->data + order[j]->data > x){j--;}elsei++;}return false; }bool TwoNodeSum(btnode* bt, int x, btnode*& p1, btnode*& p2) {vector<btnode*>order;InOrder(bt, order);if (FindSum(order,x, p1, p2)) return true;else return false; }思路二 采用兩個(gè)棧分別記錄bt的左下結(jié)點(diǎn)和右下結(jié)點(diǎn)(初始狀態(tài)保存根結(jié)點(diǎn)的左下結(jié)點(diǎn)和右下結(jié)點(diǎn))
bool TwoNodeSum(btnode* bt, int x, btnode*& p1, btnode*& p2) {stack<btnode*>left, right;btnode* p = bt, * q;while (p != NULL) {if (p->lchild != nullptr) {left.push(p->lchild); p = p->lchild;}p = bt;//注意這個(gè)步驟 此后根結(jié)點(diǎn)再次賦給pif (p->rchild != nullptr) {right.push(bt->rchild);p = p->rchild;}while (!left.empty() && !right.empty()){p1 = left.top();p2 = right.top();if (p1->data + p2->data > x)//找次大結(jié)點(diǎn) 即p2左孩子的所有右下結(jié)點(diǎn)進(jìn)棧{q = p2;right.pop();if (q->lchild != NULL){p = q->lchild;while (p) {right.push(p);p = p->rchild;}}}else if (p1->data + p2->data < x){//找次小結(jié)點(diǎn)q = p1;left.pop();if (q->rchild != NULL){p = q->rchild;while (p){left.push(p);p = p->lchild;}}}else return true;}};return false; }2.樹的層次遍歷
按從上到下從左到右的順序輸出各個(gè)結(jié)點(diǎn)的值,如果從根到某個(gè)葉節(jié)點(diǎn)的路徑上有的結(jié)點(diǎn)沒有在輸入中給出,或者給出超過一次,應(yīng)該輸出-1
樣例輸入:
(11, LL) (7, LLL) (8, R) (5, ) (4, L) (13, RL) (2, LLR) (1, RRR) (4, RR) ()
輸出:
5 4 8 11 13 4 7 2 1
3.一二叉排序樹中的關(guān)鍵字由整數(shù)構(gòu)成,為了查找某關(guān)鍵字k,會(huì)得到一個(gè)查找序列,判斷一個(gè)數(shù)組a中的序列是否為該二叉排序的查找序列
//假設(shè)序列a有n個(gè)關(guān)鍵字,如果查找成功則a[n-1]=k,用i掃描a數(shù)組,p用于在二叉樹bt中查找(p的初值指向根節(jié)點(diǎn)) 注意:沒查找一層都比較p->key與a[i]是否相等 //如果不相等 返回false bool FindSer(Node* bt, int k, int a[], int n) {if (bt == NULL) return false;if (a[n - 1] != k) return false;//先提前判斷這個(gè)Node* p = bt;int i = 0;while (i < n && p != NULL){if (p->val != a[i]) return false;if (k > p->val) p = p->right;else if (k < p->val) p = p->right;i++;}if (p != NULL) return true;return false; }4.求關(guān)鍵字分別為x,y的結(jié)點(diǎn)的最近公共祖先
Node* LCA(Node*bt,int x,int y) {{if (bt == nullptr) return NULL;if (x < bt->left->val && y < bt->left->val) return LCA(bt->left, x, y);else if (x > bt->right->val && y > bt->right->val) return LCA(bt->right, x, y);else return bt;//注意 如果以上兩種情況都不是,則bt為最近公共結(jié)點(diǎn)}5.設(shè)計(jì)算法將二叉排序樹變?yōu)橛行蜴湵?#xff0c;要求不能創(chuàng)建新結(jié)點(diǎn),只修改指針
方法一:
//1.遞歸的方法 將二叉樹bt中所有結(jié)點(diǎn)的左指針lchild轉(zhuǎn)化為指向前驅(qū)結(jié)點(diǎn),右指針轉(zhuǎn)化為指向后繼結(jié)點(diǎn) //if bt->lchild==null first = bt,if bt->rchild==null last =bt //注意需要銷毀雙鏈表 void convert(Node* bt, Node*& first, Node*& last) {if (bt == nullptr) {//空樹first = nullptr;last = nullptr;}Node* lfirst, * llast, * rfirst, * rlast;//遞歸調(diào)用時(shí)左子樹和右子樹的首位指針,一共四個(gè)if (bt->left == nullptr) first = bt;else if (bt->left != nullptr) {convert(bt->left, lfirst, llast);first = lfirst;bt->left = llast;llast->right = bt;}if (bt->right == nullptr)last = bt;else {convert(bt->right, rfirst, rlast);last = rlast;bt->right = rfirst;rfirst->left = bt;} } void Display(Node* first, Node* last) {if (first != NULL) {//輸出轉(zhuǎn)化后的雙鏈表while (first != last){cout << first->val << ",";first = first->right;}cout << first->val;//最后一個(gè)結(jié)點(diǎn)的值}} void destroy(Node*& L) {//銷毀雙鏈表Node* pre = L, *p = pre->right;//一個(gè)首結(jié)點(diǎn) 一個(gè)首結(jié)點(diǎn)的后繼結(jié)點(diǎn)while (pre->right){free(pre);pre = p;p = pre->right;}free(pre);//注意最后還有一個(gè)結(jié)點(diǎn)要釋放 }方法二:
中序遍歷 用pre全局變量指向中序遍歷當(dāng)前訪問結(jié)點(diǎn)bt的前驅(qū)節(jié)點(diǎn),初始為NULL,中序訪問bt時(shí),設(shè)置pre->rchild=bt bt->lchid = pre
兩個(gè)方法的輸出和銷毀雙鏈表函數(shù)是一樣的
6.設(shè)計(jì)算法交換二叉樹b的所有左右子樹要求空間復(fù)雜度為1
void swap(btnode*b) { btnode*temp; if(b!=NULL) { swap(b->lchild); swap(b->rchild); temp=b->lchild; b->lchild=b->rchild; b->rchild=temp; } } //基于后序遍歷算法7.設(shè)計(jì)算法交換二叉樹b的所有左右子樹要求不破壞原二叉樹結(jié)構(gòu)
btnode* swap(btnode*b) { btnode*t,*t1,*t2; if(b==NULL)t=NULL; else { t=(btnode*)malloc(sizof(btnode));//復(fù)制根節(jié)點(diǎn) t->data=b->data; t1=swap(b->lchild); t2=swap(b->rchild); t->lchild=t2; t->rchild=t1; } return t; }8.設(shè)計(jì)算法利用結(jié)點(diǎn)的右孩子指針將一棵二叉樹的葉結(jié)點(diǎn)從左往右串成單鏈表
//先序遍歷的方法,尾插法構(gòu)建葉結(jié)點(diǎn),head指向建立單鏈表的首結(jié)點(diǎn),tail指向尾結(jié)點(diǎn) //head tail以引用的方式傳入 void link(btnode*b,btnode*&head,btnode*&tail) { if(b!=NULL) { if(b->lchild==NULL&&b->rchild==NULL)//葉結(jié)點(diǎn) { if(head==NULL){//此時(shí)為第一個(gè)葉結(jié)點(diǎn) head=b; tail=head; } else { tail->rchild=b; tail=b; } } if(b->lchild!=NULL){ link(b->lchild,head,tail); } if(b->rchild!=NULL)link(b->rchild,head,tail); } //創(chuàng)建并輸出葉結(jié)點(diǎn)構(gòu)成的單鏈表 void vreateLink(btnode*b) { btnode*head=NULL,*tail; link(b,head,tail)//注意head一開始指向null tail->rchild=NULL;//注意尾結(jié)點(diǎn)的rchild置空 btnode*p=head; while(p!=NULL) { cout<<p->data<<","; p=p->next; }}9.設(shè)計(jì)算法輸出所有根結(jié)點(diǎn)到葉結(jié)點(diǎn)的路徑及其路徑和
void dispapath(vector<int>path) { vector<int>::iterator it; int sum=0; for(it=path.begin();it!=path.end();it++) { sum+=*it; cout<<*it<<" "; } cout<<sum;void pathSum(btnode*b,vector<int>path) { if(b==NULL)return; path.push_back(b->data); if(b->lchild==NULL&&b->rchild==NULL) { dispath(path);//找到一個(gè)葉結(jié)點(diǎn),輸出一條路徑 } pathSum(b->lchild,path); pathSum(b->rchild,path);//如果不是葉結(jié)點(diǎn),先序遍歷的方式繼續(xù)尋找后面的結(jié)點(diǎn)10.【面試題9-11★★★】一個(gè)含有13個(gè)元素的數(shù)組為前半部分、后半部分是遞增有序,但整個(gè)數(shù)組不是遞增數(shù)組,且不知道前、后兩個(gè)部分中的元素個(gè)數(shù),怎么最快地找出其中的一個(gè)數(shù)
如[2,4,5,6,7,8][1,3,5,6,7]
解:由于不知道前、后兩個(gè)有序部分中的元素個(gè)數(shù),所以先要將其找出來(lái)。假設(shè)前、后兩個(gè)部分分別為a[0.m]和a[m+.n-1],先采用順序方法在a[0…m]中從前向后查找,若a[i]==target(目標(biāo)元素),找到后算法結(jié)束,或者發(fā)現(xiàn)元素逆序后退出循環(huán)(即找到分界線
元素a[m]),然后在a[m+1.n-1]有序區(qū)間中采用二分查找
上述過程對(duì)應(yīng)的時(shí)間復(fù)雜度為O(n)。對(duì)應(yīng)的算法如下:
11.一個(gè)非降序數(shù)組,若target在數(shù)組中出現(xiàn),返回位置,否則返回它將插入的位置
寫法1:
int SearchInsert(int a[],int n,int target) {if(a[n-1] < target) return n;//a是一遞增數(shù)組int low=0,high = n-1;while(low <= high){int mid = (low+high)/2;if(a[mid] > target) high=mid-1;else if(a[mid] < target) low=mid+1;else return mid;}return low; }寫法2:
int SearchInsert(int a[],int n,int target) {if(a[n-1] < target) return n;//a是一遞增數(shù)組int low=0,high = n-1;while(low < high){int mid = (low+high)/2;if(a[mid] > target) high=mid;else low=mid+1;}return high; }12.二分插入排序
void BinInsertSort(int a[],int n) {int i,j,low,high,mid;int tmp;for(i = 1;i < n;i++){if(a[i] < a[i-1]){tmp = a[i];low=0;high=i-1;while(low<=high){mid = (low+high)/2;if(a[mid] > tmp) high=mid-1;else low=mid+1;}for(j=i-1;j>=high+1;j--)a[j+1]=a[j];//集中進(jìn)行元素后移a[high+1]=tmp;}} }int main() {int a[5]={1,74,3,15,6}; BinInsertSort(a,5); for(int i = 0;i < 5;i++) cout<<a[i]<<" ";}13.希爾排序
void shell(int a[],int n) {int i,j,d;int tmp;d = n/2;while(d > 0){ for(i = d;i < n;i++) { j = i - d; tmp = a[i]; while(j>=0 && a[j] > tmp) {a[j+d] = a[j];j = j-d; } a[j+d] = tmp;}d = d/2;} }是不穩(wěn)定的排序算法。
每一趟不一定產(chǎn)生有序區(qū),最后一趟產(chǎn)生全部的有序序列。
直接插入排序:每一趟產(chǎn)生的有序區(qū)不一定是全局有序的。是穩(wěn)定的排序算法。平均時(shí)間復(fù)雜度為o(n2)
對(duì)于直接插入排序,初始數(shù)據(jù)越接近正序性能越好。
14.桶排序?qū)崙?zhàn):用鏈表表示桶,從文件中讀取數(shù)字
15.一個(gè)序列用帶頭結(jié)點(diǎn)的單鏈表存儲(chǔ),采用快速排序求遞增排序
先找劃分基準(zhǔn),再分成兩半遞歸排序
void swap(int&x,int &y) { int temp = x; x = y; y = temp; } LinkNode* Paration(LinkNode* first,LinkNode*tail) { if(first == tail) return first;//只有一個(gè)結(jié)點(diǎn) LinkNode* pt = first,* p =pt->next; int temp = first->data;//基準(zhǔn)值 while(true) { if(p->data < temp) { pt = pt->next; swap(pt->data,p->data);//如果p結(jié)點(diǎn)小于temp } if(p == tail) break; p = p->next; } swap(pt->data,first->data);//找到劃分位置,即pt指向的結(jié)點(diǎn) return pt; } void QuickSort(LinkNode*&first,LinkNode*&tail) { if(first!=NULL && tail != NULL && first != tail) { LinkNode*pt = Paration(first,tail); QuickSort(first,pt); QuickSort(pt->next,tail); } }通過修改結(jié)點(diǎn)的值實(shí)現(xiàn),沒有改變結(jié)點(diǎn)地址。
16.二叉查找樹的操作
遍歷操作:
void inSort(btnode*node) { if(node!=NULL) { inSort(node->lchild);//遞歸遍歷左子樹 std::cout<<node->data<<std::endl;//輸出結(jié)點(diǎn)數(shù)據(jù) inSort(node->rchild);//遞歸遍歷右子樹 } }插入結(jié)點(diǎn)
步驟:如果二叉查找樹為空,則插入結(jié)點(diǎn)就為根節(jié)點(diǎn)
否則將待插入結(jié)點(diǎn)與根節(jié)點(diǎn)進(jìn)行比較,如果小于根節(jié)點(diǎn)就插入到左子樹,大于根節(jié)點(diǎn)就插入到右子樹中
二叉查找樹的刪除操作
考慮幾種情況:
如果沒有子女結(jié)點(diǎn),則修改父節(jié)點(diǎn)的指針,使其指向NULL,刪除結(jié)點(diǎn)x
如果結(jié)點(diǎn)x只有一個(gè)孩子結(jié)點(diǎn),修改其父節(jié)點(diǎn)和孩子結(jié)點(diǎn)的parent指針 建立聯(lián)系,如果左右孩子都存在 用左子樹中的最大值或者右子樹的最小值代替x
二叉查找樹的查找操作
btnode search(DataType k) { btnode*node = root; while(node!=NULL) { if(node->data>k) node= node->lchild; else if(node->data < k) node = node->rchild; else break;//相等則退出 } return node; }判斷一個(gè)數(shù)組是否是二叉查找樹的后序遍歷
//判斷一個(gè)數(shù)組是否是一個(gè)二叉查找樹的后序遍歷 bool postSequence(int sequence[],int len) {if(sequence == NULL || len <= 0) return false;//先驗(yàn)錯(cuò) int root = sequence[len-1];//找到根節(jié)點(diǎn) int i = 0; for(;i<len-1;i++) if(sequence[i]>root) break;//左子樹均小于根節(jié)點(diǎn) 這里i是下標(biāo)也是長(zhǎng)度 int j = i; for(;j < len-1;j++)//注意這里j是下標(biāo)而不是長(zhǎng)度 if(sequence[j] < root) return false;//如果右子樹有小于根節(jié)點(diǎn)的 返回false bool left = true;//判斷左子樹是否是查找樹 if(i > 0) {//有可能沒有左子樹,故加上i>0的判斷left = postSequence(sequence,i);//i是左子樹的長(zhǎng)度 } bool right = true; if(j < len-1) right = postSequence(sequence+i,len-i-1);//注意右子樹開始的序列和右子樹的長(zhǎng)度 return (right && left); }int main() {int s[7];for(int i = 0;i <7;i++)cin>>s[i];bool res=postSequence(s,7);if(res)cout<<"是"; }5 7 6 9 11 10 8
17.線索二叉樹
我們利用二叉樹鏈?zhǔn)酱鎯?chǔ)結(jié)構(gòu)的空指針來(lái)存儲(chǔ)結(jié)點(diǎn)的直接前驅(qū)指針和直接后繼結(jié)點(diǎn)指針,這些指針稱為線索,利用線索來(lái)保留前驅(qū)和后繼結(jié)點(diǎn)信息的二叉樹稱為線索二叉樹。
我們?cè)O(shè)定結(jié)點(diǎn)左指針指向前驅(qū)節(jié)點(diǎn),右指針指向后繼結(jié)點(diǎn),且每個(gè)結(jié)點(diǎn)增加兩個(gè)標(biāo)志量lFlag和rFlag,當(dāng)lchild rchild是指向孩子節(jié)點(diǎn)的指針時(shí)相應(yīng)的標(biāo)志量為1,是線索時(shí)為0
也就是說rchild lchild可能時(shí)指向孩子的指針也可能是線索
數(shù)據(jù)結(jié)構(gòu)如下:
template<typename DataType>class ThreadBTNode{ public: friend class ThreadBT<DataType>; ThreadBTNode(DataType data) { lFlag = rFlag =0; this->data = data; lchild = rchild = NULL; } ThreadBTNode() { lFlag = rFlag =0; lchild = rchild = NULL;//帶數(shù)據(jù)結(jié)點(diǎn)的構(gòu)造函數(shù)和空構(gòu)造函數(shù) } private: ThreadBTNode<DataType>*lchild,*rchild;//左右指針域 int lFlag,rFlag;//標(biāo)志量 DataType data;//數(shù)據(jù)域 }; template<typename DataType>class ThreadBT{ public: ThreadBT(){ root = NULL; } ThreadBT(DataType data) { root = new ThreadBTNode< DataType>(data); } private:}尋找中序遍歷線索二叉樹的前驅(qū)和后繼結(jié)點(diǎn)
線索二叉樹的前驅(qū)和后繼結(jié)點(diǎn)與使用的遍歷方式有關(guān),線索二叉樹之間的區(qū)別就是根據(jù)不同的遍歷方式其結(jié)點(diǎn)的前驅(qū)和后繼不同
對(duì)二叉樹進(jìn)行一次中序遍歷便明白結(jié)點(diǎn)node的前驅(qū)節(jié)點(diǎn)是左子樹中最右邊的結(jié)點(diǎn),后繼結(jié)點(diǎn)是右子樹最左邊的結(jié)點(diǎn)
同理查找后繼:
template<typename DataType>ThreadBTNode<DataType>*ThreadBT<DataType>::succ(ThreadBTNode<DataType>*node) { ThreadBTNode<DataType>*s; s = node->rchild; if(node->rFlag == 1) { while(s->lFlag == 1)//循環(huán)查找結(jié)點(diǎn)的右子節(jié)點(diǎn),找到右子樹的最左節(jié)點(diǎn) s = s->lchild; } return s; }先序、后序遍歷的前驅(qū)后繼
標(biāo)志ltag和rtag,并約定:
0 結(jié)點(diǎn)中的 lchild字段指向左孩子;
ltag=
1 結(jié)點(diǎn)中的 lchild字段指向前驅(qū);
0 結(jié)點(diǎn)中的 rchild字段指向右孩子;
rtag=
1 結(jié)點(diǎn)中的 rchild字段指向后繼;
(1) 先序線索二叉樹中先序后繼的求解——先序后繼
對(duì)先序線索二叉樹中任意結(jié)點(diǎn)P,求其先序后繼。
討論:
(a) 若p有左孩子 ——按照遍歷的過程描述(PPLPR)可知,
其后繼應(yīng)為:左子樹PL中的第一個(gè)結(jié)點(diǎn),
即p的左孩子結(jié)點(diǎn),
因此, p->lchild為其后繼;
(b) 否則,*p有右孩子 ——類似地,可知
p->rchild為其后繼;
? 否則,
p->rchild為其后繼線索;
由此得算法如下:
后序前驅(qū)的求解
分析:
(a) 若p有右孩子 —— 右孩子結(jié)點(diǎn)是其前驅(qū);
(b) 否則,若P有左孩子 —— 左孩子結(jié)點(diǎn)是其前驅(qū);
? 否則 —— p -> lchild是其前驅(qū)線索 。
與先序后繼的求解方式對(duì)稱。
由此得算法如下:
(6) 后序后繼的求解
分析:
(a) 根 —— 無(wú)后繼;
(b) 若*p是其父結(jié)點(diǎn)的右孩子 —— 父結(jié)點(diǎn)是其后繼;
? 若是父結(jié)點(diǎn)的左孩子 ——
無(wú)右兄弟 —— 父結(jié)點(diǎn)是其后繼;
有右兄弟
—— 右兄弟子樹的后序序列的第一個(gè)結(jié)點(diǎn)是其后繼
——右兄弟子樹中最左下的葉子結(jié)點(diǎn)。
線索二叉樹中插入結(jié)點(diǎn)
在結(jié)構(gòu)中插入一個(gè)元素或結(jié)點(diǎn)是常見的運(yùn)算。
在線索二叉樹中插入一個(gè)結(jié)點(diǎn)時(shí),不僅要實(shí)現(xiàn)指定結(jié)點(diǎn)的父子關(guān)系的運(yùn)算,還需要在插入結(jié)點(diǎn)后,通過修改線索,以維護(hù)二叉樹的線索關(guān)系。例如,在右圖線索二叉樹中,將某結(jié)點(diǎn)插入到作為結(jié)點(diǎn)F的左孩子。
如何實(shí)現(xiàn)相關(guān)的操作?
分析:對(duì)這類插入操作,通常從兩個(gè)方面來(lái)考慮其實(shí)現(xiàn):
(1)一個(gè)是父子關(guān)系的連接實(shí)現(xiàn)
按照指定關(guān)系連接即可
(2)線索關(guān)系的維護(hù)
這一關(guān)系的實(shí)現(xiàn)有一定的難度。
下面分別討論。
(1)父子關(guān)系的連接實(shí)現(xiàn)
對(duì)前述問題,由問題描述可知,
假設(shè)指針P指示到了F結(jié)點(diǎn),
連接操作如圖所示,操作如下:
p->lchild=s;
p->ltag=0;
(2)線索關(guān)系的維護(hù)
這一操作主要由如下組成:
(某些操作可能因具體問題而不同)
設(shè)置新插入結(jié)點(diǎn)的前驅(qū)、后繼線索
修改新結(jié)點(diǎn)的前驅(qū)結(jié)點(diǎn)的后繼線索
修改新結(jié)點(diǎn)的后繼結(jié)點(diǎn)的前驅(qū)線索
例如,在右圖線索二叉樹中,
將某結(jié)點(diǎn)插入到作為結(jié)點(diǎn)F的左孩子。
如何實(shí)現(xiàn)相關(guān)的操作?
下面先討論線索操作的一般性方法,
然后給出本題的操作實(shí)現(xiàn)。假設(shè)當(dāng)前結(jié)點(diǎn)為P,由于線索關(guān)系使得結(jié)點(diǎn)之間建立了線性關(guān)系,因此,插入結(jié)點(diǎn)時(shí)的線索維護(hù)類似于雙鏈表中插入結(jié)點(diǎn)。由于新結(jié)點(diǎn)是作為P的后繼,
因此,可有如下的邏輯圖:
. 二叉樹的線索化算法實(shí)現(xiàn)
——先序線索化為例
前驅(qū)結(jié)點(diǎn)的后繼線索化;
當(dāng)前結(jié)點(diǎn)的前驅(qū)線索化;
判斷當(dāng)前結(jié)點(diǎn)是否需要后繼線索化
遍歷線索二叉樹
//利用前面的尋找前驅(qū)和后繼節(jié)點(diǎn)的算法 中序 void inOrder() { if(root !=NULL) { ThreadBTNode<DataType>*s = root; while(prior(s)!=NULL) { s = prior(s);//找到起始結(jié)點(diǎn) } while(succ(s)!=NULL) { cout<<s->data<<" "; s = succ(s);//從起始結(jié)點(diǎn)開始遍歷節(jié)點(diǎn) } }18.分塊查找
基本思想:先插索引表,利用順序查找或者二分查找確定待查關(guān)鍵字k,屬于哪一個(gè)塊
根據(jù)索引表中塊首記錄的地址找到主表中塊的起始地址,利用順序查找的方法查找k,如果找到則返回地址,否則返回null
總結(jié)
以上是生活随笔為你收集整理的【练习】2021下半年数据结构刷题笔记和总结 (二) 树、查找-- 不同的排序算法、二叉排序树 平衡二叉树、哈希表查找、线索二叉树、的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【学习笔记】mongodb的使用(二)f
- 下一篇: 【flask学习笔记】flask与HTT