大一数据结构拯救,课本代码详细解析之查找算法

#include<iostream>
using namespace std;
//顺序查找,很简单不需要脑子
int Sequential_Search(int a[],int n,int key)
{
    a[0] = key;//这里默认了你已经把a[]这个数组调成了首个为空的形态,然后便可以节约时间
    int i = n;//这里为了省去判断i是否大于n这个老算法的冗杂过程,将i提出然后默认a[0]==key
    //保证了数组不会越界访问
    for (; a[i] != key; i--);
        return i;
}
//折半查找
int Search_bin(int a[], int n, int key)//这里是BinSearch代码中重复的部分根据面向对象的编程原则,将其
//置于此,方便了代码的复用
{
    int low = 1;
    int high = n;
    while (low <= high)
    {
        int mid = (low + high) / 2;
        if (key == a[mid])return mid;
        else if (key < a[mid]) high = mid - 1;
        else low = mid + 1;
    }
    return 0;
}
int BinSearch(int r[], int low, int high, int key)
{
    if (low <= high)
    {
        int mid = (low + high) / 2;
        if (key == r[mid])return mid;
        else if (key < r[mid])return BinSearch(r, low, mid - 1, key);
        return BinSearch(r, mid + 1, high, key);
    }//递归函数    
    else return 0;
}
//索引查找
//数表查找

template <class T>
class BiNode
{
public:
    T data;
    BiNode<T>* lch;
    BiNode<T>* rch;
    BiNode() :lch(NULL), rch(NULL) {};//直接在构造函数中对类的内部变量进行赋值
};
template<class T>
class BST
{
public:
    BST(T r[], int n);
    ~BST();
    BiNode<T>* Search(BiNode<T>* R, T key);
    void InsertBST(BiNode<T>*& R, BiNode<T>* s);
    void Delete(BiNode<T>*& R);
    bool DeleteBST(BiNode<T>*& R, T key);
private:
    BiNode<T>* Root;
};
template <class T>
BiNode<T>* BST<T>::Search(BiNode<T>* R, T key)
{
    if (R == NULL)
        return NULL;//查找失败
    if (key == R->data)
        return R;//查找成功
    else if (key < R->data)return Search(R->lch, key);//左子树查找
    else return Search(R->rch, key);//右子树查找
    
}
template<class T>
void BST<T>::InsertBST(BiNode<T>*& R, BiNode<T>* s)
//R为根节点,s为待插入的节点的地址
//指针引用R,目的是什么?就是既传入R本身的指针关系同时使得函数内外可以借此传递参数值
/*举例来说,如果你是传入的指针
那么你就必须得先判断R的左右孩子是否为空来传入,不然会找不到双亲,然后你还需要使得R的左右孩子进行赋值会比较麻烦
同时还将R的新值传递到了外部,这是C++实践中出现的通过引用传出数值的一种方法*/
{
    if (R==NULL)
    {
        R = s;//如果直到找到了叶子节点,就插进去
    }
    else if (s->data < R->data)
    {
        InsertBST(R->lch, s);//如果小于,则向左寻找插入位置
    }
    else
        InsertBST(R->rch, s);//如果大于,就向右寻找插入位置
}
template<class T>
BST<T>::BST(T r[], int n)
{//这个是构造函数
    Root = NULL;//初始化根节点
    for (int i = 0; i < n; i++)
    {
        BiNode<T>* s = new BiNode<T>;//新建节点,Default Operation
        s->data = r[i];//以T数组的方式进行传入数据来构造树
        s->lch = s->rch = NULL;//这一步不是第一次吃亏了哈,因为空节点不赋值,默认值并不为0
        InsertBST(Root, s);
    }
}
template<class T>
bool BST<T>::DeleteBST(BiNode<T>*& R, T key)
{
    if (R == NULL)
    {
        return false;//找到头了依然找不到对应key的节点就返回false
    }
    else
    {
        if (key == R->data)
        {
            Delete(R); return true;//等于就删除,同时return 也是结束函数调用,使得函数出栈,减少算力占用
        }
        else if (key < R->data) return DeleteBST(R->lch, key);//小于从左子树找
        else return DeleteBST(R->rch, key);//大于从右子树找
    }
}
template<class T>
void BST<T>::Delete(BiNode<T>*& R)
{
    //删除二叉排序树中的某个节点是整个二叉排序树算法中最为复杂困难的
    /*主要是你分情况,如果其是叶子节点,说明 它就是一组边的边际值,这时候直接删去即可
    * 但是如果它不是,如果它只含有左右孩子中的一个也还好,删去后把孩子接上即可
    * 困难是它有两个孩子的时候
    */
    //其次这个函数其实在BSTDelete中已找到对应key值的节点的时候才调用的,所以节点必然已经找到
    //接下来需要做的只是针对其位置情况进行的处理
    BiNode<T>* q, * s;
    if (R->lch == NULL)
    {//只有右子树,同时这也包括了左右子树都没有的情况
        //如果左右子树都没有q存R,R=NULL,再delete q是完全可行而不会触发bug的
        q = R; R = R->rch; delete q;
    }
    else if (R->rch == NULL)
    {//只有左子树
        q = R; R = R->lch; delete q;
    }
    else
    {//左右子树都存在
        /*首先思考
        * 怎么删除左子树都有的二叉排序树
        *                63
        *              55    90
                   42     70  98
                         67    
        显然删除63,你是肯定要55或者90上位顶替的,这样二叉树的变化比较小,也比较容易操作
        90  上位  那么就需要保持二叉排序树准则,使得63的整个左子树接在90最左的叶子的左孩子上
        55上位  ,那就需要让63的整个右子树接在55的整个右孩子上
        这两种方案都行
        成品图       90           或者 55
                   70  98            42    90
                 67                        70  98
               55                       67    
              42 
              显然都可行,我们选择右侧的
        */            
        q = R; s = R->lch;//先把R存 在 q里,再把其左孩子存在s里
        while (s->rch != NULL)
        {
            q = s; s = s->rch;//一直寻找左孩子,然后使得s为孩子,q为双亲
        }//结束时的效果:s成为了右叶子节点,q为其双亲,且s的右孩子为空
        //说白了上述操作就是要找到左子树的最大节点------肯定在先找左子树后一直向右找右子树
        R->data = s->data;//这时候将s(左子树最大节点)的值赋给根节点
        //技巧提取:有时候没必要破坏原数据结构,只需要修改数据就可以达成目的的就没必要着手于树的修改了
        /*如果*/
        if (q != R)//如果根节点R的左孩子的右孩子存在(就是while语句内的内容至少执行了一次)
        {
            q->rch = s->lch;//让s的左子树接到s的双亲q的右孩子位置处,这时候s一定存在,不用担心调用
            //了空指针,但是s的左子树未必存在,也无所谓,没有左子树那就把q->rch赋空相当于把s节点摘除
            //但是s仍然存着原节点的地址方便后续释放空间
        }
        else//如果R的左孩子没有右孩子
            R->lch = s->lch;//那就只需要在将R的左孩子的data赋给R后将R的左孩子节点删除即可
        //这个操作可以直接让二叉排序树跳过s节点
        delete s;//删除s很漂亮
    }
}
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值