草庐IT

二叉树(Java实现)

大数据阿嘉 2023-05-27 原文

声明:本文部分文章取自于

Java中关于二叉树详解_来学习的小张的博客-CSDN博客_java 二叉树原理

更多关于二叉树详情可以点击上面链接

目录

一:树形结构

二:二叉树

2.1 二叉树的遍历:

2.2 二叉树的查找:

2.3 二叉树删除节点:(简单版)

2.4 顺序存储二叉树:

2.5 线索化二叉树:


一:树形结构

是一种非线性数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。它具有以下的特点:

  • 有一个特殊的节点,称为根节点,根节点没有前驱节点;
  • 除根节点外,其余节点被分成M(M > 0)个互不相交的集合T1、T2、......、Tm,其中每一个集合 Ti (1 <= i<= m) 又是一棵与树类似的子树。每棵子树的根节点有且只有一个前驱,可以有0个或多个后继
  • 树是递归定义的。

  1. 节点的度一个节点含有的子树的个数称为该节点的度; 如上图:A的度为6
  2. 树的度:一棵树中,最大的节点的度称为树的度; 如上图:树的度为6
  3. 叶子节点或终端节点度为0的节点称为叶节点; 如上图:B、C、H、I…等节点为叶节点;
  4. 双亲节点或父节点若一个节点含有子节点,则这个节点称为其子节点的父节点; 如上图:A是B的父节点;
  5. 孩子节点或子节点一个节点含有的子树的根节点称为该节点的子节点; 如上图:B是A的孩子节点;
  6. 根结点:一棵树中,没有双亲结点的结点;如上图:A
  7. 节点的层次:从根开始定义起,根为第1层,根的子节点为第2层,以此类推;
  8. 树的高度或深度树中节点的最大层次; 如上图:树的高度为4

以下概念仅做了解即可

  1. 非终端节点或分支节点度不为0的节点; 如上图:D、E、F、G...等节点为分支节点;
  2. 兄弟节点具有相同父节点的节点互称为兄弟节点; 如上图:B、C是兄弟节点;
  3. 堂兄弟节点双亲在同一层的节点互为堂兄弟;如上图:H、I互为兄弟节点;
  4. 节点的祖先从根到该节点所经分支上的所有节点;如上图:A是所有节点的祖先;
  5. 子孙以某节点为根的子树中任一节点都称为该节点的子孙。如上图:所有节点都是A的子孙;
  6. 森林m(m>=0)棵互不相交的树的集合称为森林。

二:二叉树

2.1 二叉树的遍历:

使用前序、中序、后续来遍历二叉树

  • 前序遍历:先输出父节点,再遍历左子树和右子树
  • 中序遍历:先遍历左子树,再输出父节点,再遍历右子树
  • 后序遍历:先遍历左子树,再遍历右子树,最后输出父节点

小结:看输出父节点的顺序,就确定是前序、中序还是后序。


分析二叉树的前序,中序,后序的遍历步骤

1.创建一颗二叉树

2.前序遍历

  • 2.1先输出当前节点(初始的时候是root节点)
  • 2.2如果左子节点不为空,则递归继续前序遍历
  • 2.3如果右子节点不为空,则递归继续前序遍历

3.中序遍历

  • 3.1如果当前节点的左子节点不为空,则递归中序遍历
  • 3.2输出当前节点
  • 3.3如果当前的右子节点不为空,则递归中序遍历

4.后序遍历

  • 4.1如果当前节点的左子节点不为空,则递归后序遍历
  • 4.2如果当前节点的右子节点不为空,则递归后序遍历
  • 4.3输出当前节点

5.前、中、后序遍历代码实现:

public class BinaryTreeDemo {
    public static void main(String[] args) {
      //先需要创建一颗二叉树
      BinaryTree binaryTree=new BinaryTree();
      //创建需要的结点
        HeroNode root=new HeroNode(1,"宋江");
        HeroNode node2=new HeroNode(2,"吴用");
        HeroNode node3=new HeroNode(3,"卢俊义");
        HeroNode node4=new HeroNode(4,"林冲");
        HeroNode node5=new HeroNode(5,"武松");
        HeroNode node6=new HeroNode(6,"鲁智深");
        HeroNode node7=new HeroNode(7,"杨志");

        //说明,我们先手动创建该二叉树,后面我们学习递归的方式创建而常数
        root.setLeft(node2);
        root.setRight(node3);
        node2.setLeft(node4);
        node2.setRight(node5);
        node3.setLeft(node6);
        node3.setRight(node7);

        binaryTree.setRoot(root);

        //测试:
        System.out.println("前序遍历");//1,2,4,5,3,6,7
        binaryTree.preOrder();

        System.out.println("中序遍历");//4,2,5,1,6,3,7
        binaryTree.infixOrder();
        System.out.println("后序遍历");//4,5,2,6,7,3,1
        binaryTree.postOrder();
    }
}

//定义二叉树
class BinaryTree{
    private HeroNode root;//根节点

    public void setRoot(HeroNode root){
        this.root=root;
    }
    //前序遍历
    public void preOrder(){
        if(this.root!=null){
            this.root.preOrder();
        }else {
            System.out.println("二叉树为空,无法遍历");
        }
    }
    //后序遍历
    public void infixOrder(){
        if(this.root!=null){
            this.root.infixOrder();
        }else {
            System.out.println("二叉树为空,无法遍历");
        }
    }
    //后序遍历
    public void postOrder(){
        if(this.root!=null){
            this.root.postOrder();
        }else {
            System.out.println("二叉树为空,无法遍历");
        }
    }

}

//先创建HeroNode结点
class HeroNode{
    private int no;
    private String name;
    private HeroNode left;//默认为null
    private HeroNode right;//默认为null

    public HeroNode(int no, String name) {
        this.no = no;
        this.name = name;
    }

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public HeroNode getLeft() {
        return left;
    }

    public void setLeft(HeroNode left) {
        this.left = left;
    }

    public HeroNode getRight() {
        return right;
    }

    public void setRight(HeroNode right) {
        this.right = right;
    }

    @Override
    public String toString() {
        return "HeroNode [no=" + no + ", name=" + name + "]";
    }

    //编写前序遍历的方法
    public void preOrder(){
        System.out.println(this);//先输出父结点(谁调用这个方法,谁就是this)
        //递归向左子树前序遍历
        if(this.left!=null){
            this.left.preOrder();
        }
        //递归向右子树前序遍历
        if(this.right!=null){
            this.right.preOrder();
        }
    }
    //编写中序遍历的方法
    public void infixOrder(){
        //递归向左子树中序遍历
        if(this.left!=null){
            this.left.infixOrder();
        }
        //输出父结点
        System.out.println(this);
        //递归向右子树中序遍历
        if(this.right!=null){
            this.right.infixOrder();
        }
    }
    //编写后序遍历的方法
    public void postOrder(){
        //递归向左子树后序遍历
        if(this.left!=null){
            this.left.postOrder();
        }
        //递归向右子树后序遍历
        if(this.right!=null){
            this.right.postOrder();
        }
        //输出父结点
        System.out.println(this);
    }
}

2.2 二叉树的查找:

目的:使用前序、中序、后序的方式来查询指定的节点。

前序查找思路:

  • 先判断当前节点的no是否等于要查找的
  • 如果是相等的,则返回当前节点
  • 如果不相等,则判断当前节点的左子节点是否为空,如果不为空,则递归前序查找
  • 如果左递归前序查找,找到节点,则返回,否则继续判断,当前的节点的右子节点是否为空,如果不为空,则继续向右递归前序查找

中序查找思路:

  • 判断当前节点的左子节点是否为空,如果不为空,则递归中序查找
  • 如果找到,则返回,如果没有找到,就和当前节点比较,如果是则返回当前节点,否则继续进行右递归的中序查找
  • 如果右递归中序查找,找到就返回,否则为null

后序查找思路:

  • 判断当前节点的左子节点是否为空,如果不为空,则递归后序查找
  • 如果找到,就返回,如果没有找到,就判断当前节点的右子节点是否为空,如果不为空,则右递归进行后序查找,如果找到,就返回
  • 就和当前节点进行,如果找到则返回,否则返回null

查找指定节点的部分代码实现:

//编写前序查找的方法
    public HeroNode preSearch(int no){
        //先判断当前节点是否符合
        if(this.no==no){
            return this;
        }
        HeroNode resNode=null;
        //向左递归
        if(this.left!=null){
            resNode=this.left.preSearch(no);
        }
        //向右递归(判断左递归得到的节点是否为空,如果为空,则进行右递归)
        if(resNode==null&&this.right!=null){
            resNode=this.right.preSearch(no);
        }
        return resNode;//如果最终还是没有找到,则返回null
    }

    //编写中序查找的方法
    public HeroNode infixSearch(int no){
       HeroNode resNode=null;
       if(this.left!=null){
           resNode=this.left.infixSearch(no);
       }
       if(this.no==no){
           return this;
       }
       if(this.right!=null&&resNode==null){//左边没有找到,找右边
           resNode=this.right.infixSearch(no);
       }
       return resNode;//不管有没有找到,都返回resNode
    }
    //编写后序查找的方法
    public HeroNode postSearch(int no){
        HeroNode resNode=null;
        if(this.left!=null){
            resNode=this.left.postSearch(no);
        }
        if(this.right!=null&&resNode==null){
            resNode=this.right.postSearch(no);
        }
        if(this.no==no){
            return this;
        }
        return resNode;
    }

2.3 二叉树删除节点:(简单版)

规定:如果要删除的节点是叶子节点,则直接删除,如果是非叶子节点,则删除该子树。

思路:

首先先处理:

考虑如果树是空树root,如果只有一个root节点,则等价将二叉树置空。

如果不是,则进行下面步骤:

  1. 因为我们的二叉树是单向的,所以我们是判断当前节点的子节点是否需要删除节点,而不能去判断当前这个节点是不是需要删除节点。
  2. 如果当前节点的左子节点不为空,并且左子节点就是要删除节点,就将this.left=null;并且就返回(结束递归删除)
  3. 如果当前节点的右子节点不为空,并且右子节点就是要删除节点,就将this.right=null,并且就返回(结束递归删除)
  4. 如果第2和第3步没有删除节点,那么我们就需要向左子树进行递归删除。
  5. 如果第4步也没有删除节点,则应当向右子树进行递归删除。

节点删除的代码实现:

//二叉树部分:删除节点
    public void deleteNode(int no){
        if(root!=null){
            //如果只有一个root节点,这里立即判断root是不是要删除节点
            if(root.getNo()==no){
                root=null;
            }else {
                //递归删除
                root.deleteNode(no);
            }
        }else {
            System.out.println("二叉树为空,删除失败");
        }
    }
//链表部分:
//递归删除节点
    public void deleteNode(int no){
        //判断当前节点的左子节点
        if(this.left!=null&&this.left.no==no){
            this.left=null;
            return;
        }
        //判断当前节点的右子节点
        if(this.right!=null&&this.right.no==no){
            this.right=null;
            return;
        }
        //左子树递归删除
        if(this.left!=null){
            this.left.deleteNode(no);
        }
        //右子树递归删除
        if(this.right!=null){
            this.right.deleteNode(no);
        }

    }

2.4 顺序存储二叉树:

基本说明:从数据存储来啊看,数组存储方式和树的存储方式可以相互转化,即数组可以转换成树,也可以转换成数组。

 要求:

(1)图中的二叉树的节点,要求以数组的方式来存放arr【1,2,3,4,5,6,7】

(2)要求在遍历数组arr时,仍然可以以前序遍历、中序遍历和后序遍历的范围广是完成节点的遍历。

思路:

  • 顺序二叉树通常只考虑完全二叉树。
  • 数组下标为n的元素所对应的左子节点所在的下标为(2*n+1);
  • 数组下标为n的元素所对应的右子节点所在的下标为(2*n+2);
  • 数组下标为n的元素所对应的父节点为(n-1)/2;

提示:(可以自己对着上图验证一下)

顺序存储二叉树的代码实现:

public class ArrayBinaryTreeDemo {
    public static void main(String[] args) {
        int[] arr={1,2,3,4,5,6,7};
        ArrayBinaryTree arrayBinaryTree = new ArrayBinaryTree(arr);
        arrayBinaryTree.preOrder(); //1,2,4,5,3,6,7
    }
}
//编写一个ArrayBinaryTree,实现顺序存储二叉树遍历
class ArrayBinaryTree{
    private int[] arr;//存储数据节点的数组
    public ArrayBinaryTree(int[] arr){
        this.arr=arr;
    }

    //编写一个方法,完成顺序存储二叉树的前序遍历

    //重载preOrder
    public void preOrder(){
        this.preOrder(0);
    }
    public void preOrder(int index){
        //如果数组为空,或arr.length=0
        if(arr==null || arr.length==0){
            System.out.println("数组为空,不能按照二叉树的前序遍历");
        }
        //输出当前这个元素
        System.out.println(arr[index]);
        //向左递归遍历
        if((index*2+1)<arr.length){
            preOrder(2*index+1);
        }
        //向右递归遍历
        if((index*2+2)<arr.length){
            preOrder(2*index+2);
        }
    }

}

2.5 线索化二叉树:

 问题分析:

  • 当我们对上面的二叉树进行中序遍历时,数列为{8,3,10,1,6,14};
  • 但是6,8,10,14这几个节点的左右指针,并没有完全的利用上
  • 如果我们希望充分利用各个节点的左右指针,让各个节点可以指向自己的前后节点,怎么办?
  • 解决方案:线索化二叉树

使用场景:

  • 线索化的实质就是将二叉链表中的空指针改为指向前驱节点或后继节点的线索;
  • 线索化的过程就是修改二叉链表中空指针的过程,可以按照前序、中序、后序的方式进行遍历,分别生成不同的线索二叉树;
  • 有了线索二叉树之后,我们再次遍历时,就相当于操作一个双向链表。
  • 使用场景:如果我们在使用二叉树过程中经常需要遍历二叉树或者查找节点的前驱节点和后继节点,可以考虑采用线索二叉树存储结构。

线索二叉树基本介绍:

  • n个节点的二叉链表中含有(n+1)【公式:2n-(n-1)=n+1】个空指针域。利用二叉链表中的空指针域,存放指向该节点在某种遍历次序下的钱去和后继节点的指针(这种附加的指针称为“线索”)。
  • 这种加上了线索的二叉链表称为线索链表,相应的二叉树称为线索二叉树(Thread BinartTree)。根据线索性质的不同,线索二叉树可分为前序线索二叉树、中序线索二叉树和后序线索二叉树三种。
  • 一个节点的前一个节点,称为前驱节点。
  • 一个节点的后一个节点,称为后继节点。

思路分析: 

 说明:当线索化二叉树后,Node节点的属性left和right,有如下情况:

(1)left指向的是左子树,也可能是指向的前驱节点,比如 1 节点left指向的左子树,而 10 节点的left指向的就是前驱节点。

(2)rigth指向的是右子树,也可能是指向后继节点,比如 1 节点right指向的是右子树,而 10 节点的right指向的是后继节点。

public class ThreadedBinaryTreeDemo {
    /*
    中序线索化二叉树
     */
    public static void main(String[] args) {
        HeroNode root=new HeroNode(1,"tom");
        HeroNode node2=new HeroNode(3,"jack");
        HeroNode node3=new HeroNode(6,"smith");
        HeroNode node4=new HeroNode(8,"mary");
        HeroNode node5=new HeroNode(10,"king");
        HeroNode node6=new HeroNode(14,"dim");

        //二叉树,后面我们要递归创建,现在简单处理使用手动创建
        root.setLeft(node2);
        root.setRight(node3);
        node2.setLeft(node4);
        node2.setRight(node5);
        node3.setLeft(node6);

        BinaryTree binaryTree = new BinaryTree();
        binaryTree.setRoot(root);
        binaryTree.threadedNodes();

        //测试:
        HeroNode leftNode = node5.getLeft();
        HeroNode rightNode = node5.getRight();

        System.out.println("10 节点的前驱节点:"+leftNode);
        System.out.println("10 节点的后继节点:"+rightNode);

    }
}

class BinaryTree{
    private HeroNode root;//根节点

    //为了实现线索化,需要创建要给指向当前节点的前驱节点的指针
    //在递归进行线索化时,pre总是保留前一个节点
    private HeroNode pre=null;

    public void setRoot(HeroNode root){
        this.root=root;
    }

    //重载threadedNodes方法
    public void threadedNodes(){
        this.threadedNodes(root);
    }

    //编写对二叉树进行中序线索化的方法

    /**
     *
     * @param node:就是当前需要线索化的节点
     */
    public void threadedNodes(HeroNode node){
        //如果node==null,不能线索化
        if(node==null){
            return;
        }
        //(一)先线索化左子树
        threadedNodes(node.getLeft());
        //(二)线索化当前节点

        //处理当前节点的前驱节点
        //以8节点来理解
        //8节点的.left=null,8节点的.leftType=1
        if(node.getLeft()==null){
            //让当前节点的左指针指向前驱节点
            node.setLeft(pre);
            //修改当前节点的左指针的类型,指向前驱节点
            node.setLeftType(1);
        }

        //处理后继节点
        if(pre!=null&&pre.getRight()==null){
            //让前驱节点的右指针指向当前节点
            pre.setRight(node);
            //修改前驱节点的右指针类型
            pre.setRightType(1);
        }
        //!!! 每处理一个节点后,让当前节点是下一个节点的前驱节点
        pre=node;

        //(三)再线索化右子树
        threadedNodes(node.getRight());
    }


}


//先创建HeroNode结点
class HeroNode{
    private int no;
    private String name;
    private HeroNode left;//默认为null
    private HeroNode right;//默认为null
    //说明
    //1.如果leftType==0表示指向的是左子树,1则表示指向前驱节点
    //2.如果rightType==0表示指向是右子树,1则表示指向后继节点
    private int leftType;
    private int rightType;

    public HeroNode(int no, String name) {
        this.no = no;
        this.name = name;
    }

    public int getLeftType() {
        return leftType;
    }

    public void setLeftType(int leftType) {
        this.leftType = leftType;
    }

    public int getRightType() {
        return rightType;
    }

    public void setRightType(int rightType) {
        this.rightType = rightType;
    }

    public int getNo() {
        return no;
    }

    public void setNo(int no) {
        this.no = no;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public HeroNode getLeft() {
        return left;
    }

    public void setLeft(HeroNode left) {
        this.left = left;
    }

    public HeroNode getRight() {
        return right;
    }

    public void setRight(HeroNode right) {
        this.right = right;
    }

    @Override
    public String toString() {
        return "HeroNode [no=" + no + ", name=" + name + "]";
    }
}

遍历线索化二叉树:

说明:对前面的中序线索化的二叉树,进行遍历

分析:因为线索化后,各个节点指向有变化,因此原来的遍历方式不能使用,这是需要使用新的方式遍历线索化二叉树,各个节点开头通过线型方式遍历,因此无需使用递归方式,这样也提高了遍历的效率。遍历的次序应当和中序遍历保持一致。

代码实现: 

//遍历线索化二叉树
    public void threadedList(){
        //定义一个变量,存储当前遍历的节点,从root开始
        HeroNode node=root;
        while(node!=null){
            //循环的找到leftType==1的节点,第一个找到就是8节点
            //后面随着遍历而变化,因为当leftType==1是,说明该节点按照线索化处理后的有效节点
            while(node.getRightType()==0){
                node=node.getLeft();
            }

            //打印当前这个节点
            System.out.println(node);
            //如果当前节点的右指针指向的是后继节点,就一直输出
            while (node.getRightType()==1){
                //获取到当前节点的后继节点
                node=node.getRight();
                System.out.println(node);
            }
            //替换这个遍历的节点
            node=node.getRight();
        }
    }

有关二叉树(Java实现)的更多相关文章

  1. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

  2. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  3. java - 从 JRuby 调用 Java 类的问题 - 2

    我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www

  4. java - 我的模型类或其他类中应该有逻辑吗 - 2

    我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我

  5. java - 什么相当于 ruby​​ 的 rack 或 python 的 Java wsgi? - 2

    什么是ruby​​的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht

  6. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  7. Observability:从零开始创建 Java 微服务并监控它 (二) - 2

    这篇文章是继上一篇文章“Observability:从零开始创建Java微服务并监控它(一)”的续篇。在上一篇文章中,我们讲述了如何创建一个Javaweb应用,并使用Filebeat来收集应用所生成的日志。在今天的文章中,我来详述如何收集应用的指标,使用APM来监控应用并监督web服务的在线情况。源码可以在地址 https://github.com/liu-xiao-guo/java_observability 进行下载。摄入指标指标被视为可以随时更改的时间点值。当前请求的数量可以改变任何毫秒。你可能有1000个请求的峰值,然后一切都回到一个请求。这也意味着这些指标可能不准确,你还想提取最小/

  8. 【Java 面试合集】HashMap中为什么引入红黑树,而不是AVL树呢 - 2

    HashMap中为什么引入红黑树,而不是AVL树呢1.概述开始学习这个知识点之前我们需要知道,在JDK1.8以及之前,针对HashMap有什么不同。JDK1.7的时候,HashMap的底层实现是数组+链表JDK1.8的时候,HashMap的底层实现是数组+链表+红黑树我们要思考一个问题,为什么要从链表转为红黑树呢。首先先让我们了解下链表有什么不好???2.链表上述的截图其实就是链表的结构,我们来看下链表的增删改查的时间复杂度增:因为链表不是线性结构,所以每次添加的时候,只需要移动一个节点,所以可以理解为复杂度是N(1)删:算法时间复杂度跟增保持一致查:既然是非线性结构,所以查询某一个节点的时候

  9. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  10. MIMO-OFDM无线通信技术及MATLAB实现(1)无线信道:传播和衰落 - 2

     MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO

随机推荐