日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

【练习】2021下半年数据结构刷题笔记和总结 (二) 树、查找-- 不同的排序算法、二叉排序树 平衡二叉树、哈希表查找、线索二叉树、

發(fā)布時(shí)間:2024/9/30 编程问答 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【练习】2021下半年数据结构刷题笔记和总结 (二) 树、查找-- 不同的排序算法、二叉排序树 平衡二叉树、哈希表查找、线索二叉树、 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

記錄自己下半年寫題目的記錄。題目來(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

#include<queue> struct Node {int val;bool ifHas;//是否被賦值過Node* left, * right;Node() :ifHas(false), left(NULL), right(NULL) {}; }; Node* root; char s[maxn];//保存讀入結(jié)點(diǎn) void addNode(int v, char* s) {int n = strlen(s);for (int i = 0; i < n; i++){if (s[i] == 'L') {if (root->left == NULL){root->left = new Node();//結(jié)點(diǎn)不存在,建立新節(jié)點(diǎn)root = root->left;}}else if (s[i] == 'R') {if (root->right == NULL){root->right = new Node();root = root->right;}}if (root->ifHas == true) continue;root->val = v;//賦值root->ifHas = true;//置為已經(jīng)訪問} }//插入結(jié)點(diǎn)的操作 bool bfs(vector<int>& ans) {Node* temp;queue<Node*>q;q.push(root);while (!q.empty()){temp = q.front();if (temp->ifHas == false) return false;//注意這個(gè)驗(yàn)錯(cuò)。如果沒有被賦值過,表明輸入有誤ans.push_back(q.front()->val);q.pop();if (temp->left)q.push(temp->left);if (temp->right)q.push(temp->right);return true;} } bool read_input() {Node node;int val;while (1){if (scanf("%s", s) != 1) return false;if (!strcmp("()", s)) break;//讀到結(jié)尾標(biāo)志,退出循環(huán)sscanf(&s[1],"%d",&val);//s[1]是字符 &s[1]是字符串“11,LL)”addNode(val, strchr(s, ',') + 1);}return true; }

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ù)是一樣的

Node* pre = NULL; void InOrder(Node* bt) {if (bt == NULL) return;if (bt->left) {InOrder(bt->left);bt->left = pre;pre->right = bt;}if (pre != NULL)pre->right = bt;bt->left = pre;pre = bt;InOrder(bt->right);//遞歸構(gòu)造右子樹 } void convert(Node* bt, Node*& first) {//轉(zhuǎn)化為首結(jié)點(diǎn)為first的雙鏈表Node* p = bt;if (p->left != NULL){p = p->left;}first = p;pre = NULL;InOrder(bt);} int main() {Node* first,*bt;//用排序二叉樹算法建樹convert(bt, first);//轉(zhuǎn)化為有序單鏈表//輸出雙鏈表的函數(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)的算法如下:

int binSearch(int a[],int low,int high,int target) { //二分查找的函數(shù) while(low<=high) { int mid=low+(high-low)>>1;//位運(yùn)算更高效 if(a[mid]==target) return mid; else if(a[mid]>target) high=mid-1; else low=mid+1; } return -1;//沒找到 } int search(int a[],int n,int target) { int i; for(i = 0;i < n;i++) { if(a[i]==target)return i; if(a[i]>a[i+1])break; } int low=i+1,high=n-1; binSearch(a,low,high,target);//如果a[0,..m]中沒有找到目標(biāo),則在第二個(gè)遞增數(shù)組中查找

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)就插入到右子樹中

void insert(btnode*node) { btnode*temp = NULL,*cnt;//中間指針 cnt = root; while(cnt != NULL) { temp=cnt;//用于保存最后一個(gè)有效結(jié)點(diǎn) if(cnt->data > node->data) { cnt = cnt->lchild; } else { cnt = cnt->rchild; } } if(temp==NULL) //根節(jié)點(diǎn)為空 { root = node; count++;} if(node->data<temp->data) {temp->lchild = node; count++;} else {temp->rchild = node; count++;} }

二叉查找樹的刪除操作

考慮幾種情況:
如果沒有子女結(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>::prior(ThreadBTNode<DataType>*node) { ThreadBTNode<DataType>*s = node->lchild; if(node->lFlag==1) {//為1時(shí)代表node的左指針不是線索,不指向前驅(qū),如果lFlag為0,可以直接返回s while(s->Flag==1) { s = s->rchild; }//一直找結(jié)點(diǎn)的右子結(jié)點(diǎn),直到找到左子樹的最右子節(jié)點(diǎn)} return s;//返回前驅(qū) }

同理查找后繼:

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字段指向后繼;

typedef struct tbnode{elementtype data;tbnode *lchild, *rchild;int ltag, rtag;}tbnode

(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為其后繼線索;
由此得算法如下:

tbnode *presuc(tbnode *p) {if ( p -> ltag == 0 ) return ( p -> lchild );else return ( p -> rchild ); }

后序前驅(qū)的求解
分析:
(a) 若p有右孩子 —— 右孩子結(jié)點(diǎn)是其前驅(qū);
(b) 否則,若P有左孩子 —— 左孩子結(jié)點(diǎn)是其前驅(qū);
? 否則 —— p -> lchild是其前驅(qū)線索 。
與先序后繼的求解方式對(duì)稱。
由此得算法如下:

tbnode *postpre(tbnode *p) {if ( p -> rtag == 0 ) return ( p -> rchild );else return ( p -> lchild );}

(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)是否需要后繼線索化

void prethread( bnode * t, bnode * pre){ if ( t != NULL ){if (( pre != NULL ) && ( pre->rtag==1)) pre->rchild=t;if ( t->lchild == NULL ) { t->lchild = pre; t->ltag=1;}else t->ltag=0;if ( t->rchild == NULL ) t->rtag=1;else t->rtag=0;pre = t;if ( t->ltag==0) prethread( t->lchild, pre); if ( t->rtag==0) prethread( t->rchild, pre); } }

遍歷線索二叉樹

//利用前面的尋找前驅(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)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。