常用算法

常用算法

快速排序

    public int[] sortArray(int[] nums) {
        if (nums == null || nums.length < 2) return nums;
        quickSort(nums, 0, nums.length - 1);
        return nums;
    }

    public void quickSort(int[] arr, int st, int ed) {
        if (st >= ed) return ;

        int mi = arr[st + ed >> 1];
        int l = st - 1, r = ed + 1;

        while (l < r) {
            while (arr[--r] > mi) ;
            while (arr[++l] < mi) ;
            if (l < r) swap(arr, l, r);
        }

        quickSort(arr, st, r);
        quickSort(arr, r + 1, ed);
    }

快速选择第k大

    public int findKthLargest(int[] arr, int k) {
        return quickSelect(arr, 0, arr.length - 1, k);
    }

    int quickSelect(int[] arr, int k, int st, int ed){
        if(st >= ed) return arr[st];

        int l = st - 1, r = ed + 1;
        int mi = arr[l + r >> 1];

        while(l < r){
            while(arr[++l] > mi);
            while(arr[--r] < mi);
            if(l < r) swap(arr, l, r);
        }

        int pivotPos = r - st + 1;
        if(pivotPos >= k) return quickSelect(arr, st, r, k);
        return quickSelect(arr, r + 1, ed, k - pivotPos);
    }

归并排序

    public int[] sortArray(int[] nums) {
        if (nums == null || nums.length < 2) return nums;
        mergeSort(nums, 0, nums.length - 1, new int[nums.length]);

        return nums;
    }

    public void mergeSort(int[] arr, int l, int r, int[] tmp) { // tmp防止每次开数组
        if (l >= r) return ;

        int m = (l + r) >>> 1;
        mergeSort(arr, l, m, tmp); // 以m区间划分[l, r]
        mergeSort(arr, m + 1, r, tmp);

        int i = l, j = m + 1; // 左右区间分别的起始点i, j
        int p = 0;
        while (i <= m && j <= r) tmp[p++] = arr[i] <= arr[j] ? arr[i++] : arr[j++];

        while (i <= m) tmp[p++] = arr[i++];
        while (j <= r) tmp[p++] = arr[j++];

        p = 0;
        while (l <= r) arr[l++] = tmp[p++];
    }

堆排序

    public int[] sortArray(int[] arr){
        //1. 建堆
        for (int i = arr.length / 2 - 1; i >= 0; i--) { //从第一个非叶子结点从下至上,从右至左调整结构
            heapify(arr, i, arr.length);
        }
        //2. 调整堆结构+交换堆顶元素与末尾元素
        for (int j = arr.length - 1; j > 0; j--) {
            swap(arr, 0, j);
            heapify(arr, 0, j); // 重新对堆进行调整
        }

        return arr;
    }

    void heapify(int[] arr, int i, int length) { // i为父节点,2i+1为左孩子,2i+2为右孩子
        for (int k = 2 * i + 1; k < length; k = 2 * k + 1) { // 每次从左孩子开始
            if(k + 1 < length && arr[k] < arr[k + 1]) // k指向左右孩子中的最大值
                k++;
            if(arr[i] < arr[k]){ // 1. 如果父节点i < 孩子中的最大值k
                swap(arr, i, k); // 1.1 父结点i下沉到最大值k处, i与k交换
                i = k;  // 1.2 父节点i沿着孩子中的最大值k一路下沉,直到已经是大顶堆
            } else break; // 2. 否则父节点i > 孩子中的最大值,已经是大顶堆
        }
    }

KMP

    public int strStr(String str, String pattern) {
        int m = str.length(), n = pattern.length();
        if (n == 0) return 0; // 默认空的模式串pattern是匹配的
        if (m < n) return -1; // 模式串 > 匹配串

        char[] s = str.toCharArray();
        char[] p = pattern.toCharArray();
        int[] next = getNext(p);

        int i = 0, j = 0; // 
        while (i < m && j < n) {
            if (s[i] == p[j]) { // 1.如果当前字符匹配,指针分别后移一位
                ++i;
                ++j;
            } else if (j > 0) { // 2.否则,模式串指针开始向前找最大相同前后缀
                j = next[j];
            } else { // 3.直到j == 0,也没有最大相同前后缀,i位置匹配失败,从匹配串下一个字符开始匹配
                ++i;
            }
        }

        return j == n ? i - n : -1; // 如果模式串遍历完,则找到一个匹配位置:当前匹配串i处向前一个pattern
    }

    private int[] getNext(char[] pattern) {
        int n = pattern.length;
        int[] next = new int[n];
        next[0] = -1;
        // java默认next[1] = 0;
        int i = 2; // 0,1处最大相同前后缀已定义好,从i=2开始

        // 1.最大匹配前缀的下一个待检查字符pattern[pre]
        // 2.在i-1处的匹配前后缀长度:next[i-1]==pre
        int pre = 0;

        while (i < n) {
            // 最大匹配前缀的下一个字符pre和当前位置i的前一个字符i-1比较
            if (pattern[pre] == pattern[i - 1]) { // 1. 若相等,则当前位置匹配长度next[i], 是在i-1匹配长度next[i-1]==pre的基础上+1
                next[i++] = ++pre;
            } else if (pre > 0) { // 2. 否则pre向前找匹配前缀
                pre = next[pre];
            } else { // 3. 直到pre == 0也找不到,说明当前位置匹配长度next[i]=0
                // next[i++] = 0; // java数组默认是0
                i++;
            }
        }

        return next;
    }

Manacher

    public String longestPalindrome(String str) {
        char[] s = process(str);
        int len = s.length;
        int R = -1; // 全局最右回文下标
        int C = -1; // 全局回文中心
        int max = -1;
        int[] maxPalinR = new int[len]; // 每个位置的最大回文半径

        for (int i = 0; i < len; ++i) {

            maxPalinR[i] = i < R ? Math.min(R - i, maxPalinR[2 * C - i]) : 1;

            while (i + maxPalinR[i] < len && i - maxPalinR[i] >= 0) {
                if (s[i + maxPalinR[i]] == s[i - maxPalinR[i]]) {
                    maxPalinR[i]++;
                } else break;
            }

            if (i + maxPalinR[i] > R) {
                R = i + maxPalinR[i];
                C = i;
            }

            max = Math.max(max, maxPalinR[i]);
        }

        return max - 1;
    }

单源最短路径

BFS(要求权值相同)

Dijkstra = 带权BFS + FibHeap优化(要求无负权边)

    public int[] dijkstra(int[][] graph, int src) {
        // 针对零权边,还需要graph除了有权的可达边外,其余不可达边权值都为-inf
        int n = graph.length;
        int[] dist = new int[n];
        Arrays.fill(dist, Integer.MAX_VALUE); // 先默认dist[i]= +inf表示src到i不可达
        dist[src] = 0; // src到自身minDist = 0

        // 按curDist的小顶堆
        PriorityQueue<int[]> pq = new PriorityQueue<>((o1, o2) -> o1[1] - o2[1]);

        // src到自身的curDist = 0
        pq.offer(new int[]{src, 0});

        while (!pq.isEmpty()) {
            int[] cur = pq.poll();
            int curV = cur[0]; // 当前顶点curV
            int curDist = cur[1]; // src到当前顶点curV的距离curDist

            if (curDist > dist[curV]) continue;

            for (int i = 0; i < n; ++i) {
                // 遍历当前顶点curV->邻接点i的距离,加上src->curV的距离curDist,组成src->i的距离newDist
                int newDist = curDist + graph[curV][i];
                if (graph[v][i] >= 0 && newDist < dist[i]) { 
                    //取最小newDist作为src->i的最小距离
                    pq.offer(new int[]{i, dist[i] = newDist});
                }
            }
        }

        return dist;
    }

Bellman-Ford(可以负权边, 但不能存在和为负的环)

public int[] bellmanFord(int[][] graph, int src) {
    int n = graph.length;
    int[] dist = new int[n];
    Arrays.fill(dist, Integer.MAX_VALUE);
    dist[src] = 0;
    for (int i = 0; i < n - 1; ++i) {
        for (int j = 0; j < n; ++j) {
            for (int k = 0; k < n; ++k) {
                dist[k] = Math.min(dist[k], dist[j] + graph[j][k]);
            }
        }
    }
    for (int j = 0; j < n; ++j) {
        for (int k = 0; k < n; ++k) {
            dist[k] = Math.min(dist[k], dist[j] + graph[j][k]);
        }
    }
    return dist;
}

全源最短路径

遍历每个顶点, 分别使用单源最短路径

Floyd = DP(要求无负权回路)

    public int[][] floyd(int[][] graph) {
        int n = graph.length;
        int[][] dist = new int[n][n];

        // 或者直接在原图graph上改
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < n; ++j) {
                dist[i][j] = graph[i][j];
            }
        }

        // 建立最短路径矩阵:源点i经过中间点k到达目标j
        for (int k = 0; k < n; ++k) { // 枚举中间点k
            for (int i = 0; i < n; ++i) {
                for (int j = 0; j < n; ++j) {
                    dist[i][j] = Math.min(dist[i][j], dist[i][k] + dist[k][j]);
                }
            }
        }

        return dist;
    }

Johnson = 松弛遍历Dijkstra

  • 建立虚拟根节点root
  • bellman-ford求root到所有点距离h[]
  • 更新原权重w(u, v) += (h[u] - h[v])
  • 移除root, 做dijkstra
posted @ 2022-08-31 11:38  Blazer96  阅读(32)  评论(0)    收藏  举报