程序员常用的几种算法

Java程序员通常需要掌握多种算法,这些算法不仅有助于提高编程效率,而且在解决实际问题时至关重要。

以下是一些Java程序员常用的算法类别和简要说明,由于篇幅限制,这里无法提供完整的代码示例,但会给出每种算法的核心思路和部分伪代码/简单实现概述。

1、排序算法

  • 冒泡排序(Bubble Sort):

重复地交换相邻两个顺序错误的元素,直至没有再需要交换的元素为止。

1void bubbleSort(int[] arr) {
2    for (int i = 0; i < arr.length - 1; i++) {
3        for (int j = 0; j < arr.length - 1 - i; j++) {
4            if (arr[j] > arr[j + 1]) {
5                swap(arr, j, j + 1);
6            }
7        }
8    }
9}
10
11// 交换数组中两个位置i和j的元素
12void swap(int[] arr, int i, int j) {
13    int temp = arr[i];
14    arr[i] = arr[j];
15    arr[j] = temp;
16}
  • 快速排序(Quick Sort):

基于分治思想,选取一个“基准”元素,将数组划分为小于基准和大于基准的两部分,对这两部分递归地进行快速排序。

1void quickSort(int[] arr, int low, int high) {
2    if (low < high) {
3        int pivotIndex = partition(arr, low, high);
4        quickSort(arr, low, pivotIndex - 1);
5        quickSort(arr, pivotIndex + 1, high);
6    }
7}
8
9int partition(int[] arr, int low, int high) {
10    int pivot = arr[high];
11    int i = (low - 1);
12    for (int j = low; j < high; j++) {
13        if (arr[j] <= pivot) {
14            i++;
15            swap(arr, i, j);
16        }
17    }
18    swap(arr, i + 1, high);
19    return i + 1;
20}
  • 归并排序(Merge Sort):

将数组分割成两半,分别对它们进行排序,然后合并已排序的子数组。

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);
    }
}

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、查找算法:

  • 线性查找(Linear Search):

从数组的第一个元素开始,逐个检查直到找到目标值或遍历完整个数组。

1int linearSearch(int[] arr, int target) {
2    for (int i = 0; i < arr.length; i++) {
3        if (arr[i] == target) {
4            return i;
5        }
6    }
7    return -1; // 如果未找到,则返回-1或其他标记
8}
  •  二分查找(Binary Search):

在有序数组中查找目标值,每次将搜索范围减半。

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;
        } else if (arr[mid] < target) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }

    return -1; // 如果未找到,则返回-1或其他标记
}

3、字符串匹配算法

KMP算法(Knuth-Morris-Pratt Algorithm):

用于在一个字符串中查找子串出现的位置,它利用了主串的部分匹配信息避免回溯。

int kmp(String text, String pattern) {
    // 先计算pattern的next数组
    int[] next = computeNext(pattern);

    int tLen = text.length();
    int pLen = pattern.length();
    int i = 0, j = 0;

    while (i < tLen && j < pLen) {
        if (j == -1 || text.charAt(i) == pattern.charAt(j)) {
            i++;
            j++;
        } else {
            j = next[j];
        }
    }

    if (j == pLen) {
        return i - j; // 返回子串在主串中的起始索引
    } else {
        return -1; // 没找到子串
    }
}

// 计算next数组的函数省略...

4、其他重要算法

  • 图的遍历算法(如深度优先搜索DFS和广度优先搜索BFS)

深度优先搜索(DFS)和广度优先搜索(BFS)的简单Java代码示例,这里假设使用邻接列表表示图。

首先,定义一个简单的图节点类和邻接列表表示的图类:

// 定义图节点类
class Node {
    int value;
    List<Node> neighbors;

    public Node(int value) {
        this.value = value;
        this.neighbors = new ArrayList<>();
    }
}

// 定义邻接列表表示的图类
class Graph {
    Map<Integer, Node> nodes;

    public Graph() {
        this.nodes = new HashMap<>();
    }

    // 添加节点和边的方法在此省略...
}

深度优先搜索 (DFS):

import java.util.Stack;

void dfs(Node node, boolean[] visited) {
    visited[node.value] = true;
    System.out.println("Visiting Node: " + node.value);

    Stack<Node> stack = new Stack<>();
    stack.push(node);

    while (!stack.isEmpty()) {
        Node curr = stack.pop();

        for (Node neighbor : curr.neighbors) {
            if (!visited[neighbor.value]) {
                visited[neighbor.value] = true;
                System.out.println("Visiting Node: " + neighbor.value);
                stack.push(neighbor);
            }
        }
    }
}

广度优先搜索 (BFS)

1import java.util.LinkedList;
2import java.util.Queue;
3
4void bfs(Node node, boolean[] visited) {
5    visited[node.value] = true;
6    System.out.println("Visiting Node: " + node.value);
7
8    Queue<Node> queue = new LinkedList<>();
9    queue.add(node);
10
11    while (!queue.isEmpty()) {
12        Node curr = queue.poll();
13
14        for (Node neighbor : curr.neighbors) {
15            if (!visited[neighbor.value]) {
16                visited[neighbor.value] = true;
17                System.out.println("Visiting Node: " + neighbor.value);
18                queue.add(neighbor);
19            }
20        }
21    }
22}

在这两个例子中,visited 数组用于记录每个节点是否已经被访问过,防止重复访问。

深度优先搜索使用栈来按照递归的方式探索节点的邻居,

而广度优先搜索则使用队列先访问与当前节点距离最近的所有邻居节点。

  • 动态规划算法(例如背包问题、最长公共子序列等)

背包问题

0-1背包问题的一个简单动态规划解决方案的Java代码示例:

// 0-1背包问题的动态规划解决方案
public class KnapsackProblem {
    static class Item {
        int value;
        int weight;

        Item(int value, int weight) {
            this.value = value;
            this.weight = weight;
        }
    }

    // 动态规划解0-1背包问题
    public static int knapsack(int capacity, Item[] items) {
        int numItems = items.length;
        
        // 初始化二维DP表
        int[][] dp = new int[numItems + 1][capacity + 1];

        // 动态规划填表
        for (int i = 1; i <= numItems; i++) {
            for (int w = 0; w <= capacity; w++) {
                // 不选当前物品的情况
                dp[i][w] = dp[i - 1][w];
                
                // 若背包容量足够容纳当前物品,则比较选与不选的最大价值
                if (items[i - 1].weight <= w) {
                    dp[i][w] = Math.max(dp[i][w], dp[i - 1][w - items[i - 1].weight] + items[i - 1].value);
                }
            }
        }

        // 返回背包所能达到的最大价值
        return dp[numItems][capacity];
    }

    public static void main(String[] args) {
        // 假设有以下物品及其价值和重量
        Item[] items = {new Item(60, 10), new Item(100, 20), new Item(120, 30)};
        int capacity = 50; // 背包总容量

        // 解决0-1背包问题
        int maxVal = knapsack(capacity, items);
        System.out.println("能装入背包的最大价值是: " + maxVal);
    }
}

在上述代码中,我们使用一个二维数组dp来存储子问题的解。dp[i][w]表示在只有前i个物品可用,并且背包容量为w的情况下能够得到的最大价值。通过比较不包含当前物品和包含当前物品(只要背包还能装下它)这两种情况下的最大价值,我们可以更新dp[i][w]的值,最后dp[numItems][capacity]即为所求解。

对于最长公共子序列(LCS)问题,动态规划的解决方案也会类似地构建一个二维数组,并通过状态转移方程填充该数组以确定最长公共子序列的长度。不过,其状态转移逻辑会有所不同,因为它涉及到字符串的比较而非物品的价值和重量。

  • 贪心算法(如求解活动安排问题、最小生成树构造等)

解活动安排问题和最小生成树构造的贪心算法的简单Java代码示例

求解活动安排问题的贪心算法示例:

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;

class Activity {
    int start;
    int end;

    Activity(int start, int end) {
        this.start = start;
        this.end = end;
    }
}

public class ActivitySelectionGreedy {
    public List<Activity> selectActivities(Activity[] activities) {
        // 先按照结束时间升序排序
        Arrays.sort(activities, Comparator.comparingInt(a -> a.end));

        List<Activity> selectedActivities = new ArrayList<>();
        int currentTime = 0;
        
        for (Activity activity : activities) {
            // 只有当当前活动的开始时间大于等于上一个结束时间时,才可以选择该活动
            if (activity.start >= currentTime) {
                selectedActivities.add(activity);
                currentTime = activity.end; // 更新当前时间至最新结束时间
            }
        }
        
        return selectedActivities;
    }

    public static void main(String[] args) {
        Activity[] activities = {
            new Activity(1, 4),
            new Activity(3, 5),
            new Activity(0, 6),
            new Activity(5, 7)
        };

        ActivitySelectionGreedy solver = new ActivitySelectionGreedy();
        List<Activity> result = solver.selectActivities(activities);

        // 打印出可以安排的活动
        for (Activity act : result) {
            System.out.println("Start time: " + act.start + ", End time: " + act.end);
        }
    }
}

最小生成树构造的贪心算法(Prim算法)示例:

import java.util.*;

class Edge implements Comparable<Edge> {
    int src;
    int dest;
    int weight;

    Edge(int src, int dest, int weight) {
        this.src = src;
        this.dest = dest;
        this.weight = weight;
    }

    @Override
    public int compareTo(Edge other) {
        return Integer.compare(this.weight, other.weight);
    }
}

public class PrimMinimumSpanningTree {
    private static final int V = 5; // 假设图有5个顶点

    public List<Edge> primMST(List<List<Edge>> adj) {
        boolean[] mstSet = new boolean[V]; // 记录某个顶点是否已在MST中
        PriorityQueue<Edge> pq = new PriorityQueue<>(Comparator.comparing(Edge::getWeight));
        
        // 初始化:从任意一个顶点开始,将其标记为已访问
        mstSet[0] = true;
        for (Edge e : adj.get(0)) {
            pq.offer(e);
        }

        List<Edge> result = new ArrayList<>();

        while (result.size() < V - 1) {
            // 从优先队列中取出权值最小的边
            Edge edge = pq.poll();
            if (!mstSet[edge.dest]) {
                result.add(edge);
                mstSet[edge.dest] = true;

                // 将与新加入顶点相邻的所有未访问过的边放入优先队列
                for (Edge e : adj.get(edge.dest)) {
                    if (!mstSet[e.src]) {
                        pq.offer(e);
                    }
                }
            }
        }

        return result;
    }

    public static void main(String[] args) {
        // 创建邻接列表表示图结构
        List<List<Edge>> adj = new ArrayList<>(); // 这里假设已经填充了图的邻接矩阵或列表
        
        PrimMinimumSpanningTree solver = new PrimMinimumSpanningTree();
        List<Edge> minSpanningTree = solver.primMST(adj);

        // 打印最小生成树的所有边
        for (Edge edge : minSpanningTree) {
            System.out.println("Edge from " + edge.src + " to " + edge.dest + ", Weight: " + edge.weight);
        }
    }
}

注意:

示例简化了一些细节,实际应用时可能需要处理更多的边界条件和特殊情况。

对于最小生成树的构造,Prim算法要求图是以邻接矩阵或邻接表的形式表示的,这里仅展示了算法的核心部分,具体的数据结构填充以及图的读取需要根据实际情况实现。

  • 数据结构相关的算法,如链表插入删除、树的遍历(二叉树、平衡树)、哈希表的插入查找等

待更新...

 如果需要练习算法:

1、力扣:力扣 (LeetCode) 全球极客挚爱的技术成长平台

2、牛客:牛客网 - 找工作神器|笔试题库|面试经验|实习招聘内推,求职就业一站解决_牛客网

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值