zoukankan      html  css  js  c++  java
  • 第九周总结

    20182334 2019-2020-1 《数据结构与面向对象程序设计》第九周学习总结

    教材学习内容总结

    第九周我学习到的内容有:

    • 树的遍历
    • 二叉树的实现
    • 决策树
    • 中序和先序构成后树
    • 堆排序
    • 二叉排序树

    树是非线性结构,其元素组织为一个结构层次。


    计算树高度的代码为:

    public int height(){
    		return height(root);
    	}
    	public int height(BinaryNode<T> root){
    		if(root==null)
    			return 0;
    		int L_height=height(root.left);
    		int R_height=height(root.right);
    		return (L_height>=R_height)?L_height+1:R_height+1;
    	}
    
    

    树的遍历

    • 先序遍历 :访问根,自左至右遍历子树。
    public void preOrder(Node root)
        { // 前序遍历
            if (root != null)
            {
                System.out.print(root.data + " ");
                preOrder(root.left);
                preOrder(root.right);
            }
        }
    
    • 中序遍历:遍历左子树,然后访问根,然后自左至右遍历余下的各个子树。
    public void inOrder(Node root)
        { // 中序遍历
            if (root != null)
            {
                inOrder(root.left);
                System.out.print(root.data + " ");
                inOrder(root.right);
            }
        }
    
    • 后序遍历:自左至右遍历各子树,然后访问根。
     public void postOrder(Node root)
        { // 后序遍历
            if (root != null)
            {
                postOrder(root.left);
                postOrder(root.right);
                System.out.print(root.data + " ");
            }
        }
    
    
    • 层序遍历:从树的顶层到底层,从左至右,访问树中每层的每个结点。
    public void levelOrder() {
        BiTNode<E> node =root;
        LinkedList<BiTNode<E>> list = new LinkedList<>();
        list.add(node);
        while(!list.isEmpty()) {
            node=list.poll();
            System.out.print(node.data);
            if(node.lchild!=null)
                list.offer(node.lchild);
            if(node.rchild!=null)
                list.offer(node.rchild);
        }
    }
    

    二叉树的实现

    以下是实现的代码:

        public void creat(Object[] objs){  
        datas=new ArrayList<bintree>();//将一个数组的值依次转换为Node节点       
        for(Object o:objs){          
        datas.add(new bintree(o));        }//第一个数为根节点      
        root=datas.get(0);//建立二叉树        
        for (int i = 0; i <objs.length/2; i++) {//左孩子           
        
        datas.get(i).left=datas.get(i*2+1);//右孩子
        if(i*2+2<datas.size()){//避免偶数的时候下标越界                
            datas.get(i).right=datas.get(i*2+2);      
            }    
        }   
    }
    
    
    

    决策树

    用例子来说明一个决策树:

    
    import javafoundations.*;
    import java.util.Scanner;
    
    public class asd
    {
        private LinkedBinaryTree<String> tree;
        public asd()
        {
            String e1 = "TA是个人";
    
            String e2 = "TA是男性,再猜一遍!";//N
    
            String e4 = "他很帅!!再猜一遍!!";//N
            String e8 = "怎么这都猜错?再来一次!!";//N
            String e16 = "我的天还错??!!行吧再给你一条,他较高,再猜一遍!";
            String e18 = "你大爷!TA是1823班的!再猜一遍!";
            String e20 = "服了,TA是1823最帅的那个,再猜一遍!";
            String e22 = "......拜托你好好想想,再猜一遍!";
            String e24 = "...再想想??再猜一遍!";
            String e26 = "..come on 拜托!再来!";
            String e28 = "712最帅的那个!再猜一遍";
            String e30 = "算了你别猜了,赶紧洗洗睡吧!";
    
            String e31 = "你大爷!终于猜出来了!还是值得表扬!";
            String e29 = "你能猜这么久也是没谁了。";
            String  e27 = "哥你终于对了";
            String e25 = "邹佳伟?? 就这麦?怎么可能有姬旭帅?还算你有眼光";
            String e23 = "没错,姬旭是最帅的,其他男人怎么能比得过呢???搞不懂你为啥选这么久,气死为父了!!";
            String e21 = "还行";
            String e19 = "算你还不错!";
            String e17 = "你可真不容易,终于对了!";
            String e9 = "有眼光!!姬旭天下第一帅!!";//Y
            String e5 = "!!!bingo!!!";//Y
            String e3 = "!!!bingo!!!";//Y
    
            LinkedBinaryTree<String> n2, n3, n4, n5, n8, n9,
                    n17,n16,n18,n19,n20,n21,n22,n23,n24,n25,
                    n26,n27,n28,n29,n30,n31;
    
            n31 = new LinkedBinaryTree<String>(e31);
            n30 = new LinkedBinaryTree<String>(e30);
            n29 = new LinkedBinaryTree<String>(e29);
            n28 = new LinkedBinaryTree<String>(e28,n30,n31);
            n27 = new LinkedBinaryTree<String>(e27);
            n26 = new LinkedBinaryTree<String>(e26,n28,n29);
            n25 = new LinkedBinaryTree<String>(e25);
            n24 = new LinkedBinaryTree<String>(e24,n26,n27);
            n23 = new LinkedBinaryTree<String>(e23);
            n22 = new LinkedBinaryTree<String>(e22,n24,n25);
            n21 = new LinkedBinaryTree<String>(e21);
            n20 = new LinkedBinaryTree<String>(e20,n22,n23);
            n19 = new LinkedBinaryTree<String>(e19);
            n18 = new LinkedBinaryTree<String>(e18,n20,n21);
            n16 = new LinkedBinaryTree<String>(e16,n18,n19);
            n17 = new LinkedBinaryTree<String>(e17);
            n8 = new LinkedBinaryTree<String>(e8,n16,n17);
            n9 = new LinkedBinaryTree<String>(e9);
            n4 = new LinkedBinaryTree<String>(e4, n8, n9);
            n5 = new LinkedBinaryTree<String>(e5);
            n2 = new LinkedBinaryTree<String>(e2, n4, n5);
            n3 = new LinkedBinaryTree<String>(e3);
            tree = new LinkedBinaryTree<String>(e1, n2, n3);
        }
        
        public void diagnose()
        {
            Scanner scan = new Scanner(System.in);
            LinkedBinaryTree<String> current = tree;
    
            System.out.println ("开始竞猜!!");
            while (current.size() > 1)
            {
                System.out.println (current.getRootElement());
                String a = scan.nextLine();
                if (a.equalsIgnoreCase("姬旭"))
                    current = current.getRight();
                else
                    current =  current.getLeft();
            }
    
            System.out.println (current.getRootElement());
        }
    }
    

    中序和先序构造后序

    public Node<E> CreatTree(E[] array){
            nodeList = new LinkedList<Node>();
            for (int i = 0 ; i < array.length ; i++ ){
                nodeList.add(new Node(array[i]));
            }
    
            for(int j = 0;j<(array.length/2-1);j++){
                nodeList.get(j).setLeft(nodeList.get(j*2+1));
                nodeList.get(j).setright(nodeList.get(j*2+2));
            }
    
            int index = array.length/2-1;
            nodeList.get(index).setLeft(nodeList.get(index*2+1));
            if(array.length%2 == 1){
                nodeList.get(index).setright(nodeList.get(index*2+2));
            }
            root = nodeList.get(0);
            return root;
        }
    

    堆排序

    public class Sort {
        public static void main(String[] args) {
            int[] nums = {16,7,3,20,17,8};
            headSort(nums);
            for (int num : nums) {
                System.out.print(num + " ");
            }
        }
    
        /**
         * 堆排序
         */
        public static void headSort(int[] list) {
            //构造初始堆,从第一个非叶子节点开始调整,左右孩子节点中较大的交换到父节点中
            for (int i = (list.length) / 2 - 1; i >= 0; i--) {
                headAdjust(list, list.length, i);
            }
            //排序,将最大的节点放在堆尾,然后从根节点重新调整
            for (int i = list.length - 1; i >= 1; i--) {
                int temp = list[0];
                list[0] = list[i];
                list[i] = temp;
                headAdjust(list, i, 0);
            }
        }
        
        private static void headAdjust(int[] list, int len, int i) {
            int k = i, temp = list[i], index = 2 * k + 1;
            while (index < len) {
                if (index + 1 < len) {
                    if (list[index] < list[index + 1]) {
                        index = index + 1;
                    }
                }
                if (list[index] > temp) {
                    list[k] = list[index];
                    k = index;
                    index = 2 * k + 1;
                } else {
                    break;
                }
            }
            list[k] = temp;
        }
    }
    
    

    堆排序有最大堆,最小堆。

    • 最大堆:非叶子节点比左右子节点要大
    • 最小堆: 非叶子节点比左右子节点要小

    为了使大家更明白,这里放一张图:


    二叉排序树

    二叉排序树构造起来很简单,但是在删除方法的部分比较困难,在思考算法的时候有很多问题,以下是整个排序树,里面包含查找、排序、插入、删除等各种方法。

    public class BinarySearchTree
    { // 二叉搜索树类
        private class Node
        { // 节点类
            int data; // 数据域
            Node right; // 右子树
            Node left; // 左子树
        }
    
        private Node root; // 树根节点
        public void insert(int key)
        {
            Node p = new Node(); //待插入的节点
             p.data =key;
    
            if(root==null)
            {
                root=p;
            }
            else
            {
                Node parent = new Node();
                Node current=root;
                while(true)
                {
                    parent=current;
                    if(key>current.data)
                    {
                        current=current.right; // 右子树
                        if(current==null)
                        {
                            parent.right=p;
                            return;
                        }
                    }
                    else
                    {
                        current=current.left; // 左子树
                        if(current==null)
                        {
                            parent.left=p;
                            return;
                        }
                    }
                }
            }
        }
        public void preOrder(Node root)
        { // 前序遍历
            if (root != null)
            {
                System.out.print(root.data + " ");
                preOrder(root.left);
                preOrder(root.right);
            }
        }
    
        public void inOrder(Node root)
        { // 中序遍历
            if (root != null)
            {
                inOrder(root.left);
                System.out.print(root.data + " ");
                inOrder(root.right);
            }
        }
    
        public void postOrder(Node root)
        { // 后序遍历
            if (root != null)
            {
                postOrder(root.left);
                postOrder(root.right);
                System.out.print(root.data + " ");
            }
        }
    
        public Node find(int key)
        { // 从树中按照关键值查找元素
            Node current = root;
            while (current.data != key)
            {
                if (key > current.data)
                    current = current.right;
                else
                    current = current.left;
                if (current == null)
                    return null;
            }
            return current;
        }
    
        public void show(Node node)
        {    //输出节点的数据域
            if(node!=null)
                System.out.println(node.data);
            else
                System.out.println("null");
        }
    
    
        private Node getSuccessor(Node delNode)    //寻找要删除节点的中序后继结点
        {
            Node successorParent=delNode;
            Node successor=delNode;
            Node current=delNode.right;
    
            //用来寻找后继结点
            while(current!=null)
            {
                successorParent=successor;
                successor=current;
                current=current.left;
            }
    
            //如果后继结点为要删除结点的右子树的左子,需要预先调整一下要删除结点的右子树
            if(successor!=delNode.right)
            {
                successorParent.left=successor.right;
                successor.right=delNode.right;
            }
            return successor;
        }
    
        public boolean delete(int key) // 删除结点
        {
            Node current = root;
            Node parent = new Node();
            boolean isRightChild = true;
            while (current.data != key)
            {
                parent = current;
                if (key > current.data)
                {
                    current = current.right;
                    isRightChild = true;
                }
                else
                {
                    current = current.left;
                    isRightChild = false;
                }
                if (current == null) return false; // 没有找到要删除的结点
            }
            // 此时current就是要删除的结点,parent为其父结点
            // 要删除结点为叶子结点
            if (current.right == null && current.left == null)
            {
                if (current == root)
                {
                    root = null; // 整棵树清空
                }
                else
                {
                    if (isRightChild)
                        parent.right = null;
                    else
                        parent.left = null;
                }
                return true;
            }
            //要删除结点有一个子结点
            else if(current.left==null)
            {
                if(current==root)
                    root=current.right;
                else if(isRightChild)
                    parent.right=current.right;
                else
                    parent.left=current.right;
                return true;
            }
            else if(current.right==null)
            {
                if(current==root)
                    root=current.left;
                else if(isRightChild)
                    parent.right=current.left;
                else
                    parent.left=current.left;
                return true;
            }
            //要删除结点有两个子结点
            else
            {
                Node successor=getSuccessor(current);    //找到要删除结点的后继结点
    
                if(current==root)
                    root=successor;
                else if(isRightChild)
                    parent.right=successor;
                else
                    parent.left=successor;
    
                successor.left=current.left;
                return true;
            }
        }
    

    教材学习中的问题和解决过程

    • 问题1:在设计决策树时,书上代码出现equalIgnoreCase,我不知道它和equal有什么区别?

    • 问题1解决方案:上api jdk 上查了一下,发现原来equal是要完全相等,但equalIgnoreCase是不考虑大小写的,其实本质上都是一样的,,只不过是不看大小写了,这点对于用户输入Y或者y时有很大的帮助,省略了很多代码,可能这是java的好处吧!

    • 问题2:不知道平衡二叉查找树有什么用,如果不用会有什么影响?

    • 问题2解决方案:为了解决这个问题,我上网看了看别人的博客,发现了一个讲解:二叉查找树与平衡二叉树

    那么什么是平衡二叉树呢?

    平衡二叉搜索树,又被称为AVL树,且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。

    由于普通的二叉查找树会容易失去”平衡“,极端情况下,二叉查找树会退化成线性的链表,导致插入和查找的复杂度下降到 O(n) ,所以,这也是平衡二叉树设计的初衷。那么平衡二叉树如何保持”平衡“呢?根据定义,有两个重点,一是左右两子树的高度差的绝对值不能超过1,二是左右两子树也是一颗平衡二叉树。

    如下图所示,左图是一棵平衡二叉树,根节点10,左右两子树的高度差是1,而右图,虽然根节点左右两子树高度差是0,但是右子树15的左右子树高度差为2,不符合定义,所以右图不是一棵平衡二叉树。

    • 问题3:堆排序有两种方法,一直搞不清两者的区别和各自的优势。

    • 问题3解决方案:还是上网找博客,看到一篇java堆排序

    堆排序分为最大堆和最小堆,所谓最大堆,就是非叶子结点比左右子节点要大,所谓 最小堆,就是非叶子节点比左右子节点要小。

    以构建最大堆为例,堆排序的过程:

    • 1、原始数组形成一个顺序堆。数组中下标索引为i的节点,左节点是i2 +1,右节点是i2+2

    • 2、初始化堆,从最后一个叶子节点的父节点开始一层层向上遍历,使得每一对父子节点中的最大节点上浮,维持最大堆的性质。

    如果有交换位置的操作,那么要以交换后的新子节点为父节点递归遍历,以维持该分支上的最大堆性质。直到遍历到根节点,此时根节点最大。

    • 3、排序阶段:将根节点与最后一个叶子节点交换位置,交换过位置的尾部叶子节点就是从小到大的排序,最后的叶子节点的索引相对应也减1。

    然后以根节点,维护最大堆性质,同样的,如果有交换位置的操作,那么要以被交换的子节点为父节点递归遍历,以维持该分支上的最大堆性质

    堆排序的核心点在于:如果有交换位置的操作,那么要以交换后的新子节点为父节点递归遍历,以维持该分支上的最大堆性质

    代码调试中的问题和解决过程

    • 问题1:如下图,这里的getRight方法不知道为什么不对,怎么调也不对。

    • 问题1解决方案:仔细看报错的原因,原来是因为它调用的方法里定义的变量不对:

    少了Linked,所以一直错,于是我到对应的类里去看,果然发现,getLeft和getRight两个方法定义的变量不同:

    改正问题之后,发现原来都是细节地方出错,实在是不应该。

    • 问题2:在通过中序和先序创造后序时,不知道怎么用代码实现。那棵树不知道怎么联合建起来。
        String preOrder[] = {"A","B","D","H","I","E","J","M","N","C","F","G","K","L"};
        String inOrder[] = {"H","D","I","B","E","M","J","N","A","F","C","K","G","L"};
    
    • 问题2解决方案:我仔细分析了下,首先应该看先序,找到先序中的根节点,然后通过根节点确定在中序的位置,从而找到整体的左子树和右子树,例如上面这两个序列,可以先找到root A ,再确定A在inOrder中的位置,立刻就可以看出A左边的序列都是左子树,右边的序列是右子树;然后传左子树到Creat方法中,再利用递归,一个一个叉的建立起来,最后形成后序。
      creat方法:
    public Node creat(String []preOrder,int pstart,int pend,String []inOrder ,int instart,int inend){
            if(pstart > pend || instart > inend){
                return null;
            }
            String rootData = preOrder[pstart];
            Node root = new Node(rootData);
            int rootIndex = findIndexInArray(inOrder,rootData,instart,inend);
            int xiabiao = rootIndex - instart - 1;
            //左
            Node left = creat(preOrder,pstart+1,pstart+xiabiao+1,inOrder,instart,instart+xiabiao);
            //右
            Node right = creat(preOrder,pstart + xiabiao + 2,pend,inOrder,rootIndex+1,inend);
            root.setLeft(left);
            root.setright(right);
            return root;
        }
    

    具体删除的过程是:
    当删除叶子节点时,直接让他的父节点指向null就行,
    当删除有一个子树的节点时,将其父节点指向其孩子节点。
    当删除有两个孩子的节点时,先进行中序排列,按照排列出来的结果,将被删除的节点两边即视为为可代替原节点的备选节点,可以选择两者中的一个。将要被删除的节点数据删除,然后将代替节点的数据填入,之后再删除代替节点,若代替节点出现三种情况之一,即循环往复,直到全部删除。

    具体的代码实现为:

    public boolean delete(int key) // 删除结点
        {
            Node current = root;
            Node parent = new Node();
            boolean isRightChild = true;
            while (current.data != key)
            {
                parent = current;
                if (key > current.data)
                {
                    current = current.right;
                    isRightChild = true;
                }
                else
                {
                    current = current.left;
                    isRightChild = false;
                }
                if (current == null) return false; // 没有找到要删除的结点
            }
            // 此时current就是要删除的结点,parent为其父结点
            // 要删除结点为叶子结点
            if (current.right == null && current.left == null)
            {
                if (current == root)
                {
                    root = null; // 整棵树清空
                }
                else
                {
                    if (isRightChild)
                        parent.right = null;
                    else
                        parent.left = null;
                }
                return true;
            }
            //要删除结点有一个子结点
            else if(current.left==null)
            {
                if(current==root)
                    root=current.right;
                else if(isRightChild)
                    parent.right=current.right;
                else
                    parent.left=current.right;
                return true;
            }
            else if(current.right==null)
            {
                if(current==root)
                    root=current.left;
                else if(isRightChild)
                    parent.right=current.left;
                else
                    parent.left=current.left;
                return true;
            }
            //要删除结点有两个子结点
            else
            {
                Node successor=getSuccessor(current);    //找到要删除结点的后继结点
    
                if(current==root)
                    root=successor;
                else if(isRightChild)
                    parent.right=successor;
                else
                    parent.left=successor;
    
                successor.left=current.left;
                return true;
            }
        }
    

    代码托管

    上周考试错题总结

    • [x] 第一题: It is possible to implement a stack and a queue in such a way that all operations take a constant amount of time.
    • A .true
    • B .false

    解析:堆栈和队列的理想实现具有所有操作,这些操作都需要一定的时间。
    故答案选 A

    • [x] 第二题:To maintain the completeness of the tree, there is only one valid element to replace the root, and that is the element stored in the first leaf in the tree.
    • A .true
    • B .false

    解析:为了保持树的完整性,只有一个有效的元素可以替换这个元素,那就是存储在树的最后一个叶子中的元素。
    故答案选 B

    点评过的同学博客和代码

    • 本周结对学习情况
      • 20182321

      • 结对照片

      • 结对学习内容

        • 共同完成实验操作
        • 共同解决书上出现的问题。
    • 上周博客互评情况

    其他(感悟、思考等)

    已经打到10000行了,还有一个月,争取再干5000行。在这一学期的学习中,基本把所有学习的时间都给了java,但是很充实,也很 痛苦,但并快乐着。

    学习进度条

    代码行数(新增/累积) 博客量(新增/累积) 学习时间(新增/累积) 重要成长
    目标 5000行 30篇 400小时
    第一周 212/212 2/2 17/17
    第二周 132/344 2/4 17/34
    第三周 689/1033 1/5 23/67
    第四周 664/1697 2/7 20/87
    第五周 586/2283 2/9 20/107
    第六周 500/2783 1/10 26/133
    第七周 2143 /4928 2/12 40/173
    第八周 2000 /6140 2/14 40/210
    第九周 4000 /10140 3/17 40/250
    • 计划学习时间:29小时

    • 实际学习时间:40小时

    • 改进情况:不妥协,死磕到底!

    参考资料

  • 相关阅读:
    jquery扩展鼠标mousewheel事件
    addEventListener和attachEvent介绍, 原生js和jquery的兼容性写法
    HTML,CSS,font-family:中文字体的英文名称
    最好的Java和Android开发IDE---IntelliJ IDEA使用技巧
    《Thinking in Java》习题——吸血鬼数字
    Java学习之——JavaBeans
    Android 开源库——侧滑菜单栏(SlidingMenu)的导入和使用
    Android 布局学习之——Layout(布局)详解二(常见布局和布局参数)
    Android 布局学习之——Layout(布局)详解一
    Android 布局学习之——LinearLayout的layout_weight属性
  • 原文地址:https://www.cnblogs.com/cistineup/p/11890635.html
Copyright © 2011-2022 走看看