Java面向对象:实验三(自用)

设计能存储char型数据的链式二叉树结构,并实现前/中/后序的递归遍历,前/中序非递归遍历。
目的:(1)理解和掌握如何逐字符读取char型数据;(2)理解并掌握“static方法可通过类名来调用”;(3)理解并掌握内部类的基本含义、构造和使用;(4)初步理解类的设计思想:何时需要设计类,类应具有哪些功能。


import java.util.Scanner;
class ReaderChar{//此类专门用于读取数据
	String creatBinTreeStr; //用于建树所需的字符串
	int pos; //当前读取的位置下标
	ReaderChar(String s){ creatBinTreeStr=s; }//即:构造对象时将建树数据全部传入
	char getChar(){
		char x=creatBinTreeStr.charAt(pos); //获取pos位置的字符
		pos++;
		return x;
	}
}
class BinTree{
	char data;
	BinTree  L,R;
	BinTree( char x ){ data=x; }//注:所有成员变量会有默认值:0/false/null
	//注意:建树不同于创建BinTree对象,建树需要输入一组数据,形成一棵树,而构造函数,只能创建一个节点(而非一棵树)
	BinTree creatBinTree(ReaderChar r){//r是建树数据的读取器,提供getChar()方法
		//另外,不要在creatBinTree()中创建ReaderChar对象,因为涉及递归调用,每次都会创建该对象,一方面浪费空间,更为重要的是,这些ReaderChar对象是不同的对象,每次都会从0开始读取数据
		//1、读取一个字符  x=getchar();
		char x=r.getChar();
		//2、判断:若x=='#' return NULL;
		if(x=='#') return null;
		//3、能走到此处,必然不是空树,故步骤为:造结点,并给该节点的三个域赋值
		        //即: t=新节点,t.data=x;  t.L=creatBinTree();  t.R=creatBinTree();
		BinTree t=new BinTree(x); t.L=creatBinTree(r);  t.R=creatBinTree(r);
		//4、return t;
		return t;
	}
	void pre(){//前序遍历:无参。t是谁?或者说:对谁进行前序遍历?
		//C版:void pre(t){ if(t==null)return ; printf(t.data); pre(t.L); pre(t.r); }--注意:有参
		//java版:pre(t)---> t.pre(); t是谁?t是根,是this,this绝对不可能为null,但this的孩子可能为空
		System.out.print(this.data+" ");//也可省略this
		if( this.L!=null ) this.L.pre();
		if( this.R!=null ) this.R.pre();
	}
	class Stack{//栈:内部类,实际上就是内部类型,一般不外用
		BinTree[] s=new BinTree[20]; //数组
		int top;  //栈顶指针
		boolean isEmpty(){//判空
			return top==0;
		}
		void push(BinTree x){//入栈
			s[top]=x; top++;
		}
		BinTree pop(){//出栈
			top--;  return s[top];
		}
	}
	void preN(){//非递归前序遍历算法
		//1、Stack s=new Stack(); 需要一个栈;
		Stack s=new Stack();
		//2、while(t不空||栈不空){  ---要解决:t的初值是谁?是根,即this
		BinTree t=this;
		while(t!=null || s.isEmpty()==false)
		           //if( t不空 ){ 访问t; push(t); t=t.L; } //t=t.L应理解成访问左子树
		           if(t!=null){ System.out.print(t.data+" ");  s.push(t);  t=t.L; }
		           //else{ t=pop(s); t=t.r;  }//从栈中弹【出】一个元素,获取该元素的右子树
		           else {t=s.pop();  t=t.R; }
		//}
	}
	void in(){//中序遍历
		if( this.L!=null ) L.in();
		System.out.print(data+" ");//也可省略this
		if( this.R!=null ) R.in();
	}
	void post(){//后序遍历
		if( this.L!=null ) L.post();
		if( this.R!=null ) R.post();
		System.out.print(data+" ");//也可省略this
	}
}
class App{
	public static void main(String[] x){
		BinTree t=new BinTree('#');
		System.out.print("请输入二叉树建树字符,#表示null:\n");
		Scanner sc=new Scanner(System.in);
		String s=sc.nextLine(); //读一行,直至回车结束(注:读取的字符串中不包含回车)
		ReaderChar r=new ReaderChar(s); 
		t=t.creatBinTree(r);
		System.out.print("\n pre="); t.pre();
		System.out.print("\npreN="); t.preN();
		System.out.print("\n in ="); t.in();
		System.out.print("\npost="); t.post();
	}
}
//AB##C##
//ABD###CE##F##
/*总结:1、二叉树,关键在建树,建树的关键在于获取getChar(),策略为:
	           用一个能提供getChar()操作的对象--》
	           ===》需要一个类,该类中既有getchar(),又有建树数据;
	              ===》为确保每次getChar()获取不同的值,还必须有个pos,以便记录每次读取的位置,并在读取字符后进行偏移
	      2、递归遍历操作 pre(t)-->t.pre();在pre()中如何知晓调用它的是t还是别的s?无法知晓。这些t或s有个统一的名称,叫this。另外,this不可能为空,但this.L/R可能为空
	      3、前序遍历的非递归:
	         (1)内部类--栈,因为该栈的数组元素类型只能是BinTree(why?),或者说该类仅供BinTree使用,故干脆将其放在BinTree的内部(这样也可避免外部使用);
	         (2)包裹内部类的类称作囿类(包围类)。内部类实际上是囿类的一个成员。换言之,内部类可以直接定义成员变量,也可直接用在成员方法中定义变量、使用内部类的方法,如preN()中,Stack s; s.pop()
	         
	   作业:
	   1、根据非递归前序遍历,编写中序非递归遍历;
         2、如果不ReaderChar类,程序应该怎么改?
         3、把栈放在二叉树类的外面应该怎么改?
	   4、根据二叉树,编写m度树的创建,以及前/后序递归遍历、层次遍历 --- 完成此题,即被认为是【优秀】
	*/


class Circle
{
	 double radius;
	 final double PI=3.14;
	 Circle()
	   {radius=0; }
	 Circle(double r)
	   {radius=r;}
	double getRadius()
	   {return radius;}
	double getPerimeter()
	   {return 2*PI*radius;}
	double getArea()
	   {return PI*radius*radius;}       	
	 void disp()
       {System.out.println(radius);
	    System.out.println(this.getPerimeter());
	    System.out.println(this.getArea());
	    }
}
 
class Cylinder extends Circle
{     double height;
	 Cylinder(double r,double h)
	   {super(r);
	   height=h;}
	 double getHeight()
	 {return height;}
	 double getVol()
	 {return PI*radius*radius*height;}
	 void disVol()
	 {//System.out.println(super.getArea());
	 	System.out.println(this.getVol());}
}
class Ci_Cy
{public static void main(String[] args)
   {Circle c1=new Circle(2.0);
    Cylinder cy1=new Cylinder(2.0,3.0);
    c1.disp();
    cy1.disVol();
   	}
}

class SanJiao{
	private double a,b,c;
	public  SanJiao(double x,double y,double z){
		a=x; b=y; c=z;
		//this.set(x,y,z);
	}
	public SanJiao(double x, double y){
		this(x,x,y);
	}
	public SanJiao(double x){
		this(x,x,x);//等边三角	
		}
	 
	public double heronArea(){
		double p=(a+b+c)/2;
		return Math.sqrt(p*(p-a)*(p-b)*(p-c));
	}
	public void set(int a,int b,int c )
	{
		this.a=a;
		this.b=b;
		this.c=c;
	}
	public String toString()
	{
		return "三角形的三边为:"+this.a+","+this.b+","+this.c;
	}
	public void showInfo(){
		System.out.println(this);	}   	 
	 
}
class Rt extends SanJiao{
	public Rt(int a,int b)		
	{
		super(a,b,Math.sqrt(a*a+b*b));
	} 
	public Rt(int a,int b,int c)
	{
		super(a,b,c);
	}
}
class App{
	public static void main(String[] args){
	Rt r=new Rt(3,4);
	SanJiao s=new SanJiao(3,2,4);
	r.showInfo();
	}
	
}

/*3、设计带头结点的单链表LinkedList。该表存储和处理int型数据,包括用尾插法创建链表、输出表中全部数据、查找值为x的结点,删除值为x的结点作。
【目的】掌握this不得不用的场合,理解和掌握链式结构的定义、构造和使用。
C的链表构造
typedef  struct  kk{
    int data;
    struct  kk  *next;
}Node;

*/
import java.util.Scanner;
class LinkedList{//带头结点的单链表
    int data;
    LinkedList next;
    LinkedList(int x){ data=x; next=null; }
    void append(){
 		System.out.print("请输入一组数,以-1结束");//输入需要用到Scanner类
		//Scanner类的用法:1、import导入;2、创建对象 new Scanner(System.in); 3、读取数据 nextInt();
		Scanner sc=new Scanner(System.in); 
		int x=sc.nextInt(); //先读一个int型数据
		LinkedList tail, p; //其中p表示新产生的元素,tail表示链表的表尾
		tail=this; //初始时,tail=自己;
		//注意:在main中调用方式:s.append(),这里的this就是s
		while( x!=-1 ){ 
			//造结点、给结点赋值、将结点链入队列、修改表尾、继续读
			//p=new LinkedList(); p.data=x; p.next=null; //注意:java中的空指针要小些 ---无构造函数
			p=new LinkedList(x);//有构造函数可以这样写
			tail.next=p; tail=p;
			System.out.print(p.data+" ");
			x=sc.nextInt(); 
		} 
   }
   void show(){
      //for(LinkedList p=head->next; p!=null; p=p->next)打印p.data;
      for(LinkedList p=this.next; p!=null; p=p.next)
      	  System.out.print(p.data+" ");
   }
}
class App{
     public static void main(String[] x){
        //LinkedList s=new LinkedList(); //针对无构造函数的情形
        LinkedList s=new LinkedList(0); //针对有构造函数的情形
        s.append();   s.show();
     }
}
/*总结:1、this不得不用的场合,tail=this;即单链表中,表达【表头】自己
	        即:调用s.append()是,append()中tail=this; 中的this就是s;
	              调用t.append()是,append()中tail=this; 中的this就是t;
	      2、构造函数的作用:不是构造(构造,即分配空间,由new实现,空间大小由构造函数名决定),而是初始化。
	*/






posted @ 2020-11-02 17:10  Zeker62  阅读(55)  评论(0编辑  收藏  举报