java 二叉查找树和二分查找_一文带你从代码的角度详细搞懂什么是二分查找树...

一、二分查找

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

1、迭代法

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

// 二分查找法,在有序数组arr中,查找target

// 如果找到target,返回相应的索引index

// 如果没有找到target,返回-1

template

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"<

// 测试递归的二分查找法

startTime = clock();

for( int i = 0 ; i < 2*n ; i ++ ){

int v = binarySearch2(a, n, i);

if( i < n )

assert( v == i );

else

assert( v == -1 );

}

endTime = clock();

cout << "Binary Search (Recursion): " << double(endTime - startTime) / CLOCKS_PER_SEC << " s"<

delete[] a;

return 0;

}

运行结果如下:

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

二、二分搜索树

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);

}

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;

}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值