算法结构

2 阅读6分钟

图(Graph)

适用条件:用于表示网络结构、路径搜索或连通性问题。

比方:想象你在城市中寻找最短的路线。城市中的每条街道和路口就是图中的边和节点。

实现步骤

  1. 表示节点和边:定义图中的节点(顶点)和边。
  2. 初始化图:创建一个图的数据结构,通常使用邻接表或邻接矩阵。
  3. 添加节点:将节点添加到图中。
  4. 添加边:在节点之间添加边,表示它们的连接关系。
import java.util.*;

class Graph {
    private int V; // 节点数量
    private LinkedList<Integer>[] adj; // 邻接表

    // 构造函数
    Graph(int v) {
        V = v;
        adj = new LinkedList[v];
        for (int i = 0; i < v; ++i) {
            adj[i] = new LinkedList<>();
        }
    }

    // 添加边
    void addEdge(int v, int w) {
        adj[v].add(w);
    }

    // 打印图
    void printGraph() {
        for (int i = 0; i < V; i++) {
            System.out.print("Node " + i + ": ");
            for (Integer node : adj[i]) {
                System.out.print(node + " ");
            }
            System.out.println();
        }
    }

    public static void main(String[] args) {
        Graph g = new Graph(4);
        
        g.addEdge(0, 1);
        g.addEdge(0, 2);
        g.addEdge(1, 2);
        g.addEdge(2, 0);
        g.addEdge(2, 3);
        g.addEdge(3, 3);
        
        g.printGraph();
    }
}

输出结果

Node 0: 1 2 
Node 1: 2 
Node 2: 0 3 
Node 3: 3 

具体例子:社交网络、交通网络、互联网连接。

使用的算法:广度优先搜索(BFS)、深度优先搜索(DFS)、最短路径算法(Dijkstra、Floyd-Warshall)、最小生成树(Kruskal、Prim)。

二叉树(Binary Tree)

适用条件:用于快速查找、排序或存储有序数据。

比方:想象你在整理家庭族谱,每个人都有两个孩子。二叉树就像家谱中的父母与孩子的关系。

实现步骤

  1. 定义节点结构:每个节点包含数据和指向左、右子节点的指针。
  2. 初始化树:创建一个二叉树并设置根节点。
  3. 插入节点:按照一定规则插入节点(如二叉搜索树的规则)。
  4. 遍历树:按照先序、中序或后序遍历树的节点。
class Node {
    int key;
    Node left, right;

    public Node(int item) {
        key = item;
        left = right = null;
    }
}

class BinaryTree {
    Node root;

    BinaryTree() {
        root = null;
    }

    void insert(int key) {
        root = insertRec(root, key);
    }

    Node insertRec(Node root, int key) {
        if (root == null) {
            root = new Node(key);
            return root;
        }

        if (key < root.key)
            root.left = insertRec(root.left, key);
        else if (key > root.key)
            root.right = insertRec(root.right, key);

        return root;
    }

    void inorder() {
        inorderRec(root);
    }

    void inorderRec(Node root) {
        if (root != null) {
            inorderRec(root.left);
            System.out.print(root.key + " ");
            inorderRec(root.right);
        }
    }

    public static void main(String[] args) {
        BinaryTree tree = new BinaryTree();

        tree.insert(50);
        tree.insert(30);
        tree.insert(20);
        tree.insert(40);
        tree.insert(70);
        tree.insert(60);
        tree.insert(80);

        tree.inorder();
    }
}

输出结果

20 30 40 50 60 70 80 

具体例子:文件系统、表达式树、二叉搜索树。

使用的算法:树的遍历(前序遍历、中序遍历、后序遍历)、二叉搜索树操作(插入、删除、查找)、平衡树(AVL树、红黑树)。

滑动窗口(Sliding Window)

适用条件:用于高效计算连续子数组或子序列特性的方法。

比方:想象你在电影院找座位,希望找到连续的几个空座。滑动窗口就像你通过视线不断滑动,查看每一排的连续空座情况。

实现步骤

  1. 初始化窗口:设定窗口的大小和初始位置。
  2. 滑动窗口:移动窗口的位置,每次计算当前窗口内的特性。
  3. 更新结果:根据滑动窗口的计算结果更新全局结果。
public class SlidingWindow {

    public static int maxSum(int[] arr, int windowSize) {
        int n = arr.length;
        if (n < windowSize) {
            System.out.println("Invalid");
            return -1;
        }

        int windowSum = 0;
        for (int i = 0; i < windowSize; i++) {
            windowSum += arr[i];
        }

        int maxSum = windowSum;

        for (int i = windowSize; i < n; i++) {
            windowSum += arr[i] - arr[i - windowSize];
            maxSum = Math.max(maxSum, windowSum);
        }

        return maxSum;
    }

    public static void main(String[] args) {
        int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
        int k = 3;
        System.out.println(maxSum(arr, k));
    }
}

输出结果

27

具体例子:最大子数组和、字符串中的最长无重复子串、固定大小的子数组和。

使用的算法:最大子数组和(Kadane算法)、最长无重复子串、固定大小的子数组和。

栈(Stack)

适用条件:适用于逆序处理或回退操作的LIFO(后进先出)数据结构。

比方:想象你在整理书架,每次你把最新的书放在最上面。如果你想读一本书,你只能从最上面的开始,依次往下拿。

实现步骤

  1. 初始化栈:创建一个空栈。
  2. 入栈:将元素添加到栈的顶部。
  3. 出栈:从栈的顶部移除元素。
  4. 查看栈顶:查看栈顶元素而不移除它。
import java.util.Stack;

public class StackExample {

    public static void main(String[] args) {
        Stack<Integer> stack = new Stack<>();

        // 入栈
        stack.push(1);
        stack.push(2);
        stack.push(3);

        // 查看栈顶元素
        System.out.println("栈顶元素: " + stack.peek());

        // 出栈
        System.out.println("出栈元素: " + stack.pop());

        // 查看栈顶元素
        System.out.println("栈顶元素: " + stack.peek());
    }
}

输出结果

栈顶元素: 3
出栈元素: 3
栈顶元素: 2

具体例子:括号匹配、函数调用栈、浏览器的前进和后退功能。

使用的算法:深度优先搜索(DFS)、括号匹配、表达式求值(逆波兰表示法)。

队列(Queue)

适用条件:适用于顺序处理的FIFO(先进先出)数据结构。

比方:想象你在排队买电影票,先到的人先买票,后到的人在队伍末尾。

实现步骤

  1. 初始化队列:创建一个空队列。
  2. 入队:将元素添加到队列的尾部。
  3. 出队:从队列的头部移除元素。
  4. 查看队头:查看队头元素而不移除它。
import java.util.LinkedList;
import java.util.Queue;

public class QueueExample {

    public static void main(String[] args) {
        Queue<Integer> queue = new LinkedList<>();

        // 入队
        queue.offer(1);
        queue.offer(2);
        queue.offer(3);

        // 查看队头元素
        System.out.println("队头元素: " + queue.peek());

        // 出队
        System.out.println("出队元素: " + queue.poll());

        // 查看队头元素
        System.out.println("队头元素: " + queue.peek());
    }
}

输出结果

队头元素: 1
出队元素: 1
队头元素: 2

具体例子:任务调度、处理异步请求、操作系统中的进程调度。

使用的算法:广度优先搜索(BFS)、任务调度、操作系统的进程调度。

链表(Linked List)

适用条件:适用于频繁插入、删除操作的线性数据结构,其中每个元素(节点)包含一个数据部分和一个指向下一个节点的引用(指针)。

比方:想象你有一串珍珠,每一颗珍珠都是一个节点,珍珠之间用线串起来。你可以很容易地在某个位置插入或删除一颗珍珠,而不需要移动其他珍珠。

实现步骤

  1. 定义节点结构:每个节点包含数据和指向下一个节点的指针。
  2. 初始化链表:创建一个空链表。
  3. 插入节点:在链表的指定位置插入新节点。
  4. 删除节点:删除链表的指定节点。
  5. 遍历链表:从头到尾遍历链表的每个节点。
class Node {
    int data;
    Node next;

    Node(int data) {
        this.data = data;
        this.next = null;
    }
}

class LinkedList {
    Node head;

    LinkedList() {
        this.head = null;
    }

    // 插入节点到链表的开头
    void insertAtBeginning(int data) {
        Node newNode = new Node(data);
        newNode.next = head;
        head = newNode;
    }

    // 删除链表中的节点
    void deleteNode(int key) {
        Node temp = head, prev = null;

        // 如果头节点是要删除的节点
        if (temp != null && temp.data == key) {
            head = temp.next; // 改变头节点
            return;
        }

        // 查找要删除的节点,保留前一个节点
        while (temp != null && temp.data != key) {
            prev = temp;
            temp = temp.next;
        }

        // 如果找不到要删除的节点
        if (temp == null) return;

        // 断开链接
        prev.next = temp.next;
    }

    // 遍历链表
    void printList() {
        Node temp = head;
        while (temp != null) {
            System.out.print(temp.data + " ");
            temp = temp.next;
        }
    }

    public static void main(String[] args) {
        LinkedList list = new LinkedList();

        list.insertAtBeginning(1);
        list.insertAtBeginning(2);
        list.insertAtBeginning(3);

        System.out.println("链表内容:");
        list.printList();

        list.deleteNode(2);
        System.out.println("\n删除节点后的链表内容:");
        list.printList();
    }
}

输出结果

链表内容:
3 2 1 
删除节点后的链表内容:
3 1 

具体例子:实现堆栈和队列、实现LRU缓存机制、链表反转。

使用的算法:链表操作(插入、删除、查找)、双向链表、循环链表、链表反转、LRU缓存机制。