https://blog.youkuaiyun.com/LKJgdut/article/details/105649206
代码如下:
//二分查找法
----非递归实现
// 二分查找法,在有序数组arr中,查找target
// 如果找到target,返回相应的索引index
// 如果没有找到target,返回-1
template<typename T>
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;//mid要在while循环里更新的
if( arr[mid] == target )
return mid;
if( arr[mid] > target )
r = mid - 1;
else
l = mid + 1;
}
return -1;
}
----递归实现
template<typename T>
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);
}
template<typename T>
int binarySearch2(T arr[], int n, T target){
return __binarySearch2( arr , 0 , n-1, target);
}
----测试用例
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"<<endl;
// 测试递归的二分查找法
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"<<endl;
delete[] a;
return 0;
}
二分搜索树的递归实现代码如下:
(注意对比递归方法的宏观语意以及微观上是如何实现的。)
#include <iostream>
using namespace std;
template <typename Key, typename Value>
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;
}
};
//下面定义二分搜索树的属性:根节点、二分搜索树元素个数:count;
Node *root;
int count;
//初始化二分搜索树,没有结点(根节点为null)
public:
BST(){
root = NULL;
count = 0;
}
~BST(){
destroy( root );
// TODO: ~BST()
}
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<Node*> 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 );
}
};
//-----------------------------------------------------------------------**递归添加**
private:
// 向以node为根的二叉搜索树中,插入节点(key, value)
// 返回插入新节点后的二叉搜索树的根
Node* insert(Node *node, Key key, Value value){
if( node == NULL ){
count ++;
return new Node(key, value);
}
/*
如果e比当前根结点的元素小,我们调用 insert(Node *node, Key key, Value value),返回插入元素value后的以node.left为根的左子树的根:node.left。
在下一层递归的方法中:
如果node.left不存在,会直接添加到node.left位置;如果node.left存在,继续调用insert,
看以node.left 为根结点的子树的左孩子与右孩子是否存在....直到某一个node.left或者node.right不存在,就插入,
或者是直到value等于某个结点的元素,就结束递归,不将value添加到树中。
*/
if( key == node->key )
node->value = value;
else if( key < node->key )
/*
返回插入元素e后的以node.left为根的子树的根:node.left,此时由于node.left可能是变化的(可能之前node.left为null,但是现在赋值后不为null)
因此,我们必须要将当前指向左孩子的指针node.left指向返回的子二分搜索树的根,这样整个二分搜索树就连接起来。。
*/
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 preOrderNR()
{
//首先,如果当前二分搜索树的根为null,不需要遍历,直接结束程序
if(root == NULL)
return;
//创建一个栈,用于存储结点对象,记录我们下一次操作将要访问哪一些结点,类似于之前说的系统栈的功能!。
Stack<Node*> nodeStack;
nodeStack.push(root);//先将最大树的根结点入栈
//当栈不为null的时候,遍历操作栈中的结点
while (!nodeStack.isEmpty())
{
Node *cur = nodeStack.top();//将栈顶结点出栈,这个结点就是当前我们要处理的结点
nodeStack.pop();
cout<<cur->key<<endl;//处理栈顶结点(这里的处理就是打印结点元素)
/*
当前结点打印完后,我们要先处理左孩子结点所代表的树(左子树),再处理右孩子结点所代表的树(左子树),为了下一次循环可以先处理左孩子,
应该先将右孩子结点入栈,再将左孩子结点入栈。下一次循环出栈的时候,就是先处理左孩子结点。这时,又会判断左孩子结点的左右孩子结点是否存在,
存在同样按右孩子、左孩子的顺序入栈,下一次循环就将左孩子、右孩子分别出栈处理.....
直到最开始 root的 左孩子结点的左子树(以左孩子结点为根的子树)处理完,接着处理右孩子结点的右子树,直到所有结点处理完毕....
注意画图分析这种方法!
中序遍历和后续遍历也可以使用非递归实现,但是这种情况的代码比较复杂,实际应用也很少!不做实现。
*/
if(cur->right)
nodeStack.push(cur->right);
if(cur.left != NULL)
nodeStack.push(cur->left);
}
}
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;//等价于node.right = null
count --;
return rightNode;//此时node结点被删除了,或者说它失效了,指向它的指针已经指向新的二分搜索树的根 rightNode
}
//使得原来指向node.left的指针,指向返回的删除了结点后的二分搜索树的根:rightNode
node->left = removeMin(node->left);
return node;//最后将删除了结点的树的根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;
}
//----------------------------------现在,我们尝试删除二分搜索树的任意值()这部分较难
};