Java常用算法
一、排序算法
排序算法是计算机科学中最基础的算法之一,用于将一组数据按照特定顺序排列。
1.1 冒泡排序(Bubble Sort)
- 通过重复遍历列表,比较相邻元素并交换位置,直到列表有序。
- 时间复杂度:O(n²)。
public void bubbleSort(int[] arr) {
int n = arr.length;
for (int i = 0; i < n-1; i++) {
for (int j = 0; j < n-i-1; j++) {
if (arr[j] > arr[j+1]) {
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
1.2 选择排序(Selection Sort)
- 每次从未排序部分选择最小元素,放到已排序部分的末尾。
- 时间复杂度:O(n²)。
public void selectionSort(int[] arr) {
int n = arr.length;
for (int i = 0; i < n-1; i++) {
int minIndex = i;
for (int j = i+1; j < n; j++) {
if (arr[j] < arr[minIndex]) {
minIndex = j;
}
}
int temp = arr[minIndex];
arr[minIndex] = arr[i];
arr[i] = temp;
}
}
1.3 插入排序(Insertion Sort)
- 将未排序部分的元素逐个插入到已排序部分的适当位置。
- 时间复杂度:O(n²)。
public void insertionSort(int[] arr) {
int n = arr.length;
for (int i = 1; i < n; i++) {
int key = arr[i];
int j = i - 1;
while (j >= 0 && arr[j] > key) {
arr[j+1] = arr[j];
j = j - 1;
}
arr[j+1] = key;
}
}
1.4 快速排序(Quick Sort)
- 采用分治法,选择一个基准元素,将数组分为两部分,递归排序。
- 时间复杂度:平均 O(n log n),最坏 O(n²)。
public void quickSort(int[] arr, int low, int high) {
if (low < high) {
int pi = partition(arr, low, high);
quickSort(arr, low, pi-1);
quickSort(arr, pi+1, high);
}
}
private int partition(int[] arr, int low, int high) {
int pivot = arr[high];
int i = (low - 1);
for (int j = low; j < high; j++) {
if (arr[j] < pivot) {
i++;
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
int temp = arr[i+1];
arr[i+1] = arr[high];
arr[high] = temp;
return i+1;
}
1.5 归并排序(Merge Sort)
- 采用分治法,将数组分为两半,分别排序后合并。
- 时间复杂度:O(n log n)。
public void mergeSort(int[] arr, int l, int r) {
if (l < r) {
int m = (l + r) / 2;
mergeSort(arr, l, m);
mergeSort(arr, m+1, r);
merge(arr, l, m, r);
}
}
private void merge(int[] arr, int l, int m, int r) {
int n1 = m - l + 1;
int n2 = r - m;
int[] L = new int[n1];
int[] R = new int[n2];
for (int i = 0; i < n1; i++) {
L[i] = arr[l + i];
}
for (int j = 0; j < n2; j++) {
R[j] = arr[m + 1 + j];
}
int i = 0, j = 0;
int k = l;
while (i < n1 && j < n2) {
if (L[i] <= R[j]) {
arr[k] = L[i];
i++;
} else {
arr[k] = R[j];
j++;
}
k++;
}
while (i < n1) {
arr[k] = L[i];
i++;
k++;
}
while (j < n2) {
arr[k] = R[j];
j++;
k++;
}
}
二、查找算法
查找算法用于在数据结构中查找特定元素。常见的查找算法包括:
2.1 线性查找(Linear Search)
- 逐个检查每个元素,直到找到目标元素。
- 时间复杂度:O(n)。
public int linearSearch(int[] arr, int target) {
for (int i = 0; i < arr.length; i++) {
if (arr[i] == target) {
return i;
}
}
return -1;
}
2.2 二分查找(Binary Search)
- 适用于已排序的数组,通过重复将搜索范围减半来查找目标元素。
- 时间复杂度:O(log n)。
public int binarySearch(int[] arr, int target) {
int left = 0, right = arr.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (arr[mid] == target) {
return mid;
} else if (arr[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return -1;
}
三、图算法
图算法用于处理图结构数据。常见的图算法包括:
3.1 深度优先搜索(DFS)
- 从起始节点开始,沿着一条路径尽可能深入,直到无法继续为止,然后回溯。
public void dfs(int[][] graph, int start, boolean[] visited) {
visited[start] = true;
System.out.print(start + " ");
for (int i = 0; i < graph[start].length; i++) {
int next = graph[start][i];
if (!visited[next]) {
dfs(graph, next, visited);
}
}
}
3.2 广度优先搜索(BFS)
- 从起始节点开始,逐层遍历所有相邻节点。
public void bfs(int[][] graph, int start) {
boolean[] visited = new boolean[graph.length];
Queue<Integer> queue = new LinkedList<>();
visited[start] = true;
queue.add(start);
while (!queue.isEmpty()) {
int node = queue.poll();
System.out.print(node + " ");
for (int i = 0; i < graph[node].length; i++) {
int next = graph[node][i];
if (!visited[next]) {
visited[next] = true;
queue.add(next);
}
}
}
}
3.3 Dijkstra 算法
- 用于计算单源最短路径,适用于加权图。
public void dijkstra(int[][] graph, int start) {
int n = graph.length;
int[] dist = new int[n];
boolean[] visited = new boolean[n];
Arrays.fill(dist, Integer.MAX_VALUE);
dist[start] = 0;
for (int i = 0; i < n-1; i++) {
int u = minDistance(dist, visited);
visited[u] = true;
for (int v = 0; v < n; v++) {
if (!visited[v] && graph[u][v] != 0 && dist[u] != Integer.MAX_VALUE && dist[u] + graph[u][v] < dist[v]) {
dist[v] = dist[u] + graph[u][v];
}
}
}
printSolution(dist);
}
private int minDistance(int[] dist, boolean[] visited) {
int min = Integer.MAX_VALUE, minIndex = -1;
for (int i = 0; i < dist.length; i++) {
if (!visited[i] && dist[i] <= min) {
min = dist[i];
minIndex = i;
}
}
return minIndex;
}
private void printSolution(int[] dist) {
System.out.println("Vertex \t Distance from Source");
for (int i = 0; i < dist.length; i++) {
System.out.println(i + " \t\t " + dist[i]);
}
}
四、动态规划
动态规划用于解决具有重叠子问题和最优子结构性质的问题。常见的动态规划问题包括:
4.1 斐波那契数列
- 使用动态规划计算斐波那契数列的第 n 项。
public int fibonacci(int n) {
if (n <= 1) {
return n;
}
int[] dp = new int[n+1];
dp[0] = 0;
dp[1] = 1;
for (int i = 2; i <= n; i++) {
dp[i] = dp[i-1] + dp[i-2];
}
return dp[n];
}
4.2 背包问题
- 解决 0-1 背包问题,即在给定容量下选择物品使总价值最大。
public int knapsack(int[] weights, int[] values, int capacity) {
int n = weights.length;
int[][] dp = new int[n+1][capacity+1];
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= capacity; j++) {
if (weights[i-1] <= j) {
dp[i][j] = Math.max(dp[i-1][j], dp[i-1][j-weights[i-1]] + values[i-1]);
} else {
dp[i][j] = dp[i-1][j];
}
}
}
return dp[n][capacity];
}
五、贪心算法
贪心算法在每一步选择中都采取当前状态下最优的选择,希望导致全局最优解。常见的贪心算法问题包括:
5.1 活动选择问题
- 选择最大数量的互不重叠的活动。
public int activitySelection(int[] start, int[] end) {
Arrays.sort(end);
int count = 1;
int lastEnd = end[0];
for (int i = 1; i < end.length; i++) {
if (start[i] >= lastEnd) {
count++;
lastEnd = end[i];
}
}
return count;
}
六、回溯算法
回溯算法通过尝试所有可能的解来解决问题,通常用于组合、排列等问题。常见的回溯算法问题包括:
6.1 N 皇后问题
- 在 N×N 棋盘上放置 N 个皇后,使其互不攻击。
public void solveNQueens(int n) {
int[] queens = new int[n];
Arrays.fill(queens, -1);
backtrack(queens, 0, n);
}
private void backtrack(int[] queens, int row, int n) {
if (row == n) {
printQueens(queens);
return;
}
for (int col = 0; col < n; col++) {
if (isSafe(queens, row, col)) {
queens[row] = col;
backtrack(queens, row+1, n);
queens[row] = -1;
}
}
}
private boolean isSafe(int[] queens, int row, int col) {
for (int i = 0; i < row; i++) {
if (queens[i] == col || Math.abs(queens[i] - col) == Math.abs(i - row)) {
return false;
}
}
return true;
}
private void printQueens(int[] queens) {
for (int i = 0; i < queens.length; i++) {
for (int j = 0; j < queens.length; j++) {
if (queens[i] == j) {
System.out.print("Q ");
} else {
System.out.print(". ");
}
}
System.out.println();
}
System.out.println();
}
七、字符串匹配算法
字符串匹配算法用于在文本中查找特定模式的子串。常见的字符串匹配算法包括:
7.1 KMP 算法
- 通过预处理模式串,避免不必要的比较。
public int kmpSearch(String text, String pattern) {
int[] lps = computeLPSArray(pattern);
int i = 0, j = 0;
while (i < text.length()) {
if (pattern.charAt(j) == text.charAt(i)) {
i++;
j++;
}
if (j == pattern.length()) {
return i - j;
} else if (i < text.length() && pattern.charAt(j) != text.charAt(i)) {
if (j != 0) {
j = lps[j-1];
} else {
i++;
}
}
}
return -1;
}
private int[] computeLPSArray(String pattern) {
int[] lps = new int[pattern.length()];
int len = 0, i = 1;
while (i < pattern.length()) {
if (pattern.charAt(i) == pattern.charAt(len)) {
len++;
lps[i] = len;
i++;
} else {
if (len != 0) {
len = lps[len-1];
} else {
lps[i] = len;
i++;
}
}
}
return lps;
}
八、数论算法
数论算法用于解决与整数相关的数学问题。常见的数论算法包括:
8.1 欧几里得算法
- 用于计算两个整数的最大公约数(GCD)。
public int gcd(int a, int b) {
if (b == 0) {
return a;
}
return gcd(b, a % b);
}
8.2 素数检测
- 判断一个数是否为素数。
public boolean isPrime(int n) {
if (n <= 1) {
return false;
}
for (int i = 2; i * i <= n; i++) {
if (n % i == 0) {
return false;
}
}
return true;
}
九、位运算算法
位运算算法利用位操作来高效解决问题。常见的位运算算法包括:
9.1 计算二进制中 1 的个数
- 使用位运算计算一个整数的二进制表示中 1 的个数。
public int countSetBits(int n) {
int count = 0;
while (n > 0) {
count += n & 1;
n >>= 1;
}
return count;
}
9.2 判断一个数是否是 2 的幂
- 使用位运算判断一个数是否是 2 的幂。
public boolean isPowerOfTwo(int n) {
return n > 0 && (n & (n - 1)) == 0;
}