Java作为一种广泛使用的编程语言,支持实现多种算法。这些算法可以根据其用途、复杂度、数据结构和应用领域进行分类。以下是一些Java中常见的算法示例:
排序算法:
冒泡排序:
通过重复地遍历要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。遍历数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。
public class BubbleSort {
public static 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]) {
// swap arr[j+1] and arr[j]
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
public static void main(String[] args) {
int[] arr = {64, 34, 25, 12, 22, 11, 90};
bubbleSort(arr);
System.out.println("Sorted array: ");
for (int i = 0; i < arr.length; i++)
System.out.print(arr[i] + " ");
System.out.println();
}
}
选择排序:
首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
public class SelectionSort {
public static void selectionSort(int[] arr) {
int n = arr.length;
for (int i = 0; i < n-1; i++) {
// Find the minimum element in unsorted array
int min_idx = i;
for (int j = i+1; j < n; j++)
if (arr[j] < arr[min_idx])
min_idx = j;
// Swap the found minimum element with the first element
int temp = arr[min_idx];
arr[min_idx] = arr[i];
arr[i] = temp;
}
}
public static void main(String[] args) {
int[] arr = {64, 25, 12, 22, 11};
selectionSort(arr);
System.out.println("Sorted array: ");
for (int i = 0; i < arr.length; i++)
System.out.print(arr[i] + " ");
System.out.println();
}
}
插入排序:
通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
public class InsertionSort {
public static void insertionSort(int[] arr) {
int n = arr.length;
for (int i = 1; i < n; ++i) {
int key = arr[i];
int j = i - 1;
/* Move elements of arr[0..i-1], that are
greater than key, to one position ahead
of their current position */
while (j >= 0 && arr[j] > key) {
arr[j + 1] = arr[j];
j = j - 1;
}
arr[j + 1] = key;
}
}
public static void main(String[] args) {
int[] arr = {12, 11, 13, 5, 6};
insertionSort(arr);
System.out.println("Sorted array: ");
for (int i = 0; i < arr.length; i++)
System.out.print(arr[i] + " ");
System.out.println();
}
}
快速排序:
通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
public class QuickSort {
public static void sort(int[] array) {
quickSort(array, 0, array.length - 1);
}
private static void quickSort(int[] array, int low, int high) {
if (low < high) {
int pivotIndex = partition(array, low, high);
quickSort(array, low, pivotIndex - 1);
quickSort(array, pivotIndex + 1, high);
}
}
private static int partition(int[] array, int low, int high) {
int pivot = array[high];
int i = low - 1;
for (int j = low; j < high; j++) {
if (array[j] < pivot) {
i++;
swap(array, i, j);
}
}
swap(array, i + 1, high);
return i + 1;
}
private static void swap(int[] array, int i, int j) {
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
public static void main(String[] args) {
int[] array = {10, 7, 8, 9, 1, 5};
sort(array);
for (int num : array) {
System.out.print(num + " ");
}
}
}
快速排序的关键在于选择一个“基准”元素,并通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序的目的。
归并排序:
归并排序是一种高效的排序算法,它采用分治法的策略,将一个大问题分解成小问题来解决,然后将小问题的解合并以解决整个大问题。在归并排序中,我们将数组分成两半,对每一半递归地应用归并排序,然后将排序后的两半合并在一起。
public class MergeSort {
// 主函数,用于调用归并排序
public static void sort(int[] array) {
if (array.length < 2) {
return; // 基本情形:如果数组只有一个元素,就不需要排序
}
int middle = array.length / 2;
int[] left = new int[middle];
int[] right = new int[array.length - middle];
// 将数据复制到左右两个数组
for (int i = 0; i < middle; i++) {
left[i] = array[i];
}
for (int i = middle; i < array.length; i++) {
right[i - middle] = array[i];
}
// 对左右两个数组递归地进行归并排序
sort(left);
sort(right);
// 合并排序后的左右两个数组
merge(array, left, right);
}
// 合并两个已排序的数组
private static void merge(int[] result, int[] left, int[] right) {
int i = 0, j = 0, k = 0;
// 当左右两个数组都未遍历完时,比较并合并
while (i < left.length && j < right.length) {
if (left[i] <= right[j]) {
result[k++] = left[i++];
} else {
result[k++] = right[j++];
}
}
// 将剩余的元素复制到结果数组中
while (i < left.length) {
result[k++] = left[i++];
}
while (j < right.length) {
result[k++] = right[j++];
}
}
public static void main(String[] args) {
int[] array = {34, 7, 23, 32, 5, 62};
sort(array);
for (int i = 0; i < array.length; i++) {
System.out.print(array[i] + " ");
}
}
}
搜索算法:
线性搜索:
逐个检查每个元素,直到找到所需的特定元素为止。
public class LinearSearch {
public static int linearSearch(int[] arr, int target) {
for (int i = 0; i < arr.length; i++) {
if (arr[i] == target)
return i; // 返回目标的索引
}
return -1; // 如果没有找到目标,返回-1
}
public static void main(String[] args) {
int[] arr = {2, 3, 4, 10, 40};
int target = 10;
int result = linear
Search.linearSearch(arr, target);
if (result == -1)
System.out.println("Element not present in array");
else
System.out.println("Element found at index " + result);
}
}
二分搜索:
在有序数组中查找某一特定元素的搜索算法。搜索过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜索过程结束;如果某一特定元素大于或者小于中间元素,则在数组大于或小于中间元素的那一半中查找,而且跟开始一样从中间元素开始比较。如果在某一步骤数组为空,则代表找不到。
注意,二分搜索需要数组是有序的。
public class BinarySearch {
public static int binarySearch(int[] arr, int target) {
int left = 0;
int right = arr.length - 1;
while (left <= right) {
int mid = left + (right - left) / 2;
if (arr[mid] == target)
return mid;
if (arr[mid] < target)
left = mid + 1;
else
right = mid - 1;
}
return -1; // 如果没有找到目标,返回-1
}
public static void main(String[] args) {
int[] arr = {-10, -5, 0, 3, 7, 9, 12};
int target = 7;
int result = binarySearch(arr, target);
if (result == -1)
System.out.println("Element not present in array");
else
System.out.println("Element found at index " + result);
}
}
在二分搜索的实现中,我们首先定义了左边界
left
和右边界right
,然后在每次迭代中计算中间索引mid
。如果arr[mid]
等于目标值target
,则搜索成功并返回索引。如果arr[mid]
小于目标值,则搜索区间变为mid + 1
到right
(因为mid
及其左边的元素都不可能是目标值)。如果arr[mid]
大于目标值,则搜索区间变为left
到mid - 1
。如果循环结束时仍未找到目标值,则返回-1。注意,二分搜索的时间复杂度为O(log n),比线性搜索的O(n)要快得多,但它要求数组已经是有序的。如果数组未排序,则需要先对其进行排序,这会增加额外的计算成本。
数据结构算法
栈(Stack)
栈是一种后进先出(LIFO)的数据结构。Java中可以使用Stack类(尽管它不是Java集合框架的一部分,并且通常建议使用Deque实现,如ArrayDeque)或自己实现栈。
import java.util.ArrayDeque;
import java.util.Deque;
public class StackExample {
private Deque<Integer> stack;
public StackExample() {
stack = new ArrayDeque<>();
}
public void push(int value) {
stack.push(value);
}
public int pop() {
return stack.pop();
}
public boolean isEmpty() {
return stack.isEmpty();
}
// 可以添加更多方法来支持栈的操作
}
队列(Queue)
队列是一种先进先出(FIFO)的数据结构。Java中Queue接口是Java集合框架的一部分,并且有多种实现,如LinkedList、PriorityQueue等。
import java.util.LinkedList;
import java.util.Queue;
public class QueueExample {
private Queue<Integer> queue;
public QueueExample() {
queue = new LinkedList<>();
}
public void enqueue(int value) {
queue.offer(value);
}
public int dequeue() {
return queue.poll();
}
public boolean isEmpty() {
return queue.isEmpty();
}
// 可以添加更多方法来支持队列的操作
}
贪心算法
贪心算法是一种在每一步选择中都采取在当前状态下最好或最优(即最有利)的选择,从而希望导致结果是全局最好或最优的算法。
贪心算法解决问题的效率很高,但它不保证总能得到最优解。对于很多问题而言,贪心算法能够产生令人满意的解,尤其是在解决一些最优化问题时。
贪心算法的基本思路是:
- 建立数学模型来描述问题。
- 把求解的问题分成若干个子问题。
- 对每一子问题求解,得到子问题的局部最优解。
- 把子问题的解局部最优解合成原来解问题的一个解。
示例:找零钱问题
假设你有无限数量的面额为1元、5元和10元的硬币,要找出给定金额的最少硬币数量。
public class CoinChange {
public int minCoins(int[] coins, int amount) {
int[] dp = new int[amount + 1];
Arrays.fill(dp, Integer.MAX_VALUE - 1);
dp[0] = 0;
for (int i = 1; i <= amount; i++) {
for (int j = 0; j < coins.length; j++) {
if (coins[j] <= i) {
dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1);
}
}
}
return dp[amount] == Integer.MAX_VALUE - 1 ? -1 : dp[amount];
}
public static void main(String[] args) {
CoinChange cc = new CoinChange();
int[] coins = {1, 5, 10};
int amount = 11;
System.out.println("Minimum coins required: " + cc.minCoins(coins, amount));
}
}
分治算法
分治算法通过将大问题分解为小问题来解决,通常这些小问题与原问题相同,但规模更小。递归地解决这些小问题,然后将结果合并以得到原始问题的答案。
归并排序
public class MergeSort {
// 主函数,用于调用归并排序
public static void sort(int[] array) {
if (array.length < 2) {
return; // 基本情形:如果数组只有一个元素,就不需要排序
}
int middle = array.length / 2;
int[] left = new int[middle];
int[] right = new int[array.length - middle];
// 将数据复制到左右两个数组
for (int i = 0; i < middle; i++) {
left[i] = array[i];
}
for (int i = middle; i < array.length; i++) {
right[i - middle] = array[i];
}
// 对左右两个数组递归地进行归并排序
sort(left);
sort(right);
// 合并排序后的左右两个数组
merge(array, left, right);
}
// 合并两个已排序的数组
private static void merge(int[] result, int[] left, int[] right) {
int i = 0, j = 0, k = 0;
// 当左右两个数组都未遍历完时,比较并合并
while (i < left.length && j < right.length) {
if (left[i] <= right[j]) {
result[k++] = left[i++];
} else {
result[k++] = right[j++];
}
}
// 将剩余的元素复制到结果数组中
while (i < left.length) {
result[k++] = left[i++];
}
while (j < right.length) {
result[k++] = right[j++];
}
}
public static void main(String[] args) {
int[] array = { 34, 7, 23, 32, 5, 62 };
sort(array);
for (int i = 0; i < array.length; i++) {
System.out.print(array[i] + " ");
}
}
}
图算法:
深度优先搜索(DFS):
深度优先搜索( DFS, Depth-First Search) 是一种用于遍历或搜索树或图的算法。在这个算法中,我们从根节点开始,尽可能深地搜索树的分支。当节点v的所在边都已被探寻过,搜索将回溯到发现节点v的那条边的起始节点。这个过程一直进行到已发现从原始节点可达的所有节点为止。对于树来说,深度优先搜索可以简单地看作是沿着树的左子树一直向下搜索,直到到达叶子节点,然后回溯并搜索右子树。这个过程会递归地进行,直到搜索完所有的节点。
深度优先搜索算法的核心思想是:
1. 访问 : 首先访问指定的起始节点, 并将其标记为已访问。
2. 深入 : 然后寻找与当前节点相邻且未被访问的节点, 并递归地进行深度优先搜索。
3. 回溯 : 如果当前节点没有相邻的未被访问的节点, 则回溯到上一个节点, 并继续搜索其他相邻的未被访问的节点。
4. 重复 : 重复上述过程, 直到所有节点都被访问过。
import java.util.*;
public class DFS {
private LinkedList<Integer>[] adjLists; // 邻接列表
private boolean[] visited; // 访问标记数组
// 构造函数,初始化图的邻接列表和访问标记数组
public DFS(int vertices) {
adjLists = new LinkedList[vertices];
visited = new boolean[vertices];
for (int i = 0; i < vertices; i++) {
adjLists[i] = new LinkedList<Integer>();
}
}
// 添加边
void addEdge(int src, int dest) {
adjLists[src].add(dest);
}
// 递归实现的深度优先搜索
void DFSUtil(int vertex) {
visited[vertex] = true;
System.out.print(vertex + " ");
Iterator<Integer> it = adjLists[vertex].listIterator();
while (it.hasNext()) {
int adj = it.next();
if (!visited[adj]) {
DFSUtil(adj);
}
}
}
// 对外提供的DFS方法,从指定的节点开始搜索
void DFS(int vertex) {
// 初始化所有节点为未访问
for (int i = 0; i < visited.length; i++) {
visited[i] = false;
}
// 从指定的节点开始递归搜索
DFSUtil(vertex);
}
public static void main(String args[]) {
DFS g = new DFS(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);
System.out.println("深度优先遍历(从顶点2开始):");
g.DFS(2);
}
}
广度优先搜索(BFS):
广度优先搜索(BFS)是一种遍历或搜索树或图的算法,从一个节点开始,访问此节点的所有邻接节点,然后对这些邻接节点逐一进行同样的访问,以此类推,直到访问完图中所有被访问的节点。以下是一个简单的广度优先搜索的示例,用于遍历一个无向图的所有节点。
import java.util.*;
public class BFSExample {
// 使用邻接表来表示图
private Map<Integer, List<Integer>> graph;
// 构造函数,初始化图
public BFSExample() {
graph = new HashMap<>();
}
// 添加边到图中
public void addEdge(int source, int destination) {
this.graph.computeIfAbsent(source, k -> new ArrayList<>()).add(destination);
this.graph.computeIfAbsent(destination, k -> new ArrayList<>()).add(source);
}
// 广度优先搜索函数
public void bfs(int start) {
Queue<Integer> queue = new LinkedList<>();
Set<Integer> visited = new HashSet<>();
queue.add(start);
visited.add(start);
while (!queue.isEmpty()) {
int current = queue.poll();
System.out.print(current + " ");
for (int neighbor : graph.getOrDefault(current, Collections.emptyList())) {
if (!visited.contains(neighbor)) {
visited.add(neighbor);
queue.add(neighbor);
}
}
}
}
public static void main(String[] args) {
BFSExample graph = new BFSExample();
graph.addEdge(1, 2);
graph.addEdge(1, 3);
graph.addEdge(2, 4);
graph.addEdge(2, 5);
graph.addEdge(3, 6);
System.out.println("广度优先搜索(BFS):");
graph.bfs(1);
}
}
除了DFS和BFS外,还有如Dijkstra算法(用于找到图中节点之间的最短
字符串算法:
KMP算法:
用于字符串匹配的算法,可以在一个文本字符串S内查找一个词W的出现位置,这个算法由Donald Knuth、Vaughan Pratt、James H. Morris三人于1977年联合发表。
下面是KMP算法的一个Java示例实现。这个实现包含了预处理阶段,用于生成部分匹配表(也称为“前缀表”或“最长公共前后缀数组”),以及实际的字符串匹配阶段。
public class KMPAlgorithm {
// 获取模式字符串的部分匹配表
public static int[] getPartialMatchTable(String pattern) {
int[] pmt = new int[pattern.length()];
int index = 0; // pattern中前缀的单个字符最长相等前后缀长度
int len = 0; // 前缀长度
pmt = 0; // 初始化部分匹配表第一位为0
// 计算部分匹配表
while (index < pattern.length() - 1) {
if (pattern.charAt(index) == pattern.charAt(len)) {
len++;
pmt[index + 1] = len;
index++;
} else {
if (len != 0) {
len = pmt[len - 1];
} else {
pmt[index + 1] = 0;
index++;
}
}
}
return pmt;
}
// KMP算法实现
public static int KMPSearch(String txt, String pattern) {
int[] pmt = getPartialMatchTable(pattern);
int i = 0; // txt的索引
int j = 0; // pattern的索引
while (i < txt.length() && j < pattern.length()) {
if (txt.charAt(i) == pattern.charAt(j)) {
i++;
j++;
} else {
if (j != 0) {
j = pmt[j - 1];
} else {
i++;
}
}
}
if (j == pattern.length()) {
return i - j; // 返回模式字符串在文本中的起始索引
} else {
return -1; // 没有找到匹配
}
}
public static void main(String[] args) {
String txt = "ABABDABACDABABCABAB";
String pattern = "ABABCABAB";
int index = KMPSearch(txt, pattern);
if (index != -1) {
System.out.println("找到模式字符串,起始索引为:" + index);
} else {
System.out.println("未找到模式字符串");
}
}
}
Boyer-Moore算法:
Boyer-Moore算法是一种高效的字符串匹配算法, 特别适用于在较长的文本中查找较短的模式字符串。该算法的核心在于,当字符不匹配时,能够利用已经匹配过的部分信息,将模式字符串向右滑动更远的距离。
以下是Boyer-Moore算法的Java示例实现:
public class BoyerMooreAlgorithm {
// 坏字符规则表,记录每个字符最后出现的位置
private int[] badCharRule;
// 构造函数,初始化坏字符规则表
public BoyerMooreAlgorithm(String pattern) {
int patternLength = pattern.length();
badCharRule = new int; // 假设字符集是ASCII
// 初始化坏字符规则表,默认值为-1
for (int i = 0; i < 256; i++) {
badCharRule[i] = -1;
}
// 更新坏字符规则表
for (int i = 0; i < patternLength; i++) {
badCharRule[pattern.charAt(i)] = i;
}
}
// Boyer-Moore算法实现
public int search(String txt, String pattern) {
int txtLength = txt.length();
int patternLength = pattern.length();
int shift = 0;
while (shift <= (txtLength - patternLength)) {
int patternIndex = patternLength - 1;
// 从模式字符串的末尾开始匹配
while (patternIndex >= 0 && pattern.charAt(patternIndex) == txt.charAt(shift + patternIndex)) {
patternIndex--;
}
if (patternIndex < 0) {
// 找到匹配,返回模式字符串在文本中的起始索引
return shift;
} else {
// 不匹配,根据坏字符规则计算滑动距离
int badChar = txt.charAt(shift + patternLength - 1);
shift += Math.max(1, patternLength - badCharRule[badChar] - 1);
}
}
return -1; // 未找到匹配
}
public static void main(String[] args) {
String txt = "HERE IS A SIMPLE EXAMPLE";
String pattern = "EXAMPLE";
BoyerMooreAlgorithm algorithm = new BoyerMooreAlgorithm(pattern);
int index = algorithm.search(txt, pattern);
if (index != -1) {
System.out.println("找到模式字符串,起始索引为:" + index);
} else {
System.out.println("未找到模式字符串");
}
}
}
除了之前提到的KMP算法和Boyer-Moore算法外,还有其他如Rabin-Karp算法(用于字符串搜索)、Levenshtein距离(用于测量两个序列之间的差异)等。
动态规划:
动态规划通过将原问题分解为相对简单的子问题,并保存子问题的解来避免重复计算,从而解决复杂问题。
斐波那契数列:
用动态规划求解斐波那契数列,避免重复计算。
最长公共子序列(LCS):
在两个序列中找到最长公共子序列的问题。
回溯算法:
回溯算法通过探索所有可能的候选解来找出所有解的算法。如果候选解被确认不是一个解(或者至少不是最后一个解),回溯算法会通过在上一步进行一些变化来撤销上一步或上几步的计算,即“回溯”并尝试另一种可能的候选解。
八皇后问题:
在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击(即任意两个皇后都不能处于同一行、同一列或同一斜线上),问有多少种摆法。
public class EightQueens {
private static final int SIZE = 8;
private int[] queens = new int[SIZE]; // 数组下标表示行,值表示列
private int solutionsCount = 0;
public void solve() {
placeQueen(0);
System.out.println("共有 " + solutionsCount + " 种解法");
}
private void placeQueen(int row) {
if (row == SIZE) {
solutionsCount++;
printSolution();
return;
}
for (int col = 0; col < SIZE; col++) {
queens[row] = col;
if (isSafe(row, col)) {
placeQueen(row + 1);
}
}
}
private boolean isSafe(int row, int col) {
for (int i = 0; i < row; i++) {
int otherCol = queens[i];
if (otherCol == col || Math.abs(otherCol - col) == Math.abs(i - row)) {
return false;
}
}
return true;
}
private void printSolution() {
for (int row = 0; row < SIZE; row++) {
for (int col = 0; col < SIZE; col++) {
System.out.print(queens[row] == col ? "Q " : ". ");
}
System.out.println();
}
System.out.println();
}
public static void main(String[] args) {
EightQueens eq = new EightQueens();
eq.solve();
}
}
标签:arr,Java,int,void,常见,length,算法,array,public
From: https://blog.csdn.net/WZX17307935391/article/details/141337049