第九周总结

时间:2019-11-19
本文章向大家介绍第九周总结,主要包括第九周总结使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

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

代码托管

上周考试错题总结

  • 第一题: 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

  • 第二题: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小时

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

参考资料

原文地址:https://www.cnblogs.com/cistineup/p/11890635.html