博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
二分搜索树
阅读量:5140 次
发布时间:2019-06-13

本文共 11732 字,大约阅读时间需要 39 分钟。

  前言:

   先立flag吧,18年每天一个算法或数据结构知识点的学习与总结!每周5个,大约会有50个吧,感觉基础的数据结构和算法都应该掌握了!但不能每天都写博客,时间有限,每周一篇或两篇进行分享,年底进行检验结果,加油!

  这次要介绍的是二分搜索树,从名字也能看出它的实现和作用了,实现是以二叉树为基础来实现的,作用是进行数据查找。

  一、二分查找

  二分查找应该应熟悉了吧?要在顺序储存结构中进行查找,跟最中间的数据进行比较,小的话去前半部分进行查找,否则,去后半部分去查找,其实,可以用迭代和递归分别来实现二分查找。

  1、迭代法  

  首先,用迭代法来实现,代码如下:

// 二分查找法,在有序数组arr中,查找target// 如果找到target,返回相应的索引index// 如果没有找到target,返回-1template
int binarySearch(T arr[], int n, T target){ // 在arr[l...r]之中查找target int l = 0, r = n-1; while( l <= r ){ //int mid = (l + r)/2; int mid = l + (r-l)/2; if( arr[mid] == target ) return mid; if( arr[mid] > target ) r = mid - 1; else l = mid + 1; } return -1;}

  需要注意的一点是:int mid = (l + r)/2;我注释的这个求mid会有一个bug,当l和r达到int的最大值时,会出现越界的问题。这也是算法的魅力,需要注意很多细节并有很多地方需要优化!学无止境!

  2、递归法

  用递归法实现,代码如下:

// 用递归的方式写二分查找法template
int __binarySearch2(T arr[], int l, int r, T target){ if( l > r ) return -1; int mid = (l+r)/2; if( arr[mid] == target ) return mid; else if( arr[mid] > target ) return __binarySearch2(arr, 0, mid-1, target); else return __binarySearch2(arr, mid+1, r, target);}

  用递归最重要的一点就是:要有结束条件

  3、main函数测试两种方法

  写一个main函数,进行简单的测试,数据量用的比较大,PS:用一个算法进行一下优化,一看数据量就几百,根本没必要优化,数据量不过万,谈算法意义并不大!

main函数主要对两种算法进行时间对比:

int main() {    int n = 1000000;    int* a = new int[n];    for( int i = 0 ; i < n ; i ++ )        a[i] = i;    // 测试非递归二分查找法    clock_t startTime = clock();    for( int i = 0 ; i < 2*n ; i ++ ){        int v = binarySearch(a, n, i);        if( i < n )            assert( v == i );        else            assert( v == -1 );    }    clock_t endTime = clock();    cout << "Binary Search (Without Recursion): " << double(endTime - startTime) / CLOCKS_PER_SEC << " s"<
View Code

  运行结果如下:  

  会发现递归运行时间长,因为递归会反复调用,会耗时的。

  二、二分搜索树

  1、介绍

  什么是二叉搜索树呢?

  首先,它是一颗二叉树:

(1)若左子树不空,则左子树上所有结点的值均小于或等于它的的值;

(2)若右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值;

(3)左、右子树也分别为二叉排序树;

  如下图就是二叉搜索树:

  

 

  2、构建一个BST类

  先建一个BST类用于存放二叉搜索树,还包括一些构造函数、插入和查找等,代码如下:

template 
class BST {private: struct Node { Key key; Value value; //key和value值相等 Node *left; //左子树 Node *right; //右子树 //结构体的构造函数 Node(Key key, Value value) { this->key = key; this->value = value; this->left = this->right = NULL; } Node(Node *node) { this->key = node->key; this->value = node->value; this->left = node->left; this->right = node->right; } }; Node *root; int count;public: BST() { //类的构造函数 root = NULL; count = 0; } ~BST() { destroy(root); } int size() { return count; } bool isEmpty() { return count == 0; } void insert(Key key, Value value) { root = insert(root, key, value); } bool contain(Key key) { //是否包含 return contain(root, key); } Value* search(Key key) { //查找 return search(root, key); } // 前序遍历 void preOrder() { preOrder(root); } // 中序遍历 void inOrder() { inOrder(root); } // 后序遍历 void postOrder() { postOrder(root); } // 层序遍历 void levelOrder() { queue
q; q.push(root); while (!q.empty()) { Node *node = q.front(); q.pop(); cout << node->key << endl; if (node->left) q.push(node->left); if (node->right) q.push(node->right); } } // 寻找最小的键值 Key minimum() { assert(count != 0); Node* minNode = minimum(root); return minNode->key; } // 寻找最大的键值 Key maximum() { assert(count != 0); Node* maxNode = maximum(root); return maxNode->key; } // 从二叉树中删除最小值所在节点 void removeMin() { if (root) root = removeMin(root); } // 从二叉树中删除最大值所在节点 void removeMax() { if (root) root = removeMax(root); } // 从二叉树中删除键值为key的节点 void remove(Key key) { root = remove(root, key); }
View Code

  3、插入节点

  首先,它是二叉树,都具有的性质是递归,所以用递归相对比较简单,画一张图如下供参考:

  

  假如,先插入8,跟根节点12比较,比它小去左子树,跟节点5比较,比它大去右子树;再例如,插入13,跟根节点12比较,比它大去右子树;代码如下:

// 向以node为根的二叉搜索树中,插入节点(key, value)    // 返回插入新节点后的二叉搜索树的根    Node* insert(Node *node, Key key, Value value) {        if (node == NULL) {            count++;            return new Node(key, value);        }        if (key == node->key)            node->value = value;        else if (key < node->key)            node->left = insert(node->left, key, value);        else    // key > node->key            node->right = insert(node->right, key, value);        return node;    }

  4、查找节点

  跟插入的思想很像,也是不断比较,用递归的思想去查找,代码如下:

// 在以node为根的二叉搜索树中查找key所对应的value    Value* search(Node* node, Key key) {        if (node == NULL)            return NULL;        if (key == node->key)            return &(node->value);        else if (key < node->key)            return search(node->left, key);        else // key > node->key            return search(node->right, key);    }

  5、三种遍历方法

  三种方法分别是:先序遍历、中序遍历和后序遍历。画图讲解一下吧,如下图:  PS:依旧是全网最丑图,不接受反驳!

  

  三种遍历的本质:访问路径一样,访问顺序不一样。

  先序遍历:根左右,先访问根节点、再左节点、其次右节点

  中序遍历:左根右,先访问左节点、再根节点、其次右节点

  后序遍历:左右根,先访问左节点、再右节点、其次根节点

  用上面的图解释这句话,那条红线就是访问路径,三种遍历方式都是这条访问路径;用节点旁边的三个红点来表示访问和顺序,这么说,可能还是懵。

  拿先序来举个例子吧:访问路径一样,都从根节点12出发,遇到“先序红点”,直接输出12,然后是5,最后的先序结果上图下面的结果。 

 (1)先序遍历

  先序遍历程序如下:

// 对以node为根的二叉搜索树进行先序遍历    void preOrder(Node* node) {        if (node != NULL) {            cout << node->key << endl;            preOrder(node->left);            preOrder(node->right);        }    }

  (2)中序遍历

  中序遍历程序如下: 

// 对以node为根的二叉搜索树进行中序遍历    void inOrder(Node* node) {        if (node != NULL) {            inOrder(node->left);            cout << node->key << endl;            inOrder(node->right);        }    }

  (3)后序遍历

  后序遍历程序如下:

// 对以node为根的二叉搜索树进行后序遍历    void postOrder(Node* node) {        if (node != NULL) {            postOrder(node->left);            postOrder(node->right);            cout << node->key << endl;        }    }

  6、总体程序

  总体的程序如下,方便调试和使用,程序如下:

#include 
#include
#include
#include
using namespace std;template
class BST {private: struct Node { Key key; Value value; Node *left; Node *right; Node(Key key, Value value) { this->key = key; this->value = value; this->left = this->right = NULL; } Node(Node *node) { this->key = node->key; this->value = node->value; this->left = node->left; this->right = node->right; } }; Node *root; int count;public: BST() { root = NULL; count = 0; } ~BST() { destroy(root); } int size() { return count; } bool isEmpty() { return count == 0; } void insert(Key key, Value value) { root = insert(root, key, value); } bool contain(Key key) { return contain(root, key); } Value* search(Key key) { return search(root, key); } // 前序遍历 void preOrder() { preOrder(root); } // 中序遍历 void inOrder() { inOrder(root); } // 后序遍历 void postOrder() { postOrder(root); } // 层序遍历 void levelOrder() { queue
q; q.push(root); while (!q.empty()) { Node *node = q.front(); q.pop(); cout << node->key << endl; if (node->left) q.push(node->left); if (node->right) q.push(node->right); } } // 寻找最小的键值 Key minimum() { assert(count != 0); Node* minNode = minimum(root); return minNode->key; } // 寻找最大的键值 Key maximum() { assert(count != 0); Node* maxNode = maximum(root); return maxNode->key; } // 从二叉树中删除最小值所在节点 void removeMin() { if (root) root = removeMin(root); } // 从二叉树中删除最大值所在节点 void removeMax() { if (root) root = removeMax(root); } // 从二叉树中删除键值为key的节点 void remove(Key key) { root = remove(root, key); }private: // 向以node为根的二叉搜索树中,插入节点(key, value) // 返回插入新节点后的二叉搜索树的根 Node* insert(Node *node, Key key, Value value) { if (node == NULL) { count++; return new Node(key, value); } if (key == node->key) node->value = value; else if (key < node->key) node->left = insert(node->left, key, value); else // key > node->key node->right = insert(node->right, key, value); return node; } // 查看以node为根的二叉搜索树中是否包含键值为key的节点 bool contain(Node* node, Key key) { if (node == NULL) return false; if (key == node->key) return true; else if (key < node->key) return contain(node->left, key); else // key > node->key return contain(node->right, key); } // 在以node为根的二叉搜索树中查找key所对应的value Value* search(Node* node, Key key) { if (node == NULL) return NULL; if (key == node->key) return &(node->value); else if (key < node->key) return search(node->left, key); else // key > node->key return search(node->right, key); } // 对以node为根的二叉搜索树进行前序遍历 void preOrder(Node* node) { if (node != NULL) { cout << node->key << endl; preOrder(node->left); preOrder(node->right); } } // 对以node为根的二叉搜索树进行中序遍历 void inOrder(Node* node) { if (node != NULL) { inOrder(node->left); cout << node->key << endl; inOrder(node->right); } } // 对以node为根的二叉搜索树进行后序遍历 void postOrder(Node* node) { if (node != NULL) { postOrder(node->left); postOrder(node->right); cout << node->key << endl; } } void destroy(Node* node) { if (node != NULL) { destroy(node->left); destroy(node->right); delete node; count--; } } // 在以node为根的二叉搜索树中,返回最小键值的节点 Node* minimum(Node* node) { if (node->left == NULL) return node; return minimum(node->left); } // 在以node为根的二叉搜索树中,返回最大键值的节点 Node* maximum(Node* node) { if (node->right == NULL) return node; return maximum(node->right); } // 删除掉以node为根的二分搜索树中的最小节点 // 返回删除节点后新的二分搜索树的根 Node* removeMin(Node* node) { if (node->left == NULL) { Node* rightNode = node->right; delete node; count--; return rightNode; } node->left = removeMin(node->left); return node; } // 删除掉以node为根的二分搜索树中的最大节点 // 返回删除节点后新的二分搜索树的根 Node* removeMax(Node* node) { if (node->right == NULL) { Node* leftNode = node->left; delete node; count--; return leftNode; } node->right = removeMax(node->right); return node; } // 删除掉以node为根的二分搜索树中键值为key的节点 // 返回删除节点后新的二分搜索树的根 Node* remove(Node* node, Key key) { if (node == NULL) return NULL; if (key < node->key) { node->left = remove(node->left, key); return node; } else if (key > node->key) { node->right = remove(node->right, key); return node; } else { // key == node->key if (node->left == NULL) { Node *rightNode = node->right; delete node; count--; return rightNode; } if (node->right == NULL) { Node *leftNode = node->left; delete node; count--; return leftNode; } // node->left != NULL && node->right != NULL Node *successor = new Node(minimum(node->right)); count++; successor->right = removeMin(node->right); successor->left = node->left; delete node; count--; return successor; } }};void shuffle(int arr[], int n) { srand(time(NULL)); for (int i = n - 1; i >= 0; i--) { int x = rand() % (i + 1); swap(arr[i], arr[x]); }}int main() { srand(time(NULL)); BST
bst = BST
(); int n = 10; for (int i = 0; i < n; i++) { int key = rand() % n; // 为了后续测试方便,这里value值取和key值一样 int value = key; //cout<
<<" "; bst.insert(key, value); } // test remove // remove elements in random order int order[10] = { 0}; for (int i = 0; i < n; i++) order[i] = i; shuffle(order, n); for (int i = 0; i < n; i++) if (bst.contain(order[i])) { bst.remove(order[i]); cout << "After remove " << order[i] << " size = " << bst.size() << endl; } system("pause"); return 0;}
View Code

  

  总结:  

   坚持学习与分享!喜欢的帮忙推荐,有问题欢迎随时留言!

  

  

  

转载于:https://www.cnblogs.com/liudw-0215/p/9835691.html

你可能感兴趣的文章
weibo_json
查看>>
30 最小n个数
查看>>
ACM题目————最长回文串
查看>>
AOSP ON MAKO(在NEXUS 4上刷ANDROID 4.4 源代码包-下载/配置/编译/刷机)
查看>>
nativeXml使用方法
查看>>
LightOJ1074Extended Traffic(bellman_ford最短路+负环标记)
查看>>
Android Studio 编译不通过,报错“找不到org.apache.http
查看>>
SQL Server Failover Cluster (FCI) installations is the failure of the Network Name
查看>>
发布快半年了,终于有个案例了,大家有兴趣看看
查看>>
HTML几类标签的应用总结
查看>>
1.Java简介
查看>>
生无可恋的一叶知秋#百度刘超事件#
查看>>
box-sizing属性
查看>>
3.1.12 内置方法__str__(self)
查看>>
springmvc集成Freemarke配置的几点
查看>>
自己写的仿爱奇艺综艺频道轮播图,没有淡入淡出效果
查看>>
提炼游戏引擎系列:第一次迭代
查看>>
Django 学习
查看>>
Android的事件处理机制详解(二)-----基于监听的事件处理机制
查看>>
s5-12 RIP
查看>>