algorithm--这个是算法的英文单词(五)

简介: algorithm--这个是算法的英文单词

并查集


从一个逻辑题来给大家介绍并查集


现在有十个强盗


一号强盗与二号强盗是同伙


三号强盗与四号强盗是同伙


五号强盗与二号强盗是同伙


四号强盗与六号强盗是同伙


二号强盗与六号强盗是同伙


八号强盗与七号强盗是同伙


九号强盗与七号强盗是同伙


一号强盗与六号强盗是同伙


二号强盗与四号强盗是同伙


有一点需要注意 强盗同伙的同伙也是同伙,你能找出来有多少独立的犯罪团伙吗?


根据题目分析出逻辑上三个情况


part1  1 2 5 3 4 6


part    2 7 8 9


part   10


数组理解


这里数组下标按照从1开始理解;

1

2

3

4

5

6

7

8

9

10

1

2

3

4

5

6

7

8

9

10


一号和二号一组

1

1

3

4

5

6

7

8

9

10

1

2

3

4

5

6

7

8

9

10


三号和四号

1

1

3

3

5

6

7

8

9

10

1

2

3

4

5

6

7

8

9

10


五号和二号

5

5

3

3

5

6

7

8

9

10

1

2

3

4

5

6

7

8

9

10


四号和六号

5

5

3

3

5

3

7

8

9

10

1

2

3

4

5

6

7

8

9

10


二号和六号

5

5

3

3

5

5

7

8

9

10

1

2

3

4

5

6

7

8

9

10


八号和七号

5

5

5

5

5

5

7

8

9

10

1

2

3

4

5

6

7

8

9

10


九号和七号

5

5

3

3

5

5

9

9

9

10

1

2

3

4

5

6

7

8

9

10


以上是我们用数组变化的方式来理解的并查集逻辑题目,接下来是树的理解


树结构理解


image.png


并查集


其实就是 合并和查询的集合


合并:把两个不相交的集合合并为一个集合


查询,查询两个元素是否在同一个集合中


用一个元素代表集合,成为集合首领,判断是否在集合中,让元素存储首领来判断,合并需选出新的首领,将被合并的集合元素首领改成新的首领


另一种角度上说,并查集是将一个集合以树结构进行组合的数据结构.


优先级队列


PriocrityQueue, 根据优先级的顺序排队,


如果想要自定义规则需要自定义比较其 : conparator


简单使用优先级队列

package com.hyc.DataStructure.PriorityQueue;
import java.util.Comparator;
import java.util.PriorityQueue;
/**
 * @projectName: DataStructure
 * @package: com.hyc.DataStructure.PriorityQueue
 * @className: PriorityQueueTest
 * @author: 冷环渊 doomwatcher
 * @description: TODO
 * @date: 2022/2/26 12:25
 * @version: 1.0
 */
public class PriorityQueueTest {
    public static void main(String[] args) {
        //PriorityQueue<String> queue = new PriorityQueue<>();
        //queue.offer("1");
        //queue.offer("2");
        //queue.offer("3");
        //queue.offer("4");
        //System.out.println(queue.poll());
        //System.out.println(queue.poll());
        //System.out.println(queue.poll());
        //System.out.println(queue.poll());
        PriorityQueue<student> studentQueue = new PriorityQueue<>(new Comparator<>() {
            @Override
            public int compare(student o1, student o2) {
                if (o1.score == o2.score) {
                    return o1.name.compareTo(o2.name);
                }
                return o1.score - o2.score;
            }
            private static final long serialVersionUID = -2730510067769567346L;
        }
        );
        studentQueue.offer(new student("atuo", 80));
        studentQueue.offer(new student("dmc", 60));
        studentQueue.offer(new student("amc", 60));
        studentQueue.offer(new student("yqing", 100));
        System.out.println(studentQueue.poll());
        System.out.println(studentQueue.poll());
        System.out.println(studentQueue.poll());
        System.out.println(studentQueue.poll());
    }
}
class student {
    @Override
    public String toString() {
        return "student{" +
                "name='" + name + '\'' +
                ", score=" + score +
                '}';
    }
    public String name;
    public int score;
    public student(String name, int score) {
        this.name = name;
        this.score = score;
    }
}


实战题目


面试题 17.14. 最小K个数


设计一个算法,找出数组中最小的k个数。以任意顺序返回这k个数均可。


示例

输入: arr = [1,3,5,7,2,4,6,8] k = 4
输出 [1,2,3,4]

没有使用优先级队列的时候

    public static int[] smallestK(int[] arr, int k) {
        Arrays.sort(arr);
        int[] result = new int[k];
        for (int i = 0; i < k; i++) {
            result[i] = arr[i];
        }
        return result;
    }

使用了队列的


 

public static int[] smallestKByQueue(int[] arr, int k) {
        PriorityQueue<Integer> queue = new PriorityQueue<>();
        int[] result = new int[k];
        for (int i = 0; i < arr.length; i++) {
            queue.offer(arr[i]);
        }
        for (int j = 0; j < k; j++) {
            result[j] = queue.poll();
        }
        return result;
    }


使用了大顶堆


 

public static int[] smallestByHeap(int[] arr, int k) {
        PriorityQueue<Integer> queue = new PriorityQueue<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1 - o2;
            }
        });
        int[] result = new int[k];
        for (int i = 0; i < arr.length; i++) {
            queue.offer(arr[i]);
        }
        for (int i = 0; i < arr.length - k; i++) {
            queue.poll();
        }
        for (int j = 0; j < k; j++) {
            result[j] = queue.poll();
        }
        return result;
    }


这里主要是学习实战优先级队列的使用,最后提交会发现速度最快的是第一种方法


image.png



图基本介绍


  1. 前面我们学了线性表和树
  2. 线性表局限于一个直接前驱和一个直接后继的关系
  3. 树也只能有一个直接前驱也就是父节点
  4. 当我们需要表示多对多的关系时, 这里我们就用到了图。


图的举例说明


图是一种数据结构,其中结点可以具有零个或多个相邻元素。两个结点之间的连接称为边。 结点也可以称为


顶点。

image.png



图的常用概念


  1. 顶点(vertex)
  2. 边(edge)
  3. 路径
  4. 无向图(下图
    ![image-20220225144911417](https://gitee.com/cold-abyss_admin/my-image-host/raw/master/ img /image-20220225144911417.png)
  5. 有向图
  6. 带权图
    ![image-20220225144952311](https://gitee.com/cold-abyss_admin/my-image-host/raw/master/ img /image-20220225144952311.png)


图的表示方式


图的表示方式有两种:二维数组表示(邻接矩阵);链表表示(邻接表)。


邻接矩阵


邻接矩阵是表示图形中顶点之间相邻关系的矩阵,对于 n 个顶点的图而言,矩阵是的 row 和 col 表示的是 1....n  个点。


1.png


邻接表


  1. 邻接矩阵需要为每个顶点都分配 n 个边的空间,其实有很多边都是不存在,会造成空间的一定损失.
  2. 邻接表的实现只关心存在的边,不关心不存在的边。因此没有空间浪费,邻接表由数组+链表组成


1.png


图的快速入门案例


代码实现如下图结构.


image.png


思路:存储顶点 String 使用 ArrayList (2) 保存矩阵 int[][] edges存储顶点 String 使用 ArrayList (2) 保存矩阵 int[][] edges


图的深度优先遍历介绍


所谓图的遍历,即是对结点的访问。一个图有那么多个结点,如何遍历这些结点,需要特定策略,一般有两种


访问策略: (1)深度优先遍历 (2)广度优先遍历


深度优先遍历基本思想


图的深度优先搜索(Depth First Search) 。


  1. 深度优先遍历,从初始访问结点出发,初始访问结点可能有多个邻接结点,深度优先遍历的策略就是首先访问  第一个邻接结点,然后再以这个被访问的邻接结点作为初始结点,访问它的第一个邻接结点, 可以这样理解:  每次都在访问完当前结点后首先访问当前结点的第一个邻接结点。
  2. 我们可以看到,这样的访问策略是优先往纵向挖掘深入,而不是对一个结点的所有邻接结点进行横向访问。
  3. 显然,深度优先搜索是一个递归的过程


深度优先遍历算法步骤


  1. 访问初始结点 v,并标记结点 v 为已访问。
  2. 查找结点 v 的第一个邻接结点 w。
  3. 若 w 存在,则继续执行 4,如果 w 不存在,则回到第 1 步,将从 v 的下一个结点继续。
  4. 若 w 未被访问,对 w 进行深度优先遍历递归(即把 w 当做另一个 v,然后进行步骤 123)。
  5. 查找结点 v 的 w 邻接结点的下一个邻接结点,转到步骤 3。


1.png


深度优先代码实现


 

    //深度优先遍历方法
    public void dfs(boolean[] isVisted, int i) {
        //    首先输出该节点
        System.out.print(getValueByindex(i) + "->");
        //    将该节点设置为已经访问过
        isVisted[i] = true;
        //查找节点i 的第一个邻结节点
        int w = getFirstNeighbor(i);
        while (w != -1) {
            if (!isVisted[w]) {
                dfs(isVisted, w);
            }
            //    如果w节点已经被访问过了,那么我
            w = getNexttNeighbor(i, w);
        }
    }
    //对dfs进行重载,遍历我们所有的节点并且进行dfs
    public void dfs() {
        for (int i = 0; i < getNumOFVertex(); i++) {
            if (!isVisted[i]) {
                dfs(isVisted, i);
            }
        }
    }


图的广度优先遍历


广度优先遍历基本思想:


  1. 图的广度优先搜索(Broad First Search) 。
  2. 类似于一个分层搜索的过程,广度优先遍历需要使用一个队列以保持访问过的结点的顺序,以便按这个顺序来  访问这些结点的邻接结点


广度优先遍历算法步骤


  1. 访问初始结点 v 并标记结点 v 为已访问。
  2. 结点 v 入队列
  3. 当队列非空时,继续执行,否则算法结束。
  4. 出队列,取得队头结点 u。
  5. 查找结点 u 的第一个邻接结点 w。
  6. 若结点 u 的邻接结点 w 不存在,则转到步骤 3;否则循环执行以下三个步骤:
  1. 若结点 w 尚未被访问,则访问结点 w 并标记为已访问。
  2. 结点 w 入队列
  3. 查找结点 u 的继 w 邻接结点后的下一个邻接结点 w,转到步骤 6。


1.png


广度优先算法的代码实现


 

    //对一个节点进行广度优先搜索遍历
    public void bfs(boolean[] isVisted, int i) {
        //表示队列的头节点的对应下标
        int u;
        //邻节点w
        int w;
        //模拟队列记录节点访问的顺序
        LinkedList<Object> queue = new LinkedList<>();
        //输出节点信息
        System.out.print(getValueByindex(i) + "->");
        //    标记为已访问
        isVisted[i] = true;
        //    将节点加入队列
        queue.addLast(i);
        //判断只要非空就一直找
        while (!queue.isEmpty()) {
            //    取出队列头节点下标
            u = (Integer) queue.removeFirst();
            w = getFirstNeighbor(u);
            while (w != -1) {
                //    是否访问过
                if (!isVisted[w]) {
                    System.out.print(getValueByindex(w) + "->");
                    //    标记已经访问
                    isVisted[w] = true;
                    //    入队
                    queue.addLast(w);
                }
                //    如果访问过 以u 为前驱点 找w后面的第一个节点
                w = getNexttNeighbor(u, w);//体现出广度优先
            }
        }
    }
    //遍历所有的节点都进行广度优先搜索
    public void bfs() {
        for (int i = 0; i < getNumOFVertex(); i++) {
            if (!isVisted[i]) {
                bfs(isVisted, i);
            }
        }
    }


代码汇总

package com.hyc.DataStructure.garph;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedList;
/**
 * @projectName: DataStructure
 * @package: com.hyc.DataStructure.garph
 * @className: Graph
 * @author: 冷环渊 doomwatcher
 * @description: TODO
 * @date: 2022/2/22 17:52
 * @version: 1.0
 */
public class Graph {
    //存储顶点结合
    private ArrayList<String> vertexList;
    //存储图对应的邻结矩阵
    private int[][] edges;
    //表示边的数目
    private int numOFEdges;
    private boolean[] isVisted;
    public static void main(String[] args) {
        //测试一把图是否创建ok
        int n = 8;  //结点的个数
        //String Vertexs[] = {"A", "B", "C", "D", "E"};
        String Vertexs[] = {"1", "2", "3", "4", "5", "6", "7", "8"};
        //创建图对象
        Graph graph = new Graph(n);
        //循环的添加顶点
        for (String vertex : Vertexs) {
            graph.insertVertex(vertex);
        }
        //添加边
        //A-B A-C B-C B-D B-E
//    graph.insertEdge(0, 1, 1); // A-B
//    graph.insertEdge(0, 2, 1); //
//    graph.insertEdge(1, 2, 1); //
//    graph.insertEdge(1, 3, 1); //
//    graph.insertEdge(1, 4, 1); //
        //更新边的关系
        graph.insertEdges(0, 1, 1);
        graph.insertEdges(0, 2, 1);
        graph.insertEdges(1, 3, 1);
        graph.insertEdges(1, 4, 1);
        graph.insertEdges(3, 7, 1);
        graph.insertEdges(4, 7, 1);
        graph.insertEdges(2, 5, 1);
        graph.insertEdges(2, 6, 1);
        graph.insertEdges(5, 6, 1);
        //显示 邻结矩阵
        graph.showGarph();
        ////    测试深度遍历
        System.out.println("深度遍历");
        graph.dfs();
        System.out.println();
        //测试广度优先搜索
        //System.out.println("广度遍历");
        //graph.bfs();
    }
    //构造器
    public Graph(int n) {
        //    初始化矩阵和VertexList
        edges = new int[n][n];
        vertexList = new ArrayList<String>(n);
        numOFEdges = 0;
        isVisted = new boolean[n];
    }
    /**
     * @author 冷环渊 Doomwatcher
     * @context: 得到第一个邻节点的下标
     * @date: 2022/2/22 18:22
     * @param index 如果存在就是返回对应的下标 否则返回-1
     * @return: int
     */
    public int getFirstNeighbor(int index) {
        for (int j = 0; j < vertexList.size(); j++) {
            if (edges[index][j] > 0) {
                return j;
            }
        }
        return -1;
    }
    public int getNexttNeighbor(int v1, int v2) {
        for (int j = v2 + 1; j < vertexList.size(); j++) {
            if (edges[v1][j] > 0) {
                return j;
            }
        }
        return -1;
    }
    //深度优先遍历方法
    public void dfs(boolean[] isVisted, int i) {
        //    首先输出该节点
        System.out.print(getValueByindex(i) + "->");
        //    将该节点设置为已经访问过
        isVisted[i] = true;
        //查找节点i 的第一个邻结节点
        int w = getFirstNeighbor(i);
        while (w != -1) {
            if (!isVisted[w]) {
                dfs(isVisted, w);
            }
            //    如果w节点已经被访问过了,那么我
            w = getNexttNeighbor(i, w);
        }
    }
    //对dfs进行重载,遍历我们所有的节点并且进行dfs
    public void dfs() {
        for (int i = 0; i < getNumOFVertex(); i++) {
            if (!isVisted[i]) {
                dfs(isVisted, i);
            }
        }
    }
    //对一个节点进行广度优先搜索遍历
    public void bfs(boolean[] isVisted, int i) {
        //表示队列的头节点的对应下标
        int u;
        //邻节点w
        int w;
        //模拟队列记录节点访问的顺序
        LinkedList<Object> queue = new LinkedList<>();
        //输出节点信息
        System.out.print(getValueByindex(i) + "->");
        //    标记为已访问
        isVisted[i] = true;
        //    将节点加入队列
        queue.addLast(i);
        //判断只要非空就一直找
        while (!queue.isEmpty()) {
            //    取出队列头节点下标
            u = (Integer) queue.removeFirst();
            w = getFirstNeighbor(u);
            while (w != -1) {
                //    是否访问过
                if (!isVisted[w]) {
                    System.out.print(getValueByindex(w) + "->");
                    //    标记已经访问
                    isVisted[w] = true;
                    //    入队
                    queue.addLast(w);
                }
                //    如果访问过 以u 为前驱点 找w后面的第一个节点
                w = getNexttNeighbor(u, w);//体现出广度优先
            }
        }
    }
    //遍历所有的节点都进行广度优先搜索
    public void bfs() {
        for (int i = 0; i < getNumOFVertex(); i++) {
            if (!isVisted[i]) {
                bfs(isVisted, i);
            }
        }
    }
    //图中常用的方法
    //返回节点的数目
    public int getNumOFVertex() {
        return vertexList.size();
    }
    //返回节点i 对应的下标数据
    public String getValueByindex(int i) {
        return vertexList.get(i);
    }
    //返回v1和v2的权值
    public int getWeight(int v1, int v2) {
        return edges[v1][v2];
    }
    //显示矩阵
    public void showGarph() {
        for (int[] edge : edges) {
            System.out.println(Arrays.toString(edge));
        }
    }
    //    插入顶点
    public void insertVertex(String vertex) {
        vertexList.add(vertex);
    }
    /**
     * @author 冷环渊 Doomwatcher
     * @context: 添加边
     * @date: 2022/2/22 18:01
     * @param v1 表示点的下标 即使 第几个顶点 a-b a ->0 b->1
     * @param v2 和v1同理是第二个顶点的下标
     * @param weight  表示矩阵里面用什么来表示他们是关连的 0 表示没有连接 1 表示连接了
     * @return: void
     */
    public void insertEdges(int v1, int v2, int weight) {
        edges[v1][v2] = weight;
        edges[v2][v1] = weight;
        numOFEdges++;
    }
}


图的深度优先 VS 广度优先


1.png1.png

目录
相关文章
|
1月前
|
算法 搜索推荐 大数据
算法(Algorithm)
算法(Algorithm)
58 0
|
1月前
|
机器学习/深度学习 算法 程序员
C++ Algorithm 库 算法秘境探索(Algorithm Wonderland Exploration)
C++ Algorithm 库 算法秘境探索(Algorithm Wonderland Exploration)
74 1
|
6月前
|
算法 C++
【Hello Algorithm】链表相关算法题
【Hello Algorithm】链表相关算法题
23 0
|
4月前
|
机器学习/深度学习 算法 决策智能
Python高级算法——遗传算法(Genetic Algorithm)
Python高级算法——遗传算法(Genetic Algorithm)
100 0
|
4月前
|
算法 调度 Python
Python高级算法——贪心算法(Greedy Algorithm)
Python高级算法——贪心算法(Greedy Algorithm)
231 3
|
6月前
|
算法
【Hello Algorithm】贪心算法
【Hello Algorithm】贪心算法
32 0
|
6月前
|
人工智能 算法 数据挖掘
【Python算法Algorithm】专栏导读
【Python算法Algorithm】专栏导读
34 0
【Python算法Algorithm】专栏导读
|
1月前
|
传感器 算法 计算机视觉
基于肤色模型和中值滤波的手部检测算法FPGA实现,包括tb测试文件和MATLAB辅助验证
该内容是关于一个基于肤色模型和中值滤波的手部检测算法的描述,包括算法的运行效果图和所使用的软件版本(matlab2022a, vivado2019.2)。算法分为肤色分割和中值滤波两步,其中肤色模型在YCbCr色彩空间定义,中值滤波用于去除噪声。提供了一段核心程序代码,用于处理图像数据并在FPGA上实现。最终,检测结果输出到&quot;hand.txt&quot;文件。
|
1月前
|
机器学习/深度学习 算法 计算机视觉
基于yolov2深度学习网络的视频手部检测算法matlab仿真
基于yolov2深度学习网络的视频手部检测算法matlab仿真
|
1月前
|
算法
【MATLAB】语音信号识别与处理:移动中位数滤波算法去噪及谱相减算法呈现频谱
【MATLAB】语音信号识别与处理:移动中位数滤波算法去噪及谱相减算法呈现频谱
23 2