图(Graph)
适用条件:用于表示网络结构、路径搜索或连通性问题。
比方:想象你在城市中寻找最短的路线。城市中的每条街道和路口就是图中的边和节点。
实现步骤:
- 表示节点和边:定义图中的节点(顶点)和边。
- 初始化图:创建一个图的数据结构,通常使用邻接表或邻接矩阵。
- 添加节点:将节点添加到图中。
- 添加边:在节点之间添加边,表示它们的连接关系。
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)
适用条件:用于快速查找、排序或存储有序数据。
比方:想象你在整理家庭族谱,每个人都有两个孩子。二叉树就像家谱中的父母与孩子的关系。
实现步骤:
- 定义节点结构:每个节点包含数据和指向左、右子节点的指针。
- 初始化树:创建一个二叉树并设置根节点。
- 插入节点:按照一定规则插入节点(如二叉搜索树的规则)。
- 遍历树:按照先序、中序或后序遍历树的节点。
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)
适用条件:用于高效计算连续子数组或子序列特性的方法。
比方:想象你在电影院找座位,希望找到连续的几个空座。滑动窗口就像你通过视线不断滑动,查看每一排的连续空座情况。
实现步骤:
- 初始化窗口:设定窗口的大小和初始位置。
- 滑动窗口:移动窗口的位置,每次计算当前窗口内的特性。
- 更新结果:根据滑动窗口的计算结果更新全局结果。
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(后进先出)数据结构。
比方:想象你在整理书架,每次你把最新的书放在最上面。如果你想读一本书,你只能从最上面的开始,依次往下拿。
实现步骤:
- 初始化栈:创建一个空栈。
- 入栈:将元素添加到栈的顶部。
- 出栈:从栈的顶部移除元素。
- 查看栈顶:查看栈顶元素而不移除它。
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(先进先出)数据结构。
比方:想象你在排队买电影票,先到的人先买票,后到的人在队伍末尾。
实现步骤:
- 初始化队列:创建一个空队列。
- 入队:将元素添加到队列的尾部。
- 出队:从队列的头部移除元素。
- 查看队头:查看队头元素而不移除它。
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)
适用条件:适用于频繁插入、删除操作的线性数据结构,其中每个元素(节点)包含一个数据部分和一个指向下一个节点的引用(指针)。
比方:想象你有一串珍珠,每一颗珍珠都是一个节点,珍珠之间用线串起来。你可以很容易地在某个位置插入或删除一颗珍珠,而不需要移动其他珍珠。
实现步骤:
- 定义节点结构:每个节点包含数据和指向下一个节点的指针。
- 初始化链表:创建一个空链表。
- 插入节点:在链表的指定位置插入新节点。
- 删除节点:删除链表的指定节点。
- 遍历链表:从头到尾遍历链表的每个节点。
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缓存机制。