BST(排序二叉树/二叉搜索树)的插入,查找,删除
BST:
- 空树
- 左右子树若存在,左子树上任一结点都小于根结点的值,右子树上都大于根结点值;它的左右子树也都是BST。
BST提高查找、插入和删除效率
结构体:
typedef struct BST { int nValue; struct BST *pLeft; struct BST *pRight; }BinaryTree;
创建:
给定一个值,首先看树是否是空树,空树则新插入的值为根,若所要插入值 大于 ’当前结点值 ‘则向结点左子树搜索 直到遇到空值,反之亦然
void InsertBST(BinaryTree **pTree,int x);
void CreateBST(int arr[],int len);
1 void Insert(BinaryTree **pTree,int x) 2 { 3 BinaryTree *pTemp = NULL;//新建一个temp存待插入数 4 pTemp = (BinaryTree*)malloc(sizeof(BinaryTree)); 5 pTemp->nValue = x; 6 pTemp->pLeft = NULL; 7 pTemp->pRight = NULL; 8 9 //树为空时插入的直接是根 10 if(*pTree == NULL) 11 { 12 *pTree = pTemp; 13 return; 14 15 } 16 BinaryTree *pMark = *pTree; 17 while(pMark) 18 { 19 //遍历至待插入位置 20 //大于‘根结点‘情况,找到空的放右 21 if(pMark->nValue < x) 22 { 23 if(pMark->pRight == NULL) 24 { 25 pMark->pRight = pTemp; 26 return ; 27 } 28 pMark = pMark->pRight;//非空继续遍历树下一个结点 29 } 30 //小于’根结点‘情况,找到空放左侧 31 else if(pMark->nValue > x) 32 { 33 if(pMark->pLeft ==NULL) 34 { 35 pMark->pLeft = pTemp; 36 return; 37 } 38 pMark = pMark->pLeft; 39 } 40 else 41 { 42 printf("erro\n"); 43 free(pTemp); 44 pTemp = NULL;//插入失败,释放掉这块空间 45 return; 46 } 47 48 } 49 }
1 递归: 2 void Insert2(BinaryTree **pTree,int x) 3 { 5 if(*pTree == NULL) 6 { 7 BinaryTree *pTemp = NULL; 8 pTemp = (BinaryTree*)malloc(sizeof(BinaryTree)); 9 pTemp->nValue = x; 10 pTemp->pLeft = NULL; 11 pTemp->pRight = NULL; 12 *pTree = pTemp; 13 return; 14 } 15 BinaryTree *pCur = *pTree; 16 if(pCur->nValue <x) 17 Insert2(&(pCur->pRight),x); 18 else if(pCur->nValue > x) 19 Insert2(&(pCur->pLeft),x); 20 else 21 { 22 printf("erro\n"); 23 return; 24 } 25 }
1 BinaryTree* CreateBST(int arr[],int nlen) 2 { 3 if(arr == NULL || nlen <=0) 4 return NULL;//待插入为空时直接返回空 5 BinaryTree *pTree = NULL; 6 int i; 7 for(i =0;i<nlen;i++) 8 { 9 Insert(&pTree,arr[i]); 10 } 11 return pTree; 12 }
删除:先搜索结点位置,再删除
搜索和插入类似,只不过是遇到值相同停止。
删除则有3种情况:
- 是叶子结点——直接删除
- 度为1的结点——删除后结点的父亲与左孩子或者右孩子相连
- 度为2的结点——找到待删结点的左最大或者右最小来顶替这个位置,保证BST结构不被破坏。(左最大或者右最小 这两个点肯定是度为1或者度为0 的点)问题转换为度为1结点情况
老师给出的代码是倒着,先将情况3->情况2/1 对情况2而言,1是特殊情况即左右孩子为空。我自己的思路是封装3种情况,然后再判断,代码还有点问题
通过搜索找到待删结点位置,找到其父亲结点位置,本身位置,这里返回两个值,直接使用二级指针来修改这两个值(所以在删除的函数里有定义pFather后再传入)
1 void Search(BinaryTree *pTree,int x,BinaryTree **pDel,BinaryTree **pFather) 2 { 3 while(pTree) 4 { 5 6 if(x < pTree->nValue ) 7 { 8 *pFather = pTree; 9 pTree = pTree->pLeft; 10 } 11 else if(x > pTree->nValue) 12 { 13 *pFather = pTree; 14 pTree = pTree ->pRight; 15 } 16 else 17 { 18 *pDel = pTree; 19 return; 20 }}
21 pFather = NULL;//待删的点为根结点时,手动赋NULL 22 }
1 void Delete(BinaryTree **pTree,int x) 2 { 3 BinaryTree *pDel = NULL; //待删除结点 4 BinaryTree *pFather = NULL; 5 Search(*pTree,x,&pDel,&pFather); 6 if(pDel == NULL)return; 7 //根、非根 ? 8 //两个孩子,此时无法直接换根达到目的,是不是根影响不大 9 if(pDel->pRight != NULL && pDel->pLeft != NULL) 10 { 11 //找左边最大的 12 pFather = pDel; 13 BinaryTree *pMark = pDel->pLeft; //pMark标记左边最大的位置 14 15 while(pMark->pRight) 16 { 17 pFather = pMark; 18 pMark = pMark->pRight; 19 } 20 //换值 21 pDel->nValue = pMark->nValue; 22 pDel = pMark; 23 } 24 //1个孩子或者0个孩子 25 //根 ——直接换根 26 if(pFather == NULL) 27 { 28 (*pTree) = pDel->pLeft ? pDel->pLeft :pDel->pRight; 29 free(pDel); 30 pDel = NULL; 31 return; 32 } 33 //非根 34 if(x <pFather->nValue) 35 { 36 pFather->pLeft = pDel->pLeft ? pDel ->pLeft : pDel->pRight; 37 //x在pFather的左边,pDel的左存在则直接与pFather相连,反之与右相连; 38 //特殊情况两个都为NULL; 39 } 40 else 41 { 42 pFather->pRight = pDel->pLeft ? pDel ->pLeft : pDel->pRight; 43 } 44 free(pDel); 45 pDel = NULL; 46 47 }
BST变成有序双向链表(递归实现):
void BSTtoList(BinaryTree *pTree,BinaryTree **pHead,BinaryTree **pTail) { if(pTree == NULL)return; //BST中序遍历即实现有序的 //左 BSTtoList(pTree->pLeft,pHead,pTail); //根 if(*pHead == NULL) { *pHead = pTree; *pTail = pTree; } else { (*pTail)->pRight = pTree; pTree->pLeft = *pTail; (*pTail) = pTree; } //右 BSTtoList(pTree->pRight,pHead,pTail); }

浙公网安备 33010602011771号