算法与数据结构 树

树和图的区别

树其实就是不包含回路的连通无向图。或者说,只要是没有回路的连通无向图就是树。。
1.一棵树中的任意两个节点有且仅有唯一的一条路径连通
2.一棵树如果有n个节点,那么它一定恰好有n-1条边
3.在一棵树中加一条边疆或构成一个回路

什么是二叉树?

:二叉树的特点是每个节点最多有两个儿子,或每个节点最多有两棵子树。
(1)满二叉树:如果二叉树中每个内部节点都有两个儿子,这样的二叉树叫做满二叉树。或者说满二叉树所有的叶节点都有两个儿子。
严格定义:一棵树深度为h,且有2(h)-1个节点的二叉树。
(2)完全二叉树:如果一个二叉树除了最右边的位置上有一个或者几个叶节点缺少之外,其他是丰满的,那么这棵树叫做完全二叉树。
严格定义:设该二叉树高度为h,除第h 层之外,其他各层的节点数都达到最大数。

1.树在优先队列中的应用,堆的实现

堆是什么?

堆是一种特殊的完全二叉树。

(1)最小堆

所有父节点都比子节点小

(2)最大堆

所有的父节点都比子节点小

堆的作用

(1)排序,

//建立最大堆排序
import java.util.Scanner;

public class zuidadui {
    static int num;
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int n = scan.nextInt();
        int [] h = new int[n+1];
        for (int i = 1; i <= n; i++) {
            h[i] = scan.nextInt();
        }
        num = n;

        create(h);

        heapSort(h);

        for (int i = 1; i <= n; i++) {
            System.out.print(h[i]+" ");
        }
    }

    public static void swap(int t,int i,int [] h){
        int temp = h[i];
        h[i] = h[t];
        h[t] = temp;
    }

    //建立最大堆
    public static void siftdown(int i,int [] h){//传入一个需要向下调整的节点编号i,这里传入1,即从堆的顶点开始调整
        int t,flag=0;//flag用来标记是否需要继续向下调整
        while (i*2 <= num && flag == 0){
            if (h[i] < h[i*2])//先比较左节点
                t = i*2;
            else
                t = i;
            if (i*2+1 <= num){//是否有右节点
                if (h[t] < h[i*2+1])
                    t = i*2+1;
            }
            if (t!=i){//如果发现最大的节点的编号不是自己,说明子节点中有比父节点更大的
                swap(t,i,h);
                i = t;
            }
            else flag = 1;
        }
    }

    public static void create(int [] h){
        //从最后一个非叶节点到第一个节点依次向上进行调整
        for (int i = num/2; i >= 1; i--) {
            siftdown(i,h);
        }
    }

    public static void heapSort(int [] h){
        while (num > 1){
            swap(1,num,h);
            num--;
            siftdown(1,h);
        }
    }
}

//建立最小堆排序
import java.util.Scanner;

public class zuixiaodui {
    static int num; //堆的大小
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int n = scan.nextInt();
        int [] h = new int[n+1];
        for (int i = 1; i <= n; i++) {
            h[i] = scan.nextInt();
        }
        num = n;

        create(h);

        for (int i = 1; i <= n; i++) {
            System.out.print(deleteMax(h)+" ");
        }

        
    }


    /**
     * 从空堆开始,然后依次往堆中插入每一个元素
     * public static void create(int [] h){
     *         Scanner scan = new Scanner(System.in);
     *              num = 0;
     *              for (int i = 1; i <= num; i++) {
     *                  num++;
     *                  h[num] = scan.nextInt(); //或传入另一个数组
     *                  siftup(i,h);
     *              }
     *     }*/
    
    /**
     * 建立堆的方法*/
    public static void create(int [] h){
        //从最后一个非叶节点到第一个节点依次向上进行调整
        for (int i = num/2; i >= 1; i--) {
            siftdown(i,h);
        }
    }

    public static int deleteMax(int [] h){
        int t = h[1];
        h[1] = h[num];
        num--;
        siftdown(1,h);
        return t;
    }

    public static void siftdown(int i,int [] h){//传入一个需要向下调整的节点编号i,这里传入1,即从堆的顶点开始调整
        int t,flag=0;//flag用来标记是否需要继续向下调整
        while (i*2 <= num && flag == 0){
            if (h[i] > h[i*2])//先比较左节点
                t = i*2;
            else
                t = i;
            if (i*2+1 <= num){//是否有右节点
                if (h[t] > h[i*2+1])
                    t = i*2+1;
            }
            if (t!=i){//如果发现最小的节点的编号不是自己,说明子节点中有比父节点更小的
                swap(t,i,h);
                i = t;
            }
            else flag = 1;
        }
    }

    /**
     * 新增一个值*/
    public static void siftup(int i,int [] h){//传入一个需要向上调整的节点编号i
        int flag = 0;
        if (i == 1) return;  //

        while (i != 1 && flag == 0){
            if (h[i] < h[i/2])
                swap(i,i/2,h);
            else
                flag = 1;
            i = i/2;//更新编号i为它父节点的编号,从而便于下一次继续向上调整
        }
    }

    public static void swap(int t,int i,int [] h){
        int temp = h[i];
        h[i] = h[t];
        h[t] = temp;
    }
}

2.树的应用,并查集

并查集也称不相交集数据结构。

import java.util.Scanner;

public class bingchaji {
    static int [] f;
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        String s = "asdfas";
        int x,y,sum=0;
        int n = scan.nextInt();
        int m = scan.nextInt();
        f = new int[n+1];
        init(n);
        for (int i = 1; i <= m; i++) {
            x = scan.nextInt();
            y = scan.nextInt();
            merge2(x,y);
        }


        for (int i = 1; i <= n; i++) {
            if (f[i] == i)
                sum++;
        }

        for (int i = 1; i <= n; i++) {
            System.out.print(f[i]+" ");
        }

        System.out.print(sum);
    }

    //初始化
    private static void init(int n) {
        for (int i = 1; i <= n; i++) {
            f[i] = i;
        }
    }

    //合并两子集的函数
    private static void merge(int v, int u) {
        int t1 = getf(v);
        int t2 = getf(u);
        if (t1 != t2){
            //靠左原则,左边变成右边的子集,即把右边的集合,作为左边集合的子集和
            f[t2] = t1;
        }
    }

    public static void merge1(int v,int u){
        f[getf1(u)] = getf1(v);//靠左原则
    }

    public static void merge2(int v,int u){
        f[zip(u)] = zip(v);//靠左原则 ,使v为u的为前驱节点
    }

    //找爹的函数,不停的去找爹,直到找到祖宗为止,其实就是找最高领导人
    private static int getf(int v){
        if (f[v] == v)//如果它的前驱节点是它自己
            return v;
        else {//如果它的前驱节点不是它自己
            f[v]=getf(f[v]); //访问它的前驱节点,一直找到前驱节点为该前驱节点自己的点
            return f[v];
        }
    }

    private static int getf1(int v){
        while (f[v] != v)//如果该点的前驱节点不是自己,直到找到为止
            v = f[v];
        return v;
    }

    public static int zip(int v){
        if (f[v] != v)//如果它的前驱节点不是他自己
            f[v] = getf1(f[v]);//将它的前驱节点设置为(前驱节点为自己的点)的点,直接跟随根节点
        return v;
    }
}


最小生成树(这里只讨论无向图的最小生成树)

(1)Kruskal算法:先将所有的边的权值大小从大到小排序,加入边的同时,运用并查集判断两点之间是否连通

import java.util.Scanner;

public class zuixiaoshengchengshu {
    static class edge{
        int u;
        int v;
        int w;
    }
    static edge [] e;
    static int [] f;
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int count=0,sum=0;
        int n = scan.nextInt();
        int m = scan.nextInt();
        e = new edge[m+1];
        f = new int[n+1];
        for (int i = 1; i <= m; i++) {
            e[i] = new edge();
            e[i].u = scan.nextInt();
            e[i].v = scan.nextInt();
            e[i].w = scan.nextInt();
        }

        quickSort(1,m);
        init(n);

        for (int i = 1; i <= m; i++) {
            if (merge(e[i].u,e[i].v) != 0){
                count++;
                sum+=e[i].w;
            }
            if (count == n-1)
                break;
        }

        System.out.print(sum);
    }

    private static void quickSort(int left,int right) {
        int i,j,temp;
        edge t;
        if (left > right)   //'='的取舍
            return;
        temp = e[left].w;
        i = left;
        j = right;
        while (i != j){
            while (e[j].w >= temp && i < j)
                j--;
            while (e[i].w <= temp && i < j)
                i++;
            if (i < j){
                t = e[i];
                e[i] = e[j];
                e[j] = t;
            }
        }
        //基准数归位
        t = e[left];
        e[left] = e[i];
        e[i] = t;

        quickSort(left,i-1);
        quickSort(i+1,right);
    }

    //合并两子集的函数
    private static int merge(int v, int u) {
        int t1,t2;
        t1 = getf(v);
        t2 = getf(u);
        if (t1 != t2){
            //靠左原则,左边变成右边的子集,即把右边的集合,作为左边集合的子集和
            f[t2] = t1;
            return 1;
        }

		//说明这两个点是连通的,则不需要这条路径 
        return 0;
    }

    //找爹的函数,不停的去找爹,直到找到祖宗为止,其实就是找最高领导人
    private static int getf(int v){
        if (f[v] == v)
            return v;
        else {
            f[v]=getf(f[v]);
            return f[v];
        }
    }

    private static void init(int n) {
        for (int i = 1; i <= n; i++) {
            f[i] = i;
        }
    }
}

(2)Prim算法:

posted @ 2020-11-24 11:56  木有呂朋友  阅读(80)  评论(0)    收藏  举报