数据结构(中)

非线性数据结构

非线性数据结构主要在数据结构里面包括树和图(有向图,无向图)
概念:度,树里面度就是子节点的个数,但是图分为入度和出度,入度就是能够通往该节点的线路的个数,出度就是该节点通往其他节点的路径的条数。

对于一般的树的存储方式:

  1. 孩子兄弟存储法 ,通过一棵树的同级的兄弟用右子树来表示,通过左子树来表示孩子,从而构成一棵二叉树;
  2. 孩子双亲表示法通过找到双亲的孩子,以及孩子的双亲来找到二叉树;

树的遍历

对于树的遍历,这里一般又三种遍历方法:
1.层次遍历(相当于图里面的DBS)通过先遍历第一层,再遍历第二层,最后遍历第三层的方法来实现层次遍历。
2.先序遍历(相当于图里面的DFS)通过先遍历根节点,在遍历根节点的左子树,一直往右进行遍历来实现先序遍历,同样的相当于在二叉树里面的先序遍历。
3.后序遍历:对于一般树的后序遍历,一般来说相当于二叉树的中序遍历,先将其他的子树遍历完成之后,再来遍历根节点

二叉树

首先二叉树的各种操作可以参考以下代码:

import java.util.Scanner;
import java.util.Stack;
import  java.io.*;

class Data
{
    int times=0;
 char b;
}
class bnode{
    bnode left,right;
    //创建两个指针变量
    Data data=new Data();
}
//注意:该实验实现二叉树的建立,二叉树的中序遍历先序遍历后序遍历以及一些其他的操作(注意遍历要递归和非递归)
public class BiTree {
    static int number=0;


    public static bnode creattree() throws IOException {
        //创建一颗二叉树,采取先序遍历的方式
//以下是三种输入一个字符的方式:
//方法一
      //  System.out.println("请输:");
//        Scanner scanner=new Scanner(System.in);
//        String s=scanner.next();
//        char m=s.charAt(0);
        //方法二
//        BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
//        int a=br.read();
//        方法三
       char m= (char) System.in.read();
      //  System.out.println(m);
       if(m!='#'){
           bnode root=new bnode();
          // if(root.data!=null)
           root.data.b=m;//把数据存入
           number++;
           root.left=creattree();
           root.right=creattree();
           return root;
       }
       else return null;
    }
//遍历二叉树操作(递归操作)
    public  void recursion1(bnode root)
    {
        if(root==null)return ;
        //完成递归操作(先序)

        /*操作写在这里
        *
        * */
        recursion1(root.left);
        recursion1(root.right);
    }
    public void recurtion2(bnode root)
    {
        //这里写中序遍历
        if(root==null)return;
        recurtion2(root.left);
        /*
        * 这里写操作*/
        recurtion2(root.right);
    }
    public void recurtion3(bnode root)
    {
        //这里写后序遍历
        if(root==null)return ;
        recurtion3(root.left);
        recurtion3(root.right);
        /*
        * 这里写后序遍历的操作*/
    }
    //遍历二叉树操作(非递归)
    public void no_recurtion1(bnode root)
    {
        //实现先序遍历
        bnode p=root;
        Stack<bnode> a = new Stack<>();//建立一个栈结构为了存节点
        if(root==null)return;
a.push(p);
 while (!a.empty())
    {
        //栈不为空就可以进行操作了
        p=a.pop();
        /*这里写操作*/
        if(p.right!=null)a.push(p.right);
        if(p.left!=null)a.push(p.left);//后入栈先出来

    }

    }
    public void no_recurtion2(bnode root)
    {if(root==null)return;
        //实现中序遍历
        bnode p=root;
        Stack<bnode> a=new Stack<>();
        a.push(p);


            while(!a.empty())
            {
                p=a.pop();
                p.data.times++;//访问次数添加一次
              if(p.right!=null)  a.push(p.right);
               if(p.data.times==1) a.push(p);
             else   if(p.data.times==2){/*
                * 这里写操作*/
             p.data.times=0;continue;}
              if(p.left!=null)  a.push(p.left);
            }

    }
    public void no_recurtion3(bnode root)
    {if(root==null)return;
        //实现后序遍历
        bnode p=root ;
        Stack<bnode>a =new Stack<>();
        a.push(p);
      while(!a.empty())
      {
          //栈不为空
          p=a.pop();
          p.data.times++;
          if(p.data.times==2)continue;
          if(p.data.times==3) {

          /*
          * 这里写相应的操作*/
          p.data.times=0;}
          else {
              a.push(p);
              if (p.right != null) a.push(p.right);
              a.push(p);
              if (p.left != null) a.push(p.left);
          }
          }
      }


//采用非递归实现求节点个数,数值最大值,最小值,以及度为一二的点,以及叶子节点
    public int two(bnode root) {
        //实现先序遍历,叶子结点
        int num = 0;
        bnode p = root;
        Stack<bnode> a = new Stack<>();//建立一个栈结构为了存节点
        if (root == null) return 0;
        a.push(p);
        while (!a.empty()) {
            //栈不为空就可以进行操作了
            p = a.pop();
            /*这里写操作 */
            if (p.right != null && p.left != null) num++;
            if (p.right != null) a.push(p.right);
            if (p.left != null) a.push(p.left);//后入栈先出来

        }
        return num;
    }

    public int zero(bnode root) {
        //实现先序遍历,度为二的点
        int num = 0;
        bnode p = root;
        Stack<bnode> a = new Stack<>();//建立一个栈结构为了存节点
        if (root == null) return 0;
        a.push(p);
        while (!a.empty()) {
            //栈不为空就可以进行操作了
            p = a.pop();
            /*这里写操作 */
            if (p.right == null && p.left == null) num++;
            if (p.right != null) a.push(p.right);
            if (p.left != null) a.push(p.left);//后入栈先出来

        }
        return num;
    }

    public int one(bnode root) {
        //实现先序遍历,度为一的点
        int num = 0;
        bnode p = root;
        Stack<bnode> a = new Stack<>();//建立一个栈结构为了存节点
        if (root == null) return 0;
        a.push(p);
        while (!a.empty()) {
            //栈不为空就可以进行操作了
            p = a.pop();
            /*这里写操作 */
            if ((p.right != null || p.left != null)&&!(p.right != null && p.left != null)) num++;
            if (p.right != null) a.push(p.right);
            if (p.left != null) a.push(p.left);//后入栈先出来

        }
        return num;
    }

    public char max(bnode root) {
        //实现先序遍历

        bnode p = root;
        Stack<bnode> a = new Stack<>();//建立一个栈结构为了存节点
        if (root == null) return 0;
        a.push(p);
        char max = root.data.b;
        while (!a.empty()) {
            //栈不为空就可以进行操作了
            p = a.pop();
            /*这里写操作 */
        max=(char) Math.max(max,p.data.b);
            if (p.right != null) a.push(p.right);
            if (p.left != null) a.push(p.left);//后入栈先出来

        }
        return max;
    }

    public char min(bnode root) {
        //实现先序遍历

        bnode p = root;
        Stack<bnode> a = new Stack<>();//建立一个栈结构为了存节点
        if (root == null) return 0;
        a.push(p);
        char min = root.data.b;
        while (!a.empty()) {
            //栈不为空就可以进行操作了
            p = a.pop();
            /*这里写操作 */
            min=(char) Math.min(min,p.data.b);
            if (p.right != null) a.push(p.right);
            if (p.left != null) a.push(p.left);//后入栈先出来

        }
        return min;
    }

  public  int Getnum(bnode root)
    {
//求节点个数
//        if(root==null)return 0;
//        //完成递归操作(先序)
//
//        /*操作写在这里
//         *
//         * */
//        number++;
//        Getnum(this.root.left);
//        Getnum(this.root.right);
//        return number;
        return number;//注意如果
    }


//    public int hashCode() {
//
//    }

 public int Get_depth(bnode root)
    {
//求这棵树深度
        //这里写中序遍历
        if(root==null)return 0;
      int num1=Get_depth(root.left);
      int num2=Get_depth(root.right);
      return num1>num2?num1+1:num2+1;//看这两个树的深度,返回大的那个
   }


  public   int Get_sub_depth(char a,bnode root) {
      //求子树深度(先序遍历)
      if (root == null) return -1;
      //实现后序遍历
      bnode p = root;
      Stack<bnode> b = new Stack<>();
      b.push(p);
      while (!b.empty()) {
          //栈不为空
          p = b.pop();
          p.data.times++;
          if (p.data.times == 2) continue;
          if (p.data.times == 3) {
              if (a == p.data.b) return Get_depth(p);
              /*
               * 这里写相应的操作*/
              p.data.times = 0;
          } else {
              b.push(p);
              if (p.right != null) b.push(p.right);
              b.push(p);
              if (p.left != null) b.push(p.left);
          }
      }
      return -1;//没有这个子树的情况
  }
      public static void main (String[]args) throws IOException {
        BiTree biTree=new BiTree();//主函数要是想调用类里面的非静态方法就要创建一个对象,这样才能只用该对象的非静态方法
          System.out.println("请输入你的二叉树:");
          bnode root=biTree.creattree();
          int height=biTree.Get_depth(root);
          System.out.println("这棵二叉树的深度为:"+height);
          System.out.println("这棵二叉树的节点个数为:"+biTree.Getnum(root));
          System.out.println("这棵树的度为零一二的点分别为:"+biTree.zero(root)+","+biTree.one(root)+","+biTree.two(root));
          System.out.println("这棵树的数据最大值和数据最小值为:"+biTree.max(root)+","+biTree.min(root));
    }


  }

对于二叉树的操作有很多比如创建一棵二叉树,创建的时候就要用先序遍历创建二叉树,因为要现有一个根节点。遍历一棵二叉树,对于二叉树的遍历操作,有:

先序遍历

先序遍历是先找到根节点,首先遍历根节点,然后找到左子树,对左子树进行遍历,同样也是先遍历左子树的根节点,然后遍历完做指数之后,再对右子树进行同样的操作,先遍历右子树的根节点,然后再遍历右子树的左子树,以此类推。

中序遍历

后序遍历

线索二叉树

线索二叉树即通过不需要的指针域存储上一个节点的有关信息,从而能更好的遍历二叉树,这样的二叉树的实现可以用一个ltag以及rtag实现判断这两个指针到底是指向孩子节点还是父亲节点。

树变为二叉树

树变为二叉树之后,二叉树的左指针域为空的节点,对应原来的树里面两个指针域都为空的节点。(右指针域存储的都是兄弟节点,左指针域存的都是孩子节点)

二叉树里面的一些结论

满二叉树的节点个数:2k-1
满二叉树的第k层的节点个数:2k-1
完全二叉树的有多少层:[logn]-1
二叉树度为二的点的个数于度为零的点的个数的关系:n0=n2+1
二叉树孩子节点的个数:n-1

哈夫曼树

哈夫曼树也成为最优二叉树,形成过程是:
先找到两个权值最小的点,两个点合成一个节点。
再从剩下的点里面找到两个最小的点,在合成一个点。
以此类推,直到最后一个点,形成一棵最后二叉树即可。
关于Huffman编码,可以定义向右走就是一向左走就是零,如果需要找到一个叶子节点,可以找到他的路径,然后得到一串固定的Huffman编码。
如下是Huffman树的构建以及操作:

import java.util.Scanner;

public class Huffman {
    //Huffman编码

    //分配五十一个节点的空间,用来存储数据
   static node []node=new node[53];
   public void initnode()
   {
       for(int i=0;i<53;i++)
       {
           node[i]=new node();
       }
   }
    Huffman()
    {
        initnode();
        init();
    }
    public void init()
    {
        putw(node);//付给权值
        for(int i=0;i<26;i++)
        {
           //生成二十六次新节点
            Getmin(node);
        }
    }
public void putw(node[]a)
{
    int []w={186,64,13,22,32,103,21,15,47,57,4,5,32,20,57,63,15,3,48,51,80,23,8,18,2,16,1};
    char []data={' ','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
    //给他们权值
    for(int i=0;i<w.length;i++)
    {
      a[i].data=data[i];
      a[i].w=w[i];
    }
}
public void Getmin(node[]a)
{
    //将两个最小的合成一个节点
    int min = 0;//最小的权值
    int t=0,inmin=0;//所在位置
    int flag=0;
    for(int i=0;i<a.length;i++)
    {
        if(a[i].w==0){t=i;break;}//节点为空
else{
    if(a[i].parent == -1){
        //先赋值,注意如果没有比第一个小的(第一个的父亲节点已经赋值,那就不能在用第一个节点开始了)
if(flag==0){min=a[i].w;flag++;inmin=i;}
       //第一个节点没有赋父亲节点的值
    if(a[i].w<min){
        min=a[i].w;
        inmin=i;
    }
        }
}
    }
a[inmin].parent=t;
    a[t].left=inmin;
        min=a[0].w;
        inmin=0;//重新付给初始值
        for(int j=0;j<t;j++)
        {


                if(a[j].parent == -1){
                    if(flag==1){min=a[j].w;flag++;inmin=j;}
                    if(a[j].w<min){
                        min=a[j].w;
                        inmin=j;
                    }

                }



        //糖

    }
        a[inmin].parent=t;
        a[t].ringht=inmin;
        a[t].w=a[a[t].getRinght()].w+a[a[t].getLeft()].w;//给父亲节点权值
//给第t个节点赋值

    }



public String Huff(char i)
{
    int num=0;
    String A=" ";
    for (int j = 0; j <27 ; j++) {
        //遍历有字母的节点
        if(i==node[j].data){A=Huff(j);break;}
    }
    StringBuilder a=new StringBuilder(A);
    a.reverse();
    return a.toString();
}
public String Huff(int i)
{
    String a = " ";
    //第i个字符的huffman编码
    int p;
    while (node[i].getParent()!=-1)
   {
       p=node[i].parent;//在循环里面要不断更新父亲节点的值
       if(node[p].left==i)a+='0';
    else a+='1';
   i=node[i].getParent();
   }
    return a;
}

public String  Hufftoword(String huff)
{
    //put in huffman change it into a words
    String word=new String();
    int number;
    String HUF[]=huff.split(" ");
    for(int j=0;j<HUF.length;j++)
    {number =52;
    for(int i=0;i<HUF[j].length();i++)
    {
        //以左右子树为空为结束标志
        if(HUF[j].charAt(i)=='0'){
            //左走,向左子树
         number=   this.node[number].getLeft();
        }
        else if(HUF[j].charAt(i)=='1') {
            number =this.node[number].getRinght();
        }
        if(this.node[number].getLeft()==-1 ){
            word+=String.valueOf(this.node [number].data);
            break;
        }}
    }
   return word;

}

public String WordtoHuff(String word)
{
    String huff=new String();
    char []data={' ','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
    for(int i = 0; i<word.length(); i++)
    {

        for(int j=0;j<=this.word.length;j++)
        {
            if(data[j]==word.charAt(i)){huff+=this.word[j];break;}
        }
    }
    return huff;
}

    String word[]=new String[27];//创建二十七个字符对象的huffman编码
    public static void main(String[] args) {
        Huffman huffman=new Huffman();//创建一个Huffman对象
        System.out.println("零代表左走,一代表右走!");
        char []data={' ','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z'};
     //  for(int i=0;i<53;i++)
       //{
        //   System.out.println(i+" "+huffman.node[i].data+"  "  +huffman.node[i].w+" parent:"+huffman.node[i].getParent()+" left:"+huffman.node[i].getLeft()+" right:"+huffman.node[i].getRinght());
       //}


        for(int i=0;i<27;i++)
        {
            huffman.word[i]=huffman.Huff(data[i]);//存入关于哈夫曼的数据
            System.out.println(data[i]+":"+huffman.Huff(data[i]));
        }
        System.out.println("   请输入您的一串字符转为字符串:");
        Scanner scanner = new Scanner(System.in);
        String h=scanner.nextLine();
        System.out.println("转为huffman编码之后得到:");
        String huf=huffman.WordtoHuff(h);
        System.out.println(huf);
        System.out.println("再次转为字符串得到:");
        System.out.println(huffman.Hufftoword(huf));

    }
}
class  node{
   int left=-1,ringht=-1,parent=-1;
   char data=' ';
    int w;//存放权值

    public int getLeft() {
        return left;
    }

    public int getParent() {
        return parent;
    }

    public int getRinght() {
        return ringht;
    }
    }

概念:图里面包括很多概念
邻接点:两个点被一条线相连,称这两个点是一对邻接点。
强连通图,强连通分量这两个概念是基于有向图的。

邻接图和邻接矩阵
邻接图:

邻接图是一种用静态链表表示n个节点的方式,用邻接图可以实现对于一个图的存储,对于每个图,邻接图不唯一,但是只要确定邻接图之后,他的DFS以及DBS都是固定的了。

邻接矩阵:

邻接矩阵是一种表示与某个节点是否有邻接关系的矩阵,可以用邻接矩阵的值表示权值,也可以用邻接矩阵的值表示是否有连接(比如,如果有连接可以用1来表示)。
对于不同的图有不同的表示方式,比如对于有向图,可以用列矩阵来表示起点,行矩阵来表示终点,对于无向图,邻接矩阵就是对称矩阵。
无向图节点的度为:与该节点相关联边的数目
有向图节点的出度为:由该节点发出的边的数目
有向图的节点的入度为:以该节点为终点的边的数目

生成树

生成树有n个节点,
有n-1条边,
在生成树中加一条边,必成为回路,
注意生成树不唯(广度优先生成树,深度优先生成树)
注意,生成树是基于原来的图,如果是非连通图,那就是生成森林。

最小生成树

如何得到带权的图里的最小生成树呢?
利用两个集合的最小生成树一定包含树两个集合连通的最小边
1.首先找出最小边,然后将最小边看为一个集合,继续找下一个最小边
2.以此类推,直到最后将所有的节点都找完。

关键路径

关键路径即路径长度最长的路径,对于关键路径还有最早发生时间和最晚发生时间的概念。
这里的最早发生时间当然好理解,最早发生时间就是到达这个路径最早开始的时间,最晚发生时间就是这个路径开始的最晚时间(最晚时间是相对于关键路径而言的)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值