关于AVLTree(C++实现)没有统一旋转操作的问题

最近疫情比较严重,只能在家里休息,利用休息之余,我用C++把AVL树实现了一遍

大学老师只讲一些比较简单的数据结构和算法,这些高级数据结构还是需要自己主动学习并且动手来实现的,

从前只听说过AVLTree,我从看书了解原理到把它一点一点写出来最后在调试一共花了大概3天的时间。应该已经算很长时间了。

一般情况下AVL树是不用我么自己写的,但是为了有一份已经实现的代码作为我以后再来回顾算法实现的依照,我还是决定对自己狠一些把它实现了一遍

以下代码均采用C++11 标准

在ubuntu 18.04上经过编译和调试

/*
 * BinarySearchTree.h
 * 1. 添加元素时需自己做判断元素是否合法
 * 2. 除层序遍历外,本源代码均采用递归遍历,若要减少栈的消耗,应该实现递归遍历
 * 3. 本代码实现的AVL树没有统一旋转操作,采用分情况讨论LL,LR,RR,RL来进行树的平衡
 * Created on: 2020年1月29日
 *   Author: LuYonglei
 */
#ifndef SRC_BINARYSEARCHTREE_H_
#define SRC_BINARYSEARCHTREE_H_
#include <queue>
template<typename Element>
class BinarySearchTree {
public:
  BinarySearchTree(int (*cmp)(Element e1, Element e2)); //比较函数指针
  virtual ~BinarySearchTree();
  int size(); //元素的数量
  bool isEmpty(); //是否为空
  void clear() {
    //清空所有元素
    NODE *node = root_;
    root_ = nullptr;
    using namespace std;
    queue<NODE*> q;
    q.push(node);
    while (!q.empty()) {
      NODE *tmp = q.front();
      if (tmp->left != nullptr)
        q.push(tmp->left);
      if (tmp->right != nullptr)
        q.push(tmp->right);
      delete tmp;
      q.pop();
    }
  }
  void add(Element e) {
    //添加元素
    add(e, cmp_);
  }
  void remove(Element e) {
    //删除元素
    remove(Node(e, cmp_));
  }
  bool contains(Element e) {
    //是否包含某元素
    return Node(e, cmp_) != nullptr;
  }
  void preorderTraversal(bool (*visitor)(Element &e)) {
    //前序遍历
    if (visitor == nullptr)
      return;
    bool stop = false; //停止标志,若stop为true,则停止遍历
    preorderTraversal(root_, stop, visitor);
  }
  void inorderTraversal(bool (*visitor)(Element &e)) {
    //中序遍历
    if (visitor == nullptr)
      return;
    bool stop = false; //停止标志,若stop为true,则停止遍历
    inorderTraversal(root_, stop, visitor);
  }
  void postorderTraversal(bool (*visitor)(Element &e)) {
    //后序遍历
    if (visitor == nullptr)
      return;
    bool stop = false; //停止标志,若stop为true,则停止遍历
    postorderTraversal(root_, stop, visitor);
  }
  void levelOrderTraversal(bool (*visitor)(Element &e)) {
    //层序遍历,迭代实现
    if (visitor == nullptr)
      return;
    levelOrderTraversal(root_, visitor);
  }
  int height() {
    //树的高度
    return height(root_);
  }
  bool isComplete() {
    //判断是否是完全二叉树
    return isComplete(root_);
  }
private:
  int size_;
  typedef struct _Node {
    Element e;
    _Node *parent;
    _Node *left;
    _Node *right;
    int height; //节点的高度
    _Node(Element e_, _Node *parent_) :
        e(e_), parent(parent_), left(nullptr), right(nullptr), height(1) {
      //节点构造函数
    }
    inline bool isLeaf() {
      return (left == nullptr && right == nullptr);
    }
    inline bool hasTwoChildren() {
      return (left != nullptr && right != nullptr);
    }
    inline int balanceFactor() {
      //获得节点的平衡因子
      int leftHeight = left == nullptr ? 0 : left->height; //获得左子树的高度
      int rightHeight = right == nullptr ? 0 : right->height; //获得右子树的高度
      return leftHeight - rightHeight;
    }
    inline bool isBalanced() {
      //判断node是否平衡
      int balanceFactor_ = balanceFactor();
      return balanceFactor_ >= -1 && balanceFactor_ <= 1; //平衡因子为-1,0,1则返回true
    }
    inline void updateHeight() {
      //更新节点的高度
      int leftHeight = left == nullptr ? 0 : left->height; //获得左子树的高度
      int rightHeight = right == nullptr ? 0 : right->height; //获得右子树的高度
      height = 1 + (leftHeight > rightHeight ? leftHeight : rightHeight); //把节点高度更新为左右子树最大的高度+1
    }
    inline bool isLeftChild() {
      //判断节点是否是父亲节点的左子结点
      return parent != nullptr && parent->left == this;
    }
    inline bool isRightChild() {
      //判断节点是否是父亲节点的右子结点
      return parent != nullptr && parent->right == this;
    }
    inline _Node* tallerChild() {
      //获得高度更高的子树
      int leftHeight = left == nullptr ? 0 : left->height; //获得左子树的高度
      int rightHeight = right == nullptr ? 0 : right->height; //获得右子树的高度
      if (leftHeight > rightHeight)
        return left;
      if (leftHeight < rightHeight)
        return right;
      return isLeftChild() ? left : right;
    }
  } NODE;
  NODE *root_;
  int (*cmp_)(Element e1, Element e2); //为实现树的排序的个性化配置,私有成员保存一个比较函数指针
  NODE* Node(Element e, int (*cmp_)(Element e1, Element e2)) {
    //返回e元素所在的节点
    NODE *node = root_;
    while (node != nullptr) {
      int cmp = cmp_(e, node->e);
      if (cmp == 0) //找到了元素
        return node;
      if (cmp > 0) { //待寻找元素大于节点存储的元素
        node = node->right;
      } else { //待寻找元素小于节点存储的元素
        node = node->left;
      }
    }
    return nullptr;
  }
  NODE* predecessor(NODE *node) {
    //返回node的前驱节点
    if (node == nullptr)
      return nullptr;
    //前驱节点在左子树
    NODE *tmp = node->left;
    if (tmp != nullptr) {
      while (tmp->right != nullptr)
        tmp = tmp->right;
      return tmp;
    }
    //从父节点,祖父节点中寻找前驱节点
    while (node->parent != nullptr && node == node->parent->left) {
      node = node->parent;
    }
    return node->parent;
  }
  NODE* successor(NODE *node) {
    //返回node的后继节点
    if (node == nullptr)
      return nullptr;
    //后继节点在右子树
    NODE *tmp = node->right;
    if (tmp != nullptr) {
      while (tmp->left != nullptr)
        tmp = tmp->left;
      return tmp;
    }
    //从父节点,祖父节点中寻找后继节点
    while (node->parent != nullptr && node == node->parent->right) {
      node = node->parent;
    }
    return node->parent;
  }
  void afterRotate(NODE *gNode, NODE *pNode, NODE *child) {
    //在左旋转与右旋转中统一调用
    pNode->parent = gNode->parent;
    if (gNode->isLeftChild())
      gNode->parent->left = pNode;
    else if (gNode->isRightChild())
      gNode->parent->right = pNode;
    else
      //此时gNode->parent 为nullptr,gNode为root节点
      root_ = pNode;
    if (child != nullptr)
      child->parent = gNode;
    gNode->parent = pNode;
    //左右子树发生变化,所以要更新高度
    gNode->updateHeight();
    pNode->updateHeight();
  }
  void rotateLeft(NODE *gNode) {
    //对gNode进行左旋转
    NODE *pNode = gNode->right;
    NODE *child = pNode->left;
    gNode->right = child;
    pNode->left = gNode;
    afterRotate(gNode, pNode, child);
  }
  void rotateRight(NODE *gNode) {
    //对gNode进行右旋转
    NODE *pNode = gNode->left;
    NODE *child = pNode->right;
    gNode->left = child;
    pNode->right = gNode;
    afterRotate(gNode, pNode, child);
  }
  void rebalance(NODE *gNode) {
    //恢复平衡,grand为高度最低的不平衡节点
    NODE *pNode = gNode->tallerChild();
    NODE *nNode = pNode->tallerChild();
    if (pNode->isLeftChild()) {
      if (nNode->isLeftChild()) {
        //LL
        /*
         *    gNode
         *   /     对gNode右旋
         *   pNode    ====>    pNode
         *  /            /   \
         *  nNode          nNode  gNode
         */
        rotateRight(gNode);
      } else {
        //LR
        /*
         *    gNode         gNode
         *   /    对pNode左旋   /    对gNode右旋
         *   pNode   ====>    nNode   ====>    nNode
         *   \          /           /   \
         *    nNode       pNode         pNode gNode
         */
        rotateLeft(pNode);
        rotateRight(gNode);
      }
    } else {
      if (nNode->isLeftChild()) {
        //RL
        /*
         *  gNode         gNode
         *   \    对pNode右旋  \    对gNode左旋
         *   pNode   ====>    nNode   ====>    nNode
         *   /            \          /   \
         *  nNode           pNode       gNode pNode
         */
        rotateRight(pNode);
        rotateLeft(gNode);
      } else {
        //RR
        /*
         *  gNode
         *  \    对gNode左旋
         *   pNode   ====>    pNode
         *   \          /   \
         *    nNode       gNode nNode
         */
        rotateLeft(gNode);
      }
    }
  }
  void afterAdd(NODE *node) {
    //添加node之后的调整
    if (node == nullptr)
      return;
    node = node->parent;
    while (node != nullptr) {
      if (node->isBalanced()) {
        //如果节点平衡,则对其更新高度
        node->updateHeight();
      } else {
        //此时对第一个不平衡节点操作,使其平衡
        rebalance(node);
        //整棵树恢复平衡后,跳出循环
        break;
      }
      node = node->parent;
    }
  }
  void add(Element e, int (*cmp_)(Element e1, Element e2)) {
    //当树为空时,添加的节点作为树的根节点
    if (root_ == nullptr) {
      root_ = new NODE(e, nullptr);
      size_++;
      //插入一个根节点之后进行调整
      afterAdd(root_);
      return;
    }
    //当添加的节点不是第一个节点
    NODE *parent = root_;
    NODE *node = root_;
    int cmp = 0; //比较结果
    while (node != nullptr) {
      parent = node; //保存父节点
      cmp = cmp_(e, node->e); //由函数指针来比较
      if (cmp > 0) {
        node = node->right; //添加的元素大于节点中的元素
      } else if (cmp < 0) {
        node = node->left; //添加的元素小于节点中的元素
      } else {
        node->e = e; //相等时就覆盖
        return; //添加的元素等于节点中的元素,直接返回
      }
    }
    //判断要插入父节点的哪个位置
    NODE *newNode = new NODE(e, parent); //为新元素创建节点
    if (cmp > 0) {
      parent->right = newNode; //添加的元素大于节点中的元素
    } else {
      parent->left = newNode; //添加的元素小于节点中的元素
    }
    size_++;
    //添加一个新节点之后进行调整
    afterAdd(newNode);
  }
  void afterRemove(NODE *node) {
    //删除node之后的调整
    if (node == nullptr)
      return;
    node = node->parent;
    while (node != nullptr) {
      if (node->isBalanced()) {
        //如果节点平衡,则对其更新高度
        node->updateHeight();
      } else {
        //此时对不平衡节点操作,使其平衡
        rebalance(node);
      }
      node = node->parent;
    }
  }
  void remove(NODE *node_) {
    //删除某一节点
    if (node_ == nullptr)
      return;
    size_--;
    //优先删除度为2的节点
    if (node_->hasTwoChildren()) {
      NODE *pre = successor(node_); //找到node_的后继节点
      node_->e = pre->e; //用后继节点的值覆盖度为2的节点的值
      //删除后继节点(后继节点的度只能为1或0)
      node_ = pre;
    }
    //此时node_的度必然为0或1
    NODE *replacement = node_->left != nullptr ? node_->left : node_->right;
    if (replacement != nullptr) {      //node_的度为1
      replacement->parent = node_->parent;
      if (node_->parent == nullptr)      //度为1的根节点
        root_ = replacement;
      else if (node_->parent->left == node_)
        node_->parent->left = replacement;
      else
        node_->parent->right = replacement;
      //所有删除操作准备完成,准备释放节点内存前进行平衡操作
      afterRemove(node_);
      delete node_;
    } else if (node_->parent == nullptr) {      //node_是叶子节点,也是根节点
      root_ = nullptr;
      //所有删除操作准备完成,准备释放节点内存前进行平衡操作
      afterRemove(node_);
      delete node_;
    } else {      //node_是叶子节点,但不是根节点
      if (node_->parent->left == node_)
        node_->parent->left = nullptr;
      else
        node_->parent->right = nullptr;
      //所有删除操作准备完成,准备释放节点内存前进行平衡操作
      afterRemove(node_);
      delete node_;
    }
  }
  void preorderTraversal(NODE *node, bool &stop,
      bool (*visitor)(Element &e)) {
    //递归实现前序遍历
    if (node == nullptr || stop == true)
      return;
    stop = visitor(node->e);
    preorderTraversal(node->left, stop, visitor);
    preorderTraversal(node->right, stop, visitor);
  }
  void inorderTraversal(NODE *node, bool &stop, bool (*visitor)(Element &e)) {
    //递归实现中序遍历
    if (node == nullptr || stop == true)
      return;
    inorderTraversal(node->left, stop, visitor);
    if (stop == true)
      return;
    stop = visitor(node->e);
    inorderTraversal(node->right, stop, visitor);
  }
  void postorderTraversal(NODE *node, bool &stop,
      bool (*visitor)(Element &e)) {
    //递归实现后序遍历
    if (node == nullptr || stop == true)
      return;
    postorderTraversal(node->left, stop, visitor);
    postorderTraversal(node->right, stop, visitor);
    if (stop == true)
      return;
    stop = visitor(node->e);
  }
  void levelOrderTraversal(NODE *node, bool (*visitor)(Element &e)) {
    if (node == nullptr)
      return;
    using namespace std;
    queue<NODE*> q;
    q.push(node);
    while (!q.empty()) {
      NODE *node = q.front();
      if (visitor(node->e) == true)
        return;
      if (node->left != nullptr)
        q.push(node->left);
      if (node->right != nullptr)
        q.push(node->right);
      q.pop();
    }
  }
  int height(NODE *node) {
    //某一节点的高度
    return node->height;
  }
  bool isComplete(NODE *node) {
    if (node == nullptr)
      return false;
    using namespace std;
    queue<NODE*> q;
    q.push(node);
    bool leaf = false; //判断接下来的节点是否为叶子节点
    while (!q.empty()) {
      NODE *node = q.front();
      if (leaf && !node->isLeaf()) //判断叶子节点
        return false;
      if (node->left != nullptr) {
        q.push(node->left);
      } else if (node->right != nullptr) { //node->left == nullptr && node->right != nullptr
        return false;
      }
      if (node->right != nullptr) {
        q.push(node->right);
      } else { //node->right==nullptr
        leaf = true;
      }
      q.pop();
    }
    return true;
  }
};
template<typename Element>
BinarySearchTree<Element>::BinarySearchTree(int (*cmp)(Element e1, Element e2)) :
    size_(0), root_(nullptr), cmp_(cmp) {
  //树的构造函数
}
template<typename Element>
BinarySearchTree<Element>::~BinarySearchTree() {
  // 析构函数
  clear();
}
template<typename Element>
inline int BinarySearchTree<Element>::size() {
  //返回元素个数
  return size_;
}
template<typename Element>
inline bool BinarySearchTree<Element>::isEmpty() {
  //判断是否为空树
  return size_ == 0;
}
#endif /* SRC_BINARYSEARCHTREE_H_ */
main方法
/*
 * main.cpp
 *
 * Created on: 2020年1月29日
 *   Author: LuYonglei
 */
#include "BinarySearchTree.h"
#include <iostream>
#include <time.h>
using namespace std;
template<typename Element>
int compare(Element e1, Element e2) {
  //比较函数,相同返回0,e1<e2返回-1,e1>e2返回1
  return e1 == e2 ? 0 : (e1 < e2 ? -1 : 1);
}
template<typename Elemnet>
bool visitor(Elemnet &e) {
  cout << e << " ";
  cout << endl;
  return false; //若返回true,则在遍历时会退出
}
int main(int argc, char **argv) {
  BinarySearchTree<double> a(compare);
//  a.add(85);
//  a.add(19);
//  a.add(69);
//  a.add(3);
//  a.add(7);
//  a.add(99);
//  a.add(95);
//  a.add(2);
//  a.add(1);
//  a.add(70);
//  a.add(44);
//  a.add(58);
//  a.add(11);
//  a.add(21);
//  a.add(14);
//  a.add(93);
//  a.add(57);
//  a.add(4);
//  a.add(56);
//  a.remove(99);
//  a.remove(85);
//  a.remove(95);
  clock_t start = clock();
  for (int i = 0; i < 1000000; i++) {
    a.add(i);
  }
  for (int i = 0; i < 1000000; i++) {
    a.remove(i);
  }
//  a.inorderTraversal(visitor);
  clock_t end = clock();
  cout << end - start << endl;
//  cout <<a.height()<< endl;
//  cout << a.isComplete() << endl;
//  a.remove(7);
//  a.clear();
//  a.levelOrderTraversal(visitor);
//  cout << endl;
//  cout<<a.contains(0)<<endl;
}

总结

以上所述是小编给大家介绍的关于AVLTree(C++实现)没有统一旋转操作的问题,希望对大家有所帮助!

(0)

相关推荐

  • python 和c++实现旋转矩阵到欧拉角的变换方式

    在摄影测量学科中,国际摄影测量遵循OPK系统,即是xyz转角系统,而工业中往往使用zyx转角系统. 旋转矩阵的意义:描述相对地面的旋转情况,yaw-pitch-roll对应zyx对应k,p,w #include <iostream> #include<stdlib.h> #include<eigen3/Eigen/Core> #include<eigen3/Eigen/Dense> #include<stdlib.h> using namespa

  • C++实现旋转数组的二分查找

    本文实例讲述了C++实现旋转数组的二分查找方法,分享给大家供大家参考.具体方法如下: 题目要求: 旋转数组,如{3, 4, 5, 1, 2}是{1, 2, 3, 4, 5}的一个旋转,要求利用二分查找查找里面的数. 这是一道很有意思的题目,容易考虑不周全.这里给出如下解决方法: #include <iostream> using namespace std; int sequentialSearch(int *array, int size, int destValue) { int pos

  • C++实现一维向量旋转算法

    在<编程珠玑>一书的第二章提到了n元一维向量旋转算法(又称数组循环移位算法)的五种思路,并且比较了它们在时间和空间性能上的区别和优劣.本文将就这一算法做较为深入的分析.具体如下所示: 一.问题描述 将一个n元一维向量向左旋转i个位置.例如,假设n=8,i=3,向量abcdefgh旋转为向量defghabc.简单的代码使用一个n元的中间向量在n步内可完成该工作.你能否仅使用几十个额外字节的内存空间,在正比于n的时间内完成向量的旋转? 二.解决方案 思路一:将向量x中的前i个元素复制到一个临时数组

  • C++中求旋转数组中的最小数字(经典面试题)

    面试题:旋转数组的最小数字 题目:把一个数组的最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转.输入一个递增数组的旋转,输出旋转数组的最小元素.例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转,该数组的最小值为1. 算法: (1)当输入的旋转数组非法时:处理! (2)当输入的旋转数组正常时,index1 = 0:index2=length-1: a:如果arry[index1] <arry[index2]时:说明数组为原数组,并没有进行旋转:    b:如果arry[ind

  • 关于AVLTree(C++实现)没有统一旋转操作的问题

    最近疫情比较严重,只能在家里休息,利用休息之余,我用C++把AVL树实现了一遍 大学老师只讲一些比较简单的数据结构和算法,这些高级数据结构还是需要自己主动学习并且动手来实现的, 从前只听说过AVLTree,我从看书了解原理到把它一点一点写出来最后在调试一共花了大概3天的时间.应该已经算很长时间了. 一般情况下AVL树是不用我么自己写的,但是为了有一份已经实现的代码作为我以后再来回顾算法实现的依照,我还是决定对自己狠一些把它实现了一遍 以下代码均采用C++11 标准 在ubuntu 18.04上经

  • vue实现的封装全局filter并统一管理操作示例

    本文实例讲述了vue实现的封装全局filter并统一管理操作.分享给大家供大家参考,具体如下: 在前后端分离的项目中,经常会有后台返回的数据需要进过处理才能显示到页面上的场景. 使用最多的场景就是日期和时间的处理,后台一般返回的都是时间戳,那么我们就要对时间戳进行处理. 下面就拿封装全局的处理日期和时间的 filter 来展示如何 vue 如何封装全局 filter 并统一处理. 在 src 目录下新建 filters 目录用来专门存放全局过滤器,如果项目的过滤器过多,那么就要按类型分类. 我司

  • vue简单封装axios插件和接口的统一管理操作示例

    本文实例讲述了vue简单封装axios插件和接口的统一管理操作.分享给大家供大家参考,具体如下: 现在很多公司的项目都是前后端分离的项目,那么说到前后端分离,必定会有ajax请求来获得后台的数据. 在做jquery项目的时候,我们都会使用它封装好的方法来直接发起ajax请求. 在vue项目中,我们使用最多的就是axios这个插件,下面就简单的封装下这个插件并且把接口给统一化管理. 一.安装和配置 1.在项目根目录下打开终端安装 npm install axios -S 2.安装完成以后,在src

  • vue项目接口管理,所有接口都在apis文件夹中统一管理操作

    在vue开发中,会涉及到很多接口的处理,当项目足够大时,就需要定义规范统一的接口,如何定义呢? 方法可能不只一种,本文使用axios+async/await进行接口的统一管理 本文使用vue-cli生成的项目举例 使用接口管理之前 在项目的某个具体组件中调接口,把调用接口的方法直接写在mounted中,或在是methods中 比如: xxx.vue <template> <div id="areaTree"> <!-- 标题 --> <div

  • OpenCV+Imutils实现图像的旋转操作

    目录 前言 使用 OpenCV 旋转图像 使用 OpenCV 顺时针旋转图像 围绕任意点旋转图像 使用 Imutils 旋转图像 总结 前言 本文,将描述使用 OpenCV 和 Imutils 围绕任意点旋转指定角度的图像所需的步骤. 使用 OpenCV 旋转图像 使用 OpenCV 旋转图像: 1.使用 OpenCV 的 imread 函数加载所需的图像. 脚本:加载并显示原始图像 # import required library import cv2 # load image from d

  • C++AVL树4种旋转详讲(左单旋、右单旋、左右双旋、右左双旋)

    目录 引子:AVL树是因为什么出现的? 1.AVl树的的特性 2.AVl树的框架 3.AVL树的插入 3.1四种旋转(左单旋.右单旋.左右双旋.右左双旋) 3.1.1左单旋 3.1.2右单旋 3.1.3左右双旋 3.1.4右左双旋 附:AVL的性能 总结 引子:AVL树是因为什么出现的? 二叉搜索树可以缩短查找的效率,如果数据有序或接近有序二叉搜索树将退化为单支树,查找元素相当于在顺序表中搜索元素,效率低下时间复杂度:O(N) 两位俄罗斯的数学家G.M.Adelson-Velskii和E.M.L

  • 关于C++11的统一初始化语法示例详解

    前言 本文主要给大家介绍了C++11统一初始化语法的相关内容,关于在当前新标准C++11的语法看来,变量合法的初始化器有如下形式: X a1 {v}; X a2 = {v}; X a3 = v; X a4(v); 其实,上面第一种和第二种初始化方式在本质上没有任何差别,添加=则是一种习惯上的行为.使用花括号进行的列表初始化语法,其实早在C++98时代就有了,只不过历史上他们只是被用来对数组元素进行初始化操作,以及初始化自定义POD类型的数据(简单理解就是可以memcpy复制对象的类型).比如:

  • C语言左旋转字符串与翻转字符串中单词顺序的方法

    左旋转字符串 题目: 定义字符串的左旋转操作:把字符串前面的若干个字符移动到字符串的尾部. 如把字符串 abcdef  左旋转 2  位得到字符串 cdefab.请实现字符串左旋转的函数. 要求时间对长度为 n  的字符串操作的复杂度为 O(n),辅助内存为 O(1). 分析: 网上看到解法很多种,就不详细说明了. 我采用的是数组不对称的交换时间复杂度应该是O(n). 代码实现(GCC编译通过): #include "stdio.h" #include "stdlib.h&q

  • Android单点触控实现图片平移、缩放、旋转功能

    相信大家使用多点对图片进行缩放,平移的操作很熟悉了,大部分大图的浏览都具有此功能,有些app还可以对图片进行旋转操作,QQ的大图浏览就可以对图片进行旋转操作,大家都知道对图片进行缩放,平移,旋转等操作可以使用Matrix来实现,Matrix就是一个3X3的矩阵,对图片的处理可分为四个基础变换操作,Translate(平移变换).Rotate(旋转变换).Scale (缩放变换).Skew(错切变换),如果大家对Matrix不太了解的话可以看看这篇文章(点击查看),作者对每一种Matrix的变换写

  • java之左旋转字符串介绍

    题目:定义字符串的左旋转操作:把字符串前面的若干个字符移动到字符串的尾部.如把字符串abcdef左旋转2位得到字符串cdefab.请实现字符串左旋转的函数.要求时间对长度为n的字符串操作的复杂度为O(n),辅助内存为O(1). 分析:如果不考虑时间和空间复杂度的限制,最简单的方法莫过于把这道题看成是把字符串分成前后两部分,通过旋转操作把这两个部分交换位置.于是我们可以新开辟一块长度为n+1的辅助空间,把原字符串后半部分拷贝到新空间的前半部分,在把原字符串的前半部分拷贝到新空间的后半部分.不难看出

随机推荐